[SECURITY] Sensitive data got saved unencrypted in the database
This commit is contained in:
parent
11eb7f47c9
commit
80131f0e99
|
@ -47,7 +47,6 @@ class RunCommand extends Command
|
|||
}
|
||||
$jobRepo->setJobRunning($job, true);
|
||||
$jobRepo->setTempVar($job, 'consolerun', true);
|
||||
$jobRepo->parseJob($job);
|
||||
$result = $jobRepo->runNow($job, true);
|
||||
if($job->getData('crontype') == 'reboot') {
|
||||
$sleeping = true;
|
||||
|
|
|
@ -28,7 +28,6 @@ class JobController extends AbstractController
|
|||
|
||||
if($request->getMethod() == 'GET') {
|
||||
$job = $jobRepo->find($id);
|
||||
$jobRepo->parseJob($job);
|
||||
$runs = $runRepo->getRunsForJob($job, $all != 'all');
|
||||
return $this->render('job/view.html.twig', ['job' => $job, 'runs' => $runs, 'allruns' => $all == 'all']);
|
||||
} elseif($request->getMethod() == 'DELETE') {
|
||||
|
@ -44,7 +43,6 @@ class JobController extends AbstractController
|
|||
if($request->getMethod() == 'GET') {
|
||||
$jobRepo = $doctrine->getRepository(Job::class);
|
||||
$job = $jobRepo->find($id);
|
||||
$jobRepo->parseJob($job, true);
|
||||
return $this->render('job/edit.html.twig', ['job' => $job]);
|
||||
} elseif($request->getMethod() == 'POST') {
|
||||
$allValues = $request->request->all();
|
||||
|
|
|
@ -187,16 +187,17 @@ class JobRepository extends EntityRepository
|
|||
{
|
||||
$client = new Client();
|
||||
|
||||
$url = $job->getData('url');
|
||||
$user = $job->getData('basicauth-username');
|
||||
if(!empty($job->getData('vars'))) {
|
||||
foreach($job->getData('vars') as $key => $var) {
|
||||
if (!empty($job->getData('basicauth-username'))) $job->setData('basicauth-username', str_replace('{' . $key . '}', $var['value'], $job->getData('basicauth-username')));
|
||||
$job->setData('url', str_replace('{' . $key . '}', $var['value'], $job->getData('url')));
|
||||
if (!empty($user)) $user = str_replace('{' . $key . '}', ($var['issecret'] ? Secret::decrypt(base64_decode($var['value'])) : $var['value']), $job->getData('basicauth-username'));
|
||||
$url = str_replace('{' . $key . '}', ($var['issecret'] ? Secret::decrypt(base64_decode($var['value'])) : $var['value']), $job->getData('url'));
|
||||
}
|
||||
}
|
||||
|
||||
$url = $job->getData('url');
|
||||
$options['http_errors'] = false;
|
||||
$options['auth'] = !empty($job->getData('basicauth-username')) ? [$job->getData('basicauth-username'), $job->getData('basicauth-password')] : NULL;
|
||||
$options['auth'] = !empty($user) ? [$user, Secret::decrypt(base64_decode($job->getData('basicauth-password')))] : NULL;
|
||||
try {
|
||||
$res = $client->request('GET', $url, $options);
|
||||
$return['exitcode'] = $res->getStatusCode();
|
||||
|
@ -217,13 +218,13 @@ class JobRepository extends EntityRepository
|
|||
*/
|
||||
private function runCommandJob(Job &$job): array
|
||||
{
|
||||
$command = $job->getData('command');
|
||||
if(!empty($job->getData('vars'))) {
|
||||
foreach ($job->getData('vars') as $key => $var) {
|
||||
$job->setData('command', str_replace('{' . $key . '}', $var['value'], $job->getData('command')));
|
||||
$command = str_replace('{' . $key . '}', $var['value'], $job->getData('command'));
|
||||
}
|
||||
}
|
||||
|
||||
$command = $job->getData('command');
|
||||
if ($job->getData('containertype') == 'docker') {
|
||||
$command = $this->prepareDockerCommand($command, $job->getData('service'), $job->getData('container-user'));
|
||||
}
|
||||
|
@ -231,7 +232,7 @@ class JobRepository extends EntityRepository
|
|||
if($job->getData('hosttype') == 'local') {
|
||||
$return = $this->runLocalCommand($command);
|
||||
} elseif($job->getData('hosttype') == 'ssh') {
|
||||
$return = $this->runSshCommand($command, $job->getData('host'), $job->getData('user'), $job->getData('ssh-privkey'), $job->getData('privkey-password'));
|
||||
$return = $this->runSshCommand($command, $job->getData('host'), $job->getData('user'), Secret::decrypt(base64_decode($job->getData('ssh-privkey'))), Secret::decrypt(base64_decode($job->getData('privkey-password'))));
|
||||
}
|
||||
$return['failed'] = !in_array($return['exitcode'], $job->getData('response'));
|
||||
} catch (\RuntimeException $exception) {
|
||||
|
@ -272,9 +273,9 @@ class JobRepository extends EntityRepository
|
|||
$key = null;
|
||||
if(!empty($privkey)) {
|
||||
if(!empty($password)) {
|
||||
$key = PublicKeyLoader::load(base64_decode($privkey), $password);
|
||||
$key = PublicKeyLoader::load($privkey, $password);
|
||||
} else {
|
||||
$key = PublicKeyLoader::load(base64_decode($privkey));
|
||||
$key = PublicKeyLoader::load($privkey);
|
||||
}
|
||||
} elseif (!empty($password)) {
|
||||
$key = $password;
|
||||
|
@ -322,7 +323,7 @@ class JobRepository extends EntityRepository
|
|||
if($job->getData('hosttype') == 'local') {
|
||||
$this->runLocalCommand($rebootcommand);
|
||||
} elseif($job->getData('hosttype') == 'ssh') {
|
||||
$this->runSshCommand($rebootcommand, $job->getData('host'), $job->getData('user'), $job->getData('ssh-privkey') ?? '', $job->getData('privkey-password') ?? '');
|
||||
$this->runSshCommand($rebootcommand, $job->getData('host'), $job->getData('user'), Secret::decrypt(base64_decode($job->getData('ssh-privkey'))) ?? '', Secret::decrypt(base64_decode($job->getData('privkey-password'))) ?? '');
|
||||
}
|
||||
} catch (\RuntimeException $exception) {
|
||||
$return['exitcode'] = $exception->getCode();
|
||||
|
@ -343,16 +344,17 @@ class JobRepository extends EntityRepository
|
|||
|
||||
$this->setJobRunning($job, true);
|
||||
|
||||
$getservicescommand = $job->getData('getservices-command');
|
||||
if (!empty($job->getData('vars'))) {
|
||||
foreach ($job->getData('vars') as $key => $var) {
|
||||
$job->setData('getservices-command', str_replace('{' . $key . '}', $var['value'], $job->getData('getservices-command')));
|
||||
$getservicescommand = str_replace('{' . $key . '}', $var['value'], $job->getData('getservices-command'));
|
||||
}
|
||||
}
|
||||
try {
|
||||
if($job->getData('hosttype') == 'local') {
|
||||
$return = $this->runLocalCommand($job->getData('getservices-command'));
|
||||
$return = $this->runLocalCommand($getservicescommand);
|
||||
} elseif($job->getData('hosttype') == 'ssh') {
|
||||
$return = $this->runSshCommand($job->getData('getservices-command'), $job->getData('host'), $job->getData('user'), $job->getData('ssh-privkey') ?? '', $job->getData('privkey-password') ?? '');
|
||||
$return = $this->runSshCommand($getservicescommand, $job->getData('host'), $job->getData('user'), Secret::decrypt(base64_decode($job->getData('ssh-privkey'))) ?? '', Secret::decrypt(base64_decode($job->getData('privkey-password'))) ?? '');
|
||||
}
|
||||
} catch (\RuntimeException $exception) {
|
||||
$return['exitcode'] = $exception->getCode();
|
||||
|
@ -374,7 +376,6 @@ class JobRepository extends EntityRepository
|
|||
*/
|
||||
public function runNow(Job &$job, $console = false) {
|
||||
$em = $this->getEntityManager();
|
||||
$this->parseJob($job, true);
|
||||
$runRepo = $this->getEntityManager()->getRepository(Run::class);
|
||||
|
||||
if($console == false && ($runRepo->isSlowJob($job)) || count($job->getRuns()) == 0 || $job->getData('crontype') === 'reboot') {
|
||||
|
@ -422,7 +423,6 @@ class JobRepository extends EntityRepository
|
|||
{
|
||||
$em = $this->getEntityManager();
|
||||
$starttime = microtime(true);
|
||||
$this->parseJob($job, true);
|
||||
if ($job->getData('crontype') == 'http') {
|
||||
$result = $this->runHttpJob($job);
|
||||
} elseif ($job->getData('crontype') == 'command') {
|
||||
|
@ -593,17 +593,8 @@ class JobRepository extends EntityRepository
|
|||
$job->setData('getservices-command', $values['getservices-command']);
|
||||
$job->setData('getservices-response', explode(',',$values['getservices-response']));
|
||||
$job->setData('reboot-duration', $values['reboot-duration']);
|
||||
if(!empty($values['reboot-delay']) || $values['reboot-delay'] == 0) {
|
||||
$newsecretkey = count($values['var-value']);
|
||||
$values['var-id'][$newsecretkey] = 'reboot-delay';
|
||||
$values['var-issecret'][$newsecretkey] = false;
|
||||
$values['var-value'][$newsecretkey] = (int)$values['reboot-delay'];
|
||||
|
||||
$newsecretkey = count($values['var-value']);
|
||||
$values['var-id'][$newsecretkey] = 'reboot-delay-secs';
|
||||
$values['var-issecret'][$newsecretkey] = false;
|
||||
$values['var-value'][$newsecretkey] = (int)$values['reboot-delay'] * 60;
|
||||
}
|
||||
$job->setData('reboot-delay', (int)$values['reboot-delay']);
|
||||
$job->setData('reboot-delay-secs', (int)$values['reboot-delay'] * 60);
|
||||
break;
|
||||
case 'http':
|
||||
$parsedUrl = parse_url($values['url']);
|
||||
|
@ -614,10 +605,7 @@ class JobRepository extends EntityRepository
|
|||
throw new \InvalidArgumentException('Some data was invalid');
|
||||
}
|
||||
if(!empty($values['basicauth-password'])) {
|
||||
$newsecretkey = count($values['var-value']);
|
||||
$values['var-id'][$newsecretkey] = 'basicauth-password';
|
||||
$values['var-issecret'][$newsecretkey] = true;
|
||||
$values['var-value'][$newsecretkey] = $values['basicauth-password'];
|
||||
$job->setData('basicauth-password', base64_encode(Secret::encrypt($values['basicauth-password'])));
|
||||
}
|
||||
$job->setData('host', $parsedUrl['host']);
|
||||
break;
|
||||
|
@ -633,32 +621,21 @@ class JobRepository extends EntityRepository
|
|||
case 'ssh':
|
||||
$job->setData('host', $values['host']);
|
||||
$job->setData('user', $values['user']);
|
||||
$job->removeData('privkey-password');
|
||||
if(!empty($values['privkey-password'])) {
|
||||
$newsecretkey = count($values['var-value']);
|
||||
$values['var-id'][$newsecretkey] = 'privkey-password';
|
||||
$values['var-issecret'][$newsecretkey] = true;
|
||||
$values['var-value'][$newsecretkey] = $values['privkey-password'];
|
||||
$job->setData('privkey-password', base64_encode(Secret::encrypt($values['privkey-password'])));
|
||||
}
|
||||
$privkeyid = NULL;
|
||||
if(!empty($_FILES['privkey']['tmp_name'])) {
|
||||
$newsecretkey = count($values['var-value']);
|
||||
$privkeyid = $newsecretkey;
|
||||
$values['var-id'][$newsecretkey] = 'ssh-privkey';
|
||||
$values['var-issecret'][$newsecretkey] = true;
|
||||
$values['var-value'][$newsecretkey] = base64_encode(file_get_contents($_FILES['privkey']['tmp_name']));
|
||||
$job->setData('ssh-privkey', base64_encode(Secret::encrypt(file_get_contents($_FILES['privkey']['tmp_name']))));
|
||||
}
|
||||
if(isset($values['privkey-keep']) && $values['privkey-keep'] == true) {
|
||||
$privkeyid = ($privkeyid === NULL) ? count($values['var-value']) : $privkeyid ;
|
||||
$values['var-id'][$privkeyid] = 'ssh-privkey';
|
||||
$values['var-issecret'][$privkeyid] = true;
|
||||
$values['var-value'][$privkeyid] = $values['privkey-orig'];
|
||||
|
||||
$job->setData('ssh-privkey', base64_encode(Secret::encrypt($values['privkey-orig'])));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
switch($job->getData('hosttype')) {
|
||||
switch($job->getData('containertype')) {
|
||||
default:
|
||||
if($job->getData('crontype') == 'http' || $job->getData('crontype') == 'reboot' ) break;
|
||||
$job->setData('containertype', 'none');
|
||||
|
@ -670,7 +647,7 @@ class JobRepository extends EntityRepository
|
|||
$job->setData('container-user', $values['container-user']);
|
||||
break;
|
||||
}
|
||||
|
||||
$job->removeData('vars');
|
||||
if(!empty($values['var-value'])) {
|
||||
foreach($values['var-value'] as $key => $value) {
|
||||
if(!empty($value) || $value == 0) {
|
||||
|
@ -687,53 +664,6 @@ class JobRepository extends EntityRepository
|
|||
return $job;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param bool $withSecrets
|
||||
* @return Job|mixed|object|null
|
||||
*/
|
||||
public function parseJob(Job &$job, bool $withSecrets = false): void
|
||||
{
|
||||
if(!empty($job->getData('vars'))) {
|
||||
foreach ($job->getData('vars') as $key => &$value) {
|
||||
if ($value['issecret']) {
|
||||
$job->setData('vars.' . $key . '.value', ($withSecrets) ? Secret::decrypt(base64_decode($value['value'])) : '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch($job->getData('crontype')) {
|
||||
case 'http':
|
||||
if($job->hasData('vars.basicauth-password.value')) {
|
||||
$job->setData('basicauth-password', $job->getData('vars.basicauth-password.value'));
|
||||
$job->removeData('vars.basicauth-password');
|
||||
}
|
||||
break;
|
||||
case 'reboot':
|
||||
$job->setData('reboot-delay', $job->getData('vars.reboot-delay.value'));
|
||||
$job->setData('reboot-delay-secs', $job->getData('vars.reboot-delay-secs.value'));
|
||||
|
||||
$job->removeData('vars.reboot-delay');
|
||||
$job->removeData('vars.reboot-delay-secs');
|
||||
break;
|
||||
}
|
||||
|
||||
switch($job->getData('hosttype')) {
|
||||
case 'ssh':
|
||||
if($job->hasData('vars.ssh-privkey.value')) {
|
||||
$job->setData('ssh-privkey', $job->getData('vars.ssh-privkey.value'));
|
||||
$job->removeData('vars.ssh-privkey');
|
||||
}
|
||||
if($job->hasData('vars.privkey-password.value')) {
|
||||
$job->setData('privkey-password', $job->getData('vars.privkey-password.value'));
|
||||
$job->removeData('vars.privkey-password');
|
||||
}
|
||||
break;
|
||||
}
|
||||
if($job->getData('crontype') == 'http') {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return array
|
||||
|
|
|
@ -90,7 +90,6 @@ class RunRepository extends EntityRepository
|
|||
} else {
|
||||
foreach($jobids as $jobid) {
|
||||
$job = $jobRepo->find($jobid);
|
||||
$jobRepo->parseJob($job);
|
||||
$allJobs[] = $job;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
namespace App\Twig;
|
||||
|
||||
use App\Service\Secret;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFilter;
|
||||
|
||||
|
@ -11,6 +12,7 @@ class AppExtension extends AbstractExtension
|
|||
return [
|
||||
new TwigFilter('interval', [$this, 'parseInterval']),
|
||||
new TwigFilter('parsetags', [$this, 'parseTags']),
|
||||
new TwigFilter('decryptsecret', [$this, 'decryptSecret']),
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -67,4 +69,9 @@ class AppExtension extends AbstractExtension
|
|||
return 'dark';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function decryptSecret(string $text) {
|
||||
return Secret::decrypt(base64_decode($text));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,10 +137,7 @@
|
|||
<h4>Reboot job details</h4>
|
||||
<div class="mb-3">
|
||||
<label for="reboot-command">Reboot command</label>
|
||||
<input type="text" name="reboot-command" class="form-control" id="reboot-command" placeholder="systemctl reboot" value="
|
||||
{% if attribute(job.data, 'reboot-command') is defined %}
|
||||
{{ attribute(job.data, 'reboot-command') }}
|
||||
{% endif %}">
|
||||
<input type="text" name="reboot-command" class="form-control" id="reboot-command" placeholder="systemctl reboot" value="{% if attribute(job.data, 'reboot-command') is defined %}{{ attribute(job.data, 'reboot-command') }}{% endif %}">
|
||||
<small id="reboot-command-help" class="form-text text-muted">Use {reboot-delay} or {reboot-delay-secs} to add the delay in your command</small>
|
||||
</div>
|
||||
|
||||
|
@ -179,7 +176,7 @@
|
|||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="basicauth-password">Password for Basic-Auth</label>
|
||||
<input type="password" name="basicauth-password" class="form-control" placeholder="correct horse battery staple" value="{% if attribute(job.data, 'basicauth-password') is defined %}{{ attribute(job.data, 'basicauth-password') }}{% endif %}">
|
||||
<input type="password" name="basicauth-password" class="form-control" placeholder="correct horse battery staple" value="{% if attribute(job.data, 'basicauth-password') is defined %}{{ attribute(job.data, 'basicauth-password') | decryptsecret }}{% endif %}">
|
||||
<small id="basicauth-password-help" class="form-text text-muted">This field is being saved as a secret</small>
|
||||
</div>
|
||||
|
||||
|
@ -210,9 +207,9 @@
|
|||
<label for="privkey">Private key</label>
|
||||
<div class="input-group">
|
||||
<span class=" input-group-text border-end-0">
|
||||
<input type="checkbox" name="privkey-keep" class="privkey-keep" value="true" data-privkey="{% if attribute(job.data, 'ssh-privkey') is defined %}{{ attribute(job.data, 'ssh-privkey') }}{% endif %}" checked>
|
||||
<input type="checkbox" name="privkey-keep" class="privkey-keep" value="true" data-privkey="{% if attribute(job.data, 'ssh-privkey') is defined %}{{ attribute(job.data, 'ssh-privkey') | decryptsecret }}{% endif %}" checked>
|
||||
</span>
|
||||
<input type="hidden" name="privkey-orig" class="privkey-orig" value="{% if attribute(job.data, 'ssh-privkey') is defined %}{{ attribute(job.data, 'ssh-privkey') }}{% endif %}">
|
||||
<input type="hidden" name="privkey-orig" class="privkey-orig" value="{% if attribute(job.data, 'ssh-privkey') is defined %}{{ attribute(job.data, 'ssh-privkey') | decryptsecret }}{% endif %}">
|
||||
<span class="input-group-text border-start-0">Keep</span>
|
||||
<input type="file" id="privkey" name="privkey" class="form-control " disabled>
|
||||
</div>
|
||||
|
@ -221,7 +218,7 @@
|
|||
|
||||
<div class="mb-3">
|
||||
<label for="privkey-password">Password for private key</label>
|
||||
<input type="password" name="privkey-password" class="form-control" placeholder="correct horse battery staple" value="{% if attribute(job.data, 'privkey-password') is defined %}{{ attribute(job.data, 'privkey-password') }}{% endif %}">
|
||||
<input type="password" name="privkey-password" class="form-control" placeholder="correct horse battery staple" value="{% if attribute(job.data, 'privkey-password') is defined %}{{ attribute(job.data, 'privkey-password') | decryptsecret }}{% endif %}">
|
||||
<small id="privkey-password-help" class="form-text text-muted">If private key is empty this field is being used as ssh-password</small>
|
||||
<small id="privkey-password-help-2" class="form-text text-muted">This field is being saved as a secret</small>
|
||||
</div>
|
||||
|
@ -263,7 +260,7 @@
|
|||
</div>
|
||||
<span class="input-group-text border-start-0">Secret</span>
|
||||
<input type="text" name="var-id[{{ key }}]" class="form-control var-id" placeholder="name" value="{{ id }}">
|
||||
<input type="{% if var.issecret %}password{% else %}text{% endif %}" name="var-value[{{ key }}]" class="form-control var-value" placeholder="value" value="{{ var.value }}">
|
||||
<input type="{% if var.issecret %}password{% else %}text{% endif %}" name="var-value[{{ key }}]" class="form-control var-value" placeholder="value" value="{% if var.issecret %}{{ var.value | decryptsecret }}{% else %}{{ var.value }}{% endif %}">
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
|
Loading…
Reference in New Issue