Forkification
This commit is contained in:
parent
ee66c49987
commit
617a5087c8
204 changed files with 3 additions and 24020 deletions
.github/workflows
bouncer.ymlbuild.ymldynamodb.ymllaravel.ymlmariadb.ymlminio.ymlmitm-proxy.ymlmongodb.ymlmqtt.ymlmysql-proxy.ymlnode.ymloctoprint.ymlperl.ymlphp.ymlpostgres.ymlredis.ymls3db.ymlswarm-connectivity-tester.ymlswarm-monitor.ymlwordpress.yml
CODE_OF_CONDUCT.mdDockerfileLICENSE.mdReadme.mdbouncer
.gitignore.php-cs-fixer.phpDockerfileMakefileNginx-tweak.confNginxDefaultReadme.md
bin
bouncer.finishbouncer.runitbuild-compose.ymlcomposer.jsoncomposer.lockdocker-bake.hcldocker-compose.ymllogs-nginx-access.runitlogs-nginx-error.runitlogs.finishlogs.runitnginx.runitpublic
self-signed-certificates
src
templates
test
dynamodb
etc/service
installers
000_apt.sh001_bash.sh002_timezone.sh003_dos2unix.sh004_gpgagent.sh050_runit.sh100_cron.sh101_rsyslog.sh102_logrotate.sh900_utils.sh901_jq.sh902_cpu_monitoring.sh903_version_control.sh904_redis.sh905_xtail.sh999_cleanup.shconfiginstall
laravel
Dockerfilelaravel-horizon.finishlaravel-horizon.runitlaravel-scheduler.runitlaravel.runitmigrate.runitnginx.runitwait-for-mysql
mariadb
marshall
115
.github/workflows/bouncer.yml
vendored
115
.github/workflows/bouncer.yml
vendored
|
@ -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
|
163
.github/workflows/build.yml
vendored
163
.github/workflows/build.yml
vendored
|
@ -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
|
48
.github/workflows/dynamodb.yml
vendored
48
.github/workflows/dynamodb.yml
vendored
|
@ -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
|
91
.github/workflows/laravel.yml
vendored
91
.github/workflows/laravel.yml
vendored
|
@ -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
|
68
.github/workflows/mariadb.yml
vendored
68
.github/workflows/mariadb.yml
vendored
|
@ -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 }}
|
48
.github/workflows/minio.yml
vendored
48
.github/workflows/minio.yml
vendored
|
@ -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
|
103
.github/workflows/mitm-proxy.yml
vendored
103
.github/workflows/mitm-proxy.yml
vendored
|
@ -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' || '' }}
|
48
.github/workflows/mongodb.yml
vendored
48
.github/workflows/mongodb.yml
vendored
|
@ -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
|
62
.github/workflows/mqtt.yml
vendored
62
.github/workflows/mqtt.yml
vendored
|
@ -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 }}
|
62
.github/workflows/mysql-proxy.yml
vendored
62
.github/workflows/mysql-proxy.yml
vendored
|
@ -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
|
73
.github/workflows/node.yml
vendored
73
.github/workflows/node.yml
vendored
|
@ -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
|
67
.github/workflows/octoprint.yml
vendored
67
.github/workflows/octoprint.yml
vendored
|
@ -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
|
102
.github/workflows/perl.yml
vendored
102
.github/workflows/perl.yml
vendored
|
@ -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 }}
|
157
.github/workflows/php.yml
vendored
157
.github/workflows/php.yml
vendored
|
@ -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 }}
|
108
.github/workflows/postgres.yml
vendored
108
.github/workflows/postgres.yml
vendored
|
@ -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 }}
|
78
.github/workflows/redis.yml
vendored
78
.github/workflows/redis.yml
vendored
|
@ -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' || '' }}
|
104
.github/workflows/s3db.yml
vendored
104
.github/workflows/s3db.yml
vendored
|
@ -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 }}
|
109
.github/workflows/swarm-connectivity-tester.yml
vendored
109
.github/workflows/swarm-connectivity-tester.yml
vendored
|
@ -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
|
82
.github/workflows/swarm-monitor.yml
vendored
82
.github/workflows/swarm-monitor.yml
vendored
|
@ -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
|
51
.github/workflows/wordpress.yml
vendored
51
.github/workflows/wordpress.yml
vendored
|
@ -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
|
20
Readme.md
20
Readme.md
|
@ -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
4
bouncer/.gitignore
vendored
|
@ -1,4 +0,0 @@
|
|||
.idea
|
||||
docker-compose.override.yml
|
||||
vendor
|
||||
.php-cs-fixer.cache
|
|
@ -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)
|
||||
;
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -1,2 +0,0 @@
|
|||
server_names_hash_max_size 1024;
|
||||
server_names_hash_bucket_size 256;
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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.
|
|
@ -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();
|
|
@ -1,2 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
sleep 5
|
|
@ -1,5 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
sleep 3
|
||||
printf "\n\n\n\n"
|
||||
echo "Starting Bouncer"
|
||||
/app/bin/bouncer
|
|
@ -1,7 +0,0 @@
|
|||
version: "2.4"
|
||||
services:
|
||||
bouncer:
|
||||
build:
|
||||
context: .
|
||||
target: bouncer
|
||||
image: ghcr.io/benzine-framework/bouncer:latest
|
|
@ -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
3767
bouncer/composer.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -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"
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -1,2 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
tail -f /var/log/nginx/error.log
|
|
@ -1,2 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
sleep infinity
|
|
@ -1,4 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
if [[ -f /var/log/bouncer/bouncer.log ]]; then
|
||||
tail -f /var/log/bouncer/bouncer.log
|
||||
fi
|
|
@ -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 |
|
@ -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>
|
|
@ -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-----
|
|
@ -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-----
|
|
@ -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()]);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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'));
|
||||
}
|
||||
}
|
|
@ -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'));
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Bouncer\Logger\Handlers;
|
||||
|
||||
use Monolog\Handler\StreamHandler;
|
||||
|
||||
class Cli extends StreamHandler
|
||||
{
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Bouncer\Logger\Handlers;
|
||||
|
||||
use Monolog\Handler\TestHandler;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @coversNothing
|
||||
*/
|
||||
class Test extends TestHandler
|
||||
{
|
||||
}
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 |
|
@ -1 +0,0 @@
|
|||
<h1>Website A</h1>
|
Binary file not shown.
Before Width: 64px | Height: 64px | Size: 34 KiB |
|
@ -1 +0,0 @@
|
|||
<h1>Website B</h1>
|
Binary file not shown.
Before Width: 64px | Height: 64px | Size: 34 KiB |
|
@ -1 +0,0 @@
|
|||
<h1>Website C</h1>
|
|
@ -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"]
|
|
@ -1,5 +0,0 @@
|
|||
version: "3.7"
|
||||
services:
|
||||
dynamodb:
|
||||
build: .
|
||||
image: ghcr.io/benzine-framework/dynamodb:latest
|
|
@ -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
|
|
@ -1,2 +0,0 @@
|
|||
#!/bin/bash
|
||||
sleep 20
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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!"
|
|
@ -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"]
|
||||
|
|
@ -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
Loading…
Reference in a new issue