Soft merge branch 'dev' into branch '1.6'

This commit is contained in:
Théophile Diot 2024-06-08 15:17:36 +01:00
commit cc0f189e7a
No known key found for this signature in database
GPG key ID: FA995104A0BA376A
278 changed files with 4436 additions and 4265 deletions

View file

@ -94,14 +94,12 @@ jobs:
needs: [create-arm]
strategy:
matrix:
linux: [ubuntu, ubuntu-noble, debian, fedora, rhel, rhel9]
linux: [ubuntu, debian, fedora, rhel, rhel9]
platforms: [linux/amd64, linux/arm64]
include:
- release: beta
- linux: ubuntu
package: deb
- linux: ubuntu-noble
package: deb
- linux: debian
package: deb
- linux: fedora
@ -181,17 +179,12 @@ jobs:
needs: [wait-builds]
strategy:
matrix:
linux: [ubuntu, ubuntu-noble, debian, fedora, el, el9]
linux: [ubuntu, debian, fedora, el, el9]
arch: [amd64, arm64]
include:
- release: beta
repo: bunkerweb
- linux: ubuntu
separator: _
suffix: ""
version: jammy
package: deb
- linux: ubuntu-noble
separator: _
suffix: ""
version: noble
@ -204,7 +197,7 @@ jobs:
- linux: fedora
separator: "-"
suffix: "1."
version: 39
version: 40
package: rpm
- linux: el
separator: "-"
@ -219,9 +212,6 @@ jobs:
- linux: ubuntu
arch: amd64
package_arch: amd64
- linux: ubuntu-noble
arch: amd64
package_arch: amd64
- linux: debian
arch: amd64
package_arch: amd64
@ -237,9 +227,6 @@ jobs:
- linux: ubuntu
arch: arm64
package_arch: arm64
- linux: ubuntu-noble
arch: arm64
package_arch: arm64
- linux: debian
arch: arm64
package_arch: arm64

View file

@ -35,12 +35,12 @@ jobs:
python -m pip install --no-cache-dir --require-hashes -r src/common/db/requirements.txt
echo "CODEQL_PYTHON=$(which python)" >> $GITHUB_ENV
- name: Initialize CodeQL
uses: github/codeql-action/init@9fdb3e49720b44c48891d036bb502feb25684276 # v3.25.6
uses: github/codeql-action/init@2e230e8fe0ad3a14a340ad0815ddb96d599d2aff # v3.25.8
with:
languages: ${{ matrix.language }}
config-file: ./.github/codeql.yml
setup-python-dependencies: false
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@9fdb3e49720b44c48891d036bb502feb25684276 # v3.25.6
uses: github/codeql-action/analyze@2e230e8fe0ad3a14a340ad0815ddb96d599d2aff # v3.25.8
with:
category: "/language:${{matrix.language}}"

View file

@ -72,13 +72,13 @@ jobs:
endpoint: ssh://root@arm
platforms: linux/arm64,linux/arm/v7,linux/arm/v6
- name: Login to Docker Hub
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Login to ghcr
if: inputs.PUSH == true
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@ -117,7 +117,7 @@ jobs:
# Check OS vulnerabilities
- name: Check OS vulnerabilities
if: ${{ inputs.CACHE_SUFFIX != 'arm' }}
uses: aquasecurity/trivy-action@fd25fed6972e341ff0007ddb61f77e88103953c2 # v0.21.0
uses: aquasecurity/trivy-action@595be6a0f6560a0a8fc419ddf630567fc623531d # v0.22.0
with:
vuln-type: os
skip-dirs: /root/.cargo

View file

@ -43,11 +43,11 @@ jobs:
packages: write
strategy:
matrix:
linux: [ubuntu, ubuntu-noble, debian, fedora, rhel, rhel9]
linux: [ubuntu, debian, fedora, rhel, rhel9, ubuntu-jammy]
include:
- linux: ubuntu
package: deb
- linux: ubuntu-noble
- linux: ubuntu-jammy
package: deb
- linux: debian
package: deb
@ -151,12 +151,12 @@ jobs:
packages: write
steps:
- name: Login to Docker Hub
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Login to ghcr
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@ -187,18 +187,12 @@ jobs:
needs: [tests-ui-linux, tests-core-linux]
strategy:
matrix:
linux: [ubuntu, ubuntu-noble, debian, fedora, el, el9]
linux: [ubuntu, debian, fedora, el, el9, ubuntu-jammy]
arch: [amd64]
include:
- release: dev
repo: bunkerweb
- linux: ubuntu
package_arch: amd64
separator: _
suffix: ""
version: jammy
package: deb
- linux: ubuntu-noble
package_arch: amd64
separator: _
suffix: ""
@ -214,7 +208,7 @@ jobs:
package_arch: x86_64
separator: "-"
suffix: "1."
version: 39
version: 40
package: rpm
- linux: el
package_arch: x86_64
@ -228,6 +222,12 @@ jobs:
suffix: "1."
version: 9
package: rpm
- linux: ubuntu-jammy
package_arch: amd64
separator: _
suffix: ""
version: jammy
package: deb
uses: ./.github/workflows/push-packagecloud.yml
with:
SEPARATOR: ${{ matrix.separator }}

View file

@ -81,12 +81,12 @@ jobs:
endpoint: ssh://root@arm
platforms: linux/arm64,linux/arm/v7,linux/arm/v6
- name: Login to Docker Hub
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Login to ghcr
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0
with:
registry: ghcr.io
username: ${{ github.actor }}

View file

@ -35,12 +35,12 @@ jobs:
- name: Check out repository code
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Login to Docker Hub
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Login to ghcr
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0
with:
registry: ghcr.io
username: ${{ github.actor }}

View file

@ -42,7 +42,7 @@ jobs:
- name: Check out repository code
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Install ruby
uses: ruby/setup-ruby@943103cae7d3f1bb1e4951d5fcc7928b40e4b742 # v1.177.1
uses: ruby/setup-ruby@d5fb7a202fc07872cb44f00ba8e6197b70cb0c55 # v1.179.0
with:
ruby-version: "3.0"
- name: Install packagecloud
@ -60,7 +60,7 @@ jobs:
path: /tmp/${{ inputs.LINUX }}
# Remove existing packages
- name: Remove existing package
if: inputs.LINUX != 'el9' && inputs.LINUX != 'ubuntu-noble'
if: inputs.LINUX != 'el9' && inputs.LINUX != 'ubuntu-jammy'
run: package_cloud yank bunkerity/${{ inputs.REPO }}/${{ inputs.LINUX }}/${{ inputs.VERSION }} bunkerweb${{ inputs.SEPARATOR }}${{ inputs.BW_VERSION }}${{ inputs.SEPARATOR }}${{ inputs.SUFFIX }}${{ inputs.PACKAGE_ARCH }}.${{ inputs.PACKAGE }}
continue-on-error: true
env:
@ -71,9 +71,9 @@ jobs:
continue-on-error: true
env:
PACKAGECLOUD_TOKEN: ${{ secrets.PACKAGECLOUD_TOKEN }}
- name: Remove existing package ubuntu-noble
if: inputs.LINUX == 'ubuntu-noble'
run: package_cloud yank bunkerity/${{ inputs.REPO }}/ubuntu/noble bunkerweb${{ inputs.SEPARATOR }}${{ inputs.BW_VERSION }}${{ inputs.SEPARATOR }}${{ inputs.SUFFIX }}${{ inputs.PACKAGE_ARCH }}.${{ inputs.PACKAGE }}
- name: Remove existing package ubuntu-jammy
if: inputs.LINUX == 'ubuntu-jammy'
run: package_cloud yank bunkerity/${{ inputs.REPO }}/ubuntu/jammy bunkerweb${{ inputs.SEPARATOR }}${{ inputs.BW_VERSION }}${{ inputs.SEPARATOR }}${{ inputs.SUFFIX }}${{ inputs.PACKAGE_ARCH }}.${{ inputs.PACKAGE }}
continue-on-error: true
env:
PACKAGECLOUD_TOKEN: ${{ secrets.PACKAGECLOUD_TOKEN }}
@ -83,7 +83,7 @@ jobs:
# run: sudo apt install -y rename && rename 's/[0-9]\.[0-9]\.[0-9]/testing/' /tmp/${{ inputs.LINUX }}/*.${{ inputs.PACKAGE }}
# Push package
- name: Push package to packagecloud
if: inputs.LINUX != 'el9' && inputs.LINUX != 'ubuntu-noble'
if: inputs.LINUX != 'el9' && inputs.LINUX != 'ubuntu-jammy'
uses: danielmundi/upload-packagecloud@46cd0e61152bf952dbc0d1759e609d3d22649030 # v1
with:
PACKAGE-NAME: /tmp/${{ inputs.LINUX }}/*.${{ inputs.PACKAGE }}
@ -100,12 +100,12 @@ jobs:
PACKAGECLOUD-REPO: ${{ inputs.REPO }}
PACKAGECLOUD-DISTRIB: el/9
PACKAGECLOUD-TOKEN: ${{ secrets.PACKAGECLOUD_TOKEN }}
- name: Push package to packagecloud for ubuntu-noble
if: inputs.LINUX == 'ubuntu-noble'
- name: Push package to packagecloud for ubuntu-jammy
if: inputs.LINUX == 'ubuntu-jammy'
uses: danielmundi/upload-packagecloud@46cd0e61152bf952dbc0d1759e609d3d22649030 # v1
with:
PACKAGE-NAME: /tmp/${{ inputs.LINUX }}/*.${{ inputs.PACKAGE }}
PACKAGECLOUD-USERNAME: bunkerity
PACKAGECLOUD-REPO: ${{ inputs.REPO }}
PACKAGECLOUD-DISTRIB: ubuntu/noble
PACKAGECLOUD-DISTRIB: ubuntu/jammy
PACKAGECLOUD-TOKEN: ${{ secrets.PACKAGECLOUD_TOKEN }}

View file

@ -104,17 +104,12 @@ jobs:
needs: [create-arm]
strategy:
matrix:
linux: [ubuntu, ubuntu-noble, debian, fedora, rhel, rhel9]
linux: [ubuntu, debian, fedora, rhel, rhel9, ubuntu-jammy]
platforms: [linux/amd64, linux/arm64]
exclude:
- linux: ubuntu-noble
platforms: linux/arm64
include:
- release: latest
- linux: ubuntu
package: deb
- linux: ubuntu-noble
package: deb
- linux: debian
package: deb
- linux: fedora
@ -123,6 +118,8 @@ jobs:
package: rpm
- linux: rhel9
package: rpm
- linux: ubuntu-jammy
package: deb
uses: ./.github/workflows/linux-build.yml
with:
RELEASE: ${{ matrix.release }}
@ -196,20 +193,12 @@ jobs:
needs: [wait-builds]
strategy:
matrix:
linux: [ubuntu, ubuntu-noble, debian, fedora, el, el9]
linux: [ubuntu, debian, fedora, el, el9, ubuntu-jammy]
arch: [amd64, arm64]
exclude:
- linux: ubuntu-noble
arch: arm64
include:
- release: latest
repo: bunkerweb
- linux: ubuntu
separator: _
suffix: ""
version: jammy
package: deb
- linux: ubuntu-noble
separator: _
suffix: ""
version: noble
@ -222,7 +211,7 @@ jobs:
- linux: fedora
separator: "-"
suffix: "1."
version: 39
version: 40
package: rpm
- linux: el
separator: "-"
@ -234,12 +223,14 @@ jobs:
suffix: "1."
version: 9
package: rpm
- linux: ubuntu-jammy
separator: _
suffix: ""
version: jammy
package: deb
- linux: ubuntu
arch: amd64
package_arch: amd64
- linux: ubuntu-noble
arch: amd64
package_arch: amd64
- linux: debian
arch: amd64
package_arch: amd64
@ -252,6 +243,9 @@ jobs:
- linux: el9
arch: amd64
package_arch: x86_64
- linux: ubuntu-jammy
arch: amd64
package_arch: amd64
- linux: ubuntu
arch: arm64
package_arch: arm64
@ -267,6 +261,9 @@ jobs:
- linux: el9
arch: arm64
package_arch: aarch64
- linux: ubuntu-jammy
arch: arm64
package_arch: arm64
uses: ./.github/workflows/push-packagecloud.yml
with:
SEPARATOR: ${{ matrix.separator }}

View file

@ -25,6 +25,6 @@ jobs:
results_format: sarif
publish_results: true
- name: "Upload SARIF results to code scanning"
uses: github/codeql-action/upload-sarif@9fdb3e49720b44c48891d036bb502feb25684276 # v3.25.6
uses: github/codeql-action/upload-sarif@2e230e8fe0ad3a14a340ad0815ddb96d599d2aff # v3.25.8
with:
sarif_file: results.sarif

View file

@ -28,7 +28,7 @@ jobs:
uses: azure/setup-kubectl@3e0aec4d80787158d308d7b364cb1b702e7feb7f # v4.0.0
if: inputs.TYPE == 'k8s'
with:
version: "v1.28.6"
version: "v1.29.1"
- name: Set up Python 3.12
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0
if: inputs.TYPE != 'k8s'

View file

@ -37,7 +37,7 @@ jobs:
- uses: azure/setup-kubectl@3e0aec4d80787158d308d7b364cb1b702e7feb7f # v4.0.0
if: inputs.TYPE == 'k8s'
with:
version: "v1.28.2"
version: "v1.29.1"
# Remove infra
- run: kubectl delete daemonsets,replicasets,services,deployments,pods,rc,ingress,statefulsets --all --all-namespaces --timeout=60s ; kubectl delete pvc --all --timeout=60s ; kubectl delete pv --all --timeout=60s
if: inputs.TYPE == 'k8s'

View file

@ -27,7 +27,7 @@ jobs:
- name: Checkout source code
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Login to ghcr
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@ -69,15 +69,12 @@ jobs:
- uses: azure/setup-kubectl@3e0aec4d80787158d308d7b364cb1b702e7feb7f # v4.0.0
if: inputs.TYPE == 'k8s'
with:
version: "v1.28.2"
version: "v1.29.1"
- uses: azure/setup-helm@fe7b79cd5ee1e45176fcad797de68ecaf3ca4814 # v4.2.0
if: inputs.TYPE == 'k8s'
- name: Pull BW linux ubuntu test image
if: inputs.TYPE == 'linux'
run: docker pull ghcr.io/bunkerity/ubuntu-tests:testing && docker tag ghcr.io/bunkerity/ubuntu-tests:testing local/ubuntu:latest
- name: Pull BW linux ubuntu noble test image
if: inputs.TYPE == 'linux'
run: docker pull ghcr.io/bunkerity/ubuntu-noble-tests:testing && docker tag ghcr.io/bunkerity/ubuntu-noble-tests:testing local/ubuntu-noble:latest
- name: Pull BW linux debian test image
if: inputs.TYPE == 'linux'
run: docker pull ghcr.io/bunkerity/debian-tests:testing && docker tag ghcr.io/bunkerity/debian-tests:testing local/debian:latest
@ -90,6 +87,9 @@ jobs:
- name: Pull BW linux rhel9 test image
if: inputs.TYPE == 'linux'
run: docker pull ghcr.io/bunkerity/rhel9-tests:testing && docker tag ghcr.io/bunkerity/rhel9-tests:testing local/rhel9:latest
- name: Pull BW linux ubuntu jammy test image
if: inputs.TYPE == 'linux'
run: docker pull ghcr.io/bunkerity/ubuntu-jammy-tests:testing && docker tag ghcr.io/bunkerity/ubuntu-jammy-tests:testing local/ubuntu-jammy:latest
# Do tests
- name: Run tests
if: inputs.TYPE == 'docker'
@ -124,12 +124,6 @@ jobs:
env:
TEST_DOMAINS: ${{ secrets.TEST_DOMAINS_LINUX }}
ROOT_DOMAIN: ${{ secrets.ROOT_DOMAIN }}
- name: Run Linux ubuntu noble tests
if: inputs.TYPE == 'linux'
run: export $(echo "$TEST_DOMAINS" | xargs) && ./tests/main.py "linux" "ubuntu-noble"
env:
TEST_DOMAINS: ${{ secrets.TEST_DOMAINS_LINUX }}
ROOT_DOMAIN: ${{ secrets.ROOT_DOMAIN }}
- name: Run Linux debian tests
if: inputs.TYPE == 'linux'
run: export $(echo "$TEST_DOMAINS" | xargs) && ./tests/main.py "linux" "debian"
@ -154,3 +148,9 @@ jobs:
env:
TEST_DOMAINS: ${{ secrets.TEST_DOMAINS_LINUX }}
ROOT_DOMAIN: ${{ secrets.ROOT_DOMAIN }}
- name: Run Linux ubuntu-jammy tests
if: inputs.TYPE == 'linux'
run: export $(echo "$TEST_DOMAINS" | xargs) && ./tests/main.py "linux" "ubuntu-jammy"
env:
TEST_DOMAINS: ${{ secrets.TEST_DOMAINS_LINUX }}
ROOT_DOMAIN: ${{ secrets.ROOT_DOMAIN }}

View file

@ -43,12 +43,10 @@ jobs:
packages: write
strategy:
matrix:
linux: [ubuntu, ubuntu-noble, debian, fedora, rhel, rhel9]
linux: [ubuntu, debian, fedora, rhel, rhel9, ubuntu-jammy]
include:
- linux: ubuntu
package: deb
- linux: ubuntu-noble
package: deb
- linux: debian
package: deb
- linux: fedora
@ -57,6 +55,8 @@ jobs:
package: rpm
- linux: rhel9
package: rpm
- linux: ubuntu-jammy
package: deb
uses: ./.github/workflows/linux-build.yml
with:
RELEASE: testing
@ -199,12 +199,12 @@ jobs:
packages: write
steps:
- name: Login to Docker Hub
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Login to ghcr
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@ -223,18 +223,12 @@ jobs:
needs: [staging-tests, tests-ui-linux, tests-core-linux]
strategy:
matrix:
linux: [ubuntu, ubuntu-noble, debian, fedora, el, el9]
linux: [ubuntu, debian, fedora, el, el9, ubuntu-jammy]
arch: [amd64]
include:
- release: testing
repo: bunkerweb
- linux: ubuntu
package_arch: amd64
separator: _
suffix: ""
version: jammy
package: deb
- linux: ubuntu-noble
package_arch: amd64
separator: _
suffix: ""
@ -250,7 +244,7 @@ jobs:
package_arch: x86_64
separator: "-"
suffix: "1."
version: 39
version: 40
package: rpm
- linux: el
package_arch: x86_64
@ -264,6 +258,12 @@ jobs:
suffix: "1."
version: 9
package: rpm
- linux: ubuntu-jammy
package_arch: amd64
separator: _
suffix: ""
version: jammy
package: deb
uses: ./.github/workflows/push-packagecloud.yml
with:
SEPARATOR: ${{ matrix.separator }}

View file

@ -12,15 +12,15 @@ on:
jobs:
tests:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
# Prepare
- name: Checkout source code
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Set up Python 3.9
- name: Set up Python 3.12
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0
with:
python-version: "3.9"
python-version: "3.12"
- name: Install Firefox manually and dependencies
run: |
sudo add-apt-repository ppa:mozillateam/ppa -y
@ -31,10 +31,12 @@ jobs:
Pin-Priority: 1001
Package: firefox
Pin: version 1:1snap1-0ubuntu2
Pin: version 1:1snap1-0ubuntu5
Pin-Priority: -1
' | sudo tee /etc/apt/preferences.d/mozilla-firefox
sudo apt install --no-install-recommends -y openssl git nodejs tar bzip2 wget curl grep libx11-xcb1 libappindicator3-1 libasound2 libdbus-glib-1-2 libxtst6 libxt6 php-fpm unzip firefox
export NEEDRESTART_SUSPEND=1
export DEBIAN_FRONTEND=noninteractive
sudo -E apt install --no-install-recommends -y openssl git nodejs tar bzip2 wget curl grep libx11-xcb1 libappindicator3-1 libasound2t64 libdbus-glib-1-2 libxtst6 libxt6 php-fpm unzip firefox
- name: Download geckodriver
uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e # v3.0.0
with:
@ -47,7 +49,7 @@ jobs:
sudo chmod +x /usr/local/bin/geckodriver
rm -f geckodriver.tar.gz
- name: Login to ghcr
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@ -61,11 +63,13 @@ jobs:
docker rm "$container_id"
- name: Install NGINX
run: |
sudo apt install -y gnupg2 ca-certificates lsb-release ubuntu-keyring
export NEEDRESTART_SUSPEND=1
export DEBIAN_FRONTEND=noninteractive
sudo -E apt install -y gnupg2 ca-certificates lsb-release ubuntu-keyring
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" | sudo tee /etc/apt/sources.list.d/nginx.list
sudo apt update
sudo apt install -y nginx=1.26.0-1~jammy
sudo -E apt install -y nginx=1.26.1-2~noble
- name: Fix version without a starting number
if: inputs.RELEASE == 'testing' || inputs.RELEASE == 'dev'
run: echo "force-bad-version" | sudo tee -a /etc/dpkg/dpkg.cfg
@ -75,9 +79,9 @@ jobs:
echo "127.0.0.1 www.example.com" | sudo tee -a /etc/hosts
echo "127.0.0.1 app1.example.com" | sudo tee -a /etc/hosts
echo "127.0.0.1 bwadm.example.com" | sudo tee -a /etc/hosts
sudo cp ./tests/www-deb.conf /etc/php/8.1/fpm/pool.d/www.conf
sudo systemctl stop php8.1-fpm
sudo systemctl start php8.1-fpm
sudo cp ./tests/www-deb.conf /etc/php/8.3/fpm/pool.d/www.conf
sudo systemctl stop php8.3-fpm
sudo systemctl start php8.3-fpm
# BunkerWeb
sudo mkdir -p /etc/bunkerweb
echo "SERVER_NAME=www.example.com" | sudo tee /etc/bunkerweb/variables.env
@ -92,12 +96,15 @@ jobs:
sudo chown nginx:nginx /etc/bunkerweb/variables.env
sudo chmod 777 /etc/bunkerweb/variables.env
- name: Install BunkerWeb
run: sudo apt install -fy /tmp/bunkerweb.deb
run: |
export NEEDRESTART_SUSPEND=1
export DEBIAN_FRONTEND=noninteractive
sudo -E apt install -fy /tmp/bunkerweb.deb
- name: Run tests
run: |
export MAKEFLAGS="-j $(nproc)"
pip install --no-cache-dir --ignore-installed --require-hashes -r src/deps/requirements-deps.txt
pip install --break-system-packages --no-cache-dir --ignore-installed --require-hashes -r src/deps/requirements-deps.txt
cd tests/core/${{ inputs.TEST }}
find . -name "requirements.txt" -exec pip install --no-cache-dir --require-hashes --no-deps -r {} \;
find . -name "requirements.txt" -exec pip install --break-system-packages --no-cache-dir --require-hashes --no-deps -r {} \;
sudo truncate -s 0 /var/log/bunkerweb/error.log
./test.sh "linux"

View file

@ -18,7 +18,7 @@ jobs:
- name: Checkout source code
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Login to ghcr
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0
with:
registry: ghcr.io
username: ${{ github.actor }}

View file

@ -12,15 +12,15 @@ on:
jobs:
tests:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
# Prepare
- name: Checkout source code
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Set up Python 3.9
- name: Set up Python 3.12
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0
with:
python-version: "3.9"
python-version: "3.12"
- name: Install Firefox manually and dependencies
run: |
sudo add-apt-repository ppa:mozillateam/ppa -y
@ -31,10 +31,12 @@ jobs:
Pin-Priority: 1001
Package: firefox
Pin: version 1:1snap1-0ubuntu2
Pin: version 1:1snap1-0ubuntu5
Pin-Priority: -1
' | sudo tee /etc/apt/preferences.d/mozilla-firefox
sudo apt install --no-install-recommends -y openssl git nodejs tar bzip2 wget curl grep libx11-xcb1 libappindicator3-1 libasound2 libdbus-glib-1-2 libxtst6 libxt6 php-fpm unzip firefox
export NEEDRESTART_SUSPEND=1
export DEBIAN_FRONTEND=noninteractive
sudo -E apt install --no-install-recommends -y openssl git nodejs tar bzip2 wget curl grep libx11-xcb1 libappindicator3-1 libasound2t64 libdbus-glib-1-2 libxtst6 libxt6 php-fpm unzip firefox
- name: Download geckodriver
uses: nick-fields/retry@7152eba30c6575329ac0576536151aca5a72780e # v3.0.0
with:
@ -47,7 +49,7 @@ jobs:
sudo chmod +x /usr/local/bin/geckodriver
rm -f geckodriver.tar.gz
- name: Login to ghcr
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@ -61,11 +63,13 @@ jobs:
docker rm "$container_id"
- name: Install NGINX
run: |
sudo apt install -y gnupg2 ca-certificates lsb-release ubuntu-keyring
export NEEDRESTART_SUSPEND=1
export DEBIAN_FRONTEND=noninteractive
sudo -E apt install -y gnupg2 ca-certificates lsb-release ubuntu-keyring
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" | sudo tee /etc/apt/sources.list.d/nginx.list
sudo apt update
sudo apt install -y nginx=1.26.0-1~jammy
sudo -E apt install -y nginx=1.26.1-2~noble
- name: Fix version without a starting number
if: inputs.RELEASE == 'testing' || inputs.RELEASE == 'dev' || inputs.RELEASE == 'ui'
run: echo "force-bad-version" | sudo tee -a /etc/dpkg/dpkg.cfg
@ -98,12 +102,15 @@ jobs:
sudo chown nginx:nginx /etc/bunkerweb/variables.env /etc/bunkerweb/ui.env
sudo chmod 777 /etc/bunkerweb/variables.env /etc/bunkerweb/ui.env
- name: Install BunkerWeb
run: sudo apt install -fy /tmp/bunkerweb.deb
run: |
export NEEDRESTART_SUSPEND=1
export DEBIAN_FRONTEND=noninteractive
sudo -E apt install -fy /tmp/bunkerweb.deb
- name: Run tests
run: |
export MAKEFLAGS="-j $(nproc)"
pip install --no-cache-dir --ignore-installed --require-hashes -r src/deps/requirements-deps.txt
pip install --no-cache-dir --require-hashes -r tests/ui/requirements.txt
pip install --break-system-packages --no-cache-dir --ignore-installed --require-hashes -r src/deps/requirements-deps.txt
pip install --break-system-packages --no-cache-dir --require-hashes -r tests/ui/requirements.txt
cd ./tests/ui
touch test.txt
zip test.zip test.txt

View file

@ -17,7 +17,7 @@ jobs:
- name: Checkout source code
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Login to ghcr
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 # v3.2.0
with:
registry: ghcr.io
username: ${{ github.actor }}

View file

@ -13,3 +13,7 @@ src/ui/templates/account.html:hashicorp-tf-password:162
src/common/core/errors/files/error.html:aws-access-token:20
src/deps/src/nginx/src/event/quic/ngx_event_quic_protection.c:generic-api-key:164
src/deps/src/nginx/src/event/quic/ngx_event_quic_protection.c:generic-api-key:165
src/deps/src/lua-resty-openssl/lib/resty/openssl/pkey.lua:generic-api-key:438
src/deps/src/lua-resty-openssl/lib/resty/openssl/pkey.lua:generic-api-key:439
src/deps/src/lua-resty-openssl/lib/resty/openssl/pkey.lua:generic-api-key:440
src/deps/src/lua-resty-openssl/lib/resty/openssl/pkey.lua:generic-api-key:441

View file

@ -36,7 +36,7 @@ repos:
exclude: ^src/(bw/lua/middleclass.lua|common/core/antibot/captcha.lua)$
- repo: https://github.com/lunarmodules/luacheck
rev: 418f48976c73be697fe64b0eba9ea9821ac9bca8 # frozen: v1.1.2
rev: cc089e3f65acdd1ef8716cc73a3eca24a6b845e4 # frozen: v1.2.0
hooks:
- id: luacheck
exclude: ^src/(bw/lua/middleclass.lua|common/core/antibot/captcha.lua)$
@ -57,7 +57,7 @@ repos:
exclude: ^tests/
- repo: https://github.com/codespell-project/codespell
rev: 6e41aba91fb32e9feb741a6258eefeb9c6e4a482 # frozen: v2.2.6
rev: 193cd7d27cd571f79358af09a8fb8997e54f8fff # frozen: v2.3.0
hooks:
- id: codespell
name: Codespell Spell Checker

View file

@ -2,11 +2,29 @@
## v1.5.8 - ????/??/??
- [LINUX] Support Fedora 40 and drop support of Fedora 39
- [BUGFIX] Fix potential errors when upgrading from a previous version
- [BUGFIX] Fix rare bug on the web UI when editing the SERVER_NAME setting of a service
- [BUGFIX] Fix potential race conditions between the autoconf and the scheduler waiting for each other indefinitely
- [BUGFIX] Fix Let's Encrypt certificate renewal when a certificate date changes by forcing the renewal
- [BUGFIX] Fix issues with k8s integration and the save_config.py script
- [FEATURE] Add nightly build of the OWASP coreruleset that are automatically downloaded and updated
- [FEATURE] Enhance security on error pages, default server page and loading page by adding a custom `Content-Security-Policy` header with nonces and removing the `Server` header
- [FEATURE] Add new DATABASE_URI_READONLY setting to allow setting up a fallback read-only database URI in case the main database URI is not available
- [FEATURE] Add automatic fallback to either read-only on the primary database or to the read-only database URI when the main database URI is not available and automatically switch back to the main database URI when it becomes available again
- [FEATURE] Add experimental support of HTTP/3 (QUIC)
- [FEATURE] Optimize the way the scheduler handles jobs and the way the jobs are executed
- [FEATURE] Optimize the way the cache files are being refreshed from the database
- [UI] Force HTTPS on setup wizard
- [UI] Fallback to self-signed certificate when UI is installed with setup wizard and let's encrypt is not used
- [UI] Add OVERRIDE_ADMIN_CREDS environment variable to allow overriding the default admin credentials even if an admin user already exists
- [UI] Optimize the way the UI handles the requests and the responses
- [AUTOCONF] Refactor Autoconf config parsing and saving logic so that it doesn't override the scheduler or UI config every time
- [MISC] Update logger format and datefmt for better readability
- [DEPS] Updated NGINX version to v1.26.1
- [DEPS] Updated stream-lua-nginx-module version to the latest commit to incorporate the latest changes and fixes for NGINX v1.26
- [DEPS] Updated coreruleset-v4 version to v4.3.0
- [DEPS] Updated lua-resty-openssl version to v1.4.0
## v1.5.7 - 2024/05/14

View file

@ -214,6 +214,16 @@ In other words, the scheduler is the brain of BunkerWeb.
# Setup
## BunkerWeb Cloud
<p align="center">
<img alt="Docker banner" src="https://github.com/bunkerity/bunkerweb/raw/v1.5.8/docs/assets/img/bunkerweb-cloud.webp" />
</p>
BunkerWeb Cloud is the easiest way to get started with BunkerWeb. It offers you a fully managed BunkerWeb service with no hassle. Think of a like a BunkerWeb-as-a-Service !
You will find more information about BunkerWeb Cloud beta [here](https://www.bunkerweb.io/cloud?utm_campaign=self&utm_source=docs) and you can apply for free [in the BunkerWeb panel](https://panel.bunkerweb.io/order/bunkerweb-cloud/14?utm_campaign=self&utm_source=docs).
## Docker
<p align="center">
@ -273,9 +283,9 @@ You will find more information in the [Kubernetes section](https://docs.bunkerwe
List of supported Linux distros :
- Debian 12 "Bookworm"
- Ubuntu 22.04 "Jammy"
- Ubuntu 24.04 "Noble"
- Fedora 39
- Ubuntu 22.04 "Noble"
- Ubuntu 24.04 "Jammy"
- Fedora 40
- RHEL 8.9
- RHEL 9.4

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View file

@ -1,5 +1,19 @@
# Integrations
## BunkerWeb Cloud
<figure markdown>
![Overview](assets/img/bunkerweb-cloud.webp){ align=center, width="600" }
<figcaption>BunkerWeb Cloud</figcaption>
</figure>
!!! example "Beta phase"
BunkerWeb Cloud offer is in beta phase. We are actively getting feedbacks from our precious beta tester to improve the offer.
BunkerWeb Cloud is the easiest way to get started with BunkerWeb. It offers you a fully managed BunkerWeb service with no hassle. Think of a like a BunkerWeb-as-a-Service !
You will find more information about BunkerWeb Cloud beta [here](https://www.bunkerweb.io/cloud?utm_campaign=self&utm_source=docs) and you can apply for free [in the BunkerWeb panel](https://panel.bunkerweb.io/order/bunkerweb-cloud/14?utm_campaign=self&utm_source=docs).
## Docker
<figure markdown>
@ -316,11 +330,11 @@ Supported Linux distributions for BunkerWeb (amd64/x86_64 and arm64/aarch64 arch
- Debian 12 "Bookworm"
- Ubuntu 22.04 "Jammy"
- Ubuntu 24.04 "Noble"
- Fedora 39
- Fedora 40
- Red Hat Enterprise Linux (RHEL) 8.9
- Red Hat Enterprise Linux (RHEL) 9.4
Please ensure that you have **NGINX 1.26.0 installed before installing BunkerWeb**. For all distributions, except Fedora, it is mandatory to use prebuilt packages from the [official NGINX repository](https://nginx.org/en/linux_packages.html). Compiling NGINX from source or using packages from different repositories will not work with the official prebuilt packages of BunkerWeb. However, you have the option to build BunkerWeb from source.
Please ensure that you have **NGINX 1.26.1 installed before installing BunkerWeb**. For all distributions, except Fedora, it is mandatory to use prebuilt packages from the [official NGINX repository](https://nginx.org/en/linux_packages.html). Compiling NGINX from source or using packages from different repositories will not work with the official prebuilt packages of BunkerWeb. However, you have the option to build BunkerWeb from source.
To simplify the installation process, Linux package repositories for BunkerWeb are available on [PackageCloud](https://packagecloud.io/bunkerity/bunkerweb). They provide a bash script that automatically adds and trusts the repository. You can follow the provided script for automatic setup, or opt for [manual installation](https://packagecloud.io/bunkerity/bunkerweb/install) instructions if you prefer.
@ -337,11 +351,11 @@ To simplify the installation process, Linux package repositories for BunkerWeb a
| sudo tee /etc/apt/sources.list.d/nginx.list
```
You should now be able to install NGINX 1.26.0 :
You should now be able to install NGINX 1.26.1 :
```shell
sudo apt update && \
sudo apt install -y nginx=1.26.0-1~$(lsb_release -cs)
sudo apt install -y nginx=1.26.1-2~$(lsb_release -cs)
```
!!! warning "Testing/dev version"
@ -384,11 +398,11 @@ To simplify the installation process, Linux package repositories for BunkerWeb a
| sudo tee /etc/apt/sources.list.d/nginx.list
```
You should now be able to install NGINX 1.26.0 :
You should now be able to install NGINX 1.26.1 :
```shell
sudo apt update && \
sudo apt install -y nginx=1.26.0-1~$(lsb_release -cs)
sudo apt install -y nginx=1.26.1-2~$(lsb_release -cs)
```
!!! warning "Testing/dev version"
@ -420,10 +434,17 @@ To simplify the installation process, Linux package repositories for BunkerWeb a
=== "Fedora"
Fedora already provides NGINX 1.26.0 that we support :
!!! info "Fedora Update Testing"
If you can't find the NGINX version listed in the stable repository, you can enable the `updates-testing` repository :
```shell
sudo dnf config-manager --set-enabled updates-testing
```
Fedora already provides NGINX 1.26.1 that we support :
```shell
sudo dnf install -y nginx-1.26.0
sudo dnf install -y nginx-1.26.1
```
Optional step : if you want to automatically enable the [setup wizard](web-ui.md#setup-wizard) when BunkerWeb is installed, export the following variable :
@ -471,10 +492,10 @@ To simplify the installation process, Linux package repositories for BunkerWeb a
module_hotfixes=true
```
You should now be able to install NGINX 1.26.0 :
You should now be able to install NGINX 1.26.1 :
```shell
sudo dnf install nginx-1.26.0
sudo dnf install nginx-1.26.1
```
Optional step : if you want to automatically enable the [setup wizard](web-ui.md#setup-wizard) when BunkerWeb is installed, export the following variable :

View file

@ -10,7 +10,7 @@ But dedicating time to a specific technology may not be easy depending on your b
Getting professional services in addition to the open-source or PRO version is the ideal solution to cover your business needs. You can focus on your top priorities and rely on a trusted partner when it comes to web security.
Please note that professionnal services are directly offered by [Bunkerity](https://www.bunkerity.com/?utm_campaign=self&utm_source=doc), the company maintaining the BunkerWeb project, through our [BunkerWeb Panel](https://panel.bunkerweb.io/?utm_campaign=self&utm_source=doc) online platform.
Please note that professional services are directly offered by [Bunkerity](https://www.bunkerity.com/?utm_campaign=self&utm_source=doc), the company maintaining the BunkerWeb project, through our [BunkerWeb Panel](https://panel.bunkerweb.io/?utm_campaign=self&utm_source=doc) online platform.
## Which professional services do you offer ?

View file

@ -1,4 +1,4 @@
mike==2.1.1
mkdocs-material[imaging]==9.5.24
mkdocs-print-site-plugin==2.4.1
mkdocs-material[imaging]==9.5.26
mkdocs-print-site-plugin==2.5.0
pytablewriter==1.2.0

View file

@ -16,9 +16,9 @@ cairosvg==2.7.1 \
--hash=sha256:432531d72347291b9a9ebfb6777026b607563fd8719c46ee742db0aef7271ba0 \
--hash=sha256:8a5222d4e6c3f86f1f7046b63246877a63b49923a1cd202184c3a634ef546b3b
# via mkdocs-material
certifi==2024.2.2 \
--hash=sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f \
--hash=sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1
certifi==2024.6.2 \
--hash=sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516 \
--hash=sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56
# via requests
cffi==1.16.0 \
--hash=sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc \
@ -317,9 +317,9 @@ mkdocs-get-deps==0.2.0 \
--hash=sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c \
--hash=sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134
# via mkdocs
mkdocs-material==9.5.24 \
--hash=sha256:02d5aaba0ee755e707c3ef6e748f9acb7b3011187c0ea766db31af8905078a34 \
--hash=sha256:e12cd75954c535b61e716f359cf2a5056bf4514889d17161fdebd5df4b0153c6
mkdocs-material==9.5.26 \
--hash=sha256:56aeb91d94cffa43b6296fa4fbf0eb7c840136e563eecfd12c2d9e92e50ba326 \
--hash=sha256:5d01fb0aa1c7946a1e3ae8689aa2b11a030621ecb54894e35aabb74c21016312
# via
# -r requirements.in
# mkdocs-print-site-plugin
@ -327,9 +327,9 @@ mkdocs-material-extensions==1.3.1 \
--hash=sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443 \
--hash=sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31
# via mkdocs-material
mkdocs-print-site-plugin==2.4.1 \
--hash=sha256:8c05bdd6b34095fdfdb77a37a117106d4ba362ac5145a1664a3bef53b8cc9ba5 \
--hash=sha256:c62eda1d47b65e48aa5a9316cb8ea93c035a342b5648b84bd892f48729aea6c9
mkdocs-print-site-plugin==2.5.0 \
--hash=sha256:48b3d41ae80384de72062b2712fce677f2e46d8364d9fe603ba837b0cf7156a4 \
--hash=sha256:95dccc8d5cc8a59da67815a2d3304ef0101b065e363f2b9ac919c23d6196dd24
# via -r requirements.in
packaging==24.0 \
--hash=sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5 \
@ -600,9 +600,9 @@ regex==2024.5.15 \
--hash=sha256:f8ec0c2fea1e886a19c3bee0cd19d862b3aa75dcdfb42ebe8ed30708df64687a \
--hash=sha256:f9ebd0a36102fcad2f03696e8af4ae682793a5d30b46c647eaf280d6cfb32796
# via mkdocs-material
requests==2.32.2 \
--hash=sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289 \
--hash=sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c
requests==2.32.3 \
--hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \
--hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6
# via
# importlib-metadata
# importlib-resources
@ -685,7 +685,7 @@ webencodings==0.5.1 \
# via
# cssselect2
# tinycss2
zipp==3.18.2 \
--hash=sha256:6278d9ddbcfb1f1089a88fde84481528b07b0e10474e09dcfe53dad4069fa059 \
--hash=sha256:dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e
zipp==3.19.2 \
--hash=sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19 \
--hash=sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c
# via pytablewriter

View file

@ -137,7 +137,7 @@ Besides the HTTPS / SSL/TLS configuration, the following settings related to HTT
| `AUTO_REDIRECT_HTTP_TO_HTTPS` | `yes` | When set to `yes`, will redirect every HTTP request to HTTPS only if BunkerWeb is configured with HTTPS. |
| `SSL_PROTOCOLS` | `TLSv1.2 TLSv1.3` | List of supported SSL/TLS protocols when SSL is enabled. |
| `HTTP2` | `yes` | When set to `yes`, will enable HTTP2 protocol support when using HTTPS. |
| `HTTP3` | `yes` | When set to `yes`, will enable HTTP3 protocol support when using HTTPS. |
| `HTTP3` | `no` | When set to `yes`, will enable HTTP3 protocol support when using HTTPS. |
| `HTTP3_ALT_SVC_PORT` | `443` | HTTP3 alternate service port. This value will be used as part of the Alt-Svc header. |
| `LISTEN_HTTP` | `yes` | When set to `no`, BunkerWeb will not listen for HTTP requests. Useful if you want HTTPS only for example. |

View file

@ -229,7 +229,7 @@
Get support and more information :
- [Order professionnal support](https://panel.bunkerweb.io/?utm_source=doc&utm_campaign=self)
- [Order professional support](https://panel.bunkerweb.io/?utm_source=doc&utm_campaign=self)
- [Create an issue on GitHub](https://github.com/bunkerity/bunkerweb/issues)
- [Join the BunkerWeb Discord server](https://discord.bunkerity.com)
@ -239,27 +239,40 @@
=== "SQLite"
1. **Remove the existing database file.**
1. **Stop the Stack.**
```bash
docker compose down
```
2. **Remove the existing database file.**
```bash
docker exec -u 0 -i <scheduler_container> rm -f /var/lib/bunkerweb/db.sqlite3
```
2. **Restore the backup.**
3. **Restore the backup.**
```bash
docker exec -i -T <scheduler_container> sqlite3 /var/lib/bunkerweb/db.sqlite3 < /path/to/backup/directory/backup.sql
```
=== "MySQL/MariaDB"
3. **Stop the Scheduler container.**
4. **Fix permissions.**
```bash
docker compose down <scheduler_container>
docker exec -u 0 -i <scheduler_container> chown root:nginx /var/lib/bunkerweb/db.sqlite3
docker exec -u 0 -i <scheduler_container> chmod 770 /var/lib/bunkerweb/db.sqlite3
```
4. **Restore the backup.**
=== "MySQL/MariaDB"
5. **Stop the Stack.**
```bash
docker compose down
```
6. **Restore the backup.**
```bash
docker exec -e MYSQL_PWD=<your_password> -i -T <database_container> mysql -u <username> <database_name> < /path/to/backup/directory/backup.sql
@ -267,25 +280,25 @@
=== "PostgreSQL"
5. **Stop the Scheduler container.**
7. **Stop the Stack.**
```bash
docker compose down <scheduler_container>
docker compose down
```
6. **Remove the existing database.**
8. **Remove the existing database.**
```bash
docker exec -i <database_container> dropdb -U <username> --force <database_name>
```
7. **Recreate the database.**
9. **Recreate the database.**
```bash
docker exec -i <database_container> createdb -U <username> <database_name>
```
8. **Restore the backup.**
10. **Restore the backup.**
```bash
docker exec -i -T <database_container> psql -U <username> -d <database_name> < /path/to/backup/directory/backup.sql
@ -309,10 +322,9 @@
...
```
3. **Restart the containers**.
3. **Start the containers**.
```bash
docker compose down
docker compose up -d
```
@ -321,8 +333,7 @@
4. **Stop the services**.
```bash
systemctl stop bunkerweb
systemctl stop bunkerweb-ui
systemctl stop bunkerweb bunkerweb-ui
```
5. **Restore the backup**.
@ -330,8 +341,10 @@
=== "SQLite"
```bash
rm -f /var/lib/bunkerweb/db.sqlite3
sqlite3 /var/lib/bunkerweb/db.sqlite3 < /path/to/backup/directory/backup.sql
sudo rm -f /var/lib/bunkerweb/db.sqlite3
sudo sqlite3 /var/lib/bunkerweb/db.sqlite3 < /path/to/backup/directory/backup.sql
sudo chown root:nginx /var/lib/bunkerweb/db.sqlite3
sudo chmod 770 /var/lib/bunkerweb/db.sqlite3
```
=== "MySQL/MariaDB"

View file

@ -10,8 +10,9 @@ services:
context: ../..
dockerfile: ./src/bw/Dockerfile
ports:
- 80:8080
- 443:8443
- 80:8080/tcp
- 443:8443/tcp
- 443:8443/udp
labels:
- "bunkerweb.INSTANCE=yes"
environment:

View file

@ -10,8 +10,9 @@ services:
context: ../..
dockerfile: ./src/bw/Dockerfile
ports:
- 80:8080
- 443:8443
- 80:8080/tcp
- 443:8443/tcp
- 443:8443/udp
labels:
- "bunkerweb.INSTANCE=yes"
environment:

View file

@ -10,8 +10,9 @@ services:
context: ../..
dockerfile: ./src/bw/Dockerfile
ports:
- 80:8080
- 443:8443
- 80:8080/tcp
- 443:8443/tcp
- 443:8443/udp
labels:
- "bunkerweb.INSTANCE=yes"
environment:

View file

@ -10,8 +10,9 @@ services:
context: ../..
dockerfile: ./src/bw/Dockerfile
ports:
- 80:8080
- 443:8443
- 80:8080/tcp
- 443:8443/tcp
- 443:8443/udp
labels:
- "bunkerweb.INSTANCE=yes"
environment:

View file

@ -10,8 +10,9 @@ services:
context: ../..
dockerfile: ./src/bw/Dockerfile
ports:
- 80:8080
- 443:8443
- 80:8080/tcp
- 443:8443/tcp
- 443:8443/udp
labels:
- "bunkerweb.INSTANCE=yes"
environment:

View file

@ -4,8 +4,9 @@ services:
context: ../..
dockerfile: ./src/bw/Dockerfile
ports:
- 80:8080
- 443:8443
- 80:8080/tcp
- 443:8443/tcp
- 443:8443/udp
labels:
- "bunkerweb.INSTANCE=yes"
environment:

View file

@ -8,8 +8,9 @@ services:
context: ../..
dockerfile: ./src/bw/Dockerfile
ports:
- 80:8080
- 443:8443
- 80:8080/tcp
- 443:8443/tcp
- 443:8443/udp
labels:
- "bunkerweb.INSTANCE=yes"
environment:

View file

@ -8,8 +8,9 @@ services:
context: ../..
dockerfile: ./src/bw/Dockerfile
ports:
- 80:8080
- 443:8443
- 80:8080/tcp
- 443:8443/tcp
- 443:8443/udp
labels:
- "bunkerweb.INSTANCE=yes"
environment:

View file

@ -8,8 +8,9 @@ services:
context: ../..
dockerfile: ./src/bw/Dockerfile
ports:
- 80:8080
- 443:8443
- 80:8080/tcp
- 443:8443/tcp
- 443:8443/udp
labels:
- "bunkerweb.INSTANCE=yes"
environment:

View file

@ -4,8 +4,9 @@ services:
context: ../..
dockerfile: ./src/bw/Dockerfile
ports:
- 80:8080
- 443:8443
- 80:8080/tcp
- 443:8443/tcp
- 443:8443/udp
labels:
- "bunkerweb.INSTANCE=yes"
environment:

View file

@ -4,13 +4,13 @@ from contextlib import suppress
from datetime import datetime
from os import getenv
from time import sleep
from typing import Any, Dict, List, Optional
from ConfigCaller import ConfigCaller # type: ignore
from Database import Database # type: ignore
from logger import setup_logger # type: ignore
class Config(ConfigCaller):
class Config:
def __init__(self):
super().__init__()
self.__logger = setup_logger("Config", getenv("LOG_LEVEL", "INFO"))
@ -40,25 +40,24 @@ class Config(ConfigCaller):
self._settings.update(plugin["settings"])
def __get_full_env(self) -> dict:
env_instances = {"SERVER_NAME": ""}
for instance in self.__instances:
for variable, value in instance["env"].items():
env_instances[variable] = value
env_services = {}
config = {"SERVER_NAME": "", "MULTISITE": "yes"}
for service in self.__services:
server_name = service["SERVER_NAME"].split(" ")[0]
if not server_name:
continue
for variable, value in service.items():
env_services[f"{server_name}_{variable}"] = value
env_instances["SERVER_NAME"] += f" {server_name}"
env_instances["SERVER_NAME"] = env_instances["SERVER_NAME"].strip()
return self._full_env(env_instances, env_services)
if self._db.is_setting(variable, multisite=True):
config[f"{server_name}_{variable}"] = value
config["SERVER_NAME"] += f" {server_name}"
config["SERVER_NAME"] = config["SERVER_NAME"].strip()
return config
def update_needed(self, instances, services, configs={}) -> bool:
def update_needed(self, instances: List[Dict[str, Any]], services: List[Dict[str, str]], configs: Optional[Dict[str, Dict[str, bytes]]] = None) -> bool:
if instances != self.__instances:
return True
elif services != self.__services:
return True
elif configs != self.__configs:
elif (configs or {}) != self.__configs:
return True
return False
@ -76,15 +75,31 @@ class Config(ConfigCaller):
):
ready = True
continue
else:
self.__logger.warning("Scheduler is already applying a configuration, retrying in 5 seconds ...")
def have_to_wait(self) -> bool:
db_metadata = self._db.get_metadata()
return isinstance(db_metadata, str) or any(
v
for k, v in db_metadata.items()
if k in ("custom_configs_changed", "external_plugins_changed", "pro_plugins_changed", "config_changed", "instances_changed")
)
def apply(
self, instances: List[Dict[str, Any]], services: List[Dict[str, str]], configs: Optional[Dict[str, Dict[str, bytes]]] = None, first: bool = False
) -> bool:
success = True
err = self._try_database_readonly()
if err:
return False
while not self._db.is_initialized():
self.__logger.warning("Database is not initialized, retrying in 5 seconds ...")
sleep(5)
if not ready:
raise Exception("Too many retries while waiting for scheduler to apply configuration...")
self.wait_applying()
def apply(self, instances, services, configs={}, first=False) -> bool:
success = True
configs = configs or {}
changes = []
if instances != self.__instances or first:
@ -116,37 +131,10 @@ class Config(ConfigCaller):
custom_configs.append(
{
"value": data,
"exploded": [
site,
config_type,
name.replace(".conf", ""),
],
"exploded": [site, config_type, name.replace(".conf", "")],
}
)
current_time = datetime.now()
ready = False
while not ready and (datetime.now() - current_time).seconds < 120:
db_metadata = self._db.get_metadata()
if isinstance(db_metadata, str) or not db_metadata["is_initialized"]:
self.__logger.warning("Database is not initialized, retrying in 5s ...")
sleep(5)
continue
ready = True
if not ready:
self.__logger.error(f"Timeout while waiting for database to be initialized, ignoring changes ...\ndb data: {db_metadata}")
return False
# wait until changes are applied
ready = False
with suppress(BaseException):
self.wait_applying()
ready = True
if not ready:
self.__logger.warning("Timeout while waiting for scheduler to apply configuration, continuing anyway...")
# update instances in database
if "instances" in changes:
err = self._db.update_instances(self.__instances, "autoconf", changed=False)
@ -154,11 +142,13 @@ class Config(ConfigCaller):
self.__logger.error(f"Failed to update instances: {err}")
# save config to database
changed_plugins = []
if "config" in changes:
err = self._db.save_config(self.__config, "autoconf", changed=False)
if err:
if isinstance(err, str):
success = False
self.__logger.error(f"Can't save config in database: {err}, config may not work as expected")
changed_plugins = err
# save custom configs to database
if "custom_configs" in changes:
@ -168,10 +158,40 @@ class Config(ConfigCaller):
self.__logger.error(f"Can't save autoconf custom configs in database: {err}, custom configs may not work as expected")
# update changes in db
ret = self._db.checked_changes(changes, value=True)
ret = self._db.checked_changes(changes, plugins_changes=changed_plugins, value=True)
if ret:
self.__logger.error(f"An error occurred when setting the changes to checked in the database : {ret}")
self.__logger.info("Successfully saved new configuration 🚀")
return success
def _try_database_readonly(self) -> bool:
if not self._db.readonly:
try:
self._db.test_write()
except BaseException:
self._db.readonly = True
return True
if self._db.database_uri and self._db.readonly:
try:
self._db.retry_connection(pool_timeout=1)
self._db.retry_connection(log=False)
self._db.readonly = False
self.__logger.info("The database is no longer read-only, defaulting to read-write mode")
except BaseException:
try:
self._db.retry_connection(readonly=True, pool_timeout=1)
self._db.retry_connection(readonly=True, log=False)
except BaseException:
if self._db.database_uri_readonly:
with suppress(BaseException):
self._db.retry_connection(fallback=True, pool_timeout=1)
self._db.retry_connection(fallback=True, log=False)
self._db.readonly = True
if self._db.readonly:
self.__logger.error("Database is in read-only mode, configuration will not be saved")
return self._db.readonly

View file

@ -66,10 +66,6 @@ class Controller(Config):
def _to_services(self, controller_service):
pass
@abstractmethod
def _get_static_services(self):
pass
def _set_autoconf_load_db(self):
if not self._loaded:
ret = self._db.set_metadata({"autoconf_loaded": True})
@ -79,10 +75,12 @@ class Controller(Config):
self._loaded = True
def get_services(self):
while not self._get_controller_services():
sleep(1)
services = []
for controller_service in self._get_controller_services():
services.extend(self._to_services(controller_service))
services.extend(self._get_static_services())
return services
@abstractmethod

View file

@ -1,5 +1,6 @@
#!/usr/bin/env python3
from time import sleep
from typing import Any, Dict, List
from docker import DockerClient
from re import compile as re_compile
@ -22,54 +23,25 @@ class DockerController(Controller):
return self.__client.containers.list(filters={"label": "bunkerweb.SERVER_NAME"})
def _to_instances(self, controller_instance) -> List[dict]:
return [
{
"name": controller_instance.name,
"hostname": controller_instance.name,
"health": controller_instance.status == "running" and controller_instance.attrs["State"]["Health"]["Status"] == "healthy",
"env": self._get_scheduler_env(),
}
]
instance = {}
instance["name"] = controller_instance.name
instance["hostname"] = controller_instance.name
instance["health"] = controller_instance.status == "running" and controller_instance.attrs["State"]["Health"]["Status"] == "healthy"
instance["env"] = {}
for env in controller_instance.attrs["Config"]["Env"]:
variable = env.split("=")[0]
value = env.replace(f"{variable}=", "", 1)
instance["env"][variable] = value
return [instance]
def _to_services(self, controller_service) -> List[dict]:
service = {}
for variable, value in controller_service.labels.items():
if not variable.startswith("bunkerweb."):
continue
real_variable = variable.replace("bunkerweb.", "", 1)
if not self._is_setting_context(real_variable, "multisite"):
continue
service[real_variable] = value
service[variable.replace("bunkerweb.", "", 1)] = value
return [service]
def _get_scheduler_env(self) -> Dict[str, str]:
env = {}
for instance in self.__client.containers.list(filters={"label": "bunkerweb.type=scheduler"}):
if not instance.attrs or not instance.attrs.get("Config", {}).get("Env"):
continue
for env_var in instance.attrs["Config"]["Env"]:
variable = env_var.split("=")[0]
value = env_var.replace(f"{variable}=", "", 1)
env[variable] = value
return env
def _get_static_services(self) -> List[dict]:
services = []
variables = self._get_scheduler_env()
if "SERVER_NAME" in variables and variables["SERVER_NAME"].strip():
for server_name in variables["SERVER_NAME"].strip().split(" "):
if not server_name:
continue
service = {"SERVER_NAME": server_name}
for variable, value in variables.items():
prefix = variable.split("_")[0]
real_variable = variable.replace(f"{prefix}_", "", 1)
if prefix == server_name and self._is_setting_context(real_variable, "multisite"):
service[real_variable] = value
services.append(service)
return services
def get_configs(self) -> Dict[str, Dict[str, Any]]:
configs = {config_type: {} for config_type in self._supported_config_types}
# get site configs from labels
@ -112,21 +84,34 @@ class DockerController(Controller):
def process_events(self):
self._set_autoconf_load_db()
for event in self.__client.events(decode=True, filters={"type": "container"}):
applied = False
try:
if not self.__process_event(event):
continue
self.wait_applying()
self._update_settings()
self._instances = self.get_instances()
self._services = self.get_services()
self._configs = self.get_configs()
if not self.update_needed(self._instances, self._services, configs=self._configs):
continue
self._logger.info("Caught Docker event, deploying new configuration ...")
if not self.apply_config():
self._logger.error("Error while deploying new configuration")
else:
self._logger.info("Successfully deployed new configuration 🚀")
self._set_autoconf_load_db()
to_apply = False
while not applied:
waiting = self.have_to_wait()
self._update_settings()
self._instances = self.get_instances()
self._services = self.get_services()
self._configs = self.get_configs()
if not to_apply and not self.update_needed(self._instances, self._services, configs=self._configs):
applied = True
continue
to_apply = True
if waiting:
sleep(1)
continue
self._logger.info("Caught Docker event, deploying new configuration ...")
if not self.apply_config():
self._logger.error("Error while deploying new configuration")
else:
self._logger.info("Successfully deployed new configuration 🚀")
self._set_autoconf_load_db()
applied = True
except:
self._logger.error(f"Exception while processing events :\n{format_exc()}")

View file

@ -3,7 +3,7 @@
from contextlib import suppress
from time import sleep
from traceback import format_exc
from typing import Dict, List
from typing import List
from kubernetes import client, config, watch
from kubernetes.client.exceptions import ApiException
from threading import Thread, Lock
@ -39,6 +39,26 @@ class IngressController(Controller):
health = True
break
instance["health"] = health
instance["env"] = {}
pod = None
for container in controller_instance.spec.containers:
if container.name == "bunkerweb":
pod = container
break
if not pod:
self._logger.warning(f"Missing container bunkerweb in pod {controller_instance.metadata.name}")
else:
for env in pod.env:
instance["env"][env.name] = env.value or ""
for controller_service in self._get_controller_services():
if controller_service.metadata.annotations:
for (
annotation,
value,
) in controller_service.metadata.annotations.items():
if not annotation.startswith("bunkerweb.io/"):
continue
instance["env"][annotation.replace("bunkerweb.io/", "", 1)] = value
return [instance]
def _get_controller_services(self) -> list:
@ -120,9 +140,7 @@ class IngressController(Controller):
server_name = service["SERVER_NAME"].strip().split(" ")[0]
if not variable.startswith(f"{server_name}_"):
continue
variable = variable.replace(f"{server_name}_", "", 1)
if self._is_setting_context(variable, "multisite"):
service[variable] = value
service[variable.replace(f"{server_name}_", "", 1)] = value
# parse tls
if controller_service.spec.tls:
@ -156,37 +174,6 @@ class IngressController(Controller):
service["CUSTOM_SSL_KEY_DATA"] = secret_tls.data["tls.key"]
return services
def _get_scheduler_env(self) -> Dict[str, str]:
variables = {}
for instance in self.__corev1.list_pod_for_all_namespaces(watch=False).items:
if not instance.metadata.annotations or "bunkerweb.io/SCHEDULER" not in instance.metadata.annotations:
continue
pod = None
for container in instance.spec.containers:
if container.name == "bunkerweb-scheduler":
pod = container
break
if not pod:
continue
variables = {env.name: env.value or "" for env in pod.env}
return variables
def _get_static_services(self) -> List[dict]:
services = []
variables = self._get_scheduler_env()
if "SERVER_NAME" in variables and variables["SERVER_NAME"].strip():
for server_name in variables["SERVER_NAME"].strip().split(" "):
service = {"SERVER_NAME": server_name}
for variable, value in variables.items():
prefix = variable.split("_")[0]
real_variable = variable.replace(f"{prefix}_", "", 1)
if prefix == server_name and self._is_setting_context(real_variable, "multisite"):
service[real_variable] = value
services.append(service)
return services
def get_configs(self) -> dict:
configs = {config_type: {} for config_type in self._supported_config_types}
for configmap in self.__corev1.list_config_map_for_all_namespaces(watch=False).items:
@ -254,21 +241,37 @@ class IngressController(Controller):
while True:
locked = False
error = False
applied = False
try:
for event in w.stream(what):
with self.__internal_lock:
locked = True
if not self.__process_event(event):
locked = False
continue
self.wait_applying()
applied = False
self.__internal_lock.acquire()
locked = True
if not self.__process_event(event):
self.__internal_lock.release()
locked = False
continue
to_apply = False
while not applied:
waiting = self.have_to_wait()
self._update_settings()
self._instances = self.get_instances()
self._services = self.get_services()
self._configs = self.get_configs()
if not self.update_needed(self._instances, self._services, configs=self._configs):
locked = False
if not to_apply and not self.update_needed(self._instances, self._services, configs=self._configs):
if locked:
self.__internal_lock.release()
locked = False
applied = True
continue
to_apply = True
if waiting:
sleep(1)
continue
self._logger.info(f"Caught kubernetes event ({watch_type}), deploying new configuration ...")
try:
ret = self.apply_config()
@ -276,10 +279,15 @@ class IngressController(Controller):
self._logger.error("Error while deploying new configuration ...")
else:
self._logger.info("Successfully deployed new configuration 🚀")
self._set_autoconf_load_db()
except:
self._logger.error(f"Exception while deploying new configuration :\n{format_exc()}")
locked = False
applied = True
if locked:
self.__internal_lock.release()
locked = False
except ApiException as e:
if e.status != 410:
self._logger.error(f"API exception while reading k8s event (type = {watch_type}) :\n{format_exc()}")

View file

@ -32,6 +32,12 @@ class SwarmController(Controller):
def _to_instances(self, controller_instance) -> List[dict]:
self.__swarm_instances.append(controller_instance.id)
instances = []
instance_env = {}
for env in controller_instance.attrs["Spec"]["TaskTemplate"]["ContainerSpec"]["Env"]:
variable = env.split("=")[0]
value = env.replace(f"{variable}=", "", 1)
instance_env[variable] = value
for task in controller_instance.tasks():
if task["DesiredState"] != "running":
continue
@ -51,41 +57,9 @@ class SwarmController(Controller):
for variable, value in controller_service.attrs["Spec"]["Labels"].items():
if not variable.startswith("bunkerweb."):
continue
real_variable = variable.replace("bunkerweb.", "", 1)
if not self._is_setting_context(real_variable, "multisite"):
continue
service[real_variable] = value
service[variable.replace("bunkerweb.", "", 1)] = value
return [service]
def _get_scheduler_env(self) -> Dict[str, str]:
env = {}
for instance in self.__client.services.list(filters={"label": "bunkerweb.type=scheduler"}):
if not instance.attrs or not instance.attrs.get("Spec", {}).get("TaskTemplate", {}).get("ContainerSpec", {}).get("Env"):
continue
for env in instance.attrs["Spec"]["TaskTemplate"]["ContainerSpec"]["Env"]:
variable = env.split("=")[0]
value = env.replace(f"{variable}=", "", 1)
env[variable] = value
return env
def _get_static_services(self) -> List[dict]:
services = []
variables = self._get_scheduler_env()
if "SERVER_NAME" in variables and variables["SERVER_NAME"].strip():
for server_name in variables["SERVER_NAME"].strip().split(" "):
if not server_name:
continue
service = {}
service["SERVER_NAME"] = server_name
for variable, value in variables.items():
prefix = variable.split("_")[0]
real_variable = variable.replace(f"{prefix}_", "", 1)
if prefix == server_name and self._is_setting_context(real_variable, "multisite"):
service[real_variable] = value
services.append(service)
return services
def get_configs(self) -> Dict[str, Dict[str, Any]]:
self.__swarm_configs = []
configs = {}
@ -148,31 +122,53 @@ class SwarmController(Controller):
while True:
locked = False
error = False
applied = False
try:
for event in self.__client.events(decode=True, filters={"type": event_type}):
with self.__internal_lock:
locked = True
if not self.__process_event(event):
locked = False
continue
try:
self.wait_applying()
applied = False
self.__internal_lock.acquire()
locked = True
if not self.__process_event(event):
self.__internal_lock.release()
locked = False
continue
try:
to_apply = False
while not applied:
waiting = self.have_to_wait()
self._update_settings()
self._instances = self.get_instances()
self._services = self.get_services()
self._configs = self.get_configs()
if not self.update_needed(self._instances, self._services, configs=self._configs):
locked = False
if not to_apply and not self.update_needed(self._instances, self._services, configs=self._configs):
if locked:
self.__internal_lock.release()
locked = False
applied = True
continue
to_apply = True
if waiting:
sleep(1)
continue
self._logger.info(f"Caught Swarm event ({event_type}), deploying new configuration ...")
if not self.apply_config():
self._logger.error("Error while deploying new configuration")
else:
self._logger.info("Successfully deployed new configuration 🚀")
self._logger.info(
"Successfully deployed new configuration 🚀",
)
self._set_autoconf_load_db()
except:
self._logger.error(f"Exception while processing Swarm event ({event_type}) :\n{format_exc()}")
locked = False
applied = True
except BaseException:
self._logger.error(f"Exception while processing Swarm event ({event_type}) :\n{format_exc()}")
if locked:
self.__internal_lock.release()
locked = False
except:
self._logger.error(f"Exception while reading Swarm event ({event_type}) :\n{format_exc()}")
error = True

View file

@ -59,11 +59,7 @@ try:
LOGGER.info(f"Instance #{i} : {instance['name']}")
i += 1
# Run first configuration
ret = controller.apply_config()
if not ret:
LOGGER.error("Error while applying initial configuration")
_exit(1)
controller.wait_applying(True)
# Process events
Path(sep, "var", "tmp", "bunkerweb", "autoconf.healthy").write_text("ok")

View file

@ -1,2 +1,2 @@
docker==7.1.0
kubernetes==29.0.0
kubernetes==30.1.0

View file

@ -8,9 +8,9 @@ cachetools==5.3.3 \
--hash=sha256:0abad1021d3f8325b2fc1d2e9c8b9c9d57b04c3932657a72465447332c24d945 \
--hash=sha256:ba29e2dfa0b8b556606f097407ed1aa62080ee108ab0dc5ec9d6a723a007d105
# via google-auth
certifi==2024.2.2 \
--hash=sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f \
--hash=sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1
certifi==2024.6.2 \
--hash=sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516 \
--hash=sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56
# via
# kubernetes
# requests
@ -110,17 +110,17 @@ docker==7.1.0 \
--hash=sha256:ad8c70e6e3f8926cb8a92619b832b4ea5299e2831c14284663184e200546fa6c \
--hash=sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0
# via -r requirements.in
google-auth==2.29.0 \
--hash=sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360 \
--hash=sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415
google-auth==2.30.0 \
--hash=sha256:8df7da660f62757388b8a7f249df13549b3373f24388cb5d2f1dd91cc18180b5 \
--hash=sha256:ab630a1320f6720909ad76a7dbdb6841cdf5c66b328d690027e4867bdfb16688
# via kubernetes
idna==3.7 \
--hash=sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc \
--hash=sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0
# via requests
kubernetes==29.0.0 \
--hash=sha256:ab8cb0e0576ccdfb71886366efb102c6a20f268d817be065ce7f9909c631e43e \
--hash=sha256:c4812e227ae74d07d53c88293e564e54b850452715a59a927e7e1bc6b9a60459
kubernetes==30.1.0 \
--hash=sha256:41e4c77af9f28e7a6c314e3bd06a8c6229ddd787cad684e0ab9f69b498e98ebc \
--hash=sha256:e212e8b7579031dd2e512168b617373bc1e03888d41ac4e04039240a292d478d
# via -r requirements.in
oauthlib==3.2.2 \
--hash=sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca \
@ -195,9 +195,9 @@ pyyaml==6.0.1 \
--hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \
--hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f
# via kubernetes
requests==2.32.2 \
--hash=sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289 \
--hash=sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c
requests==2.32.3 \
--hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \
--hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6
# via
# docker
# kubernetes

View file

@ -1,4 +1,4 @@
FROM nginx:1.26.0-alpine-slim@sha256:be13c98f606eef87521627d5c794a98ac1e5a8fcb085e75acdc0c9d66a28666c AS builder
FROM nginx:1.26.1-alpine-slim@sha256:3df0d85b2e46d4195e7436c22694ef65944c48624282292cadfbf58ee0ad34ce AS builder
# Install temporary requirements for the dependencies
RUN apk add --no-cache bash autoconf libtool automake geoip-dev g++ gcc curl-dev libxml2-dev pcre-dev make linux-headers musl-dev gd-dev gnupg brotli-dev openssl-dev patch readline-dev yajl yajl-dev yajl-tools py3-pip
@ -42,7 +42,7 @@ COPY src/common/utils utils
COPY src/VERSION VERSION
COPY misc/*.ascii misc/
FROM nginx:1.26.0-alpine-slim@sha256:be13c98f606eef87521627d5c794a98ac1e5a8fcb085e75acdc0c9d66a28666c
FROM nginx:1.26.1-alpine-slim@sha256:3df0d85b2e46d4195e7436c22694ef65944c48624282292cadfbf58ee0ad34ce
# Set default umask to prevent huge recursive chmod increasing the final image size
RUN umask 027

Binary file not shown.

Binary file not shown.

View file

@ -20,6 +20,7 @@ for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in ((
from API import API # type: ignore
from ApiCaller import ApiCaller # type: ignore
from common_utils import get_integration # type: ignore
from logger import setup_logger # type: ignore
@ -71,6 +72,7 @@ class CLI(ApiCaller):
assert isinstance(self.__variables, dict), "Failed to get variables from database"
self.__integration = get_integration()
self.__use_redis = self.__get_variable("USE_REDIS", "no") == "yes"
self.__redis = None
if self.__use_redis:
@ -113,7 +115,7 @@ class CLI(ApiCaller):
sentinel_hosts = self.__get_variable("REDIS_SENTINEL_HOSTS", [])
if isinstance(sentinel_hosts, str):
sentinel_hosts = [host.split(":") if ":" in host else host for host in sentinel_hosts.split(" ") if host]
sentinel_hosts = [host.split(":") if ":" in host else (host, "26379") for host in sentinel_hosts.split(" ") if host]
if sentinel_hosts:
sentinel_username = self.__get_variable("REDIS_SENTINEL_USERNAME", None) or None
@ -178,8 +180,17 @@ class CLI(ApiCaller):
self.__logger.error("USE_REDIS is set to yes but REDIS_HOST or REDIS_SENTINEL_HOSTS is not set, disabling redis")
self.__use_redis = False
for db_instance in self.__db.get_instances():
self.apis.append(API(db_instance["hostname"], db_instance["port"], db_instance["server_name"]))
if Path(sep, "usr", "sbin", "nginx").exists() or self.__integration == "Linux":
return super().__init__(
[
API(
f"http://127.0.0.1:{self.__get_variable('API_HTTP_PORT', '5000')}",
host=self.__get_variable("API_SERVER_NAME", "bwapi"),
)
]
)
super().__init__()
self.auto_setup(self.__integration)
def __get_variable(self, variable: str, default: Optional[Any] = None) -> Optional[str]:
return getenv(variable, self.__variables.get(variable, default))

View file

@ -49,7 +49,7 @@ init_by_lua_block {
if not ok then
logger:log(ERR, plugin)
else
local ok, err = datastore:set("plugin_" .. plugin.id, plugin, true)
local ok, err = datastore:set("plugin_" .. plugin.id, plugin, nil, true)
if not ok then
logger:log(ERR, "can't save " .. plugin.id .. " into datastore : " .. err)
else

View file

@ -1,4 +1,6 @@
server {
# server name (vhost)
server_name '{{ SERVER_NAME.split(" ")[0] }}';
# listen
{% if LISTEN_STREAM == "yes" +%}
@ -18,7 +20,6 @@ server {
set $reason '';
set $reason_data '';
set $ctx_ref '';
set $server_name '{{ SERVER_NAME.split(" ")[0] }}';
# include config files
include {{ NGINX_PREFIX }}server-stream/*.conf;

View file

@ -4,15 +4,15 @@ ssl_protocols {{ SSL_PROTOCOLS }};
ssl_prefer_server_ciphers on;
ssl_session_tickets off;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_cache shared:MozSSLStream:10m;
{% if "TLSv1.2" in SSL_PROTOCOLS +%}
ssl_dhparam /etc/nginx/dhparam;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
{% endif %}
listen 0.0.0.0:{{ LISTEN_STREAM_PORT_SSL }} ssl {% if USE_UDP == "yes" %} udp {% endif %}{% if USE_PROXY_PROTOCOL == "yes" %} proxy_protocol {% endif %};
listen 0.0.0.0:{{ LISTEN_STREAM_PORT_SSL }} ssl {% if USE_PROXY_PROTOCOL == "yes" %} proxy_protocol {% endif %};
{% if USE_IPV6 == "yes" +%}
listen [::]:{{ LISTEN_STREAM_PORT_SSL }} ssl {% if USE_UDP == "yes" %} udp {% endif %}{% if USE_PROXY_PROTOCOL == "yes" %} proxy_protocol {% endif %};
listen [::]:{{ LISTEN_STREAM_PORT_SSL }} ssl {% if USE_PROXY_PROTOCOL == "yes" %} proxy_protocol {% endif %};
{% endif %}
ssl_certificate_by_lua_block {

View file

@ -23,7 +23,7 @@ resolver_timeout 30s;
tcp_nodelay on;
# lua path and dicts
lua_package_path "/usr/share/bunkerweb/lua/?.lua;/usr/share/bunkerweb/core/?.lua;/etc/bunkerweb/plugins/?.lua;/etc/bunkerweb/pro/plugins/?.lua;/usr/share/bunkerweb/deps/lib/lua/?.lua;;";
lua_package_path "/usr/share/bunkerweb/lua/?.lua;/usr/share/bunkerweb/core/?.lua;/etc/bunkerweb/plugins/?.lua;/etc/bunkerweb/pro/plugins/?.lua;/usr/share/bunkerweb/deps/lib/lua/?.lua;/usr/share/bunkerweb/deps/lib/lua/?/init.lua;;";
lua_package_cpath "/usr/share/bunkerweb/deps/lib/?.so;/usr/share/bunkerweb/deps/lib/lua/?.so;;";
lua_ssl_trusted_certificate "/usr/share/bunkerweb/misc/root-ca.pem";
lua_ssl_verify_depth 2;

View file

@ -45,49 +45,57 @@ def backup_database(current_time: datetime, db: Database = None, backup_dir: Pat
database: Literal["sqlite", "mariadb", "mysql", "postgresql"] = db.database_uri.split(":")[0].split("+")[0] # type: ignore
backup_file = backup_dir.joinpath(f"backup-{database}-{current_time.strftime('%Y-%m-%d_%H-%M-%S')}.zip")
LOGGER.debug(f"Backup file path: {backup_file}")
stderr = "Table 'db.test_"
current_time = datetime.now()
if database == "sqlite":
match = DB_STRING_RX.search(db.database_uri)
if not match:
LOGGER.error(f"Invalid database string provided: {db.database_uri}, skipping backup ...")
while "Table 'db.test_" in stderr and (datetime.now() - current_time).total_seconds() < 10:
if database == "sqlite":
match = DB_STRING_RX.search(db.database_uri)
if not match:
LOGGER.error(f"Invalid database string provided: {db.database_uri}, skipping backup ...")
sys_exit(1)
db_path = Path(match.group("path"))
LOGGER.info("Creating a backup for the SQLite database ...")
proc = run(["sqlite3", db_path.as_posix(), ".dump"], stdout=PIPE, stderr=PIPE)
else:
db_host = db.database_uri.rsplit("@", 1)[1].split("/")[0].split(":")
db_port = None
if len(db_host) == 1:
db_host = db_host[0]
else:
db_host, db_port = db_host
db_user = db.database_uri.split("://")[1].split(":")[0]
db_password = db.database_uri.split("://")[1].split(":")[1].rsplit("@", 1)[0]
db_database_name = db.database_uri.split("/")[-1].split("?")[0]
if database in ("mariadb", "mysql"):
LOGGER.info("Creating a backup for the MariaDB/MySQL database ...")
cmd = ["mysqldump", "-h", db_host, "-u", db_user, db_database_name]
if db_port:
cmd.extend(["-P", db_port])
proc = run(cmd, stdout=PIPE, stderr=PIPE, env=environ | {"MYSQL_PWD": db_password})
elif database == "postgresql":
LOGGER.info("Creating a backup for the PostgreSQL database ...")
cmd = ["pg_dump", "-h", db_host, "-U", db_user, db_database_name, "-w"]
if db_port:
cmd.extend(["-p", db_port])
proc = run(cmd, stdout=PIPE, stderr=PIPE, env=environ | {"PGPASSWORD": db_password})
stderr = proc.stderr.decode()
if "Table 'db.test_" not in stderr and proc.returncode != 0:
LOGGER.error(f"Failed to dump the database: {stderr}")
sys_exit(1)
db_path = Path(match.group("path"))
LOGGER.info("Creating a backup for the SQLite database ...")
proc = run(["sqlite3", db_path.as_posix(), ".dump"], stdout=PIPE, stderr=PIPE)
else:
db_host = db.database_uri.rsplit("@", 1)[1].split("/")[0].split(":")
db_port = None
if len(db_host) == 1:
db_host = db_host[0]
else:
db_host, db_port = db_host
db_user = db.database_uri.split("://")[1].split(":")[0]
db_password = db.database_uri.split("://")[1].split(":")[1].rsplit("@", 1)[0]
db_database_name = db.database_uri.split("/")[-1]
if database in ("mariadb", "mysql"):
LOGGER.info("Creating a backup for the MariaDB/MySQL database ...")
cmd = ["mysqldump", "-h", db_host, "-u", db_user, db_database_name]
if db_port:
cmd.extend(["-P", db_port])
proc = run(cmd, stdout=PIPE, stderr=PIPE, env=environ | {"MYSQL_PWD": db_password})
elif database == "postgresql":
LOGGER.info("Creating a backup for the PostgreSQL database ...")
cmd = ["pg_dump", "-h", db_host, "-U", db_user, db_database_name, "-w"]
if db_port:
cmd.extend(["-p", db_port])
proc = run(cmd, stdout=PIPE, stderr=PIPE, env=environ | {"PGPASSWORD": db_password})
if proc.returncode != 0:
LOGGER.error(f"Failed to dump the database: {proc.stderr.decode()}")
if (datetime.now() - current_time).total_seconds() >= 10:
LOGGER.error("Failed to dump the database: Timeout reached")
sys_exit(1)
with ZipFile(backup_file, "w", compression=ZIP_DEFLATED) as zipf:
@ -142,7 +150,7 @@ def restore_database(backup_file: Path, db: Database = None) -> Database:
db_user = db.database_uri.split("://")[1].split(":")[0]
db_password = db.database_uri.split("://")[1].split(":")[1].rsplit("@", 1)[0]
db_database_name = db.database_uri.split("/")[-1]
db_database_name = db.database_uri.split("/")[-1].split("?")[0]
if database in ("mariadb", "mysql"):
LOGGER.info("Restoring the MariaDB/MySQL database ...")
@ -179,7 +187,7 @@ def restore_database(backup_file: Path, db: Database = None) -> Database:
LOGGER.error(f"Failed to restore the database: {proc.stderr.decode()}")
sys_exit(1)
err = db.checked_changes(value=True)
err = db.checked_changes(plugins_changes="all", value=True)
if err:
LOGGER.error(f"Error while applying changes to the database: {err}, you may need to reload the application")

View file

@ -0,0 +1,87 @@
#!/usr/bin/env python3
from os import getenv, sep
from os.path import join
from sys import exit as sys_exit, path as sys_path
from traceback import format_exc
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",))]:
if deps_path not in sys_path:
sys_path.append(deps_path)
from requests import get
from common_utils import get_version # type: ignore
from logger import setup_logger # type: ignore
LOGGER = setup_logger("UPDATE-CHECK", getenv("LOG_LEVEL", "INFO"))
status = 0
try:
def get_latest_stable_release():
response = get("https://api.github.com/repos/bunkerity/bunkerweb/releases", headers={"User-Agent": "BunkerWeb"}, timeout=3)
response.raise_for_status()
releases = response.json()
for release in releases:
if not release["prerelease"]:
return release
return None
latest_release = get_latest_stable_release()
if not latest_release:
status = 1
LOGGER.error("Failed to fetch latest release information")
sys_exit(status)
current_version = get_version()
latest_version = latest_release["tag_name"].removeprefix("v")
if current_version != latest_version:
# Version details
latest_version_text = f"\033[1;92m{latest_version}\033[0m"
current_version_text = f"\033[1;93m{current_version}\033[0m"
release_notes_url = f"https://github.com/bunkerity/bunkerweb/releases/v{latest_version}"
release_notes_url_text = f"\033[4;94m{release_notes_url}\033[0m"
# Centering based on the longest line length
alert_message = "🚨 A NEW VERSION OF BUNKERWEB IS AVAILABLE! 🚨"
latest_version_line = f"Latest Version: {latest_version_text}"
current_version_line = f"Current Version: {current_version_text}"
release_notes_url_line = f"Release Notes: {release_notes_url_text}"
# Determine the longest line length
longest_line_length = max(
len(alert_message),
len(latest_version_line),
len(current_version_line),
len(release_notes_url_line),
)
# Centering the lines within the box width
alert_message_padded = alert_message.center(longest_line_length - 13)
latest_version_padded = latest_version_line.center(longest_line_length)
current_version_padded = current_version_line.center(longest_line_length)
LOGGER.warning(
(
f"\n\033[1;91m{'*' * (longest_line_length - 5)}\n"
f"\033[1;91m*{' ' * (longest_line_length - 7)}*\n"
f"\033[1;91m* \033[1;97m{alert_message_padded}\033[0m \033[0;91m*\n"
f"\033[1;91m*{' ' * (longest_line_length - 7)}*\n"
f"\033[1;91m* \033[1;97m{latest_version_padded}\033[0;91m *\n"
f"\033[1;91m* \033[1;97m{current_version_padded}\033[0;91m *\n"
f"\033[1;91m*{' ' * (longest_line_length - 7)}*\n"
f"\033[1;91m* \033[1;97m{release_notes_url_line}\033[0;91m *\n"
f"\033[1;91m*{' ' * (longest_line_length - 7)}*\n"
f"\033[1;91m{'*' * (longest_line_length - 5)}\033[0m"
)
)
else:
LOGGER.info(f"Latest version is already installed: {current_version}")
except:
status = 2
LOGGER.error(f"Exception while running update-check.py :\n{format_exc()}")
sys_exit(status)

View file

@ -19,9 +19,9 @@
"reload": true
},
{
"name": "download-plugins",
"file": "download-plugins.py",
"every": "once",
"name": "update-check",
"file": "update-check.py",
"every": "day",
"reload": false
}
]

View file

@ -74,7 +74,7 @@ try:
# Check if we're using let's encrypt
use_letsencrypt = False
is_multisite = getenv("MULTISITE", "no") == "yes"
all_domains = getenv("SERVER_NAME", "")
all_domains = getenv("SERVER_NAME", "").lower()
server_names = all_domains.split(" ")
if getenv("AUTO_LETS_ENCRYPT", "no") == "yes":
@ -88,7 +88,7 @@ try:
if not use_letsencrypt:
LOGGER.info("Let's Encrypt is not activated, skipping generation...")
sys_exit(0)
elif not getenv("SERVER_NAME"):
elif not all_domains:
LOGGER.warning("There are no server names, skipping generation...")
sys_exit(0)
@ -105,7 +105,7 @@ try:
for first_server in server_names:
if not first_server or getenv(f"{first_server}_AUTO_LETS_ENCRYPT", getenv("AUTO_LETS_ENCRYPT", "no")) != "yes":
continue
domains_server_names[first_server] = getenv(f"{first_server}_SERVER_NAME", first_server)
domains_server_names[first_server] = getenv(f"{first_server}_SERVER_NAME", first_server).lower()
# Singlesite case
else:
domains_server_names = {server_names[0]: all_domains}
@ -172,7 +172,7 @@ try:
if getenv("LETS_ENCRYPT_CLEAR_OLD_CERTS", "no") == "yes":
LOGGER.info("Clear old certificates is activated, removing old / no longer used certificates...")
for elem in chain(DATA_PATH.glob("archive/*"), DATA_PATH.glob("live/*"), DATA_PATH.glob("renewal/*")):
if elem.name.replace(".conf", "") not in generated_domains:
if elem.name.replace(".conf", "") not in generated_domains and elem.name != "README":
LOGGER.warning(f"Removing old certificate {elem}")
if elem.is_dir():
rmtree(elem, ignore_errors=True)

View file

@ -1,35 +0,0 @@
#!/usr/bin/env python3
from os import getenv, sep
from os.path import basename, join
from sys import exit as sys_exit, path as sys_path
from traceback import format_exc
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",))]:
if deps_path not in sys_path:
sys_path.append(deps_path)
from requests import get
from common_utils import get_version # type: ignore
from logger import setup_logger # type: ignore
LOGGER = setup_logger("UPDATE-CHECK", getenv("LOG_LEVEL", "INFO"))
status = 0
try:
current_version = f"v{get_version().strip()}"
response = get("https://github.com/bunkerity/bunkerweb/releases/latest", headers={"User-Agent": "BunkerWeb"}, allow_redirects=True, timeout=10)
response.raise_for_status()
latest_version = basename(response.url)
if current_version != latest_version:
LOGGER.warning(f"* \n* \n* 🚨 A new version of BunkerWeb is available: {latest_version} (current: {current_version}) 🚨\n* \n* ")
else:
LOGGER.info(f"Latest version is already installed: {current_version}")
except:
status = 2
LOGGER.error(f"Exception while running update-check.py :\n{format_exc()}")
sys_exit(status)

View file

@ -203,17 +203,17 @@
"every": "once",
"reload": false
},
{
"name": "update-check",
"file": "update-check.py",
"every": "day",
"reload": false
},
{
"name": "anonymous-report",
"file": "anonymous-report.py",
"every": "day",
"reload": false
},
{
"name": "download-plugins",
"file": "download-plugins.py",
"every": "once",
"reload": false
}
]
}

View file

@ -0,0 +1,9 @@
{% if USE_MODSECURITY == "yes" and MODSECURITY_CRS_VERSION == "3" and HTTP3 == "yes" +%}
SecAction \
"id:900230,\
phase:1,\
nolog,\
pass,\
t:none,\
setvar:'tx.allowed_http_versions=HTTP/1.0 HTTP/1.1 HTTP/2 HTTP/2.0 HTTP/3 HTTP/3.0'"
{% endif %}

View file

@ -20,11 +20,11 @@ from jobs import Job # type: ignore
def check_line(line):
with suppress(ValueError):
if "/" in line:
ip_network(line)
if b"/" in line:
ip_network(line.decode())
return True, line
else:
ip_address(line)
ip_address(line.decode())
return True, line
return False, b""

View file

@ -11,7 +11,7 @@ from pathlib import Path
from re import compile as re_compile
from sys import argv, path as sys_path
from threading import Lock
from typing import Any, Dict, List, Literal, Optional, Tuple, Union
from typing import Any, Dict, List, Literal, Optional, Set, Tuple, Union
from time import sleep
from uuid import uuid4
from zipfile import ZIP_DEFLATED, ZipFile
@ -71,6 +71,7 @@ def set_sqlite_pragma(dbapi_connection, _):
class Database:
DB_STRING_RX = re_compile(r"^(?P<database>(mariadb|mysql)(\+pymysql)?|sqlite(\+pysqlite)?|postgresql(\+psycopg)?):/+(?P<path>/[^\s]+)")
READONLY_ERROR = ("readonly", "read-only", "command denied", "Access denied")
def __init__(
self, logger: Logger, sqlalchemy_string: Optional[str] = None, *, ui: bool = False, pool: Optional[bool] = None, log: bool = True, **kwargs
@ -78,7 +79,7 @@ class Database:
"""Initialize the database"""
self.logger = logger
self.readonly = False
self.last_fallback = None
self.last_connection_retry = None
if pool:
self.logger.warning("The pool parameter is deprecated, it will be removed in the next version")
@ -133,6 +134,7 @@ class Database:
"pool_recycle": 1800,
"pool_size": 40,
"max_overflow": 20,
"pool_timeout": 5,
} | kwargs
try:
@ -183,19 +185,17 @@ class Database:
self.sql_engine.dispose(close=True)
self.sql_engine = create_engine(self.database_uri_readonly, **self._engine_kwargs)
self.readonly = True
self.last_fallback = datetime.now()
fallback = True
continue
self.logger.error(f"Can't connect to database after {DATABASE_RETRY_TIMEOUT} seconds: {e}")
_exit(1)
if "readonly" in str(e) or "read-only" in str(e) or "command denied" in str(e):
if any(error in str(e) for error in self.READONLY_ERROR):
if log:
self.logger.warning("The database is read-only. Retrying in read-only mode in 5 seconds ...")
self.sql_engine.dispose(close=True)
self.sql_engine = create_engine(sqlalchemy_string, **self._engine_kwargs)
self.readonly = True
self.last_fallback = datetime.now()
if "Unknown table" in str(e):
not_connected = False
continue
@ -218,6 +218,12 @@ class Database:
if self.sql_engine:
self.sql_engine.dispose()
def test_read(self):
"""Test the read access to the database"""
self.logger.debug("Testing read access to the database ...")
with self.__db_session() as session:
session.execute(text("SELECT 1"))
def test_write(self):
"""Test the write access to the database"""
self.logger.debug("Testing write access to the database ...")
@ -227,10 +233,12 @@ class Database:
session.execute(text(f"DROP TABLE IF EXISTS test_{table_name}"))
session.commit()
def retry_connection(self, *, readonly: bool = False, fallback: bool = False, **kwargs) -> None:
def retry_connection(self, *, readonly: bool = False, fallback: bool = False, log: bool = True, **kwargs) -> None:
"""Retry the connection to the database"""
self.last_connection_retry = datetime.now()
self.logger.debug(f"Retrying the connection to the database {'in read-only mode' if readonly else ''}{' with fallback' if fallback else ''} ...")
if log:
self.logger.debug(f"Retrying the connection to the database{' in read-only mode' if readonly else ''}{' with fallback' if fallback else ''} ...")
assert self.sql_engine is not None
@ -245,9 +253,10 @@ class Database:
conn.execute(text("SELECT 1"))
return
table_name = uuid4().hex
with self.sql_engine.connect() as conn:
conn.execute(text("CREATE TABLE IF NOT EXISTS test (id INT)"))
conn.execute(text("DROP TABLE test"))
conn.execute(text(f"CREATE TABLE IF NOT EXISTS test_{table_name} (id INT)"))
conn.execute(text(f"DROP TABLE IF EXISTS test_{table_name}"))
@contextmanager
def __db_session(self) -> Any:
@ -257,21 +266,6 @@ class Database:
self.logger.error("The database engine is not initialized")
_exit(1)
if self.database_uri and self.readonly and self.last_fallback and (datetime.now() - self.last_fallback).total_seconds() > 30:
# ? If the database is forced to be read-only, we try to connect as a non read-only user every time until the database is writable
try:
self.retry_connection(pool_timeout=1)
self.readonly = False
self.logger.info("The database is no longer read-only, defaulting to read-write mode")
except (OperationalError, DatabaseError):
try:
self.retry_connection(readonly=True, pool_timeout=1)
except (OperationalError, DatabaseError):
if self.database_uri_readonly:
with suppress(OperationalError, DatabaseError):
self.retry_connection(fallback=True, pool_timeout=1)
self.readonly = True
with LOCK:
session = None
try:
@ -283,24 +277,25 @@ class Database:
if session:
session.rollback()
if "readonly" in str(e) or "read-only" in str(e) or "command denied" in str(e):
if any(error in str(e) for error in self.READONLY_ERROR):
self.logger.warning("The database is read-only, retrying in read-only mode ...")
try:
self.retry_connection(readonly=True, pool_timeout=1)
self.retry_connection(readonly=True, log=False)
except (OperationalError, DatabaseError):
if self.database_uri_readonly:
self.logger.warning("Can't connect to the database in read-only mode, falling back to read-only one")
with suppress(OperationalError, DatabaseError):
self.retry_connection(fallback=True, pool_timeout=1)
self.retry_connection(fallback=True, log=False)
self.readonly = True
self.last_fallback = datetime.now()
elif isinstance(e, (ConnectionRefusedError, OperationalError)) and self.database_uri_readonly:
self.logger.warning("Can't connect to the database, falling back to read-only one ...")
with suppress(OperationalError, DatabaseError):
self.retry_connection(fallback=True, pool_timeout=1)
self.retry_connection(fallback=True, log=False)
self.readonly = True
self.last_fallback = datetime.now()
raise
raise
finally:
if session:
session.remove()
@ -349,8 +344,11 @@ class Database:
"custom_configs_changed": False,
"external_plugins_changed": False,
"pro_plugins_changed": False,
"config_changed": False,
"instances_changed": False,
"last_custom_configs_change": None,
"last_external_plugins_change": None,
"last_pro_plugins_change": None,
"last_instances_change": None,
"integration": "unknown",
"version": "1.5.8",
"database_version": "Unknown", # ? Extracted from the database
@ -398,15 +396,15 @@ class Database:
return ""
def checked_changes(self, changes: Optional[List[str]] = None, value: Optional[bool] = False) -> str:
def checked_changes(
self,
changes: Optional[List[str]] = None,
plugins_changes: Optional[Union[Literal["all"], Set[str], List[str], Tuple[str]]] = None,
value: Optional[bool] = False,
) -> str:
"""Set changed bit for config, custom configs, instances and plugins"""
changes = changes or [
"config",
"custom_configs",
"external_plugins",
"pro_plugins",
"instances",
]
changes = changes or ["config", "custom_configs", "external_plugins", "pro_plugins", "instances"]
plugins_changes = plugins_changes or set()
with self.__db_session() as session:
if self.readonly:
return "The database is read-only, the changes will not be saved"
@ -417,18 +415,32 @@ class Database:
if not metadata:
return "The metadata are not set yet, try again"
current_time = datetime.now()
if "config" in changes:
if not metadata.first_config_saved:
metadata.first_config_saved = True
metadata.config_changed = value
if "custom_configs" in changes:
metadata.custom_configs_changed = value
metadata.last_custom_configs_change = current_time
if "external_plugins" in changes:
metadata.external_plugins_changed = value
metadata.last_external_plugins_change = current_time
if "pro_plugins" in changes:
metadata.pro_plugins_changed = value
metadata.last_pro_plugins_change = current_time
if "instances" in changes:
metadata.instances_changed = value
metadata.last_instances_change = current_time
if plugins_changes:
if plugins_changes == "all":
session.query(Plugins).update({Plugins.config_changed: value, Plugins.last_config_change: current_time})
else:
session.query(Plugins).filter(Plugins.id.in_(plugins_changes)).update(
{Plugins.config_changed: value, Plugins.last_config_change: current_time}
)
session.commit()
except BaseException as e:
return str(e)
@ -456,7 +468,7 @@ class Database:
if db_version != bunkerweb_version:
self.logger.warning(f"Database version ({db_version}) is different from Bunkerweb version ({bunkerweb_version}), migrating ...")
curren_time = datetime.now()
current_time = datetime.now()
error = True
while error:
try:
@ -464,7 +476,7 @@ class Database:
metadata.reflect(self.sql_engine)
error = False
except BaseException as e:
if (datetime.now() - curren_time).total_seconds() > 10:
if (datetime.now() - current_time).total_seconds() > 10:
raise e
sleep(1)
@ -971,7 +983,7 @@ class Database:
if db_version and db_version != bunkerweb_version:
for table_name, data in old_data.items():
if table_name == "bw_metadata" or not data:
if not data:
continue
self.logger.warning(f'Restoring data for table "{table_name}"')
@ -990,6 +1002,15 @@ class Database:
with self.__db_session() as session:
try:
if table_name == "bw_metadata":
existing_row = session.query(Metadata).filter_by(id=1).first()
if not existing_row:
session.add(Metadata(**row))
session.commit()
continue
session.query(Metadata).filter_by(id=1).update(row)
continue
# Check if the row already exists in the table
existing_row = session.query(Base.metadata.tables[table_name]).filter_by(**row).first()
if not existing_row:
@ -1004,16 +1025,36 @@ class Database:
return True, ""
def save_config(self, config: Dict[str, Any], method: str, changed: Optional[bool] = True) -> str:
def save_config(self, config: Dict[str, Any], method: str, changed: Optional[bool] = True) -> Union[str, Set[str]]:
"""Save the config in the database"""
to_put = []
with self.__db_session() as session:
if self.readonly:
return "The database is read-only, the changes will not be saved"
# Delete all the old config
session.query(Global_values).filter(Global_values.method == method).delete()
session.query(Services_settings).filter(Services_settings.method == method).delete()
changed_plugins = set()
changed_services = False
for db_global_config in session.query(Global_values).filter_by(method=method).all():
key = db_global_config.setting_id
if db_global_config.suffix:
key = f"{key}_{db_global_config.suffix}"
if key not in config and (db_global_config.suffix or f"{key}_0" not in config):
session.delete(db_global_config)
changed_plugins.add(session.query(Settings).with_entities(Settings.plugin_id).filter_by(id=db_global_config.setting_id).first().plugin_id)
if key == "SERVER_NAME":
changed_services = True
for db_service_config in session.query(Services_settings).filter_by(method=method).all():
key = f"{db_service_config.service_id}_{db_service_config.setting_id}"
if db_service_config.suffix:
key = f"{key}_{db_service_config.suffix}"
if key not in config and (db_service_config.suffix or f"{key}_0" not in config):
session.delete(db_service_config)
changed_plugins.add(session.query(Settings).with_entities(Settings.plugin_id).filter_by(id=db_service_config.setting_id).first().plugin_id)
if config:
config.pop("DATABASE_URI", None)
@ -1023,7 +1064,11 @@ class Database:
services = config.get("SERVER_NAME", [])
if isinstance(services, str):
services = services.split(" ")
services = services.strip().split(" ")
for i, service in enumerate(services):
if not service:
services.pop(i)
if db_services:
missing_ids = [service.id for service in db_services if service.method == method and service.id not in services]
@ -1034,6 +1079,7 @@ class Database:
session.query(Services_settings).filter(Services_settings.service_id.in_(missing_ids)).delete()
session.query(Custom_configs).filter(Custom_configs.service_id.in_(missing_ids)).delete()
session.query(Jobs_cache).filter(Jobs_cache.service_id.in_(missing_ids)).delete()
changed_services = True
drafts = {service for service in services if config.pop(f"{service}_IS_DRAFT", "no") == "yes"}
db_drafts = {service.id for service in db_services if service.is_draft}
@ -1046,6 +1092,7 @@ class Database:
if missing_drafts:
# Remove drafts that are no longer in the list
session.query(Services).filter(Services.id.in_(missing_drafts)).update({Services.is_draft: False})
changed_services = True
for draft in drafts:
if draft not in db_drafts:
@ -1054,6 +1101,7 @@ class Database:
db_ids[draft] = {"method": method, "is_draft": True}
elif method == db_ids[draft]["method"]:
session.query(Services).filter(Services.id == draft).update({Services.is_draft: True})
changed_services = True
if config.get("MULTISITE", "no") == "yes":
global_values = []
@ -1064,7 +1112,7 @@ class Database:
suffix = int(key.split("_")[-1])
key = key[: -len(str(suffix)) - 1]
setting = session.query(Settings).with_entities(Settings.default).filter_by(id=key).first()
setting = session.query(Settings).with_entities(Settings.default, Settings.plugin_id).filter_by(id=key).first()
if not setting and services:
try:
@ -1075,10 +1123,12 @@ class Database:
if server_name not in db_ids:
to_put.append(Services(id=server_name, method=method, is_draft=server_name in drafts))
db_ids[server_name] = {"method": method, "is_draft": server_name in drafts}
if server_name not in drafts:
changed_services = True
key = key.replace(f"{server_name}_", "")
original_key = original_key.replace(f"{server_name}_", "")
setting = session.query(Settings).with_entities(Settings.default).filter_by(id=key).first()
setting = session.query(Settings).with_entities(Settings.default, Settings.plugin_id).filter_by(id=key).first()
if not setting:
continue
@ -1086,11 +1136,7 @@ class Database:
service_setting = (
session.query(Services_settings)
.with_entities(Services_settings.value, Services_settings.method)
.filter_by(
service_id=server_name,
setting_id=key,
suffix=suffix,
)
.filter_by(service_id=server_name, setting_id=key, suffix=suffix)
.first()
)
@ -1100,45 +1146,31 @@ class Database:
):
continue
to_put.append(
Services_settings(
service_id=server_name,
setting_id=key,
value=value,
suffix=suffix,
method=method,
)
)
elif method in (service_setting.method, "autoconf") and service_setting.value != value:
if key != "SERVER_NAME" and (
(original_key not in config and value == setting.default) or (original_key in config and value == config[original_key])
):
session.query(Services_settings).filter(
Services_settings.service_id == server_name,
Services_settings.setting_id == key,
Services_settings.suffix == suffix,
).delete()
continue
session.query(Services_settings).filter(
changed_plugins.add(setting.plugin_id)
to_put.append(Services_settings(service_id=server_name, setting_id=key, value=value, suffix=suffix, method=method))
elif (
method == service_setting.method or (service_setting.method not in ("scheduler", "autoconf") and method == "autoconf")
) and service_setting.value != value:
changed_plugins.add(setting.plugin_id)
query = session.query(Services_settings).filter(
Services_settings.service_id == server_name,
Services_settings.setting_id == key,
Services_settings.suffix == suffix,
).update(
{
Services_settings.value: value,
Services_settings.method: method,
}
)
if key != "SERVER_NAME" and (
(original_key not in config and value == setting.default) or (original_key in config and value == config[original_key])
):
query.delete()
continue
query.update({Services_settings.value: value, Services_settings.method: method})
elif setting and original_key not in global_values:
global_values.append(original_key)
global_value = (
session.query(Global_values)
.with_entities(Global_values.value, Global_values.method)
.filter_by(
setting_id=key,
suffix=suffix,
)
.filter_by(setting_id=key, suffix=suffix)
.first()
)
@ -1146,31 +1178,18 @@ class Database:
if value == setting.default:
continue
to_put.append(
Global_values(
setting_id=key,
value=value,
suffix=suffix,
method=method,
)
)
elif method in (global_value.method, "autoconf") and global_value.value != value:
if value == setting.default:
session.query(Global_values).filter(
Global_values.setting_id == key,
Global_values.suffix == suffix,
).delete()
continue
changed_plugins.add(setting.plugin_id)
to_put.append(Global_values(setting_id=key, value=value, suffix=suffix, method=method))
elif (
method == global_value.method or (global_value.method not in ("scheduler", "autoconf") and method == "autoconf")
) and global_value.value != value:
changed_plugins.add(setting.plugin_id)
query = session.query(Global_values).filter(Global_values.setting_id == key, Global_values.suffix == suffix)
session.query(Global_values).filter(
Global_values.setting_id == key,
Global_values.suffix == suffix,
).update(
{
Global_values.value: value,
Global_values.method: method,
}
)
if value == setting.default:
query.delete()
continue
query.update({Global_values.value: value, Global_values.method: method})
else:
if (
config.get("SERVER_NAME", "www.example.com")
@ -1180,6 +1199,7 @@ class Database:
.first()
):
to_put.append(Services(id=config.get("SERVER_NAME", "www.example.com").split(" ")[0], method=method))
changed_services = True
for key, value in config.items():
suffix = 0
@ -1187,7 +1207,7 @@ class Database:
suffix = int(key.split("_")[-1])
key = key[: -len(str(suffix)) - 1]
setting = session.query(Settings).with_entities(Settings.default).filter_by(id=key).first()
setting = session.query(Settings).with_entities(Settings.default, Settings.plugin_id).filter_by(id=key).first()
if not setting:
continue
@ -1203,26 +1223,21 @@ class Database:
if value == setting.default:
continue
to_put.append(
Global_values(
setting_id=key,
value=value,
suffix=suffix,
method=method,
)
)
elif global_value.method == method and value != global_value.value:
if value == setting.default:
session.query(Global_values).filter(
Global_values.setting_id == key,
Global_values.suffix == suffix,
).delete()
continue
changed_plugins.add(setting.plugin_id)
to_put.append(Global_values(setting_id=key, value=value, suffix=suffix, method=method))
elif (
method == global_value.method or (global_value.method not in ("scheduler", "autoconf") and method == "autoconf")
) and value != global_value.value:
changed_plugins.add(setting.plugin_id)
query = session.query(Global_values).filter(Global_values.setting_id == key, Global_values.suffix == suffix)
session.query(Global_values).filter(
Global_values.setting_id == key,
Global_values.suffix == suffix,
).update({Global_values.value: value})
if value == setting.default:
query.delete()
continue
query.update({Global_values.value: value, Global_values.method: method})
if changed_services:
changed_plugins = set(plugin.id for plugin in session.query(Plugins).with_entities(Plugins.id).all())
if changed:
with suppress(ProgrammingError, OperationalError):
@ -1230,7 +1245,9 @@ class Database:
if metadata is not None:
if not metadata.first_config_saved:
metadata.first_config_saved = True
metadata.config_changed = True
if changed_plugins:
session.query(Plugins).filter(Plugins.id.in_(changed_plugins)).update({Plugins.config_changed: True})
try:
session.add_all(to_put)
@ -1238,7 +1255,7 @@ class Database:
except BaseException as e:
return str(e)
return ""
return changed_plugins
def save_custom_configs(
self,
@ -1313,17 +1330,18 @@ class Database:
if not custom_conf:
to_put.append(Custom_configs(**custom_config))
elif custom_config["checksum"] != custom_conf.checksum and method in (custom_conf.method, "autoconf"):
elif custom_config["checksum"] != custom_conf.checksum and (
method == custom_conf.method or (custom_conf.method not in ("scheduler", "autoconf") and method == "autoconf")
):
custom_conf.data = custom_config["data"]
custom_conf.checksum = custom_config["checksum"]
if method == "autoconf":
custom_conf.method = method
custom_conf.method = method
if changed:
with suppress(ProgrammingError, OperationalError):
metadata = session.query(Metadata).get(1)
if metadata is not None:
metadata.custom_configs_changed = True
metadata.last_custom_configs_change = datetime.now()
try:
session.add_all(to_put)
@ -1333,7 +1351,7 @@ class Database:
return message
def get_config(self, methods: bool = False, with_drafts: bool = False) -> Dict[str, Any]:
def get_config(self, global_only: bool = False, methods: bool = False, with_drafts: bool = False) -> Dict[str, Any]:
"""Get the config from the database"""
with self.__db_session() as session:
config = {}
@ -1373,7 +1391,7 @@ class Database:
if not with_drafts:
services = services.filter_by(is_draft=False)
if is_multisite:
if not global_only and is_multisite:
for service in services:
config[f"{service.id}_IS_DRAFT"] = "yes" if service.is_draft else "no"
if methods:
@ -2050,8 +2068,10 @@ class Database:
if metadata is not None:
if _type == "external":
metadata.external_plugins_changed = True
metadata.last_external_plugins_change = datetime.now()
elif _type == "pro":
metadata.pro_plugins_changed = True
metadata.last_pro_plugins_change = datetime.now()
try:
session.add_all(to_put)
@ -2267,6 +2287,7 @@ class Database:
metadata = session.query(Metadata).get(1)
if metadata is not None:
metadata.instances_changed = True
metadata.last_instances_change = datetime.now()
try:
session.commit()
@ -2299,6 +2320,7 @@ class Database:
metadata = session.query(Metadata).get(1)
if metadata is not None:
metadata.instances_changed = True
metadata.last_instances_change = datetime.now()
try:
session.add_all(to_put)

View file

@ -58,6 +58,8 @@ class Plugins(Base):
method = Column(METHODS_ENUM, default="manual", nullable=False)
data = Column(LargeBinary(length=(2**32) - 1), nullable=True)
checksum = Column(String(128), nullable=True)
config_changed = Column(Boolean, default=False, nullable=True)
last_config_change = Column(DateTime, nullable=True)
settings = relationship("Settings", back_populates="plugin", cascade="all, delete-orphan")
jobs = relationship("Jobs", back_populates="plugin", cascade="all, delete-orphan")
@ -242,9 +244,12 @@ class Metadata(Base):
autoconf_loaded = Column(Boolean, default=False, nullable=True)
scheduler_first_start = Column(Boolean, nullable=True)
custom_configs_changed = Column(Boolean, default=False, nullable=True)
last_custom_configs_change = Column(DateTime, nullable=True)
external_plugins_changed = Column(Boolean, default=False, nullable=True)
last_external_plugins_change = Column(DateTime, nullable=True)
pro_plugins_changed = Column(Boolean, default=False, nullable=True)
config_changed = Column(Boolean, default=False, nullable=True)
last_pro_plugins_change = Column(DateTime, nullable=True)
instances_changed = Column(Boolean, default=False, nullable=True)
last_instances_change = Column(DateTime, nullable=True)
integration = Column(INTEGRATIONS_ENUM, default="Unknown", nullable=False)
version = Column(String(32), default="1.5.8", nullable=False)

View file

@ -1,4 +1,4 @@
cryptography==42.0.7
cryptography==42.0.8
psycopg[c,pool]==3.1.19
PyMySQL==1.1.1
sqlalchemy==2.0.30

View file

@ -58,39 +58,39 @@ cffi==1.16.0 \
--hash=sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956 \
--hash=sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357
# via cryptography
cryptography==42.0.7 \
--hash=sha256:02c0eee2d7133bdbbc5e24441258d5d2244beb31da5ed19fbb80315f4bbbff55 \
--hash=sha256:0d563795db98b4cd57742a78a288cdbdc9daedac29f2239793071fe114f13785 \
--hash=sha256:16268d46086bb8ad5bf0a2b5544d8a9ed87a0e33f5e77dd3c3301e63d941a83b \
--hash=sha256:1a58839984d9cb34c855197043eaae2c187d930ca6d644612843b4fe8513c886 \
--hash=sha256:2954fccea107026512b15afb4aa664a5640cd0af630e2ee3962f2602693f0c82 \
--hash=sha256:2e47577f9b18723fa294b0ea9a17d5e53a227867a0a4904a1a076d1646d45ca1 \
--hash=sha256:31adb7d06fe4383226c3e963471f6837742889b3c4caa55aac20ad951bc8ffda \
--hash=sha256:3577d029bc3f4827dd5bf8bf7710cac13527b470bbf1820a3f394adb38ed7d5f \
--hash=sha256:36017400817987670037fbb0324d71489b6ead6231c9604f8fc1f7d008087c68 \
--hash=sha256:362e7197754c231797ec45ee081f3088a27a47c6c01eff2ac83f60f85a50fe60 \
--hash=sha256:3de9a45d3b2b7d8088c3fbf1ed4395dfeff79d07842217b38df14ef09ce1d8d7 \
--hash=sha256:4f698edacf9c9e0371112792558d2f705b5645076cc0aaae02f816a0171770fd \
--hash=sha256:5482e789294854c28237bba77c4c83be698be740e31a3ae5e879ee5444166582 \
--hash=sha256:5e44507bf8d14b36b8389b226665d597bc0f18ea035d75b4e53c7b1ea84583cc \
--hash=sha256:779245e13b9a6638df14641d029add5dc17edbef6ec915688f3acb9e720a5858 \
--hash=sha256:789caea816c6704f63f6241a519bfa347f72fbd67ba28d04636b7c6b7da94b0b \
--hash=sha256:7f8b25fa616d8b846aef64b15c606bb0828dbc35faf90566eb139aa9cff67af2 \
--hash=sha256:8cb8ce7c3347fcf9446f201dc30e2d5a3c898d009126010cbd1f443f28b52678 \
--hash=sha256:93a3209f6bb2b33e725ed08ee0991b92976dfdcf4e8b38646540674fc7508e13 \
--hash=sha256:a3a5ac8b56fe37f3125e5b72b61dcde43283e5370827f5233893d461b7360cd4 \
--hash=sha256:a47787a5e3649008a1102d3df55424e86606c9bae6fb77ac59afe06d234605f8 \
--hash=sha256:a79165431551042cc9d1d90e6145d5d0d3ab0f2d66326c201d9b0e7f5bf43604 \
--hash=sha256:a987f840718078212fdf4504d0fd4c6effe34a7e4740378e59d47696e8dfb477 \
--hash=sha256:a9bc127cdc4ecf87a5ea22a2556cab6c7eda2923f84e4f3cc588e8470ce4e42e \
--hash=sha256:bd13b5e9b543532453de08bcdc3cc7cebec6f9883e886fd20a92f26940fd3e7a \
--hash=sha256:c65f96dad14f8528a447414125e1fc8feb2ad5a272b8f68477abbcc1ea7d94b9 \
--hash=sha256:d8e3098721b84392ee45af2dd554c947c32cc52f862b6a3ae982dbb90f577f14 \
--hash=sha256:e6b79d0adb01aae87e8a44c2b64bc3f3fe59515280e00fb6d57a7267a2583cda \
--hash=sha256:e6b8f1881dac458c34778d0a424ae5769de30544fc678eac51c1c8bb2183e9da \
--hash=sha256:e9b2a6309f14c0497f348d08a065d52f3020656f675819fc405fb63bbcd26562 \
--hash=sha256:ecbfbc00bf55888edda9868a4cf927205de8499e7fabe6c050322298382953f2 \
--hash=sha256:efd0bf5205240182e0f13bcaea41be4fdf5c22c5129fc7ced4a0282ac86998c9
cryptography==42.0.8 \
--hash=sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad \
--hash=sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583 \
--hash=sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b \
--hash=sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c \
--hash=sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1 \
--hash=sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648 \
--hash=sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949 \
--hash=sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba \
--hash=sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c \
--hash=sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9 \
--hash=sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d \
--hash=sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c \
--hash=sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e \
--hash=sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2 \
--hash=sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d \
--hash=sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7 \
--hash=sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70 \
--hash=sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2 \
--hash=sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7 \
--hash=sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14 \
--hash=sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe \
--hash=sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e \
--hash=sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71 \
--hash=sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961 \
--hash=sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7 \
--hash=sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c \
--hash=sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28 \
--hash=sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842 \
--hash=sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902 \
--hash=sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801 \
--hash=sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a \
--hash=sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e
# via -r requirements.armv7.in
greenlet==3.0.3 \
--hash=sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67 \
@ -222,9 +222,9 @@ sqlalchemy==2.0.30 \
--hash=sha256:f7703c2010355dd28f53deb644a05fc30f796bd8598b43f0ba678878780b6e4c \
--hash=sha256:fa561138a64f949f3e889eb9ab8c58e1504ab351d6cf55259dc4c248eaa19da6
# via -r requirements.armv7.in
typing-extensions==4.12.0 \
--hash=sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8 \
--hash=sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594
typing-extensions==4.12.2 \
--hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \
--hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8
# via
# psycopg
# psycopg-pool

View file

@ -1,4 +1,4 @@
cryptography==42.0.7
cryptography==42.0.8
psycopg[binary,pool]==3.1.19
PyMySQL==1.1.1
sqlalchemy==2.0.30

View file

@ -58,39 +58,39 @@ cffi==1.16.0 \
--hash=sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956 \
--hash=sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357
# via cryptography
cryptography==42.0.7 \
--hash=sha256:02c0eee2d7133bdbbc5e24441258d5d2244beb31da5ed19fbb80315f4bbbff55 \
--hash=sha256:0d563795db98b4cd57742a78a288cdbdc9daedac29f2239793071fe114f13785 \
--hash=sha256:16268d46086bb8ad5bf0a2b5544d8a9ed87a0e33f5e77dd3c3301e63d941a83b \
--hash=sha256:1a58839984d9cb34c855197043eaae2c187d930ca6d644612843b4fe8513c886 \
--hash=sha256:2954fccea107026512b15afb4aa664a5640cd0af630e2ee3962f2602693f0c82 \
--hash=sha256:2e47577f9b18723fa294b0ea9a17d5e53a227867a0a4904a1a076d1646d45ca1 \
--hash=sha256:31adb7d06fe4383226c3e963471f6837742889b3c4caa55aac20ad951bc8ffda \
--hash=sha256:3577d029bc3f4827dd5bf8bf7710cac13527b470bbf1820a3f394adb38ed7d5f \
--hash=sha256:36017400817987670037fbb0324d71489b6ead6231c9604f8fc1f7d008087c68 \
--hash=sha256:362e7197754c231797ec45ee081f3088a27a47c6c01eff2ac83f60f85a50fe60 \
--hash=sha256:3de9a45d3b2b7d8088c3fbf1ed4395dfeff79d07842217b38df14ef09ce1d8d7 \
--hash=sha256:4f698edacf9c9e0371112792558d2f705b5645076cc0aaae02f816a0171770fd \
--hash=sha256:5482e789294854c28237bba77c4c83be698be740e31a3ae5e879ee5444166582 \
--hash=sha256:5e44507bf8d14b36b8389b226665d597bc0f18ea035d75b4e53c7b1ea84583cc \
--hash=sha256:779245e13b9a6638df14641d029add5dc17edbef6ec915688f3acb9e720a5858 \
--hash=sha256:789caea816c6704f63f6241a519bfa347f72fbd67ba28d04636b7c6b7da94b0b \
--hash=sha256:7f8b25fa616d8b846aef64b15c606bb0828dbc35faf90566eb139aa9cff67af2 \
--hash=sha256:8cb8ce7c3347fcf9446f201dc30e2d5a3c898d009126010cbd1f443f28b52678 \
--hash=sha256:93a3209f6bb2b33e725ed08ee0991b92976dfdcf4e8b38646540674fc7508e13 \
--hash=sha256:a3a5ac8b56fe37f3125e5b72b61dcde43283e5370827f5233893d461b7360cd4 \
--hash=sha256:a47787a5e3649008a1102d3df55424e86606c9bae6fb77ac59afe06d234605f8 \
--hash=sha256:a79165431551042cc9d1d90e6145d5d0d3ab0f2d66326c201d9b0e7f5bf43604 \
--hash=sha256:a987f840718078212fdf4504d0fd4c6effe34a7e4740378e59d47696e8dfb477 \
--hash=sha256:a9bc127cdc4ecf87a5ea22a2556cab6c7eda2923f84e4f3cc588e8470ce4e42e \
--hash=sha256:bd13b5e9b543532453de08bcdc3cc7cebec6f9883e886fd20a92f26940fd3e7a \
--hash=sha256:c65f96dad14f8528a447414125e1fc8feb2ad5a272b8f68477abbcc1ea7d94b9 \
--hash=sha256:d8e3098721b84392ee45af2dd554c947c32cc52f862b6a3ae982dbb90f577f14 \
--hash=sha256:e6b79d0adb01aae87e8a44c2b64bc3f3fe59515280e00fb6d57a7267a2583cda \
--hash=sha256:e6b8f1881dac458c34778d0a424ae5769de30544fc678eac51c1c8bb2183e9da \
--hash=sha256:e9b2a6309f14c0497f348d08a065d52f3020656f675819fc405fb63bbcd26562 \
--hash=sha256:ecbfbc00bf55888edda9868a4cf927205de8499e7fabe6c050322298382953f2 \
--hash=sha256:efd0bf5205240182e0f13bcaea41be4fdf5c22c5129fc7ced4a0282ac86998c9
cryptography==42.0.8 \
--hash=sha256:013629ae70b40af70c9a7a5db40abe5d9054e6f4380e50ce769947b73bf3caad \
--hash=sha256:2346b911eb349ab547076f47f2e035fc8ff2c02380a7cbbf8d87114fa0f1c583 \
--hash=sha256:2f66d9cd9147ee495a8374a45ca445819f8929a3efcd2e3df6428e46c3cbb10b \
--hash=sha256:2f88d197e66c65be5e42cd72e5c18afbfae3f741742070e3019ac8f4ac57262c \
--hash=sha256:31f721658a29331f895a5a54e7e82075554ccfb8b163a18719d342f5ffe5ecb1 \
--hash=sha256:343728aac38decfdeecf55ecab3264b015be68fc2816ca800db649607aeee648 \
--hash=sha256:5226d5d21ab681f432a9c1cf8b658c0cb02533eece706b155e5fbd8a0cdd3949 \
--hash=sha256:57080dee41209e556a9a4ce60d229244f7a66ef52750f813bfbe18959770cfba \
--hash=sha256:5a94eccb2a81a309806027e1670a358b99b8fe8bfe9f8d329f27d72c094dde8c \
--hash=sha256:6b7c4f03ce01afd3b76cf69a5455caa9cfa3de8c8f493e0d3ab7d20611c8dae9 \
--hash=sha256:7016f837e15b0a1c119d27ecd89b3515f01f90a8615ed5e9427e30d9cdbfed3d \
--hash=sha256:81884c4d096c272f00aeb1f11cf62ccd39763581645b0812e99a91505fa48e0c \
--hash=sha256:81d8a521705787afe7a18d5bfb47ea9d9cc068206270aad0b96a725022e18d2e \
--hash=sha256:8d09d05439ce7baa8e9e95b07ec5b6c886f548deb7e0f69ef25f64b3bce842f2 \
--hash=sha256:961e61cefdcb06e0c6d7e3a1b22ebe8b996eb2bf50614e89384be54c48c6b63d \
--hash=sha256:9c0c1716c8447ee7dbf08d6db2e5c41c688544c61074b54fc4564196f55c25a7 \
--hash=sha256:a0608251135d0e03111152e41f0cc2392d1e74e35703960d4190b2e0f4ca9c70 \
--hash=sha256:a0c5b2b0585b6af82d7e385f55a8bc568abff8923af147ee3c07bd8b42cda8b2 \
--hash=sha256:ad803773e9df0b92e0a817d22fd8a3675493f690b96130a5e24f1b8fabbea9c7 \
--hash=sha256:b297f90c5723d04bcc8265fc2a0f86d4ea2e0f7ab4b6994459548d3a6b992a14 \
--hash=sha256:ba4f0a211697362e89ad822e667d8d340b4d8d55fae72cdd619389fb5912eefe \
--hash=sha256:c4783183f7cb757b73b2ae9aed6599b96338eb957233c58ca8f49a49cc32fd5e \
--hash=sha256:c9bb2ae11bfbab395bdd072985abde58ea9860ed84e59dbc0463a5d0159f5b71 \
--hash=sha256:cafb92b2bc622cd1aa6a1dce4b93307792633f4c5fe1f46c6b97cf67073ec961 \
--hash=sha256:d45b940883a03e19e944456a558b67a41160e367a719833c53de6911cabba2b7 \
--hash=sha256:dc0fdf6787f37b1c6b08e6dfc892d9d068b5bdb671198c72072828b80bd5fe4c \
--hash=sha256:dea567d1b0e8bc5764b9443858b673b734100c2871dc93163f58c46a97a83d28 \
--hash=sha256:dec9b018df185f08483f294cae6ccac29e7a6e0678996587363dc352dc65c842 \
--hash=sha256:e3ec3672626e1b9e55afd0df6d774ff0e953452886e06e0f1eb7eb0c832e8902 \
--hash=sha256:e599b53fd95357d92304510fb7bda8523ed1f79ca98dce2f43c115950aa78801 \
--hash=sha256:fa76fbb7596cc5839320000cdd5d0955313696d9511debab7ee7278fc8b5c84a \
--hash=sha256:fff12c88a672ab9c9c1cf7b0c80e3ad9e2ebd9d828d955c126be4fd3e5578c9e
# via -r requirements.in
greenlet==3.0.3 \
--hash=sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67 \
@ -284,9 +284,9 @@ sqlalchemy==2.0.30 \
--hash=sha256:f7703c2010355dd28f53deb644a05fc30f796bd8598b43f0ba678878780b6e4c \
--hash=sha256:fa561138a64f949f3e889eb9ab8c58e1504ab351d6cf55259dc4c248eaa19da6
# via -r requirements.in
typing-extensions==4.12.0 \
--hash=sha256:8cbcdc8606ebcb0d95453ad7dc5065e6237b6aa230a31e81d0f440c30fed5fd8 \
--hash=sha256:b349c66bea9016ac22978d800cfff206d5f9816951f12a7d0ec5578b0a819594
typing-extensions==4.12.2 \
--hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \
--hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8
# via
# psycopg
# psycopg-pool

View file

@ -86,6 +86,9 @@ class Configurator:
return {}
servers = {}
for server_name in self.__variables["SERVER_NAME"].strip().split(" "):
if not server_name:
continue
if not re_search(self.__settings["SERVER_NAME"]["regex"], server_name):
self.__logger.warning(f"Ignoring server name {server_name} because regex is not valid")
continue

View file

@ -39,6 +39,7 @@ if __name__ == "__main__":
parser.add_argument("--output", default=join(sep, "etc", "nginx"), type=str, help="where to write the rendered files")
parser.add_argument("--target", default=join(sep, "etc", "nginx"), type=str, help="where nginx will search for configurations files")
parser.add_argument("--variables", type=str, help="path to the file containing environment variables")
parser.add_argument("--no-linux-reload", action="store_true", help="disable linux reload")
args = parser.parse_args()
settings_path = Path(args.settings)

View file

@ -1,4 +1,4 @@
jinja2==3.1.4
python-dotenv==1.0.1
redis==5.0.4
requests==2.32.2
redis==5.0.5
requests==2.32.3

View file

@ -8,9 +8,9 @@ async-timeout==4.0.3 \
--hash=sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f \
--hash=sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028
# via redis
certifi==2024.2.2 \
--hash=sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f \
--hash=sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1
certifi==2024.6.2 \
--hash=sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516 \
--hash=sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56
# via requests
charset-normalizer==3.3.2 \
--hash=sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027 \
@ -178,13 +178,13 @@ python-dotenv==1.0.1 \
--hash=sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca \
--hash=sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a
# via -r requirements.in
redis==5.0.4 \
--hash=sha256:7adc2835c7a9b5033b7ad8f8918d09b7344188228809c98df07af226d39dec91 \
--hash=sha256:ec31f2ed9675cc54c21ba854cfe0462e6faf1d83c8ce5944709db8a4700b9c61
redis==5.0.5 \
--hash=sha256:30b47d4ebb6b7a0b9b40c1275a19b87bb6f46b3bed82a89012cf56dea4024ada \
--hash=sha256:3417688621acf6ee368dec4a04dd95881be24efd34c79f00d31f62bb528800ae
# via -r requirements.in
requests==2.32.2 \
--hash=sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289 \
--hash=sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c
requests==2.32.3 \
--hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \
--hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6
# via -r requirements.in
urllib3==2.2.1 \
--hash=sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d \

View file

@ -90,6 +90,8 @@ if __name__ == "__main__":
LOGGER.error(f"Missing RX rights on directory : {path}")
sys_exit(1)
tmp_config = {}
if args.variables:
variables_path = Path(args.variables)
LOGGER.info(f"Variables : {variables_path}")
@ -191,11 +193,13 @@ if __name__ == "__main__":
sys_exit(0)
changes = []
changed_plugins = set()
err = db.save_config(settings, args.method, changed=False)
if err:
if isinstance(err, str):
LOGGER.warning(f"Couldn't save config to database : {err}, config may not work as expected")
else:
changed_plugins = err
changes.append("config")
LOGGER.info("Config successfully saved to database")
@ -220,7 +224,7 @@ if __name__ == "__main__":
if not args.no_check_changes:
# update changes in db
ret = db.checked_changes(changes, value=True)
ret = db.checked_changes(changes, plugins_changes=changed_plugins, value=True)
if ret:
LOGGER.error(f"An error occurred when setting the changes to checked in the database : {ret}")
except SystemExit as e:

View file

@ -1,59 +0,0 @@
#!/usr/bin/env python3
from glob import glob
from json import JSONDecodeError, loads
from os import sep
from os.path import join
from pathlib import Path
from re import match
from traceback import format_exc
from typing import Any, Dict, Literal, Union
from logger import setup_logger
class ConfigCaller:
def __init__(self):
self.__logger = setup_logger("Config", "INFO")
self._settings = loads(Path(sep, "usr", "share", "bunkerweb", "settings.json").read_text(encoding="utf-8"))
for plugin in glob(join(sep, "usr", "share", "bunkerweb", "core", "*", "plugin.json")) + glob(
join(sep, "etc", "bunkerweb", "plugins", "*", "plugin.json")
):
try:
self._settings.update(loads(Path(plugin).read_text(encoding="utf-8"))["settings"])
except KeyError:
self.__logger.error(
f'Error while loading plugin metadata file at {plugin} : missing "settings" key',
)
except JSONDecodeError:
self.__logger.error(
f"Exception while loading plugin metadata file at {plugin} :\n{format_exc()}",
)
def _is_setting(self, setting) -> bool:
return setting in self._settings
def _is_setting_context(self, setting: str, context: Union[Literal["global"], Literal["multisite"]]) -> bool:
if self._is_setting(setting):
return self._settings[setting]["context"] == context
elif match(r"^.+_\d+$", setting):
multiple_setting = "_".join(setting.split("_")[:-1])
return (
self._is_setting(multiple_setting) and self._settings[multiple_setting]["context"] == context and "multiple" in self._settings[multiple_setting]
)
return False
def _full_env(self, env_instances: Dict[str, Any], env_services: Dict[str, Any]) -> Dict[str, Any]:
full_env = {}
# Fill with default values
for k, v in self._settings.items():
full_env[k] = v["default"]
# Replace with instances values
for k, v in env_instances.items():
full_env[k] = v
if not self._is_setting_context(k, "global") and env_instances.get("MULTISITE", "no") == "yes" and env_instances.get("SERVER_NAME", "") != "":
for server_name in env_instances["SERVER_NAME"].split(" "):
full_env[f"{server_name}_{k}"] = v
# Replace with services values
full_env = full_env | env_services
return full_env

View file

@ -25,8 +25,8 @@ setLoggerClass(BWLogger)
default_level = _nameToLevel.get(getenv("CUSTOM_LOG_LEVEL", getenv("LOG_LEVEL", "INFO")).upper(), INFO)
basicConfig(
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
datefmt="[%Y-%m-%d %H:%M:%S]",
format="%(asctime)s [%(name)s] [%(process)d] [%(levelname)s] - %(message)s",
datefmt="[%Y-%m-%d %H:%M:%S %z]",
level=default_level,
)

View file

@ -25,13 +25,13 @@
"name": "ModSecurity-nginx v1.0.3",
"url": "https://github.com/SpiderLabs/ModSecurity-nginx.git",
"commit": "d59e4ad121df702751940fd66bcc0b3ecb51a079",
"post_install": "patch --forward src/deps/src/modsecurity-nginx/src/ngx_http_modsecurity_log.c src/deps/misc/modsecurity-nginx.patch && patch --forward src/deps/src/modsecurity-nginx/config src/deps/misc/config.patch && patch --forward src/deps/src/modsecurity-nginx/src/ngx_http_modsecurity_common.h src/deps/misc/ngx_http_modsecurity_common.h.patch && patch --forward src/deps/src/modsecurity-nginx/src/ngx_http_modsecurity_module.c src/deps/misc/ngx_http_modsecurity_module.c.patch && patch --forward src/deps/src/modsecurity-nginx/config src/deps/misc/modsecurity-nginx-config.patch"
"post_install": "patch --forward src/deps/src/modsecurity-nginx/src/ngx_http_modsecurity_log.c src/deps/misc/modsecurity-nginx.patch && patch --forward src/deps/src/modsecurity-nginx/src/ngx_http_modsecurity_common.h src/deps/misc/ngx_http_modsecurity_common.h.patch && patch --forward src/deps/src/modsecurity-nginx/src/ngx_http_modsecurity_module.c src/deps/misc/ngx_http_modsecurity_module.c.patch && patch --forward src/deps/src/modsecurity-nginx/config src/deps/misc/modsecurity-nginx-config.patch"
},
{
"id": "nginx",
"name": "Nginx v1.26.0",
"name": "Nginx v1.26.1",
"url": "https://github.com/nginx/nginx.git",
"commit": "361f6bf4b1cd5057d8f6365d1c0a94f3d72824e4"
"commit": "02725ce722bc7b34fa307d611eac3956ef744efa"
},
{
"id": "ngx_brotli",
@ -151,9 +151,9 @@
},
{
"id": "lua-resty-openssl",
"name": "lua-resty-openssl v1.3.1",
"name": "lua-resty-openssl v1.4.0",
"url": "https://github.com/fffonion/lua-resty-openssl.git",
"commit": "643956d990b3ef4d9b52d0b77ba4d69d7e912dcc",
"commit": "e56da6c5f285ff0e40fce703b870e83ee8eac321",
"post_install": "rm -r src/deps/src/lua-resty-openssl/t"
},
{

View file

@ -169,8 +169,7 @@ do_and_check_cmd make PREFIX=/usr/share/bunkerweb/deps LUA_LIB_DIR=/usr/share/bu
# Installing lua-resty-openssl
echo " Installing lua-resty-openssl"
export CHANGE_DIR="/tmp/bunkerweb/deps/src/lua-resty-openssl"
do_and_check_cmd make LUA_LIB_DIR=/usr/share/bunkerweb/deps/lib/lua install
do_and_check_cmd bash -c "mv lib/resty/openssl.lua /usr/share/bunkerweb/deps/lib/lua/resty/"
do_and_check_cmd make PREFIX=/usr/share/bunkerweb/deps LUA_LIB_DIR=/usr/share/bunkerweb/deps/lib/lua install
# Installing lua-ffi-zlib
echo " Installing lua-ffi-zlib"

View file

@ -1,18 +0,0 @@
@@ -110,7 +110,7 @@
ngx_module_type=HTTP_FILTER
ngx_module_name="$ngx_addon_name"
ngx_module_srcs="$ngx_addon_dir/src/ngx_http_modsecurity_module.c \
- $ngx_addon_dir/src/ngx_http_modsecurity_pre_access.c \
+ $ngx_addon_dir/src/ngx_http_modsecurity_access.c \
$ngx_addon_dir/src/ngx_http_modsecurity_header_filter.c \
$ngx_addon_dir/src/ngx_http_modsecurity_body_filter.c \
$ngx_addon_dir/src/ngx_http_modsecurity_log.c \
@@ -141,7 +141,7 @@
NGX_ADDON_SRCS="\
$NGX_ADDON_SRCS \
$ngx_addon_dir/src/ngx_http_modsecurity_module.c \
- $ngx_addon_dir/src/ngx_http_modsecurity_pre_access.c \
+ $ngx_addon_dir/src/ngx_http_modsecurity_access.c \
$ngx_addon_dir/src/ngx_http_modsecurity_header_filter.c \
$ngx_addon_dir/src/ngx_http_modsecurity_body_filter.c \
$ngx_addon_dir/src/ngx_http_modsecurity_log.c \

View file

@ -1,8 +1,19 @@
@@ -126,6 +126,7 @@
ngx_http_v2_filter_module \
ngx_http_range_header_filter_module \
@@ -14,3 +14,3 @@
ngx_feature_libs="-lmodsecurity"
-ngx_feature_test='printf("hello");'
+ngx_feature_test='msc_init();'
ngx_modsecurity_opt_I=
@@ -112,3 +112,3 @@
ngx_module_srcs="$ngx_addon_dir/src/ngx_http_modsecurity_module.c \
- $ngx_addon_dir/src/ngx_http_modsecurity_pre_access.c \
+ $ngx_addon_dir/src/ngx_http_modsecurity_access.c \
$ngx_addon_dir/src/ngx_http_modsecurity_header_filter.c \
@@ -128,2 +128,3 @@
ngx_http_gzip_filter_module \
+ ngx_http_brotli_filter_module \
$ngx_module_name \
$modsecurity_dependency";
@@ -143,3 +144,3 @@
$ngx_addon_dir/src/ngx_http_modsecurity_module.c \
- $ngx_addon_dir/src/ngx_http_modsecurity_pre_access.c \
+ $ngx_addon_dir/src/ngx_http_modsecurity_access.c \
$ngx_addon_dir/src/ngx_http_modsecurity_header_filter.c \

View file

@ -1,5 +1,5 @@
pip==24.0
pip-compile-multi==2.6.3
pip-compile-multi==2.6.4
pip-tools==7.4.1
pip-upgrader==1.4.15
setuptools==70.0.0

View file

@ -8,9 +8,9 @@ build==1.2.1 \
--hash=sha256:526263f4870c26f26c433545579475377b2b7588b6f1eac76a001e873ae3e19d \
--hash=sha256:75e10f767a433d9a86e50d83f418e83efc18ede923ee5ff7df93b6cb0306c5d4
# via pip-tools
certifi==2024.2.2 \
--hash=sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f \
--hash=sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1
certifi==2024.6.2 \
--hash=sha256:3cd43f1c6fa7dedc5899d69d3ad0398fd018ad1a17fba83ddaf78aa46c747516 \
--hash=sha256:ddc6c8ce995e6987e7faf5e3f1b02b302836a0e5d98ece18392cb1a36c72ad56
# via requests
charset-normalizer==3.3.2 \
--hash=sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027 \
@ -137,9 +137,9 @@ pip==24.0 \
# via
# build
# pip-upgrader
pip-compile-multi==2.6.3 \
--hash=sha256:50f7db9f5c57a2d11035805b6d86620c4d640c560f22e3c8b509e133456d8a53 \
--hash=sha256:f0b950a2175d0b86fd5a186d2f786bc8f7c013acd2e856a2bc948d194e0e4c92
pip-compile-multi==2.6.4 \
--hash=sha256:1cf1f1eaa68b7993303edf83e0dc40b8b9bb4dced334b0c18332f4aeae318e3b \
--hash=sha256:487741913c0eba7aeee6e0cd79fe123df3a22f3489e565c9851eb259dd3c3d52
# via -r requirements-deps.in
pip-tools==7.4.1 \
--hash=sha256:4c690e5fbae2f21e87843e89c26191f0d9454f362d8acdbd695716493ec8b3a9 \
@ -157,9 +157,9 @@ pyproject-hooks==1.1.0 \
# via
# build
# pip-tools
requests==2.32.2 \
--hash=sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289 \
--hash=sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c
requests==2.32.3 \
--hash=sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760 \
--hash=sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6
# via
# -r requirements-deps.in
# pip-tools
@ -192,9 +192,9 @@ wheel==0.43.0 \
# via
# -r requirements-deps.in
# pip-tools
zipp==3.18.2 \
--hash=sha256:6278d9ddbcfb1f1089a88fde84481528b07b0e10474e09dcfe53dad4069fa059 \
--hash=sha256:dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e
zipp==3.19.2 \
--hash=sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19 \
--hash=sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c
# via
# -r requirements-deps.in
# pip-tools

View file

@ -54,9 +54,9 @@ wheel==0.43.0 \
# via
# -r requirements.in
# pip-tools
zipp==3.18.2 \
--hash=sha256:6278d9ddbcfb1f1089a88fde84481528b07b0e10474e09dcfe53dad4069fa059 \
--hash=sha256:dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e
zipp==3.19.2 \
--hash=sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19 \
--hash=sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c
# via
# -r requirements.in
# pip-tools

View file

@ -30,14 +30,14 @@ jobs:
# latest and one version older for valgrind and perf test
- nginx: "1.19.9"
openssl: "3.1.5"
openssl_fips: "3.0.8"
openssl_fips: "3.0.9"
extras: "valgrind"
lua_nginx_module: "v0.10.20"
lua_resty_core: "v0.1.22"
nginx_cc_opts: "-Wno-error"
- nginx: "1.21.4"
openssl: "3.1.5"
openssl_fips: "3.0.8"
openssl_fips: "3.0.9"
extras: "valgrind"
lua_nginx_module: "v0.10.25"
lua_resty_core: "v0.1.27"
@ -49,25 +49,33 @@ jobs:
lua_resty_core: "v0.1.28"
- nginx: "1.25.3"
openssl: "3.0.13"
openssl_fips: "3.0.8"
openssl_fips: "3.0.9"
extras: "valgrind perf lua-kong-nginx-module"
lua_nginx_module: "v0.10.26"
lua_resty_core: "v0.1.28"
nginx_cc_opts: "-Wno-error"
- nginx: "1.25.3"
openssl: "3.1.5"
openssl_fips: "3.0.8"
openssl_fips: "3.0.9"
extras: "valgrind perf lua-kong-nginx-module"
lua_nginx_module: "v0.10.26"
lua_resty_core: "v0.1.28"
nginx_cc_opts: "-Wno-error"
- nginx: "1.25.3"
openssl: "3.2.1"
openssl_fips: "3.0.8"
openssl_fips: "3.0.9"
extras: "valgrind perf lua-kong-nginx-module"
lua_nginx_module: "v0.10.26"
lua_resty_core: "v0.1.28"
nginx_cc_opts: "-Wno-error"
- nginx: "1.25.3"
openssl: "3.3.0"
openssl_fips: "3.0.9"
extras: "valgrind perf lua-kong-nginx-module"
lua_nginx_module: "v0.10.26"
lua_resty_core: "v0.1.28"
nginx_cc_opts: "-Wno-error"
env:
JOBS: 3

View file

@ -2,12 +2,22 @@
## [Unreleased]
<a name="1.4.0"></a>
## [1.4.0] - 2024-05-27
### bug fixes
- **ec:** add missing cdef for EC_POINT_free [2093e88](https://github.com/fffonion/lua-resty-openssl/commit/2093e8814ccfbe830ba594a71f05870cac208e9c)
### features
- **pkey:** allow pkey.new to compose from parameters [91a30f6](https://github.com/fffonion/lua-resty-openssl/commit/91a30f6988e3fc696363ce1445b49a3f6ee8f35e)
- **pkey:** add pkey:verify_raw [0016308](https://github.com/fffonion/lua-resty-openssl/commit/0016308c9e3a2ccfdfe674ede64e462060f7b13b)
<a name="1.3.1"></a>
## [1.3.1] - 2024-04-22
### bug fixes
- **aux/jwk:** remove ecx.d if exporting as public key [9d34ff8](https://github.com/fffonion/lua-resty-openssl/commit/9d34ff8fd79debbcf155f74af0b161083b6a8385)
- **aux/nginx:** fix the typo of get_socket_ssl in the stream module [0aa315e](https://github.com/fffonion/lua-resty-openssl/commit/0aa315efe3d98d38d8d77dedf687958b62d8b184)
- **aux/nginx:** remove extra sanity test that prevent usage of lua-kong-nginx-module [7bd2d0a](https://github.com/fffonion/lua-resty-openssl/commit/7bd2d0aabe82219071fe9fd1b30e49ff88bd5472)
- **aux/nginx:** fix the typo of get_socket_ssl in the stream module [ad18b3c](https://github.com/fffonion/lua-resty-openssl/commit/ad18b3c18c7ec3db175aabfbf6928141c3b53b17)
- **aux/nginx:** remove extra sanity test that prevent usage of lua-kong-nginx-module [2323526](https://github.com/fffonion/lua-resty-openssl/commit/2323526766b131bde94052449c41a331e93288bd)
<a name="1.3.0"></a>
@ -591,7 +601,8 @@
- **x509:** export pubkey [ede4f81](https://github.com/fffonion/lua-resty-openssl/commit/ede4f817cb0fe092ad6f9ab5d6ecdcde864a9fd8)
[Unreleased]: https://github.com/fffonion/lua-resty-openssl/compare/1.3.1...HEAD
[Unreleased]: https://github.com/fffonion/lua-resty-openssl/compare/1.4.0...HEAD
[1.4.0]: https://github.com/fffonion/lua-resty-openssl/compare/1.3.1...1.4.0
[1.3.1]: https://github.com/fffonion/lua-resty-openssl/compare/1.3.0...1.3.1
[1.3.0]: https://github.com/fffonion/lua-resty-openssl/compare/1.2.1...1.3.0
[1.2.1]: https://github.com/fffonion/lua-resty-openssl/compare/1.2.0...1.2.1

View file

@ -66,6 +66,7 @@ Table of Contents
+ [pkey:encrypt](#pkeyencrypt)
+ [pkey:decrypt](#pkeydecrypt)
+ [pkey:sign_raw](#pkeysign_raw)
+ [pkey:verify_raw](#pkeyverify_raw)
+ [pkey:verify_recover](#pkeyverify_recover)
+ [pkey:derive](#pkeyderive)
+ [pkey:tostring](#pkeytostring)
@ -871,13 +872,14 @@ for EC keys. See [pkey:sign](#pkeysign) and [pkey.verify](#pkeyverify) for detai
- When running outside of OpenResty, needs to install a JSON library (`cjson` or `dkjson`)
and `basexx`.
[Back to TOC](#table-of-contents)
#### Key generation
**syntax**: *pk, err = pkey.new(config?)*
Generate a new public key or private key.
To generate RSA key, `config` table can have `bits` and `exp` field to control key generation.
When `config` is emitted, this function generates a 2048 bit RSA key with `exponent` of 65537,
which is equivalent to:
@ -936,6 +938,27 @@ pkey.new({
```
[Back to TOC](#table-of-contents)
#### Key composition
**syntax**: *pk, err = pkey.new(config?)*
Compose a public or private key using existing parameters. To see
list of parameters for each key, refer to [pkey:set_parameters](#pkeyset_parameters).
Only `type` and `params` should exist in `config` table, all other keys will be ignored.
```lua
local private_bn = require "resty.openssl.bn".new("7F48282CCA4C1A65D589C06DBE9C42AE50FBFFDF3A18CBB48498E1DE47F11BE1A3486CD8FA950D68F111970F922279D8", 16)
local p_384, err = assert(require("resty.openssl.pkey").new({
type = "EC",
params = {
private = private_bn,
group = "secp384r1",
}
}))
```
[Back to TOC](#table-of-contents)
@ -1195,6 +1218,8 @@ pk:sign(message, nil, nil, {
-- in pkeyutl CLI the above is equivalent to: `openssl pkeyutl -sign -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256
```
To sign a message without doing message digest, please check [pkey:sign_raw](#pkeysign_raw).
[Back to TOC](#table-of-contents)
### pkey:verify
@ -1262,6 +1287,8 @@ ngx.say(ngx.encode_base64(signature))
```
To verify a message without doing message digest, please check [pkey:verify_raw](#pkeyverify_raw) and [pkey:verify_recover](#pkeyverify_recover).
[Back to TOC](#table-of-contents)
### pkey:encrypt
@ -1325,6 +1352,25 @@ for an example.
[Back to TOC](#table-of-contents)
### pkey:verify_raw
**syntax**: *ok, err = pk:verify_raw(signature, data, md_alg, padding?, opts?)*
Verify the cipher text `signature` with the message `data` with pkey instance, which must loaded a public key. Set the message digest to `md_alg` but doesn't do message digest
automatically, in other words, this function assumes `data` has already been hashed with `md_alg`.
When `md_alg` is undefined, for RSA and EC keys, this function does SHA256 by default. For Ed25519 or Ed448 keys, no default value is set.
The optinal fourth argument `padding` has same meaning as in [pkey:sign](#pkeysign).
If omitted, `padding` is default to `pkey.PADDINGS.RSA_PKCS1_PADDING`.
The fifth optional argument `opts` has same meaning as in [pkey:sign](#pkeysign).
See [examples/raw-sign-and-recover.lua](https://github.com/fffonion/lua-resty-openssl/blob/master/examples/raw-sign-and-recover.lua)
for an example.
[Back to TOC](#table-of-contents)
### pkey:verify_recover
**syntax**: *txt, err = pk:verify_recover(signature, padding?, opts?)*

View file

@ -7,11 +7,29 @@ local original = "original text"
-- same as nodejs: crypto.privateEncrypt
-- php: openssl_private_encrypt
local digested = assert(priv:sign_raw(original))
local signed = assert(priv:sign_raw(original))
print("Digested message: " .. ngx.encode_base64(digested))
print("Signed message: " .. ngx.encode_base64(signed))
-- same as nodejs: crypto.publicDecrypt
-- php: openssl_public_decrypt
local recovered = assert(pub:verify_recover(digested))
local recovered = assert(pub:verify_recover(signed))
print("Recovered message: " .. recovered)
local priv = assert(pkey.new({
type = "EC",
}))
local pub = assert(pkey.new(priv:to_PEM("public")))
local md_alg = "sha512"
local hashed = require "resty.openssl.digest".new(md_alg):final(original)
local signed = assert(priv:sign_raw(hashed))
print("Signed message: " .. ngx.encode_base64(signed))
-- same as nodejs: crypto.publicDecrypt
-- php: openssl_public_decrypt
local verified = assert(pub:verify_raw(signed, hashed, md_alg))
print("Verification result: ", verified)

View file

@ -24,7 +24,7 @@ try_require_modules()
local _M = {
_VERSION = '1.3.1',
_VERSION = '1.4.0',
}
function _M.load_modules()

View file

@ -177,6 +177,7 @@ function _M.load_jwk(txt)
if key ~= nil then
return key
end
key_free = function() end
else
return nil, "not yet supported jwk type \"" .. (tbl["kty"] or "nil") .. "\""
end

View file

@ -125,7 +125,8 @@ else
} ngx_connection_s;
]]
else
error("resty.openssl.auxiliary.nginx doesn't support Nginx version " .. ngx_version, 2)
error("resty.openssl.auxiliary.nginx development mode doesn't support Nginx version " .. ngx_version ..
", please compile nginx with lua-resty-openssl-aux-module or lua-kong-nginx-module.", 2)
end
ffi.cdef [[
@ -167,7 +168,8 @@ else
local NO_C_MODULE_WARNING_MSG_SHOWN = false
local NO_C_MODULE_WARNING_MSG = "note resty.openssl.auxiliary.nginx is using plain FFI " ..
"and it's only intended to be used in development, " ..
"consider using lua-resty-openssl.aux-module in production."
"consider using lua-resty-openssl-aux-module or " ..
"lua-kong-nginx-module in production."
local function get_ngx_ssl_from_req()
if not NO_C_MODULE_WARNING_MSG_SHOWN then

View file

@ -158,6 +158,8 @@ function _M.set_parameters(ec_key_st, opts)
end
end
return true
end
return _M

View file

@ -34,6 +34,7 @@ ffi.cdef [[
BIGNUM *x, BIGNUM *y, BN_CTX *ctx);
EC_POINT *EC_POINT_bn2point(const EC_GROUP *group, const BIGNUM *bn,
EC_POINT *p, BN_CTX *ctx);
void EC_POINT_free(EC_POINT *point);
point_conversion_form_t EC_KEY_get_conv_form(const EC_KEY *key);

View file

@ -41,6 +41,10 @@ ffi.cdef [[
int EVP_PKEY_sign(EVP_PKEY_CTX *ctx,
unsigned char *sig, size_t *siglen,
const unsigned char *tbs, size_t tbslen);
int EVP_PKEY_verify_init(EVP_PKEY_CTX *ctx);
int EVP_PKEY_verify(EVP_PKEY_CTX *ctx,
const unsigned char *sig, size_t siglen,
const unsigned char *tbs, size_t tbslen);
int EVP_PKEY_verify_recover_init(EVP_PKEY_CTX *ctx);
int EVP_PKEY_verify_recover(EVP_PKEY_CTX *ctx,
unsigned char *rout, size_t *routlen,

View file

@ -402,6 +402,67 @@ local function generate_key(config)
return ctx_ptr[0]
end
local function compose_key(config)
local typ = config.type or 'RSA'
local key_type
if typ == "RSA" then
key_type = evp_macro.EVP_PKEY_RSA
elseif typ == "EC" then
key_type = evp_macro.EVP_PKEY_EC
elseif evp_macro.ecx_curves[typ] then
key_type = evp_macro.ecx_curves[typ]
else
return nil, "unsupported type " .. typ
end
if key_type == 0 then
return nil, "the linked OpenSSL library doesn't support " .. typ .. " key"
end
local key, err, key_free, _
if key_type == evp_macro.EVP_PKEY_EC then
key = C.EC_KEY_new()
if key == nil then
return nil, "EC_KEY_new failed"
end
key_free = C.EC_KEY_free
_, err = ec_lib.set_parameters(key, config.params)
elseif key_type == evp_macro.EVP_PKEY_RSA then
key = C.RSA_new()
if key == nil then
return nil, "RSA_new failed"
end
key_free = C.RSA_free
_, err = rsa_lib.set_parameters(key, config.params)
elseif key_type == evp_macro.EVP_PKEY_ED25519 or
key_type == evp_macro.EVP_PKEY_X25519 or
key_type == evp_macro.EVP_PKEY_ED448 or
key_type == evp_macro.EVP_PKEY_X448 then
key_free = function() end
key, err = ecx_lib.set_parameters(key_type, nil, config.params)
end
if err then
return nil, "failed to construct " .. typ.. " key from parameters: " .. err
end
local ctx = C.EVP_PKEY_new()
if ctx == nil then
key_free(key)
return nil, "EVP_PKEY_new() failed"
end
local code = C.EVP_PKEY_assign(ctx, key_type, key)
if code ~= 1 then
key_free(key)
C.EVP_PKEY_free(ctx)
return nil, "EVP_PKEY_assign() failed"
end
return ctx
end
local load_key_try_funcs = {} do
-- TODO: pkcs1 load functions are not required in openssl 3.0
local _load_key_try_funcs = {
@ -506,9 +567,13 @@ function _M.new(s, opts)
local ctx, err
s = s or {}
if type(s) == 'table' then
ctx, err = generate_key(s)
if s.params then
ctx, err = compose_key(s)
else
ctx, err = generate_key(s)
end
if err then
err = "pkey.new:generate_key: " .. err
err = "pkey.new:new_key: " .. err
end
elseif type(s) == 'string' then
ctx, err = load_pem_der(s, opts or empty_table, load_key_try_funcs)
@ -656,8 +721,9 @@ end
local ASYMMETRIC_OP_ENCRYPT = 0x1
local ASYMMETRIC_OP_DECRYPT = 0x2
local ASYMMETRIC_OP_SIGN_RAW = 0x4
local ASYMMETRIC_OP_VERIFY_RECOVER = 0x8
local ASYMMETRIC_OP_SIGN_RAW = 0x3
local ASYMMETRIC_OP_VERIFY_RAW = 0x4
local ASYMMETRIC_OP_VERIFY_RECOVER = 0x5
local function asymmetric_routine(self, s, op, padding, opts)
if type(s) ~= "string" then
@ -701,6 +767,10 @@ local function asymmetric_routine(self, s, op, padding, opts)
fint = C.EVP_PKEY_sign_init
f = C.EVP_PKEY_sign
op_name = "sign"
elseif op == ASYMMETRIC_OP_VERIFY_RAW then
fint = C.EVP_PKEY_verify_init
f = C.EVP_PKEY_verify
op_name = "verify"
elseif op == ASYMMETRIC_OP_VERIFY_RECOVER then
fint = C.EVP_PKEY_verify_recover_init
f = C.EVP_PKEY_verify_recover
@ -725,13 +795,25 @@ local function asymmetric_routine(self, s, op, padding, opts)
return nil, "pkey:asymmetric_routine: " .. err
end
local length = ptr_of_size_t(self.buf_size)
if f(pkey_ctx, self.buf, length, s, #s) <= 0 then
return nil, format_error("pkey:asymmetric_routine EVP_PKEY_" .. op_name)
local buf, buf_len
if opts and opts.buf_in then
buf = opts.buf_in
buf_len = #buf
else
buf = self.buf
buf_len = ptr_of_size_t(self.buf_size)
end
return ffi_str(self.buf, length[0]), nil
code = f(pkey_ctx, buf, buf_len, s, #s)
if code <= 0 then
return nil, format_error("pkey:asymmetric_routine EVP_PKEY_" .. op_name, code)
end
if not opts or not opts.buf_in then
return ffi_str(self.buf, buf_len[0]), nil
end
return true
end
_M.PADDINGS = rsa_macro.paddings
@ -753,6 +835,16 @@ function _M:sign_raw(s, padding, opts)
return asymmetric_routine(self, s, ASYMMETRIC_OP_SIGN_RAW, padding, opts)
end
function _M:verify_raw(signature, hashed_message, md_alg, padding, opts)
opts = opts or {}
opts.buf_in = signature
if md_alg then
table.insert(opts, "digest:" .. md_alg)
end
return asymmetric_routine(self, hashed_message, ASYMMETRIC_OP_VERIFY_RAW, padding, opts)
end
function _M:verify_recover(s, padding, opts)
return asymmetric_routine(self, s, ASYMMETRIC_OP_VERIFY_RECOVER, padding, opts)
end

View file

@ -1,8 +1,8 @@
package = "lua-resty-openssl"
version = "1.3.1-1"
version = "1.4.0-1"
source = {
url = "git+https://github.com/fffonion/lua-resty-openssl.git",
tag = "1.3.1"
tag = "1.4.0"
}
description = {
detailed = "FFI-based OpenSSL binding for LuaJIT.",

View file

@ -1 +1 @@
jinja2==3.1.3
jinja2==3.1.4

View file

@ -12,7 +12,7 @@ ngx_feature_name=
ngx_feature_run=no
ngx_feature_incs="#include <modsecurity/modsecurity.h>"
ngx_feature_libs="-lmodsecurity"
ngx_feature_test='printf("hello");'
ngx_feature_test='msc_init();'
ngx_modsecurity_opt_I=
ngx_modsecurity_opt_L=
@ -190,4 +190,4 @@ if [ "$ngx_module_link" != DYNAMIC ] ; then
exit 1
fi
HTTP_FILTER_MODULES="${modules}"
fi
fi

View file

@ -478,3 +478,4 @@ f8134640e8615448205785cf00b0bc810489b495 release-1.25.1
294a3d07234f8f65d7b0e0b0e2c5b05c12c5da0a release-1.25.3
173a0a7dbce569adbb70257c6ec4f0f6bc585009 release-1.25.4
8618e4d900cc71082fbe7dc72af087937d64faf5 release-1.25.5
a58202a8c41bf0bd97eef1b946e13105a105520d release-1.26.0

View file

@ -19,7 +19,7 @@ else
#include <atomic_ops.h>"
ngx_feature_path=
ngx_feature_libs="-latomic_ops"
ngx_feature_test="long n = 0;
ngx_feature_test="AO_t n = 0;
if (!AO_compare_and_swap(&n, 0, 1))
return 1;
if (AO_fetch_and_add(&n, 1) != 1)

View file

@ -5,6 +5,62 @@
<change_log title="nginx">
<changes ver="1.26.1" date="2024-05-29">
<change type="security">
<para lang="ru">
при использовании HTTP/3 обработка специально созданной QUIC-сессии могла
приводить к падению рабочего процесса, отправке клиенту содержимого памяти
рабочего процесса на системах с MTU больше 4096 байт, а также потенциально
могла иметь другие последствия
(CVE-2024-32760, CVE-2024-31079, CVE-2024-35200, CVE-2024-34161).<br/>
Спасибо Nils Bars из CISPA.
</para>
<para lang="en">
when using HTTP/3, processing of a specially crafted QUIC session might
cause a worker process crash, worker process memory disclosure on systems
with MTU larger than 4096 bytes, or might have potential other impact
(CVE-2024-32760, CVE-2024-31079, CVE-2024-35200, CVE-2024-34161).<br/>
Thanks to Nils Bars of CISPA.
</para>
</change>
<change type="bugfix">
<para lang="ru">
уменьшено потребление памяти для долгоживущих запросов,
если используются директивы gzip, gunzip, ssi, sub_filter или grpc_pass.
</para>
<para lang="en">
reduced memory consumption for long-lived requests
if "gzip", "gunzip", "ssi", "sub_filter", or "grpc_pass" directives are used.
</para>
</change>
<change type="bugfix">
<para lang="ru">
nginx не собирался gcc 14,
если использовался параметр --with-atomic.<br/>
Спасибо Edgar Bonet.
</para>
<para lang="en">
nginx could not be built by gcc 14
if the --with-atomic option was used.<br/>
Thanks to Edgar Bonet.
</para>
</change>
<change type="bugfix">
<para lang="ru">
в HTTP/3.
</para>
<para lang="en">
in HTTP/3.
</para>
</change>
</changes>
<changes ver="1.26.0" date="2024-04-23">
<change>

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