Forkification

This commit is contained in:
Greyscale 2024-05-16 18:38:19 +02:00
parent ee66c49987
commit 617a5087c8
204 changed files with 3 additions and 24020 deletions

View file

@ -1,115 +0,0 @@
name: Build Bouncer
permissions:
contents: read
packages: write
on:
push:
branches:
- main
paths:
- bouncer/**
- .github/workflows/bouncer.yml
workflow_call:
workflow_dispatch:
concurrency:
group: bouncer-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
bouncer-build:
name: Build Bouncer
runs-on: ubuntu-latest
steps:
- name: "Setup: Checkout Source"
uses: actions/checkout@v4
with:
sparse-checkout: |
bouncer
- name: "Setup: Get Date"
id: date
run: |
{
echo "datetime=$(date +'%Y-%m-%d %H:%M:%S')"
echo "date=$(date +'%Y-%m-%d')"
echo "time=$(date +'%H:%M:%S')"
echo "container_build_datetime=$(date -u +'%Y-%m-%dT%H:%M:%S.%3NZ')"
} >> "$GITHUB_OUTPUT"
- name: "Setup: PHP"
uses: shivammathur/setup-php@v2
with:
php-version: 8.2
env:
runner: self-hosted
- name: "Setup: Setup QEMU"
uses: docker/setup-qemu-action@v3
- name: "Setup: Expose GitHub Runtime"
uses: crazy-max/ghaction-github-runtime@v3
- name: "Setup: Setup Docker Buildx"
uses: docker/setup-buildx-action@v2
- name: "Setup: Login to Docker Hub"
uses: docker/login-action@v3
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: "Setup: Login to GHCR"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: matthewbaggett
password: ${{ secrets.GHCR_PASSWORD }}
- name: "Setup: Find Composer Cache Directory"
id: composer-cache
working-directory: bouncer
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: "Setup: Composer Cache"
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-bouncer-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-bouncer-composer-
- name: "Dependencies: Composer Install"
working-directory: bouncer
run: composer install --ignore-platform-reqs
- name: "Build: Build & Push Image"
uses: docker/build-push-action@v5
with:
context: bouncer
target: bouncer
platforms: ${{ !env.ACT && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
pull: true
push: true
build-args: |
GIT_SHA=${{ github.sha }}
GIT_BUILD_ID=${{ github.ref_name }}
BUILD_DATE=${{ steps.date.outputs.container_build_datetime }}
GIT_COMMIT_MESSAGE=${{ github.event.head_commit.message }}
tags: |
${{ !env.ACT && 'benzine/bouncer:latest' || '' }}
${{ !env.ACT && 'ghcr.io/benzine-framework/bouncer:latest' || 'ghcr.io/benzine-framework/bouncer:devel' }}
cache-from: ${{ !env.ACT && 'type=gha' || '' }}
cache-to: ${{ !env.ACT && 'type=gha,mode=max' || '' }}
build-contexts: |
php:cli=docker-image://ghcr.io/benzine-framework/php:cli-8.2
- name: "Post-Build: Validate build"
shell: bash
run: |
docker \
run \
--rm \
ghcr.io/benzine-framework/bouncer:latest \
/usr/bin/install-report

View file

@ -1,163 +0,0 @@
name: Build
on:
schedule:
- cron: "30 5 * * 2" # Build on tuesday morning at 5:30am
workflow_dispatch:
concurrency:
group: build-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
permissions: read-all
jobs:
qc-before:
name: QC Trunk
uses: ./.github/workflows/trunk.check.yml
secrets: inherit
permissions: write-all
marshall:
needs: qc-before
name: Marshall
uses: ./.github/workflows/marshall.yml
secrets: inherit
permissions:
contents: read
packages: write
php:
needs: marshall
name: PHP
uses: ./.github/workflows/php.yml
secrets: inherit
permissions:
contents: read
packages: write
#laravel:
# needs: php
# name: PHP Vanity Tags
# uses: ./.github/workflows/laravel.yml
# secrets: inherit
# permissions:
# contents: read
# packages: write
bouncer:
needs: php
name: Nginx Load Balancer (Bouncer)
uses: ./.github/workflows/bouncer.yml
secrets: inherit
permissions:
contents: read
packages: write
mitm-proxy:
needs: qc-before
name: Man-in-the-middle proxy w/Healthchecks
uses: ./.github/workflows/mitm-proxy.yml
secrets: inherit
permissions:
contents: read
packages: write
mqtt:
needs: qc-before
name: MQTT w/Healthchecks
uses: ./.github/workflows/mqtt.yml
secrets: inherit
permissions:
contents: read
packages: write
mariadb:
needs: qc-before
name: MariaDB w/Healthcheck
uses: ./.github/workflows/mariadb.yml
secrets: inherit
permissions:
contents: read
packages: write
postgres:
needs: qc-before
name: Postgres w/Healthcheck
uses: ./.github/workflows/postgres.yml
secrets: inherit
permissions:
contents: read
packages: write
mysql-proxy:
needs: qc-before
name: MySQL Proxy
uses: ./.github/workflows/mysql-proxy.yml
secrets: inherit
permissions:
contents: read
packages: write
#node:
# needs: marshall
# name: Node
# uses: ./.github/workflows/node.yml
# secrets: inherit
# permissions:
# contents: read
# packages: write
redis:
needs: qc-before
name: Redis w/Healthcheck
uses: ./.github/workflows/redis.yml
secrets: inherit
permissions:
contents: read
packages: write
swarm-monitor:
needs: php
name: Swarm Monitor
uses: ./.github/workflows/swarm-monitor.yml
secrets: inherit
permissions:
contents: read
packages: write
wordpress:
needs: php
name: Wordpress
uses: ./.github/workflows/wordpress.yml
secrets: inherit
permissions:
contents: read
packages: write
dynamodb:
needs: qc-before
name: DynamoDB Dev Container
uses: ./.github/workflows/dynamodb.yml
secrets: inherit
permissions:
contents: read
packages: write
minio:
needs: qc-before
name: Minio Dev Container
uses: ./.github/workflows/minio.yml
secrets: inherit
permissions:
contents: read
packages: write
mongodb:
needs: qc-before
name: MongoDB Dev Container
uses: ./.github/workflows/mongodb.yml
secrets: inherit
permissions:
contents: read
packages: write

View file

@ -1,48 +0,0 @@
name: DynamoDB
permissions:
contents: read
packages: write
on:
workflow_call:
workflow_dispatch:
jobs:
dynamodb-build:
name: "Build"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
sparse-checkout: dynamodb
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v2
- name: "Setup: Login to Docker Hub"
uses: docker/login-action@v3
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: "Setup: Login to GHCR"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: matthewbaggett
password: ${{ secrets.GHCR_PASSWORD }}
- uses: docker/build-push-action@v5
name: Build & Push
with:
context: dynamodb
platforms: linux/amd64,linux/arm64
pull: true
push: true
tags: |
gone/dynamodb
benzine/dynamodb
ghcr.io/benzine-framework/dynamodb

View file

@ -1,91 +0,0 @@
name: Build Laravel Container
permissions:
contents: read
packages: write
on:
workflow_call:
workflow_dispatch:
push:
branches:
- main
paths:
- "laravel/**"
- ".github/workflows/laravel.yml"
jobs:
laravel-build:
name: "Build: Laravel Container"
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
version:
- "8.0"
- "8.1"
- "8.2"
- "8.3"
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: laravel
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v2
- name: "Setup: Login to Docker Hub"
uses: docker/login-action@v3
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: "Setup: Login to GHCR"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: matthewbaggett
password: ${{ secrets.GHCR_PASSWORD }}
- uses: docker/build-push-action@v3
name: Build & Push
with:
context: laravel
platforms: ${{ !env.ACT && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
pull: true
push: true
build-args: |
PHP_VERSION=${{ matrix.version }}
tags: |
ghcr.io/benzine-framework/laravel:php-${{ matrix.version }}
benzine/laravel:php-${{ matrix.version }}
matthewbaggett/laravel:${{ matrix.version }}
cache-from: ${{ !env.ACT && 'type=gha' || 'type=local,src=/tmp' }}
cache-to: ${{ !env.ACT && 'type=gha,mode=max' || 'type=local,dest=/tmp' }}
build-contexts: |
php:nginx=docker-image://ghcr.io/benzine-framework/php:nginx-8.2
laravel-tag-latest:
name: "Tag: Laravel Container"
runs-on: ubuntu-latest
needs: [laravel-build]
steps:
- name: "Setup: Login to Docker Hub"
uses: docker/login-action@v3
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: "Setup: Login to GHCR"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: matthewbaggett
password: ${{ secrets.GHCR_PASSWORD }}
- name: "Retag matthewbaggett/laravel:8.3 to matthewbaggett/laravel:latest"
if: ${{ !env.ACT }}
run: |
docker pull matthewbaggett/laravel:8.3
docker tag matthewbaggett/laravel:8.3 matthewbaggett/laravel:latest
docker push matthewbaggett/laravel:latest

View file

@ -1,68 +0,0 @@
name: MariaDB w/Healthcheck
permissions:
contents: read
packages: write
on:
workflow_call:
workflow_dispatch:
push:
branches:
- main
paths:
- mariadb/**
- .github/workflows/mariadb.yml
jobs:
mariadb-build:
name: "Build"
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
version:
- latest
- 11
- 11.2
- 11.1
- 11.0
- 10
- 10.11
- "10.10"
- 10.6
- 10.5
- 10.4
steps:
- uses: actions/checkout@v3
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v2
- name: "Setup: Login to Docker Hub"
uses: docker/login-action@v3
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: "Setup: Login to GHCR"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: matthewbaggett
password: ${{ secrets.GHCR_PASSWORD }}
- uses: docker/build-push-action@v5
name: Build & Push
with:
context: mariadb
platforms: ${{ env.ACT && 'linux/amd64' || 'linux/amd64,linux/arm64' }}
pull: true
push: true
tags: |
benzine/mariadb:${{ matrix.version }}
ghcr.io/benzine-framework/mariadb:${{ matrix.version }}
build-contexts: |
mariadb:injected-version=docker-image://mariadb:${{ matrix.version }}

View file

@ -1,48 +0,0 @@
name: Minio
permissions:
contents: read
packages: write
on:
workflow_call:
workflow_dispatch:
jobs:
minio-build:
name: "Build"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
sparse-checkout: minio
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v2
- name: "Setup: Login to Docker Hub"
uses: docker/login-action@v3
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: "Setup: Login to GHCR"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: matthewbaggett
password: ${{ secrets.GHCR_PASSWORD }}
- uses: docker/build-push-action@v5
name: Build & Push
with:
context: minio
platforms: linux/amd64,linux/arm64
pull: true
push: true
tags: |
gone/minio
benzine/minio
ghcr.io/benzine-framework/minio

View file

@ -1,103 +0,0 @@
name: MITMproxy w/Healthcheck
permissions:
contents: read
packages: write
on:
workflow_call:
workflow_dispatch:
push:
branches:
- main
paths:
- mitm-proxy/**
- .github/workflows/mitm-proxy.yml
jobs:
mitmproxy-build:
name: "MITM proxy with Healthchecks"
runs-on: ubuntu-latest
strategy:
fail-fast: false
max-parallel: 15
matrix:
mitmproxy:
- latest
- 10.2.2
- 10.2.1
- 10.2.0
- 10.1.6
- 10.1.5
- 10.1.4
- 10.1.3
- 10.1.2
- 10.1.1
- 10.1.0
- 10.0.0
- 9.0.1
- 9.0.0
- 8.1.1
- 8.1.0
- 8.0.0
- 7.0.4
- 7.0.3
- 7.0.2
- 7.0.1
- 7.0.0
- 6.0.2
- 6.0.1
- 6.0.0
- 5.3.0
- 5.2
- 5.1.1
- 5.1.0
- 5.0.1
- 5.0.0
steps:
- name: "Setup: Setup QEMU"
uses: docker/setup-qemu-action@v3
- name: "Setup: Expose GitHub Runtime"
uses: crazy-max/ghaction-github-runtime@v3
- name: "Setup: Setup Docker Buildx"
uses: docker/setup-buildx-action@v2
- name: "Setup: Checkout Source"
uses: actions/checkout@v4
with:
sparse-checkout: |
mitm-proxy
- name: "Setup: Login to Docker Hub"
uses: docker/login-action@v3
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: "Setup: Login to GHCR"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: matthewbaggett
password: ${{ secrets.GHCR_PASSWORD }}
- name: "Build: Build MITM proxy with healthchecks"
uses: docker/build-push-action@v5
with:
context: mitm-proxy
build-contexts: |
mitmproxy:version=docker-image://mitmproxy/mitmproxy:${{ matrix.mitmproxy }}
build-args: |
MITMPROXY_VERSION=${{ matrix.mitmproxy }}
GIT_SHA=${{ github.sha }}
BUILD_DATE=${{ github.event.repository.pushed_at }}
platforms: ${{ (!env.ACT && !(startsWith('6.',matrix.mitmproxy) || startsWith('5.', matrix.mitmproxy))) && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
pull: true
push: true
tags: |
${{ format('benzine/mitmproxy:{0}', matrix.mitmproxy) }}
${{ format('ghcr.io/benzine-framework/mitmproxy:{0}', matrix.mitmproxy) }}
cache-from: ${{ !env.ACT && 'type=gha' || '' }}
cache-to: ${{ !env.ACT && 'type=gha,mode=max' || '' }}

View file

@ -1,48 +0,0 @@
name: MongoDB
permissions:
contents: read
packages: write
on:
workflow_call:
workflow_dispatch:
jobs:
mongodb-build:
name: "Build"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
sparse-checkout: mongodb
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v2
- name: "Setup: Login to Docker Hub"
uses: docker/login-action@v3
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: "Setup: Login to GHCR"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: matthewbaggett
password: ${{ secrets.GHCR_PASSWORD }}
- uses: docker/build-push-action@v5
name: Build & Push
with:
context: mongodb
platforms: linux/amd64,linux/arm64
pull: true
push: true
tags: |
gone/mongodb
benzine/mongodb
ghcr.io/benzine-framework/mongodb

View file

@ -1,62 +0,0 @@
name: Mosquitto
permissions:
contents: read
packages: write
on:
workflow_call:
workflow_dispatch:
jobs:
mqtt-build:
name: "Build"
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
version:
- latest
- 2
- 2.0.18
- 2.0.14
- 1.6
openssl:
- ""
- "-openssl"
exclude:
- version: latest
openssl: "-openssl"
steps:
- uses: actions/checkout@v3
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v2
- name: "Setup: Login to Docker Hub"
uses: docker/login-action@v3
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: "Setup: Login to GHCR"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: matthewbaggett
password: ${{ secrets.GHCR_PASSWORD }}
- uses: docker/build-push-action@v5
name: Build & Push
with:
context: mqtt
platforms: linux/amd64,linux/arm64
pull: true
push: true
tags: |
gone/mqtt:${{ matrix.version }}${{ matrix.openssl }}
benzine/mqtt:${{ matrix.version }}${{ matrix.openssl }}
ghcr.io/benzine-framework/mqtt:${{ matrix.version }}${{ matrix.openssl }}
build-contexts: |
eclipse-mosquitto:injected-version=docker-image://eclipse-mosquitto:${{ matrix.version }}${{ matrix.openssl }}

View file

@ -1,62 +0,0 @@
name: Build MySQL Proxy
permissions:
contents: read
packages: write
on:
workflow_call:
workflow_dispatch:
concurrency:
group: mysql-proxy-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
mysql-proxy-build:
name: "Build"
runs-on: ubuntu-latest
steps:
- name: "Setup: Setup QEMU"
uses: docker/setup-qemu-action@v3
- name: "Setup: Expose GitHub Runtime"
uses: crazy-max/ghaction-github-runtime@v3
- name: "Setup: Setup Docker Buildx"
uses: docker/setup-buildx-action@v2
- name: "Setup: Login to Docker Hub"
uses: docker/login-action@v3
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: "Setup: Login to GHCR"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: matthewbaggett
password: ${{ secrets.GHCR_PASSWORD }}
- name: "Setup: Checkout Source"
uses: actions/checkout@v4
with:
sparse-checkout: |
mysql-proxy
- name: "Build: Build & Push Image"
uses: docker/build-push-action@v5
with:
context: mysql-proxy
target: mysql-proxy
platforms: ${{ !env.ACT && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
pull: true
push: true
tags: |
${{ !env.ACT && 'benzine/mysql-proxy:latest' || '' }}
${{ !env.ACT && 'ghcr.io/benzine-framework/mysql-proxy:latest' || 'ghcr.io/benzine-framework/mysql-proxy:devel' }}
cache-from: ${{ !env.ACT && 'type=gha' || '' }}
cache-to: ${{ !env.ACT && 'type=gha,mode=max' || '' }}
build-contexts: |
marshall:version=docker-image://ghcr.io/benzine-framework/marshall:focal

View file

@ -1,73 +0,0 @@
name: Build NodeJS Flavours
permissions:
contents: read
packages: write
on:
workflow_call:
workflow_dispatch:
concurrency:
group: nodejs-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
node-flavours-build:
name: "Build"
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
yarn:
- latest
- "1.22.21"
- "1.22.20"
- "1.22.0"
- "1.21.0"
- "1.20.0"
node:
- latest
- "16.13.0"
- "16.20.2"
- "17.9.1"
- "18.19.0"
- "19.9.0"
- "20.9.0"
- "21.6.1"
steps:
- uses: actions/checkout@v3
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v2
- name: "Setup: Login to Docker Hub"
uses: docker/login-action@v3
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: "Setup: Login to GHCR"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: matthewbaggett
password: ${{ secrets.GHCR_PASSWORD }}
- uses: docker/build-push-action@v3
name: Build & Push
with:
context: node
file: node/Dockerfile
target: nodejs
platforms: linux/amd64,linux/arm64
pull: true
push: true
tags: |
gone/node:${{ matrix.node }}-${{ matrix.yarn }}
benzine/node:${{ matrix.node }}-${{ matrix.yarn }}
ghcr.io/benzine-framework/node:${{ matrix.node }}-${{ matrix.yarn }}
build-args: |
NODE_VERSION=${{ matrix.node }}
YARN_VERSION=${{ matrix.yarn }}
build-contexts: |
marshall:build=docker-image://ghcr.io/benzine-framework/marshall:focal

View file

@ -1,67 +0,0 @@
name: Build Octoprint
permissions:
contents: read
packages: write
on:
workflow_call:
workflow_dispatch:
push:
branches:
- main
paths:
- "octoprint/**"
- ".github/workflows/octoprint.yml"
jobs:
octoprint-build:
name: "Bake Octoprint"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v2
- uses: docker/login-action@v3
name: Login to Docker Hub
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- uses: docker/build-push-action@v3
name: Build Octoprint
with:
context: octoprint
target: octoprint
platforms: linux/amd64,linux/arm64,linux/arm/v7
pull: true
push: true
tags: matthewbaggett/octoprint:latest
octoprint-mjpg-build:
name: "Bake Octoprint for yuvu cameras"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v2
- name: "Setup: Login to Docker Hub"
uses: docker/login-action@v3
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: "Setup: Login to GHCR"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: matthewbaggett
password: ${{ secrets.GHCR_PASSWORD }}
- uses: docker/build-push-action@v3
name: Build Octoprint MJPG Streamer Variant
with:
context: octoprint
target: octoprint-mjpg-streamer
platforms: linux/amd64,linux/arm64,linux/arm/v7
pull: true
push: true
tags: matthewbaggett/octoprint:mjpg-streamer-yu12

View file

@ -1,102 +0,0 @@
name: Build Perl
on:
workflow_call:
workflow_dispatch:
push:
branches:
- main
paths:
- "perl/**"
- ".github/workflows/perl.yml"
workflow_run:
workflows:
- "Marshall"
types:
- completed
permissions:
contents: read
packages: write
jobs:
perl-build:
name: "Build Perl"
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
ubuntu:
- focal
- jammy
- lunar
- mantic
#- noble
#- devel
- rolling
- latest
steps:
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v2
- name: "Setup: Login to Docker Hub"
uses: docker/login-action@v3
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: "Setup: Login to GHCR"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: matthewbaggett
password: ${{ secrets.GHCR_PASSWORD }}
- uses: actions/checkout@v4
with:
sparse-checkout: perl
- uses: docker/build-push-action@v5
name: "Build: Build & Push"
with:
context: perl
target: release
platforms: linux/amd64,linux/arm64,linux/ppc64le,linux/s390x,linux/arm/v7
push: true
tags: |
ghcr.io/benzine-framework/perl:${{ matrix.ubuntu }}
matthewbaggett/perl:${{ matrix.ubuntu }}
benzine/perl:${{ matrix.ubuntu }}
build-args: |
${{ steps.build_args.outputs.result }}
cache-from: ${{ !env.ACT && 'type=gha' || '' }}
cache-to: ${{ !env.ACT && 'type=gha,mode=max' || '' }}
build-contexts: |
marshall:build=docker-image://ghcr.io/benzine-framework/marshall:${{ matrix.ubuntu }}
- name: "Get perl binary version"
id: perl_version
run: |
echo "perl_version=$(docker run --rm ghcr.io/benzine-framework/perl:${{ matrix.ubuntu }} perl -v | grep "This is perl" | cut -d'(' -f2 | cut -d')' -f1 | sed 's/[^0-9.]*//g')" >> $GITHUB_OUTPUT
- uses: docker/build-push-action@v5
name: "Build: Build & Push (Versioned)"
with:
context: perl
target: release
platforms: linux/amd64,linux/arm64,linux/ppc64le,linux/s390x,linux/arm/v7
push: true
tags: |
ghcr.io/benzine-framework/perl:${{ steps.perl_version.outputs.perl_version }}-${{ matrix.ubuntu }}
matthewbaggett/perl:${{ steps.perl_version.outputs.perl_version }}-${{ matrix.ubuntu }}
benzine/perl:${{ steps.perl_version.outputs.perl_version }}-${{ matrix.ubuntu }}
${{ matrix.ubuntu == 'focal' || matrix.ubuntu == 'jammy' || matrix.ubuntu == 'lunar' && 'ghcr.io/benzine-framework/perl:${{ steps.perl_version.outputs.perl_version }}' || '' }}
${{ matrix.ubuntu == 'focal' || matrix.ubuntu == 'jammy' || matrix.ubuntu == 'lunar' && 'matthewbaggett/perl:${{ steps.perl_version.outputs.perl_version }}' || '' }}
${{ matrix.ubuntu == 'focal' || matrix.ubuntu == 'jammy' || matrix.ubuntu == 'lunar' && 'benzine/perl:${{ steps.perl_version.outputs.perl_version }}' || '' }}
build-args: |
${{ steps.build_args.outputs.result }}
cache-from: ${{ !env.ACT && 'type=gha' || '' }}
cache-to: ${{ !env.ACT && 'type=gha,mode=max' || '' }}
build-contexts: |
marshall:build=docker-image://ghcr.io/benzine-framework/marshall:${{ matrix.ubuntu }}

View file

@ -1,157 +0,0 @@
name: Build PHP
on:
workflow_call:
workflow_dispatch:
permissions:
contents: read
packages: write
jobs:
php-flavours-build:
name: "Build PHP Flavours"
runs-on: ubuntu-latest
strategy:
fail-fast: false
max-parallel: 15
matrix:
variant:
- cli
- nginx
- apache
version:
- "7.0"
- "7.1"
- "7.2"
- "7.3"
- "7.4"
- "8.0"
- "8.1"
- "8.2"
- "8.3"
env:
PHP_PACKAGES_70: git mariadb-client php7.0-apcu php7.0-bcmath php7.0-bz2 php7.0-cli php7.0-curl php7.0-gd php7.0-imap php7.0-imagick php7.0-intl php7.0-json php7.0-ldap php7.0-mbstring php7.0-mcrypt php7.0-memcache php7.0-memcached php7.0-mongodb php7.0-mysql php7.0-opcache php7.0-pgsql php7.0-phpdbg php7.0-pspell php7.0-redis php7.0-soap php7.0-sqlite php7.0-xdebug php7.0-xml php7.0-zip postgresql-client
PHP_PACKAGES_71: git mariadb-client php7.1-apcu php7.1-bcmath php7.1-bz2 php7.1-cli php7.1-curl php7.1-gd php7.1-imap php7.1-imagick php7.1-intl php7.1-json php7.1-ldap php7.1-mbstring php7.1-mcrypt php7.1-memcache php7.1-memcached php7.1-mongodb php7.1-mysql php7.1-opcache php7.1-pgsql php7.1-phpdbg php7.1-pspell php7.1-redis php7.1-soap php7.1-sqlite php7.1-xdebug php7.1-xml php7.1-zip postgresql-client
PHP_PACKAGES_72: git mariadb-client php7.2-apcu php7.2-bcmath php7.2-bz2 php7.2-cli php7.2-curl php7.2-gd php7.2-imap php7.2-imagick php7.2-intl php7.2-json php7.2-ldap php7.2-mbstring php7.2-memcache php7.2-memcached php7.2-mongodb php7.2-mysql php7.2-opcache php7.2-pgsql php7.2-phpdbg php7.2-pspell php7.2-redis php7.2-soap php7.2-sqlite php7.2-xdebug php7.2-xml php7.2-zip postgresql-client
PHP_PACKAGES_73: git htop mariadb-client php7.3-apcu php7.3-bcmath php7.3-bz2 php7.3-cli php7.3-curl php7.3-gd php7.3-imap php7.3-imagick php7.3-intl php7.3-json php7.3-ldap php7.3-mbstring php7.3-memcache php7.3-memcached php7.3-mongodb php7.3-mysql php7.3-opcache php7.3-pgsql php7.3-phpdbg php7.3-pspell php7.3-redis php7.3-soap php7.3-sqlite php7.3-xdebug php7.3-xml php7.3-zip postgresql-client
PHP_PACKAGES_74: git htop mariadb-client php7.4-apcu php7.4-bcmath php7.4-bz2 php7.4-cli php7.4-curl php7.4-gd php7.4-imap php7.4-imagick php7.4-intl php7.4-json php7.4-ldap php7.4-mbstring php7.4-memcache php7.4-mongodb php7.4-mysql php7.4-opcache php7.4-pgsql php7.4-phpdbg php7.4-pspell php7.4-redis php7.4-soap php7.4-sqlite php7.4-xdebug php7.4-xml php7.4-zip postgresql-client
PHP_PACKAGES_80: git htop mariadb-client php8.0-apcu php8.0-bcmath php8.0-bz2 php8.0-cli php8.0-curl php8.0-gd php8.0-imap php8.0-imagick php8.0-intl php8.0-ldap php8.0-mailparse php8.0-mbstring php8.0-memcache php8.0-mongodb php8.0-mysql php8.0-opcache php8.0-pgsql php8.0-phpdbg php8.0-pspell php8.0-redis php8.0-soap php8.0-sqlite php8.0-xdebug php8.0-xml php8.0-zip postgresql-client
PHP_PACKAGES_81: git htop mariadb-client php8.1-apcu php8.1-bcmath php8.1-bz2 php8.1-cli php8.1-curl php8.1-gd php8.1-imap php8.1-imagick php8.1-intl php8.1-ldap php8.1-mailparse php8.1-mbstring php8.1-memcache php8.1-mongodb php8.1-mysql php8.1-opcache php8.1-pgsql php8.1-phpdbg php8.1-pspell php8.1-redis php8.1-soap php8.1-sqlite php8.1-xdebug php8.1-xml php8.1-zip postgresql-client
PHP_PACKAGES_82: git htop mariadb-client php8.2-apcu php8.2-bcmath php8.2-bz2 php8.2-cli php8.2-curl php8.2-gd php8.2-imap php8.2-imagick php8.2-intl php8.2-ldap php8.2-mailparse php8.2-mbstring php8.2-memcache php8.2-mongodb php8.2-mysql php8.2-opcache php8.2-pgsql php8.2-phpdbg php8.2-pspell php8.2-redis php8.2-soap php8.2-sqlite php8.2-xdebug php8.2-xml php8.2-zip postgresql-client
PHP_PACKAGES_83: git htop mariadb-client php8.3-apcu php8.3-bcmath php8.3-bz2 php8.3-cli php8.3-curl php8.3-gd php8.3-imap php8.3-imagick php8.3-intl php8.3-ldap php8.3-mailparse php8.3-mbstring php8.3-memcache php8.3-mongodb php8.3-mysql php8.3-opcache php8.3-pgsql php8.3-phpdbg php8.3-pspell php8.3-redis php8.3-soap php8.3-sqlite php8.3-xdebug php8.3-xml php8.3-zip postgresql-client
COMPOSER_VERSION_70: 2.2.22
COMPOSER_VERSION_71: 2.2.22
steps:
- name: Interpolate Envs
id: vars
run: |
{
VERSION=$(echo "${{ matrix.version }}" | tr -d '.')
PACKAGE_LIST=PHP_PACKAGES_$VERSION
echo php_packages=${!PACKAGE_LIST}
COMPOSER_VERSION=COMPOSER_VERSION_$VERSION
echo composer_version=${!COMPOSER_VERSION:-"latest-stable"}
} >> "$GITHUB_OUTPUT"
- name: Show mangled envs
run: |
echo "Packages to be installed:"
echo ${{ steps.vars.outputs.php_packages }}
- uses: actions/github-script@v3
name: Build Args
id: build_args
with:
result-encoding: string
script: return `PHP_PACKAGES=${{ steps.vars.outputs.php_packages }}\nPHP_VERSION=${{ matrix.version }}\nCOMPOSER_VERSION=${{ steps.vars.outputs.composer_version }}`
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v2
- name: "Setup: Login to Docker Hub"
uses: docker/login-action@v3
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: "Setup: Login to GHCR"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: matthewbaggett
password: ${{ secrets.GHCR_PASSWORD }}
- uses: actions/checkout@v4
with:
sparse-checkout: php
- uses: docker/build-push-action@v5
name: "Build: Build & Push"
with:
context: php
target: php-${{ matrix.variant }}
platforms: ${{ !env.ACT && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
pull: true
push: true
tags: |
ghcr.io/benzine-framework/php:${{ matrix.variant }}-${{ matrix.version }}
gone/php:${{ matrix.variant }}-${{ matrix.version }}
benzine/php:${{ matrix.variant }}-${{ matrix.version }}
build-args: |
${{ steps.build_args.outputs.result }}
cache-from: ${{ !env.ACT && 'type=gha' || '' }}
cache-to: ${{ !env.ACT && 'type=gha,mode=max' || '' }}
build-contexts: |
marshall:build=docker-image://ghcr.io/benzine-framework/marshall:focal
- name: "Validate build"
shell: bash
run: |
docker \
run \
--rm \
ghcr.io/benzine-framework/php:${{ matrix.variant }}-${{ matrix.version }} \
/usr/bin/install-report
php-vanity-tags:
name: Vanity Tags
runs-on: ubuntu-latest
needs:
- php-flavours-build
env:
latest-stable-version: "8.3"
base_tag: "ghcr.io/benzine-framework/php"
strategy:
fail-fast: false
matrix:
variant:
- cli
- nginx
- apache
output_tag:
- "benzine/php"
- "gone/php"
- "ghcr.io/benzine-framework/php"
steps:
- name: "Setup: Login to Docker Hub"
uses: docker/login-action@v3
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: "Setup: Login to GHCR"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: matthewbaggett
password: ${{ secrets.GHCR_PASSWORD }}
- name: "Retag ${{ env.base_tag }}:${{ matrix.variant }}-${{ env.latest-stable-version }} to ${{ matrix.output_tag }}:${{ matrix.variant }}"
if: ${{ github.ref == 'refs/heads/main' }}
shell: bash
run: |
docker pull ${{ env.base_tag }}:${{ matrix.variant }}-${{ env.latest-stable-version }}
docker tag ${{ env.base_tag }}:${{ matrix.variant }}-${{ env.latest-stable-version }} ${{ matrix.output_tag }}:${{ matrix.variant }}
docker push ${{ matrix.output_tag }}:${{ matrix.variant }}

View file

@ -1,108 +0,0 @@
name: Postgres w/Healthcheck
permissions:
contents: read
packages: write
on:
workflow_call:
workflow_dispatch:
push:
branches:
- main
paths:
- postgres/**
- .github/workflows/postgres.yml
jobs:
postgres-build:
name: "Build"
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
version:
- latest
- 16
- 16.2
- 16.1
- "16.0"
- 15
- 15.6
- 15.5
- 15.4
- 15.3
- 15.2
- 15.1
- "15.0"
- 14
- 14.11
- 14.10
- 14.9
- 14.8
- 14.7
- 14.6
- 14.5
- 14.4
- 14.3
- 14.2
- 14.1
- "14.0"
- 13
- 13.14
- 12
- 12.18
alpine:
- "-alpine"
- ""
exclude:
- version: latest
alpine: "-alpine"
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: postgres
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v2
- name: "Setup: Login to Docker Hub"
uses: docker/login-action@v3
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: "Setup: Login to GHCR"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: matthewbaggett
password: ${{ secrets.GHCR_PASSWORD }}
- name: "Setup: Docker meta"
id: meta
uses: docker/metadata-action@v5
with:
images: |
benzine/postgres
ghcr.io/benzine-framework/postgres
labels: |
org.opencontainers.image.title=Postgres ${{ matrix.version }}${{ matrix.alpine }} w/healthcheck
org.opencontainers.image.description=Postgres ${{ matrix.version }} with Healthcheck
org.opencontainers.image.vendor=Matthew Baggett <matthew@baggett.me>
tags: |
type=raw,value=${{ matrix.version }}${{ matrix.alpine }},enable=true
- name: "Docker: Build & Push"
uses: docker/build-push-action@v5
with:
context: postgres
platforms: ${{ env.ACT && 'linux/amd64' || 'linux/amd64,linux/arm64' }}
pull: true
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
annotations: ${{ steps.meta.outputs.annotations }}
build-contexts: |
postgres:injected-version=docker-image://postgres:${{ matrix.version }}${{ matrix.alpine }}

View file

@ -1,78 +0,0 @@
name: Redis w/Healthcheck
permissions:
contents: read
packages: write
on:
workflow_call:
workflow_dispatch:
jobs:
redis-build:
name: "Build"
runs-on: ubuntu-latest
strategy:
fail-fast: false
max-parallel: 15
matrix:
redis:
- 3
- 4
- 5
- 6
- 6-alpine
- 6-buster
- "6.0"
- 6.2
- 7
- 7-alpine
- 7-bookworm
- "7.0"
- 7.2
- latest
steps:
- name: "Setup: Setup QEMU"
uses: docker/setup-qemu-action@v3
- name: "Setup: Expose GitHub Runtime"
uses: crazy-max/ghaction-github-runtime@v3
- name: "Setup: Setup Docker Buildx"
uses: docker/setup-buildx-action@v2
- name: "Setup: Checkout Source"
uses: actions/checkout@v4
with:
sparse-checkout: |
redis
- name: "Setup: Login to Docker Hub"
uses: docker/login-action@v3
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: "Setup: Login to GHCR"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: matthewbaggett
password: ${{ secrets.GHCR_PASSWORD }}
- name: "Build: Build Redis with Healthchecks"
uses: docker/build-push-action@v5
with:
context: redis
build-contexts: |
redis:version=docker-image://redis:${{ matrix.redis }}
build-args: |
HEALTH_INTERVAL=10s
platforms: ${{ !env.ACT && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
pull: true
push: true
tags: |
${{ format('ghcr.io/benzine-framework/redis:{0}', matrix.redis) }}
${{ format('benzine/redis:{0}',matrix.redis) || '' }}
cache-from: ${{ !env.ACT && 'type=gha' || '' }}
cache-to: ${{ !env.ACT && 'type=gha,mode=max' || '' }}

View file

@ -1,104 +0,0 @@
name: Build S3DB
on:
workflow_call:
workflow_dispatch:
workflow_run:
workflows: ["Postgres", "MariaDB"]
types: [completed]
push:
branches:
- main
paths:
- s3db/**
- .github/workflows/s3db.yml
concurrency:
group: ${{ github.workflow }}-s3db-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
s3db-build:
name: Build S3DB
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
database:
- postgres
- mariadb
include:
- database: postgres
version: 16
- database: postgres
version: 15
- database: mariadb
version: 11
- database: mariadb
version: 10
steps:
- name: "Setup: PHP"
uses: shivammathur/setup-php@v2
with:
php-version: 8.1
env:
runner: self-hosted
- name: "Setup: Setup QEMU"
uses: docker/setup-qemu-action@v3
- name: "Setup: Expose GitHub Runtime"
uses: crazy-max/ghaction-github-runtime@v3
- name: "Setup: Setup Docker Buildx"
uses: docker/setup-buildx-action@v2
- name: "Setup: Login to Docker Hub"
uses: docker/login-action@v3
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: "Setup: Login to GHCR"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: matthewbaggett
password: ${{ secrets.GHCR_PASSWORD }}
- name: "Setup: Checkout Source"
uses: actions/checkout@v4
with:
sparse-checkout: |
s3db
- name: "Setup: Configure Cache"
uses: actions/cache@v4
with:
path: s3db
key: ${{ runner.os }}-s3db-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-s3db-composer-
- name: "Dependencies: Composer Install"
working-directory: s3db
run: composer install --ignore-platform-reqs
- name: "Build: Build & Push Image"
uses: docker/build-push-action@v5
with:
context: s3db
target: ${{ matrix.database }}
file: s3db/Dockerfile.${{ matrix.database }}
platforms: ${{ !env.ACT && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
push: true
tags: |
ghcr.io/benzine-framework/s3db:${{ matrix.database }}-${{ matrix.version }}
benzine/s3db:${{ matrix.database }}-${{ matrix.version }}
cache-from: ${{ !env.ACT && 'type=gha' || '' }}
cache-to: ${{ !env.ACT && 'type=gha,mode=max' || '' }}
build-contexts: |
postgres:injected-version=docker-image://ghcr.io/benzine-framework/postgres:${{ matrix.version }}-alpine
mariadb:injected-version=docker-image://ghcr.io/benzine-framework/mariadb:${{ matrix.version }}

View file

@ -1,109 +0,0 @@
name: Build Swarm Connectivity Tester
permissions:
contents: read
packages: write
on:
push:
branches:
- main
paths:
- swarm-connectivity-tester/**
- .github/workflows/swarm-connectivity-tester.yml
workflow_call:
workflow_dispatch:
concurrency:
group: swarm-connectivity-tester-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
swarm-connectivity-tester-build:
name: Build Swarm Connectivity Tester
runs-on: ubuntu-latest
steps:
- name: "Setup: Checkout Source"
uses: actions/checkout@v4
with:
sparse-checkout: |
swarm-connectivity-tester
- name: "Setup: PHP"
uses: shivammathur/setup-php@v2
with:
php-version: 8.2
env:
runner: self-hosted
- name: "Setup: Setup QEMU"
uses: docker/setup-qemu-action@v3
- name: "Setup: Expose GitHub Runtime"
uses: crazy-max/ghaction-github-runtime@v3
- name: "Setup: Setup Docker Buildx"
uses: docker/setup-buildx-action@v2
- name: "Setup: Login to Docker Hub"
uses: docker/login-action@v3
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: "Setup: Login to GHCR"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: matthewbaggett
password: ${{ secrets.GHCR_PASSWORD }}
- name: "Setup: Find Composer Cache Directory"
id: composer-cache
working-directory: swarm-connectivity-tester
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: "Setup: Composer Cache"
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-swarm-connectivity-tester-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-swarm-connectivity-tester-composer-
- name: "Dependencies: Composer Install"
working-directory: swarm-connectivity-tester
run: composer install --ignore-platform-reqs
- name: "Build: Build & Push Target Image"
uses: docker/build-push-action@v5
with:
context: swarm-connectivity-tester
target: connect-target
platforms: ${{ !env.ACT && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
pull: true
push: true
tags: |
${{ !env.ACT && 'benzine/swarm-connectivity-tester:target' || '' }}
${{ !env.ACT && 'ghcr.io/benzine-framework/swarm-connectivity-tester:target' || 'ghcr.io/benzine-framework/swarm-connectivity-tester:target-devel' }}
cache-from: ${{ !env.ACT && 'type=gha' || '' }}
cache-to: ${{ !env.ACT && 'type=gha,mode=max' || '' }}
build-contexts: |
php:cli=docker-image://ghcr.io/benzine-framework/php:cli-8.2
php:nginx=docker-image://ghcr.io/benzine-framework/php:nginx-8.2
- name: "Build: Build & Push Report Image"
uses: docker/build-push-action@v5
with:
context: swarm-connectivity-tester
target: connect-reporter
platforms: ${{ !env.ACT && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
pull: true
push: true
tags: |
${{ !env.ACT && 'benzine/swarm-connectivity-tester:reporter' || '' }}
${{ !env.ACT && 'ghcr.io/benzine-framework/swarm-connectivity-tester:reporter' || 'ghcr.io/benzine-framework/swarm-connectivity-tester:reporter-devel' }}
cache-from: ${{ !env.ACT && 'type=gha' || '' }}
cache-to: ${{ !env.ACT && 'type=gha,mode=max' || '' }}
build-contexts: |
php:cli=docker-image://ghcr.io/benzine-framework/php:cli-8.2
php:nginx=docker-image://ghcr.io/benzine-framework/php:nginx-8.2

View file

@ -1,82 +0,0 @@
name: Build Swarm Monitor
permissions:
contents: read
packages: write
on:
workflow_call:
workflow_dispatch:
jobs:
swarm-monitor-build:
name: Build Swarm Monitor
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
component:
- agent
- stats
steps:
- name: "Setup: PHP"
uses: shivammathur/setup-php@v2
with:
php-version: 8.1
env:
runner: self-hosted
- name: "Setup: Setup QEMU"
uses: docker/setup-qemu-action@v3
- name: "Setup: Expose GitHub Runtime"
uses: crazy-max/ghaction-github-runtime@v3
- name: "Setup: Setup Docker Buildx"
uses: docker/setup-buildx-action@v2
- name: "Setup: Login to Docker Hub"
uses: docker/login-action@v3
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: "Setup: Login to GHCR"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: matthewbaggett
password: ${{ secrets.GHCR_PASSWORD }}
- name: "Setup: Checkout Source"
uses: actions/checkout@v4
with:
sparse-checkout: |
swarm-monitor
- name: "Setup: Configure Cache"
uses: actions/cache@v4
with:
path: swarm-monitor
key: ${{ runner.os }}-swarm-monitor-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-swarm-monitor-composer-
- name: "Dependencies: Composer Install"
working-directory: swarm-monitor
run: composer install --ignore-platform-reqs
- name: "Build: Build & Push Image"
uses: docker/build-push-action@v5
with:
context: swarm-monitor
target: swarm-${{ matrix.component }}
platforms: ${{ !env.ACT && 'linux/amd64,linux/arm64' || 'linux/amd64' }}
pull: true
push: true
tags: |
matthewbaggett/swarm-agent:${{ matrix.component }}
cache-from: ${{ !env.ACT && 'type=gha' || 'type=local,src=/tmp' }}
cache-to: ${{ !env.ACT && 'type=gha,mode=max' || 'type=local,dest=/tmp' }}
build-contexts: |
php:cli=docker-image://ghcr.io/benzine-framework/php:cli-8.2

View file

@ -1,51 +0,0 @@
name: Build Wordpress Container
permissions:
contents: read
packages: write
on:
workflow_call:
workflow_dispatch:
push:
branches:
- main
paths:
- "wordpress/**"
- ".github/workflows/wordpress.yml"
jobs:
wordpress-build:
name: "Bake Wordpress Container"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v2
- name: "Setup: Login to Docker Hub"
uses: docker/login-action@v3
with:
username: matthewbaggett
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: "Setup: Login to GHCR"
uses: docker/login-action@v3
with:
registry: ghcr.io
username: matthewbaggett
password: ${{ secrets.GHCR_PASSWORD }}
- uses: docker/build-push-action@v3
name: Build & Push
with:
context: wordpress
platforms: linux/amd64,linux/arm64
pull: true
push: true
tags: |
matthewbaggett/wordpress
build-contexts: |
php:nginx=docker-image://ghcr.io/benzine-framework/php:nginx-8.2

View file

@ -1,18 +1,4 @@
# Benzine Docker Containers
Docker Runit / Marshall
=======================
This repository contains the Dockerfiles for the Benzine containers.
| Container | Tags | Build status | Description |
| -------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
| PHP 8.3 CLI | <ul><li>benzine/php:cli-8.3</li><li>ghcr.io/benzine-framework/php:cli-8.3</li></ul> | [PHP 8.3 CLI Build Status](https://github.com/benzine-framework/docker/actions/workflows/php-flavours.yml/badge.svg) | Multi-thread PHP 8.3 CLI container built atop Runit |
| PHP 8.2 CLI | <ul><li>benzine/php:cli-8.2</li><li>ghcr.io/benzine-framework/php:cli-8.2</li></ul> | [PHP 8.2 CLI Build Status](https://github.com/benzine-framework/docker/actions/workflows/php-flavours.yml/badge.svg) | Multi-thread PHP 8.2 CLI container built atop Runit |
| PHP 8.1 CLI | <ul><li>benzine/php:cli-8.1</li><li>ghcr.io/benzine-framework/php:cli-8.1</li></ul> | [PHP 8.1 CLI Build Status](https://github.com/benzine-framework/docker/actions/workflows/php-flavours.yml/badge.svg) | Multi-thread PHP 8.1 CLI container built atop Runit |
| PHP 7.4 CLI | <ul><li>benzine/php:cli-7.4</li><li>ghcr.io/benzine-framework/php:cli-7.4</li></ul> | [PHP 7.4 CLI Build Status](https://github.com/benzine-framework/docker/actions/workflows/php-flavours.yml/badge.svg) | Multi-thread PHP 7.4 CLI container built atop Runit |
| PHP 8.3 NGINX | <ul><li>benzine/php:nginx-8.3</li><li>ghcr.io/benzine-framework/php:nginx-8.3</li></ul> | [PHP 8.3 NGINX Build Status](https://github.com/benzine-framework/docker/actions/workflows/php-flavours.yml/badge.svg) | Multi-thread PHP 8.3 NGINX container built atop Runit |
| PHP 8.2 NGINX | <ul><li>benzine/php:nginx-8.2</li><li>ghcr.io/benzine-framework/php:nginx-8.2</li></ul> | [PHP 8.2 NGINX Build Status](https://github.com/benzine-framework/docker/actions/workflows/php-flavours.yml/badge.svg) | Multi-thread PHP 8.2 NGINX container built atop Runit |
| PHP 8.1 NGINX | <ul><li>benzine/php:nginx-8.1</li><li>ghcr.io/benzine-framework/php:nginx-8.1</li></ul> | [PHP 8.1 NGINX Build Status](https://github.com/benzine-framework/docker/actions/workflows/php-flavours.yml/badge.svg) | Multi-thread PHP 8.1 NGINX container built atop Runit |
| PHP 7.4 NGINX | <ul><li>benzine/php:nginx-7.4</li><li>ghcr.io/benzine-framework/php:nginx-7.4</li></ul> | [PHP 7.4 NGINX Build Status](https://github.com/benzine-framework/docker/actions/workflows/php-flavours.yml/badge.svg) | Multi-thread PHP 7.4 NGINX container built atop Runit |
| PHP 8.3 Apache | <ul><li>benzine/php:apache-8.3</li><li>ghcr.io/benzine-framework/php:apache-8.3</li></ul> | [PHP 8.3 Apache Build Status](https://github.com/benzine-framework/docker/actions/workflows/php-flavours.yml/badge.svg) | Multi-thread PHP 8.3 Apache container built atop Runit |
| PHP 8.2 Apache | <ul><li>benzine/php:apache-8.2</li><li>ghcr.io/benzine-framework/php:apache-8.2</li></ul> | [PHP 8.2 Apache Build Status](https://github.com/benzine-framework/docker/actions/workflows/php-flavours.yml/badge.svg) | Multi-thread PHP 8.2 Apache container built atop Runit |
| PHP 8.1 Apache | <ul><li>benzine/php:apache-8.1</li><li>ghcr.io/benzine-framework/php:apache-8.1</li></ul> | [PHP 8.1 Apache Build Status](https://github.com/benzine-framework/docker/actions/workflows/php-flavours.yml/badge.svg) | Multi-thread PHP 8.1 Apache container built atop Runit |
| PHP 7.4 Apache | <ul><li>benzine/php:apache-7.4</li><li>ghcr.io/benzine-framework/php:apache-7.4</li></ul> | [PHP 7.4 Apache Build Status](https://github.com/benzine-framework/docker/actions/workflows/php-flavours.yml/badge.svg) | Multi-thread PHP 7.4 Apache container built atop Runit |
Base container to build multi-threaded, multi-process containers using runit as the runtime.

4
bouncer/.gitignore vendored
View file

@ -1,4 +0,0 @@
.idea
docker-compose.override.yml
vendor
.php-cs-fixer.cache

View file

@ -1,40 +0,0 @@
<?php
$finder = PhpCsFixer\Finder::create();
$finder->in(__DIR__);
return (new PhpCsFixer\Config())
->setRiskyAllowed(true)
->setHideProgress(false)
->setRules([
'@PhpCsFixer' => true,
// '@PhpCsFixer:risky' => true,
'@PHP82Migration' => true,
'@PHP80Migration:risky' => true,
'@PSR12' => true,
'@PSR12:risky' => true,
'@PHPUnit100Migration:risky' => true,
'binary_operator_spaces' => [
'default' => 'align_single_space_minimal',
'operators' => [
'=' => 'align_single_space',
'=>' => 'align_single_space',
],
],
'types_spaces' => [
'space' => 'single',
'space_multiple_catch' => 'single',
],
// Annoyance-fixers:
'concat_space' => ['spacing' => 'one'], // This one is a matter of taste.
'no_superfluous_phpdoc_tags' => [
'allow_mixed' => false,
'allow_unused_params' => false,
'remove_inheritdoc' => true,
],
'yoda_style' => false, // Disabled as its annoying. Comes with @PhpCsFixer
'native_function_invocation' => false, // Disabled as adding count($i) -> \count($i) is annoying, but supposedly more performant
])
->setFinder($finder)
;

View file

@ -1,115 +0,0 @@
# checkov:skip=CKV_DOCKER_3 user cannot be determined at this stage.
FROM php:cli 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"
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
# ts:skip=AC_DOCKER_0002 Mis-detecting usage of apt instead of apt-get
# Install nginx, certbot
RUN apt-get -qq update && \
# Install pre-dependencies to use apt-key.
apt-get -yqq install --no-install-recommends \
lsb-core \
gnupg \
&& \
# Add nginx ppa
sh -c 'echo "deb http://ppa.launchpad.net/nginx/stable/ubuntu $(lsb_release -sc) main" \
> /etc/apt/sources.list.d/nginx-stable.list' && \
# Add nginx key
apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C300EE8C && \
# Update
apt-get -qq update && \
# Install Nginx, Certbot bits and apache2-utils for htpasswd generation
apt-get -yqq install --no-install-recommends \
nginx \
python3-certbot-nginx \
apache2-utils \
&& \
# Cleanup
apt-get remove -yqq \
lsb-core \
cups-common \
&& \
apt-get autoremove -yqq && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /var/lib/dpkg/status.old /var/cache/debconf/templates.dat /var/log/dpkg.log /var/log/lastlog /var/log/apt/*.log
# copy some default self-signed certs
COPY self-signed-certificates /certs
# Install runits for services
COPY nginx.runit /etc/service/nginx/run
#COPY logs.runit /etc/service/nginx-logs/run
#COPY logs.finish /etc/service/nginx-logs/finish
COPY bouncer.runit /etc/service/bouncer/run
COPY bouncer.finish /etc/service/bouncer/finish
#COPY logs-nginx-access.runit /etc/service/logs-nginx-access/run
#COPY logs-nginx-error.runit /etc/service/logs-nginx-error/run
RUN chmod +x /etc/service/*/run /etc/service/*/finish
# Copy default nginx bits
COPY NginxDefault /etc/nginx/sites-enabled/default.conf
COPY Nginx-tweak.conf /etc/nginx/conf.d/tweak.conf
# Disable daemonising in nginx
RUN sed -i '1s;^;daemon off\;\n;' /etc/nginx/nginx.conf && \
sed -i 's|include /etc/nginx/sites-enabled/*|include /etc/nginx/sites-enabled/*.conf|g' /etc/nginx/nginx.conf && \
rm /etc/nginx/sites-enabled/default && \
rm -R /etc/nginx/sites-available
# Copy over vendored code plus install just in case
COPY vendor /app/vendor
COPY composer.* /app/
RUN composer install
# Copy over application code
COPY public /app/public
COPY bin /app/bin
COPY src /app/src
COPY templates /app/templates
RUN chmod +x /app/bin/bouncer
# stuff some envs from build
ARG BUILD_DATE
ARG GIT_SHA
ARG GIT_BUILD_ID
ARG GIT_COMMIT_MESSAGE
ENV BUILD_DATE=${BUILD_DATE} \
GIT_SHA=${GIT_SHA} \
GIT_BUILD_ID=${GIT_BUILD_ID} \
GIT_COMMIT_MESSAGE=${GIT_COMMIT_MESSAGE}
# Create some volumes for logs and certs
VOLUME /etc/letsencrypt
VOLUME /var/log/bouncer
# Expose ports
EXPOSE 80
EXPOSE 443
# Set a healthcheck to curl the bouncer and expect a 200
HEALTHCHECK --start-period=30s \
CMD curl -s -o /dev/null -w "200" http://localhost:80/ || exit 1
RUN printenv | sort
# checkov:skip=CKV_DOCKER_3 user cannot be determined at this stage.
FROM php:nginx as test-app-a
COPY ./test/public-web-a /app/public
HEALTHCHECK --start-period=30s \
CMD curl -s -o /dev/null -w "200" http://localhost:80/ || exit 1
# checkov:skip=CKV_DOCKER_3 user cannot be determined at this stage.
FROM php:nginx as test-app-b
COPY ./test/public-web-b /app/public
HEALTHCHECK --start-period=30s \
CMD curl -s -o /dev/null -w "200" http://localhost:80/ || exit 1
# checkov:skip=CKV_DOCKER_3 user cannot be determined at this stage.
FROM php:nginx as test-app-c
COPY ./test/public-web-c /app/public
HEALTHCHECK --start-period=30s \
CMD curl -s -o /dev/null -w "200" http://localhost:80/ || exit 1

View file

@ -1,59 +0,0 @@
all: fix build-n-push
php-cs-fixer:
docker run --rm -v $(shell pwd):/data cytopia/php-cs-fixer fix
fix: php-cs-fixer
image_reference_devel:=ghcr.io/benzine-framework/bouncer:devel
build-n-push: fix
docker build \
--build-arg BUILD_DATE="$(shell date -u +"%Y-%m-%dT%H:%M:%SZ")" \
--build-arg GIT_SHA="$(shell git rev-parse HEAD)" \
--build-arg GIT_BUILD_ID="$(shell git rev-parse --abbrev-ref HEAD)-$(shell git describe --tags --dirty --always | sed -e 's/^v//')" \
--build-arg GIT_COMMIT_MESSAGE="$(shell git log -1 --pretty=%B | head -n1)" \
--build-context php:cli=docker-image://ghcr.io/benzine-framework/php:cli-8.2 \
--tag benzine/bouncer \
--tag $(image_reference_devel) \
--target bouncer \
--progress plain \
.
docker push $(image_reference_devel)
test-as-service: clean
docker build -t bouncer --target bouncer .
docker build -t test-app-a --target test-app-a .
docker build -t test-app-b --target test-app-b .
-docker network create --driver overlay bouncer-test
$(MAKE) start_bouncer
$(MAKE) start_test_a
$(MAKE) start_test_b
docker service logs -f bouncer
start_test_a:
docker service create \
--network bouncer-test \
--name test-app-a \
--env BOUNCER_DOMAIN=test-a.local \
--env BOUNCER_ALLOW_NON_SSL=yes \
--publish 8081:80 \
test-app-a
start_test_b:
docker service create \
--network bouncer-test \
--name test-app-b \
--env BOUNCER_DOMAIN=test-b.local \
--env BOUNCER_ALLOW_NON_SSL=yes \
--publish 8082:80 \
test-app-b
start_bouncer:
docker service create \
--network bouncer-test \
--name bouncer \
--publish 8080:80 \
--mount type=bind,destination=/var/run/docker.sock,source=/var/run/docker.sock \
bouncer
clean:
-docker service rm bouncer test-app-a test-app-b
#-docker network rm bouncer-test
#-docker image rm test-app-a test-app-b bouncer

View file

@ -1,2 +0,0 @@
server_names_hash_max_size 1024;
server_names_hash_bucket_size 256;

View file

@ -1,29 +0,0 @@
server {
listen 80 default_server;
listen [::]:80 default_server;
listen 443 default_server ssl;
listen [::]:443 default_server ssl;
client_max_body_size 1024M;
root /app/public;
index index.html index.htm;
ssl_certificate /certs/example.crt;
ssl_certificate_key /certs/example.key;
# ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# ssl_ciphers HIGH:!aNULL:!MD5;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
location ~ /\.ht {
deny all;
}
}

View file

@ -1,70 +0,0 @@
# Automatic Swarm Nginx Load Balancer
## Environment variables
This container has its own environment variables, AS WELL AS scanning for some environment variables associated with your services.
These should not be confused.
### Load Balancer Configuration
#### Main configuration
| Key | Default | Options | Behaviour |
| --------------------------------------------- | ----------------------------------------------- | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| DOCKER_HOST | false | | Define a http endpoint representing your docker socket. If this is null, it connects to /var/lib/docker.sock |
| GLOBAL_CERT | false | Contents of an ssl certificate | If you want to provide a single cert for all endpoints, perhaps with a catch-all that may be later overriden, you can provide the whole contents of a certificates file here. |
| GLOBAL_CERT_KEY | false | Contents of an ssl certificates private key | The private key related to GLOBAL CERT. These must be provided in tandem. |
| BOUNCER_FORCED_UPDATE_INTERVAL_SECONDS | 0 | positive numbers | To force the bouncer to update on a schedule even if no changes are detected, measured in seconds |
| BOUNCER_MAXIMUM_NGINX_CONFIG_CREATION_NOTICES | 15 | positive numbers | To limit the number lines of output regarding which domains have been configured. Any more domains than this count, and none will be output, instead replaced by "More than 15 Nginx configs generated.. Too many to show them all!" |
| BOUNCER_ALLOW_NON_SSL | Defaults to enabled. | Values are "yes" or "true", anything else is false | By default, should HTTP only traffic be allowed to hit this service? If disabled, http traffic is forwarded towards https. This can be over-ridden per-service with the same env. |
| LOG_NAME | bouncer | | The name of the log file to write to |
| LOG_FILE | /var/log/bouncer/bouncer.log | | The path to the log file to write to |
| LOG_LEVEL | debug | info, debug, critical etc | The level of logging to write to the log file. See Monolog docs. |
| LOG_LEVEL_NAME_LENGTH | 4 | positive numbers | The length of the level name to be written to the log file. See Monolog docs. |
| LOG_LINE_FORMAT | [%datetime%] %level_name%: %channel%: %message% | | The format of the log line. See Monolog docs. |
| LOG_COLOUR | true | true, false | Whether to colourise the log output sent to stdout. |
#### For using with Lets Encrypt:tm:
| Key | Default | Options | Behaviour |
| ------------------------- | --------- | ------------------------- | ------------------------------------------------------------------------------------ |
| BOUNCER_LETSENCRYPT_MODE | 'staging' | 'staging' or 'production' | Determine if this is going to connect to a production or staging Lets Encrypt server |
| BOUNCER_LETSENCRYPT_EMAIL | | 'bob@example.com' | Email address to associate with lets encrypt |
#### For using S3-compatable storage for generated cert synchronisation with Lets Encrypt
| Key | Default | Options | Behaviour |
| ---------------------------------- | ------- | --------------- | ------------------------------------------------------------------------------------- |
| BOUNCER_S3_BUCKET | false | | enable S3 behaviour to store lets-encrypt generated certs |
| BOUNCER_S3_ENDPOINT | false | | define s3 endpoint to override default AWS s3 implementation, for example, with minio |
| BOUNCER_S3_KEY_ID | false | | S3 API Key ID |
| BOUNCER_s3_KEY_SECRET | false | | S3 API Key Secret |
| BOUNCER_S3_REGION | false | | S3 API Region |
| BOUNCER_S3_USE_PATH_STYLE_ENDPOINT | false | `true or false` | Needed for minio |
| BOUNCER_S3_PREFIX | false | | Prefix file path in s3 bucket |
### Served Instance Configuration
These environment variables need to be applied to the CONSUMING SERVICE and not the loadbalancer container itself.
| Key | Example | Behaviour |
| ------------------------------ | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| BOUNCER_DOMAIN | "a.example.com" | The domain that should be directed to this container |
| BOUNCER_LABEL | "MyService" | The label that should be directed to this container |
| BOUNCER_IGNORE | not set | If set, the bouncer will ignore this service. Useful for services that are not ready to be exposed yet, or inherit BOUNCER_DOMAIN but need to be quashed. |
| BOUNCER_AUTH | "username:password" e.g "root:toor" | Add a HTTP BASIC auth requirement to this hostname. |
| BOUNCER_HOST_OVERRIDE | "localhost:80" | Override the host header that is sent to the service. Useful for services that are not aware of their own hostname, or annoying things like [mitmproxy](https://github.com/mitmproxy/mitmproxy/issues/3234) |
| BOUNCER_LETSENCRYPT | Values are "yes" or "true", anything else is false | To enable, or disable Lets Encrypt service for this hostname |
| BOUNCER_CERT | "-----BEGIN CERTIFICATE-----\nMIIFfTCCBGWgAwIBAgIQCgFBQgAAABAhQ..." | The contents of a custom certificate to use for this hostname. |
| BOUNCER_CERT_KEY | "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCB..." | The contents of a custom private key to use for this hostname. |
| BOUNCER_TARGET_PORT | 9000 | Explicitly define the port you want to hit the service on, in case of ambiguity |
| BOUNCER_ALLOW_NON_SSL | Defaults to enabled. Values are "yes" or "true", anything else is false | Should HTTP only traffic be allowed to hit this service? If disabled, http traffic is forwarded towards https |
| BOUNCER_ALLOW_WEBSOCKETS | Defaults to enabled. Values are "yes" or "true", anything else is false | Enable websocket behaviour |
| BOUNCER_ALLOW_LARGE_PAYLOADS | Defaults to disabled. | Allows overriding the default nginx payload size. Related to BOUNCER_MAX_PAYLOADS_MEGABYTES |
| BOUNCER_MAX_PAYLOADS_MEGABYTES | numbers | Size of max payload to allow, in megabytes. Requires BOUNCER_ALLOW_LARGE_PAYLOADS to be enabled |
| BOUNCER_PROXY_TIMEOUT_SECONDS | 60 | The timeout for the proxy to wait for a response from the service, in seconds. |
| BOUNCER_CUSTOM_NGINX_CONFIG | Contents of nginx config file, optionally base64 encoded | Allows you to provide a custom nginx config file for this service. This will entirely replace the default config for this service. This is hella dangerous. |
## Security considerations
If you're putting this behind access control to the docker socket, it will need access to the /swarm /services and /containers endpoints of the docker api.

View file

@ -1,8 +0,0 @@
#!/usr/bin/env php
<?php
use Bouncer\Bouncer;
define("APP_ROOT", realpath(__DIR__. "/.."));
require_once APP_ROOT . '/vendor/autoload.php';
(new Bouncer())->run();

View file

@ -1,2 +0,0 @@
#!/usr/bin/env bash
sleep 5

View file

@ -1,5 +0,0 @@
#!/usr/bin/env bash
sleep 3
printf "\n\n\n\n"
echo "Starting Bouncer"
/app/bin/bouncer

View file

@ -1,7 +0,0 @@
version: "2.4"
services:
bouncer:
build:
context: .
target: bouncer
image: ghcr.io/benzine-framework/bouncer:latest

View file

@ -1,47 +0,0 @@
{
"name": "benzine/bouncer",
"description": "Automated Docker-swarm aware Nginx configuration management",
"type": "project",
"config": {
"sort-packages": true
},
"license": "GPL-3.0-or-later",
"require": {
"php": "^8.1",
"ext-curl": "*",
"ext-json": "*",
"ext-openssl": "*",
"adambrett/shell-wrapper": "~1.0",
"bramus/monolog-colored-line-formatter": "~3.1",
"guzzlehttp/guzzle": "^7.8",
"kint-php/kint": "^3.3",
"league/flysystem": "^2.5",
"league/flysystem-aws-s3-v3": "^2.5",
"monolog/monolog": "^3.5",
"nesbot/carbon": "^2.72",
"phpspec/php-diff": "^1.1",
"spatie/emoji": "^2.3",
"symfony/yaml": "^6.4",
"twig/twig": "^3.8"
},
"authors": [
{
"name": "Matthew Baggett",
"email": "matthew@baggett.me"
}
],
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.46"
},
"autoload": {
"psr-4": {
"Bouncer\\": "src/"
}
},
"scripts": {
"fix": "php-cs-fixer fix"
},
"bin": [
"bin/bouncer"
]
}

3767
bouncer/composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,21 +0,0 @@
group "default" {
targets = [
"bouncer",
]
}
variable "PLATFORMS" {
default = [
"arm64",
"amd64",
]
}
target "bouncer" {
context = "."
dockerfile = "Dockerfile"
platforms = PLATFORMS
tags = [
"benzine/bouncer:latest",
"ghcr.io/benzine-framework/bouncer:latest",
]
target = "bouncer"
}

View file

@ -1,52 +0,0 @@
version: "3.5"
services:
bouncer:
image: ghcr.io/benzine-framework/bouncer:devel
build:
context: .
target: bouncer
additional_contexts:
- php:cli=docker-image://ghcr.io/benzine-framework/php:cli-8.2
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./:/app
# environment:
# - BOUNCER_LETSENCRYPT_MODE=staging
# - BOUNCER_LETSENCRYPT_EMAIL=matthew@baggett.me
# - BOUNCER_S3_ENDPOINT=http://grey.ooo:9000
# - BOUNCER_S3_KEY_ID=geusebio
# - BOUNCER_S3_KEY_SECRET=changeme
# - BOUNCER_S3_BUCKET=bouncer-certificates
# - BOUNCER_S3_USE_PATH_STYLE_ENDPOINT="yes"
ports:
- 127.0.99.100:80:80
- 127.0.99.100:443:443
web-a:
image: test-app-a
build:
context: .
target: test-app-a
additional_contexts:
- php:nginx=docker-image://ghcr.io/benzine-framework/php:nginx-8.2
volumes:
- ./test/public-web-a:/app/public
environment:
- BOUNCER_DOMAIN=a.web.grey.ooo
- BOUNCER_TARGET_PORT=80
# - BOUNCER_LETSENCRYPT=true
web-b:
image: test-app-b
build:
context: .
target: test-app-b
additional_contexts:
- php:nginx=docker-image://ghcr.io/benzine-framework/php:nginx-8.2
volumes:
- ./test/public-web-b:/app/public
environment:
- BOUNCER_DOMAIN=b.web.grey.ooo
- BOUNCER_TARGET_PORT=80
# - BOUNCER_LETSENCRYPT=true

View file

@ -1,3 +0,0 @@
#!/usr/bin/env bash
# shellcheck disable=SC2312
tail -f /var/log/nginx/access.log | sed --unbuffered 's|.*\[.*\] |[NGINX] |g' | grep -v /v1/ping

View file

@ -1,2 +0,0 @@
#!/usr/bin/env bash
tail -f /var/log/nginx/error.log

View file

@ -1,2 +0,0 @@
#!/usr/bin/env bash
sleep infinity

View file

@ -1,4 +0,0 @@
#!/usr/bin/env bash
if [[ -f /var/log/bouncer/bouncer.log ]]; then
tail -f /var/log/bouncer/bouncer.log
fi

View file

@ -1,3 +0,0 @@
#!/usr/bin/env bash
echo "Starting Nginx"
/usr/sbin/nginx

Binary file not shown.

Before

Width: 64px  |  Height: 64px  |  Size: 34 KiB

View file

@ -1,9 +0,0 @@
<html>
<head>
<title>👻 Nothing to see here!</title>
</head>
<body>
<h1>Oops!</h1>
<p>There's nothing here.</p>
</body>
</html>

View file

@ -1,22 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDnTCCAoWgAwIBAgIUJYUUmBQ2/ERZ7xinAJzVhiFWViYwDQYJKoZIhvcNAQEL
BQAwXTELMAkGA1UEBhMCVVMxEDAOBgNVBAgMB0Zsb3JpZGExDjAMBgNVBAcMBU1p
YW1pMRYwFAYDVQQKDA1FeGFtcGxlIEdyb3VwMRQwEgYDVQQDDAtleGFtcGxlLm9y
ZzAgFw0yMTA1MzAxNzU4MzlaGA8yMTIxMDUwNjE3NTgzOVowXTELMAkGA1UEBhMC
VVMxEDAOBgNVBAgMB0Zsb3JpZGExDjAMBgNVBAcMBU1pYW1pMRYwFAYDVQQKDA1F
eGFtcGxlIEdyb3VwMRQwEgYDVQQDDAtleGFtcGxlLm9yZzCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBANJa9OcoCW+mej8qDMCCTGnqMAuUqBIj1wZLgOdT
4DHriq1vKi1JLsDZkYekrCq/sfWo97kDXsdK6YN4+mua5EN4cTG3mSpal+RgLTc2
HMKHFfgzPzIN/n5AEqzdVZb5j0P3LoUNH687AlplW0BB+K64Gw//2KPx0Q8Fkhq2
I97V8SRpqds78PJHzhfuZNs/AUFpFXnYHJyO2Q63Btq2aoTMQyoLDRBBxin70II2
6Cjh3k6EhMY+HuYS1AjfI8cDQw289asJBLa6zPoD0VGaGNfCSrOzxrUqfhIoOkuY
W7rOIsK6rSSu1neSKQIiOLVjQxifxrQIIKTQhRiSplgD9LUCAwEAAaNTMFEwHQYD
VR0OBBYEFADK74w4AGeETK72k/htsnol9ye0MB8GA1UdIwQYMBaAFADK74w4AGeE
TK72k/htsnol9ye0MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
AKElv0xx95lD2leXEOfD6DKakrzuE8lONmcrkfjehTOd7jbqblnj8u1DCWytwB8P
gEr5FXve0iy7avGoNkU33MufbbQokAMoTs/IA+rwMfv0unupT1aYN8TTEXJJ100j
MXBsq/PvNkBNwkBcXjYHHsVjdM3bptbaw9A4V9opfMjQXAY5wuk3rBBm8On2rJKy
Qksh/uLoe8wbZ5dvLv9oc9sRpIilaSy8TcbrHkDIaWA5WCdVFfcayDGYdjhCYLGW
tj/48g0THvJv6JvVYwFJqTM690YUSlxaOHQE2ZneLytocVyAdEL2MMldRezvtI1z
1OXOia2G7koNYtS7cD8G1IM=
-----END CERTIFICATE-----

View file

@ -1,28 +0,0 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDSWvTnKAlvpno/
KgzAgkxp6jALlKgSI9cGS4DnU+Ax64qtbyotSS7A2ZGHpKwqv7H1qPe5A17HSumD
ePprmuRDeHExt5kqWpfkYC03NhzChxX4Mz8yDf5+QBKs3VWW+Y9D9y6FDR+vOwJa
ZVtAQfiuuBsP/9ij8dEPBZIatiPe1fEkaanbO/DyR84X7mTbPwFBaRV52BycjtkO
twbatmqEzEMqCw0QQcYp+9CCNugo4d5OhITGPh7mEtQI3yPHA0MNvPWrCQS2usz6
A9FRmhjXwkqzs8a1Kn4SKDpLmFu6ziLCuq0krtZ3kikCIji1Y0MYn8a0CCCk0IUY
kqZYA/S1AgMBAAECggEARqfQjPgwuzTi6OZ55AugGQ9VVf53uagaKH4h7RGKQ5pH
OVwWgaGMN7CcpkAUqEM9RjOcCaPtKOmrp8Jx8sTTGSqScs2lf8lwLYB0j4/4dwqi
wXyNJIX4znU9EJ1Di3OFwKF9Gam/077xWmWjEeFW43DpfiVEokSuIOqRGbHGOKlt
2ygHJu+rmPapEPyYqSWQnAkYX0DW/KCAGiyIAqph/SgrCDTdsxbNOa2OwDygPC54
7xW0yCduvgFLh9bxedF8iifzRkPw710cxyqVsYwHiwugDgxL4NiK1DlWbpBimab5
ocye9+ElymMZ8DTjpA85cXny/TtoqJfqTs1YGYgrvQKBgQDwHnAcY0BjQ4o+ZneG
oqBJeQ8KCMRU4pEIa5QOOeUr46gtiPIfcFh/BJUHQ61qk7gcJj5BV2GXNS7+m+sU
RC3Usblm9twwxZn7mfoOk4z9NEfBI2MXmbB8ARjAQBCost+3KQAoSIL1AyDKiAlY
2JfMt+73+kwUsg7b9g0pYIfn/wKBgQDgRJPlSIxJs2mbjzUwVBAeslct2W0dehrh
V0sXPxEhJHWX6P343vLqRHRsKgqhbU/vy+3JrIS9ftwGKcmb+Y9EJgYrR+D3ZYzs
idSOsunSspJgbCG5mHE1VQhr8IpHeCkuSt22aFErLfsjzXWZIewK2tqZN1QUjdc5
EJHOD4UDSwKBgFYRYvgZ72NlOzFAw0kkE7YiSWy8Vbtjdr8A6JHs2KNRt9+Sfc8d
Eut8dfqjnI5eIpkccCY1rwpnCtBCjRG3moHprl4k0Co/OgGAYKxG4TuFOM8W4xb7
hNH+BqQqko4Vh7D8Zk0KKL6v/1n5RvhssoSzzVlfg1PLux3G5VLWggB7AoGAAP/N
OORN27Y07kCBGCoHuFtLECU72znEDOT6rKvXQ7KJ45diKk2z/182tZSqX3XBOWxL
Lu7Z2I5MJKri/xLplIAm3uJ/GhsVuagTjl81s36gMFXLAKyxNG+gjfqQYykh5dbn
jfyBABRAXjR4JaqFBrda6fvZIA5RuytbuvNOwGkCgYAUs82tDGLiqyMPd2jgYS3k
aL62f0TLKHjmTCmRca7IqXbqcMbAj+LgAHI2HfCfjc4KWd68ZGRLcpDlehMcis1f
PQi3HW+2b9dAZX6+HAIGiVem//ckYXgUza4MMosh0hXquGs1yJ/VNWC+HPIHrj6X
9tvvvHnGKav329q/Z/8K/A==
-----END PRIVATE KEY-----

View file

@ -1,954 +0,0 @@
<?php
declare(strict_types=1);
namespace Bouncer;
use AdamBrett\ShellWrapper\Command\Builder as CommandBuilder;
use AdamBrett\ShellWrapper\Runners\Exec;
use Aws\S3\S3Client;
use GuzzleHttp\Client as Guzzle;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\ServerException;
use League\Flysystem\AwsS3V3\AwsS3V3Adapter;
use League\Flysystem\FileAttributes;
use League\Flysystem\Filesystem;
use League\Flysystem\FilesystemException;
use League\Flysystem\Local\LocalFilesystemAdapter;
use Monolog\Logger;
use Bouncer\Logger\Formatter;
use Spatie\Emoji\Emoji;
use Symfony\Component\Yaml\Yaml;
use Twig\Environment as Twig;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
use Twig\Loader\FilesystemLoader as TwigLoader;
use GuzzleHttp\Exception\GuzzleException;
use Monolog\Processor;
use Bouncer\Settings\Settings;
class Bouncer
{
private array $environment;
private Guzzle $docker;
private TwigLoader $loader;
private Twig $twig;
private Filesystem $configFilesystem;
private Filesystem $certificateStoreLocal;
private ?Filesystem $certificateStoreRemote = null;
private Filesystem $providedCertificateStore;
private Logger $logger;
private array $previousContainerState = [];
private array $previousSwarmState = [];
private array $fileHashes;
private bool $swarmMode = false;
private bool $useGlobalCert = false;
private int $forcedUpdateIntervalSeconds = 0;
private ?int $lastUpdateEpoch = null;
private int $maximumNginxConfigCreationNotices = 15;
private Settings $settings;
private const DEFAULT_DOCKER_SOCKET = '/var/run/docker.sock';
private const FILESYSTEM_CONFIG_DIR = '/etc/nginx/sites-enabled';
private const FILESYSTEM_CERTS_DIR = '/etc/nginx/certs';
private const FILESYSTEM_CERTS_PROVIDED_DIR = '/certs';
public function __construct()
{
$this->environment = array_merge($_ENV, $_SERVER);
ksort($this->environment);
$this->settings = new Settings();
$this->logger = new \Bouncer\Logger\Logger(
settings: $this->settings,
processIdProcessor: new Processor\ProcessIdProcessor(),
memoryPeakUsageProcessor: new Processor\MemoryPeakUsageProcessor(),
psrLogMessageProcessor: new Processor\PsrLogMessageProcessor(),
coloredLineFormatter: new Formatter\ColourLine($this->settings),
lineFormatter: new Formatter\Line($this->settings),
);
if (isset($this->environment['DOCKER_HOST'])) {
$this->logger->info('Connecting to {docker_host}', ['emoji' => Emoji::electricPlug(), 'docker_host' => $this->environment['DOCKER_HOST']]);
$this->docker = new Guzzle(['base_uri' => $this->environment['DOCKER_HOST']]);
} else {
$this->logger->info('Connecting to {docker_host}', ['emoji' => Emoji::electricPlug(), 'docker_host' => Bouncer::DEFAULT_DOCKER_SOCKET]);
$this->docker = new Guzzle(['base_uri' => 'http://localhost', 'curl' => [CURLOPT_UNIX_SOCKET_PATH => Bouncer::DEFAULT_DOCKER_SOCKET]]);
}
$this->loader = new TwigLoader([__DIR__ . '/../templates']);
$this->twig = new Twig($this->loader);
// Set up Filesystem for sites-enabled path
$this->configFilesystem = new Filesystem(new LocalFilesystemAdapter(Bouncer::FILESYSTEM_CONFIG_DIR));
// Set up Local certificate store
$this->certificateStoreLocal = new Filesystem(new LocalFilesystemAdapter(Bouncer::FILESYSTEM_CERTS_DIR));
// Set up Local certificate store for certificates provided to us
$this->providedCertificateStore = new Filesystem(new LocalFilesystemAdapter(Bouncer::FILESYSTEM_CERTS_PROVIDED_DIR));
// Set up Remote certificate store, if configured
if (isset($this->environment['BOUNCER_S3_BUCKET'])) {
$this->certificateStoreRemote = new Filesystem(
new AwsS3V3Adapter(
new S3Client([
'endpoint' => $this->environment['BOUNCER_S3_ENDPOINT'],
'use_path_style_endpoint' => isset($this->environment['BOUNCER_S3_USE_PATH_STYLE_ENDPOINT']),
'credentials' => [
'key' => $this->environment['BOUNCER_S3_KEY_ID'],
'secret' => $this->environment['BOUNCER_S3_KEY_SECRET'],
],
'region' => $this->environment['BOUNCER_S3_REGION'] ?? 'us-east',
'version' => 'latest',
]),
$this->environment['BOUNCER_S3_BUCKET'],
$this->environment['BOUNCER_S3_PREFIX'] ?? ''
)
);
}
}
public function getMaximumNginxConfigCreationNotices(): int
{
return $this->maximumNginxConfigCreationNotices;
}
public function setMaximumNginxConfigCreationNotices(int $maximumNginxConfigCreationNotices): Bouncer
{
$this->maximumNginxConfigCreationNotices = $maximumNginxConfigCreationNotices;
return $this;
}
public function isSwarmMode(): bool
{
return $this->swarmMode;
}
public function setSwarmMode(bool $swarmMode): Bouncer
{
$this->swarmMode = $swarmMode;
return $this;
}
public function isUseGlobalCert(): bool
{
return $this->useGlobalCert;
}
public function setUseGlobalCert(bool $useGlobalCert): Bouncer
{
$this->useGlobalCert = $useGlobalCert;
return $this;
}
public function getForcedUpdateIntervalSeconds(): int
{
return $this->forcedUpdateIntervalSeconds;
}
public function setForcedUpdateIntervalSeconds(int $forcedUpdateIntervalSeconds): Bouncer
{
$this->forcedUpdateIntervalSeconds = $forcedUpdateIntervalSeconds;
return $this;
}
/**
* @return Target[]
*
* @throws GuzzleException
*/
public function findContainersContainerMode(): array
{
$bouncerTargets = [];
$containers = json_decode($this->docker->request('GET', 'containers/json')->getBody()->getContents(), true);
foreach ($containers as $container) {
$envs = [];
$container = json_decode($this->docker->request('GET', "containers/{$container['Id']}/json")->getBody()->getContents(), true);
if (
!isset($container['Config']['Env'])
) {
continue;
}
// Parse all the environment variables and store them in an array.
foreach ($container['Config']['Env'] as $env) {
[$envKey, $envVal] = explode('=', $env, 2);
if (str_starts_with($envKey, 'BOUNCER_')) {
$envs[$envKey] = $envVal;
}
}
ksort($envs);
// If there are no BOUNCER_* environment variables, skip this service.
if (count($envs) == 0) {
continue;
}
// If BOUNCER_IGNORE is set, skip this service.
if (isset($envs['BOUNCER_IGNORE'])) {
continue;
}
if (isset($envs['BOUNCER_DOMAIN'])) {
$bouncerTarget = (new Target(
logger: $this->logger,
settings: $this->settings,
))
->setId($container['Id'])
;
$bouncerTarget = $this->parseContainerEnvironmentVariables($envs, $bouncerTarget);
if (!empty($container['NetworkSettings']['IPAddress'])) {
// As per docker service
$bouncerTarget->setEndpointHostnameOrIp($container['NetworkSettings']['IPAddress']);
} else {
// As per docker compose
$networks = array_values($container['NetworkSettings']['Networks']);
$bouncerTarget->setEndpointHostnameOrIp($networks[0]['IPAddress']);
}
$bouncerTarget->setTargetPath(sprintf('http://%s:%d', $bouncerTarget->getEndpointHostnameOrIp(), $bouncerTarget->getPort() >= 0 ? $bouncerTarget->getPort() : 80));
$bouncerTarget->setUseGlobalCert($this->isUseGlobalCert());
$valid = $bouncerTarget->isEndpointValid();
// $this->logger->debug(sprintf(
// '%s Decided that %s has the endpoint %s and it %s.',
// Emoji::magnifyingGlassTiltedLeft(),
// $bouncerTarget->getName(),
// $bouncerTarget->getEndpointHostnameOrIp(),
// $valid ? 'is valid' : 'is not valid'
// ));
if ($valid) {
$bouncerTargets[] = $bouncerTarget;
}
}
}
return $bouncerTargets;
}
public function findContainersSwarmMode(): array
{
$bouncerTargets = [];
$services = json_decode($this->docker->request('GET', 'services')->getBody()->getContents(), true);
if (isset($services['message'])) {
$this->logger->debug('Something happened while interrogating services.. This node is not a swarm node, cannot have services: {message}', ['emoji' => Emoji::warning() . ' ', 'message' => $services['message']]);
} else {
foreach ($services as $service) {
$envs = [];
if (
!isset($service['Spec'])
|| !isset($service['Spec']['TaskTemplate'])
|| !isset($service['Spec']['TaskTemplate']['ContainerSpec'])
|| !isset($service['Spec']['TaskTemplate']['ContainerSpec']['Env'])
) {
continue;
}
// Parse all the environment variables and store them in an array.
foreach ($service['Spec']['TaskTemplate']['ContainerSpec']['Env'] as $env) {
[$envKey, $envVal] = explode('=', $env, 2);
if (str_starts_with($envKey, 'BOUNCER_')) {
$envs[$envKey] = $envVal;
}
}
ksort($envs);
// If there are no BOUNCER_* environment variables, skip this service.
if (count($envs) == 0) {
continue;
}
// if BOUNCER_IGNORE is set, skip this service.
if (isset($envs['BOUNCER_IGNORE'])) {
continue;
}
$bouncerTarget = (new Target(
logger: $this->logger,
settings: $this->settings,
));
if (isset($envs['BOUNCER_LABEL'])) {
$bouncerTarget->setLabel($envs['BOUNCER_LABEL']);
}
if (isset($envs['BOUNCER_DOMAIN'])) {
$bouncerTarget->setId($service['ID']);
$bouncerTarget->setLabel($service['Spec']['Name']);
$bouncerTarget = $this->parseContainerEnvironmentVariables($envs, $bouncerTarget);
if ($bouncerTarget->hasCustomNginxConfig()) {
$this->logger->info('Custom nginx config for {label} is provided.', ['emoji' => Emoji::artistPalette(), 'label' => $bouncerTarget->getLabel()]);
$bouncerTargets[] = $bouncerTarget;
continue;
}
if ($bouncerTarget->isPortSet()) {
$bouncerTarget->setEndpointHostnameOrIp($service['Spec']['Name']);
// $this->logger->info('{label}: Ports for {target_name} has been explicitly set to {host}:{port}.', ['emoji' => Emoji::warning().' ', 'target_name' => $bouncerTarget->getName(), 'host' => $bouncerTarget->getEndpointHostnameOrIp(), 'port' => $bouncerTarget->getPort()]);
} elseif (isset($service['Endpoint']['Ports'])) {
$bouncerTarget->setEndpointHostnameOrIp('172.17.0.1');
$bouncerTarget->setPort(intval($service['Endpoint']['Ports'][0]['PublishedPort']));
} else {
$this->logger->warning('{label}: ports block missing for {target_name}. Try setting BOUNCER_TARGET_PORT.', ['emoji' => Emoji::warning() . ' ', 'label' => $bouncerTarget->getLabel(), 'target_name' => $bouncerTarget->getName()]);
\Kint::dump(
$bouncerTarget->getId(),
$bouncerTarget->getLabel(),
$envs
);
continue;
}
$bouncerTarget->setTargetPath(sprintf('http://%s:%d', $bouncerTarget->getEndpointHostnameOrIp(), $bouncerTarget->getPort()));
$bouncerTarget->setUseGlobalCert($this->isUseGlobalCert());
if ($bouncerTarget->isEndpointValid() || $bouncerTarget->hasCustomNginxConfig()) {
$bouncerTargets[] = $bouncerTarget;
} else {
$this->logger->debug(
'Decided that {target_name} has the endpoint {endpoint} and it is not valid.',
[
'emoji' => Emoji::magnifyingGlassTiltedLeft(),
'target_name' => $bouncerTarget->getName(),
'endpoint' => $bouncerTarget->getEndpointHostnameOrIp(),
]
);
}
}
}
}
return $bouncerTargets;
}
public function run(): void
{
$this->logger->info('Starting Bouncer. Built {build_id} on {build_date}, {build_ago}', ['emoji' => Emoji::redHeart() . ' ', 'build_id' => $this->settings->get('build/id'), 'build_date' => $this->settings->get('build/date')->toDateTimeString(), 'build_ago' => $this->settings->get('build/date')->ago()]);
$this->logger->info('Build #{git_sha}: "{build_message}"', ['emoji' => Emoji::memo(), 'git_sha' => $this->settings->get('build/sha_short'), 'build_message' => $this->settings->get('build/message')]);
$this->logger->debug(' > HTTPS Listener is on {https_port}', ['emoji' => Emoji::ship(), 'https_port' => $this->settings->get('bouncer/https_port')]);
$this->logger->debug(' > HTTP Listener is on {http_port}', ['emoji' => Emoji::ship(), 'http_port' => $this->settings->get('bouncer/http_port')]);
// Allow defined global cert if set
if ($this->settings->has('ssl/global_cert') && $this->settings->has('ssl/global_cert_key')) {
$this->setUseGlobalCert(true);
$this->providedCertificateStore->write('global.crt', str_replace('\\n', "\n", trim($this->settings->get('ssl/global_cert'), '"')));
$this->providedCertificateStore->write('global.key', str_replace('\\n', "\n", trim($this->settings->get('ssl/global_cert_key'), '"')));
}
$this->logger->debug(' > Global Cert is {enabled}', ['emoji' => Emoji::globeShowingEuropeAfrica(), 'enabled' => $this->isUseGlobalCert() ? 'enabled' : 'disabled']);
// Determine forced update interval.
if ($this->settings->has('bouncer/forced_update_interval_seconds')) {
$this->setForcedUpdateIntervalSeconds($this->settings->get('bouncer/forced_update_interval_seconds'));
}
$this->logger->debug(' > Forced Update Interval is {state}', ['emoji' => Emoji::watch(), 'state' => $this->getForcedUpdateIntervalSeconds() > 0 ? $this->getForcedUpdateIntervalSeconds() : 'disabled']);
// Determine maximum notices for nginx config creation.
if ($this->settings->has('bouncer/max_nginx_config_creation_notices')) {
$maxConfigCreationNotices = $this->settings->get('bouncer/max_nginx_config_creation_notices');
$originalMaximumNginxConfigCreationNotices = $this->getMaximumNginxConfigCreationNotices();
$this->setMaximumNginxConfigCreationNotices($maxConfigCreationNotices);
$this->logger->debug(' > Maximum Nginx config creation notices has been over-ridden: {original} => {new}', ['emoji' => Emoji::hikingBoot(), 'original' => $originalMaximumNginxConfigCreationNotices, 'new' => $this->getMaximumNginxConfigCreationNotices()]);
}
// State if non-SSL is allowed. This is processed in the Target class.
$this->logger->debug(' > Allow non-SSL is {enabled}', ['emoji' => Emoji::ship(), 'enabled' => $this->settings->get('ssl/allow_non_ssl') ? 'enabled' : 'disabled']);
try {
$this->stateHasChanged();
} catch (ConnectException $connectException) {
$this->logger->critical('Could not connect to docker socket! Did you forget to map it?', ['emoji' => Emoji::cryingCat()]);
exit(1);
}
while (true) {
$this->runLoop();
}
}
public function parseContainerEnvironmentVariables(array $envs, Target $bouncerTarget): Target
{
// Process label and name specifically before all else.
foreach (array_filter($envs) as $envKey => $envVal) {
switch ($envKey) {
case 'BOUNCER_LABEL':
$bouncerTarget->setLabel($envVal);
break;
case 'BOUNCER_DOMAIN':
$domains = explode(',', $envVal);
array_walk($domains, function (&$domain, $key): void {
$domain = trim($domain);
});
$bouncerTarget->setDomains($domains);
break;
}
}
foreach (array_filter($envs) as $envKey => $envVal) {
switch ($envKey) {
case 'BOUNCER_AUTH':
[$username, $password] = explode(':', $envVal);
$bouncerTarget->setAuth($username, $password);
// $this->logger->info('{label}: Basic Auth has been enabled.', ['emoji' => Emoji::key(), 'label' => $bouncerTarget->getLabel(),]);
break;
case 'BOUNCER_HOST_OVERRIDE':
$bouncerTarget->setHostOverride($envVal);
$this->logger->warning('{label}: Host reported to container overridden and set to {host_override}.', ['emoji' => Emoji::hikingBoot(), 'label' => $bouncerTarget->getLabel(), 'host_override' => $bouncerTarget->getHostOverride()]);
break;
case 'BOUNCER_LETSENCRYPT':
$bouncerTarget->setLetsEncrypt(in_array(strtolower($envVal), ['yes', 'true'], true));
break;
case 'BOUNCER_CERT':
$bouncerTarget->setCustomCert($envVal);
$this->logger->info('{label}: Custom cert specified', ['emoji' => Emoji::locked(), 'label' => $bouncerTarget->getLabel()]);
break;
case 'BOUNCER_CERT_KEY':
$bouncerTarget->setCustomCertKey($envVal);
break;
case 'BOUNCER_TARGET_PORT':
$bouncerTarget->setPort(intval($envVal));
// $this->logger->info('{label}: Target port set to {port}.', ['emoji' => Emoji::ship(), 'label' => $bouncerTarget->getLabel(), 'port' => $bouncerTarget->getPort(),]);
break;
case 'BOUNCER_ALLOW_NON_SSL':
$bouncerTarget->setAllowNonSSL(in_array(strtolower($envVal), ['yes', 'true'], true));
break;
case 'BOUNCER_ALLOW_WEBSOCKETS':
$bouncerTarget->setAllowWebsocketSupport(in_array(strtolower($envVal), ['yes', 'true'], true));
break;
case 'BOUNCER_ALLOW_LARGE_PAYLOADS':
$bouncerTarget->setAllowLargePayloads(in_array(strtolower($envVal), ['yes', 'true'], true));
break;
case 'BOUNCER_PROXY_TIMEOUT_SECONDS':
$bouncerTarget->setProxyTimeoutSeconds(is_numeric($envVal) ? intval($envVal) : null);
break;
case 'BOUNCER_CUSTOM_NGINX_CONFIG':
// If envval is base64 encoded, decode it first
if (preg_match('/^[a-zA-Z0-9\/\r\n+]*={0,2}$/', $envVal)) {
$envVal = base64_decode($envVal);
}
$this->logger->info('Custom nginx config for {label} is provided.', ['emoji' => Emoji::artistPalette(), 'label' => $bouncerTarget->getLabel()]);
$bouncerTarget->setCustomNginxConfig($envVal);
break;
}
}
return $bouncerTarget;
}
private function dockerGetContainers(): array
{
return json_decode($this->docker->request('GET', 'containers/json')->getBody()->getContents(), true);
}
private function dockerGetContainer(string $id): array
{
return json_decode($this->docker->request('GET', "containers/{$id}/json")->getBody()->getContents(), true);
}
private function dockerEnvHas(string $key, ?array $envs): bool
{
if ($envs === null) {
return false;
}
foreach ($envs as $env) {
if (stripos($env, '=') !== false) {
[$envKey, $envVal] = explode('=', $env, 2);
if ($envKey === $key) {
return true;
}
}
}
return false;
}
private function dockerEnvFilter(?array $envs): array
{
if ($envs === null) {
return [];
}
$envs = array_filter(array_map(function ($env) {
if (stripos($env, '=') !== false) {
[$envKey, $envVal] = explode('=', $env, 2);
if (strlen($envVal) > 65) {
return sprintf('%s=CRC32(%s)', $envKey, crc32($envVal));
}
return sprintf('%s=%s', $envKey, $envVal);
}
return $env;
}, $envs));
sort($envs);
return $envs;
}
/**
* Returns true when something has changed.
*
* @throws GuzzleException
*/
private function stateHasChanged(): bool
{
$isTainted = false;
if ($this->lastUpdateEpoch === null) {
$isTainted = true;
} elseif ($this->forcedUpdateIntervalSeconds > 0 && $this->lastUpdateEpoch <= time() - $this->forcedUpdateIntervalSeconds) {
$this->logger->warning('Forced update interval of {interval_seconds} seconds has been reached, forcing update.', ['emoji' => Emoji::watch(), 'interval_seconds' => $this->forcedUpdateIntervalSeconds]);
$isTainted = true;
} elseif ($this->previousContainerState === []) {
$this->logger->warning('Initial state has not been set, forcing update.', ['emoji' => Emoji::watch()]);
$isTainted = true;
} elseif ($this->previousSwarmState === []) {
$this->logger->warning('Initial swarm state has not been set, forcing update.', ['emoji' => Emoji::watch()]);
$isTainted = true;
}
// Standard Containers
$newContainerState = [];
$containers = $this->dockerGetContainers();
foreach ($containers as $container) {
$inspect = $this->dockerGetContainer($container['Id']);
$name = ltrim($inspect['Name'], '/');
$env = $inspect['Config']['Env'] ?? [];
// if (!$this->dockerEnvHas('BOUNCER_DOMAIN', $env)) {
// continue;
// }
$newContainerState[$name] = [
'name' => $name,
'created' => $inspect['Created'],
'image' => $inspect['Image'],
'status' => $inspect['State']['Status'],
'env' => $this->dockerEnvFilter($env),
];
if (is_array($newContainerState[$name]['env'])) {
sort($newContainerState[$name]['env']);
}
}
ksort($newContainerState);
// Calculate Container State Hash
$containerStateDiff = $this->diff($this->previousContainerState, $newContainerState);
if (!$isTainted && !empty($containerStateDiff)) {
if ($this->settings->if('logger/show_state_deltas')) {
$this->logger->warning('Container state has changed', ['emoji' => Emoji::warning() . ' ']);
echo $containerStateDiff;
}
$isTainted = true;
}
$this->previousContainerState = $newContainerState;
// Swarm Services
$newSwarmState = [];
if ($this->isSwarmMode()) {
$services = json_decode($this->docker->request('GET', 'services')->getBody()->getContents(), true);
if (isset($services['message'])) {
$this->logger->warning('Something happened while interrogating services.. This node is not a swarm node, cannot have services: {message}', ['emoji' => Emoji::warning() . ' ', 'message' => $services['message']]);
} else {
foreach ($services as $service) {
$name = $service['Spec']['Name'];
$env = $service['Spec']['TaskTemplate']['ContainerSpec']['Env'] ?? [];
// if (!$this->dockerEnvHas('BOUNCER_DOMAIN', $env)) {
// continue;
// }
$newSwarmState[$name] = [
'id' => $service['ID'],
'mode' => isset($service['Spec']['Mode']['Replicated']) ?
sprintf('replicated:%d', $service['Spec']['Mode']['Replicated']['Replicas']) :
(isset($service['Spec']['Mode']['Global']) ? 'global' : 'none'),
'created' => $service['CreatedAt'],
'image' => $service['Spec']['TaskTemplate']['ContainerSpec']['Image'],
'versionIndex' => $service['Version']['Index'],
'updateStatus' => $service['UpdateStatus']['State'] ?? 'unknown',
'env' => $this->dockerEnvFilter($env),
];
}
}
}
ksort($newSwarmState);
// Calculate Swarm State Hash, if applicable
$swarmStateDiff = $this->diff($this->previousSwarmState, $newSwarmState);
if ($this->isSwarmMode() && !$isTainted && !empty($swarmStateDiff)) {
if ($this->settings->if('logger/show_state_deltas')) {
$this->logger->warning('Swarm state has changed', ['emoji' => Emoji::warning() . ' ']);
echo $swarmStateDiff;
}
$isTainted = true;
}
$this->previousSwarmState = $newSwarmState;
return $isTainted;
}
private function diff($a, $b)
{
return (new \Diff(
explode(
"\n",
Yaml::dump(input: $a, inline: 5, indent: 2)
),
explode(
"\n",
Yaml::dump(input: $b, inline: 5, indent: 2)
)
))->render(new \Diff_Renderer_Text_Unified());
}
private function runLoop(): void
{
if ($this->s3Enabled()) {
$this->getCertificatesFromS3();
}
try {
$determineSwarmMode = json_decode($this->docker->request('GET', 'swarm')->getBody()->getContents(), true);
$this->setSwarmMode(!isset($determineSwarmMode['message']));
} catch (ServerException $exception) {
$this->setSwarmMode(false);
} catch (ConnectException $exception) {
$this->logger->critical('Unable to connect to docker socket!', ['emoji' => Emoji::warning() . ' ']);
$this->logger->critical($exception->getMessage());
exit(1);
}
$this->logger->debug(' > Swarm mode is {enabled}.', ['emoji' => Emoji::honeybee(), 'enabled' => $this->isSwarmMode() ? 'enabled' : 'disabled']);
$targets = array_values(
array_merge(
$this->findContainersContainerMode(),
$this->isSwarmMode() ? $this->findContainersSwarmMode() : []
)
);
// Use some bs to sort the targets by domain from right to left.
$sortedTargets = [];
foreach ($targets as $target) {
$sortedTargets[strrev($target->getName())] = $target;
}
ksort($sortedTargets);
$targets = array_values($sortedTargets);
// Re-generate nginx configs
$this->logger->info('Found {num_services} services with BOUNCER_DOMAIN set', ['emoji' => Emoji::magnifyingGlassTiltedLeft(), 'num_services' => count($targets)]);
$this->generateNginxConfigs($targets);
$this->generateLetsEncryptCerts($targets);
if ($this->s3Enabled()) {
$this->writeCertificatesToS3();
}
// if any of the targets has requiresForcedScanning set to true, we need to force an update
if (array_reduce($targets, fn ($carry, $target) => $carry || $target->requiresForcedScanning(), false)) {
$this->logger->warning('Forcing an update in 5 seconds because one or more targets require it.', ['emoji' => Emoji::warning()]);
sleep(5);
return;
}
// Wait for next change
$this->waitUntilContainerChange();
}
private function waitUntilContainerChange(): void
{
while ($this->stateHasChanged() === false) {
sleep(5);
}
$this->lastUpdateEpoch = time();
}
private function s3Enabled(): bool
{
return $this->certificateStoreRemote instanceof Filesystem;
}
private function getCertificatesFromS3(): void
{
$this->logger->info(sprintf('%s Downloading Certificates from S3', Emoji::CHARACTER_DOWN_ARROW));
foreach ($this->certificateStoreRemote->listContents('/', true) as $file) {
/** @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));
continue;
}
$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())));
}
$this->fileHashes[$localPath] = sha1($this->certificateStoreLocal->read($localPath));
}
}
// Copy certs into /live because certbot is a pain.
foreach ($this->certificateStoreLocal->listContents('/archive', true) as $newLocalCert) {
/** @var FileAttributes $newLocalCert */
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);
}
$this->logger->debug(sprintf(' > Mirroring %s to %s (%d bytes)', $newLocalCert->path(), $livePath, $newLocalCert->fileSize()));
$this->certificateStoreLocal->writeStream($livePath, $this->certificateStoreLocal->readStream($newLocalCert->path()));
}
}
}
private function fileChanged(string $localPath)
{
if (!isset($this->fileHashes[$localPath])) {
return true;
}
if (sha1($this->certificateStoreLocal->read($localPath)) != $this->fileHashes[$localPath]) {
return true;
}
return false;
}
private function writeCertificatesToS3(): void
{
$this->logger->info('Uploading Certificates to S3', ['emoji' => Emoji::CHARACTER_UP_ARROW]);
foreach ($this->certificateStoreLocal->listContents('/archive', true) as $file) {
/** @var FileAttributes $file */
if ($file->isFile()) {
$remotePath = str_replace('archive/', '', $file->path());
if ($file->fileSize() == 0) {
$this->logger->warning(' > Skipping uploading {file}, file is garbage (empty).', ['file' => $file->path()]);
} elseif (!$this->certificateStoreRemote->fileExists($remotePath) || $this->fileChanged($file->path())) {
$this->logger->debug(' > Uploading {file} ({bytes} bytes)', ['file' => $file->path(), 'bytes' => $file->fileSize()]);
$this->certificateStoreRemote->write($remotePath, $this->certificateStoreLocal->read($file->path()));
} else {
$this->logger->debug(' > Skipping uploading {file}, file not changed.', ['file' => $file->path()]);
}
}
}
}
/**
* @param $targets Target[]
*/
private function generateNginxConfigs(array $targets): void
{
$changedTargets = [];
foreach ($targets as $target) {
if ($this->generateNginxConfig($target)) {
$changedTargets[strrev($target->getName())] = $target;
}
}
// @var Target[] $changedTargets
ksort($changedTargets);
$changedTargets = array_values($changedTargets);
if (count($changedTargets) <= $this->getMaximumNginxConfigCreationNotices()) {
/** @var Target $target */
foreach ($changedTargets as $target) {
$context = [
'label' => $target->getLabel(),
'domain' => $target->getPresentationdomain(),
'file' => $target->getNginxConfigFileName(),
'config_dir' => Bouncer::FILESYSTEM_CONFIG_DIR,
];
$this->logger->info('Created {label}', $context + ['emoji' => Emoji::pencil() . ' ']);
$this->logger->debug(' -> {config_dir}/{file}', $context + ['emoji' => Emoji::pencil() . ' ']);
$this->logger->debug(' -> {domain}', $context + ['emoji' => Emoji::pencil() . ' ']);
$this->logger->critical('{label} cert type is {cert_type}', $context + ['emoji' => Emoji::catFace(), 'cert_type' => $target->getTypeCertInUse()->name]);
}
} else {
$this->logger->info('More than {num_max} Nginx configs generated.. Too many to show them all!', ['emoji' => Emoji::pencil() . ' ', 'num_max' => $this->getMaximumNginxConfigCreationNotices()]);
}
$this->logger->info('Updated {num_created} Nginx configs, {num_changed} changed..', ['emoji' => Emoji::pencil() . ' ', 'num_created' => count($targets), 'num_changed' => count($changedTargets)]);
$this->pruneNonExistentConfigs($targets);
}
/**
* @param $targets Target[]
*
* @throws FilesystemException
*/
protected function pruneNonExistentConfigs(array $targets): void
{
$expectedFiles = [
'default.conf',
];
foreach ($targets as $target) {
$expectedFiles = array_merge($expectedFiles, $target->getExpectedFiles());
}
foreach ($this->configFilesystem->listContents('/') as $file) {
if (!in_array($file['path'], $expectedFiles)) {
$this->logger->info('Removing {file}', ['emoji' => Emoji::wastebasket(), 'file' => $file['path']]);
$this->configFilesystem->delete($file['path']);
}
}
}
/**
* @throws FilesystemException
* @throws LoaderError
* @throws RuntimeError
* @throws SyntaxError
*/
private function generateNginxConfig(Target $target): bool
{
$configData = $target->hasCustomNginxConfig() ? $target->getCustomNginxConfig() : $this->twig->render('NginxTemplate.twig', $target->__toArray());
$changed = false;
$configFileHash = $this->configFilesystem->fileExists($target->getNginxConfigFileName()) ? sha1($this->configFilesystem->read($target->getNginxConfigFileName())) : null;
if (sha1($configData) != $configFileHash) {
$this->configFilesystem->write($target->getNginxConfigFileName(), $configData);
$changed = true;
}
if ($target->isUseCustomCert()) {
$this->configFilesystem->write($target->getCustomCertPath(), $target->getCustomCert());
$this->configFilesystem->write($target->getCustomCertKeyPath(), $target->getCustomCertKey());
}
if ($target->hasAuth()) {
$authFileHash = $this->configFilesystem->fileExists($target->getBasicAuthFileName()) ? $this->configFilesystem->read($target->getBasicAuthHashFileName()) : null;
if ($target->getAuthHash() != $authFileHash) {
$this->configFilesystem->write($target->getBasicAuthHashFileName(), $target->getAuthHash());
$this->configFilesystem->write($target->getBasicAuthFileName(), $target->getBasicAuthFileData());
$changed = true;
}
}
return $changed;
}
/**
* @param Target[] $targets
*
* @throws FilesystemException
*/
private function generateLetsEncryptCerts(array $targets): void
{
foreach ($targets as $target) {
if (!$target->isLetsEncrypt()) {
continue;
}
$testAgeFile = "/archive/{$target->getName()}/fullchain1.pem";
if ($this->certificateStoreLocal->fileExists($testAgeFile)) {
$dubious = false;
if ($this->certificateStoreLocal->fileSize($testAgeFile) == 0) {
// File is empty, check its age instead.
$timeRemainingSeconds = $this->certificateStoreLocal->lastModified($testAgeFile) - time();
$dubious = true;
} else {
$ssl = openssl_x509_parse($this->certificateStoreLocal->read($testAgeFile));
$timeRemainingSeconds = $ssl['validTo_time_t'] - time();
}
if ($timeRemainingSeconds > 2592000) {
$this->logger->info(
'Skipping {target_name}, certificate is {validity} for {duration_days} days',
[
'emoji' => Emoji::CHARACTER_PARTYING_FACE,
'target_name' => $target->getName(),
'validity' => $dubious ? 'dubiously good' : 'still good',
'duration_days' => round($timeRemainingSeconds / 86400),
]
);
$target->setUseTemporaryCert(false);
$this->generateNginxConfig($target);
continue;
}
}
// Start running shell commands...
$shell = new Exec();
// Disable nginx tweaks
$this->logger->debug('Moving nginx tweak file out of the way..', ['emoji' => Emoji::rightArrow()]);
$disableNginxTweaksCommand = (new CommandBuilder('mv'))
->addSubCommand('/etc/nginx/conf.d/tweak.conf')
->addSubCommand('/etc/nginx/conf.d/tweak.disabled')
;
$shell->run($disableNginxTweaksCommand);
// Generate letsencrypt cert
$command = new CommandBuilder('/usr/bin/certbot');
$command->addSubCommand('certonly');
$command->addArgument('nginx');
if ($this->environment['BOUNCER_LETSENCRYPT_MODE'] != 'production') {
$command->addArgument('test-cert');
}
$command->addFlag('d', implode(',', $target->getDomains()));
$command->addFlag('n');
$command->addFlag('m', $this->environment['BOUNCER_LETSENCRYPT_EMAIL']);
$command->addArgument('agree-tos');
$this->logger->info('Generating letsencrypt for {target_name} - {command}', ['emoji' => Emoji::pencil() . ' ', 'target_name' => $target->getName(), 'command' => $command->__toString()]);
$shell->run($command);
if ($shell->getReturnValue() == 0) {
$this->logger->info('Generating successful', ['emoji' => Emoji::partyPopper()]);
} else {
$this->logger->critical('Generating failed!', ['emoji' => Emoji::warning() . ' ']);
}
// Re-enable nginx tweaks
$this->logger->debug('Moving nginx tweak file back in place..', ['emoji' => Emoji::leftArrow()]);
$disableNginxTweaksCommand = (new CommandBuilder('mv'))
->addSubCommand('/etc/nginx/conf.d/tweak.disabled')
->addSubCommand('/etc/nginx/conf.d/tweak.conf')
;
$shell->run($disableNginxTweaksCommand);
$target->setUseTemporaryCert(false);
$this->generateNginxConfig($target);
}
$this->restartNginx();
}
private function restartNginx(): void
{
$shell = new Exec();
$command = new CommandBuilder('/usr/sbin/nginx');
$command->addFlag('s', 'reload');
$this->logger->info('Restarting nginx', ['emoji' => Emoji::timerClock() . ' ']);
$nginxRestartOutput = $shell->run($command);
$this->logger->debug('Nginx restarted {restart_output}', ['restart_output' => $nginxRestartOutput, 'emoji' => Emoji::partyPopper()]);
}
}

View file

@ -1,18 +0,0 @@
<?php
declare(strict_types=1);
namespace Bouncer;
enum EnumCertType
{
case NO_CERT;
case LETSENCRYPT_CERT;
case TEMPORARY_CERT;
case GLOBAL_CERT;
case CUSTOM_CERT;
}

View file

@ -1,22 +0,0 @@
<?php
declare(strict_types=1);
namespace Bouncer\Logger\Formatter;
use Bouncer\Settings\Settings;
use Bramus\Monolog\Formatter\ColoredLineFormatter;
use Bramus\Monolog\Formatter\ColorSchemes\TrafficLight;
class ColourLine extends ColoredLineFormatter
{
public function __construct(private readonly Settings $settings)
{
parent::__construct(
new TrafficLight(),
$this->settings->get('logger/line_format'),
'G:i',
);
$this->setMaxLevelNameLength($settings->get('logger/max_level_name_length'));
}
}

View file

@ -1,20 +0,0 @@
<?php
declare(strict_types=1);
namespace Bouncer\Logger\Formatter;
use Monolog\Formatter\LineFormatter;
use Bouncer\Settings\Settings;
class Line extends LineFormatter
{
public function __construct(private readonly Settings $settings)
{
parent::__construct(
$this->settings->get('logger/line_format'),
'G:i',
);
$this->setMaxLevelNameLength($settings->get('logger/max_level_name_length'));
}
}

View file

@ -1,11 +0,0 @@
<?php
declare(strict_types=1);
namespace Bouncer\Logger\Handlers;
use Monolog\Handler\StreamHandler;
class Cli extends StreamHandler
{
}

View file

@ -1,18 +0,0 @@
<?php
declare(strict_types=1);
namespace Bouncer\Logger\Handlers;
use Monolog\Handler\StreamHandler;
class File extends StreamHandler
{
public function withUri($uri)
{
$this->url = $uri;
$this->stream = null;
return $this;
}
}

View file

@ -1,16 +0,0 @@
<?php
declare(strict_types=1);
namespace Bouncer\Logger\Handlers;
use Monolog\Handler\TestHandler;
/**
* @internal
*
* @coversNothing
*/
class Test extends TestHandler
{
}

View file

@ -1,38 +0,0 @@
<?php
declare(strict_types=1);
namespace Bouncer\Logger;
use Bouncer\Settings\Settings;
use Monolog\Processor;
class Logger extends \Monolog\Logger
{
public function __construct(
private readonly Settings $settings,
private readonly Processor\ProcessIdProcessor $processIdProcessor,
private readonly Processor\MemoryPeakUsageProcessor $memoryPeakUsageProcessor,
private readonly Processor\PsrLogMessageProcessor $psrLogMessageProcessor,
private readonly Formatter\ColourLine $coloredLineFormatter,
private readonly Formatter\Line $lineFormatter,
) {
parent::__construct('Bouncer');
$this
->pushProcessor($this->processIdProcessor)
->pushProcessor($this->memoryPeakUsageProcessor)
->pushProcessor($this->psrLogMessageProcessor)
;
$this->pushHandler(
(new Handlers\Cli('php://stdout', $this->settings->get('logger/level')))
->setFormatter($this->settings->get('logger/coloured_output') ? $this->coloredLineFormatter : $this->lineFormatter)
);
$this->pushHandler(
(new Handlers\File($this->settings->get('logger/path'), $this->settings->get('logger/level')))
->setFormatter($this->lineFormatter)
);
}
}

View file

@ -1,127 +0,0 @@
<?php
declare(strict_types=1);
namespace Bouncer\Settings;
use Carbon\Carbon;
use Monolog\Level;
class Settings implements SettingsInterface
{
private array $settings;
public static function getEnvironment(mixed $key, mixed $default = null): mixed
{
if (is_array($key)) {
foreach ($key as $k) {
$value = Settings::getEnvironment($k);
if ($value !== null) {
return $value;
}
}
return $default;
}
$environment = array_merge($_ENV, $_SERVER);
return $environment[$key] ?? $default;
}
public static function isEnabled(string $key, bool $default = false): bool
{
$environment = array_merge($_ENV, $_SERVER);
$expressionsOfYes = ['true', 'enable', 'enabled', 'yes', 'on'];
if (isset($environment[$key])) {
return in_array(strtolower($environment[$key]), $expressionsOfYes);
}
return $default;
}
public function __construct()
{
$this->settings = [
'build' => [
'sha' => Settings::getEnvironment('GIT_SHA', 'unknown'),
'sha_short' => substr(Settings::getEnvironment('GIT_SHA', 'unknown'), 0, 7),
'id' => Settings::getEnvironment('GIT_BUILD_ID', 'unknown'),
'date' => Carbon::parse(Settings::getEnvironment('BUILD_DATE')),
'message' => trim(Settings::getEnvironment('GIT_COMMIT_MESSAGE', '')),
],
'logger' => [
'name' => Settings::getEnvironment('LOG_NAME', 'bouncer'),
'path' => Settings::getEnvironment('LOG_FILE', '/var/log/bouncer/bouncer.log'),
'level' => Level::fromName(Settings::getEnvironment('LOG_LEVEL', 'DEBUG')),
'line_format' => Settings::getEnvironment('LOG_LINE_FORMAT', '[%datetime%] %context.emoji% %level_name%: %channel%: %message%') . "\n",
'max_level_name_length' => Settings::getEnvironment('LOG_LEVEL_NAME_LENGTH', 4),
'coloured_output' => Settings::isEnabled('LOG_COLOUR', true),
'show_state_deltas' => Settings::isEnabled('LOG_SHOW_STATE_DELTAS'),
],
'ssl' => [
'allow_non_ssl' => Settings::isEnabled('BOUNCER_ALLOW_NON_SSL', true),
'global_cert' => Settings::getEnvironment('GLOBAL_CERT'),
'global_cert_key' => Settings::getEnvironment('GLOBAL_CERT_KEY'),
],
'bouncer' => [
'http_port' => intval(Settings::getEnvironment('BOUNCER_HTTP_PORT', 80)),
'https_port' => intval(Settings::getEnvironment('BOUNCER_HTTPS_PORT', 443)),
'forced_update_interval_seconds' => intval(Settings::getEnvironment('BOUNCER_FORCED_UPDATE_INTERVAL_SECONDS', 0)),
'max_nginx_config_creation_notices' => intval(Settings::getEnvironment('BOUNCER_MAXIMUM_NGINX_CONFIG_CREATION_NOTICES', 15)),
],
];
}
public function get(string $key = '', mixed $default = null): mixed
{
if (stripos($key, '/') !== false) {
$s = $this->settings;
$steps = explode('/', $key);
while (count($steps) > 0) {
$b = array_shift($steps);
if (isset($s[$b])) {
$s = $s[$b];
} else {
return $default;
}
}
return $s;
}
return (empty($key)) ? $this->settings : $this->settings[$key];
}
public function has(string $key = ''): bool
{
return $this->get($key) !== null;
}
public function if(string $key): bool
{
return $this->has($key) && $this->get($key) == true;
}
public function set(string $key, mixed $value): self
{
$keys = explode('/', $key);
$arrayPointer = &$this->settings;
// extract the last key
$last_key = array_pop($keys);
// walk/build the array to the specified key
while ($arrayKey = array_shift($keys)) {
if (!array_key_exists($arrayKey, $arrayPointer)) {
$arrayPointer[$arrayKey] = [];
}
$arrayPointer = &$arrayPointer[$arrayKey];
}
// set the final key
$arrayPointer[$last_key] = $value;
return $this;
}
}

View file

@ -1,16 +0,0 @@
<?php
declare(strict_types=1);
namespace Bouncer\Settings;
interface SettingsInterface
{
public function get(string $key = '', mixed $default = null): mixed;
public function has(string $key = ''): bool;
public function if(string $key): bool;
public function set(string $key, mixed $value): self;
}

View file

@ -1,496 +0,0 @@
<?php
declare(strict_types=1);
namespace Bouncer;
use Bouncer\Logger\Logger;
use Bouncer\Settings\Settings;
use Spatie\Emoji\Emoji;
class Target
{
private string $id;
private ?string $label = null;
private array $domains;
private string $endpointHostnameOrIp;
private ?int $port = null;
private bool $letsEncrypt = false;
private string $targetPath;
private bool $allowNonSSL;
private bool $useTemporaryCert = false;
private bool $useGlobalCert = false;
private ?string $customCert = null;
private ?string $customCertKey = null;
private bool $allowWebsocketSupport = true;
private bool $allowLargePayloads = false;
private ?int $proxyTimeoutSeconds = null;
private ?string $username = null;
private ?string $password = null;
private ?string $hostOverride = null;
private ?string $customNginxConfig = null;
private bool $requiresForcedScanning = false;
public function __construct(
private Logger $logger,
private Settings $settings,
) {
$this->allowNonSSL = $this->settings->get('ssl/allow_non_ssl', true);
}
public function __toArray()
{
if ($this->settings->has('ssl/global_cert') && $this->settings->get('ssl/global_cert') === true) {
if ($this->getTypeCertInUse() != EnumCertType::GLOBAL_CERT) {
$this->logger->debug('{label} has overridden cert type of {cert_type}', ['emoji' => Emoji::exclamationQuestionMark() . ' ', 'label' => $this->getLabel(), 'cert_type' => $this->getTypeCertInUse()->name]);
}
}
return [
'portHttp' => $this->settings->get('bouncer/http_port'),
'portHttps' => $this->settings->get('bouncer/https_port'),
] + [
'id' => $this->getId(),
'name' => $this->getName(),
'label' => $this->getLabel(),
'serverName' => $this->getNginxServerName(),
'certType' => $this->getTypeCertInUse()->name,
'targetPath' => $this->getTargetPath(),
'customCertFile' => $this->getCustomCertPath(),
'customCertKeyFile' => $this->getCustomCertKeyPath(),
'useCustomCert' => $this->isUseCustomCert(),
'allowNonSSL' => $this->isAllowNonSSL(),
'allowWebsocketSupport' => $this->isAllowWebsocketSupport(),
'allowLargePayloads' => $this->isAllowLargePayloads(),
'proxyTimeoutSeconds' => $this->getProxyTimeoutSeconds(),
'hasAuth' => $this->hasAuth(),
'authFile' => $this->getBasicAuthFileName(),
'hasHostOverride' => $this->hasHostOverride(),
'hostOverride' => $this->getHostOverride(),
];
}
public function getHostOverride(): ?string
{
return $this->hostOverride;
}
public function hasHostOverride(): bool
{
return $this->hostOverride !== null;
}
public function setHostOverride(string $hostOverride): self
{
$this->hostOverride = $hostOverride;
return $this;
}
public function getUsername(): ?string
{
return $this->username;
}
/**
* @param string
*/
public function setUsername(string $username): self
{
$this->username = $username;
return $this;
}
public function getPassword(): ?string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
public function getCustomCert(): ?string
{
return $this->customCert;
}
public function setCustomCert(?string $customCert): Target
{
$this->customCert = $customCert;
return $this;
}
public function getCustomCertKey(): ?string
{
return $this->customCertKey;
}
public function setCustomCertKey(?string $customCertKey): Target
{
$this->customCertKey = $customCertKey;
return $this;
}
public function isUseCustomCert(): bool
{
return $this->customCert !== null && $this->customCertKey !== null;
}
public function getAuth(): array
{
return [
'username' => $this->getUsername(),
'password' => $this->getPassword(),
];
}
public function getAuthHash(): string
{
return sha1(implode(':', $this->getAuth()));
}
public function setAuth(string $username, string $password): self
{
return $this->setUsername($username)->setPassword($password);
}
public function hasAuth(): bool
{
return $this->username != null && $this->password != null;
}
public function getNginxConfigFileName(): string
{
return "{$this->getName()}.conf";
}
public function getBasicAuthFileName(): string
{
return "{$this->getName()}.secret";
}
public function getBasicAuthHashFileName(): string
{
return "{$this->getBasicAuthFileName()}.hash";
}
public function getCustomCertPath(): string
{
return "{$this->getName()}.public.pem";
}
public function getCustomCertKeyPath(): string
{
return "{$this->getName()}.private.pem";
}
public function getBasicAuthFileData(): string
{
$output = shell_exec(sprintf('htpasswd -nibB -C10 %s %s', $this->getUsername(), $this->getPassword()));
return trim($output) . "\n";
}
/**
* Return an array of files that should exist for this target.
*/
public function getExpectedFiles(): array
{
return array_filter([
$this->getNginxConfigFileName(),
$this->hasAuth() ? $this->getBasicAuthFileName() : null,
$this->hasAuth() ? $this->getBasicAuthHashFileName() : null,
$this->isUseCustomCert() ? $this->getCustomCertPath() : null,
$this->isUseCustomCert() ? $this->getCustomCertKeyPath() : null,
]);
}
public function getProxyTimeoutSeconds(): ?int
{
return $this->proxyTimeoutSeconds;
}
public function setProxyTimeoutSeconds(?int $proxyTimeoutSeconds): self
{
$this->proxyTimeoutSeconds = $proxyTimeoutSeconds;
return $this;
}
public function isUseTemporaryCert(): bool
{
return $this->useTemporaryCert;
}
public function setUseTemporaryCert(bool $useTemporaryCert): self
{
$this->useTemporaryCert = $useTemporaryCert;
return $this;
}
public function isUseGlobalCert(): bool
{
return $this->useGlobalCert;
}
public function setUseGlobalCert(bool $useGlobalCert): self
{
// $this->logger->critical('setUseGlobalCert: {useGlobalCert}', ['useGlobalCert' => $useGlobalCert ? 'yes' : 'no']);
$this->useGlobalCert = $useGlobalCert;
return $this;
}
public function getTypeCertInUse(): EnumCertType
{
return match (true) {
$this->isUseCustomCert() => EnumCertType::CUSTOM_CERT,
$this->isLetsEncrypt() => EnumCertType::LETSENCRYPT_CERT,
$this->isUseTemporaryCert() => EnumCertType::TEMPORARY_CERT,
$this->isUseGlobalCert() => EnumCertType::GLOBAL_CERT,
default => EnumCertType::NO_CERT,
};
}
public function isAllowWebsocketSupport(): bool
{
return $this->allowWebsocketSupport;
}
public function setAllowWebsocketSupport(bool $allowWebsocketSupport): self
{
$this->allowWebsocketSupport = $allowWebsocketSupport;
return $this;
}
public function isAllowLargePayloads(): bool
{
return $this->allowLargePayloads;
}
public function setAllowLargePayloads(bool $allowLargePayloads): self
{
$this->allowLargePayloads = $allowLargePayloads;
return $this;
}
public function getId(): string
{
return $this->id;
}
public function setId(string $id): self
{
$this->id = $id;
return $this;
}
/**
* @return string
*/
public function getDomains(): array
{
return $this->domains;
}
public function getNginxServerNames(): array
{
$serverNames = [];
foreach ($this->domains as $domain) {
if (stripos($domain, '*') !== false) {
$serverNames[] = sprintf('~^(.*)%s$', str_replace('*', '', $domain));
} else {
$serverNames[] = $domain;
}
}
return $serverNames;
}
public function getNginxServerName(): string
{
return implode(' ', $this->getNginxServerNames());
}
/**
* @param string[] $domains
*/
public function setDomains(array $domains): self
{
$this->domains = $domains;
$this->updateLogger();
return $this;
}
public function isLetsEncrypt(): bool
{
return $this->letsEncrypt;
}
public function setLetsEncrypt(bool $letsEncrypt): self
{
$this->letsEncrypt = $letsEncrypt;
return $this;
}
public function getTargetPath(): string
{
return $this->targetPath;
}
public function setTargetPath(string $targetPath): self
{
$this->targetPath = $targetPath;
return $this;
}
public function getEndpointHostnameOrIp(): string
{
return $this->endpointHostnameOrIp;
}
public function setEndpointHostnameOrIp(string $endpointHostnameOrIp): self
{
$this->endpointHostnameOrIp = $endpointHostnameOrIp;
return $this;
}
public function getPort(): ?int
{
return $this->port;
}
public function isPortSet(): bool
{
return $this->port !== null;
}
public function setPort(int $port): self
{
$this->port = $port;
return $this;
}
public function getName(): string
{
return str_replace('*.', '', reset($this->domains));
}
public function getLabel(): string
{
return $this->label ?? $this->getName();
}
public function setLabel(string $label): self
{
$this->label = $label;
$this->updateLogger();
// $this->logger->debug('Target label set to {label}', ['emoji' => Emoji::label(), 'label' => $label]);
return $this;
}
public function isAllowNonSSL(): bool
{
return $this->allowNonSSL;
}
public function setAllowNonSSL(bool $allowNonSSL): self
{
$this->allowNonSSL = $allowNonSSL;
return $this;
}
public function getLogger(): Logger
{
return $this->logger;
}
public function updateLogger(): self
{
$this->logger = $this->logger->withName($this->getLabel());
return $this;
}
public function isEndpointValid(): bool
{
// Is it just an IP?
if (filter_var($this->getEndpointHostnameOrIp(), FILTER_VALIDATE_IP)) {
// $this->logger->debug(sprintf('%s isEndpointValid: %s is a normal IP', Emoji::magnifyingGlassTiltedRight(), $this->getEndpointHostnameOrIp()));
return true;
}
// Is it a Hostname that resolves?
$resolved = gethostbyname($this->getEndpointHostnameOrIp());
if (filter_var($resolved, FILTER_VALIDATE_IP)) {
// $this->logger->critical(sprintf('%s isEndpointValid: %s is a hostname that resolves to a normal IP %s', Emoji::magnifyingGlassTiltedRight(), $this->getEndpointHostnameOrIp(), $resolved));
return true;
}
$this->logger->critical('isEndpointValid: {endpoint} is a hostname that does not resolve', ['emoji' => Emoji::magnifyingGlassTiltedRight(), 'endpoint' => $this->getEndpointHostnameOrIp()]);
$this->setRequiresForcedScanning(true);
return false;
}
public function getPresentationDomain(): string
{
return sprintf(
'%s://%s%s',
'https',
$this->getUsername() && $this->getPassword() ?
sprintf('%s:%s@', $this->getUsername(), $this->getPassword()) :
'',
$this->getName()
);
}
public function setCustomNginxConfig(string $config): self
{
$this->customNginxConfig = $config;
return $this;
}
public function getCustomNginxConfig(): ?string
{
return $this->customNginxConfig;
}
public function hasCustomNginxConfig(): bool
{
return $this->customNginxConfig !== null;
}
public function requiresForcedScanning(): bool
{
return $this->requiresForcedScanning;
}
public function setRequiresForcedScanning(bool $requiresForcedScanning): void
{
$this->requiresForcedScanning = $requiresForcedScanning;
}
}

View file

@ -1,82 +0,0 @@
server {
{% if allowNonSSL %}
# Non-SSL Traffic is allowed
listen {{ portHttp }};
listen [::]:{{ portHttp }};
{% endif %}
# SSL Traffic is allowed
listen {{ portHttps }} ssl;
listen [::]:{{ portHttps }} ssl;
server_name {{ serverName }};
access_log /var/log/bouncer/{{ name }}.access.log;
error_log /var/log/bouncer/{{ name }}.error.log;
{% if certType == 'TEMPORARY_CERT' %}
ssl_certificate /certs/example.crt;
ssl_certificate_key /certs/example.key;
{% elseif certType == 'GLOBAL_CERT' %}
ssl_certificate /certs/global.crt;
ssl_certificate_key /certs/global.key;
{% elseif certType == 'CUSTOM_CERT' %}
ssl_certificate /etc/nginx/sites-enabled/{{ customCertFile }};
ssl_certificate_key /etc/nginx/sites-enabled/{{ customCertKeyFile }};
{% elseif certType == 'LETSENCRYPT_CERT' %}
ssl_certificate /etc/letsencrypt/live/{{ name }}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/{{ name }}/privkey.pem;
{% endif %}
# ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# ssl_ciphers HIGH:!aNULL:!MD5;
{% if allowLargePayloads %}
client_max_body_size 0;
{% endif %}
location / {
{% if hasHostOverride %}
proxy_set_header Host {{ hostOverride }};
{% else %}
proxy_set_header Host $host;
{% endif %}
# Server to send the request on to
proxy_pass "{{ targetPath }}";
# Standard headers setting origin data
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
{% if hasAuth %}
# Http Basic Auth
auth_basic "closed site";
auth_basic_user_file sites-enabled/{{ authFile }};
{% endif %}
{% if allowWebsocketSupport %}
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_cache_bypass $http_upgrade;
proxy_buffering off;
proxy_set_header Origin "";
{% endif %}
{% if proxyTimeoutSeconds %}
# Proxy timeouts
proxy_read_timeout {{ proxyTimeoutSeconds }};
proxy_connect_timeout {{ proxyTimeoutSeconds }};
proxy_send_timeout {{ proxyTimeoutSeconds }};
{% endif %}
}
}
{% if not allowNonSSL %}
server {
# Redirect non-ssl to ssl
listen {{ portHttp }};
listen [::]:{{ portHttp }};
server_name {{ serverName }};
return 301 https://$host$request_uri;
}
{% endif %}

Binary file not shown.

Before

Width: 64px  |  Height: 64px  |  Size: 34 KiB

View file

@ -1 +0,0 @@
<h1>Website A</h1>

Binary file not shown.

Before

Width: 64px  |  Height: 64px  |  Size: 34 KiB

View file

@ -1 +0,0 @@
<h1>Website B</h1>

Binary file not shown.

Before

Width: 64px  |  Height: 64px  |  Size: 34 KiB

View file

@ -1 +0,0 @@
<h1>Website C</h1>

View file

@ -1,4 +0,0 @@
FROM amazon/dynamodb-local
HEALTHCHECK --interval=5s --timeout=3s --start-period=0s --retries=5 \
CMD curl --silent --output /dev/null http://localhost:8000/shell/
CMD ["-jar", "/home/dynamodblocal/DynamoDBLocal.jar", "-inMemory"]

View file

@ -1,5 +0,0 @@
version: "3.7"
services:
dynamodb:
build: .
image: ghcr.io/benzine-framework/dynamodb:latest

View file

@ -1,26 +0,0 @@
FROM php:nginx
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"
# hadolint ignore=DL3008
RUN apt-get update -qq && \
apt-get install -yqq --no-install-recommends \
php8.1-mailparse \
cpulimit \
&& \
apt-get autoremove -yqq && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /var/lib/dpkg/status.old /var/cache/debconf/templates.dat /var/log/dpkg.log /var/log/lastlog /var/log/apt/*.log
COPY nginx.runit /etc/service/nginx/run
COPY laravel.runit /etc/service/laravel/run
COPY laravel-horizon.runit /etc/service/horizon/run
COPY laravel-horizon.finish /etc/service/horizon/finish
COPY laravel-scheduler.runit /etc/service/scheduler/run
COPY migrate.runit /etc/service/migrate/run
COPY wait-for-mysql /usr/bin/wait-for-mysql
RUN chmod +x /etc/service/*/run /etc/service/*/finish /usr/bin/wait-for-mysql
HEALTHCHECK --interval=10s --timeout=3s \
CMD curl -f http://localhost/ || exit 1
RUN adduser laravel
USER laravel

View file

@ -1,2 +0,0 @@
#!/bin/bash
sleep 20

View file

@ -1,28 +0,0 @@
#!/usr/bin/env bash
rm -f /var/lock/laravel_horizon_started
# If horizon is present, publish the frontend assets, if HORIZON_UI is set to "on"
if [[ ${HORIZON_ENABLE,,} == "on" ]]; then
if [[ -f "/app/config/horizon.php" ]]; then
if [[ ${MIGRATE_ENABLE} == "on" ]]; then
echo "[HORIZON] Waiting until Migration Complete."
until [[ -f /var/lock/laravel_migration_complete ]]; do
sleep 1
done
echo "[HORIZON] Migration is complete, running Horizon."
fi
if [[ ${HORIZON_UI,,} == "on" ]]; then
echo "[HORIZON] Publishing horizon frontend assets"
php /app/artisan horizon:publish
fi
echo "[HORIZON] Running laravel horizon runner"
cpulimit -l 30 -- php /app/artisan horizon
touch /var/lock/laravel_horizon_started
else
echo "[HORIZON] Horizon is not present."
fi
else
echo "[HORIZON] Not enabled. To enable this feature, set HORIZON_ENABLE = on."
fi
sleep infinity

View file

@ -1,18 +0,0 @@
#!/bin/bash
cd /app || exit
if [[ ${SCHEDULER_ENABLE,,} == "on" ]]; then
if [[ ${MIGRATE_ENABLE} == "on" ]]; then
echo "[SCHEDULER] Waiting until Migration Complete."
until [[ -f /var/lock/laravel_migration_complete ]]; do
sleep 1
done
echo "[SCHEDULER] Migrations complete, starting scheduler"
fi
while true; do
php /app/artisan schedule:run
sleep 59
done
else
echo "[SCHEDULER] Not enabled. To enable this feature, set SCHEDULER_ENABLE = on."
sleep infinity
fi

View file

@ -1,34 +0,0 @@
#!/usr/bin/env bash
rm -f /var/lock/laravel_ready
echo "[LARAVEL-FIXER] Fixing laravel application permissions"
mkdir -p /app/storage /app/bootstrap/cache
find /app/storage -type d -exec chmod 777 {} \;
find /app/bootstrap/cache -type d -exec chmod 777 {} \;
find /app/storage -type f -not -name ".gitignore" -exec chmod 666 {} \;
find /app/bootstrap/cache -type f -exec chmod 666 {} \;
touch /app/storage/logs/laravel.log
chmod 777 -R /app/storage
chmod +x /app/artisan
php /app/artisan package:discover
if [[ ${REGENERATE_KEYS,,} == "on" ]]; then
php /app/artisan key:generate
php /app/artisan passport:keys --force
fi
echo "[LARAVEL-FIXER] Waiting for mysql..."
/usr/bin/wait-for-mysql
echo "[LARAVEL-FIXER] Mysql Ready, Laravel Ready."
php /app/artisan wipe
touch /var/lock/laravel_ready
# Output the laravel log to the docker terminal.
tail -n0 -f /app/storage/logs/laravel-*.log /app/storage/logs/laravel.log &
# Sleep forever (and sleep again incase the sleep process is killed)
while true; do
sleep infinity
done

View file

@ -1,43 +0,0 @@
#!/usr/bin/env bash
rm -f /var/lock/laravel_migration_underway \
/var/lock/laravel_migration_complete
if [[ ${MIGRATE_ENABLE,,} == "on" ]]; then
# Give a moment for services to wake up
echo "[MIGRATION] Waiting until Laravel Ready."
sleep 3
until [[ -f /var/lock/laravel_ready ]]; do
sleep 1
done
echo "[MIGRATION] Laravel is ready, running migrations..."
cd /app || exit
# Run migration
touch /var/lock/laravel_migration_underway
if [[ ${MIGRATE_CLEAN,,} == "on" ]]; then
php /app/artisan migrate:fresh --force
php /app/artisan migrate --force # First run will fail due to permissions. We can ignore, but need to migrate again to finish.
else
# If we run this on first commit, it is the same as migrate:fresh, first run may fail and we need to try one more time.
php /app/artisan migrate --force || php /app/artisan migrate --force
fi
if [[ ${SEEDERS,,} == "on" ]]; then
php /app/artisan db:seed -q
fi
rm /var/lock/laravel_migration_underway
touch /var/lock/laravel_migration_complete
echo "[MIGRATION] Migration complete!"
else
echo "[MIGRATION] Not enabled. Set MIGRATE_ENABLE = on to enable."
fi
# Sleep forever (and sleep again in case the sleep process is killed)
while true; do
sleep infinity
done

View file

@ -1,17 +0,0 @@
#!/usr/bin/env bash
if [[ -z ${SSL_CERTIFICATE} ]]; then
echo "No certificate set, using defaults"
else
echo "Setting /certs/example.crt and /certs/example.key"
echo "${SSL_CERTIFICATE}" >/certs/example.crt
echo "${SSL_CERTIFICATE_KEY}" >/certs/example.key
fi
if [[ ${HTTP_ENABLE,,} == "on" ]]; then
echo "[NGINX] Starting Nginx"
/usr/sbin/nginx
else
echo "[NGINX] HTTP_ENABLE not set, Nginx not running"
sleep infinity
fi

View file

@ -1,14 +0,0 @@
#!/bin/bash
MYSQL_HOST=${MYSQL_HOST:-"localhost"}
MYSQL_PORT=${MYSQL_PORT:-3306}
echo -n "Waiting for MySQL..."
while ! mysqladmin ping -h"${MYSQL_HOST}" -P"${MYSQL_PORT}" --silent; do
sleep 1
echo -n "."
done
sleep 1
while ! mysqladmin ping -h"${MYSQL_HOST}" -P"${MYSQL_PORT}" --silent; do
sleep 1
echo -n "."
done
echo -e "\nConnected to MySQL!"

View file

@ -1,14 +0,0 @@
# checkov:skip=CKV_DOCKER_3 We're not adding a user, its coming down from on-high in mariadb.
FROM mariadb:injected-version
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"
# If healthcheck.sh isn't baked into the underlying image, crash.
RUN which healthcheck.sh
# Add healthcheck
HEALTHCHECK --start-period=30s --interval=10s --timeout=30s --retries=3 \
CMD ["healthcheck.sh", "--su-mysql", "--connect", "--innodb_initialized"]

View file

@ -1,20 +0,0 @@
This code of conduct outlines our expectations for participants within the open source community. Anyone who violates this code of conduct may be banned from contributing here.
# Requirements
- **Be friendly and patient.**
- **Be welcoming** _We strive to be a community that welcomes and supports people of all backgrounds and identities._
- **Be respectful** _Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor manners._
# Unacceptable Behaviour
- Offensive comments related to gender, sexual orientation, disability, mental illness, physical appearance, body size, race, age, regional discrimination, political or religious affiliation.
- Threats of violence, both physical and psycological.
- Incitement of violence towards any individual, including encouraging a person to commit suicide or to engage in self-harm.
- Continued communication after requests to cease.
# Interactions
- Don't just tell somebody they are wrong, or what they have done is wrong. You must always explain what is wrong, and why it is wrong.
- Don't reject contributions that are partially complete and then go and commit your own version. Try to work with the author to complete their work.
- We encourage everyone to participate and are committed to building a community for all, we seek to treat everyone both as fairly and equally as possible.

Some files were not shown because too many files have changed in this diff Show more