Coming stomping through with some updates.

This commit is contained in:
Greyscale 2022-06-15 11:22:24 +02:00
parent 5f72da1959
commit 85c242ba63
No known key found for this signature in database
GPG key ID: 74BAFF55434DA4B2
5 changed files with 146 additions and 89 deletions

View file

@ -1,4 +1,4 @@
FROM benzine/php:cli-8.0 as bouncer
FROM benzine/php:cli-8.1 as bouncer
LABEL maintainer="Matthew Baggett <matthew@baggett.me>" \
org.label-schema.vcs-url="https://github.com/benzine-framework/docker" \
org.opencontainers.image.source="https://github.com/benzine-framework/docker"

View file

@ -1,3 +1,14 @@
all: fix build-n-push
php-cs-fixer:
docker run --rm -v $(PWD):/data cytopia/php-cs-fixer fix --config=.php-cs-fixer.php bouncer
fix: php-cs-fixer
build-n-push:
docker build --tag benzine/bouncer --tag ghcr.io/benzine-framework/bouncer --target bouncer .
#docker push benzine/bouncer
docker push ghcr.io/benzine-framework/bouncer
test-as-service: clean
docker build -t bouncer --target bouncer .
docker build -t test-app-a --target test-app-a .

View file

@ -19,20 +19,26 @@ server {
# ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# ssl_ciphers HIGH:!aNULL:!MD5;
{% if allowLargeEntities %}
client_max_body_size {{ maxEntitySizeMegabytes }}M;
{% endif %}
location / {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Host $host;
{% if allowWebsocketSupport %}
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
{% endif %}
proxy_pass {{ targetPath }};
}
}
%}
{% if not allowNonSSL %}
server {
listen 80;

View file

@ -26,8 +26,10 @@ class BouncerTarget
private bool $letsEncrypt = false;
private string $targetPath;
private bool $allowNonSSL = true;
private bool $useTemporaryCert = true;
private bool $allowWebsocketSupport = true;
private bool $allowLargeEntities = false;
private int $maxEntitySizeMegabytes = 256;
public function __toArray()
{
@ -39,6 +41,9 @@ class BouncerTarget
'targetPath' => $this->getTargetPath(),
'useTemporaryCert' => $this->isUseTemporaryCert(),
'allowNonSSL' => $this->isAllowNonSSL(),
'allowWebsocketSupport' => $this->isAllowWebsocketSupport(),
'allowLargeEntities' => $this->isAllowLargeEntities(),
'maxEntitySizeMegabytes' => $this->getMaxEntitySizeMegabytes(),
];
}
@ -54,6 +59,42 @@ class BouncerTarget
return $this;
}
public function isAllowWebsocketSupport(): bool
{
return $this->allowWebsocketSupport;
}
public function setAllowWebsocketSupport(bool $allowWebsocketSupport): BouncerTarget
{
$this->allowWebsocketSupport = $allowWebsocketSupport;
return $this;
}
public function isAllowLargeEntities(): bool
{
return $this->allowLargeEntities;
}
public function setAllowLargeEntities(bool $allowLargeEntities): BouncerTarget
{
$this->allowLargeEntities = $allowLargeEntities;
return $this;
}
public function getMaxEntitySizeMegabytes(): int
{
return $this->maxEntitySizeMegabytes;
}
public function setMaxEntitySizeMegabytes(int $maxEntitySizeMegabytes): BouncerTarget
{
$this->maxEntitySizeMegabytes = $maxEntitySizeMegabytes;
return $this;
}
public function getId(): string
{
return $this->id;
@ -164,24 +205,6 @@ class Bouncer
private array $fileHashes;
private bool $swarmMode = false;
/**
* @return bool
*/
public function isSwarmMode(): bool
{
return $this->swarmMode;
}
/**
* @param bool $swarmMode
* @return Bouncer
*/
public function setSwarmMode(bool $swarmMode): Bouncer
{
$this->swarmMode = $swarmMode;
return $this;
}
public function __construct()
{
$this->environment = array_merge($_ENV, $_SERVER);
@ -234,6 +257,18 @@ class Bouncer
}
}
public function isSwarmMode(): bool
{
return $this->swarmMode;
}
public function setSwarmMode(bool $swarmMode): Bouncer
{
$this->swarmMode = $swarmMode;
return $this;
}
/**
* @throws \GuzzleHttp\Exception\GuzzleException
*
@ -271,49 +306,51 @@ class Bouncer
$networks = array_values($inspect['NetworkSettings']['Networks']);
$bouncerTarget->setIp($networks[0]['IPAddress']);
}
//$this->logger->debug(sprintf('Decided that %s has the ip %s', $bouncerTarget->getName(), $bouncerTarget->getIp()));
// $this->logger->debug(sprintf('Decided that %s has the ip %s', $bouncerTarget->getName(), $bouncerTarget->getIp()));
$bouncerTarget->setTargetPath(sprintf('http://%s:%d/', $bouncerTarget->getIp(), $bouncerTarget->getPort()));
$bouncerTargets[] = $bouncerTarget;
}
}
return $bouncerTargets;
}
public function findContainersSwarmMode(): array
{
$bouncerTargets = [];
$services = json_decode($this->client->request('GET', 'services')->getBody()->getContents(), true);
if(isset($services['message'])){
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']));
}else{
foreach($services as $service){
} else {
foreach ($services as $service) {
$envs = [];
if(
if (
!isset($service['Spec'])
|| !isset($service['Spec']['TaskTemplate'])
|| !isset($service['Spec']['TaskTemplate']['ContainerSpec'])
|| !isset($service['Spec']['TaskTemplate']['ContainerSpec']['Env'])
)
{
) {
continue;
}
foreach($service['Spec']['TaskTemplate']['ContainerSpec']['Env'] as $env){
list($eKey, $eVal) = explode("=", $env,2);
foreach ($service['Spec']['TaskTemplate']['ContainerSpec']['Env'] as $env) {
[$eKey, $eVal] = explode('=', $env, 2);
$envs[$eKey] = $eVal;
}
if(isset($envs['BOUNCER_DOMAIN'])) {
if (isset($envs['BOUNCER_DOMAIN'])) {
$bouncerTarget = (new BouncerTarget())
->setId($service['ID']);
->setId($service['ID'])
;
$bouncerTarget = $this->parseContainerEnvironmentVariables($envs, $bouncerTarget);
$bouncerTarget->setIp("172.17.0.1");
$bouncerTarget->setIp('172.17.0.1');
$bouncerTarget->setPort($service['Endpoint']['Ports'][0]['PublishedPort']);
$bouncerTarget->setTargetPath(sprintf('http://%s:%d/', $bouncerTarget->getIp(), $bouncerTarget->getPort()));
//$this->logger->debug(sprintf('Decided that %s has the target path %s', $bouncerTarget->getName(), $bouncerTarget->getTargetPath()));
// $this->logger->debug(sprintf('Decided that %s has the target path %s', $bouncerTarget->getName(), $bouncerTarget->getTargetPath()));
$bouncerTargets[] = $bouncerTarget;
}
@ -326,10 +363,12 @@ class Bouncer
public function run(): void
{
$this->logger->info(sprintf('%s Starting Bouncer...', Emoji::CHARACTER_TIMER_CLOCK));
try {
$this->stateHasChanged();
}catch(\GuzzleHttp\Exception\ConnectException $connectException){
$this->logger->critical(sprintf("%s Could not connect to docker socket! Did you map it?", Emoji::CHARACTER_CRYING_CAT));
} catch (\GuzzleHttp\Exception\ConnectException $connectException) {
$this->logger->critical(sprintf('%s Could not connect to docker socket! Did you map it?', Emoji::CHARACTER_CRYING_CAT));
exit;
}
while (true) {
@ -337,6 +376,44 @@ class Bouncer
}
}
public function parseContainerEnvironmentVariables(array $envs, BouncerTarget $bouncerTarget): BouncerTarget
{
foreach ($envs as $eKey => $eVal) {
switch ($eKey) {
case 'BOUNCER_DOMAIN':
$domains = explode(',', $eVal);
array_walk($domains, function (&$domain, $key): void {
$domain = trim($domain);
});
$bouncerTarget->setDomains($domains);
break;
case 'BOUNCER_LETSENCRYPT':
$bouncerTarget->setLetsEncrypt(in_array(strtolower($eVal), ['yes', 'true'], true));
break;
case 'BOUNCER_TARGET_PORT':
$bouncerTarget->setPort($eVal);
break;
case 'BOUNCER_ALLOW_NON_SSL':
$bouncerTarget->setAllowNonSSL(in_array(strtolower($eVal), ['yes', 'true'], true));
break;
case 'BOUNCER_ALLOW_WEBSOCKETS':
$bouncerTarget->setAllowWebsocketSupport(in_array(strtolower($eVal), ['yes', 'true'], true));
break;
}
}
return $bouncerTarget;
}
/**
* Returns true when something has changed.
*
@ -350,7 +427,7 @@ class Bouncer
$containers = json_decode($this->client->request('GET', 'containers/json')->getBody()->getContents(), true);
foreach ($containers as $container) {
$inspect = json_decode($this->client->request('GET', "containers/{$container['Id']}/json")->getBody()->getContents(), true);
$newInstanceStates['container-' . $inspect['Id']] = implode('::', [
$newInstanceStates['container-'.$inspect['Id']] = implode('::', [
$inspect['Name'],
$inspect['Created'],
$inspect['Image'],
@ -361,17 +438,17 @@ class Bouncer
// Swarm Services
$services = json_decode($this->client->request('GET', 'services')->getBody()->getContents(), true);
if(isset($services['message'])){
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']));
}else{
foreach($services as $service){
$newInstanceStates['service-' . $service['ID']] = implode("::", [
$service['Version']['Index']
} else {
foreach ($services as $service) {
$newInstanceStates['service-'.$service['ID']] = implode('::', [
$service['Version']['Index'],
]);
}
}
$newStateHash = sha1(implode("\n", $newInstanceStates));
//$this->logger->debug(sprintf("Old state = %s. New State = %s.", substr($this->instanceStateHash,0,7), substr($newStateHash, 0,7)));
// $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;
@ -388,7 +465,7 @@ class Bouncer
}
$determineSwarmMode = json_decode($this->client->request('GET', 'swarm')->getBody()->getContents(), true);
$this->setSwarmMode(!isset($determineSwarmMode['message']));
$this->logger->info(sprintf("%s Swarm mode is %s.", Emoji::CHARACTER_HONEYBEE, $this->isSwarmMode() ? 'enabled' : 'disabled'));
$this->logger->info(sprintf('%s Swarm mode is %s.', Emoji::CHARACTER_HONEYBEE, $this->isSwarmMode() ? 'enabled' : 'disabled'));
$targets = $this->isSwarmMode() ? $this->findContainersSwarmMode() : $this->findContainersContainerMode();
$this->logger->info(sprintf('%s Found %d services with BOUNCER_DOMAIN set', Emoji::CHARACTER_MAGNIFYING_GLASS_TILTED_LEFT, count($targets)));
@ -422,7 +499,7 @@ class Bouncer
/** @var FileAttributes $file */
if ($file->isFile()) {
$localPath = "archive/{$file->path()}";
#$this->logger->debug(sprintf(" > Downloading {$file->path()} "));
// $this->logger->debug(sprintf(" > Downloading {$file->path()} "));
$this->certificateStoreLocal->writeStream($localPath, $this->certificateStoreRemote->readStream($file->path()));
$this->fileHashes[$localPath] = sha1($this->certificateStoreLocal->read($localPath));
}
@ -434,8 +511,8 @@ class Bouncer
if ($newLocalCert->isFile() && pathinfo($newLocalCert->path(), PATHINFO_EXTENSION) == 'pem') {
$livePath = str_replace('archive/', 'live/', $newLocalCert->path());
// Stupid dirty hack bullshit reee
for($i = 1; $i <= 9; $i++){
$livePath = str_replace("$i.pem", ".pem", $livePath);
for ($i = 1; $i <= 9; ++$i) {
$livePath = str_replace("{$i}.pem", '.pem', $livePath);
}
$this->certificateStoreLocal->writeStream($livePath, $this->certificateStoreLocal->readStream($newLocalCert->path()));
}
@ -462,11 +539,10 @@ class Bouncer
if ($file->isFile()) {
$remotePath = str_replace('archive/', '', $file->path());
if (!$this->certificateStoreRemote->fileExists($remotePath) || $this->fileChanged($file->path())) {
#$this->logger->debug(sprintf(" > Uploading {$file->path()} "));
// $this->logger->debug(sprintf(" > Uploading {$file->path()} "));
$this->certificateStoreRemote->writeStream($remotePath, $this->certificateStoreLocal->readStream($file->path()));
} else {
#$this->logger->debug(sprintf(" > Skipping uploading {$file->path()}, file not changed."));
}
// $this->logger->debug(sprintf(" > Skipping uploading {$file->path()}, file not changed."));
}
}
}
@ -508,6 +584,7 @@ class Bouncer
$target->setUseTemporaryCert(false);
$this->generateNginxConfig($target);
continue;
}
}
@ -549,44 +626,6 @@ class Bouncer
$this->logger->info(sprintf('%s Restarting nginx', Emoji::CHARACTER_TIMER_CLOCK));
$shell->run($command);
}
/**
* @param array $envs
* @param BouncerTarget $bouncerTarget
* @return BouncerTarget
*/
public function parseContainerEnvironmentVariables(array $envs, BouncerTarget $bouncerTarget): BouncerTarget
{
foreach ($envs as $eKey => $eVal) {
switch ($eKey) {
case 'BOUNCER_DOMAIN':
$domains = explode(',', $eVal);
array_walk($domains, function (&$domain, $key): void {
$domain = trim($domain);
});
$bouncerTarget->setDomains($domains);
break;
case 'BOUNCER_LETSENCRYPT':
$bouncerTarget->setLetsEncrypt(in_array(strtolower($eVal), ['yes', 'true'], true));
break;
case 'BOUNCER_TARGET_PORT':
$bouncerTarget->setPort($eVal);
break;
case 'BOUNCER_ALLOW_NON_SSL':
$bouncerTarget->setAllowNonSSL(in_array(strtolower($eVal), ['yes', 'true'], true));
break;
}
}
return $bouncerTarget;
}
}
(new Bouncer())->run();

View file

@ -18,7 +18,8 @@
"bramus/monolog-colored-line-formatter": "~3.0",
"adambrett/shell-wrapper": "dev-master",
"league/flysystem-aws-s3-v3": "^2.1",
"spatie/emoji": "^2.3"
"spatie/emoji": "^2.3",
"ext-openssl": "*"
},
"authors": [
{