Debuggin'.
This commit is contained in:
parent
6e709df58b
commit
e7594723bc
1 changed files with 112 additions and 54 deletions
166
bouncer/bouncer
166
bouncer/bouncer
|
|
@ -61,7 +61,7 @@ class BouncerTarget
|
|||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
* @return null|string
|
||||
*/
|
||||
public function getUsername(): ?string
|
||||
{
|
||||
|
|
@ -70,16 +70,18 @@ class BouncerTarget
|
|||
|
||||
/**
|
||||
* @param string
|
||||
*
|
||||
* @return BouncerTarget
|
||||
*/
|
||||
public function setUsername(string $username): BouncerTarget
|
||||
{
|
||||
$this->username = $username;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
* @return null|string
|
||||
*/
|
||||
public function getPassword(): ?string
|
||||
{
|
||||
|
|
@ -88,11 +90,13 @@ class BouncerTarget
|
|||
|
||||
/**
|
||||
* @param string $password
|
||||
*
|
||||
* @return BouncerTarget
|
||||
*/
|
||||
public function setPassword(string $password): BouncerTarget
|
||||
{
|
||||
$this->password = $password;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
|
@ -101,24 +105,25 @@ class BouncerTarget
|
|||
return $this->setUsername($username)->setPassword($password);
|
||||
}
|
||||
|
||||
public function hasAuth() : bool
|
||||
public function hasAuth(): bool
|
||||
{
|
||||
return $this->username != null && $this->password != null;
|
||||
}
|
||||
|
||||
public function getFileName() : string
|
||||
public function getFileName(): string
|
||||
{
|
||||
return "{$this->getName()}.conf";
|
||||
}
|
||||
|
||||
public function getAuthFileName() : string
|
||||
public function getAuthFileName(): string
|
||||
{
|
||||
return "{$this->getName()}.secret";
|
||||
}
|
||||
|
||||
public function getAuthFileData() : string
|
||||
public function getAuthFileData(): string
|
||||
{
|
||||
$output = shell_exec(sprintf("htpasswd -nibB -C10 %s %s",$this->getUsername(), $this->getPassword()));
|
||||
$output = shell_exec(sprintf('htpasswd -nibB -C10 %s %s', $this->getUsername(), $this->getPassword()));
|
||||
|
||||
return trim($output)."\n";
|
||||
}
|
||||
|
||||
|
|
@ -156,7 +161,7 @@ class BouncerTarget
|
|||
$this->useGlobalCert = $useGlobalCert;
|
||||
|
||||
// Global cert overrides temporary certs.
|
||||
if($useGlobalCert) {
|
||||
if ($useGlobalCert) {
|
||||
$this->setUseTemporaryCert(false);
|
||||
}
|
||||
|
||||
|
|
@ -321,7 +326,10 @@ class Bouncer
|
|||
private ?Filesystem $certificateStoreRemote = null;
|
||||
private Filesystem $providedCertificateStore;
|
||||
private Logger $logger;
|
||||
private string $instanceStateHash = '';
|
||||
private string $instanceContainerStateHash = '';
|
||||
private array $previousContainerState = [];
|
||||
private string $instanceSwarmStateHash = '';
|
||||
private array $previousSwarmState = [];
|
||||
private array $fileHashes;
|
||||
private bool $swarmMode = false;
|
||||
private bool $useGlobalCert = false;
|
||||
|
|
@ -461,9 +469,9 @@ class Bouncer
|
|||
}
|
||||
|
||||
/**
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*
|
||||
* @return BouncerTarget[]
|
||||
*
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*/
|
||||
public function findContainersContainerMode(): array
|
||||
{
|
||||
|
|
@ -549,7 +557,7 @@ class Bouncer
|
|||
|
||||
if ($bouncerTarget->isPortSet()) {
|
||||
$bouncerTarget->setEndpointHostnameOrIp($service['Spec']['Name']);
|
||||
// $this->logger->info(sprintf('Ports for %s has been explicitly set to %s:%d.', $bouncerTarget->getName(), $bouncerTarget->getEndpointHostnameOrIp(), $bouncerTarget->getPort()));
|
||||
// $this->logger->info(sprintf('Ports for %s has been explicitly set to %s:%d.', $bouncerTarget->getName(), $bouncerTarget->getEndpointHostnameOrIp(), $bouncerTarget->getPort()));
|
||||
} elseif (isset($service['Endpoint']['Ports'])) {
|
||||
$bouncerTarget->setEndpointHostnameOrIp('172.17.0.1');
|
||||
$bouncerTarget->setPort($service['Endpoint']['Ports'][0]['PublishedPort']);
|
||||
|
|
@ -566,12 +574,12 @@ class Bouncer
|
|||
|
||||
if ($bouncerTarget->isEndpointValid()) {
|
||||
$bouncerTargets[] = $bouncerTarget;
|
||||
}else{
|
||||
} else {
|
||||
$this->logger->debug(sprintf(
|
||||
'%s Decided that %s has the endpoint %s and it is not valid.',
|
||||
Emoji::magnifyingGlassTiltedLeft(),
|
||||
$bouncerTarget->getName(),
|
||||
$bouncerTarget->getEndpointHostnameOrIp(),
|
||||
'%s Decided that %s has the endpoint %s and it is not valid.',
|
||||
Emoji::magnifyingGlassTiltedLeft(),
|
||||
$bouncerTarget->getName(),
|
||||
$bouncerTarget->getEndpointHostnameOrIp(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -611,7 +619,7 @@ class Bouncer
|
|||
break;
|
||||
|
||||
case 'BOUNCER_AUTH':
|
||||
list($username, $password) = explode(":", $eVal);
|
||||
[$username, $password] = explode(':', $eVal);
|
||||
$bouncerTarget->setAuth($username, $password);
|
||||
|
||||
break;
|
||||
|
|
@ -651,6 +659,16 @@ class Bouncer
|
|||
return $bouncerTarget;
|
||||
}
|
||||
|
||||
private function dockerGetContainers(): array
|
||||
{
|
||||
return json_decode($this->client->request('GET', 'containers/json')->getBody()->getContents(), true);
|
||||
}
|
||||
|
||||
private function dockerGetContainer(string $id): array
|
||||
{
|
||||
return json_decode($this->client->request('GET', "containers/{$id}/json")->getBody()->getContents(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true when something has changed.
|
||||
*
|
||||
|
|
@ -659,42 +677,79 @@ class Bouncer
|
|||
private function stateHasChanged(): bool
|
||||
{
|
||||
if ($this->lastUpdateEpoch === null || $this->lastUpdateEpoch <= time() - $this->forcedUpdateIntervalSeconds) {
|
||||
$this->logger->debug(sprintf('%s Forced update interval has been reached, forcing update.', Emoji::watch()));
|
||||
|
||||
return true;
|
||||
}
|
||||
$newInstanceStates = [];
|
||||
|
||||
// Standard Containers
|
||||
$containers = json_decode($this->client->request('GET', 'containers/json')->getBody()->getContents(), true);
|
||||
$newContainerState = [];
|
||||
$containers = $this->dockerGetContainers();
|
||||
foreach ($containers as $container) {
|
||||
$inspect = json_decode($this->client->request('GET', "containers/{$container['Id']}/json")->getBody()->getContents(), true);
|
||||
$newInstanceStates['container-'.$inspect['Id']] = implode('::', [
|
||||
$inspect = $this->dockerGetContainer($container['Id']);
|
||||
$newContainerState[$inspect['Id']] = [
|
||||
$inspect['Name'],
|
||||
$inspect['Created'],
|
||||
$inspect['Image'],
|
||||
$inspect['State']['Status'],
|
||||
sha1(implode('|', $inspect['Config']['Env'])),
|
||||
]);
|
||||
$inspect['Config']['Env'],
|
||||
];
|
||||
ksort($newContainerState[$inspect['Id']]);
|
||||
}
|
||||
ksort($newContainerState);
|
||||
|
||||
// Calculate Container State Hash
|
||||
$newContainerStateHash = sha1(json_encode($newContainerState));
|
||||
if ($this->instanceContainerStateHash != $newContainerStateHash) {
|
||||
\Kint::dump(array_diff(
|
||||
message: 'Swarm State has changed',
|
||||
previous: $this->previousContainerState,
|
||||
new: $newContainerState,
|
||||
diff: array_diff($this->previousContainerState, $newContainerState),
|
||||
previousHash: $this->instanceContainerStateHash,
|
||||
newHash: $newContainerStateHash,
|
||||
));
|
||||
$this->instanceContainerStateHash = $newContainerStateHash;
|
||||
$this->logger->debug(sprintf('%s Container state has changed', Emoji::warning()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Swarm Services
|
||||
$newSwarmState = [];
|
||||
if ($this->isSwarmMode()) {
|
||||
$services = json_decode($this->client->request('GET', 'services')->getBody()->getContents(), true);
|
||||
if (isset($services['message'])) {
|
||||
$this->logger->debug(sprintf('Something happened while interrogating services.. This node is not a swarm node, cannot have services: %s', $services['message']));
|
||||
$this->logger->warning(sprintf('Something happened while interrogating services.. This node is not a swarm node, cannot have services: %s', $services['message']));
|
||||
} else {
|
||||
foreach ($services as $service) {
|
||||
$newInstanceStates['service-'.$service['ID']] = implode('::', [
|
||||
$newSwarmState[$service['ID']] = [
|
||||
$service['Version']['Index'],
|
||||
]);
|
||||
];
|
||||
ksort($newSwarmState[$service['ID']]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$newStateHash = sha1(implode("\n", $newInstanceStates));
|
||||
// $this->logger->debug(sprintf("Old state = %s. New State = %s.", substr($this->instanceStateHash,0,7), substr($newStateHash, 0,7)));
|
||||
if ($this->instanceStateHash != $newStateHash) {
|
||||
$this->instanceStateHash = $newStateHash;
|
||||
ksort($newSwarmState);
|
||||
|
||||
return true;
|
||||
// Calculate Swarm State Hash, if applicable
|
||||
if ($this->isSwarmMode()) {
|
||||
$newSwarmStateHash = sha1(json_encode($newSwarmState));
|
||||
if ($this->instanceSwarmStateHash != $newSwarmStateHash) {
|
||||
\Kint::dump(array_diff(
|
||||
message: 'Swarm State has changed',
|
||||
previous: $this->previousSwarmState,
|
||||
new: $newSwarmState,
|
||||
diff: array_diff($this->previousSwarmState, $newSwarmState),
|
||||
previousHash: $this->instanceSwarmStateHash,
|
||||
newHash: $newSwarmStateHash,
|
||||
));
|
||||
$this->instanceSwarmStateHash = $newSwarmStateHash;
|
||||
$this->previousSwarmState = $newSwarmState;
|
||||
$this->logger->debug(sprintf('%s Swarm state has changed', Emoji::warning()));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -759,16 +814,17 @@ class Bouncer
|
|||
/** @var FileAttributes $file */
|
||||
if ($file->isFile()) {
|
||||
$localPath = "archive/{$file->path()}";
|
||||
if($file->fileSize() == 0){
|
||||
$this->logger->warning(sprintf(" > Downloading %s to %s was skipped, because it was empty", $file->path(), $localPath));
|
||||
if ($file->fileSize() == 0) {
|
||||
$this->logger->warning(sprintf(' > Downloading %s to %s was skipped, because it was empty', $file->path(), $localPath));
|
||||
|
||||
continue;
|
||||
}
|
||||
$this->logger->debug(sprintf(" > Downloading %s to %s (%d bytes)", $file->path(), $localPath, $file->fileSize()));
|
||||
$this->logger->debug(sprintf(' > Downloading %s to %s (%d bytes)', $file->path(), $localPath, $file->fileSize()));
|
||||
$this->certificateStoreLocal->writeStream($localPath, $this->certificateStoreRemote->readStream($file->path()));
|
||||
if($this->certificateStoreLocal->fileSize($localPath) == $this->certificateStoreRemote->fileSize($file->path())){
|
||||
$this->logger->debug(sprintf(" > Filesize for %s matches %s on remote (%d bytes)", $localPath, $file->path(), $this->certificateStoreLocal->fileSize($localPath)));
|
||||
}else{
|
||||
$this->logger->critical(sprintf(" > Filesize for %s DOES NOT MATCH %s on remote (%d != %d bytes)", $localPath, $file->path(), $this->certificateStoreLocal->fileSize($localPath), $this->certificateStoreRemote->fileSize($file->path())));
|
||||
if ($this->certificateStoreLocal->fileSize($localPath) == $this->certificateStoreRemote->fileSize($file->path())) {
|
||||
$this->logger->debug(sprintf(' > Filesize for %s matches %s on remote (%d bytes)', $localPath, $file->path(), $this->certificateStoreLocal->fileSize($localPath)));
|
||||
} else {
|
||||
$this->logger->critical(sprintf(' > Filesize for %s DOES NOT MATCH %s on remote (%d != %d bytes)', $localPath, $file->path(), $this->certificateStoreLocal->fileSize($localPath), $this->certificateStoreRemote->fileSize($file->path())));
|
||||
}
|
||||
$this->fileHashes[$localPath] = sha1($this->certificateStoreLocal->read($localPath));
|
||||
}
|
||||
|
|
@ -783,7 +839,7 @@ class Bouncer
|
|||
for ($i = 1; $i <= 9; ++$i) {
|
||||
$livePath = str_replace("{$i}.pem", '.pem', $livePath);
|
||||
}
|
||||
$this->logger->debug(sprintf(" > Mirroring %s to %s (%d bytes)", $newLocalCert->path(), $livePath, $newLocalCert->fileSize()));
|
||||
$this->logger->debug(sprintf(' > Mirroring %s to %s (%d bytes)', $newLocalCert->path(), $livePath, $newLocalCert->fileSize()));
|
||||
$this->certificateStoreLocal->writeStream($livePath, $this->certificateStoreLocal->readStream($newLocalCert->path()));
|
||||
}
|
||||
}
|
||||
|
|
@ -808,10 +864,10 @@ class Bouncer
|
|||
/** @var FileAttributes $file */
|
||||
if ($file->isFile()) {
|
||||
$remotePath = str_replace('archive/', '', $file->path());
|
||||
if ($file->fileSize() == 0){
|
||||
if ($file->fileSize() == 0) {
|
||||
$this->logger->warning(sprintf(" > Skipping uploading {$file->path()}, file is garbage (empty)."));
|
||||
} elseif (!$this->certificateStoreRemote->fileExists($remotePath) || $this->fileChanged($file->path())) {
|
||||
$this->logger->debug(sprintf(" > Uploading %s (%d bytes)", $file->path(), $file->fileSize()));
|
||||
$this->logger->debug(sprintf(' > Uploading %s (%d bytes)', $file->path(), $file->fileSize()));
|
||||
$this->certificateStoreRemote->write($remotePath, $this->certificateStoreLocal->read($file->path()));
|
||||
} else {
|
||||
$this->logger->debug(sprintf(" > Skipping uploading {$file->path()}, file not changed."));
|
||||
|
|
@ -845,7 +901,7 @@ class Bouncer
|
|||
{
|
||||
$configData = $this->twig->render('NginxTemplate.twig', $target->__toArray());
|
||||
$this->configFilesystem->write($target->getFileName(), $configData);
|
||||
if($target->hasAuth()){
|
||||
if ($target->hasAuth()) {
|
||||
$this->configFilesystem->write($target->getAuthFileName(), $target->getAuthFileData());
|
||||
}
|
||||
|
||||
|
|
@ -867,11 +923,11 @@ class Bouncer
|
|||
$testAgeFile = "/archive/{$target->getName()}/fullchain1.pem";
|
||||
if ($this->certificateStoreLocal->fileExists($testAgeFile)) {
|
||||
$dubious = false;
|
||||
if($this->certificateStoreLocal->fileSize($testAgeFile) == 0){
|
||||
if ($this->certificateStoreLocal->fileSize($testAgeFile) == 0) {
|
||||
// File is empty, check its age instead.
|
||||
$timeRemainingSeconds = $this->certificateStoreLocal->lastModified($testAgeFile) - time();
|
||||
$dubious = true;
|
||||
}else{
|
||||
} else {
|
||||
$ssl = openssl_x509_parse($this->certificateStoreLocal->read($testAgeFile));
|
||||
$timeRemainingSeconds = $ssl['validTo_time_t'] - time();
|
||||
}
|
||||
|
|
@ -880,7 +936,7 @@ class Bouncer
|
|||
'%s Skipping %s, certificate is %s for %d days',
|
||||
Emoji::CHARACTER_PARTYING_FACE,
|
||||
$target->getName(),
|
||||
$dubious ? "dubiously good" : "still good",
|
||||
$dubious ? 'dubiously good' : 'still good',
|
||||
round($timeRemainingSeconds / 86400)
|
||||
));
|
||||
|
||||
|
|
@ -895,10 +951,11 @@ class Bouncer
|
|||
$shell = new Exec();
|
||||
|
||||
// Disable nginx tweaks
|
||||
$this->logger->debug("Moving nginx tweak file..");
|
||||
$disableNginxTweaksCommand = (new CommandBuilder("mv"))
|
||||
->addSubCommand("/etc/nginx/conf.d/tweak.conf")
|
||||
->addSubCommand("/etc/nginx/conf.d/tweak.disabled");
|
||||
$this->logger->debug('Moving nginx tweak file..');
|
||||
$disableNginxTweaksCommand = (new CommandBuilder('mv'))
|
||||
->addSubCommand('/etc/nginx/conf.d/tweak.conf')
|
||||
->addSubCommand('/etc/nginx/conf.d/tweak.disabled')
|
||||
;
|
||||
$shell->run($disableNginxTweaksCommand);
|
||||
|
||||
// Generate letsencrypt cert
|
||||
|
|
@ -922,10 +979,11 @@ class Bouncer
|
|||
}
|
||||
|
||||
// Re-enable nginx tweaks
|
||||
$this->logger->debug("Moving nginx tweak file back..");
|
||||
$disableNginxTweaksCommand = (new CommandBuilder("mv"))
|
||||
->addSubCommand("/etc/nginx/conf.d/tweak.disabled")
|
||||
->addSubCommand("/etc/nginx/conf.d/tweak.conf");
|
||||
$this->logger->debug('Moving nginx tweak file back..');
|
||||
$disableNginxTweaksCommand = (new CommandBuilder('mv'))
|
||||
->addSubCommand('/etc/nginx/conf.d/tweak.disabled')
|
||||
->addSubCommand('/etc/nginx/conf.d/tweak.conf')
|
||||
;
|
||||
$shell->run($disableNginxTweaksCommand);
|
||||
|
||||
$target->setUseTemporaryCert(false);
|
||||
|
|
|
|||
Loading…
Reference in a new issue