mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
Merge pull request #969 from bunkerity/dev
Merge branch "dev" into branch "staging"
This commit is contained in:
commit
2e661fc063
1236 changed files with 131262 additions and 15740 deletions
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
|
|
@ -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@e8893c57a1f3a2b659b6b55564fdfdbbd2982911 # v3.24.0
|
||||
uses: github/codeql-action/init@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
config-file: ./.github/codeql.yml
|
||||
setup-python-dependencies: false
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@e8893c57a1f3a2b659b6b55564fdfdbbd2982911 # v3.24.0
|
||||
uses: github/codeql-action/analyze@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
|
|
|||
6
.github/workflows/container-build.yml
vendored
6
.github/workflows/container-build.yml
vendored
|
|
@ -63,10 +63,10 @@ jobs:
|
|||
SSH_IP: ${{ secrets.ARM_SSH_IP }}
|
||||
SSH_CONFIG: ${{ secrets.ARM_SSH_CONFIG }}
|
||||
- name: Setup Buildx
|
||||
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
|
||||
uses: docker/setup-buildx-action@0d103c3126aa41d772a8362f6aa67afac040f80c # v3.1.0
|
||||
if: inputs.CACHE_SUFFIX != 'arm'
|
||||
- name: Setup Buildx (ARM)
|
||||
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
|
||||
uses: docker/setup-buildx-action@0d103c3126aa41d772a8362f6aa67afac040f80c # v3.1.0
|
||||
if: inputs.CACHE_SUFFIX == 'arm'
|
||||
with:
|
||||
endpoint: ssh://root@arm
|
||||
|
|
@ -117,7 +117,7 @@ jobs:
|
|||
# Check OS vulnerabilities
|
||||
- name: Check OS vulnerabilities
|
||||
if: ${{ inputs.CACHE_SUFFIX != 'arm' }}
|
||||
uses: aquasecurity/trivy-action@84384bd6e777ef152729993b8145ea352e9dd3ef # v0.17.0
|
||||
uses: aquasecurity/trivy-action@062f2592684a31eb3aa050cc61e7ca1451cecd3d # v0.18.0
|
||||
with:
|
||||
vuln-type: os
|
||||
skip-dirs: /root/.cargo
|
||||
|
|
|
|||
4
.github/workflows/linux-build.yml
vendored
4
.github/workflows/linux-build.yml
vendored
|
|
@ -72,10 +72,10 @@ jobs:
|
|||
SSH_IP: ${{ secrets.ARM_SSH_IP }}
|
||||
SSH_CONFIG: ${{ secrets.ARM_SSH_CONFIG }}
|
||||
- name: Setup Buildx
|
||||
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
|
||||
uses: docker/setup-buildx-action@0d103c3126aa41d772a8362f6aa67afac040f80c # v3.1.0
|
||||
if: startsWith(env.ARCH, 'arm') == false
|
||||
- name: Setup Buildx (ARM)
|
||||
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
|
||||
uses: docker/setup-buildx-action@0d103c3126aa41d772a8362f6aa67afac040f80c # v3.1.0
|
||||
if: startsWith(env.ARCH, 'arm') == true
|
||||
with:
|
||||
endpoint: ssh://root@arm
|
||||
|
|
|
|||
2
.github/workflows/push-docker.yml
vendored
2
.github/workflows/push-docker.yml
vendored
|
|
@ -58,7 +58,7 @@ jobs:
|
|||
SSH_IP: ${{ secrets.ARM_SSH_IP }}
|
||||
SSH_CONFIG: ${{ secrets.ARM_SSH_CONFIG }}
|
||||
- name: Setup Buildx (ARM)
|
||||
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
|
||||
uses: docker/setup-buildx-action@0d103c3126aa41d772a8362f6aa67afac040f80c # v3.1.0
|
||||
with:
|
||||
endpoint: ssh://root@arm
|
||||
platforms: linux/arm64,linux/arm/v7,linux/arm/v6
|
||||
|
|
|
|||
2
.github/workflows/push-github.yml
vendored
2
.github/workflows/push-github.yml
vendored
|
|
@ -19,7 +19,7 @@ jobs:
|
|||
# Get PDF doc
|
||||
- name: Get documentation
|
||||
if: inputs.VERSION != 'testing'
|
||||
uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2
|
||||
uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
|
||||
with:
|
||||
name: BunkerWeb_documentation_v${{ inputs.VERSION }}.pdf
|
||||
# Create tag
|
||||
|
|
|
|||
6
.github/workflows/push-packagecloud.yml
vendored
6
.github/workflows/push-packagecloud.yml
vendored
|
|
@ -42,18 +42,18 @@ jobs:
|
|||
- name: Check out repository code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Install ruby
|
||||
uses: ruby/setup-ruby@22fdc77bf4148f810455b226c90fb81b5cbc00a7 # v1.171.0
|
||||
uses: ruby/setup-ruby@d4526a55538b775af234ba4af27118ed6f8f6677 # v1.172.0
|
||||
with:
|
||||
ruby-version: "3.0"
|
||||
- name: Install packagecloud
|
||||
run: gem install package_cloud
|
||||
# Download packages
|
||||
- uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2
|
||||
- uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
|
||||
if: inputs.LINUX != 'el' && inputs.LINUX != 'el9'
|
||||
with:
|
||||
name: package-${{ inputs.LINUX }}-${{ inputs.PACKAGE_ARCH }}
|
||||
path: /tmp/${{ inputs.LINUX }}
|
||||
- uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2
|
||||
- uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
|
||||
if: inputs.LINUX == 'el' || inputs.LINUX == 'el9'
|
||||
with:
|
||||
name: package-rh${{ inputs.LINUX }}-${{ inputs.PACKAGE_ARCH }}
|
||||
|
|
|
|||
2
.github/workflows/scorecards-analysis.yml
vendored
2
.github/workflows/scorecards-analysis.yml
vendored
|
|
@ -25,6 +25,6 @@ jobs:
|
|||
results_format: sarif
|
||||
publish_results: true
|
||||
- name: "Upload SARIF results to code scanning"
|
||||
uses: github/codeql-action/upload-sarif@e8893c57a1f3a2b659b6b55564fdfdbbd2982911 # v3.24.0
|
||||
uses: github/codeql-action/upload-sarif@8a470fddafa5cbb6266ee11b37ef4d8aae19c571 # v3.24.6
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
|
|
|||
3
.github/workflows/staging-create-infra.yml
vendored
3
.github/workflows/staging-create-infra.yml
vendored
|
|
@ -30,7 +30,7 @@ jobs:
|
|||
uses: azure/setup-kubectl@901a10e89ea615cf61f57ac05cecdf23e7de06d8 # v3.2
|
||||
if: inputs.TYPE == 'k8s'
|
||||
with:
|
||||
version: "v1.28.2"
|
||||
version: "v1.28.6"
|
||||
- name: Set up Python 3.12
|
||||
uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5.0.0
|
||||
if: inputs.TYPE != 'k8s'
|
||||
|
|
@ -46,7 +46,6 @@ jobs:
|
|||
- run: ./tests/create.sh ${{ inputs.TYPE }}
|
||||
env:
|
||||
CICD_SECRETS: ${{ secrets.CICD_SECRETS }}
|
||||
K8S_IP: ${{ secrets.K8S_IP }}
|
||||
- run: |
|
||||
tar -cf terraform.tar /tmp/${{ inputs.TYPE }}
|
||||
echo "$SECRET_KEY" > /tmp/.secret_key
|
||||
|
|
|
|||
2
.github/workflows/staging-delete-infra.yml
vendored
2
.github/workflows/staging-delete-infra.yml
vendored
|
|
@ -23,7 +23,7 @@ jobs:
|
|||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Install terraform
|
||||
uses: hashicorp/setup-terraform@a1502cd9e758c50496cc9ac5308c4843bcd56d36 # v3.0.0
|
||||
- uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2
|
||||
- uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
|
||||
with:
|
||||
name: tf-${{ inputs.TYPE }}
|
||||
path: /tmp
|
||||
|
|
|
|||
2
.github/workflows/staging-tests.yml
vendored
2
.github/workflows/staging-tests.yml
vendored
|
|
@ -43,7 +43,7 @@ jobs:
|
|||
if: inputs.TYPE == 'swarm'
|
||||
- name: Install test dependencies
|
||||
run: pip3 install --no-cache-dir --require-hashes --no-deps -r tests/requirements.txt
|
||||
- uses: actions/download-artifact@eaceaf801fd36c7dee90939fad912460b18a1ffe # v4.1.2
|
||||
- uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
|
||||
with:
|
||||
name: tf-k8s
|
||||
path: /tmp
|
||||
|
|
|
|||
1
.github/workflows/staging.yml
vendored
1
.github/workflows/staging.yml
vendored
|
|
@ -85,7 +85,6 @@ jobs:
|
|||
secrets:
|
||||
CICD_SECRETS: ${{ secrets.CICD_SECRETS }}
|
||||
SECRET_KEY: ${{ secrets.SECRET_KEY }}
|
||||
K8S_IP: ${{ secrets.K8S_IP }}
|
||||
prepare-tests-core:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
|
|
|||
3
.github/workflows/tests-ui-linux.yml
vendored
3
.github/workflows/tests-ui-linux.yml
vendored
|
|
@ -75,10 +75,12 @@ 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 app2.example.com" | sudo tee -a /etc/hosts
|
||||
echo "127.0.0.1 app3.example.com" | sudo tee -a /etc/hosts
|
||||
# BunkerWeb
|
||||
sudo mkdir -p /etc/bunkerweb
|
||||
echo "SERVER_NAME=" | sudo tee /etc/bunkerweb/variables.env
|
||||
echo "HTTP_PORT=80" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "BAD_BEHAVIOR_THRESHOLD=20" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo 'DNS_RESOLVERS=9.9.9.9 8.8.8.8 8.8.4.4' | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo 'API_LISTEN_IP=127.0.0.1' | sudo tee -a /etc/bunkerweb/variables.env
|
||||
echo "MULTISITE=yes" | sudo tee -a /etc/bunkerweb/variables.env
|
||||
|
|
@ -116,6 +118,7 @@ jobs:
|
|||
}' | tee plugin.json
|
||||
zip discord.zip plugin.json
|
||||
rm plugin.json
|
||||
sudo truncate -s 0 /var/log/bunkerweb/error.log
|
||||
./tests.sh "linux" ${{ inputs.TEST }}
|
||||
env:
|
||||
MODE: ${{ inputs.RELEASE }}
|
||||
|
|
|
|||
|
|
@ -1 +1,9 @@
|
|||
src/ui/templates/profile.html:hashicorp-tf-password:343
|
||||
src/ui/templates/account.html:hashicorp-tf-password:194
|
||||
src/ui/templates/account.html:hashicorp-tf-password:255
|
||||
src/ui/templates/account.html:hashicorp-tf-password:292
|
||||
src/ui/templates/account.html:hashicorp-tf-password:329
|
||||
src/ui/templates/account.html:hashicorp-tf-password:417
|
||||
src/ui/templates/account.html:hashicorp-tf-password:470
|
||||
src/ui/templates/settings_plugins.html:hashicorp-tf-password:87
|
||||
src/ui/templates/settings_plugins.html:hashicorp-tf-password:297
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ repos:
|
|||
- id: check-case-conflict
|
||||
|
||||
- repo: https://github.com/psf/black
|
||||
rev: e026c93888f91a47a9c9f4e029f3eb07d96375e6 # frozen: 24.1.1
|
||||
rev: 6fdf8a4af28071ed1d079c01122b34c5d587207a # frozen: 24.2.0
|
||||
hooks:
|
||||
- id: black
|
||||
name: Black Python Formatter
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ docs/
|
|||
env/
|
||||
*/env/
|
||||
*.min*
|
||||
src/common/core/modsecurity/
|
||||
src/common/core/modsecurity/files/
|
||||
src/deps/src/
|
||||
mkdocs.yml
|
||||
CHANGELOG.md
|
||||
|
|
@ -16,5 +16,6 @@ flatpickr.*
|
|||
src/ui/static/js/editor/*
|
||||
src/ui/static/js/utils/purify/*
|
||||
src/ui/templates/*
|
||||
src/common/core/*/ui/*
|
||||
datepicker-foundation.css
|
||||
examples/*
|
||||
|
|
|
|||
23
CHANGELOG.md
23
CHANGELOG.md
|
|
@ -5,27 +5,48 @@
|
|||
- [LINUX] Support RHEL 9.3
|
||||
- [BUGFIX] Fix issues with the antibot feature ([#866](https://github.com/bunkerity/bunkerweb/issues/866), [#870](https://github.com/bunkerity/bunkerweb/issues/870))
|
||||
- [BUGFIX] Fix Bad behavior whitelist check in access phase
|
||||
- [BUGFIX] Fix ModSecurity FP on antibot page
|
||||
- [BUGFIX] Fix Whitelist core plugin missing a check for empty server_name in multisite mode
|
||||
- [BUGFIX] Fix Templator missing some common configs
|
||||
- [LINUX] Add logrotate support for the logs
|
||||
- [UI] Add bans management page in the web UI
|
||||
- [UI] Add blocked requests page in the web UI
|
||||
- [UI] Add the possibility to clone a service in the web UI
|
||||
- [UI] Add the possibility to set a service as draft in the web UI
|
||||
- [UI] Enhance the Content-Security-Policy header in the web UI
|
||||
- [UI] Add some core plugins pages in the web UI
|
||||
- [FEATURE] Add setting REDIS_SSL_VERIFY to activate/disable the SSL certificate verification when using Redis
|
||||
- [FEATURE] Add Redis Sentinel fallback to master automatically if no slaves are available
|
||||
- [FEATURE] Add Redis Sentinel support for bwcli
|
||||
- [FEATURE] Add new Metrics core plugin that will allow metrics collection and retrieval of internal metrics
|
||||
- [FEATURE] Add setting DATABASE_LOG_LEVEL to control SQLAlchemy loggers separately from the main one
|
||||
- [FEATURE] Add whitelist check for the default-server as well
|
||||
- [FEATURE] Add the possibility to choose between the coreruleset v3 and v4 that will be used by ModSecurity (default is v3)
|
||||
- [FEATURE] Add the TIMERS_LOG_LEVEL setting to control the log level of the lua timers
|
||||
- [FEATURE] Add pro version management to core plugins, the scheduler and the web UI
|
||||
- [FEATURE] Add REVERSE_PROXY_CUSTOM_HOST setting to set a custom Host header when using reverse proxy
|
||||
- [MISC] Add a better custom certificate cache handling
|
||||
- [MISC] Updated Linux base images in Dockerfiles
|
||||
- [MISC] Add recommended dialects to databases string
|
||||
- [MISC] Refine the data sent in the anonymous reporting feature and move the setting and the job to the "jobs" plugin
|
||||
- [MISC] BunkerWeb will now load the default loading page even on 404 errors when generating the configuration
|
||||
- [MISC] Update database schema to support the new pro version and optimize it
|
||||
- [MISC] Refactor SSL/TLS logics to make it more consistent
|
||||
- [MISC] Use ed5519 key instead of RSA for default/fallback certificates
|
||||
- [MISC] Refactor certbot-new job to optimize the certbot requests
|
||||
- [DOCUMENTATION] Update web UI's setup wizard instructions in the documentation
|
||||
- [DOCUMENTATION] Update plugins documentation to reflect the new plugin system
|
||||
- [DOCUMENTATION] Update ModSecurity documentation to reflect the new changes in the Security Tuning section
|
||||
- [DOCUMENTATION] Add pro version documentation
|
||||
- [DEPS] Updated stream-lua-nginx-module to v0.0.14
|
||||
- [DEPS] Updated lua-nginx-module version to v0.10.26
|
||||
- [DEPS] Updated libmaxminddb version to v1.9.1
|
||||
- [DEPS] Updated lua-resty-core to v0.1.28
|
||||
- [DEPS] Updated zlib version to v1.3.1
|
||||
- [DEPS] Updated ModSecurity version to v3.0.12
|
||||
- [DEPS] Updated lua-resty-mlcache version to v2.6.1
|
||||
- [DEPS] Updated coreruleset version to v3.3.5
|
||||
- [DEPS] Added coreruleset version v4.0.0
|
||||
- [DEPS] Updated lua-resty-mlcache version to v2.7.0
|
||||
|
||||
## v1.5.5 - 2024/01/12
|
||||
|
||||
|
|
|
|||
|
|
@ -6,16 +6,28 @@ BunkerWeb is maintained by [Bunkerity](https://www.bunkerity.com/?utm_campaign=s
|
|||
|
||||
## Do you offer professional services ?
|
||||
|
||||
Yes, we offer professional services related to BunkerWeb such as :
|
||||
Yes, we offer professional services related to BunkerWeb.
|
||||
|
||||
**We have a [dedicated panel](https://panel.bunkerweb.io/?utm_campaign=self&utm_source=doc) to centralize all professional requests.**
|
||||
|
||||
You can also contact use at [contact@bunkerity.com](mailto:contact@bunkerity.com) if you are interested.
|
||||
|
||||
### Support
|
||||
|
||||
You can get in touch with us about any of the following :
|
||||
|
||||
- Consulting
|
||||
- Support
|
||||
- Custom development
|
||||
- Partnership
|
||||
|
||||
**We have a [dedicated panel](https://panel.bunkerweb.io/?utm_campaign=self&utm_source=doc) to centralize all professional requests.**
|
||||
### Pro version
|
||||
|
||||
You can also contact use at [contact@bunkerity.com](mailto:contact@bunkerity.com) if you are interested.
|
||||
We have a (pro version of BunkerWeb)[https://panel.bunkerweb.io/?utm_campaign=self&utm_source=doc#pro], which includes :
|
||||
|
||||
- additional features and plugins
|
||||
- regular updates to improve your experience
|
||||
- we take your feedback into account to develop the plugins or offer you custom plugins.
|
||||
|
||||
## Where to get community support ?
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 903 KiB After Width: | Height: | Size: 907 KiB |
BIN
docs/assets/img/pro-from-ui.webp
Normal file
BIN
docs/assets/img/pro-from-ui.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 87 KiB |
BIN
docs/assets/img/pro-home-card.png
Normal file
BIN
docs/assets/img/pro-home-card.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
|
|
@ -26,6 +26,10 @@ If you think that a new integration should be supported, do not hesitate to open
|
|||
|
||||
## Settings
|
||||
|
||||
!!! tip "Pro settings"
|
||||
|
||||
Some plugins are reserved for the **pro version**. [Find out more about the pro version here.](https://panel.bunkerweb.io/?utm_campaign=self&utm_source=doc#pro)
|
||||
|
||||
Once BunkerWeb is integrated into your environment, you will need to configure it to serve and protect your web applications.
|
||||
|
||||
The configuration of BunkerWeb is done by using what we call the "settings" or "variables". Each setting is identified by a name such as `AUTO_LETS_ENCRYPT` or `USE_ANTIBOT`. You can assign values to the settings to configure BunkerWeb.
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ BunkerWeb contains primary [security features](security-tuning.md) as part of th
|
|||
|
||||
- **Free as in "freedom"** : BunkerWeb is licensed under the free [AGPLv3 license](https://www.gnu.org/licenses/agpl-3.0.en.html), embracing the principles of freedom and openness. Enjoy the freedom to use, modify, and distribute the software, backed by a supportive community.
|
||||
|
||||
- **Professional services** : Get technical support, tailored consulting and custom development directly from the maintainers of BunkerWeb. Visit the [BunkerWeb Panel](https://panel.bunkerweb.io/?utm_campaign=self&utm_source=doc) for more information.
|
||||
- **Professional services** : Get technical support, tailored consulting and custom development directly from the maintainers of BunkerWeb. Visit the [BunkerWeb Panel](https://panel.bunkerweb.io/?utm_campaign=self&utm_source=doc#pro) for more information.
|
||||
|
||||
## Security features
|
||||
|
||||
|
|
@ -61,12 +61,26 @@ A demo website protected with BunkerWeb is available at [demo.bunkerweb.io](http
|
|||
|
||||
## Professional services
|
||||
|
||||
### Support panel
|
||||
|
||||
Get the most of BunkerWeb by getting professional services directly from the maintainers of the project. From technical support to tailored consulting and development, we are here to assist you in the security of your web services.
|
||||
|
||||
You will find more information by visiting the [BunkerWeb Panel](https://panel.bunkerweb.io/?utm_campaign=self&utm_source=doc), our dedicated platform for professional services.
|
||||
|
||||
Don't hesitate to [contact us](https://panel.bunkerweb.io/contact.php?utm_campaign=self&utm_source=doc) if you have any question, we will be more than happy to respond to your needs.
|
||||
|
||||
### Pro version
|
||||
|
||||
BunkerWeb has a pro version to further improve the security of your applications and make managing them faster and easier (email reporting, more settings options...).
|
||||
|
||||
We have centralised version management and support within the panel for greater ease of use.
|
||||
|
||||
More detailed information about this version can be found [by visiting our website.](https://www.bunkerweb.io/#services?utm_campaign=self&utm_source=doc)
|
||||
|
||||
If you're interested, upgrade to the pro version in just a few clicks, [following the steps here.](https://panel.bunkerweb.io/?utm_campaign=self&utm_source=doc#pro)
|
||||
|
||||
Note that in the documentation, it will be mentioned when a feature is for the pro version.
|
||||
|
||||
## Ecosystem, community and resources
|
||||
|
||||
Official websites, tools and resources about BunkerWeb :
|
||||
|
|
|
|||
|
|
@ -1,23 +1,25 @@
|
|||
{% extends "base.html" %} {% block outdated %} You're not viewing the
|
||||
documentation of the latest version.
|
||||
<a href="{{ '../' ~ base_url }}">
|
||||
<strong>Click here to view latest.</strong>
|
||||
</a>
|
||||
{% endblock %} {% block announce %} 📢 Looking for technical support, tailored
|
||||
consulting or custom development for BunkerWeb ? Visit the
|
||||
<a
|
||||
href="https://panel.bunkerweb.io/?utm_campaign=self&utm_source=doc"
|
||||
style="color: #3f6ec6; text-decoration: underline"
|
||||
>BunkerWeb Panel</a
|
||||
>
|
||||
for more information on our enterprise offers. {% endblock %} {% block libs %}
|
||||
<script
|
||||
async
|
||||
defer
|
||||
data-domain="docs.bunkerweb.io"
|
||||
src="https://data.bunkerity.com/js/script.js"
|
||||
></script>
|
||||
<script defer>
|
||||
{% extends "base.html" %}
|
||||
{% block outdated %}
|
||||
You're not viewing the
|
||||
documentation of the latest version.
|
||||
<a href="{{ '../' ~ base_url }}">
|
||||
<strong>Click here to view latest.</strong>
|
||||
</a>
|
||||
{% endblock %}
|
||||
{% block announce %}
|
||||
📢 Looking for technical support, tailored
|
||||
consulting or custom development for BunkerWeb ? Visit the
|
||||
<a href="https://panel.bunkerweb.io/?utm_campaign=self&utm_source=doc"
|
||||
style="color: #3f6ec6;
|
||||
text-decoration: underline">BunkerWeb Panel</a>
|
||||
for more information on our enterprise offers.
|
||||
{% endblock %}
|
||||
{% block libs %}
|
||||
<script async
|
||||
defer
|
||||
data-domain="docs.bunkerweb.io"
|
||||
src="https://data.bunkerity.com/js/script.js"></script>
|
||||
<script defer>
|
||||
// Lazy load images and embed youtube videos
|
||||
window.addEventListener("load", () => {
|
||||
document.querySelectorAll("[data-src]").forEach((el) => {
|
||||
|
|
@ -30,5 +32,77 @@ for more information on our enterprise offers. {% endblock %} {% block libs %}
|
|||
.querySelector('div.md-search[data-md-component="search"][role="dialog"]')
|
||||
.setAttribute("aria-label", "Search in documentation");
|
||||
} catch (err) {}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<script defer>
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
const bannerEl = document.querySelector('aside.md-banner');
|
||||
let defaultContent = [{ "content" : 'Need premium support ? <a style="text-decoration:underline; color : white;" href="https://panel.bunkerweb.io/?utm_campaign=self&utm_source=doc">Check BunkerWeb Panel</a>'},
|
||||
{ "content" : 'Try BunkerWeb on our <a style="text-decoration:underline; color : white;" href="https://demo.bunkerweb.io/link/?utm_campaign=self&utm_source=doc">demo web app !</a>'},
|
||||
{ "content" : 'All information about BunkerWeb on our <a style="text-decoration:underline; color : white;" href="https://www.bunkerweb.io/?utm_campaign=self&utm_source=doc">website !</a>'}
|
||||
]
|
||||
|
||||
function setBannerStyle() {
|
||||
const bannerItem = bannerEl.querySelector('.md-banner__inner');
|
||||
bannerEl.style.backgroundColor = "#36ce7a";
|
||||
bannerItem.style.textAlign = "center";
|
||||
bannerItem.style.transition = "all 0.5s ease-out";
|
||||
}
|
||||
|
||||
function setDefault() {
|
||||
const bannerItem = bannerEl.querySelector('.md-banner__inner');
|
||||
const clone = bannerItem.cloneNode(true);
|
||||
clone.innerHTML = defaultContent[defaultContent.length - 1]["content"];
|
||||
bannerEl.replaceChild(clone, bannerItem);
|
||||
}
|
||||
|
||||
function startBannerLoop() {
|
||||
const switchDelay = 7000;
|
||||
let i = 0;
|
||||
|
||||
setInterval(() => {
|
||||
// Update or reset index
|
||||
if(i + 1 === defaultContent.length - 1) {
|
||||
i = 0;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
|
||||
// Prepare data with next el with opacity 0 too
|
||||
const currItem = bannerEl.querySelector('.md-banner__inner');
|
||||
currItem.style.opacity = 0;
|
||||
const clone = currItem.cloneNode(true);
|
||||
clone.innerHTML = defaultContent[i]["content"];
|
||||
setTimeout(() => {
|
||||
bannerEl.replaceChild(clone, currItem);
|
||||
setTimeout(() => {
|
||||
const newItem = bannerEl.querySelector('.md-banner__inner');
|
||||
newItem.style.opacity = 1;
|
||||
}, 20);
|
||||
}, 600);
|
||||
|
||||
}, switchDelay);
|
||||
}
|
||||
|
||||
function runBanner() {
|
||||
// Try to get dynamic content
|
||||
// Else keep static ones
|
||||
fetch("https://www.bunkerweb.io/api/bw-ui-news")
|
||||
.then((res) => {
|
||||
return res.json();
|
||||
})
|
||||
.then((res) => {
|
||||
defaultContent = res.data;
|
||||
startBannerLoop();
|
||||
})
|
||||
.catch((e) => { startBannerLoop();
|
||||
});
|
||||
}
|
||||
|
||||
setBannerStyle();
|
||||
setDefault();
|
||||
runBanner()
|
||||
|
||||
})
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
|||
174
docs/plugins.md
174
docs/plugins.md
|
|
@ -45,11 +45,44 @@ The first step is to install the plugin by putting the plugin files inside the c
|
|||
cp -rp ./bunkerweb-plugins/* ./bw-data/plugins
|
||||
```
|
||||
|
||||
Because the scheduler runs as an unprivileged user with UID and GID 101, you will need to edit the permissions :
|
||||
!!! warning "Using local folder for persistent data"
|
||||
The scheduler runs as an **unprivileged user with UID 101 and GID 101** inside the container. The reason behind this is security : in case a vulnerability is exploited, the attacker won't have full root (UID/GID 0) privileges.
|
||||
But there is a downside : if you use a **local folder for the persistent data**, you will need to **set the correct permissions** so the unprivileged user can write data to it. Something like that should do the trick :
|
||||
|
||||
```shell
|
||||
chown -R 101:101 ./bw-data
|
||||
```
|
||||
```shell
|
||||
mkdir bw-data && \
|
||||
chown root:101 bw-data && \
|
||||
chmod 770 bw-data
|
||||
```
|
||||
|
||||
Alternatively, if the folder already exists :
|
||||
|
||||
```shell
|
||||
chown -R root:101 bw-data && \
|
||||
chmod -R 770 bw-data
|
||||
```
|
||||
|
||||
If you are using [Docker in rootless mode](https://docs.docker.com/engine/security/rootless) or [podman](https://podman.io/), UIDs and GIDs in the container will be mapped to different ones in the host. You will first need to check your initial subuid and subgid :
|
||||
|
||||
```shell
|
||||
grep ^$(whoami): /etc/subuid && \
|
||||
grep ^$(whoami): /etc/subgid
|
||||
```
|
||||
|
||||
For example, if you have a value of **100000**, the mapped UID/GID will be **100100** (100000 + 100) :
|
||||
|
||||
```shell
|
||||
mkdir bw-data && \
|
||||
sudo chgrp 100100 bw-data && \
|
||||
chmod 770 bw-data
|
||||
```
|
||||
|
||||
Or if the folder already exists :
|
||||
|
||||
```shell
|
||||
sudo chgrp -R 100100 bw-data && \
|
||||
chmod -R 770 bw-data
|
||||
```
|
||||
|
||||
Then you can mount the volume when starting your Docker stack :
|
||||
|
||||
|
|
@ -276,7 +309,7 @@ plugin /
|
|||
plugin.json
|
||||
```
|
||||
|
||||
- **conf_type.conf** : add a [custom NGINX configurations.](/quickstart-guide/#custom-configurations)
|
||||
- **conf_type.conf** : add a [custom NGINX configurations.](quickstart-guide.md#custom-configurations)
|
||||
|
||||
- **actions.py** : script to execute on flask server.
|
||||
This script is running on flask context, you have access to lib and utils like `jinja2`, `requests`, etc...
|
||||
|
|
@ -544,59 +577,97 @@ BunkerWeb uses an internal job scheduler for periodic tasks like renewing certif
|
|||
|
||||
### Plugin page
|
||||
|
||||
Everything related to the web UI is located inside the subfolder **ui** as we seen in the [previous structure section.](#structure)
|
||||
Everything related to the web UI is located inside the subfolder **ui** as we seen in the [previous structure section.](#structure).
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
When you want to create a plugin page, you need two files :
|
||||
|
||||
- **template.html** that will be accessible with a **GET /plugins/<*plugin_id*>**.
|
||||
|
||||
- **actions.py** where you can add some scripting and logic with a **POST /plugins/<*plugin_id*>**. Notice that this file **need a function with the same name as the plugin** to work. This file is needed even if the function is empty.
|
||||
|
||||
#### Basic example
|
||||
|
||||
!!! info "Jinja 2 template"
|
||||
The **template.html** file is a Jinja2 template, please refer to the [Jinja2 documentation](https://jinja.palletsprojects.com) if needed.
|
||||
|
||||
A plugin page can have a form that is used to submit data to the plugin. To get the values of the form, you need to put a **actions.py** file in the **ui** folder. Inside the file, **you must define a function that has the same name as the plugin**. This function will be called when the form is submitted. You can then use the **request** object (from the [Flask library](https://flask.palletsprojects.com)) to get the values of the form. The form's action must finish with **/plugins/<*plugin_id*>**. The helper function `url_for` will generate for you the prefix of the URL : `{{ url_for('plugins') }}/plugin_id`.
|
||||
We can put aside the **actions.py** file and start **only using the template on a GET situation**. The template can access app context and libs, so you can use Jinja, request or flask utils.
|
||||
|
||||
If you want to display variables generated from your **actions.py** in your template file, you can return a dictionary with variables name as keys and variables value as values. Here is dummy example where we return a single variable :
|
||||
For example, you can get the request arguments in your template like this :
|
||||
|
||||
```python
|
||||
def myplugin() :
|
||||
return {"foo": "bar"}
|
||||
```
|
||||
|
||||
And we display it in the **template.html** file :
|
||||
```html
|
||||
{% if foo %}
|
||||
Content of foo is : {{ foo }}.
|
||||
{% endif %}
|
||||
<p>request args : {{ request.args.get() }}.</p>
|
||||
```
|
||||
|
||||
Please note that every form submission is protected via a CSRF token, you will need to include the following snippet into your forms :
|
||||
```html
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
```
|
||||
|
||||
Retrieving user submitted data is pretty simple, thanks to the request module provided by Flask :
|
||||
|
||||
```python
|
||||
from flask import request
|
||||
|
||||
def myplugin() :
|
||||
my_form_value = request.form["my_form_input"]
|
||||
```
|
||||
#### Actions.py
|
||||
|
||||
!!! info "Python libraries"
|
||||
You can use Python libraries that are already available like :
|
||||
`Flask`, `Flask-Login`, `Flask-WTF`, `beautifulsoup4`, `docker`, `Jinja2`, `python-magic` and `requests`. To see the full list, you can have a look at the Web UI [requirements.txt](https://github.com/bunkerity/bunkerweb/blobsrc/ui/requirements.txt). If you need external libraries, you can install them inside the **ui** folder of your plugin and then use the classical **import** directive.
|
||||
|
||||
#### Utils
|
||||
!!! warning "CSRF Token"
|
||||
|
||||
To easily update the content of a template inside the UI with JSON, a **SetupPlugin class** is available in `src/ui/static/js/plugins/setup.js` and will be executed when the plugin template is load.
|
||||
Please note that every form submission is protected via a CSRF token, you will need to include the following snippet into your forms :
|
||||
```html
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
```
|
||||
|
||||
|
||||
You can power-up your plugin page with additional scripting with the **actions.py** file when sending a **POST /plugins/<*plugin_id*>**.
|
||||
|
||||
Here is what is send to the function :
|
||||
|
||||
For example, in case **actions.py** return this JSON :
|
||||
```python
|
||||
def plugin():
|
||||
return {"message": "ok", "data": {"name": "test"}}
|
||||
function(app=app, args=request.args.to_dict() or request.json or None)
|
||||
```
|
||||
|
||||
I need to add this on my **template.html** :
|
||||
Some examples of what you can do :
|
||||
|
||||
- Retrieve form submitted data
|
||||
|
||||
```python
|
||||
from flask import request
|
||||
|
||||
def myplugin(**kwargs) :
|
||||
my_form_value = request.form["my_form_input"]
|
||||
```
|
||||
|
||||
- Access app methods
|
||||
|
||||
```python
|
||||
from flask import request
|
||||
|
||||
def myplugin(**kwargs) :
|
||||
config = kwargs['app'].config["CONFIG"].get_config(methods=False)
|
||||
```
|
||||
|
||||
**You need to retrieve JSON compatible data from this function**, app will return this as response :
|
||||
|
||||
```python
|
||||
return jsonify({"message": "ok", "data": <function_output>}), 200
|
||||
```
|
||||
|
||||
#### Updating template
|
||||
|
||||
To easily update the content of a template inside the UI with JSON, a **SetupPlugin class** is available in `src/ui/static/js/plugins/setup.js`.
|
||||
|
||||
!!! info "Check core plugins"
|
||||
|
||||
Core plugins are using this class. Feel free to look at them in order to see in details how it works.
|
||||
|
||||
For example, in case **actions.py** return this :
|
||||
|
||||
```python
|
||||
def myplugin(**kwargs):
|
||||
return {"name": "My awesome plugin"}
|
||||
```
|
||||
|
||||
I can add this on my **template.html** :
|
||||
|
||||
```html
|
||||
<p data-name></p>
|
||||
|
||||
<script>
|
||||
new SetupPlugin({
|
||||
name: {
|
||||
|
|
@ -608,16 +679,25 @@ I need to add this on my **template.html** :
|
|||
</script>
|
||||
```
|
||||
|
||||
!!! info "Check core plugins"
|
||||
|
||||
Core plugins are using this utils. Feel free to look at them in order to see in details how each `key` is working.
|
||||
**This class will send a POST request, and will try to match the dict key to a JSON key and update your template**.
|
||||
|
||||
|
||||
| key | Type | Description |
|
||||
| :--------: | :----: | :------------------------------------------------------------------------------------------- |
|
||||
| `name ` | string | Replace `name` by the JSON key to extract the related value. |
|
||||
| `el` | element| Select element you want the value to be updated. |
|
||||
| `value` | any | Default value on template load or in case retrieving JSON failed. |
|
||||
| `type` | string | Define the script behavior with the incoming value. Available : `text`, `list` and `status`. |
|
||||
| `textEl` | element| Optional additional text content when type is `status`. |
|
||||
| `listNames`| string | List of data keys when type is `list`. |
|
||||
Here it will look for a `name` key in the JSON response, and will set the `data` on the defined `el`.
|
||||
In case there is no `data` matching, this will keep or set the `value` key data.
|
||||
|
||||
This class has two arguments `SetupPlugin(setup, url)` :
|
||||
|
||||
- `url`(optional) : current endpoint by default. You can define another url or add arguments.
|
||||
|
||||
- `setup` : a dict of dict with needed data to update properly the template with the incoming data.
|
||||
|
||||
**setup details**
|
||||
|
||||
| key | Type | Description |
|
||||
| :--------: | :--------: | :------------------------------------------------------------------------------------------- |
|
||||
| `dict name` | string | Replace `dict name` by the JSON key to extract the related value. |
|
||||
| `el` | DOM element| Select element you want the value to be updated. |
|
||||
| `value` | any | Default value on template load or in case retrieving JSON failed. |
|
||||
| `type` | string | Define the script behavior with the incoming value. Available : `text`, `list` and `status`. |
|
||||
| `textEl` | DOM element| Optional additional text content when type is `status`. |
|
||||
| `listNames`| string | List of data keys when type is `list`. |
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ Please note that professionnal services are directly offered by [Bunkerity](http
|
|||
|
||||
## Which professional services do you offer ?
|
||||
|
||||
### Support
|
||||
|
||||
We offer technical support around the BunkerWeb solution. By using this service, we will assist you on the technical issues (installation, configuration, false positive, ...).
|
||||
|
||||
According to your needs you have the choice between "one time" and subscriptions offers.
|
||||
|
|
@ -23,6 +25,20 @@ In addition to the support service, we also offer custom services around the Bun
|
|||
- Consulting : a dedicated expert will give you advices on your project
|
||||
- Development : if you need specific features in BunkerWeb, we can do it for you
|
||||
|
||||
[You can check for technical support here.](https://panel.bunkerweb.io/contact.php?utm_campaign=self&utm_source=doc)
|
||||
|
||||
### Pro version
|
||||
|
||||
A BunkerWeb pro version is available.
|
||||
|
||||
With this version you'll get a power-up version with more plugins and details settings.
|
||||
|
||||
Any features from pro version are up to date, and we have an active listening on your feedbacks in order to enhance them and fit your needs.
|
||||
|
||||
Switching to the pro version is done centrally via the panel, which is also used for support.
|
||||
|
||||
If you are interested, please [visit the dedicated page.](https://panel.bunkerweb.io/?utm_campaign=self&utm_source=doc#pro)
|
||||
|
||||
## How can I get more information ?
|
||||
|
||||
You will find more information by visiting the [BunkerWeb Panel](https://panel.bunkerweb.io/?utm_campaign=self&utm_source=doc), our dedicated platform for professional services.
|
||||
|
|
|
|||
|
|
@ -2426,3 +2426,17 @@ By default, BunkerWeb will only listen on IPv4 addresses and won't use IPv6 for
|
|||
|
||||
...
|
||||
```
|
||||
|
||||
## And now ?
|
||||
|
||||
This quickstart is just a tiny fraction of the functionality that BunkerWeb has to offer. Be curious and see what else BunkerWeb has to offer.
|
||||
|
||||
You can take a detailed look at the many [plugins and settings available](settings.md) in this solution.
|
||||
|
||||
Details on the use of certain plugins can also be found on the [security tuning page.](security-tuning.md)
|
||||
|
||||
For simplified use of the solution, don't hesitate to [try out the web UI.](web-ui.md)
|
||||
|
||||
Please note that **BunkerWeb comes in both free and pro versions**, with pro features indicated to avoid confusion.
|
||||
|
||||
If you'd like to find out more about the pro version, and use the solution to the full, [visit the panel.](https://panel.bunkerweb.io/?utm_campaign=self&utm_source=doc#pro)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
mike==2.0.0
|
||||
mkdocs==1.5.3
|
||||
mkdocs-material[imaging]==9.5.9
|
||||
mkdocs-material[imaging]==9.5.11
|
||||
mkdocs-print-site-plugin==2.3.6
|
||||
pytablewriter==1.2.0
|
||||
|
|
|
|||
|
|
@ -207,9 +207,9 @@ importlib-metadata==7.0.1 \
|
|||
# markdown
|
||||
# mike
|
||||
# mkdocs
|
||||
importlib-resources==6.1.1 \
|
||||
--hash=sha256:3893a00122eafde6894c59914446a512f728a0c1a45f9bb9b63721b6bacf0b4a \
|
||||
--hash=sha256:e8bf90d8213b486f428c9c39714b920041cb02c184686a3dee24905aaa8105d6
|
||||
importlib-resources==6.1.2 \
|
||||
--hash=sha256:308abf8474e2dba5f867d279237cd4076482c3de7104a40b41426370e891549b \
|
||||
--hash=sha256:9a0a862501dc38b68adebc82970140c9e4209fc99601782925178f8386339938
|
||||
# via mike
|
||||
jinja2==3.1.3 \
|
||||
--hash=sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa \
|
||||
|
|
@ -311,12 +311,11 @@ mkdocs==1.5.3 \
|
|||
# -r requirements.in
|
||||
# mike
|
||||
# mkdocs-material
|
||||
mkdocs-material==9.5.9 \
|
||||
--hash=sha256:635df543c01c25c412d6c22991872267723737d5a2f062490f33b2da1c013c6d \
|
||||
--hash=sha256:a5d62b73b3b74349e45472bfadc129c871dd2d4add68d84819580597b2f50d5d
|
||||
mkdocs-material==9.5.11 \
|
||||
--hash=sha256:788ee0f3e036dca2dc20298d65e480297d348a44c9d7b2ee05c5262983e66072 \
|
||||
--hash=sha256:7af7f8af0dea16175558f3fb9245d26c83a17199baa5f157755e63d7437bf971
|
||||
# via
|
||||
# -r requirements.in
|
||||
# mkdocs-material
|
||||
# mkdocs-print-site-plugin
|
||||
mkdocs-material-extensions==1.3.1 \
|
||||
--hash=sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443 \
|
||||
|
|
@ -613,9 +612,9 @@ requests==2.31.0 \
|
|||
# importlib-resources
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
setuptools==69.1.0 \
|
||||
--hash=sha256:850894c4195f09c4ed30dba56213bf7c3f21d86ed6bdaafb5df5972593bfc401 \
|
||||
--hash=sha256:c054629b81b946d63a9c6e732bc8b2513a7c3ea645f11d0139a2191d735c60c6
|
||||
setuptools==69.1.1 \
|
||||
--hash=sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56 \
|
||||
--hash=sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8
|
||||
# via mkdocs-material
|
||||
six==1.16.0 \
|
||||
--hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
|
||||
|
|
@ -642,10 +641,9 @@ typepy==1.3.2 \
|
|||
# dataproperty
|
||||
# pytablewriter
|
||||
# tabledata
|
||||
# typepy
|
||||
urllib3==2.2.0 \
|
||||
--hash=sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20 \
|
||||
--hash=sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224
|
||||
urllib3==2.2.1 \
|
||||
--hash=sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d \
|
||||
--hash=sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19
|
||||
# via requests
|
||||
verspec==0.1.0 \
|
||||
--hash=sha256:741877d5633cc9464c45a469ae2a31e801e6dbbaa85b9675d481cda100f11c31 \
|
||||
|
|
|
|||
|
|
@ -106,16 +106,16 @@ STREAM support :x:
|
|||
|
||||
[Cross-Origin Resource Sharing](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) lets you manage how your service can be contacted from different origins. Please note that you will have to allow the `OPTIONS` HTTP method using the `ALLOWED_METHODS` if you want to enable it (more info [here](#allowed-methods)). Here is the list of settings related to CORS :
|
||||
|
||||
| Setting | Default | Context |Multiple| Description |
|
||||
|------------------------|------------------------------------------------------------------------------------|---------|--------|-------------------------------------------------------------------|
|
||||
|`USE_CORS` |`no` |multisite|no |Use CORS |
|
||||
|`CORS_ALLOW_ORIGIN` |`*` |multisite|no |Allowed origins to make CORS requests : PCRE regex or *. |
|
||||
|`CORS_EXPOSE_HEADERS` |`Content-Length,Content-Range` |multisite|no |Value of the Access-Control-Expose-Headers header. |
|
||||
|`CORS_MAX_AGE` |`86400` |multisite|no |Value of the Access-Control-Max-Age header. |
|
||||
|`CORS_ALLOW_CREDENTIALS`|`no` |multisite|no |Send the Access-Control-Allow-Credentials header. |
|
||||
|`CORS_ALLOW_METHODS` |`GET, POST, OPTIONS` |multisite|no |Value of the Access-Control-Allow-Methods header. |
|
||||
|`CORS_ALLOW_HEADERS` |`DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range`|multisite|no |Value of the Access-Control-Allow-Headers header. |
|
||||
|`CORS_DENY_REQUEST` |`yes` |multisite|no |Deny request and don't send it to backend if Origin is not allowed.|
|
||||
| Setting | Default | Context | Multiple | Description |
|
||||
| ------------------------ | ------------------------------------------------------------------------------------ | --------- | -------- | ------------------------------------------------------------------- |
|
||||
| `USE_CORS` | `no` | multisite | no | Use CORS |
|
||||
| `CORS_ALLOW_ORIGIN` | `*` | multisite | no | Allowed origins to make CORS requests : PCRE regex or *. |
|
||||
| `CORS_EXPOSE_HEADERS` | `Content-Length,Content-Range` | multisite | no | Value of the Access-Control-Expose-Headers header. |
|
||||
| `CORS_MAX_AGE` | `86400` | multisite | no | Value of the Access-Control-Max-Age header. |
|
||||
| `CORS_ALLOW_CREDENTIALS` | `no` | multisite | no | Send the Access-Control-Allow-Credentials header. |
|
||||
| `CORS_ALLOW_METHODS` | `GET, POST, OPTIONS` | multisite | no | Value of the Access-Control-Allow-Methods header. |
|
||||
| `CORS_ALLOW_HEADERS` | `DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range` | multisite | no | Value of the Access-Control-Allow-Headers header. |
|
||||
| `CORS_DENY_REQUEST` | `yes` | multisite | no | Deny request and don't send it to backend if Origin is not allowed. |
|
||||
|
||||
Here is some examples of possible values for `CORS_ALLOW_ORIGIN` setting :
|
||||
|
||||
|
|
@ -133,7 +133,7 @@ Besides the HTTPS / SSL/TLS configuration, the following settings related to HTT
|
|||
| :---------------------------: | :---------------: | :----------------------------------------------------------------------------------------------------------- |
|
||||
| `REDIRECT_HTTP_TO_HTTPS` | `no` | When set to `yes`, will redirect every HTTP request to HTTPS even if BunkerWeb is not configured with HTTPS. |
|
||||
| `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. |
|
||||
| `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. |
|
||||
| `LISTEN_HTTP` | `yes` | When set to `no`, BunkerWeb will not listen for HTTP requests. Useful if you want HTTPS only for example. |
|
||||
|
||||
|
|
@ -147,7 +147,7 @@ Here is the list of related settings :
|
|||
|
||||
| Setting | Default | Description |
|
||||
| :------------------------: | :----------------------: | :----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `AUTO_LETS_ENCRYPT` | `no` | When set to `yes`, HTTPS / SSL/TLS will be enabled with automatic certificate generation and renewal from Let's Encrypt. |
|
||||
| `AUTO_LETS_ENCRYPT` | `no` | When set to `yes`, HTTPS / SSL/TLS will be enabled with automatic certificate generation and renewal from Let's Encrypt. |
|
||||
| `EMAIL_LETS_ENCRYPT` | `contact@{FIRST_SERVER}` | Email to use when generating certificates. Let's Encrypt will send notifications to that email like certificate expiration. |
|
||||
| `USE_LETS_ENCRYPT_STAGING` | `no` | When set to `yes`, the staging server of Let's Encrypt will be used instead of the production one. Useful when doing tests to avoid being "blocked" due to limits. |
|
||||
|
||||
|
|
@ -159,11 +159,11 @@ STREAM support :white_check_mark:
|
|||
|
||||
If you want to use your own certificates, here is the list of related settings :
|
||||
|
||||
| Setting |Default| Context |Multiple| Description |
|
||||
|-----------------|-------|---------|--------|--------------------------------------------------------------------------------|
|
||||
|`USE_CUSTOM_SSL` |`no` |multisite|no |Use custom HTTPS / SSL/TLS certificate. |
|
||||
|`CUSTOM_SSL_CERT`| |multisite|no |Full path of the certificate or bundle file (must be readable by the scheduler).|
|
||||
|`CUSTOM_SSL_KEY` | |multisite|no |Full path of the key file (must be readable by the scheduler). |
|
||||
| Setting | Default | Context | Multiple | Description |
|
||||
| ----------------- | ------- | --------- | -------- | -------------------------------------------------------------------------------- |
|
||||
| `USE_CUSTOM_SSL` | `no` | multisite | no | Use custom HTTPS / SSL/TLS certificate. |
|
||||
| `CUSTOM_SSL_CERT` | | multisite | no | Full path of the certificate or bundle file (must be readable by the scheduler). |
|
||||
| `CUSTOM_SSL_KEY` | | multisite | no | Full path of the key file (must be readable by the scheduler). |
|
||||
|
||||
|
||||
When `USE_CUSTOM_SSL` is set to `yes`, BunkerWeb will check every day if the custom certificate specified in `CUSTOM_SSL_CERT` is modified and will reload NGINX if that's the case.
|
||||
|
|
@ -176,11 +176,11 @@ STREAM support :white_check_mark:
|
|||
|
||||
If you want to quickly test HTTPS / SSL/TLS for staging/dev environment you can configure BunkerWeb to generate self-signed certificates, here is the list of related settings :
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :------------------------: | :--------------------: | :------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Setting | Default | Description |
|
||||
| :------------------------: | :--------------------: | :----------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `GENERATE_SELF_SIGNED_SSL` | `no` | When set to `yes`, HTTPS / SSL/TLS will be enabled with automatic self-signed certificate generation and renewal from Let's Encrypt. |
|
||||
| `SELF_SIGNED_SSL_EXPIRY` | `365` | Number of days for the certificate expiration (**-days** value used with **openssl**). |
|
||||
| `SELF_SIGNED_SSL_SUBJ` | `/CN=www.example.com/` | Certificate subject to use (**-subj** value used with **openssl**). |
|
||||
| `SELF_SIGNED_SSL_EXPIRY` | `365` | Number of days for the certificate expiration (**-days** value used with **openssl**). |
|
||||
| `SELF_SIGNED_SSL_SUBJ` | `/CN=www.example.com/` | Certificate subject to use (**-subj** value used with **openssl**). |
|
||||
|
||||
When using stream mode, you will need to use the `LISTEN_STREAM_PORT_SSL` setting in order to choose your listening SSL/TLS port.
|
||||
|
||||
|
|
@ -190,12 +190,21 @@ STREAM support :x:
|
|||
|
||||
ModSecurity is integrated and enabled by default alongside the OWASP Core Rule Set within BunkerWeb. Here is the list of related settings :
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :-------------------: | :-----: | :---------------------------------------------------------------------------------------------------- |
|
||||
| `USE_MODSECURITY` | `yes` | When set to `yes`, ModSecurity will be enabled. |
|
||||
| `USE_MODSECURITY_CRS` | `yes` | When set to `yes` and `USE_MODSECURITY` is also set to `yes`, the OWASP Core Rule Set will be loaded. |
|
||||
| Setting | Default | Description |
|
||||
| :-----------------------: | :-----: | :---------------------------------------------------------------------------------------------------- |
|
||||
| `USE_MODSECURITY` | `yes` | When set to `yes`, ModSecurity will be enabled. |
|
||||
| `USE_MODSECURITY_CRS` | `yes` | When set to `yes` and `USE_MODSECURITY` is also set to `yes`, the OWASP Core Rule Set will be loaded. |
|
||||
| `MODSECURITY_CRS_VERSION` | `3` | Version of the OWASP Core Rule Set to use. |
|
||||
|
||||
We strongly recommend keeping both ModSecurity and the OWASP Core Rule Set enabled. The only downsides are the false positives that may occur. But they can be fixed with some efforts and the CRS team maintains a list of exclusions for common applications (e.g., WordPress, Nextcloud, Drupal, Cpanel, ...).
|
||||
!!! warning "ModSecurity and the OWASP Core Rule Set"
|
||||
**We strongly recommend keeping both ModSecurity and the OWASP Core Rule Set enabled**. The only downsides are the false positives that may occur. But they can be fixed with some efforts and the CRS team maintains a list of exclusions for common applications (e.g., WordPress, Nextcloud, Drupal, Cpanel, ...).
|
||||
|
||||
You can choose between the following versions of the OWASP Core Rule Set :
|
||||
|
||||
- **3** : The version [v3.3.5](https://github.com/coreruleset/coreruleset/releases/tag/v3.3.5) of the OWASP Core Rule Set (***default***)
|
||||
- **4** : The version [v4.0.0](https://github.com/coreruleset/coreruleset/releases/tag/v4.0.0) of the OWASP Core Rule Set
|
||||
|
||||
### Custom configurations
|
||||
|
||||
Tuning ModSecurity and the CRS can be done using [custom configurations](quickstart-guide.md#custom-configurations) :
|
||||
|
||||
|
|
@ -236,7 +245,7 @@ That kind of security measure is implemented and enabled by default in BunkerWeb
|
|||
| `BAD_BEHAVIOR_STATUS_CODES` | `400 401 403 404 405 429 444` | List of HTTP status codes considered as "suspicious". |
|
||||
| `BAD_BEHAVIOR_BAN_TIME` | `86400` | The duration time (in seconds) of a ban when a client reached the threshold. |
|
||||
| `BAD_BEHAVIOR_THRESHOLD` | `10` | Maximum number of "suspicious" HTTP status codes within the time period. |
|
||||
| `BAD_BEHAVIOR_COUNT_TIME` | `60` | Period of time during which we count "suspicious" HTTP status codes. |
|
||||
| `BAD_BEHAVIOR_COUNT_TIME` | `60` | Period of time during which we count "suspicious" HTTP status codes. |
|
||||
|
||||
In other words, with the default values, if a client generates more than `10` status codes from the list `400 401 403 404 405 429 444` within `60` seconds their IP address will be banned for `86400` seconds.
|
||||
|
||||
|
|
@ -259,19 +268,19 @@ That kind of security is implemented but not enabled by default in BunkerWeb and
|
|||
|
||||
Here is the list of related settings :
|
||||
|
||||
| Setting | Default | Context |Multiple| Description |
|
||||
|---------------------------|------------|---------|--------|------------------------------------------------------------------------------------------------------------------------------|
|
||||
|`USE_ANTIBOT` |`no` |multisite|no |Activate antibot feature. |
|
||||
|`ANTIBOT_URI` |`/challenge`|multisite|no |Unused URI that clients will be redirected to to solve the challenge. |
|
||||
|`ANTIBOT_RECAPTCHA_SCORE` |`0.7` |multisite|no |Minimum score required for reCAPTCHA challenge. |
|
||||
|`ANTIBOT_RECAPTCHA_SITEKEY`| |multisite|no |Sitekey for reCAPTCHA challenge. |
|
||||
|`ANTIBOT_RECAPTCHA_SECRET` | |multisite|no |Secret for reCAPTCHA challenge. |
|
||||
|`ANTIBOT_HCAPTCHA_SITEKEY` | |multisite|no |Sitekey for hCaptcha challenge. |
|
||||
|`ANTIBOT_HCAPTCHA_SECRET` | |multisite|no |Secret for hCaptcha challenge. |
|
||||
|`ANTIBOT_TURNSTILE_SITEKEY`| |multisite|no |Sitekey for Turnstile challenge. |
|
||||
|`ANTIBOT_TURNSTILE_SECRET` | |multisite|no |Secret for Turnstile challenge. |
|
||||
|`ANTIBOT_TIME_RESOLVE` |`60` |multisite|no |Maximum time (in seconds) clients have to resolve the challenge. Once this time has passed, a new challenge will be generated.|
|
||||
|`ANTIBOT_TIME_VALID` |`86400` |multisite|no |Maximum validity time of solved challenges. Once this time has passed, clients will need to resolve a new one. |
|
||||
| Setting | Default | Context | Multiple | Description |
|
||||
| --------------------------- | ------------ | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `USE_ANTIBOT` | `no` | multisite | no | Activate antibot feature. |
|
||||
| `ANTIBOT_URI` | `/challenge` | multisite | no | Unused URI that clients will be redirected to to solve the challenge. |
|
||||
| `ANTIBOT_RECAPTCHA_SCORE` | `0.7` | multisite | no | Minimum score required for reCAPTCHA challenge. |
|
||||
| `ANTIBOT_RECAPTCHA_SITEKEY` | | multisite | no | Sitekey for reCAPTCHA challenge. |
|
||||
| `ANTIBOT_RECAPTCHA_SECRET` | | multisite | no | Secret for reCAPTCHA challenge. |
|
||||
| `ANTIBOT_HCAPTCHA_SITEKEY` | | multisite | no | Sitekey for hCaptcha challenge. |
|
||||
| `ANTIBOT_HCAPTCHA_SECRET` | | multisite | no | Secret for hCaptcha challenge. |
|
||||
| `ANTIBOT_TURNSTILE_SITEKEY` | | multisite | no | Sitekey for Turnstile challenge. |
|
||||
| `ANTIBOT_TURNSTILE_SECRET` | | multisite | no | Secret for Turnstile challenge. |
|
||||
| `ANTIBOT_TIME_RESOLVE` | `60` | multisite | no | Maximum time (in seconds) clients have to resolve the challenge. Once this time has passed, a new challenge will be generated. |
|
||||
| `ANTIBOT_TIME_VALID` | `86400` | multisite | no | Maximum validity time of solved challenges. Once this time has passed, clients will need to resolve a new one. |
|
||||
|
||||
Please note that antibot feature is using a cookie to maintain a session with clients. If you are using BunkerWeb in a clustered environment, you will need to set the `SESSIONS_SECRET` and `SESSIONS_NAME` settings to another value than the default one (which is `random`). You will find more info about sessions [here](settings.md#sessions).
|
||||
|
||||
|
|
@ -287,30 +296,30 @@ STREAM support :warning:
|
|||
|
||||
You can use the following settings to set up blacklisting :
|
||||
|
||||
| Setting | Default | Context |Multiple| Description |
|
||||
|----------------------------------|------------------------------------------------------------------------------------------------------------------------------|---------|--------|------------------------------------------------------------------------------------------------|
|
||||
|`USE_BLACKLIST` |`yes` |multisite|no |Activate blacklist feature. |
|
||||
|`BLACKLIST_IP` | |multisite|no |List of IP/network, separated with spaces, to block. |
|
||||
|`BLACKLIST_IP_URLS` |`https://www.dan.me.uk/torlist/?exit` |global |no |List of URLs, separated with spaces, containing bad IP/network to block. |
|
||||
|`BLACKLIST_RDNS_GLOBAL` |`yes` |multisite|no |Only perform RDNS blacklist checks on global IP addresses. |
|
||||
|`BLACKLIST_RDNS` |`.shodan.io .censys.io` |multisite|no |List of reverse DNS suffixes, separated with spaces, to block. |
|
||||
|`BLACKLIST_RDNS_URLS` | |global |no |List of URLs, separated with spaces, containing reverse DNS suffixes to block. |
|
||||
|`BLACKLIST_ASN` | |multisite|no |List of ASN numbers, separated with spaces, to block. |
|
||||
|`BLACKLIST_ASN_URLS` | |global |no |List of URLs, separated with spaces, containing ASN to block. |
|
||||
|`BLACKLIST_USER_AGENT` | |multisite|no |List of User-Agent (PCRE regex), separated with spaces, to block. |
|
||||
|`BLACKLIST_USER_AGENT_URLS` |`https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/_generator_lists/bad-user-agents.list`|global |no |List of URLs, separated with spaces, containing bad User-Agent to block. |
|
||||
|`BLACKLIST_URI` | |multisite|no |List of URI (PCRE regex), separated with spaces, to block. |
|
||||
|`BLACKLIST_URI_URLS` | |global |no |List of URLs, separated with spaces, containing bad URI to block. |
|
||||
|`BLACKLIST_IGNORE_IP` | |multisite|no |List of IP/network, separated with spaces, to ignore in the blacklist. |
|
||||
|`BLACKLIST_IGNORE_IP_URLS` | |global |no |List of URLs, separated with spaces, containing IP/network to ignore in the blacklist. |
|
||||
|`BLACKLIST_IGNORE_RDNS` | |multisite|no |List of reverse DNS suffixes, separated with spaces, to ignore in the blacklist. |
|
||||
|`BLACKLIST_IGNORE_RDNS_URLS` | |global |no |List of URLs, separated with spaces, containing reverse DNS suffixes to ignore in the blacklist.|
|
||||
|`BLACKLIST_IGNORE_ASN` | |multisite|no |List of ASN numbers, separated with spaces, to ignore in the blacklist. |
|
||||
|`BLACKLIST_IGNORE_ASN_URLS` | |global |no |List of URLs, separated with spaces, containing ASN to ignore in the blacklist. |
|
||||
|`BLACKLIST_IGNORE_USER_AGENT` | |multisite|no |List of User-Agent (PCRE regex), separated with spaces, to ignore in the blacklist. |
|
||||
|`BLACKLIST_IGNORE_USER_AGENT_URLS`| |global |no |List of URLs, separated with spaces, containing User-Agent to ignore in the blacklist. |
|
||||
|`BLACKLIST_IGNORE_URI` | |multisite|no |List of URI (PCRE regex), separated with spaces, to ignore in the blacklist. |
|
||||
|`BLACKLIST_IGNORE_URI_URLS` | |global |no |List of URLs, separated with spaces, containing URI to ignore in the blacklist. |
|
||||
| Setting | Default | Context | Multiple | Description |
|
||||
| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | --------- | -------- | ------------------------------------------------------------------------------------------------ |
|
||||
| `USE_BLACKLIST` | `yes` | multisite | no | Activate blacklist feature. |
|
||||
| `BLACKLIST_IP` | | multisite | no | List of IP/network, separated with spaces, to block. |
|
||||
| `BLACKLIST_IP_URLS` | `https://www.dan.me.uk/torlist/?exit` | global | no | List of URLs, separated with spaces, containing bad IP/network to block. |
|
||||
| `BLACKLIST_RDNS_GLOBAL` | `yes` | multisite | no | Only perform RDNS blacklist checks on global IP addresses. |
|
||||
| `BLACKLIST_RDNS` | `.shodan.io .censys.io` | multisite | no | List of reverse DNS suffixes, separated with spaces, to block. |
|
||||
| `BLACKLIST_RDNS_URLS` | | global | no | List of URLs, separated with spaces, containing reverse DNS suffixes to block. |
|
||||
| `BLACKLIST_ASN` | | multisite | no | List of ASN numbers, separated with spaces, to block. |
|
||||
| `BLACKLIST_ASN_URLS` | | global | no | List of URLs, separated with spaces, containing ASN to block. |
|
||||
| `BLACKLIST_USER_AGENT` | | multisite | no | List of User-Agent (PCRE regex), separated with spaces, to block. |
|
||||
| `BLACKLIST_USER_AGENT_URLS` | `https://raw.githubusercontent.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/master/_generator_lists/bad-user-agents.list` | global | no | List of URLs, separated with spaces, containing bad User-Agent to block. |
|
||||
| `BLACKLIST_URI` | | multisite | no | List of URI (PCRE regex), separated with spaces, to block. |
|
||||
| `BLACKLIST_URI_URLS` | | global | no | List of URLs, separated with spaces, containing bad URI to block. |
|
||||
| `BLACKLIST_IGNORE_IP` | | multisite | no | List of IP/network, separated with spaces, to ignore in the blacklist. |
|
||||
| `BLACKLIST_IGNORE_IP_URLS` | | global | no | List of URLs, separated with spaces, containing IP/network to ignore in the blacklist. |
|
||||
| `BLACKLIST_IGNORE_RDNS` | | multisite | no | List of reverse DNS suffixes, separated with spaces, to ignore in the blacklist. |
|
||||
| `BLACKLIST_IGNORE_RDNS_URLS` | | global | no | List of URLs, separated with spaces, containing reverse DNS suffixes to ignore in the blacklist. |
|
||||
| `BLACKLIST_IGNORE_ASN` | | multisite | no | List of ASN numbers, separated with spaces, to ignore in the blacklist. |
|
||||
| `BLACKLIST_IGNORE_ASN_URLS` | | global | no | List of URLs, separated with spaces, containing ASN to ignore in the blacklist. |
|
||||
| `BLACKLIST_IGNORE_USER_AGENT` | | multisite | no | List of User-Agent (PCRE regex), separated with spaces, to ignore in the blacklist. |
|
||||
| `BLACKLIST_IGNORE_USER_AGENT_URLS` | | global | no | List of URLs, separated with spaces, containing User-Agent to ignore in the blacklist. |
|
||||
| `BLACKLIST_IGNORE_URI` | | multisite | no | List of URI (PCRE regex), separated with spaces, to ignore in the blacklist. |
|
||||
| `BLACKLIST_IGNORE_URI_URLS` | | global | no | List of URLs, separated with spaces, containing URI to ignore in the blacklist. |
|
||||
|
||||
When using stream mode, only IP, RDNS and ASN checks will be done.
|
||||
|
||||
|
|
@ -320,20 +329,20 @@ STREAM support :warning:
|
|||
|
||||
You can use the following settings to set up greylisting :
|
||||
|
||||
| Setting |Default| Context |Multiple| Description |
|
||||
|--------------------------|-------|---------|--------|----------------------------------------------------------------------------------------------|
|
||||
|`USE_GREYLIST` |`no` |multisite|no |Activate greylist feature. |
|
||||
|`GREYLIST_IP` | |multisite|no |List of IP/network, separated with spaces, to put into the greylist. |
|
||||
|`GREYLIST_IP_URLS` | |global |no |List of URLs, separated with spaces, containing good IP/network to put into the greylist. |
|
||||
|`GREYLIST_RDNS_GLOBAL` |`yes` |multisite|no |Only perform RDNS greylist checks on global IP addresses. |
|
||||
|`GREYLIST_RDNS` | |multisite|no |List of reverse DNS suffixes, separated with spaces, to put into the greylist. |
|
||||
|`GREYLIST_RDNS_URLS` | |global |no |List of URLs, separated with spaces, containing reverse DNS suffixes to put into the greylist.|
|
||||
|`GREYLIST_ASN` | |multisite|no |List of ASN numbers, separated with spaces, to put into the greylist. |
|
||||
|`GREYLIST_ASN_URLS` | |global |no |List of URLs, separated with spaces, containing ASN to put into the greylist. |
|
||||
|`GREYLIST_USER_AGENT` | |multisite|no |List of User-Agent (PCRE regex), separated with spaces, to put into the greylist. |
|
||||
|`GREYLIST_USER_AGENT_URLS`| |global |no |List of URLs, separated with spaces, containing good User-Agent to put into the greylist. |
|
||||
|`GREYLIST_URI` | |multisite|no |List of URI (PCRE regex), separated with spaces, to put into the greylist. |
|
||||
|`GREYLIST_URI_URLS` | |global |no |List of URLs, separated with spaces, containing bad URI to put into the greylist. |
|
||||
| Setting | Default | Context | Multiple | Description |
|
||||
| -------------------------- | ------- | --------- | -------- | ---------------------------------------------------------------------------------------------- |
|
||||
| `USE_GREYLIST` | `no` | multisite | no | Activate greylist feature. |
|
||||
| `GREYLIST_IP` | | multisite | no | List of IP/network, separated with spaces, to put into the greylist. |
|
||||
| `GREYLIST_IP_URLS` | | global | no | List of URLs, separated with spaces, containing good IP/network to put into the greylist. |
|
||||
| `GREYLIST_RDNS_GLOBAL` | `yes` | multisite | no | Only perform RDNS greylist checks on global IP addresses. |
|
||||
| `GREYLIST_RDNS` | | multisite | no | List of reverse DNS suffixes, separated with spaces, to put into the greylist. |
|
||||
| `GREYLIST_RDNS_URLS` | | global | no | List of URLs, separated with spaces, containing reverse DNS suffixes to put into the greylist. |
|
||||
| `GREYLIST_ASN` | | multisite | no | List of ASN numbers, separated with spaces, to put into the greylist. |
|
||||
| `GREYLIST_ASN_URLS` | | global | no | List of URLs, separated with spaces, containing ASN to put into the greylist. |
|
||||
| `GREYLIST_USER_AGENT` | | multisite | no | List of User-Agent (PCRE regex), separated with spaces, to put into the greylist. |
|
||||
| `GREYLIST_USER_AGENT_URLS` | | global | no | List of URLs, separated with spaces, containing good User-Agent to put into the greylist. |
|
||||
| `GREYLIST_URI` | | multisite | no | List of URI (PCRE regex), separated with spaces, to put into the greylist. |
|
||||
| `GREYLIST_URI_URLS` | | global | no | List of URLs, separated with spaces, containing bad URI to put into the greylist. |
|
||||
|
||||
When using stream mode, only IP, RDNS and ASN checks will be done.
|
||||
|
||||
|
|
@ -343,20 +352,20 @@ STREAM support :warning:
|
|||
|
||||
You can use the following settings to set up whitelisting :
|
||||
|
||||
| Setting | Default | Context |Multiple| Description |
|
||||
|---------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|--------|----------------------------------------------------------------------------------|
|
||||
|`USE_WHITELIST` |`yes` |multisite|no |Activate whitelist feature. |
|
||||
|`WHITELIST_IP` |`20.191.45.212 40.88.21.235 40.76.173.151 40.76.163.7 20.185.79.47 52.142.26.175 20.185.79.15 52.142.24.149 40.76.162.208 40.76.163.23 40.76.162.191 40.76.162.247 54.208.102.37 107.21.1.8`|multisite|no |List of IP/network, separated with spaces, to put into the whitelist. |
|
||||
|`WHITELIST_IP_URLS` | |global |no |List of URLs, separated with spaces, containing good IP/network to whitelist. |
|
||||
|`WHITELIST_RDNS_GLOBAL` |`yes` |multisite|no |Only perform RDNS whitelist checks on global IP addresses. |
|
||||
|`WHITELIST_RDNS` |`.google.com .googlebot.com .yandex.ru .yandex.net .yandex.com .search.msn.com .baidu.com .baidu.jp .crawl.yahoo.net .fwd.linkedin.com .twitter.com .twttr.com .discord.com` |multisite|no |List of reverse DNS suffixes, separated with spaces, to whitelist. |
|
||||
|`WHITELIST_RDNS_URLS` | |global |no |List of URLs, separated with spaces, containing reverse DNS suffixes to whitelist.|
|
||||
|`WHITELIST_ASN` |`32934` |multisite|no |List of ASN numbers, separated with spaces, to whitelist. |
|
||||
|`WHITELIST_ASN_URLS` | |global |no |List of URLs, separated with spaces, containing ASN to whitelist. |
|
||||
|`WHITELIST_USER_AGENT` | |multisite|no |List of User-Agent (PCRE regex), separated with spaces, to whitelist. |
|
||||
|`WHITELIST_USER_AGENT_URLS`| |global |no |List of URLs, separated with spaces, containing good User-Agent to whitelist. |
|
||||
|`WHITELIST_URI` | |multisite|no |List of URI (PCRE regex), separated with spaces, to whitelist. |
|
||||
|`WHITELIST_URI_URLS` | |global |no |List of URLs, separated with spaces, containing bad URI to whitelist. |
|
||||
| Setting | Default | Context | Multiple | Description |
|
||||
| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -------- | ---------------------------------------------------------------------------------- |
|
||||
| `USE_WHITELIST` | `yes` | multisite | no | Activate whitelist feature. |
|
||||
| `WHITELIST_IP` | `20.191.45.212 40.88.21.235 40.76.173.151 40.76.163.7 20.185.79.47 52.142.26.175 20.185.79.15 52.142.24.149 40.76.162.208 40.76.163.23 40.76.162.191 40.76.162.247 54.208.102.37 107.21.1.8` | multisite | no | List of IP/network, separated with spaces, to put into the whitelist. |
|
||||
| `WHITELIST_IP_URLS` | | global | no | List of URLs, separated with spaces, containing good IP/network to whitelist. |
|
||||
| `WHITELIST_RDNS_GLOBAL` | `yes` | multisite | no | Only perform RDNS whitelist checks on global IP addresses. |
|
||||
| `WHITELIST_RDNS` | `.google.com .googlebot.com .yandex.ru .yandex.net .yandex.com .search.msn.com .baidu.com .baidu.jp .crawl.yahoo.net .fwd.linkedin.com .twitter.com .twttr.com .discord.com` | multisite | no | List of reverse DNS suffixes, separated with spaces, to whitelist. |
|
||||
| `WHITELIST_RDNS_URLS` | | global | no | List of URLs, separated with spaces, containing reverse DNS suffixes to whitelist. |
|
||||
| `WHITELIST_ASN` | `32934` | multisite | no | List of ASN numbers, separated with spaces, to whitelist. |
|
||||
| `WHITELIST_ASN_URLS` | | global | no | List of URLs, separated with spaces, containing ASN to whitelist. |
|
||||
| `WHITELIST_USER_AGENT` | | multisite | no | List of User-Agent (PCRE regex), separated with spaces, to whitelist. |
|
||||
| `WHITELIST_USER_AGENT_URLS` | | global | no | List of URLs, separated with spaces, containing good User-Agent to whitelist. |
|
||||
| `WHITELIST_URI` | | multisite | no | List of URI (PCRE regex), separated with spaces, to whitelist. |
|
||||
| `WHITELIST_URI_URLS` | | global | no | List of URLs, separated with spaces, containing bad URI to whitelist. |
|
||||
|
||||
When using stream mode, only IP, RDNS and ASN checks will be done.
|
||||
|
||||
|
|
@ -373,11 +382,11 @@ Please be aware, this feature is new and further improvements will be added soon
|
|||
|
||||
Here is the list of settings related to reverse scan :
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :----------: | :--------------------------------------------------------------------------: | :--------------------------------------------- |
|
||||
| `USE_REVERSE_SCAN` | `no` | When set to `yes`, will enable ReverseScan. |
|
||||
| `REVERSE_SCAN_PORTS` | `22 80 443 3128 8000 8080` | List of suspicious ports to scan. |
|
||||
| `REVERSE_SCAN_TIMEOUT` | `500` | Specify the maximum timeout (in ms) when scanning a port. |
|
||||
| Setting | Default | Description |
|
||||
| :--------------------: | :------------------------: | :-------------------------------------------------------- |
|
||||
| `USE_REVERSE_SCAN` | `no` | When set to `yes`, will enable ReverseScan. |
|
||||
| `REVERSE_SCAN_PORTS` | `22 80 443 3128 8000 8080` | List of suspicious ports to scan. |
|
||||
| `REVERSE_SCAN_TIMEOUT` | `500` | Specify the maximum timeout (in ms) when scanning a port. |
|
||||
|
||||
## BunkerNet
|
||||
|
||||
|
|
@ -421,12 +430,12 @@ STREAM support :white_check_mark:
|
|||
|
||||
The following settings are related to the Limiting connections feature :
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :--------------------: | :-----: | :----------------------------------------------------------------------------------------- |
|
||||
| `USE_LIMIT_CONN` | `yes` | When set to `yes`, will limit the maximum number of concurrent connections for a given IP. |
|
||||
| `LIMIT_CONN_MAX_HTTP1` | `10` | Maximum number of concurrent connections when using HTTP1 protocol. |
|
||||
| `LIMIT_CONN_MAX_HTTP2` | `100` | Maximum number of concurrent streams when using HTTP2 protocol. |
|
||||
| `LIMIT_CONN_MAX_STREAM`| `10` | Maximum number of connections per IP when using stream. |
|
||||
| Setting | Default | Description |
|
||||
| :---------------------: | :-----: | :----------------------------------------------------------------------------------------- |
|
||||
| `USE_LIMIT_CONN` | `yes` | When set to `yes`, will limit the maximum number of concurrent connections for a given IP. |
|
||||
| `LIMIT_CONN_MAX_HTTP1` | `10` | Maximum number of concurrent connections when using HTTP1 protocol. |
|
||||
| `LIMIT_CONN_MAX_HTTP2` | `100` | Maximum number of concurrent streams when using HTTP2 protocol. |
|
||||
| `LIMIT_CONN_MAX_STREAM` | `10` | Maximum number of connections per IP when using stream. |
|
||||
|
||||
### Requests
|
||||
|
||||
|
|
@ -434,15 +443,15 @@ STREAM support :x:
|
|||
|
||||
The following settings are related to the Limiting requests feature :
|
||||
|
||||
| Setting |Default| Context |Multiple| Description |
|
||||
|-----------------------|-------|---------|--------|---------------------------------------------------------------------------------------------|
|
||||
|`USE_LIMIT_REQ` |`yes` |multisite|no |Activate limit requests feature. |
|
||||
|`LIMIT_REQ_URL` |`/` |multisite|yes |URL (PCRE regex) where the limit request will be applied or special value / for all requests.|
|
||||
|`LIMIT_REQ_RATE` |`2r/s` |multisite|yes |Rate to apply to the URL (s for second, m for minute, h for hour and d for day). |
|
||||
|`USE_LIMIT_CONN` |`yes` |multisite|no |Activate limit connections feature. |
|
||||
|`LIMIT_CONN_MAX_HTTP1` |`10` |multisite|no |Maximum number of connections per IP when using HTTP/1.X protocol. |
|
||||
|`LIMIT_CONN_MAX_HTTP2` |`100` |multisite|no |Maximum number of streams per IP when using HTTP/2 protocol. |
|
||||
|`LIMIT_CONN_MAX_STREAM`|`10` |multisite|no |Maximum number of connections per IP when using stream. |
|
||||
| Setting | Default | Context | Multiple | Description |
|
||||
| ----------------------- | ------- | --------- | -------- | --------------------------------------------------------------------------------------------- |
|
||||
| `USE_LIMIT_REQ` | `yes` | multisite | no | Activate limit requests feature. |
|
||||
| `LIMIT_REQ_URL` | `/` | multisite | yes | URL (PCRE regex) where the limit request will be applied or special value / for all requests. |
|
||||
| `LIMIT_REQ_RATE` | `2r/s` | multisite | yes | Rate to apply to the URL (s for second, m for minute, h for hour and d for day). |
|
||||
| `USE_LIMIT_CONN` | `yes` | multisite | no | Activate limit connections feature. |
|
||||
| `LIMIT_CONN_MAX_HTTP1` | `10` | multisite | no | Maximum number of connections per IP when using HTTP/1.X protocol. |
|
||||
| `LIMIT_CONN_MAX_HTTP2` | `100` | multisite | no | Maximum number of streams per IP when using HTTP/2 protocol. |
|
||||
| `LIMIT_CONN_MAX_STREAM` | `10` | multisite | no | Maximum number of connections per IP when using stream. |
|
||||
|
||||
Please note that you can add different rates for different URLs by adding a number as a suffix to the settings for example : `LIMIT_REQ_URL_1=^/url1$`, `LIMIT_REQ_RATE_1=5r/d`, `LIMIT_REQ_URL_2=^/url2/subdir/.*$`, `LIMIT_REQ_RATE_2=1r/m`, ...
|
||||
|
||||
|
|
@ -459,10 +468,10 @@ The country security feature allows you to apply policy based on the country of
|
|||
|
||||
Here is the list of related settings :
|
||||
|
||||
| Setting |Default| Context |Multiple| Description |
|
||||
|-------------------|-------|---------|--------|--------------------------------------------------------------------------------------------------------------|
|
||||
|`BLACKLIST_COUNTRY`| |multisite|no |Deny access if the country of the client is in the list (ISO 3166-1 alpha-2 format separated with spaces). |
|
||||
|`WHITELIST_COUNTRY`| |multisite|no |Deny access if the country of the client is not in the list (ISO 3166-1 alpha-2 format separated with spaces).|
|
||||
| Setting | Default | Context | Multiple | Description |
|
||||
| ------------------- | ------- | --------- | -------- | -------------------------------------------------------------------------------------------------------------- |
|
||||
| `BLACKLIST_COUNTRY` | | multisite | no | Deny access if the country of the client is in the list (ISO 3166-1 alpha-2 format separated with spaces). |
|
||||
| `WHITELIST_COUNTRY` | | multisite | no | Deny access if the country of the client is not in the list (ISO 3166-1 alpha-2 format separated with spaces). |
|
||||
|
||||
Using both country blacklist and whitelist at the same time makes no sense. If you do, please note that only the whitelist will be executed.
|
||||
|
||||
|
|
@ -474,10 +483,10 @@ STREAM support :x:
|
|||
|
||||
You can quickly protect sensitive resources like the admin area for example, by requiring HTTP basic authentication. Here is the list of related settings :
|
||||
|
||||
| Setting | Default | Description |
|
||||
| :-----------------------: | :---------------: | :------------------------------------------------------------------------------------------- |
|
||||
| `USE_AUTH_BASIC` | `no` | When set to `yes` HTTP auth basic will be enabled. |
|
||||
| `AUTH_BASIC_LOCATION` | `sitewide` | Location (URL) of the sensitive resource. Use special value `sitewide` to enable everywhere. |
|
||||
| Setting | Default | Description |
|
||||
| :-------------------: | :---------------: | :------------------------------------------------------------------------------------------- |
|
||||
| `USE_AUTH_BASIC` | `no` | When set to `yes` HTTP auth basic will be enabled. |
|
||||
| `AUTH_BASIC_LOCATION` | `sitewide` | Location (URL) of the sensitive resource. Use special value `sitewide` to enable everywhere. |
|
||||
| `AUTH_BASIC_USER` | `changeme` | The username required. |
|
||||
| `AUTH_BASIC_PASSWORD` | `changeme` | The password required. |
|
||||
| `AUTH_BASIC_TEXT` | `Restricted area` | Text to display in the auth prompt. |
|
||||
|
|
@ -488,8 +497,8 @@ You can deploy complex authentication (e.g. SSO), by using the auth request sett
|
|||
|
||||
**Auth request settings are related to reverse proxy rules.**
|
||||
|
||||
| Setting | Default | Context |Multiple| Description |
|
||||
|---------------------------------------|----------------------------------|---------|--------|--------------------------------------------------------------------------------------------------------------------|
|
||||
|`REVERSE_PROXY_AUTH_REQUEST` | |multisite|yes |Enable authentication using an external provider (value of auth_request directive). |
|
||||
|`REVERSE_PROXY_AUTH_REQUEST_SIGNIN_URL`| |multisite|yes |Redirect clients to sign-in URL when using REVERSE_PROXY_AUTH_REQUEST (used when auth_request call returned 401). |
|
||||
|`REVERSE_PROXY_AUTH_REQUEST_SET` | |multisite|yes |List of variables to set from the authentication provider, separated with ; (values of auth_request_set directives).|
|
||||
| Setting | Default | Context | Multiple | Description |
|
||||
| --------------------------------------- | ------- | --------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| `REVERSE_PROXY_AUTH_REQUEST` | | multisite | yes | Enable authentication using an external provider (value of auth_request directive). |
|
||||
| `REVERSE_PROXY_AUTH_REQUEST_SIGNIN_URL` | | multisite | yes | Redirect clients to sign-in URL when using REVERSE_PROXY_AUTH_REQUEST (used when auth_request call returned 401). |
|
||||
| `REVERSE_PROXY_AUTH_REQUEST_SET` | | multisite | yes | List of variables to set from the authentication provider, separated with ; (values of auth_request_set directives). |
|
||||
|
|
|
|||
111
docs/settings.md
111
docs/settings.md
|
|
@ -15,39 +15,41 @@ When settings are considered as "multiple", it means that you can have multiple
|
|||
|
||||
STREAM support :warning:
|
||||
|
||||
| Setting | Default | Context |Multiple| Description |
|
||||
|------------------------------|------------------------------------------------------------------------------------------------------------------------|---------|--------|--------------------------------------------------|
|
||||
|`IS_LOADING` |`no` |global |no |Internal use : set to yes when BW is loading. |
|
||||
|`NGINX_PREFIX` |`/etc/nginx/` |global |no |Where nginx will search for configurations. |
|
||||
|`HTTP_PORT` |`8080` |global |no |HTTP port number which bunkerweb binds to. |
|
||||
|`HTTPS_PORT` |`8443` |global |no |HTTPS port number which bunkerweb binds to. |
|
||||
|`MULTISITE` |`no` |global |no |Multi site activation. |
|
||||
|`SERVER_NAME` |`www.example.com` |multisite|no |List of the virtual hosts served by bunkerweb. |
|
||||
|`WORKER_PROCESSES` |`auto` |global |no |Number of worker processes. |
|
||||
|`WORKER_RLIMIT_NOFILE` |`2048` |global |no |Maximum number of open files for worker processes.|
|
||||
|`WORKER_CONNECTIONS` |`1024` |global |no |Maximum number of connections per worker. |
|
||||
|`LOG_FORMAT` |`$host $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"`|global |no |The format to use for access logs. |
|
||||
|`LOG_LEVEL` |`notice` |global |no |The level to use for error logs. |
|
||||
|`DNS_RESOLVERS` |`127.0.0.11` |global |no |DNS addresses of resolvers to use. |
|
||||
|`DATASTORE_MEMORY_SIZE` |`64m` |global |no |Size of the internal datastore. |
|
||||
|`CACHESTORE_MEMORY_SIZE` |`64m` |global |no |Size of the internal cachestore. |
|
||||
|`CACHESTORE_IPC_MEMORY_SIZE` |`16m` |global |no |Size of the internal cachestore (ipc). |
|
||||
|`CACHESTORE_MISS_MEMORY_SIZE` |`16m` |global |no |Size of the internal cachestore (miss). |
|
||||
|`CACHESTORE_LOCKS_MEMORY_SIZE`|`16m` |global |no |Size of the internal cachestore (locks). |
|
||||
|`USE_API` |`yes` |global |no |Activate the API to control BunkerWeb. |
|
||||
|`API_HTTP_PORT` |`5000` |global |no |Listen port number for the API. |
|
||||
|`API_LISTEN_IP` |`0.0.0.0` |global |no |Listen IP address for the API. |
|
||||
|`API_SERVER_NAME` |`bwapi` |global |no |Server name (virtual host) for the API. |
|
||||
|`API_WHITELIST_IP` |`127.0.0.0/8` |global |no |List of IP/network allowed to contact the API. |
|
||||
|`AUTOCONF_MODE` |`no` |global |no |Enable Autoconf Docker integration. |
|
||||
|`SWARM_MODE` |`no` |global |no |Enable Docker Swarm integration. |
|
||||
|`KUBERNETES_MODE` |`no` |global |no |Enable Kubernetes integration. |
|
||||
|`SERVER_TYPE` |`http` |multisite|no |Server type : http or stream. |
|
||||
|`LISTEN_STREAM` |`yes` |multisite|no |Enable listening for non-ssl (passthrough). |
|
||||
|`LISTEN_STREAM_PORT` |`1337` |multisite|no |Listening port for non-ssl (passthrough). |
|
||||
|`LISTEN_STREAM_PORT_SSL` |`4242` |multisite|no |Listening port for ssl (passthrough). |
|
||||
|`USE_UDP` |`no` |multisite|no |UDP listen instead of TCP (stream). |
|
||||
|`USE_IPV6` |`no` |global |no |Enable IPv6 connectivity. |
|
||||
| Setting | Default | Context |Multiple| Description |
|
||||
|------------------------------|------------------------------------------------------------------------------------------------------------------------|---------|--------|------------------------------------------------------------|
|
||||
|`IS_LOADING` |`no` |global |no |Internal use : set to yes when BW is loading. |
|
||||
|`NGINX_PREFIX` |`/etc/nginx/` |global |no |Where nginx will search for configurations. |
|
||||
|`HTTP_PORT` |`8080` |global |no |HTTP port number which bunkerweb binds to. |
|
||||
|`HTTPS_PORT` |`8443` |global |no |HTTPS port number which bunkerweb binds to. |
|
||||
|`MULTISITE` |`no` |global |no |Multi site activation. |
|
||||
|`SERVER_NAME` |`www.example.com` |multisite|no |List of the virtual hosts served by bunkerweb. |
|
||||
|`WORKER_PROCESSES` |`auto` |global |no |Number of worker processes. |
|
||||
|`WORKER_RLIMIT_NOFILE` |`2048` |global |no |Maximum number of open files for worker processes. |
|
||||
|`WORKER_CONNECTIONS` |`1024` |global |no |Maximum number of connections per worker. |
|
||||
|`LOG_FORMAT` |`$host $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"`|global |no |The format to use for access logs. |
|
||||
|`LOG_LEVEL` |`notice` |global |no |The level to use for error logs. |
|
||||
|`DNS_RESOLVERS` |`127.0.0.11` |global |no |DNS addresses of resolvers to use. |
|
||||
|`DATASTORE_MEMORY_SIZE` |`64m` |global |no |Size of the internal datastore. |
|
||||
|`CACHESTORE_MEMORY_SIZE` |`64m` |global |no |Size of the internal cachestore. |
|
||||
|`CACHESTORE_IPC_MEMORY_SIZE` |`16m` |global |no |Size of the internal cachestore (ipc). |
|
||||
|`CACHESTORE_MISS_MEMORY_SIZE` |`16m` |global |no |Size of the internal cachestore (miss). |
|
||||
|`CACHESTORE_LOCKS_MEMORY_SIZE`|`16m` |global |no |Size of the internal cachestore (locks). |
|
||||
|`USE_API` |`yes` |global |no |Activate the API to control BunkerWeb. |
|
||||
|`API_HTTP_PORT` |`5000` |global |no |Listen port number for the API. |
|
||||
|`API_LISTEN_IP` |`0.0.0.0` |global |no |Listen IP address for the API. |
|
||||
|`API_SERVER_NAME` |`bwapi` |global |no |Server name (virtual host) for the API. |
|
||||
|`API_WHITELIST_IP` |`127.0.0.0/8` |global |no |List of IP/network allowed to contact the API. |
|
||||
|`AUTOCONF_MODE` |`no` |global |no |Enable Autoconf Docker integration. |
|
||||
|`SWARM_MODE` |`no` |global |no |Enable Docker Swarm integration. |
|
||||
|`KUBERNETES_MODE` |`no` |global |no |Enable Kubernetes integration. |
|
||||
|`SERVER_TYPE` |`http` |multisite|no |Server type : http or stream. |
|
||||
|`LISTEN_STREAM` |`yes` |multisite|no |Enable listening for non-ssl (passthrough). |
|
||||
|`LISTEN_STREAM_PORT` |`1337` |multisite|no |Listening port for non-ssl (passthrough). |
|
||||
|`LISTEN_STREAM_PORT_SSL` |`4242` |multisite|no |Listening port for ssl (passthrough). |
|
||||
|`USE_UDP` |`no` |multisite|no |UDP listen instead of TCP (stream). |
|
||||
|`USE_IPV6` |`no` |global |no |Enable IPv6 connectivity. |
|
||||
|`IS_DRAFT` |`no` |multisite|no |Internal use : set to yes when the service is in draft mode.|
|
||||
|`TIMERS_LOG_LEVEL` |`debug` |global |no |Log level for timers. |
|
||||
|
||||
|
||||
## Core settings
|
||||
|
|
@ -219,9 +221,10 @@ STREAM support :white_check_mark:
|
|||
|
||||
Integrate easily the Database.
|
||||
|
||||
| Setting | Default |Context|Multiple| Description |
|
||||
|--------------|-----------------------------------------|-------|--------|--------------------------------------------------|
|
||||
|`DATABASE_URI`|`sqlite:////var/lib/bunkerweb/db.sqlite3`|global |no |The database URI, following the sqlalchemy format.|
|
||||
| Setting | Default |Context|Multiple| Description |
|
||||
|--------------------|-----------------------------------------|-------|--------|--------------------------------------------------|
|
||||
|`DATABASE_URI` |`sqlite:////var/lib/bunkerweb/db.sqlite3`|global |no |The database URI, following the sqlalchemy format.|
|
||||
|`DATABASE_LOG_LEVEL`|`warning` |global |no |The level to use for database logs. |
|
||||
|
||||
### DNSBL
|
||||
|
||||
|
|
@ -312,6 +315,16 @@ Manage HTTP headers sent to clients.
|
|||
|`X_CONTENT_TYPE_OPTIONS` |`nosniff` |multisite|no |Value for the X-Content-Type-Options header. |
|
||||
|`X_XSS_PROTECTION` |`1; mode=block` |multisite|no |Value for the X-XSS-Protection header. |
|
||||
|
||||
### Jobs
|
||||
|
||||
STREAM support :white_check_mark:
|
||||
|
||||
Fake core plugin for internal jobs.
|
||||
|
||||
| Setting |Default|Context|Multiple| Description |
|
||||
|-----------------------|-------|-------|--------|-----------------------------------------------|
|
||||
|`SEND_ANONYMOUS_REPORT`|`yes` |global |no |Send anonymous report to BunkerWeb maintainers.|
|
||||
|
||||
### Let's Encrypt
|
||||
|
||||
STREAM support :white_check_mark:
|
||||
|
|
@ -340,6 +353,18 @@ Limit maximum number of requests and connections.
|
|||
|`LIMIT_CONN_MAX_HTTP2` |`100` |multisite|no |Maximum number of streams per IP when using HTTP/2 protocol. |
|
||||
|`LIMIT_CONN_MAX_STREAM`|`10` |multisite|no |Maximum number of connections per IP when using stream. |
|
||||
|
||||
### Metrics
|
||||
|
||||
STREAM support :warning:
|
||||
|
||||
Metrics collection and retrieve.
|
||||
|
||||
| Setting |Default| Context |Multiple| Description |
|
||||
|------------------------------|-------|---------|--------|---------------------------------------------------------|
|
||||
|`USE_METRICS` |`yes` |multisite|no |Enable collection and retrieval of internal metrics. |
|
||||
|`METRICS_MEMORY_SIZE` |`16m` |global |no |Size of the internal storage for metrics. |
|
||||
|`METRICS_MAX_BLOCKED_REQUESTS`|`100` |global |no |Maximum number of blocked requests to store (per worker).|
|
||||
|
||||
### Miscellaneous
|
||||
|
||||
STREAM support :warning:
|
||||
|
|
@ -365,7 +390,6 @@ Miscellaneous settings.
|
|||
|`OPEN_FILE_CACHE_VALID` |`30s` |multisite|no |Open file cache valid time |
|
||||
|`EXTERNAL_PLUGIN_URLS` | |global |no |List of external plugins URLs (direct download to .zip or .tar file) to download and install (URLs are separated with space).|
|
||||
|`DENY_HTTP_STATUS` |`403` |global |no |HTTP status code to send when the request is denied (403 or 444). When using 444, BunkerWeb will close the connection. |
|
||||
|`SEND_ANONYMOUS_REPORT` |`yes` |global |no |Send anonymous report to BunkerWeb maintainers. |
|
||||
|
||||
### ModSecurity
|
||||
|
||||
|
|
@ -377,6 +401,7 @@ Management of the ModSecurity WAF.
|
|||
|---------------------------------|--------------|---------|--------|------------------------------------------|
|
||||
|`USE_MODSECURITY` |`yes` |multisite|no |Enable ModSecurity WAF. |
|
||||
|`USE_MODSECURITY_CRS` |`yes` |multisite|no |Enable OWASP Core Rule Set. |
|
||||
|`MODSECURITY_CRS_VERSION` |`3` |multisite|no |Version of the OWASP Core Rule Set to use.|
|
||||
|`MODSECURITY_SEC_AUDIT_ENGINE` |`RelevantOnly`|multisite|no |SecAuditEngine directive of ModSecurity. |
|
||||
|`MODSECURITY_SEC_RULE_ENGINE` |`On` |multisite|no |SecRuleEngine directive of ModSecurity. |
|
||||
|`MODSECURITY_SEC_AUDIT_LOG_PARTS`|`ABCFHZ` |multisite|no |SecAuditLogParts directive of ModSecurity.|
|
||||
|
|
@ -394,6 +419,16 @@ Manage local or remote PHP-FPM.
|
|||
|`LOCAL_PHP` | |multisite|no |Path to the PHP-FPM socket file. |
|
||||
|`LOCAL_PHP_PATH` | |multisite|no |Root folder containing files in the local PHP-FPM instance. |
|
||||
|
||||
### Pro
|
||||
|
||||
STREAM support :x:
|
||||
|
||||
Pro settings for the Pro version of BunkerWeb.
|
||||
|
||||
| Setting |Default|Context|Multiple| Description |
|
||||
|-----------------|-------|-------|--------|-------------------------------------------------|
|
||||
|`PRO_LICENSE_KEY`| |global |no |The License Key for the Pro version of BunkerWeb.|
|
||||
|
||||
### Real IP
|
||||
|
||||
STREAM support :warning:
|
||||
|
|
@ -434,6 +469,7 @@ Redis server configuration when using BunkerWeb in cluster mode.
|
|||
|`REDIS_PORT` |`6379` |global |no |Redis server port. |
|
||||
|`REDIS_DATABASE` |`0` |global |no |Redis database number. |
|
||||
|`REDIS_SSL` |`no` |global |no |Use SSL/TLS connection with Redis server. |
|
||||
|`REDIS_SSL_VERIFY` |`no` |global |no |Verify the certificate of Redis server. |
|
||||
|`REDIS_TIMEOUT` |`1000` |global |no |Redis server timeout (in ms) for connect, read and write. |
|
||||
|`REDIS_KEEPALIVE_IDLE` |`30000`|global |no |Max idle time (in ms) before closing redis connection in the pool. |
|
||||
|`REDIS_KEEPALIVE_POOL` |`10` |global |no |Max number of redis connection(s) kept in the pool. |
|
||||
|
|
@ -478,6 +514,7 @@ Manage reverse proxy configurations.
|
|||
|`REVERSE_PROXY_READ_TIMEOUT` |`60s` |multisite|yes |Timeout when reading from the proxied resource. |
|
||||
|`REVERSE_PROXY_SEND_TIMEOUT` |`60s` |multisite|yes |Timeout when sending to the proxied resource. |
|
||||
|`REVERSE_PROXY_INCLUDES` | |multisite|yes |Additional configuration to include in the location block, separated with spaces. |
|
||||
|`REVERSE_PROXY_CUSTOM_HOST` | |multisite|no |Override Host header sent to upstream server. |
|
||||
|
||||
### Reverse scan
|
||||
|
||||
|
|
|
|||
|
|
@ -1728,3 +1728,27 @@ After a successful login/password combination, you will be prompted to enter you
|
|||
```shell
|
||||
systemctl restart bunkerweb
|
||||
```
|
||||
|
||||
## Upgrade to PRO
|
||||
|
||||
In case you have buy a (pro version)[https://panel.bunkerweb.io/?utm_campaign=self&utm_source=doc#pro] and you already have a BunkerWeb setup with an UI, you can upgrade doing this :
|
||||
|
||||
- access the **global config page**.
|
||||
- click on the **pro plugin**
|
||||
- fill the **setting Pro License Key**
|
||||
- **save** your changes
|
||||
|
||||
|
||||
<figure markdown>
|
||||
{ align=center, width="1000" }
|
||||
<figcaption>Upgrade to PRO from UI</figcaption>
|
||||
</figure>
|
||||
|
||||
If your license key is valid, the upgrade to the pro version will take place automatically in the background.
|
||||
|
||||
You can check the status of your version by going to the **home page**, you will see this in case of success :
|
||||
|
||||
<figure markdown>
|
||||
{ align=center, width="450" }
|
||||
<figcaption>PRO version card</figcaption>
|
||||
</figure>
|
||||
|
|
|
|||
45
misc/format-jinja-files.sh
Executable file
45
misc/format-jinja-files.sh
Executable file
|
|
@ -0,0 +1,45 @@
|
|||
#!/bin/bash
|
||||
|
||||
first_arg=$1
|
||||
args=("$@")
|
||||
|
||||
if [ "$first_arg" == "manual" ]; then
|
||||
echo "⏳ Running djLint for web UI Jinja files..."
|
||||
djlint --reformat --profile=jinja --exclude=src/ui/templates/setup.html src/ui/templates
|
||||
ret=$?
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo "❌ djLint failed for web UI template files. Exiting..."
|
||||
exit $ret
|
||||
fi
|
||||
echo "✅ djLint for web UI template files passed."
|
||||
|
||||
|
||||
echo "⏳ Running djLint for core plugins UI Jinja files..."
|
||||
djlint --reformat --profile=jinja src/common/core/*/ui
|
||||
ret=$?
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo "❌ djLint failed for core plugins UI template files. Exiting..."
|
||||
exit $ret
|
||||
fi
|
||||
echo "✅ djLint for core plugins UI template files passed."
|
||||
|
||||
echo "⏳ Running djLint for documentation Jinja files..."
|
||||
djlint --reformat --profile=jinja docs/
|
||||
ret=$?
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo "❌ djLint failed for documentation files. Exiting..."
|
||||
exit $ret
|
||||
fi
|
||||
echo "✅ djLint for documentation files passed."
|
||||
|
||||
echo "✅ All djLint commands passed"
|
||||
else
|
||||
echo "⏳ Running custom djLint command with args: ${args[*]}"
|
||||
djlint --reformat --profile=jinja "${args[@]}"
|
||||
ret=$?
|
||||
if [ $ret -ne 0 ]; then
|
||||
echo "❌ djLint failed for custom command. Exiting..."
|
||||
exit $ret
|
||||
fi
|
||||
echo "✅ djLint for custom command passed."
|
||||
fi
|
||||
|
|
@ -232,7 +232,7 @@ class IngressController(Controller):
|
|||
obj = event["object"]
|
||||
metadata = obj.metadata if obj else None
|
||||
annotations = metadata.annotations if metadata else None
|
||||
data = getattr(obj, 'data', None) if obj else None
|
||||
data = getattr(obj, "data", None) if obj else None
|
||||
if not obj:
|
||||
return False
|
||||
if obj.kind == "Pod":
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
FROM nginx:1.24.0-alpine@sha256:76ca7f6bfe01c3e22e3af85fd37c30149ece3ac2a444973687cab1765abca115 AS builder
|
||||
FROM nginx:1.24.0-alpine@sha256:6845649eadc1f0a5dacaf5bb3f01b480ce200ae1249114be11fef9d389196eaf AS builder
|
||||
|
||||
# Install temporary requirements for the dependencies
|
||||
RUN apk add --no-cache --virtual .build-deps 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
|
||||
|
|
@ -67,8 +67,8 @@ RUN apk add --no-cache pcre bash python3 yajl && \
|
|||
mkdir -p /var/www/html && \
|
||||
mkdir -p /etc/bunkerweb && \
|
||||
mkdir -p /data/cache && ln -s /data/cache /var/cache/bunkerweb && \
|
||||
for dir in $(echo "configs plugins") ; do mkdir -p "/data/${dir}" && ln -s "/data/${dir}" "/etc/bunkerweb/${dir}" ; done && \
|
||||
for dir in $(echo "configs/http configs/stream configs/server-http configs/server-stream configs/default-server-http configs/default-server-stream configs/modsec configs/modsec-crs") ; do mkdir "/data/${dir}" ; done && \
|
||||
for dir in $(echo "pro configs plugins") ; do mkdir -p "/data/${dir}" && ln -s "/data/${dir}" "/etc/bunkerweb/${dir}" ; done && \
|
||||
for dir in $(echo "pro/plugins configs/http configs/stream configs/server-http configs/server-stream configs/default-server-http configs/default-server-stream configs/modsec configs/modsec-crs") ; do mkdir "/data/${dir}" ; done && \
|
||||
chown -R root:nginx /data /etc/nginx /var/cache/bunkerweb /etc/bunkerweb /var/tmp/bunkerweb /var/run/bunkerweb /var/log/bunkerweb /usr/bin/bwcli && \
|
||||
chmod -R 770 /data /etc/nginx /var/cache/bunkerweb /var/tmp/bunkerweb /var/log/bunkerweb /var/run/bunkerweb && \
|
||||
chmod 750 cli/main.py gen/main.py helpers/*.sh entrypoint.sh /usr/bin/bwcli deps/python/bin/* && \
|
||||
|
|
@ -78,7 +78,7 @@ RUN apk add --no-cache pcre bash python3 yajl && \
|
|||
ln -s /proc/1/fd/1 /var/log/bunkerweb/access.log
|
||||
|
||||
# Fix CVEs
|
||||
RUN apk add --no-cache "libwebp>=1.2.4-r3" "curl>=8.3.0-r0" "libcurl>=8.3.0-r0" "nghttp2-libs>=1.51.0-r2" "libx11>=1.8.7-r0" "libssl3>=3.0.12-r1" "libcrypto3>=3.0.12-r1" "libexpat>=2.6.0-r0"
|
||||
RUN apk add --no-cache "curl>=8.5.0-r0" "libcurl>=8.5.0-r0" "libcrypto3>=3.0.12-r4" "libexpat>=2.6.0-r0" "libssl3>=3.0.12-r4" "libwebp>=1.2.4-r3" "libx11>=1.8.7-r0" "nghttp2-libs>=1.51.0-r2"
|
||||
|
||||
EXPOSE 8080/tcp 8443/tcp
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ function trap_reload() {
|
|||
trap "trap_reload" HUP
|
||||
|
||||
# generate "temp" config
|
||||
echo -e "IS_LOADING=yes\nUSE_BUNKERNET=no\nSERVER_NAME=\nAPI_HTTP_PORT=${API_HTTP_PORT:-5000}\nAPI_SERVER_NAME=${API_SERVER_NAME:-bwapi}\nAPI_WHITELIST_IP=${API_WHITELIST_IP:-127.0.0.0/8}\nUSE_REAL_IP=${USE_REAL_IP:-no}\nUSE_PROXY_PROTOCOL=${USE_PROXY_PROTOCOL:-no}\nREAL_IP_FROM=${REAL_IP_FROM:-192.168.0.0/16 172.16.0.0/12 10.0.0.0/8}\nREAL_IP_HEADER=${REAL_IP_HEADER:-X-Forwarded-For}\nHTTP_PORT=${HTTP_PORT:-8080}\nHTTPS_PORT=${HTTPS_PORT:-8443}" > /tmp/variables.env
|
||||
echo -e "IS_LOADING=yes\nUSE_BUNKERNET=no\nSEND_ANONYMOUS_REPORT=no\nSERVER_NAME=\nMODSECURITY_CRS_VERSION=${MODSECURITY_CRS_VERSION:-4}\nAPI_HTTP_PORT=${API_HTTP_PORT:-5000}\nAPI_SERVER_NAME=${API_SERVER_NAME:-bwapi}\nAPI_WHITELIST_IP=${API_WHITELIST_IP:-127.0.0.0/8}\nUSE_REAL_IP=${USE_REAL_IP:-no}\nUSE_PROXY_PROTOCOL=${USE_PROXY_PROTOCOL:-no}\nREAL_IP_FROM=${REAL_IP_FROM:-192.168.0.0/16 172.16.0.0/12 10.0.0.0/8}\nREAL_IP_HEADER=${REAL_IP_HEADER:-X-Forwarded-For}\nHTTP_PORT=${HTTP_PORT:-8080}\nHTTPS_PORT=${HTTPS_PORT:-8443}" > /tmp/variables.env
|
||||
python3 /usr/share/bunkerweb/gen/main.py --variables /tmp/variables.env
|
||||
|
||||
# start nginx
|
||||
|
|
|
|||
|
|
@ -128,6 +128,8 @@ api.global.POST["^/confs$"] = function(self)
|
|||
destination = "/etc/bunkerweb/configs"
|
||||
elseif self.ctx.bw.uri == "/plugins" then
|
||||
destination = "/etc/bunkerweb/plugins"
|
||||
elseif self.ctx.bw.uri == "/pro_plugins" then
|
||||
destination = "/etc/bunkerweb/pro/plugins"
|
||||
end
|
||||
local form, err = upload:new(4096)
|
||||
if not form then
|
||||
|
|
@ -175,6 +177,8 @@ api.global.POST["^/custom_configs$"] = api.global.POST["^/confs$"]
|
|||
|
||||
api.global.POST["^/plugins$"] = api.global.POST["^/confs$"]
|
||||
|
||||
api.global.POST["^/pro_plugins$"] = api.global.POST["^/confs$"]
|
||||
|
||||
api.global.POST["^/unban$"] = function(self)
|
||||
read_body()
|
||||
local data = get_body_data()
|
||||
|
|
|
|||
|
|
@ -787,6 +787,7 @@ utils.get_phases = function()
|
|||
"log_stream",
|
||||
"log_default",
|
||||
"timer",
|
||||
"init_workers"
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
|
|
@ -211,24 +211,30 @@ class CLI(ApiCaller):
|
|||
|
||||
def unban(self, ip: str) -> Tuple[bool, str]:
|
||||
if self.__redis:
|
||||
ok = self.__redis.delete(f"bans_ip_{ip}")
|
||||
if not ok:
|
||||
self.__logger.error(f"Failed to delete ban for {ip} from redis")
|
||||
try:
|
||||
ok = self.__redis.delete(f"bans_ip_{ip}")
|
||||
if not ok:
|
||||
self.__logger.error(f"Failed to delete ban for {ip} from redis")
|
||||
except Exception as e:
|
||||
self.__logger.error(f"Failed to delete ban for {ip} from redis: {e}")
|
||||
|
||||
if self.send_to_apis("POST", "/unban", data={"ip": ip}):
|
||||
return True, f"IP {ip} has been unbanned"
|
||||
return False, "error"
|
||||
return False, f"Failed to unban {ip}"
|
||||
|
||||
def ban(self, ip: str, exp: float, reason: str) -> Tuple[bool, str]:
|
||||
if self.__redis:
|
||||
ok = self.__redis.set(f"bans_ip_{ip}", dumps({"reason": reason, "date": time()}))
|
||||
if not ok:
|
||||
self.__logger.error(f"Failed to ban {ip} in redis")
|
||||
self.__redis.expire(f"bans_ip_{ip}", int(exp))
|
||||
try:
|
||||
ok = self.__redis.set(f"bans_ip_{ip}", dumps({"reason": reason, "date": time()}))
|
||||
if not ok:
|
||||
self.__logger.error(f"Failed to ban {ip} in redis")
|
||||
self.__redis.expire(f"bans_ip_{ip}", int(exp))
|
||||
except Exception as e:
|
||||
self.__logger.error(f"Failed to ban {ip} in redis: {e}")
|
||||
|
||||
if self.send_to_apis("POST", "/ban", data={"ip": ip, "exp": exp, "reason": reason}):
|
||||
return (True, f"IP {ip} has been banned for {format_remaining_time(exp)} with reason {reason}")
|
||||
return False, "error"
|
||||
return False, f"Failed to ban {ip}"
|
||||
|
||||
def bans(self) -> Tuple[bool, str]:
|
||||
servers = {}
|
||||
|
|
|
|||
|
|
@ -18,19 +18,26 @@ server {
|
|||
# HTTPS listen
|
||||
{% set os = import("os") %}
|
||||
{% if os.path.isfile("/var/cache/bunkerweb/default-server-cert/cert.pem") +%}
|
||||
{% if has_variable(all, "USE_CUSTOM_SSL", "yes") or has_variable(all, "AUTO_LETS_ENCRYPT", "yes") or has_variable(all, "GENERATE_SELF_SIGNED_SSL", "yes") +%}
|
||||
listen 0.0.0.0:{{ HTTPS_PORT }} ssl {% if HTTP2 == "yes" %}http2{% endif %} default_server {% if USE_PROXY_PROTOCOL == "yes" %}proxy_protocol{% endif %};
|
||||
{% if USE_IPV6 == "yes" +%}
|
||||
listen [::]:{{ HTTPS_PORT }} ssl {% if HTTP2 == "yes" %}http2{% endif %} default_server {% if USE_PROXY_PROTOCOL == "yes" %}proxy_protocol{% endif %};
|
||||
{% endif %}
|
||||
ssl_protocols {{ SSL_PROTOCOLS }};
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_tickets off;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:MozSSL: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 %}
|
||||
ssl_certificate /var/cache/bunkerweb/default-server-cert/cert.pem;
|
||||
ssl_certificate_key /var/cache/bunkerweb/default-server-cert/cert.key;
|
||||
listen 0.0.0.0:{{ HTTPS_PORT }} ssl {% if HTTP2 == "yes" %}http2{% endif %} default_server {% if USE_PROXY_PROTOCOL == "yes" %}proxy_protocol{% endif %};
|
||||
{% if USE_IPV6 == "yes" +%}
|
||||
listen [::]:{{ HTTPS_PORT }} ssl {% if HTTP2 == "yes" %}http2{% endif %} default_server {% if USE_PROXY_PROTOCOL == "yes" %}proxy_protocol{% endif %};
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if IS_LOADING == "yes" +%}
|
||||
root /usr/share/bunkerweb/loading;
|
||||
index index.html;
|
||||
try_files /index.html =404;
|
||||
{% endif %}
|
||||
|
||||
# include core and plugins default-server configurations
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ resolver {{ DNS_RESOLVERS }} {% if USE_IPV6 == "no" %}ipv6=off{% endif %};
|
|||
port_in_redirect off;
|
||||
|
||||
# lua configs
|
||||
lua_package_path "/usr/share/bunkerweb/lua/?.lua;/usr/share/bunkerweb/core/?.lua;/etc/bunkerweb/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;;";
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ init_by_lua_block {
|
|||
-- Load plugins into the datastore
|
||||
logger:log(NOTICE, "saving plugins into datastore ...")
|
||||
local plugins = {}
|
||||
local plugin_paths = { "/usr/share/bunkerweb/core", "/etc/bunkerweb/plugins" }
|
||||
local plugin_paths = { "/usr/share/bunkerweb/core", "/etc/bunkerweb/plugins", "/etc/bunkerweb/pro/plugins" }
|
||||
for i, plugin_path in ipairs(plugin_paths) do
|
||||
local paths = popen("find -L " .. plugin_path .. " -maxdepth 1 -type d ! -path " .. plugin_path)
|
||||
for path in paths:lines() do
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ init_by_lua_block {
|
|||
-- Load plugins into the datastore
|
||||
logger:log(NOTICE, "saving plugins into datastore ...")
|
||||
local plugins = {}
|
||||
local plugin_paths = { "/usr/share/bunkerweb/core", "/etc/bunkerweb/plugins" }
|
||||
local plugin_paths = { "/usr/share/bunkerweb/core", "/etc/bunkerweb/plugins", "/etc/bunkerweb/pro/plugins" }
|
||||
for i, plugin_path in ipairs(plugin_paths) do
|
||||
local paths = popen("find -L " .. plugin_path .. " -maxdepth 1 -type d ! -path " .. plugin_path)
|
||||
for path in paths:lines() do
|
||||
|
|
|
|||
|
|
@ -1,10 +1,90 @@
|
|||
lua_shared_dict worker_lock 16k;
|
||||
|
||||
init_worker_by_lua_block {
|
||||
-- Libs
|
||||
local helpers = require "bunkerweb.helpers"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local ngx = ngx
|
||||
local ERR = ngx.ERR
|
||||
local INFO = ngx.INFO
|
||||
local worker = ngx.worker
|
||||
local worker_id = worker.id
|
||||
local worker_pid = worker.pid
|
||||
local timer_at = ngx.timer.at
|
||||
local require_plugin = helpers.require_plugin
|
||||
local new_plugin = helpers.new_plugin
|
||||
local call_plugin = helpers.call_plugin
|
||||
local get_variable = utils.get_variable
|
||||
local tostring = tostring
|
||||
local time = os.time
|
||||
local randomseed = math.randomseed
|
||||
|
||||
-- Instantiate objects
|
||||
local logger = require "bunkerweb.logger":new("INIT-WORKERS-" .. tostring(worker_id()))
|
||||
local datastore = require "bunkerweb.datastore":new()
|
||||
|
||||
-- Random seed
|
||||
randomseed(time() + worker_pid())
|
||||
|
||||
-- Don't go further we are in loading state
|
||||
local is_loading, err = get_variable("IS_LOADING", false)
|
||||
if not is_loading then
|
||||
logger:log(ERR, "utils.get_variable() failed : " .. err)
|
||||
return
|
||||
elseif is_loading == "yes" then
|
||||
return
|
||||
end
|
||||
|
||||
logger:log(INFO, "init_workers phase started")
|
||||
|
||||
-- Get plugins order
|
||||
local order, err = datastore:get("plugins_order", true)
|
||||
if not order then
|
||||
logger:log(ERR, "can't get plugins order from datastore : " .. err)
|
||||
return
|
||||
end
|
||||
|
||||
-- Call init_workers() methods
|
||||
logger:log(INFO, "calling init_workers() methods of plugins ...")
|
||||
for i, plugin_id in ipairs(order.init_workers) do
|
||||
-- Require call
|
||||
local plugin_lua, err = require_plugin(plugin_id)
|
||||
if plugin_lua == false then
|
||||
logger:log(ERR, err)
|
||||
elseif plugin_lua == nil then
|
||||
logger:log(INFO, err)
|
||||
else
|
||||
-- Check if plugin has init_worker method
|
||||
if plugin_lua.init_workers ~= nil then
|
||||
-- New call
|
||||
local ok, plugin_obj = new_plugin(plugin_lua)
|
||||
if not ok then
|
||||
logger:log(ERR, plugin_obj)
|
||||
else
|
||||
local ok, ret = call_plugin(plugin_obj, "init_workers")
|
||||
if not ok then
|
||||
logger:log(ERR, ret)
|
||||
elseif not ret.ret then
|
||||
logger:log(ERR, plugin_id .. ":init_workers() call failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(INFO, plugin_id .. ":init_workers() call successful : " .. ret.msg)
|
||||
end
|
||||
end
|
||||
else
|
||||
logger:log(INFO, "skipped execution of " .. plugin_id .. " because method init_workers() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(INFO, "called init_workers() methods of plugins")
|
||||
|
||||
-- End
|
||||
logger:log(INFO, "init_workers phase ended")
|
||||
|
||||
-- Our timer function
|
||||
local ready_work = function(premature)
|
||||
-- Libs
|
||||
local helpers = require "bunkerweb.helpers"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
-- Instantiate objects
|
||||
local logger = require "bunkerweb.logger":new("INIT-WORKER")
|
||||
|
|
@ -22,9 +102,10 @@ init_worker_by_lua_block {
|
|||
local new_plugin = helpers.new_plugin
|
||||
local call_plugin = helpers.call_plugin
|
||||
local tostring = tostring
|
||||
local get_variable = utils.get_variable
|
||||
|
||||
-- Don't go further we are in loading state
|
||||
local is_loading, err = require "bunkerweb.utils".get_variable("IS_LOADING", false)
|
||||
local is_loading, err = get_variable("IS_LOADING", false)
|
||||
if not is_loading then
|
||||
logger:log(ERR, "utils.get_variable() failed : " .. err)
|
||||
return
|
||||
|
|
@ -34,6 +115,7 @@ init_worker_by_lua_block {
|
|||
|
||||
-- Recurrent timer
|
||||
local recurrent_timer
|
||||
local timers_log_level = ngx[utils.get_variable("TIMERS_LOG_LEVEL", false):upper()]
|
||||
recurrent_timer = function(premature)
|
||||
|
||||
local worker_id = tostring(worker.id())
|
||||
|
|
@ -44,7 +126,7 @@ init_worker_by_lua_block {
|
|||
logger:log(WARN, "worker is exiting, disabling timer")
|
||||
return
|
||||
end
|
||||
logger:log(INFO, "timer started")
|
||||
logger:log(timers_log_level, "timer started")
|
||||
|
||||
-- Get plugins order
|
||||
local order, err = datastore:get("plugins_order", true)
|
||||
|
|
@ -54,14 +136,14 @@ init_worker_by_lua_block {
|
|||
end
|
||||
|
||||
-- Call timer() methods
|
||||
logger:log(INFO, "calling timer() methods of plugins ...")
|
||||
logger:log(timers_log_level, "calling timer() methods of plugins ...")
|
||||
for i, plugin_id in ipairs(order.timer) do
|
||||
-- Require call
|
||||
local plugin_lua, err = require_plugin(plugin_id)
|
||||
if plugin_lua == false then
|
||||
logger:log(ERR, err)
|
||||
elseif plugin_lua == nil then
|
||||
logger:log(INFO, err)
|
||||
logger:log(timers_log_level, err)
|
||||
else
|
||||
-- Check if plugin has timer method
|
||||
if plugin_lua.timer ~= nil then
|
||||
|
|
@ -76,15 +158,15 @@ init_worker_by_lua_block {
|
|||
elseif not ret.ret then
|
||||
logger:log(ERR, plugin_id .. ":timer() call failed : " .. ret.msg)
|
||||
else
|
||||
logger:log(INFO, plugin_id .. ":timer() call successful : " .. ret.msg)
|
||||
logger:log(timers_log_level, plugin_id .. ":timer() call successful : " .. ret.msg)
|
||||
end
|
||||
end
|
||||
else
|
||||
logger:log(INFO, "skipped execution of " .. plugin_id .. " because method timer() is not defined")
|
||||
logger:log(timers_log_level, "skipped execution of " .. plugin_id .. " because method timer() is not defined")
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(INFO, "called timer() methods of plugins")
|
||||
logger:log(timers_log_level, "called timer() methods of plugins")
|
||||
local hdl
|
||||
hdl, err = timer_at(5, recurrent_timer)
|
||||
if not hdl then
|
||||
|
|
@ -189,5 +271,5 @@ init_worker_by_lua_block {
|
|||
end
|
||||
|
||||
-- Start timer
|
||||
ngx.timer.at(5, ready_work)
|
||||
timer_at(5, ready_work)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ access_by_lua_block {
|
|||
local INFO = ngx.INFO
|
||||
local WARN = ngx.WARN
|
||||
local NOTICE = ngx.NOTICE
|
||||
local HTTP_MOVED_TEMPORARILY = ngx.HTTP_MOVED_TEMPORARILY
|
||||
local fill_ctx = helpers.fill_ctx
|
||||
local save_ctx = helpers.save_ctx
|
||||
local require_plugin = helpers.require_plugin
|
||||
|
|
@ -99,7 +100,7 @@ access_by_lua_block {
|
|||
else
|
||||
logger:log(INFO, plugin_id .. ":access() call successful : " .. ret.msg)
|
||||
end
|
||||
if ret.status then
|
||||
if ret.status and not ret.redirect then
|
||||
if ret.status == get_deny_status() then
|
||||
set_reason(plugin_id, ret.data, ctx)
|
||||
logger:log(WARN, "denied access from " .. plugin_id .. " : " .. ret.msg)
|
||||
|
|
@ -109,6 +110,9 @@ access_by_lua_block {
|
|||
status = ret.status
|
||||
break
|
||||
elseif ret.redirect then
|
||||
if ret.status then
|
||||
status = ret.status
|
||||
end
|
||||
logger:log(NOTICE, plugin_id .. " redirect to " .. ret.redirect .. " : " .. ret.msg)
|
||||
redirect = ret.redirect
|
||||
break
|
||||
|
|
@ -141,7 +145,7 @@ access_by_lua_block {
|
|||
|
||||
-- Redirect if needed
|
||||
if redirect then
|
||||
return ngx_redirect(redirect)
|
||||
return ngx_redirect(redirect, status or HTTP_MOVED_TEMPORARILY)
|
||||
end
|
||||
|
||||
return true
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ server {
|
|||
set $reason_data '';
|
||||
set $ctx_ref '';
|
||||
|
||||
# include config files
|
||||
include {{ NGINX_PREFIX }}server-http/*.conf;
|
||||
|
||||
# include LUA files
|
||||
include {{ NGINX_PREFIX }}set-lua.conf;
|
||||
include {{ NGINX_PREFIX }}ssl-certificate-lua.conf;
|
||||
|
|
@ -30,6 +33,4 @@ server {
|
|||
include {{ NGINX_PREFIX }}header-lua.conf;
|
||||
include {{ NGINX_PREFIX }}log-lua.conf;
|
||||
|
||||
# include config files
|
||||
include {{ NGINX_PREFIX }}server-http/*.conf;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,12 +10,10 @@ 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 %}
|
||||
|
||||
{% if AUTO_LETS_ENCRYPT == "yes" or USE_CUSTOM_SSL == "yes" or GENERATE_SELF_SIGNED_SSL == "yes" %}
|
||||
listen 0.0.0.0:{{ HTTPS_PORT }} ssl {% if HTTP2 == "yes" %}http2{% endif %} {% if USE_PROXY_PROTOCOL == "yes" %}proxy_protocol{% endif %};
|
||||
{% if USE_IPV6 == "yes" +%}
|
||||
listen [::]:{{ HTTPS_PORT }} ssl {% if HTTP2 == "yes" %}http2{% endif %} {% if USE_PROXY_PROTOCOL == "yes" %}proxy_protocol{% endif %};
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
ssl_certificate_by_lua_block {
|
||||
local class = require "middleclass"
|
||||
|
|
@ -32,6 +30,7 @@ ssl_certificate_by_lua_block {
|
|||
local is_internal = ngx_req.is_internal
|
||||
local ERR = ngx.ERR
|
||||
local INFO = ngx.INFO
|
||||
local clear_certs = ssl.clear_certs
|
||||
local set_cert = ssl.set_cert
|
||||
local set_priv_key = ssl.set_priv_key
|
||||
local require_plugin = helpers.require_plugin
|
||||
|
|
@ -77,7 +76,11 @@ ssl_certificate_by_lua_block {
|
|||
logger:log(INFO, plugin_id .. ":ssl_certificate() call successful : " .. ret.msg)
|
||||
if ret.status then
|
||||
logger:log(INFO, plugin_id .. " is setting certificate/key : " .. ret.msg)
|
||||
local ok, err = set_cert(ret.status[1])
|
||||
local ok, err = clear_certs()
|
||||
if not ok then
|
||||
logger:log(ERR, "error while clearing certificates : " .. err)
|
||||
end
|
||||
ok, err = set_cert(ret.status[1])
|
||||
if not ok then
|
||||
logger:log(ERR, "error while setting certificate : " .. err)
|
||||
else
|
||||
|
|
|
|||
|
|
@ -20,11 +20,12 @@ server {
|
|||
set $ctx_ref '';
|
||||
set $server_name '{{ SERVER_NAME.split(" ")[0] }}';
|
||||
|
||||
# include config files
|
||||
include {{ NGINX_PREFIX }}server-stream/*.conf;
|
||||
|
||||
# include LUA files
|
||||
include {{ NGINX_PREFIX }}ssl-certificate-stream-lua.conf;
|
||||
include {{ NGINX_PREFIX }}preread-stream-lua.conf;
|
||||
include {{ NGINX_PREFIX }}log-stream-lua.conf;
|
||||
|
||||
# include config files
|
||||
include {{ NGINX_PREFIX }}server-stream/*.conf;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,12 +10,10 @@ 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 %}
|
||||
|
||||
{% if AUTO_LETS_ENCRYPT == "yes" or USE_CUSTOM_SSL == "yes" or GENERATE_SELF_SIGNED_SSL == "yes" %}
|
||||
listen 0.0.0.0:{{ LISTEN_STREAM_PORT_SSL }} ssl {% if USE_UDP == "yes" %} udp {% endif %}{% 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 %};
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
ssl_certificate_by_lua_block {
|
||||
local class = require "middleclass"
|
||||
|
|
|
|||
|
|
@ -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;/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;;";
|
||||
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;
|
||||
|
|
|
|||
3
src/common/core/antibot/confs/modsec-crs/antibot.conf
Normal file
3
src/common/core/antibot/confs/modsec-crs/antibot.conf
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{% if USE_ANTIBOT != "no" +%}
|
||||
SecRule REQUEST_FILENAME "@rx ^{{ ANTIBOT_URI }}$" "nolog,phase:4,allow,id:1010"
|
||||
{% endif %}
|
||||
|
|
@ -1,74 +1,44 @@
|
|||
{% extends "base.html" %} {% block content %}
|
||||
<input
|
||||
type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden
|
||||
/>
|
||||
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
{% if is_used %}
|
||||
<!-- info-->
|
||||
<div
|
||||
class="h-fit transition hover:scale-102 col-span-12 md:col-span-6 2xl:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
||||
<div
|
||||
class="h-fit dark:brightness-110 max-h-none sm:max-h-28 hover:scale-102 transition col-span-12 md:col-span-6 2xl:col-span-4 flex p-4 justify-between w-full shadow-md break-words bg-white dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p
|
||||
class="mb-2 font-sans text-sm font-semibold leading-normal uppercase dark:text-white dark:opacity-60"
|
||||
>
|
||||
Challenges
|
||||
</p>
|
||||
<h5 data-count class="mb-1 font-bold dark:text-white/90">"unknown"</h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5"
|
||||
>total failed
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div
|
||||
role="img"
|
||||
class="dark:brightness-90 inline-block w-12 h-12 text-center rounded-circle bg-sky-700"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="scale-75 leading-none text-lg relative fill-white"
|
||||
>
|
||||
<path
|
||||
d="M11.7 2.805a.75.75 0 0 1 .6 0A60.65 60.65 0 0 1 22.83 8.72a.75.75 0 0 1-.231 1.337 49.948 49.948 0 0 0-9.902 3.912l-.003.002c-.114.06-.227.119-.34.18a.75.75 0 0 1-.707 0A50.88 50.88 0 0 0 7.5 12.173v-.224c0-.131.067-.248.172-.311a54.615 54.615 0 0 1 4.653-2.52.75.75 0 0 0-.65-1.352 56.123 56.123 0 0 0-4.78 2.589 1.858 1.858 0 0 0-.859 1.228 49.803 49.803 0 0 0-4.634-1.527.75.75 0 0 1-.231-1.337A60.653 60.653 0 0 1 11.7 2.805Z"
|
||||
/>
|
||||
<path
|
||||
d="M13.06 15.473a48.45 48.45 0 0 1 7.666-3.282c.134 1.414.22 2.843.255 4.284a.75.75 0 0 1-.46.711 47.87 47.87 0 0 0-8.105 4.342.75.75 0 0 1-.832 0 47.87 47.87 0 0 0-8.104-4.342.75.75 0 0 1-.461-.71c.035-1.442.121-2.87.255-4.286.921.304 1.83.634 2.726.99v1.27a1.5 1.5 0 0 0-.14 2.508c-.09.38-.222.753-.397 1.11.452.213.901.434 1.346.66a6.727 6.727 0 0 0 .551-1.607 1.5 1.5 0 0 0 .14-2.67v-.645a48.549 48.549 0 0 1 3.44 1.667 2.25 2.25 0 0 0 2.12 0Z"
|
||||
/>
|
||||
<path
|
||||
d="M4.462 19.462c.42-.419.753-.89 1-1.395.453.214.902.435 1.347.662a6.742 6.742 0 0 1-1.286 1.794.75.75 0 0 1-1.06-1.06Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
|
||||
<script>
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<input type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden />
|
||||
<div class="core-layout">
|
||||
{% if is_used and is_metrics %}
|
||||
<!-- info-->
|
||||
<div class="core-card">
|
||||
<h5 class="core-card-info-title">INFO</h5>
|
||||
<div class="core-card-info-list">
|
||||
<p data-info class="core-card-info-text"></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
<div class="core-card-metrics">
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p class="core-card-metrics-name">Challenges</p>
|
||||
<h5 data-count class="core-card-title">"unknown"</h5>
|
||||
<p class="core-card-metrics-subtitle">
|
||||
<span class="core-card-metrics-subtitle-content error">total failed</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-metrics-svg-container blue">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="size-base core-card-metrics-svg">
|
||||
<path d="M11.7 2.805a.75.75 0 0 1 .6 0A60.65 60.65 0 0 1 22.83 8.72a.75.75 0 0 1-.231 1.337 49.948 49.948 0 0 0-9.902 3.912l-.003.002c-.114.06-.227.119-.34.18a.75.75 0 0 1-.707 0A50.88 50.88 0 0 0 7.5 12.173v-.224c0-.131.067-.248.172-.311a54.615 54.615 0 0 1 4.653-2.52.75.75 0 0 0-.65-1.352 56.123 56.123 0 0 0-4.78 2.589 1.858 1.858 0 0 0-.859 1.228 49.803 49.803 0 0 0-4.634-1.527.75.75 0 0 1-.231-1.337A60.653 60.653 0 0 1 11.7 2.805Z" />
|
||||
<path d="M13.06 15.473a48.45 48.45 0 0 1 7.666-3.282c.134 1.414.22 2.843.255 4.284a.75.75 0 0 1-.46.711 47.87 47.87 0 0 0-8.105 4.342.75.75 0 0 1-.832 0 47.87 47.87 0 0 0-8.104-4.342.75.75 0 0 1-.461-.71c.035-1.442.121-2.87.255-4.286.921.304 1.83.634 2.726.99v1.27a1.5 1.5 0 0 0-.14 2.508c-.09.38-.222.753-.397 1.11.452.213.901.434 1.346.66a6.727 6.727 0 0 0 .551-1.607 1.5 1.5 0 0 0 .14-2.67v-.645a48.549 48.549 0 0 1 3.44 1.667 2.25 2.25 0 0 0 2.12 0Z" />
|
||||
<path d="M4.462 19.462c.42-.419.753-.89 1-1.395.453.214.902.435 1.347.662a6.742 6.742 0 0 1-1.286 1.794.75.75 0 0 1-1.06-1.06Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<script nonce="{{script_nonce}}">
|
||||
// Use SetupPlugin class that is on static/js/plugins/setup.js
|
||||
const setPlugin = new SetupPlugin({
|
||||
info: {
|
||||
|
|
@ -82,47 +52,29 @@
|
|||
type: "text",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
{% else %}
|
||||
<div
|
||||
class="h-fit transition hover:scale-102 col-span-12 md:col-span-6 2xl:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">Deactivated</h5>
|
||||
<!-- icon -->
|
||||
<div
|
||||
role="img"
|
||||
class="dark:brightness-90 inline-block w-12 h-12 text-center rounded-circle bg-yellow-500"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="scale-75 leading-none text-lg relative fill-yellow-500 stroke-white"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</script>
|
||||
{% else %}
|
||||
<div class="core-card">
|
||||
<div class="core-card-wrap">
|
||||
<h5 class="core-card-deactivated-title">Deactivated</h5>
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-svg-container">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="core-card-deactivated-svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<div class="core-card-text-container">
|
||||
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center my-2">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
This plugin need to be activated to get metrics.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
from operator import itemgetter
|
||||
|
||||
|
||||
def badbehavior(**kwargs):
|
||||
try:
|
||||
# Here we will have a list { 'counter_403': X, 'counter_401': Y ... }
|
||||
data = kwargs["app"].config["INSTANCES"].get_metrics("badbehavior")
|
||||
format_data = []
|
||||
# Format to fit [{code: 403, count: X}, {code: 401, count: Y} ...]
|
||||
for key, value in data.items():
|
||||
format_data[key] = {"code": int(key.split("_")[1]), "count": value}
|
||||
|
||||
format_data = [{"code": int(key.split("_")[1]), "count": int(value)} for key, value in data.items()]
|
||||
format_data.sort(key=itemgetter("count"), reverse=True)
|
||||
return {"items": format_data}
|
||||
|
||||
except:
|
||||
return {"items": []}
|
||||
|
|
|
|||
|
|
@ -1,77 +1,44 @@
|
|||
{% extends "base.html" %} {% block content %}
|
||||
<input
|
||||
type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden
|
||||
/>
|
||||
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
{% if is_used %}
|
||||
<!-- info-->
|
||||
<div
|
||||
class="h-fit transition hover:scale-102 col-span-12 md:col-span-6 2xl:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
||||
<div
|
||||
data-fetch-success-show
|
||||
class="hidden 2xl:col-span-4 3xl:col-span-3 w-full md:max-w-[350px] overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 col-span-12 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="col-span-12 flex">
|
||||
<h5 class="mb-4 mt-2 font-bold dark:text-white/90 mx-2">
|
||||
BAD BEHAVIOR LIST
|
||||
</h5>
|
||||
</div>
|
||||
<div class="col-span-12 overflow-y-auto overflow-x-auto">
|
||||
<!-- list container-->
|
||||
<div class="min-w-[250px] w-full grid grid-cols-12 rounded p-2">
|
||||
<!-- header-->
|
||||
<p
|
||||
class="dark:text-gray-300 h-8 text-sm font-bold col-span-6 m-0 pb-2 border-b border-gray-400"
|
||||
>
|
||||
Error code
|
||||
</p>
|
||||
<p
|
||||
class="dark:text-gray-300 h-8 text-sm font-bold col-span-6 m-0 pb-2 border-b border-gray-400"
|
||||
>
|
||||
Count
|
||||
</p>
|
||||
|
||||
<!-- end header-->
|
||||
<!-- list -->
|
||||
<ul class="col-span-12 w-full">
|
||||
<li
|
||||
data-item
|
||||
class="items-center grid grid-cols-12 border-b border-gray-300 py-2.5"
|
||||
>
|
||||
<p
|
||||
data-name="code"
|
||||
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-6 m-0 my-1"
|
||||
></p>
|
||||
<p
|
||||
data-name="count"
|
||||
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-6 m-0 my-1"
|
||||
></p>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- end list-->
|
||||
</div>
|
||||
<!-- end list container-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<input type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden />
|
||||
<div class="core-layout">
|
||||
{% if is_used and is_metrics %}
|
||||
<!-- info-->
|
||||
<div class="core-card">
|
||||
<h5 class="core-card-title">INFO</h5>
|
||||
<div class="core-card-text-container">
|
||||
<p data-info class="core-card-text"></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
<div data-fetch-success-show class="hidden core-card-list w-small">
|
||||
<div class="core-card-list-title-container">
|
||||
<h5 class="core-card-list-title">BAD BEHAVIOR LIST</h5>
|
||||
</div>
|
||||
<div class="core-card-list-container">
|
||||
<!-- list container-->
|
||||
<div class="w-small core-card-list-wrap">
|
||||
<!-- header-->
|
||||
<p class="core-card-list-header col-span-6">Error code</p>
|
||||
<p class="core-card-list-header col-span-6">Count</p>
|
||||
<!-- end header-->
|
||||
<!-- list -->
|
||||
<ul class="col-span-12 w-full">
|
||||
<li data-item class="core-card-list-item col-span-6">
|
||||
<p data-name="code" class="core-card-list-item-content col-span-6"></p>
|
||||
<p data-name="count" class="core-card-list-item-content col-span-6"></p>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- end list-->
|
||||
</div>
|
||||
<!-- end list container-->
|
||||
</div>
|
||||
</div>
|
||||
<script nonce="{{script_nonce}}">
|
||||
// Use SetupPlugin class that is on static/js/plugins/setup.js
|
||||
const setPlugin = new SetupPlugin({
|
||||
info: {
|
||||
|
|
@ -86,46 +53,29 @@
|
|||
listNames: ["code", "count"],
|
||||
},
|
||||
});
|
||||
</script>
|
||||
{% else %}
|
||||
<div
|
||||
class="h-fit transition hover:scale-102 col-span-12 md:col-span-6 2xl:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">Deactivated</h5>
|
||||
<!-- icon -->
|
||||
<div
|
||||
role="img"
|
||||
class="dark:brightness-90 inline-block w-12 h-12 text-center rounded-circle bg-yellow-500"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="scale-75 leading-none text-lg relative fill-yellow-500 stroke-white"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</script>
|
||||
{% else %}
|
||||
<div class="core-card">
|
||||
<div class="core-card-wrap">
|
||||
<h5 class="core-card-title">Deactivated</h5>
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-svg-container">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="core-card-deactivated-svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<div class="core-card-text-container">
|
||||
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center my-2">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
This plugin need to be activated to get metrics.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ def blacklist(**kwargs):
|
|||
"counter_blacklist_ip",
|
||||
"counter_blacklist_rdns",
|
||||
"counter_blacklist_asn",
|
||||
"counter_blacklist_usa",
|
||||
"counter_blacklist_ua",
|
||||
]
|
||||
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -1,234 +1,130 @@
|
|||
{% extends "base.html" %} {% block content %}
|
||||
<input
|
||||
type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden
|
||||
/>
|
||||
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
{% if is_used %}
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
<!-- info-->
|
||||
<div
|
||||
class="h-fit transition hover:scale-102 col-span-12 md:col-span-6 2xl:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="h-fit dark:brightness-110 max-h-none sm:max-h-28 hover:scale-102 transition col-span-12 md:col-span-6 2xl:col-span-4 flex p-4 justify-between w-full shadow-md break-words bg-white dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p
|
||||
class="mb-2 font-sans text-sm font-semibold leading-normal uppercase dark:text-white dark:opacity-60"
|
||||
>
|
||||
URL
|
||||
</p>
|
||||
<h5 data-count-url class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5">
|
||||
denied
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div
|
||||
role="img"
|
||||
class="dark:brightness-90 inline-block w-12 h-12 text-center rounded-circle bg-red-600"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="scale-[0.6] leading-none text-lg relative fill-white"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M19.902 4.098a3.75 3.75 0 0 0-5.304 0l-4.5 4.5a3.75 3.75 0 0 0 1.035 6.037.75.75 0 0 1-.646 1.353 5.25 5.25 0 0 1-1.449-8.45l4.5-4.5a5.25 5.25 0 1 1 7.424 7.424l-1.757 1.757a.75.75 0 1 1-1.06-1.06l1.757-1.757a3.75 3.75 0 0 0 0-5.304Zm-7.389 4.267a.75.75 0 0 1 1-.353 5.25 5.25 0 0 1 1.449 8.45l-4.5 4.5a5.25 5.25 0 1 1-7.424-7.424l1.757-1.757a.75.75 0 1 1 1.06 1.06l-1.757 1.757a3.75 3.75 0 1 0 5.304 5.304l4.5-4.5a3.75 3.75 0 0 0-1.035-6.037.75.75 0 0 1-.354-1Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="h-fit dark:brightness-110 max-h-none sm:max-h-28 hover:scale-102 transition col-span-12 md:col-span-6 2xl:col-span-4 flex p-4 justify-between w-full shadow-md break-words bg-white dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p
|
||||
class="mb-2 font-sans text-sm font-semibold leading-normal uppercase dark:text-white dark:opacity-60"
|
||||
>
|
||||
IP
|
||||
</p>
|
||||
<h5 data-count-ip class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5">
|
||||
denied
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div
|
||||
role="img"
|
||||
class="dark:brightness-90 inline-block w-12 h-12 text-center rounded-circle bg-lime-600"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="scale-50 leading-none text-lg relative fill-white"
|
||||
>
|
||||
<path
|
||||
d="M3.53 2.47a.75.75 0 0 0-1.06 1.06l18 18a.75.75 0 1 0 1.06-1.06l-18-18ZM20.25 5.507v11.561L5.853 2.671c.15-.043.306-.075.467-.094a49.255 49.255 0 0 1 11.36 0c1.497.174 2.57 1.46 2.57 2.93ZM3.75 21V6.932l14.063 14.063L12 18.088l-7.165 3.583A.75.75 0 0 1 3.75 21Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="h-fit dark:brightness-110 max-h-none sm:max-h-28 hover:scale-102 transition col-span-12 md:col-span-6 2xl:col-span-4 flex p-4 justify-between w-full shadow-md break-words bg-white dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p
|
||||
class="mb-2 font-sans text-sm font-semibold leading-normal uppercase dark:text-white dark:opacity-60"
|
||||
>
|
||||
RDNS
|
||||
</p>
|
||||
<h5 data-count-rdns class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5">
|
||||
denied
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div
|
||||
role="img"
|
||||
class="dark:brightness-90 inline-block w-12 h-12 text-center rounded-circle bg-indigo-500"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="scale-[0.6] leading-none text-lg relative fill-white"
|
||||
>
|
||||
<path
|
||||
d="M11.625 16.5a1.875 1.875 0 1 0 0-3.75 1.875 1.875 0 0 0 0 3.75Z"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M5.625 1.5H9a3.75 3.75 0 0 1 3.75 3.75v1.875c0 1.036.84 1.875 1.875 1.875H16.5a3.75 3.75 0 0 1 3.75 3.75v7.875c0 1.035-.84 1.875-1.875 1.875H5.625a1.875 1.875 0 0 1-1.875-1.875V3.375c0-1.036.84-1.875 1.875-1.875Zm6 16.5c.66 0 1.277-.19 1.797-.518l1.048 1.048a.75.75 0 0 0 1.06-1.06l-1.047-1.048A3.375 3.375 0 1 0 11.625 18Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
<path
|
||||
d="M14.25 5.25a5.23 5.23 0 0 0-1.279-3.434 9.768 9.768 0 0 1 6.963 6.963A5.23 5.23 0 0 0 16.5 7.5h-1.875a.375.375 0 0 1-.375-.375V5.25Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="h-fit dark:brightness-110 max-h-none sm:max-h-28 hover:scale-102 transition col-span-12 md:col-span-6 2xl:col-span-4 flex p-4 justify-between w-full shadow-md break-words bg-white dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p
|
||||
class="mb-2 font-sans text-sm font-semibold leading-normal uppercase dark:text-white dark:opacity-60"
|
||||
>
|
||||
ASN
|
||||
</p>
|
||||
<h5 data-count-asn class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5">
|
||||
denied
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div
|
||||
role="img"
|
||||
class="dark:brightness-90 inline-block w-12 h-12 text-center rounded-circle bg-blue-700"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="scale-[0.6] leading-none text-lg relative fill-white"
|
||||
>
|
||||
<path
|
||||
d="M21.721 12.752a9.711 9.711 0 0 0-.945-5.003 12.754 12.754 0 0 1-4.339 2.708 18.991 18.991 0 0 1-.214 4.772 17.165 17.165 0 0 0 5.498-2.477ZM14.634 15.55a17.324 17.324 0 0 0 .332-4.647c-.952.227-1.945.347-2.966.347-1.021 0-2.014-.12-2.966-.347a17.515 17.515 0 0 0 .332 4.647 17.385 17.385 0 0 0 5.268 0ZM9.772 17.119a18.963 18.963 0 0 0 4.456 0A17.182 17.182 0 0 1 12 21.724a17.18 17.18 0 0 1-2.228-4.605ZM7.777 15.23a18.87 18.87 0 0 1-.214-4.774 12.753 12.753 0 0 1-4.34-2.708 9.711 9.711 0 0 0-.944 5.004 17.165 17.165 0 0 0 5.498 2.477ZM21.356 14.752a9.765 9.765 0 0 1-7.478 6.817 18.64 18.64 0 0 0 1.988-4.718 18.627 18.627 0 0 0 5.49-2.098ZM2.644 14.752c1.682.971 3.53 1.688 5.49 2.099a18.64 18.64 0 0 0 1.988 4.718 9.765 9.765 0 0 1-7.478-6.816ZM13.878 2.43a9.755 9.755 0 0 1 6.116 3.986 11.267 11.267 0 0 1-3.746 2.504 18.63 18.63 0 0 0-2.37-6.49ZM12 2.276a17.152 17.152 0 0 1 2.805 7.121c-.897.23-1.837.353-2.805.353-.968 0-1.908-.122-2.805-.353A17.151 17.151 0 0 1 12 2.276ZM10.122 2.43a18.629 18.629 0 0 0-2.37 6.49 11.266 11.266 0 0 1-3.746-2.504 9.754 9.754 0 0 1 6.116-3.985Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="h-fit dark:brightness-110 max-h-none sm:max-h-28 hover:scale-102 transition col-span-12 md:col-span-6 2xl:col-span-4 flex p-4 justify-between w-full shadow-md break-words bg-white dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p
|
||||
class="mb-2 font-sans text-sm font-semibold leading-normal uppercase dark:text-white dark:opacity-60"
|
||||
>
|
||||
User Agent
|
||||
</p>
|
||||
<h5 data-count-user-agent class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5">
|
||||
denied
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div
|
||||
role="img"
|
||||
class="dark:brightness-90 inline-block w-12 h-12 text-center rounded-circle bg-amber-500"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="scale-50 leading-none text-lg relative fill-white"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M7.5 6a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM3.751 20.105a8.25 8.25 0 0 1 16.498 0 .75.75 0 0 1-.437.695A18.683 18.683 0 0 1 12 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 0 1-.437-.695Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
|
||||
<script>
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<input type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden />
|
||||
<div class="core-layout">
|
||||
{% if is_used and is_metrics %}
|
||||
<div class="core-layout">
|
||||
<!-- info-->
|
||||
<div class="core-card">
|
||||
<h5 class="core-card-title">INFO</h5>
|
||||
<div class="core-card-text-container">
|
||||
<p data-info class="core-card-text"></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
</div>
|
||||
<div class="core-card-metrics">
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p class="core-card-metrics-name">URL</p>
|
||||
<h5 data-count-url class="core-card-title"></h5>
|
||||
<p class="core-card-metrics-subtitle">
|
||||
<span class="core-card-metrics-subtitle-content error">denied</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-svg-container red">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="size-medium core-card-metrics-svg">
|
||||
<path fill-rule="evenodd" d="M19.902 4.098a3.75 3.75 0 0 0-5.304 0l-4.5 4.5a3.75 3.75 0 0 0 1.035 6.037.75.75 0 0 1-.646 1.353 5.25 5.25 0 0 1-1.449-8.45l4.5-4.5a5.25 5.25 0 1 1 7.424 7.424l-1.757 1.757a.75.75 0 1 1-1.06-1.06l1.757-1.757a3.75 3.75 0 0 0 0-5.304Zm-7.389 4.267a.75.75 0 0 1 1-.353 5.25 5.25 0 0 1 1.449 8.45l-4.5 4.5a5.25 5.25 0 1 1-7.424-7.424l1.757-1.757a.75.75 0 1 1 1.06 1.06l-1.757 1.757a3.75 3.75 0 1 0 5.304 5.304l4.5-4.5a3.75 3.75 0 0 0-1.035-6.037.75.75 0 0 1-.354-1Z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<div class="core-card-metrics">
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p class="core-card-metrics-name">IP</p>
|
||||
<h5 data-count-ip class="core-card-title"></h5>
|
||||
<p class="core-card-metrics-subtitle">
|
||||
<span class="core-card-metrics-subtitle-content error">denied</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-svg-container lime">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="size-small core-card-metrics-svg">
|
||||
<path d="M3.53 2.47a.75.75 0 0 0-1.06 1.06l18 18a.75.75 0 1 0 1.06-1.06l-18-18ZM20.25 5.507v11.561L5.853 2.671c.15-.043.306-.075.467-.094a49.255 49.255 0 0 1 11.36 0c1.497.174 2.57 1.46 2.57 2.93ZM3.75 21V6.932l14.063 14.063L12 18.088l-7.165 3.583A.75.75 0 0 1 3.75 21Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<div class="core-card-metrics">
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p class="core-card-metrics-name">RDNS</p>
|
||||
<h5 data-count-rdns class="core-card-title"></h5>
|
||||
<p class="core-card-metrics-subtitle">
|
||||
<span class="core-card-metrics-subtitle-content error">denied</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-metrics-svg-container indigo">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="size-medium core-card-metrics-svg">
|
||||
<path d="M11.625 16.5a1.875 1.875 0 1 0 0-3.75 1.875 1.875 0 0 0 0 3.75Z" />
|
||||
<path fill-rule="evenodd" d="M5.625 1.5H9a3.75 3.75 0 0 1 3.75 3.75v1.875c0 1.036.84 1.875 1.875 1.875H16.5a3.75 3.75 0 0 1 3.75 3.75v7.875c0 1.035-.84 1.875-1.875 1.875H5.625a1.875 1.875 0 0 1-1.875-1.875V3.375c0-1.036.84-1.875 1.875-1.875Zm6 16.5c.66 0 1.277-.19 1.797-.518l1.048 1.048a.75.75 0 0 0 1.06-1.06l-1.047-1.048A3.375 3.375 0 1 0 11.625 18Z" clip-rule="evenodd" />
|
||||
<path d="M14.25 5.25a5.23 5.23 0 0 0-1.279-3.434 9.768 9.768 0 0 1 6.963 6.963A5.23 5.23 0 0 0 16.5 7.5h-1.875a.375.375 0 0 1-.375-.375V5.25Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<div class="core-card-metrics">
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p class="core-card-metrics-name">ASN</p>
|
||||
<h5 data-count-asn class="core-card-title"></h5>
|
||||
<p class="core-card-metrics-subtitle">
|
||||
<span class="core-card-metrics-subtitle-content error">denied</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-svg-container blue">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="size-medium core-card-metrics-svg">
|
||||
<path d="M21.721 12.752a9.711 9.711 0 0 0-.945-5.003 12.754 12.754 0 0 1-4.339 2.708 18.991 18.991 0 0 1-.214 4.772 17.165 17.165 0 0 0 5.498-2.477ZM14.634 15.55a17.324 17.324 0 0 0 .332-4.647c-.952.227-1.945.347-2.966.347-1.021 0-2.014-.12-2.966-.347a17.515 17.515 0 0 0 .332 4.647 17.385 17.385 0 0 0 5.268 0ZM9.772 17.119a18.963 18.963 0 0 0 4.456 0A17.182 17.182 0 0 1 12 21.724a17.18 17.18 0 0 1-2.228-4.605ZM7.777 15.23a18.87 18.87 0 0 1-.214-4.774 12.753 12.753 0 0 1-4.34-2.708 9.711 9.711 0 0 0-.944 5.004 17.165 17.165 0 0 0 5.498 2.477ZM21.356 14.752a9.765 9.765 0 0 1-7.478 6.817 18.64 18.64 0 0 0 1.988-4.718 18.627 18.627 0 0 0 5.49-2.098ZM2.644 14.752c1.682.971 3.53 1.688 5.49 2.099a18.64 18.64 0 0 0 1.988 4.718 9.765 9.765 0 0 1-7.478-6.816ZM13.878 2.43a9.755 9.755 0 0 1 6.116 3.986 11.267 11.267 0 0 1-3.746 2.504 18.63 18.63 0 0 0-2.37-6.49ZM12 2.276a17.152 17.152 0 0 1 2.805 7.121c-.897.23-1.837.353-2.805.353-.968 0-1.908-.122-2.805-.353A17.151 17.151 0 0 1 12 2.276ZM10.122 2.43a18.629 18.629 0 0 0-2.37 6.49 11.266 11.266 0 0 1-3.746-2.504 9.754 9.754 0 0 1 6.116-3.985Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<div class="core-card-metrics">
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p class="core-card-metrics-name">User Agent</p>
|
||||
<h5 data-count-user-agent class="core-card-title"></h5>
|
||||
<p class="core-card-metrics-subtitle">
|
||||
<span class="core-card-metrics-subtitle-content error">denied</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-svg-container amber">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="size-small core-card-metrics-svg">
|
||||
<path fill-rule="evenodd" d="M7.5 6a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM3.751 20.105a8.25 8.25 0 0 1 16.498 0 .75.75 0 0 1-.437.695A18.683 18.683 0 0 1 12 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 0 1-.437-.695Z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<script nonce="{{script_nonce}}">
|
||||
// Use SetupPlugin class that is on static/js/plugins/setup.js
|
||||
const setPlugin = new SetupPlugin({
|
||||
info: {
|
||||
|
|
@ -262,46 +158,29 @@
|
|||
type: "text",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
{% else %}
|
||||
<div
|
||||
class="h-fit transition hover:scale-102 col-span-12 md:col-span-6 2xl:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">Deactivated</h5>
|
||||
<!-- icon -->
|
||||
<div
|
||||
role="img"
|
||||
class="dark:brightness-90 inline-block w-12 h-12 text-center rounded-circle bg-yellow-500"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="scale-75 leading-none text-lg relative fill-yellow-500 stroke-white"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</script>
|
||||
{% else %}
|
||||
<div class="core-card">
|
||||
<div class="core-card-wrap">
|
||||
<h5 class="core-card-title">Deactivated</h5>
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-svg-container">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="core-card-deactivated-svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<div class="core-card-text-container">
|
||||
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center my-2">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
This plugin need to be activated to get metrics.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -204,9 +204,20 @@ function bunkernet:log(bypass_checks)
|
|||
end
|
||||
local ok, err
|
||||
-- luacheck: ignore 212 431
|
||||
local function report_callback(premature, obj, ip, reason, reason_data, method, url, headers, use_redis)
|
||||
local function report_callback(
|
||||
premature,
|
||||
obj,
|
||||
ip,
|
||||
reason,
|
||||
reason_data,
|
||||
method,
|
||||
url,
|
||||
headers,
|
||||
server_name,
|
||||
use_redis
|
||||
)
|
||||
local status, _
|
||||
ok, err, status, _ = obj:report(ip, reason, reason_data, method, url, headers)
|
||||
ok, err, status, _ = obj:report(ip, reason, reason_data, method, url, headers, server_name)
|
||||
if status == 429 then
|
||||
obj.logger:log(WARN, "bunkernet API is rate limiting us")
|
||||
elseif not ok then
|
||||
|
|
@ -229,7 +240,8 @@ function bunkernet:log(bypass_checks)
|
|||
reason_data,
|
||||
self.ctx.bw.request_method,
|
||||
self.ctx.bw.request_uri,
|
||||
ngx.req.get_headers()
|
||||
ngx.req.get_headers(),
|
||||
self.ctx.bw.server_name
|
||||
)
|
||||
if not hdr then
|
||||
return self:ret(false, "can't create report timer : " .. err)
|
||||
|
|
@ -263,10 +275,48 @@ function bunkernet:request(method, url, data)
|
|||
if not httpc then
|
||||
return false, "can't instantiate http object : " .. err
|
||||
end
|
||||
|
||||
local os_data = {
|
||||
name = "Linux",
|
||||
version = "Unknown",
|
||||
version_id = "Unknown",
|
||||
version_codename = "Unknown",
|
||||
id = "Unknown",
|
||||
arch = "Unknown",
|
||||
}
|
||||
local uname_cmd = io.popen("uname -m")
|
||||
if uname_cmd then
|
||||
os_data.arch = uname_cmd:read("*a"):gsub("\n", "")
|
||||
uname_cmd:close()
|
||||
end
|
||||
|
||||
local file = io.open("/etc/os-release", "r")
|
||||
if file then
|
||||
for line in file:lines() do
|
||||
local key, value = line:match("^(%w+)=(.+)$")
|
||||
if key and value then
|
||||
value = value:gsub('"', "")
|
||||
if key == "NAME" then
|
||||
os_data.name = value
|
||||
elseif key == "VERSION" then
|
||||
os_data.version = value
|
||||
elseif key == "VERSION_ID" then
|
||||
os_data.version_id = value
|
||||
elseif key == "VERSION_CODENAME" then
|
||||
os_data.version_codename = value
|
||||
elseif key == "ID" then
|
||||
os_data.id = value
|
||||
end
|
||||
end
|
||||
end
|
||||
file:close()
|
||||
end
|
||||
|
||||
local all_data = {
|
||||
id = self.bunkernet_id,
|
||||
version = self.version,
|
||||
integration = self.integration,
|
||||
os = os_data,
|
||||
}
|
||||
if data then
|
||||
for k, v in pairs(data) do
|
||||
|
|
@ -299,7 +349,7 @@ function bunkernet:ping()
|
|||
return self:request("GET", "/ping", {})
|
||||
end
|
||||
|
||||
function bunkernet:report(ip, reason, reason_data, method, url, headers)
|
||||
function bunkernet:report(ip, reason, reason_data, method, url, headers, server_name)
|
||||
local data = {
|
||||
ip = ip,
|
||||
reason = reason,
|
||||
|
|
@ -307,6 +357,7 @@ function bunkernet:report(ip, reason, reason_data, method, url, headers)
|
|||
method = method,
|
||||
url = url,
|
||||
headers = headers,
|
||||
server_name = server_name,
|
||||
}
|
||||
return self:request("POST", "/report", data)
|
||||
end
|
||||
|
|
@ -317,9 +368,15 @@ function bunkernet:api()
|
|||
return self:ret(false, "success")
|
||||
end
|
||||
-- Check id
|
||||
if not self.bunkernet_id then
|
||||
local id, err_id = self.datastore:get("plugin_bunkernet_id", true)
|
||||
if not id and err_id ~= "not found" then
|
||||
return self:ret(true, "error while getting bunkernet id : " .. err_id, HTTP_INTERNAL_SERVER_ERROR)
|
||||
elseif not id then
|
||||
return self:ret(true, "missing instance ID", HTTP_INTERNAL_SERVER_ERROR)
|
||||
end
|
||||
self.bunkernet_id = id
|
||||
self.version = get_version(self.ctx)
|
||||
self.integration = get_integration(self.ctx)
|
||||
-- Send ping request
|
||||
local ok, err, status, _ = self:ping()
|
||||
if not ok then
|
||||
|
|
|
|||
|
|
@ -5,12 +5,19 @@ from pathlib import Path
|
|||
from requests import request as requests_request, ReadTimeout
|
||||
from typing import Literal, Optional, Tuple, Union
|
||||
|
||||
from jobs import get_os_info, get_integration, get_version # type: ignore
|
||||
|
||||
|
||||
def request(method: Union[Literal["POST"], Literal["GET"]], url: str, _id: Optional[str] = None) -> Tuple[bool, Optional[int], Union[str, dict]]:
|
||||
data = {"integration": get_integration(), "version": get_version()}
|
||||
headers = {"User-Agent": f"BunkerWeb/{get_version()}"}
|
||||
if _id is not None:
|
||||
data = {
|
||||
"integration": get_integration(),
|
||||
"version": get_version(),
|
||||
"os": get_os_info(),
|
||||
}
|
||||
if _id:
|
||||
data["id"] = _id
|
||||
|
||||
headers = {"User-Agent": f"BunkerWeb/{data['version']}"}
|
||||
try:
|
||||
resp = requests_request(
|
||||
method,
|
||||
|
|
@ -50,27 +57,3 @@ def data() -> Tuple[bool, Optional[int], Union[str, dict]]:
|
|||
|
||||
def get_id() -> str:
|
||||
return Path(sep, "var", "cache", "bunkerweb", "bunkernet", "instance.id").read_text(encoding="utf-8").strip()
|
||||
|
||||
|
||||
def get_version() -> str:
|
||||
return Path(sep, "usr", "share", "bunkerweb", "VERSION").read_text(encoding="utf-8").strip()
|
||||
|
||||
|
||||
def get_integration() -> str:
|
||||
try:
|
||||
integration_path = Path(sep, "usr", "share", "bunkerweb", "INTEGRATION")
|
||||
os_release_path = Path(sep, "etc", "os-release")
|
||||
if getenv("KUBERNETES_MODE", "no").lower() == "yes":
|
||||
return "kubernetes"
|
||||
elif getenv("SWARM_MODE", "no").lower() == "yes":
|
||||
return "swarm"
|
||||
elif getenv("AUTOCONF_MODE", "no").lower() == "yes":
|
||||
return "autoconf"
|
||||
elif integration_path.is_file():
|
||||
return integration_path.read_text(encoding="utf-8").strip().lower()
|
||||
elif os_release_path.is_file() and "Alpine" in os_release_path.read_text(encoding="utf-8"):
|
||||
return "docker"
|
||||
|
||||
return "linux"
|
||||
except:
|
||||
return "unknown"
|
||||
|
|
|
|||
|
|
@ -1,53 +1,34 @@
|
|||
{% extends "base.html" %} {% block content %}
|
||||
<input
|
||||
type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden
|
||||
/>
|
||||
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
{% if is_used %}
|
||||
|
||||
<!-- info-->
|
||||
<div
|
||||
class="h-fit transition hover:scale-102 col-span-12 md:col-span-6 2xl:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
||||
<div
|
||||
class="col-span-12 md:col-span-6 2xl:col-span-3 3xl:col-span-2 w-fit h-fit transition hover:scale-102 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="mx-1 flex justify-start items-center">
|
||||
<h5 class="mb-0 font-bold dark:text-white/90 mr-4">STATUS</h5>
|
||||
<svg
|
||||
data-status-svg
|
||||
class="w-6 h-6"
|
||||
viewBox="0 0 100 100"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle cx="50" cy="50" r="50" />
|
||||
</svg>
|
||||
</div>
|
||||
<p
|
||||
data-status-text
|
||||
class="mx-1 transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
></p>
|
||||
</div>
|
||||
|
||||
<!-- end status -->
|
||||
|
||||
<script>
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<input type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden />
|
||||
<div class="core-layout">
|
||||
{% if is_used %}
|
||||
<!-- info-->
|
||||
<div class="core-card">
|
||||
<h5 class="core-card-title">INFO</h5>
|
||||
<div class="core-card-text-container">
|
||||
<p data-info class="core-card-text"></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
<div class="core-card-status">
|
||||
<div class="core-card-status-container">
|
||||
<h5 class="core-card-status-title">STATUS</h5>
|
||||
<svg data-status-svg
|
||||
class="core-card-status-svg info"
|
||||
viewBox="0 0 100 100"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="50" cy="50" r="50" />
|
||||
</svg>
|
||||
</div>
|
||||
<p data-status-text class="core-card-text"></p>
|
||||
</div>
|
||||
<!-- end status -->
|
||||
<script nonce="{{script_nonce}}">
|
||||
// Use SetupPlugin class that is on static/js/plugins/setup.js
|
||||
const setPlugin = new SetupPlugin({
|
||||
info: {
|
||||
|
|
@ -63,48 +44,29 @@
|
|||
textEl: document.querySelector("[data-status-text]"),
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
{% else %}
|
||||
<div
|
||||
class="h-fit transition hover:scale-102 col-span-12 md:col-span-6 2xl:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">Deactivated</h5>
|
||||
<!-- icon -->
|
||||
<div
|
||||
role="img"
|
||||
class="dark:brightness-90 inline-block w-12 h-12 text-center rounded-circle bg-yellow-500"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="scale-75 leading-none text-lg relative fill-yellow-500 stroke-white"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</script>
|
||||
{% else %}
|
||||
<div class="core-card">
|
||||
<div class="core-card-wrap">
|
||||
<h5 class="core-card-title">Deactivated</h5>
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-svg-container">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="core-card-deactivated-svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<div class="core-card-text-container">
|
||||
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center my-2">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
This plugin need to be activated to get metrics.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1,73 +1,44 @@
|
|||
{% extends "base.html" %} {% block content %}
|
||||
|
||||
<input
|
||||
type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden
|
||||
/>
|
||||
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
{% if is_used %}
|
||||
<!-- info-->
|
||||
<div
|
||||
class="h-fit transition hover:scale-102 col-span-12 md:col-span-6 2xl:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
||||
<div
|
||||
class="h-fit dark:brightness-110 max-h-none sm:max-h-28 hover:scale-102 transition col-span-12 md:col-span-6 2xl:col-span-4 flex p-4 justify-between w-full shadow-md break-words bg-white dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p
|
||||
class="mb-2 font-sans text-sm font-semibold leading-normal uppercase dark:text-white dark:opacity-60"
|
||||
>
|
||||
CORS
|
||||
</p>
|
||||
<h5 data-count class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5"
|
||||
>request blocked</span
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div
|
||||
role="img"
|
||||
class="dark:brightness-90 inline-block w-12 h-12 text-center rounded-circle bg-red-700"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="scale-75 leading-none text-lg relative fill-red-700 stroke-white"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M18.364 18.364A9 9 0 0 0 5.636 5.636m12.728 12.728A9 9 0 0 1 5.636 5.636m12.728 12.728L5.636 5.636"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
|
||||
<script>
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<input type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden />
|
||||
<div class="core-layout">
|
||||
{% if is_used and is_metrics %}
|
||||
<!-- info-->
|
||||
<div class="core-card">
|
||||
<h5 class="core-card-title">INFO</h5>
|
||||
<div class="core-card-text-container">
|
||||
<p data-info class="core-card-text"></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
<div class="core-card-metrics">
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p class="core-card-metrics-name">CORS</p>
|
||||
<h5 data-count class="core-card-title"></h5>
|
||||
<p class="core-card-metrics-subtitle">
|
||||
<span class="core-card-metrics-subtitle-content error">request blocked</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-svg-container red">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="scale-75 leading-none text-lg relative fill-red-700 stroke-white">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 0 0 5.636 5.636m12.728 12.728A9 9 0 0 1 5.636 5.636m12.728 12.728L5.636 5.636" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<script nonce="{{script_nonce}}">
|
||||
// Use SetupPlugin class that is on static/js/plugins/setup.js
|
||||
const setPlugin = new SetupPlugin({
|
||||
info: {
|
||||
|
|
@ -81,46 +52,29 @@
|
|||
type: "text",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
{% else %}
|
||||
<div
|
||||
class="h-fit transition hover:scale-102 col-span-12 md:col-span-6 2xl:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">Deactivated</h5>
|
||||
<!-- icon -->
|
||||
<div
|
||||
role="img"
|
||||
class="dark:brightness-90 inline-block w-12 h-12 text-center rounded-circle bg-yellow-500"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="scale-75 leading-none text-lg relative fill-yellow-500 stroke-white"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</script>
|
||||
{% else %}
|
||||
<div class="core-card">
|
||||
<div class="core-card-wrap">
|
||||
<h5 class="core-card-title">Deactivated</h5>
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-svg-container">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="core-card-deactivated-svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<div class="core-card-text-container">
|
||||
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center my-2">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
This plugin need to be activated to get metrics.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1,71 +1,44 @@
|
|||
{% extends "base.html" %} {% block content %}
|
||||
<input
|
||||
type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden
|
||||
/>
|
||||
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
<!-- info-->
|
||||
<div
|
||||
class="h-fit transition hover:scale-102 col-span-12 md:col-span-6 2xl:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
||||
<div
|
||||
class="h-fit dark:brightness-110 max-h-none sm:max-h-28 hover:scale-102 transition col-span-12 md:col-span-6 2xl:col-span-4 flex p-4 justify-between w-full shadow-md break-words bg-white dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p
|
||||
class="mb-2 font-sans text-sm font-semibold leading-normal uppercase dark:text-white dark:opacity-60"
|
||||
>
|
||||
Country
|
||||
</p>
|
||||
<h5 data-count class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5"
|
||||
>request blocked</span
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div
|
||||
role="img"
|
||||
class="dark:brightness-90 inline-block w-12 h-12 text-center rounded-circle bg-red-700"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="scale-75 leading-none text-lg relative fill-red-700 stroke-white"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M18.364 18.364A9 9 0 0 0 5.636 5.636m12.728 12.728A9 9 0 0 1 5.636 5.636m12.728 12.728L5.636 5.636"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
|
||||
<script>
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<input type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden />
|
||||
<div class="core-layout">
|
||||
{% if is_used and is_metrics %}
|
||||
<!-- info-->
|
||||
<div class="core-card">
|
||||
<h5 class="core-card-title">INFO</h5>
|
||||
<div class="core-card-text-container">
|
||||
<p data-info class="core-card-text"></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
<div class="core-card-metrics">
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p class="core-card-metrics-name">Country</p>
|
||||
<h5 data-count class="core-card-title"></h5>
|
||||
<p class="core-card-metrics-subtitle">
|
||||
<span class="core-card-metrics-subtitle-content error">request blocked</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-svg-container red">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="scale-75 leading-none text-lg relative fill-red-700 stroke-white">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 0 0 5.636 5.636m12.728 12.728A9 9 0 0 1 5.636 5.636m12.728 12.728L5.636 5.636" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<script nonce="{{script_nonce}}">
|
||||
// Use SetupPlugin class that is on static/js/plugins/setup.js
|
||||
const setPlugin = new SetupPlugin({
|
||||
info: {
|
||||
|
|
@ -79,6 +52,29 @@
|
|||
type: "text",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</script>
|
||||
{% else %}
|
||||
<div class="core-card">
|
||||
<div class="core-card-wrap">
|
||||
<h5 class="core-card-title">Deactivated</h5>
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-svg-container">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="core-card-deactivated-svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<div class="core-card-text-container">
|
||||
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,14 @@ function customcert:initialize(ctx)
|
|||
plugin.initialize(self, "customcert", ctx)
|
||||
end
|
||||
|
||||
function customcert:set()
|
||||
local https_configured = self.variables["USE_CUSTOM_SSL"]
|
||||
if https_configured == "yes" then
|
||||
self.ctx.bw.https_configured = "yes"
|
||||
end
|
||||
return self:ret(true, "set https_configured to " .. https_configured)
|
||||
end
|
||||
|
||||
function customcert:init()
|
||||
local ret_ok, ret_err = true, "success"
|
||||
if has_variable("USE_CUSTOM_SSL", "yes") then
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
def db():
|
||||
return {
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"info": "test",
|
||||
"driver": "SQLite",
|
||||
"version": "13.2",
|
||||
"size": "14.8",
|
||||
},
|
||||
}
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
{% extends "base.html" %} {% block content %}
|
||||
<input
|
||||
type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden
|
||||
/>
|
||||
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
<!-- info-->
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
<div
|
||||
class="h-fit transition hover:scale-102 col-span-12 md:col-span-6 2xl:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- driver-->
|
||||
<div
|
||||
class="h-fit transition hover:scale-102 col-span-12 md:col-span-6 2xl:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">DB</h5>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center mt-4">
|
||||
<p
|
||||
class="transition duration-300 ease-in-out font-bold mb-0 font-sans text-sm leading-normal uppercase dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
DRIVER
|
||||
<span
|
||||
data-driver
|
||||
class="ml-1 font-semibold transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="mx-1 flex justify-start items-center mt-1 mb-4">
|
||||
<p
|
||||
class="transition duration-300 ease-in-out font-bold mb-0 font-sans text-sm leading-normal uppercase dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
VERSION
|
||||
<span
|
||||
data-version
|
||||
class="ml-1 font-semibold transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end driver -->
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
||||
<div
|
||||
class="h-fit dark:brightness-110 max-h-none sm:max-h-28 hover:scale-102 transition col-span-12 md:col-span-6 2xl:col-span-4 flex p-4 justify-between w-full shadow-md break-words bg-white dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p
|
||||
class="mb-2 font-sans text-sm font-semibold leading-normal uppercase dark:text-white dark:opacity-60"
|
||||
>
|
||||
SIZE
|
||||
</p>
|
||||
<h5 data-size class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-sky-500 mx-0.5">
|
||||
MB used on database
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div
|
||||
role="img"
|
||||
class="dark:brightness-90 inline-block w-12 h-12 text-center rounded-circle bg-sky-700"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="scale-[0.6] leading-none text-lg relative fill-white"
|
||||
>
|
||||
<path
|
||||
d="M21 6.375c0 2.692-4.03 4.875-9 4.875S3 9.067 3 6.375 7.03 1.5 12 1.5s9 2.183 9 4.875Z"
|
||||
/>
|
||||
<path
|
||||
d="M12 12.75c2.685 0 5.19-.586 7.078-1.609a8.283 8.283 0 0 0 1.897-1.384c.016.121.025.244.025.368C21 12.817 16.97 15 12 15s-9-2.183-9-4.875c0-.124.009-.247.025-.368a8.285 8.285 0 0 0 1.897 1.384C6.809 12.164 9.315 12.75 12 12.75Z"
|
||||
/>
|
||||
<path
|
||||
d="M12 16.5c2.685 0 5.19-.586 7.078-1.609a8.282 8.282 0 0 0 1.897-1.384c.016.121.025.244.025.368 0 2.692-4.03 4.875-9 4.875s-9-2.183-9-4.875c0-.124.009-.247.025-.368a8.284 8.284 0 0 0 1.897 1.384C6.809 15.914 9.315 16.5 12 16.5Z"
|
||||
/>
|
||||
<path
|
||||
d="M12 20.25c2.685 0 5.19-.586 7.078-1.609a8.282 8.282 0 0 0 1.897-1.384c.016.121.025.244.025.368 0 2.692-4.03 4.875-9 4.875s-9-2.183-9-4.875c0-.124.009-.247.025-.368a8.284 8.284 0 0 0 1.897 1.384C6.809 19.664 9.315 20.25 12 20.25Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Use SetupPlugin class that is on static/js/plugins/setup.js
|
||||
const setPlugin = new SetupPlugin({
|
||||
info: {
|
||||
el: document.querySelector("[data-info]"),
|
||||
value: "{{ plugin['description'] or ''}}",
|
||||
type: "text",
|
||||
},
|
||||
driver: {
|
||||
el: document.querySelector("[data-driver]"),
|
||||
value: "",
|
||||
type: "text",
|
||||
},
|
||||
version: {
|
||||
el: document.querySelector("[data-version]"),
|
||||
value: "",
|
||||
type: "text",
|
||||
},
|
||||
size: {
|
||||
el: document.querySelector("[data-size]"),
|
||||
value: "",
|
||||
type: "text",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
@ -1,72 +1,44 @@
|
|||
{% extends "base.html" %} {% block content %}
|
||||
<input
|
||||
type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden
|
||||
/>
|
||||
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
{% if is_used %}
|
||||
<!-- info-->
|
||||
<div
|
||||
class="h-fit transition hover:scale-102 col-span-12 md:col-span-4 2xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
||||
<div
|
||||
class="h-fit dark:brightness-110 max-h-none sm:max-h-28 hover:scale-102 transition col-span-12 md:col-span-6 2xl:col-span-4 flex p-4 justify-between w-full shadow-md break-words bg-white dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p
|
||||
class="mb-2 font-sans text-sm font-semibold leading-normal uppercase dark:text-white dark:opacity-60"
|
||||
>
|
||||
DNSBL
|
||||
</p>
|
||||
<h5 data-count class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5"
|
||||
>request blocked</span
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div
|
||||
role="img"
|
||||
class="dark:brightness-90 inline-block w-12 h-12 text-center rounded-circle bg-red-700"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="scale-75 leading-none text-lg relative fill-red-700 stroke-white"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M18.364 18.364A9 9 0 0 0 5.636 5.636m12.728 12.728A9 9 0 0 1 5.636 5.636m12.728 12.728L5.636 5.636"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
|
||||
<script>
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<input type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden />
|
||||
<div class="core-layout">
|
||||
{% if is_used and is_metrics %}
|
||||
<!-- info-->
|
||||
<div class="core-card">
|
||||
<h5 class="core-card-title">INFO</h5>
|
||||
<div class="core-card-text-container">
|
||||
<p data-info class="core-card-text"></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
<div class="core-card-metrics">
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p class="core-card-metrics-name">DNSBL</p>
|
||||
<h5 data-count class="core-card-title"></h5>
|
||||
<p class="core-card-metrics-subtitle">
|
||||
<span class="core-card-metrics-subtitle-content error">request blocked</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-svg-container red">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="scale-75 leading-none text-lg relative fill-red-700 stroke-white">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 0 0 5.636 5.636m12.728 12.728A9 9 0 0 1 5.636 5.636m12.728 12.728L5.636 5.636" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<script nonce="{{script_nonce}}">
|
||||
// Use SetupPlugin class that is on static/js/plugins/setup.js
|
||||
const setPlugin = new SetupPlugin({
|
||||
info: {
|
||||
|
|
@ -80,47 +52,29 @@
|
|||
type: "text",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
{% else %}
|
||||
<div
|
||||
class="h-fit transition hover:scale-102 col-span-12 md:col-span-6 2xl:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">Deactivated</h5>
|
||||
<!-- icon -->
|
||||
<div
|
||||
role="img"
|
||||
class="dark:brightness-90 inline-block w-12 h-12 text-center rounded-circle bg-yellow-500"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="scale-75 leading-none text-lg relative fill-yellow-500 stroke-white"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</script>
|
||||
{% else %}
|
||||
<div class="core-card">
|
||||
<div class="core-card-wrap">
|
||||
<h5 class="core-card-title">Deactivated</h5>
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-svg-container">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="core-card-deactivated-svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<div class="core-card-text-container">
|
||||
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center my-2">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
This plugin need to be activated to get metrics.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
from operator import itemgetter
|
||||
|
||||
|
||||
def errors(**kwargs):
|
||||
try:
|
||||
# Here we will have a list { 'counter_403': X, 'counter_401': Y ... }
|
||||
data = kwargs["app"].config["INSTANCES"].get_metrics("errors")
|
||||
format_data = []
|
||||
# Format to fit [{code: 403, count: X}, {code: 401, count: Y} ...]
|
||||
for key, value in data.items():
|
||||
format_data[key] = {"code": int(key.split("_")[1]), "count": value}
|
||||
|
||||
format_data = [{"code": int(key.split("_")[1]), "count": int(value)} for key, value in data.items()]
|
||||
format_data.sort(key=itemgetter("count"), reverse=True)
|
||||
return {"items": format_data}
|
||||
|
||||
except:
|
||||
return {"items": []}
|
||||
|
|
|
|||
|
|
@ -1,73 +1,43 @@
|
|||
{% extends "base.html" %} {% block content %}
|
||||
<input
|
||||
type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden
|
||||
/>
|
||||
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
<!-- info-->
|
||||
<div
|
||||
class="h-fit transition hover:scale-102 col-span-12 md:col-span-4 2xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
||||
<div
|
||||
data-fetch-success-show
|
||||
class="hidden col-span-12 md:col-span-8 3xl:col-span-9 w-full md:max-w-[400px] overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="col-span-12">
|
||||
<h5 class="mb-4 mt-2 font-bold dark:text-white/90 mx-2">ERRORS LIST</h5>
|
||||
</div>
|
||||
<div class="col-span-12 overflow-y-auto overflow-x-auto">
|
||||
<!-- list container-->
|
||||
<div class="min-w-[350px] w-full grid grid-cols-12 rounded p-2">
|
||||
<!-- header-->
|
||||
<p
|
||||
class="dark:text-gray-300 h-8 text-sm font-bold col-span-8 m-0 pb-2 border-b border-gray-400"
|
||||
>
|
||||
Code error
|
||||
</p>
|
||||
<p
|
||||
class="dark:text-gray-300 h-8 text-sm font-bold col-span-4 m-0 pb-2 border-b border-gray-400"
|
||||
>
|
||||
Count
|
||||
</p>
|
||||
<!-- end header-->
|
||||
<!-- list -->
|
||||
<ul class="col-span-12 w-full">
|
||||
<li
|
||||
data-item
|
||||
class="items-center grid grid-cols-12 border-b border-gray-300 py-2.5"
|
||||
>
|
||||
<p
|
||||
data-name="code"
|
||||
class="ml-1 dark:text-gray-400 dark:opacity-80 text-sm col-span-8 m-0 my-1"
|
||||
></p>
|
||||
<p
|
||||
data-name="count"
|
||||
class="ml-1 dark:text-gray-400 dark:opacity-80 text-sm col-span-4 m-0 my-1"
|
||||
></p>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- end list-->
|
||||
</div>
|
||||
<!-- end list container-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<input type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden />
|
||||
<div class="core-layout">
|
||||
<!-- info-->
|
||||
<div class="core-card">
|
||||
<h5 class="core-card-title">INFO</h5>
|
||||
<div class="core-card-text-container">
|
||||
<p data-info class="core-card-text"></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
<div data-fetch-success-show class="hidden core-card-list w-medium">
|
||||
<div class="core-card-list-container">
|
||||
<h5 class="core-card-list-title">ERRORS LIST</h5>
|
||||
</div>
|
||||
<div class="core-card-list-container">
|
||||
<!-- list container-->
|
||||
<div class="core-card-list-wrap w-medium">
|
||||
<!-- header-->
|
||||
<p class="core-card-list-header col-span-8">Code error</p>
|
||||
<p class="core-card-list-header col-span-4">Count</p>
|
||||
<!-- end header-->
|
||||
<!-- list -->
|
||||
<ul class="col-span-12 w-full">
|
||||
<li data-item class="core-card-list-item">
|
||||
<p data-name="code" class="core-card-list-item-content col-span-8"></p>
|
||||
<p data-name="count" class="core-card-list-item-content col-span-4"></p>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- end list-->
|
||||
</div>
|
||||
<!-- end list container-->
|
||||
</div>
|
||||
</div>
|
||||
<script nonce="{{script_nonce}}">
|
||||
// Use SetupPlugin class that is on static/js/plugins/setup.js
|
||||
const setPlugin = new SetupPlugin({
|
||||
info: {
|
||||
|
|
@ -82,7 +52,6 @@
|
|||
listNames: ["code", "count"],
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
|
||||
</script>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1,72 +1,44 @@
|
|||
{% extends "base.html" %} {% block content %}
|
||||
<input
|
||||
type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden
|
||||
/>
|
||||
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
{% if is_used %}
|
||||
|
||||
<!-- info-->
|
||||
<div
|
||||
class="h-fit transition hover:scale-102 col-span-12 md:col-span-6 2xl:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
<div
|
||||
class="h-fit dark:brightness-110 max-h-none sm:max-h-28 hover:scale-102 transition col-span-12 md:col-span-6 2xl:col-span-4 flex p-4 justify-between w-full shadow-md break-words bg-white dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p
|
||||
class="mb-2 font-sans text-sm font-semibold leading-normal uppercase dark:text-white dark:opacity-60"
|
||||
>
|
||||
GREYLIST
|
||||
</p>
|
||||
<h5 data-count class="mb-1 font-bold dark:text-white/90"></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-red-500 mx-0.5"
|
||||
>request blocked</span
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div
|
||||
role="img"
|
||||
class="dark:brightness-90 inline-block w-12 h-12 text-center rounded-circle bg-red-700"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="scale-75 leading-none text-lg relative fill-red-700 stroke-white"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M18.364 18.364A9 9 0 0 0 5.636 5.636m12.728 12.728A9 9 0 0 1 5.636 5.636m12.728 12.728L5.636 5.636"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
|
||||
<script>
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<input type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden />
|
||||
<div class="core-layout">
|
||||
{% if is_used and is_metrics %}
|
||||
<!-- info-->
|
||||
<div class="core-card">
|
||||
<h5 class="core-card-title">INFO</h5>
|
||||
<div class="core-card-text-container">
|
||||
<p data-info class="core-card-text"></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
<div class="core-card-metrics">
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p class="core-card-metrics-name">GREYLIST</p>
|
||||
<h5 data-count class="core-card-title"></h5>
|
||||
<p class="core-card-metrics-subtitle">
|
||||
<span class="core-card-metrics-subtitle-content error">request blocked</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-svg-container red">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="scale-75 leading-none text-lg relative fill-red-700 stroke-white">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 0 0 5.636 5.636m12.728 12.728A9 9 0 0 1 5.636 5.636m12.728 12.728L5.636 5.636" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<script nonce="{{script_nonce}}">
|
||||
// Use SetupPlugin class that is on static/js/plugins/setup.js
|
||||
const setPlugin = new SetupPlugin({
|
||||
info: {
|
||||
|
|
@ -80,46 +52,29 @@
|
|||
type: "text",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
{% else %}
|
||||
<div
|
||||
class="h-fit transition hover:scale-102 col-span-12 md:col-span-6 2xl:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">Deactivated</h5>
|
||||
<!-- icon -->
|
||||
<div
|
||||
role="img"
|
||||
class="dark:brightness-90 inline-block w-12 h-12 text-center rounded-circle bg-yellow-500"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="scale-75 leading-none text-lg relative fill-yellow-500 stroke-white"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</script>
|
||||
{% else %}
|
||||
<div class="core-card">
|
||||
<div class="core-card-wrap">
|
||||
<h5 class="core-card-title">Deactivated</h5>
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-svg-container">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="core-card-deactivated-svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<div class="core-card-text-container">
|
||||
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center my-2">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
This plugin need to be activated to get metrics.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
map $scheme $header_cookie_secure {
|
||||
default "";
|
||||
"https" "secure";
|
||||
}
|
||||
|
|
@ -1,9 +1,5 @@
|
|||
{% for k, v in all.items() %}
|
||||
{% if k.startswith("COOKIE_FLAGS") and v != "" +%}
|
||||
{% if COOKIE_AUTO_SECURE_FLAG == "yes" and (AUTO_LETS_ENCRYPT == "yes" or USE_CUSTOM_SSL == "yes" or GENERATE_SELF_SIGNED_SSL == "yes") +%}
|
||||
set_cookie_flag {{ v }} secure;
|
||||
{% else +%}
|
||||
set_cookie_flag {{ v }};
|
||||
{% endif +%}
|
||||
set_cookie_flag {{ v }};
|
||||
{% endif +%}
|
||||
{% endfor %}
|
||||
|
|
|
|||
|
|
@ -112,6 +112,27 @@ function headers:header()
|
|||
ngx_header[header] = nil
|
||||
end
|
||||
end
|
||||
-- Set secure flag
|
||||
local set_cookie = ngx_header["Set-Cookie"]
|
||||
if self.ctx.bw.scheme == "https" and self.variables["COOKIE_AUTO_SECURE_FLAG"] == "yes" and set_cookie ~= nil then
|
||||
local new_set_cookie = nil
|
||||
if type(set_cookie) == "string" then
|
||||
new_set_cookie = set_cookie
|
||||
if not set_cookie:find("[Ss]ecure") then
|
||||
new_set_cookie = new_set_cookie .. "; Secure"
|
||||
end
|
||||
elseif type(set_cookie) == "table" then
|
||||
new_set_cookie = {}
|
||||
for _, single_set_cookie in ipairs(set_cookie) do
|
||||
local check_set_cookie = single_set_cookie
|
||||
if not check_set_cookie:find("[Ss]ecure") then
|
||||
check_set_cookie = check_set_cookie .. "; Secure"
|
||||
end
|
||||
table.insert(new_set_cookie, check_set_cookie)
|
||||
end
|
||||
end
|
||||
ngx_header["Set-Cookie"] = new_set_cookie
|
||||
end
|
||||
return self:ret(true, "edited headers for request")
|
||||
end
|
||||
|
||||
|
|
|
|||
138
src/common/core/jobs/jobs/anonymous-report.py
Normal file
138
src/common/core/jobs/jobs/anonymous-report.py
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from json import dumps
|
||||
from os import getenv, sep
|
||||
from os.path import join
|
||||
from pathlib import Path
|
||||
from platform import machine
|
||||
from re import compile as re_compile
|
||||
from sys import exit as sys_exit, path as sys_path, version
|
||||
from traceback import format_exc
|
||||
from typing import Any, Dict
|
||||
|
||||
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",), ("db",))]:
|
||||
if deps_path not in sys_path:
|
||||
sys_path.append(deps_path)
|
||||
|
||||
from Database import Database # type: ignore
|
||||
from logger import setup_logger # type: ignore
|
||||
from jobs import cache_file, is_cached_file # type: ignore
|
||||
|
||||
from requests import post
|
||||
|
||||
logger = setup_logger("ANONYMOUS-REPORT", getenv("LOG_LEVEL", "INFO"))
|
||||
status = 0
|
||||
|
||||
if getenv("SEND_ANONYMOUS_REPORT", "yes") != "yes":
|
||||
logger.info("Skipping the sending of anonymous report (disabled)")
|
||||
sys_exit(status)
|
||||
|
||||
anonymous_report_path = Path(sep, "var", "cache", "bunkerweb", "anonymous_report")
|
||||
anonymous_report_path.mkdir(parents=True, exist_ok=True)
|
||||
tmp_anonymous_report_path = Path(sep, "var", "tmp", "bunkerweb", "anonymous_report")
|
||||
tmp_anonymous_report_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
try:
|
||||
db = Database(logger, sqlalchemy_string=getenv("DATABASE_URI", None), pool=False)
|
||||
if is_cached_file(anonymous_report_path.joinpath("last_report.json"), "day", db):
|
||||
logger.info("Skipping the sending of anonymous report (already sent today)")
|
||||
sys_exit(0)
|
||||
|
||||
# ? Get version and integration of BunkerWeb
|
||||
data: Dict[str, Any] = db.get_metadata()
|
||||
|
||||
data["is_pro"] = "yes" if data["is_pro"] else "no"
|
||||
data.pop("pro_expire", None)
|
||||
data.pop("pro_services", None)
|
||||
data.pop("pro_overlapped", None)
|
||||
data.pop("pro_status", None)
|
||||
|
||||
db_config = db.get_config(methods=True, with_drafts=True)
|
||||
services = db_config.get("SERVER_NAME", {"value": ""})["value"].split(" ")
|
||||
multisite = db_config.get("MULTISITE", {"value": "no"})["value"] == "yes"
|
||||
|
||||
DATABASE_VERSION_REGEX = re_compile(r"(\d+(?:\.\d+)*)")
|
||||
database_version = DATABASE_VERSION_REGEX.search(data.pop("database_version")) or "Unknown"
|
||||
if database_version != "Unknown":
|
||||
database_version = database_version.group(1)
|
||||
|
||||
data["integration"] = data["integration"].lower()
|
||||
data["database"] = f"{db.database_uri.split(':')[0].split('+')[0]}/{database_version}"
|
||||
data["service_number"] = str(len(services))
|
||||
data["draft_service_number"] = 0
|
||||
data["python_version"] = version.split(" ")[0]
|
||||
|
||||
data["use_ui"] = "no"
|
||||
# Multisite case
|
||||
if multisite:
|
||||
for server in services:
|
||||
if db_config.get(f"{server}_USE_UI", db_config.get("USE_UI", {"value": "no"}))["value"] == "yes":
|
||||
data["use_ui"] = "yes"
|
||||
if db_config.get(f"{server}_IS_DRAFT", db_config.get("IS_DRAFT", {"value": "no"}))["value"] == "yes":
|
||||
data["draft_service_number"] += 1
|
||||
# Singlesite case
|
||||
else:
|
||||
if db_config.get("USE_UI", {"value": "no"})["value"] == "yes":
|
||||
data["use_ui"] = "yes"
|
||||
if db_config.get("IS_DRAFT", {"value": "no"})["value"] == "yes":
|
||||
data["draft_service_number"] = 1
|
||||
|
||||
data["draft_service_number"] = str(data["draft_service_number"])
|
||||
data["external_plugins"] = []
|
||||
data["pro_plugins"] = []
|
||||
|
||||
for plugin in db.get_plugins():
|
||||
if plugin["type"] == "external":
|
||||
data["external_plugins"].append(f"{plugin['id']}/{plugin['version']}")
|
||||
elif plugin["type"] == "pro":
|
||||
data["pro_plugins"].append(f"{plugin['id']}/{plugin['version']}")
|
||||
|
||||
data["os"] = {
|
||||
"name": "Linux",
|
||||
"version": "Unknown",
|
||||
"version_id": "Unknown",
|
||||
"version_codename": "Unknown",
|
||||
"id": "Unknown",
|
||||
"arch": machine(),
|
||||
}
|
||||
os_release = Path("/etc/os-release")
|
||||
if os_release.exists():
|
||||
for line in os_release.read_text().splitlines():
|
||||
if "=" not in line or line.split("=")[0].strip().lower() not in data["os"]:
|
||||
continue
|
||||
data["os"][line.split("=")[0].lower()] = line.split("=")[1].strip('"')
|
||||
|
||||
data["non_default_settings"] = {}
|
||||
for setting, setting_data in db_config.items():
|
||||
if isinstance(setting_data, dict) and setting_data["method"] != "default":
|
||||
for server in services:
|
||||
if setting.startswith(server + "_"):
|
||||
setting = setting[len(server) + 1 :] # noqa: E203
|
||||
if setting not in data["non_default_settings"]:
|
||||
data["non_default_settings"][setting] = 1
|
||||
break
|
||||
data["non_default_settings"][setting] += 1
|
||||
break
|
||||
else:
|
||||
if setting not in data["non_default_settings"]:
|
||||
data["non_default_settings"][setting] = 1
|
||||
|
||||
for key in data["non_default_settings"].copy():
|
||||
data["non_default_settings"][key] = str(data["non_default_settings"][key])
|
||||
|
||||
data["bw_instances_number"] = str(len(db.get_instances()))
|
||||
|
||||
tmp_anonymous_report_path.joinpath("last_report.json").write_text(dumps(data, indent=4), encoding="utf-8")
|
||||
|
||||
response = post("https://api.bunkerweb.io/data", json=data, headers={"User-Agent": f"BunkerWeb/{data['version']}"}, allow_redirects=True, timeout=10)
|
||||
response.raise_for_status()
|
||||
|
||||
cached, err = cache_file(tmp_anonymous_report_path.joinpath("last_report.json"), anonymous_report_path.joinpath("last_report.json"), None, db)
|
||||
except SystemExit as e:
|
||||
status = e.code
|
||||
except:
|
||||
status = 2
|
||||
logger.error(f"Exception while running anonymous-report.py :\n{format_exc()}")
|
||||
|
||||
sys_exit(status)
|
||||
|
|
@ -2,15 +2,15 @@
|
|||
|
||||
from hashlib import sha256
|
||||
from io import BytesIO
|
||||
from os import getenv, listdir, chmod, _exit, sep
|
||||
from os.path import basename, dirname, join, normpath
|
||||
from os import getenv, listdir, chmod, sep
|
||||
from os.path import basename, join, normpath
|
||||
from pathlib import Path
|
||||
from stat import S_IEXEC
|
||||
from sys import exit as sys_exit, path as sys_path
|
||||
from threading import Lock
|
||||
from uuid import uuid4
|
||||
from glob import glob
|
||||
from json import loads
|
||||
from json import JSONDecodeError, loads
|
||||
from shutil import copytree, rmtree
|
||||
from tarfile import open as tar_open
|
||||
from traceback import format_exc
|
||||
|
|
@ -40,15 +40,26 @@ logger = setup_logger("Jobs.download-plugins", getenv("LOG_LEVEL", "INFO"))
|
|||
status = 0
|
||||
|
||||
|
||||
def install_plugin(plugin_dir, db) -> bool:
|
||||
def install_plugin(plugin_dir: str, db) -> bool:
|
||||
plugin_path = Path(plugin_dir)
|
||||
plugin_file = plugin_path.joinpath("plugin.json")
|
||||
|
||||
if not plugin_file.is_file():
|
||||
logger.error(f"Skipping installation of plugin {plugin_path.name} (plugin.json not found)")
|
||||
return False
|
||||
|
||||
# Load plugin.json
|
||||
metadata = loads(plugin_path.joinpath("plugin.json").read_text(encoding="utf-8"))
|
||||
try:
|
||||
metadata = loads(plugin_file.read_text(encoding="utf-8"))
|
||||
except JSONDecodeError:
|
||||
logger.error(f"Skipping installation of plugin {plugin_path.name} (plugin.json is not valid)")
|
||||
return False
|
||||
|
||||
# Don't go further if plugin is already installed
|
||||
if EXTERNAL_PLUGINS_DIR.joinpath(metadata["id"], "plugin.json").is_file():
|
||||
old_version = None
|
||||
|
||||
for plugin in db.get_plugins(external=True):
|
||||
for plugin in db.get_plugins(_type="external"):
|
||||
if plugin["id"] == metadata["id"]:
|
||||
old_version = plugin["version"]
|
||||
break
|
||||
|
|
@ -67,7 +78,7 @@ def install_plugin(plugin_dir, db) -> bool:
|
|||
# Copy the plugin
|
||||
copytree(plugin_dir, join(sep, "etc", "bunkerweb", "plugins", metadata["id"]))
|
||||
# Add u+x permissions to jobs files
|
||||
for job_file in glob(join(plugin_dir, "jobs", "*")):
|
||||
for job_file in glob(join(sep, "etc", "bunkerweb", "plugins", "jobs", "*")):
|
||||
st = Path(job_file).stat()
|
||||
chmod(job_file, st.st_mode | S_IEXEC)
|
||||
logger.info(f"Plugin {metadata['id']} installed")
|
||||
|
|
@ -79,7 +90,7 @@ try:
|
|||
plugin_urls = getenv("EXTERNAL_PLUGIN_URLS")
|
||||
if not plugin_urls:
|
||||
logger.info("No external plugins to download")
|
||||
_exit(0)
|
||||
sys_exit(0)
|
||||
|
||||
db = Database(logger, sqlalchemy_string=getenv("DATABASE_URI"), pool=False)
|
||||
plugin_nbr = 0
|
||||
|
|
@ -134,31 +145,25 @@ try:
|
|||
logger.error(f"Unknown file type for {plugin_url}, either zip or tar are supported, skipping...")
|
||||
continue
|
||||
except:
|
||||
logger.error(
|
||||
f"Exception while decompressing plugin(s) from {plugin_url} :\n{format_exc()}",
|
||||
)
|
||||
logger.error(f"Exception while decompressing plugin(s) from {plugin_url} :\n{format_exc()}")
|
||||
status = 2
|
||||
continue
|
||||
|
||||
# Install plugins
|
||||
try:
|
||||
for plugin_dir in glob(join(temp_dir, "**", "plugin.json"), recursive=True):
|
||||
for plugin_dir in glob(join(temp_dir, "*")):
|
||||
try:
|
||||
if install_plugin(dirname(plugin_dir), db):
|
||||
if install_plugin(plugin_dir, db):
|
||||
plugin_nbr += 1
|
||||
except FileExistsError:
|
||||
logger.warning(
|
||||
f"Skipping installation of plugin {basename(dirname(plugin_dir))} (already installed)",
|
||||
)
|
||||
logger.warning(f"Skipping installation of plugin {basename(plugin_dir)} (already installed)")
|
||||
except:
|
||||
logger.error(
|
||||
f"Exception while installing plugin(s) from {plugin_url} :\n{format_exc()}",
|
||||
)
|
||||
logger.error(f"Exception while installing plugin(s) from {plugin_url} :\n{format_exc()}")
|
||||
status = 2
|
||||
|
||||
if not plugin_nbr:
|
||||
logger.info("No external plugins to update to database")
|
||||
_exit(0)
|
||||
sys_exit(0)
|
||||
|
||||
external_plugins = []
|
||||
external_plugins_ids = []
|
||||
|
|
@ -179,7 +184,7 @@ try:
|
|||
|
||||
plugin_file.update(
|
||||
{
|
||||
"external": True,
|
||||
"type": "external",
|
||||
"page": False,
|
||||
"method": "scheduler",
|
||||
"data": value,
|
||||
|
|
@ -195,7 +200,7 @@ try:
|
|||
|
||||
lock = Lock()
|
||||
|
||||
for plugin in db.get_plugins(external=True, with_data=True):
|
||||
for plugin in db.get_plugins(_type="external", with_data=True):
|
||||
if plugin["method"] != "scheduler" and plugin["id"] not in external_plugins_ids:
|
||||
external_plugins.append(plugin)
|
||||
|
||||
|
|
@ -210,11 +215,13 @@ try:
|
|||
status = 1
|
||||
logger.info("External plugins downloaded and installed")
|
||||
|
||||
except SystemExit as e:
|
||||
status = e.code
|
||||
except:
|
||||
status = 2
|
||||
logger.error(f"Exception while running download-plugins.py :\n{format_exc()}")
|
||||
|
||||
for plugin_tmp in glob(join(sep, "var", "tmp", "bunkerweb", "plugins-*")):
|
||||
for plugin_tmp in glob(join(sep, "var", "tmp", "bunkerweb", "plugins", "*")):
|
||||
rmtree(plugin_tmp, ignore_errors=True)
|
||||
|
||||
sys_exit(status)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,17 @@
|
|||
"description": "Fake core plugin for internal jobs.",
|
||||
"version": "1.0",
|
||||
"stream": "yes",
|
||||
"settings": {},
|
||||
"settings": {
|
||||
"SEND_ANONYMOUS_REPORT": {
|
||||
"context": "global",
|
||||
"default": "yes",
|
||||
"help": "Send anonymous report to BunkerWeb maintainers.",
|
||||
"id": "send-anonymous-report",
|
||||
"label": "Send anonymous report",
|
||||
"regex": "^(yes|no)$",
|
||||
"type": "check"
|
||||
}
|
||||
},
|
||||
"jobs": [
|
||||
{
|
||||
"name": "mmdb-country",
|
||||
|
|
@ -23,6 +33,12 @@
|
|||
"file": "download-plugins.py",
|
||||
"every": "once",
|
||||
"reload": false
|
||||
},
|
||||
{
|
||||
"name": "anonymous-report",
|
||||
"file": "anonymous-report.py",
|
||||
"every": "day",
|
||||
"reload": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ for deps_path in [
|
|||
sys_path.append(deps_path)
|
||||
|
||||
from Database import Database # type: ignore
|
||||
from jobs import get_integration # type: ignore
|
||||
from logger import setup_logger # type: ignore
|
||||
from API import API # type: ignore
|
||||
|
||||
|
|
@ -28,25 +29,11 @@ status = 0
|
|||
|
||||
try:
|
||||
# Get env vars
|
||||
bw_integration = "Linux"
|
||||
integration_path = Path(sep, "usr", "share", "bunkerweb", "INTEGRATION")
|
||||
os_release_path = Path(sep, "etc", "os-release")
|
||||
if getenv("KUBERNETES_MODE", "no") == "yes":
|
||||
bw_integration = "Kubernetes"
|
||||
elif getenv("SWARM_MODE", "no") == "yes":
|
||||
bw_integration = "Swarm"
|
||||
elif getenv("AUTOCONF_MODE", "no") == "yes":
|
||||
bw_integration = "Autoconf"
|
||||
elif integration_path.is_file():
|
||||
bw_integration = integration_path.read_text(encoding="utf-8").strip()
|
||||
elif os_release_path.is_file() and "Alpine" in os_release_path.read_text(encoding="utf-8"):
|
||||
bw_integration = "Docker"
|
||||
|
||||
token = getenv("CERTBOT_TOKEN", "")
|
||||
validation = getenv("CERTBOT_VALIDATION", "")
|
||||
|
||||
# Cluster case
|
||||
if bw_integration in ("Docker", "Swarm", "Kubernetes", "Autoconf"):
|
||||
if get_integration() in ("Docker", "Swarm", "Kubernetes", "Autoconf"):
|
||||
db = Database(logger, sqlalchemy_string=getenv("DATABASE_URI", None), pool=False)
|
||||
lock = Lock()
|
||||
|
||||
|
|
@ -54,39 +41,20 @@ try:
|
|||
instances = db.get_instances()
|
||||
|
||||
for instance in instances:
|
||||
api = API(
|
||||
f"http://{instance['hostname']}:{instance['port']}",
|
||||
host=instance["server_name"],
|
||||
)
|
||||
sent, err, status, resp = api.request(
|
||||
"POST",
|
||||
"/lets-encrypt/challenge",
|
||||
data={"token": token, "validation": validation},
|
||||
)
|
||||
api = API(f"http://{instance['hostname']}:{instance['port']}", host=instance["server_name"])
|
||||
sent, err, status, resp = api.request("POST", "/lets-encrypt/challenge", data={"token": token, "validation": validation})
|
||||
if not sent:
|
||||
status = 1
|
||||
logger.error(f"Can't send API request to {api.endpoint}/lets-encrypt/challenge : {err}")
|
||||
elif status != 200:
|
||||
status = 1
|
||||
logger.error(
|
||||
f"Error while sending API request to {api.endpoint}/lets-encrypt/challenge : status = {resp['status']}, msg = {resp['msg']}",
|
||||
)
|
||||
logger.error(f"Error while sending API request to {api.endpoint}/lets-encrypt/challenge : status = {resp['status']}, msg = {resp['msg']}")
|
||||
else:
|
||||
logger.info(
|
||||
f"Successfully sent API request to {api.endpoint}/lets-encrypt/challenge",
|
||||
)
|
||||
logger.info(f"Successfully sent API request to {api.endpoint}/lets-encrypt/challenge")
|
||||
|
||||
# Linux case
|
||||
else:
|
||||
root_dir = Path(
|
||||
sep,
|
||||
"var",
|
||||
"tmp",
|
||||
"bunkerweb",
|
||||
"lets-encrypt",
|
||||
".well-known",
|
||||
"acme-challenge",
|
||||
)
|
||||
root_dir = Path(sep, "var", "tmp", "bunkerweb", "lets-encrypt", ".well-known", "acme-challenge")
|
||||
root_dir.mkdir(parents=True, exist_ok=True)
|
||||
root_dir.joinpath(token).write_text(validation, encoding="utf-8")
|
||||
except:
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ for deps_path in [
|
|||
sys_path.append(deps_path)
|
||||
|
||||
from Database import Database # type: ignore
|
||||
from jobs import get_integration # type: ignore
|
||||
from logger import setup_logger # type: ignore
|
||||
from API import API # type: ignore
|
||||
|
||||
|
|
@ -28,60 +29,29 @@ status = 0
|
|||
|
||||
try:
|
||||
# Get env vars
|
||||
bw_integration = "Linux"
|
||||
integration_path = Path(sep, "usr", "share", "bunkerweb", "INTEGRATION")
|
||||
os_release_path = Path(sep, "etc", "os-release")
|
||||
if getenv("KUBERNETES_MODE", "no") == "yes":
|
||||
bw_integration = "Kubernetes"
|
||||
elif getenv("SWARM_MODE", "no") == "yes":
|
||||
bw_integration = "Swarm"
|
||||
elif getenv("AUTOCONF_MODE", "no") == "yes":
|
||||
bw_integration = "Autoconf"
|
||||
elif integration_path.is_file():
|
||||
bw_integration = integration_path.read_text(encoding="utf-8").strip()
|
||||
elif os_release_path.is_file() and "Alpine" in os_release_path.read_text(encoding="utf-8"):
|
||||
bw_integration = "Docker"
|
||||
|
||||
token = getenv("CERTBOT_TOKEN", "")
|
||||
|
||||
# Cluster case
|
||||
if bw_integration in ("Docker", "Swarm", "Kubernetes", "Autoconf"):
|
||||
if get_integration() in ("Docker", "Swarm", "Kubernetes", "Autoconf"):
|
||||
db = Database(logger, sqlalchemy_string=getenv("DATABASE_URI", None), pool=False)
|
||||
lock = Lock()
|
||||
with lock:
|
||||
instances = db.get_instances()
|
||||
|
||||
for instance in instances:
|
||||
api = API(
|
||||
f"http://{instance['hostname']}:{instance['port']}",
|
||||
host=instance["server_name"],
|
||||
)
|
||||
api = API(f"http://{instance['hostname']}:{instance['port']}", host=instance["server_name"])
|
||||
sent, err, status, resp = api.request("DELETE", "/lets-encrypt/challenge", data={"token": token})
|
||||
if not sent:
|
||||
status = 1
|
||||
logger.error(f"Can't send API request to {api.endpoint}/lets-encrypt/challenge : {err}")
|
||||
elif status != 200:
|
||||
status = 1
|
||||
logger.error(
|
||||
f"Error while sending API request to {api.endpoint}/lets-encrypt/challenge : status = {resp['status']}, msg = {resp['msg']}",
|
||||
)
|
||||
logger.error(f"Error while sending API request to {api.endpoint}/lets-encrypt/challenge : status = {resp['status']}, msg = {resp['msg']}")
|
||||
else:
|
||||
logger.info(
|
||||
f"Successfully sent API request to {api.endpoint}/lets-encrypt/challenge",
|
||||
)
|
||||
logger.info(f"Successfully sent API request to {api.endpoint}/lets-encrypt/challenge")
|
||||
# Linux case
|
||||
else:
|
||||
challenge_path = Path(
|
||||
sep,
|
||||
"var",
|
||||
"tmp",
|
||||
"bunkerweb",
|
||||
"lets-encrypt",
|
||||
".well-known",
|
||||
"acme-challenge",
|
||||
token,
|
||||
)
|
||||
challenge_path.unlink(missing_ok=True)
|
||||
Path(sep, "var", "tmp", "bunkerweb", "lets-encrypt", ".well-known", "acme-challenge", token).unlink(missing_ok=True)
|
||||
except:
|
||||
status = 1
|
||||
logger.error(f"Exception while running certbot-cleanup.py :\n{format_exc()}")
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
from io import BytesIO
|
||||
from os import getenv, sep
|
||||
from os.path import join
|
||||
from pathlib import Path
|
||||
from subprocess import DEVNULL, STDOUT, run
|
||||
from sys import exit as sys_exit, path as sys_path
|
||||
from tarfile import open as tar_open
|
||||
|
|
@ -23,6 +22,7 @@ for deps_path in [
|
|||
sys_path.append(deps_path)
|
||||
|
||||
from Database import Database # type: ignore
|
||||
from jobs import get_integration # type: ignore
|
||||
from logger import setup_logger # type: ignore
|
||||
from API import API # type: ignore
|
||||
|
||||
|
|
@ -31,34 +31,17 @@ status = 0
|
|||
|
||||
try:
|
||||
# Get env vars
|
||||
bw_integration = "Linux"
|
||||
integration_path = Path(sep, "usr", "share", "bunkerweb", "INTEGRATION")
|
||||
os_release_path = Path(sep, "etc", "os-release")
|
||||
if getenv("KUBERNETES_MODE", "no") == "yes":
|
||||
bw_integration = "Kubernetes"
|
||||
elif getenv("SWARM_MODE", "no") == "yes":
|
||||
bw_integration = "Swarm"
|
||||
elif getenv("AUTOCONF_MODE", "no") == "yes":
|
||||
bw_integration = "Autoconf"
|
||||
elif integration_path.is_file():
|
||||
bw_integration = integration_path.read_text(encoding="utf-8").strip()
|
||||
elif os_release_path.is_file() and "Alpine" in os_release_path.read_text(encoding="utf-8"):
|
||||
bw_integration = "Docker"
|
||||
|
||||
token = getenv("CERTBOT_TOKEN", "")
|
||||
|
||||
logger.info(f"Certificates renewal for {getenv('RENEWED_DOMAINS')} successful")
|
||||
|
||||
# Cluster case
|
||||
if bw_integration in ("Docker", "Swarm", "Kubernetes", "Autoconf"):
|
||||
if get_integration() in ("Docker", "Swarm", "Kubernetes", "Autoconf"):
|
||||
# Create tarball of /var/cache/bunkerweb/letsencrypt
|
||||
tgz = BytesIO()
|
||||
|
||||
with tar_open(mode="w:gz", fileobj=tgz, compresslevel=3) as tf:
|
||||
tf.add(
|
||||
join(sep, "var", "cache", "bunkerweb", "letsencrypt", "etc"),
|
||||
arcname="etc",
|
||||
)
|
||||
tf.add(join(sep, "var", "cache", "bunkerweb", "letsencrypt", "etc"), arcname="etc")
|
||||
tgz.seek(0, 0)
|
||||
files = {"archive.tar.gz": tgz}
|
||||
|
||||
|
|
@ -95,15 +78,7 @@ try:
|
|||
logger.info(f"Successfully sent API request to {api.endpoint}/reload")
|
||||
# Linux case
|
||||
else:
|
||||
if (
|
||||
run(
|
||||
[join(sep, "usr", "sbin", "nginx"), "-s", "reload"],
|
||||
stdin=DEVNULL,
|
||||
stderr=STDOUT,
|
||||
check=False,
|
||||
).returncode
|
||||
!= 0
|
||||
):
|
||||
if run([join(sep, "usr", "sbin", "nginx"), "-s", "reload"], stdin=DEVNULL, stderr=STDOUT, check=False).returncode != 0:
|
||||
status = 1
|
||||
logger.error("Error while reloading nginx")
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from traceback import format_exc
|
|||
from tarfile import open as tar_open
|
||||
from io import BytesIO
|
||||
from shutil import rmtree
|
||||
from re import findall, MULTILINE
|
||||
from re import MULTILINE, search
|
||||
|
||||
for deps_path in [
|
||||
join(sep, "usr", "share", "bunkerweb", *paths)
|
||||
|
|
@ -29,24 +29,31 @@ from jobs import get_file_in_db, set_file_in_db # type: ignore
|
|||
logger = setup_logger("LETS-ENCRYPT.new", getenv("LOG_LEVEL", "INFO"))
|
||||
status = 0
|
||||
|
||||
CERTBOT_BIN = join(sep, "usr", "share", "bunkerweb", "deps", "python", "bin", "certbot")
|
||||
|
||||
def certbot_new(domains: str, email: str, letsencrypt_path: Path, letsencrypt_job_path: Path) -> int:
|
||||
LETS_ENCRYPT_PATH = Path(sep, "var", "cache", "bunkerweb", "letsencrypt")
|
||||
LETS_ENCRYPT_JOBS_PATH = Path(sep, "usr", "share", "bunkerweb", "core", "letsencrypt", "jobs")
|
||||
LETS_ENCRYPT_WORK_DIR = join(sep, "var", "lib", "bunkerweb", "letsencrypt")
|
||||
LETS_ENCRYPT_LOGS_DIR = join(sep, "var", "log", "bunkerweb")
|
||||
|
||||
|
||||
def certbot_new(domains: str, email: str, use_letsencrypt_staging: bool = False) -> int:
|
||||
return run(
|
||||
[
|
||||
join(sep, "usr", "share", "bunkerweb", "deps", "python", "bin", "certbot"),
|
||||
CERTBOT_BIN,
|
||||
"certonly",
|
||||
"--config-dir",
|
||||
str(letsencrypt_path.joinpath("etc")),
|
||||
LETS_ENCRYPT_PATH.joinpath("etc").as_posix(),
|
||||
"--work-dir",
|
||||
join(sep, "var", "lib", "bunkerweb", "letsencrypt"),
|
||||
LETS_ENCRYPT_WORK_DIR,
|
||||
"--logs-dir",
|
||||
join(sep, "var", "log", "bunkerweb"),
|
||||
LETS_ENCRYPT_LOGS_DIR,
|
||||
"--manual",
|
||||
"--preferred-challenges=http",
|
||||
"--manual-auth-hook",
|
||||
str(letsencrypt_job_path.joinpath("certbot-auth.py")),
|
||||
LETS_ENCRYPT_JOBS_PATH.joinpath("certbot-auth.py").as_posix(),
|
||||
"--manual-cleanup-hook",
|
||||
str(letsencrypt_job_path.joinpath("certbot-cleanup.py")),
|
||||
LETS_ENCRYPT_JOBS_PATH.joinpath("certbot-cleanup.py").as_posix(),
|
||||
"-n",
|
||||
"-d",
|
||||
domains,
|
||||
|
|
@ -55,52 +62,26 @@ def certbot_new(domains: str, email: str, letsencrypt_path: Path, letsencrypt_jo
|
|||
"--agree-tos",
|
||||
"--expand",
|
||||
]
|
||||
+ (["--staging"] if getenv("USE_LETS_ENCRYPT_STAGING", "no") == "yes" else []),
|
||||
+ (["--staging"] if use_letsencrypt_staging else []),
|
||||
stdin=DEVNULL,
|
||||
stderr=STDOUT,
|
||||
env=environ.copy() | {"PYTHONPATH": join(sep, "usr", "share", "bunkerweb", "deps", "python")},
|
||||
).returncode
|
||||
|
||||
|
||||
def certbot_check_domains(domains: list[str], letsencrypt_path: Path) -> int:
|
||||
proc = run(
|
||||
[
|
||||
join(sep, "usr", "share", "bunkerweb", "deps", "python", "bin", "certbot"),
|
||||
"certificates",
|
||||
"--config-dir",
|
||||
str(letsencrypt_path.joinpath("etc")),
|
||||
"--work-dir",
|
||||
join(sep, "var", "lib", "bunkerweb", "letsencrypt"),
|
||||
"--logs-dir",
|
||||
join(sep, "var", "log", "bunkerweb"),
|
||||
],
|
||||
stdin=DEVNULL,
|
||||
stdout=PIPE,
|
||||
stderr=STDOUT,
|
||||
text=True,
|
||||
env=environ.copy() | {"PYTHONPATH": join(sep, "usr", "share", "bunkerweb", "deps", "python")},
|
||||
)
|
||||
if proc.returncode != 0:
|
||||
logger.error(f"Error while checking certificates :\n{proc.stdout}")
|
||||
return 2
|
||||
first_needed_domain = domains[0]
|
||||
needed_domains = set(domains)
|
||||
for raw_domains in findall(r"^ Domains: (.*)$", proc.stdout, MULTILINE):
|
||||
current_domains = raw_domains.split(" ")
|
||||
if current_domains[0] == first_needed_domain and set(current_domains) == needed_domains:
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
status = 0
|
||||
|
||||
try:
|
||||
# Check if we're using let's encrypt
|
||||
use_letsencrypt = False
|
||||
is_multisite = getenv("MULTISITE", "no") == "yes"
|
||||
all_domains = getenv("SERVER_NAME", "")
|
||||
server_names = all_domains.split(" ")
|
||||
|
||||
if getenv("AUTO_LETS_ENCRYPT", "no") == "yes":
|
||||
use_letsencrypt = True
|
||||
elif getenv("MULTISITE", "no") == "yes":
|
||||
for first_server in getenv("SERVER_NAME", "").split(" "):
|
||||
elif is_multisite:
|
||||
for first_server in server_names:
|
||||
if first_server and getenv(f"{first_server}_AUTO_LETS_ENCRYPT", "no") == "yes":
|
||||
use_letsencrypt = True
|
||||
break
|
||||
|
|
@ -108,129 +89,91 @@ try:
|
|||
if not use_letsencrypt:
|
||||
logger.info("Let's Encrypt is not activated, skipping generation...")
|
||||
_exit(0)
|
||||
elif not getenv("SERVER_NAME"):
|
||||
logger.warning("There are no server names, skipping generation...")
|
||||
_exit(0)
|
||||
|
||||
# Create directory if it doesn't exist
|
||||
letsencrypt_path = Path(sep, "var", "cache", "bunkerweb", "letsencrypt")
|
||||
letsencrypt_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
letsencrypt_job_path = Path(sep, "usr", "share", "bunkerweb", "core", "letsencrypt", "jobs")
|
||||
# Create directories if they doesn't exist
|
||||
LETS_ENCRYPT_PATH.mkdir(parents=True, exist_ok=True)
|
||||
Path(sep, "var", "lib", "bunkerweb", "letsencrypt").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Get env vars
|
||||
bw_integration = "Linux"
|
||||
integration_path = Path(sep, "usr", "share", "bunkerweb", "INTEGRATION")
|
||||
os_release_path = Path(sep, "etc", "os-release")
|
||||
if getenv("KUBERNETES_MODE", "no") == "yes":
|
||||
bw_integration = "Kubernetes"
|
||||
elif getenv("SWARM_MODE", "no") == "yes":
|
||||
bw_integration = "Swarm"
|
||||
elif getenv("AUTOCONF_MODE", "no") == "yes":
|
||||
bw_integration = "Autoconf"
|
||||
elif integration_path.is_file():
|
||||
bw_integration = integration_path.read_text(encoding="utf-8").strip()
|
||||
elif os_release_path.is_file() and "Alpine" in os_release_path.read_text(encoding="utf-8"):
|
||||
bw_integration = "Docker"
|
||||
|
||||
# Extract letsencrypt folder if it exists in db
|
||||
db = Database(logger, sqlalchemy_string=getenv("DATABASE_URI", None), pool=False)
|
||||
db = Database(logger, sqlalchemy_string=getenv("DATABASE_URI"), pool=False)
|
||||
|
||||
tgz = get_file_in_db("folder.tgz", db, job_name="certbot-renew")
|
||||
if tgz:
|
||||
# Delete folder if needed
|
||||
if letsencrypt_path.exists():
|
||||
rmtree(str(letsencrypt_path), ignore_errors=True)
|
||||
letsencrypt_path.mkdir(parents=True, exist_ok=True)
|
||||
if LETS_ENCRYPT_PATH.exists():
|
||||
rmtree(LETS_ENCRYPT_PATH, ignore_errors=True)
|
||||
LETS_ENCRYPT_PATH.mkdir(parents=True, exist_ok=True)
|
||||
# Extract it
|
||||
with tar_open(name="folder.tgz", mode="r:gz", fileobj=BytesIO(tgz)) as tf:
|
||||
tf.extractall(str(letsencrypt_path))
|
||||
tf.extractall(LETS_ENCRYPT_PATH)
|
||||
logger.info("Successfully retrieved Let's Encrypt data from db cache")
|
||||
else:
|
||||
logger.info("No Let's Encrypt data found in db cache")
|
||||
|
||||
domains_to_ask = []
|
||||
# Multisite case
|
||||
if getenv("MULTISITE", "no") == "yes" and getenv("SERVER_NAME"):
|
||||
for first_server in getenv("SERVER_NAME", "").split(" "):
|
||||
if (
|
||||
not first_server
|
||||
or getenv(
|
||||
f"{first_server}_AUTO_LETS_ENCRYPT",
|
||||
getenv("AUTO_LETS_ENCRYPT", "no"),
|
||||
)
|
||||
!= "yes"
|
||||
):
|
||||
if is_multisite:
|
||||
domains_sever_names = {}
|
||||
|
||||
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 = getenv(f"{first_server}_SERVER_NAME", first_server)
|
||||
if certbot_check_domains(domains.split(" "), letsencrypt_path) == 1:
|
||||
logger.info(
|
||||
f"Certificates already exists for domain(s) {domains}",
|
||||
)
|
||||
continue
|
||||
|
||||
real_email = getenv(
|
||||
f"{first_server}_EMAIL_LETS_ENCRYPT",
|
||||
getenv("EMAIL_LETS_ENCRYPT", f"contact@{first_server}"),
|
||||
)
|
||||
if not real_email:
|
||||
real_email = f"contact@{first_server}"
|
||||
|
||||
logger.info(
|
||||
f"Asking certificates for domains : {domains} (email = {real_email}) ...",
|
||||
)
|
||||
if (
|
||||
certbot_new(
|
||||
domains.replace(" ", ","),
|
||||
real_email,
|
||||
letsencrypt_path,
|
||||
letsencrypt_job_path,
|
||||
)
|
||||
!= 0
|
||||
):
|
||||
status = 2
|
||||
logger.error(
|
||||
f"Certificate generation failed for domain(s) {domains} ...",
|
||||
)
|
||||
continue
|
||||
else:
|
||||
status = 1 if status == 0 else status
|
||||
logger.info(f"Certificate generation succeeded for domain(s) : {domains}")
|
||||
|
||||
domains_sever_names[first_server] = getenv(f"{first_server}_SERVER_NAME", first_server)
|
||||
# Singlesite case
|
||||
elif getenv("AUTO_LETS_ENCRYPT", "no") == "yes" and getenv("SERVER_NAME"):
|
||||
first_server = getenv("SERVER_NAME", "").split(" ")[0]
|
||||
domains = getenv("SERVER_NAME", "")
|
||||
else:
|
||||
domains_sever_names = {server_names[0]: all_domains}
|
||||
|
||||
if certbot_check_domains(domains.split(" "), letsencrypt_path) == 1:
|
||||
logger.info(
|
||||
f"Certificates already exists for domain(s) {domains}",
|
||||
)
|
||||
proc = run(
|
||||
[CERTBOT_BIN, "certificates", "--config-dir", LETS_ENCRYPT_PATH.joinpath("etc").as_posix(), "--work-dir", LETS_ENCRYPT_WORK_DIR, "--logs-dir", LETS_ENCRYPT_LOGS_DIR],
|
||||
stdin=DEVNULL,
|
||||
stdout=PIPE,
|
||||
stderr=STDOUT,
|
||||
text=True,
|
||||
env=environ.copy() | {"PYTHONPATH": join(sep, "usr", "share", "bunkerweb", "deps", "python")},
|
||||
)
|
||||
stdout = proc.stdout
|
||||
|
||||
if proc.returncode != 0:
|
||||
logger.error(f"Error while checking certificates :\n{proc.stdout}")
|
||||
domains_to_ask = server_names
|
||||
else:
|
||||
for first_server, domains in domains_sever_names.items():
|
||||
current_domains = search(rf"Domains: {first_server}(?P<domains>.*)$", stdout, MULTILINE)
|
||||
if not current_domains:
|
||||
domains_to_ask.append(first_server)
|
||||
continue
|
||||
elif set(f"{first_server}{current_domains.groupdict()['domains']}".strip().split(" ")) != set(domains.split(" ")):
|
||||
logger.warning(f"Domains for {first_server} are not the same as in the certificate, asking new certificate...")
|
||||
domains_to_ask.append(first_server)
|
||||
continue
|
||||
logger.info(f"Certificates already exists for domain(s) {domains}")
|
||||
|
||||
for first_server, domains in domains_sever_names.items():
|
||||
if first_server not in domains_to_ask:
|
||||
continue
|
||||
|
||||
real_email = getenv(f"{first_server}_EMAIL_LETS_ENCRYPT", getenv("EMAIL_LETS_ENCRYPT", f"contact@{first_server}"))
|
||||
if not real_email:
|
||||
real_email = f"contact@{first_server}"
|
||||
|
||||
use_letsencrypt_staging = getenv(f"{first_server}_USE_LETS_ENCRYPT_STAGING", getenv("USE_LETS_ENCRYPT_STAGING", "no")) == "yes"
|
||||
|
||||
logger.info(f"Asking certificates for domain(s) : {domains} (email = {real_email}) to Let's Encrypt {'staging ' if use_letsencrypt_staging else ''}...")
|
||||
if certbot_new(domains.replace(" ", ","), real_email, use_letsencrypt_staging) != 0:
|
||||
status = 2
|
||||
logger.error(f"Certificate generation failed for domain(s) {domains} ...")
|
||||
continue
|
||||
else:
|
||||
real_email = getenv("EMAIL_LETS_ENCRYPT", f"contact@{first_server}")
|
||||
if not real_email:
|
||||
real_email = f"contact@{first_server}"
|
||||
|
||||
logger.info(
|
||||
f"Asking certificates for domain(s) : {domains} (email = {real_email}) ...",
|
||||
)
|
||||
if (
|
||||
certbot_new(
|
||||
domains.replace(" ", ","),
|
||||
real_email,
|
||||
letsencrypt_path,
|
||||
letsencrypt_job_path,
|
||||
)
|
||||
!= 0
|
||||
):
|
||||
status = 2
|
||||
logger.error(f"Certificate generation failed for domain(s) : {domains}")
|
||||
else:
|
||||
status = 1
|
||||
logger.info(f"Certificate generation succeeded for domain(s) : {domains}")
|
||||
status = 1 if status == 0 else status
|
||||
logger.info(f"Certificate generation succeeded for domain(s) : {domains}")
|
||||
|
||||
# Put new folder in cache
|
||||
bio = BytesIO()
|
||||
with tar_open("folder.tgz", mode="w:gz", fileobj=bio, compresslevel=9) as tgz:
|
||||
tgz.add(str(letsencrypt_path), arcname=".")
|
||||
tgz.add(LETS_ENCRYPT_PATH, arcname=".")
|
||||
bio.seek(0, 0)
|
||||
|
||||
# Put tgz in cache
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ from jobs import get_file_in_db, set_file_in_db # type: ignore
|
|||
logger = setup_logger("LETS-ENCRYPT.renew", getenv("LOG_LEVEL", "INFO"))
|
||||
status = 0
|
||||
|
||||
LETS_ENCRYPT_PATH = Path(sep, "var", "cache", "bunkerweb", "letsencrypt")
|
||||
|
||||
try:
|
||||
# Check if we're using let's encrypt
|
||||
use_letsencrypt = False
|
||||
|
|
@ -44,37 +46,21 @@ try:
|
|||
_exit(0)
|
||||
|
||||
# Create directory if it doesn't exist
|
||||
letsencrypt_path = Path(sep, "var", "cache", "bunkerweb", "letsencrypt")
|
||||
letsencrypt_path.mkdir(parents=True, exist_ok=True)
|
||||
LETS_ENCRYPT_PATH.mkdir(parents=True, exist_ok=True)
|
||||
Path(sep, "var", "lib", "bunkerweb", "letsencrypt").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Get env vars
|
||||
bw_integration = "Linux"
|
||||
integration_path = Path(sep, "usr", "share", "bunkerweb", "INTEGRATION")
|
||||
os_release_path = Path(sep, "etc", "os-release")
|
||||
if getenv("KUBERNETES_MODE", "no") == "yes":
|
||||
bw_integration = "Kubernetes"
|
||||
elif getenv("SWARM_MODE", "no") == "yes":
|
||||
bw_integration = "Swarm"
|
||||
elif getenv("AUTOCONF_MODE", "no") == "yes":
|
||||
bw_integration = "Autoconf"
|
||||
elif integration_path.is_file():
|
||||
bw_integration = integration_path.read_text(encoding="utf-8").strip()
|
||||
elif os_release_path.is_file() and "Alpine" in os_release_path.read_text(encoding="utf-8"):
|
||||
bw_integration = "Docker"
|
||||
|
||||
# Extract letsencrypt folder if it exists in db
|
||||
db = Database(logger, sqlalchemy_string=getenv("DATABASE_URI", None), pool=False)
|
||||
db = Database(logger, sqlalchemy_string=getenv("DATABASE_URI"), pool=False)
|
||||
|
||||
tgz = get_file_in_db("folder.tgz", db)
|
||||
if tgz:
|
||||
# Delete folder if needed
|
||||
if letsencrypt_path.exists():
|
||||
rmtree(str(letsencrypt_path), ignore_errors=True)
|
||||
letsencrypt_path.mkdir(parents=True, exist_ok=True)
|
||||
if LETS_ENCRYPT_PATH.exists():
|
||||
rmtree(LETS_ENCRYPT_PATH, ignore_errors=True)
|
||||
LETS_ENCRYPT_PATH.mkdir(parents=True, exist_ok=True)
|
||||
# Extract it
|
||||
with tar_open(name="folder.tgz", mode="r:gz", fileobj=BytesIO(tgz)) as tf:
|
||||
tf.extractall(str(letsencrypt_path))
|
||||
tf.extractall(LETS_ENCRYPT_PATH)
|
||||
logger.info("Successfully retrieved Let's Encrypt data from db cache")
|
||||
else:
|
||||
logger.info("No Let's Encrypt data found in db cache")
|
||||
|
|
@ -86,7 +72,7 @@ try:
|
|||
"renew",
|
||||
"--no-random-sleep-on-renew",
|
||||
"--config-dir",
|
||||
str(letsencrypt_path.joinpath("etc")),
|
||||
LETS_ENCRYPT_PATH.joinpath("etc").as_posix(),
|
||||
"--work-dir",
|
||||
join(sep, "var", "lib", "bunkerweb", "letsencrypt"),
|
||||
"--logs-dir",
|
||||
|
|
@ -105,7 +91,7 @@ try:
|
|||
# Put new folder in cache
|
||||
bio = BytesIO()
|
||||
with tar_open("folder.tgz", mode="w:gz", fileobj=bio, compresslevel=9) as tgz:
|
||||
tgz.add(str(letsencrypt_path), arcname=".")
|
||||
tgz.add(LETS_ENCRYPT_PATH, arcname=".")
|
||||
bio.seek(0, 0)
|
||||
|
||||
# Put tgz in cache
|
||||
|
|
|
|||
|
|
@ -33,6 +33,14 @@ function letsencrypt:initialize(ctx)
|
|||
plugin.initialize(self, "letsencrypt", ctx)
|
||||
end
|
||||
|
||||
function letsencrypt:set()
|
||||
local https_configured = self.variables["AUTO_LETS_ENCRYPT"]
|
||||
if https_configured == "yes" then
|
||||
self.ctx.bw.https_configured = "yes"
|
||||
end
|
||||
return self:ret(true, "set https_configured to " .. https_configured)
|
||||
end
|
||||
|
||||
function letsencrypt:init()
|
||||
local ret_ok, ret_err = true, "success"
|
||||
if has_variable("AUTO_LETS_ENCRYPT", "yes") then
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
from operator import itemgetter
|
||||
|
||||
|
||||
def limit(**kwargs):
|
||||
try:
|
||||
# Here we will have a list { 'limit_uri_url1': X, 'limit_uri_url2': Y ... }
|
||||
|
|
@ -5,9 +8,13 @@ def limit(**kwargs):
|
|||
format_data = []
|
||||
# Format to fit [{url: "url1", count: X}, {url: "url2", count: Y} ...]
|
||||
for key, value in data.items():
|
||||
format_data[key] = {"url": key.replace("limit_uri_", ""), "count": value}
|
||||
|
||||
key = key.split("/", 1)
|
||||
if len(key) > 1:
|
||||
key = key[1]
|
||||
else:
|
||||
key = ""
|
||||
format_data.append({"url": f"/{key}", "count": int(value)})
|
||||
format_data.sort(key=itemgetter("count"), reverse=True)
|
||||
return {"items": format_data}
|
||||
|
||||
except:
|
||||
return {"items": []}
|
||||
|
|
|
|||
|
|
@ -1,77 +1,44 @@
|
|||
{% extends "base.html" %} {% block content %}
|
||||
<input
|
||||
type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden
|
||||
/>
|
||||
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
{% if is_used %}
|
||||
<!-- info-->
|
||||
<div
|
||||
class="h-fit transition hover:scale-102 col-span-12 md:col-span-4 2xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
|
||||
<div
|
||||
data-fetch-success-show
|
||||
class="hidden col-span-12 md:col-span-8 3xl:col-span-9 w-full xl:max-w-[600px] overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="col-span-12">
|
||||
<h5 class="mb-4 mt-2 font-bold dark:text-white/90 mx-2">
|
||||
LIMIT AND REQUEST LIST
|
||||
</h5>
|
||||
</div>
|
||||
<div class="col-span-12 overflow-y-auto overflow-x-auto">
|
||||
<!-- list container-->
|
||||
<div class="min-w-[500px] w-full grid grid-cols-12 rounded p-2">
|
||||
<!-- header-->
|
||||
<p
|
||||
class="dark:text-gray-300 h-8 text-sm font-bold col-span-8 m-0 pb-2 border-b border-gray-400"
|
||||
>
|
||||
URL
|
||||
</p>
|
||||
<p
|
||||
class="dark:text-gray-300 h-8 text-sm font-bold col-span-4 m-0 pb-2 border-b border-gray-400"
|
||||
>
|
||||
Count
|
||||
</p>
|
||||
|
||||
<!-- end header-->
|
||||
<!-- list -->
|
||||
<ul class="col-span-12 w-full">
|
||||
<li
|
||||
data-item
|
||||
class="items-center grid grid-cols-12 border-b border-gray-300 py-2.5"
|
||||
>
|
||||
<p
|
||||
data-name="url"
|
||||
class="ml-1 dark:text-gray-400 dark:opacity-80 text-sm col-span-8 m-0 my-1"
|
||||
></p>
|
||||
<p
|
||||
data-name="count"
|
||||
class="ml-1 dark:text-gray-400 dark:opacity-80 text-sm col-span-4 m-0 my-1"
|
||||
></p>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- end list-->
|
||||
</div>
|
||||
<!-- end list container-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<input type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden />
|
||||
<div class="core-layout">
|
||||
{% if is_used and is_metrics %}
|
||||
<!-- info-->
|
||||
<div class="core-card">
|
||||
<h5 class="core-card-title">INFO</h5>
|
||||
<div class="core-card-text-container">
|
||||
<p data-info class="core-card-text"></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
<div data-fetch-success-show class="hidden core-card-list w-large">
|
||||
<div class="core-card-list-container">
|
||||
<h5 class="core-card-list-title">LIMIT AND REQUEST LIST</h5>
|
||||
</div>
|
||||
<div class="core-card-list-container">
|
||||
<!-- list container-->
|
||||
<div class="core-card-list-wrap w-large">
|
||||
<!-- header-->
|
||||
<p class="core-card-list-header col-span-8">URL</p>
|
||||
<p class="core-card-list-header col-span-4">Count</p>
|
||||
<!-- end header-->
|
||||
<!-- list -->
|
||||
<ul class="col-span-12 w-full">
|
||||
<li data-item class="core-card-list-item">
|
||||
<p data-name="url" class="core-card-list-item-content col-span-8"></p>
|
||||
<p data-name="count" class="core-card-list-item-content col-span-4"></p>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- end list-->
|
||||
</div>
|
||||
<!-- end list container-->
|
||||
</div>
|
||||
</div>
|
||||
<script nonce="{{script_nonce}}">
|
||||
// Use SetupPlugin class that is on static/js/plugins/setup.js
|
||||
const setPlugin = new SetupPlugin({
|
||||
info: {
|
||||
|
|
@ -86,46 +53,29 @@
|
|||
listNames: ["url", "count"],
|
||||
},
|
||||
});
|
||||
</script>
|
||||
{% else %}
|
||||
<div
|
||||
class="h-fit transition hover:scale-102 col-span-12 md:col-span-6 2xl:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<div class="flex justify-between">
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">Deactivated</h5>
|
||||
<!-- icon -->
|
||||
<div
|
||||
role="img"
|
||||
class="dark:brightness-90 inline-block w-12 h-12 text-center rounded-circle bg-yellow-500"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="scale-75 leading-none text-lg relative fill-yellow-500 stroke-white"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</script>
|
||||
{% else %}
|
||||
<div class="core-card">
|
||||
<div class="core-card-wrap">
|
||||
<h5 class="core-card-title">Deactivated</h5>
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-svg-container">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="core-card-deactivated-svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<div class="core-card-text-container">
|
||||
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center my-2">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
>
|
||||
This plugin need to be activated to get metrics.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -4,4 +4,58 @@ location / {
|
|||
set $reason_data "";
|
||||
return {{ DENY_HTTP_STATUS }};
|
||||
}
|
||||
ssl_client_hello_by_lua_block {
|
||||
local ssl_clt = require "ngx.ssl.clienthello"
|
||||
local utils = require "bunkerweb.utils"
|
||||
local clogger = require "bunkerweb.logger"
|
||||
local cdatastore = require "bunkerweb.datastore"
|
||||
local logger = clogger:new("SSL-DISABLE")
|
||||
local datastore = cdatastore:new()
|
||||
local ngx = ngx
|
||||
local exit = ngx.exit
|
||||
local ERROR = ngx.ERROR
|
||||
local WARN = ngx.WARN
|
||||
local ERR = ngx.ERR
|
||||
local INFO = ngx.INFO
|
||||
local get_variable = utils.get_variable
|
||||
local host, err = ssl_clt.get_client_hello_server_name()
|
||||
if not host then
|
||||
logger:log(WARN, "can't get SNI host, denying access : " .. (err or "no SNI"))
|
||||
return exit(ERROR)
|
||||
end
|
||||
logger:log(INFO, "SNI host is " .. host)
|
||||
local multisite, err = get_variable("MULTISITE", false)
|
||||
if not multisite then
|
||||
logger:log(ERR, "can't get MULTISITE variable : " .. err)
|
||||
return
|
||||
end
|
||||
if multisite == "no" then
|
||||
local domains, err = get_variable("SERVER_NAME", false)
|
||||
if not domains then
|
||||
logger:log(ERR, "can't get SERVER_NAME variable : " .. err)
|
||||
return
|
||||
end
|
||||
for domain in domains:gmatch("%S+") do
|
||||
if host == domain then
|
||||
return
|
||||
end
|
||||
end
|
||||
else
|
||||
local variables, err = datastore:get("variables", true)
|
||||
if not variables then
|
||||
logger:log(ERR, "can't get variables : " .. err)
|
||||
return
|
||||
end
|
||||
for server_name, server_vars in pairs(variables) do
|
||||
local domains = server_vars["SERVER_NAME"]
|
||||
for domain in domains:gmatch("%S+") do
|
||||
if host == domain then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
logger:log(WARN, "unknown SNI host " .. host .. ", denying access")
|
||||
exit(ERROR)
|
||||
}
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{% if IS_LOADING != "yes" and DISABLE_DEFAULT_SERVER == "no" +%}
|
||||
root /usr/share/bunkerweb/core/misc/files;
|
||||
location / {
|
||||
try_files /default.html =404;
|
||||
}
|
||||
{% if IS_LOADING != "yes" and DISABLE_DEFAULT_SERVER == "no" +%}
|
||||
root /usr/share/bunkerweb/core/misc/files;
|
||||
location / {
|
||||
try_files /default.html =404;
|
||||
}
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
{% if REDIRECT_HTTP_TO_HTTPS == "yes" +%}
|
||||
if ($scheme = http) {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
{% elif AUTO_REDIRECT_HTTP_TO_HTTPS == "yes" +%}
|
||||
{% if AUTO_LETS_ENCRYPT == "yes" or USE_CUSTOM_SSL == "yes" or GENERATE_SELF_SIGNED_SSL == "yes" +%}
|
||||
if ($scheme = http) {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
{% endif +%}
|
||||
{% endif +%}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from json import dumps
|
||||
from os import getenv, sep
|
||||
from os.path import join
|
||||
from pathlib import Path
|
||||
from sys import exit as sys_exit, path as sys_path
|
||||
from traceback import format_exc
|
||||
from typing import Any, Dict
|
||||
|
||||
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",), ("db",))]:
|
||||
if deps_path not in sys_path:
|
||||
sys_path.append(deps_path)
|
||||
|
||||
from Database import Database # type: ignore
|
||||
from logger import setup_logger # type: ignore
|
||||
from jobs import cache_file, is_cached_file # type: ignore
|
||||
|
||||
from requests import post
|
||||
|
||||
logger = setup_logger("ANONYMOUS-REPORT", getenv("LOG_LEVEL", "INFO"))
|
||||
status = 0
|
||||
|
||||
if getenv("SEND_ANONYMOUS_REPORT", "yes") != "yes":
|
||||
logger.info("Skipping the sending of anonymous report (disabled)")
|
||||
sys_exit(status)
|
||||
|
||||
anonymous_report_path = Path(sep, "var", "cache", "bunkerweb", "anonymous_report")
|
||||
anonymous_report_path.mkdir(parents=True, exist_ok=True)
|
||||
tmp_anonymous_report_path = Path(sep, "var", "tmp", "bunkerweb", "anonymous_report")
|
||||
tmp_anonymous_report_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
try:
|
||||
db = Database(logger, sqlalchemy_string=getenv("DATABASE_URI", None), pool=False)
|
||||
if is_cached_file(anonymous_report_path.joinpath("last_report.json"), "day", db):
|
||||
logger.info("Skipping the sending of anonymous report (already sent today)")
|
||||
sys_exit(0)
|
||||
|
||||
# ? Get version and integration of BunkerWeb
|
||||
data: Dict[str, Any] = db.get_metadata()
|
||||
data["integration"] = data["integration"].lower()
|
||||
data["database"] = db.database_uri.split(":")[0].split("+")[0]
|
||||
data["service_number"] = str(len(getenv("SERVER_NAME", "").split(" ")))
|
||||
data["use_ui"] = getenv("USE_UI", "no")
|
||||
if data["use_ui"] == "no":
|
||||
for server in getenv("SERVER_NAME", "").split(" "):
|
||||
if getenv(f"{server}_USE_UI", "no") == "yes":
|
||||
data["use_ui"] = "yes"
|
||||
break
|
||||
data["external_plugins"] = [plugin["id"] for plugin in db.get_plugins(external=True)]
|
||||
tmp_anonymous_report_path.joinpath("last_report.json").write_text(dumps(data, indent=4), encoding="utf-8")
|
||||
|
||||
response = post("https://api.bunkerweb.io/data", json=data, headers={"User-Agent": f"BunkerWeb/{data['version']}"}, allow_redirects=True, timeout=10)
|
||||
response.raise_for_status()
|
||||
|
||||
cached, err = cache_file(tmp_anonymous_report_path.joinpath("last_report.json"), anonymous_report_path.joinpath("last_report.json"), None, db)
|
||||
except SystemExit as e:
|
||||
status = e.code
|
||||
except:
|
||||
status = 2
|
||||
logger.error(f"Exception while running anonymous-report.py :\n{format_exc()}")
|
||||
|
||||
sys_exit(status)
|
||||
|
|
@ -39,7 +39,7 @@ try:
|
|||
"-nodes",
|
||||
"-x509",
|
||||
"-newkey",
|
||||
"rsa:4096",
|
||||
"ed25519",
|
||||
"-keyout",
|
||||
str(cert_path.joinpath("cert.key")),
|
||||
"-out",
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ local misc = class("misc", plugin)
|
|||
local ngx = ngx
|
||||
local HTTP_NOT_ALLOWED = ngx.HTTP_NOT_ALLOWED
|
||||
local HTTP_BAD_REQUEST = ngx.HTTP_BAD_REQUEST
|
||||
local HTTP_MOVED_PERMANENTLY = ngx.HTTP_MOVED_PERMANENTLY
|
||||
local regex_match = utils.regex_match
|
||||
|
||||
function misc:initialize(ctx)
|
||||
|
|
@ -15,6 +16,21 @@ function misc:initialize(ctx)
|
|||
end
|
||||
|
||||
function misc:access()
|
||||
-- Check if we need to redirect to HTTPS
|
||||
if
|
||||
self.ctx.bw.scheme == "http"
|
||||
and (
|
||||
(self.ctx.bw.https_configured == "yes" and self.variables["AUTO_REDIRECT_HTTP_TO_HTTPS"] == "yes")
|
||||
or self.variables["REDIRECT_HTTP_TO_HTTPS"] == "yes"
|
||||
)
|
||||
then
|
||||
return self:ret(
|
||||
true,
|
||||
"redirect to HTTPS",
|
||||
HTTP_MOVED_PERMANENTLY,
|
||||
"https://" .. self.ctx.bw.http_host .. self.ctx.bw.request_uri
|
||||
)
|
||||
end
|
||||
-- Check if method is valid
|
||||
local method = self.ctx.bw.request_method
|
||||
if not method or not regex_match(method, "^[A-Z]+$") then
|
||||
|
|
@ -23,10 +39,10 @@ function misc:access()
|
|||
-- Check if method is allowed
|
||||
for allowed_method in self.variables["ALLOWED_METHODS"]:gmatch("[^|]+") do
|
||||
if method == allowed_method then
|
||||
self:set_metric("counters", "failed_method", 1)
|
||||
return self:ret(true, "method " .. method .. " is allowed")
|
||||
end
|
||||
end
|
||||
self:set_metric("counters", "failed_method", 1)
|
||||
return self:ret(true, "method " .. method .. " is not allowed", HTTP_NOT_ALLOWED)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -158,15 +158,6 @@
|
|||
"regex": "^(403|444)$",
|
||||
"type": "select",
|
||||
"select": ["403", "444"]
|
||||
},
|
||||
"SEND_ANONYMOUS_REPORT": {
|
||||
"context": "global",
|
||||
"default": "yes",
|
||||
"help": "Send anonymous report to BunkerWeb maintainers.",
|
||||
"id": "send-anonymous-report",
|
||||
"label": "Send anonymous report",
|
||||
"regex": "^(yes|no)$",
|
||||
"type": "check"
|
||||
}
|
||||
},
|
||||
"jobs": [
|
||||
|
|
@ -181,12 +172,6 @@
|
|||
"file": "update-check.py",
|
||||
"every": "day",
|
||||
"reload": false
|
||||
},
|
||||
{
|
||||
"name": "anonymous-report",
|
||||
"file": "anonymous-report.py",
|
||||
"every": "day",
|
||||
"reload": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,118 +1,66 @@
|
|||
{% extends "base.html" %} {% block content %}
|
||||
<input
|
||||
type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden
|
||||
/>
|
||||
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
<div class="col-span-12 grid grid-cols-12 gap-4">
|
||||
<!-- info-->
|
||||
<div
|
||||
class="h-fit transition hover:scale-102 col-span-12 md:col-span-6 2xl:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
|
||||
|
||||
<div class="mx-1 flex justify-start items-center my-4">
|
||||
<p
|
||||
data-info
|
||||
class="transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-500 dark:opacity-80"
|
||||
></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="h-fit dark:brightness-110 max-h-none sm:max-h-28 hover:scale-102 transition col-span-12 md:col-span-6 2xl:col-span-4 flex p-4 justify-between w-full shadow-md break-words bg-white dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p
|
||||
class="mb-2 font-sans text-sm font-semibold leading-normal uppercase dark:text-white dark:opacity-60"
|
||||
>
|
||||
DEFAULT SERVER DISABLED
|
||||
</p>
|
||||
<h5
|
||||
data-count-server-disabled
|
||||
class="mb-1 font-bold dark:text-white/90"
|
||||
></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-sky-500 mx-0.5">
|
||||
total
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div
|
||||
role="img"
|
||||
class="dark:brightness-90 inline-block w-12 h-12 text-center rounded-circle bg-orange-600"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="scale-[0.55] leading-none text-lg relative fill-white"
|
||||
>
|
||||
<path
|
||||
d="M4.08 5.227A3 3 0 0 1 6.979 3H17.02a3 3 0 0 1 2.9 2.227l2.113 7.926A5.228 5.228 0 0 0 18.75 12H5.25a5.228 5.228 0 0 0-3.284 1.153L4.08 5.227Z"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M5.25 13.5a3.75 3.75 0 1 0 0 7.5h13.5a3.75 3.75 0 1 0 0-7.5H5.25Zm10.5 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm3.75-.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="h-fit dark:brightness-110 max-h-none sm:max-h-28 hover:scale-102 transition col-span-12 md:col-span-6 2xl:col-span-4 flex p-4 justify-between w-full shadow-md break-words bg-white dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
|
||||
>
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p
|
||||
class="mb-2 font-sans text-sm font-semibold leading-normal uppercase dark:text-white dark:opacity-60"
|
||||
>
|
||||
DISALLOWED METHODS
|
||||
</p>
|
||||
<h5
|
||||
data-count-disallowed-methods
|
||||
class="mb-1 font-bold dark:text-white/90"
|
||||
></h5>
|
||||
|
||||
<p class="mb-0 dark:text-white dark:opacity-60">
|
||||
<span class="font-bold leading-normal text-sm text-sky-500 mx-0.5">
|
||||
count
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div
|
||||
role="img"
|
||||
class="dark:brightness-90 inline-block w-12 h-12 text-center rounded-circle bg-lime-600"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="scale-50 leading-none text-lg relative fill-white"
|
||||
>
|
||||
<path
|
||||
d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
|
||||
<script>
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<input type="csrf_token"
|
||||
name="csrf_token"
|
||||
value="{{ csrf_token }}"
|
||||
class="hidden"
|
||||
hidden />
|
||||
<div class="core-layout">
|
||||
{% if is_used and is_metrics %}
|
||||
<div class="core-layout">
|
||||
<!-- info-->
|
||||
<div class="core-card">
|
||||
<h5 class="core-card-title">INFO</h5>
|
||||
<div class="core-card-text-container">
|
||||
<p data-info class="core-card-text"></p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
</div>
|
||||
<div class="core-card-metrics">
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p class="core-card-metrics-name">DEFAULT SERVER DISABLED</p>
|
||||
<h5 data-count-server-disabled class="core-card-title"></h5>
|
||||
<p class="core-card-metrics-subtitle">
|
||||
<span class="core-card-metrics-subtitle-content info">total</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-svg-container orange">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="scale-[0.55] core-card-metrics-svg">
|
||||
<path d="M4.08 5.227A3 3 0 0 1 6.979 3H17.02a3 3 0 0 1 2.9 2.227l2.113 7.926A5.228 5.228 0 0 0 18.75 12H5.25a5.228 5.228 0 0 0-3.284 1.153L4.08 5.227Z" />
|
||||
<path fill-rule="evenodd" d="M5.25 13.5a3.75 3.75 0 1 0 0 7.5h13.5a3.75 3.75 0 1 0 0-7.5H5.25Zm10.5 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm3.75-.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0Z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<div class="core-card-metrics">
|
||||
<!-- text -->
|
||||
<div>
|
||||
<p class="core-card-metrics-name">DISALLOWED METHODS</p>
|
||||
<h5 data-count-disallowed-methods class="core-card-title"></h5>
|
||||
<p class="core-card-metrics-subtitle">
|
||||
<span class="core-card-metrics-subtitle-content info">count</span>
|
||||
</p>
|
||||
</div>
|
||||
<!-- end text -->
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-svg-container lime">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
class="size-small core-card-metrics-svg">
|
||||
<path d="M18.75 12.75h1.5a.75.75 0 0 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM12 6a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 6ZM12 18a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 12 18ZM3.75 6.75h1.5a.75.75 0 1 0 0-1.5h-1.5a.75.75 0 0 0 0 1.5ZM5.25 18.75h-1.5a.75.75 0 0 1 0-1.5h1.5a.75.75 0 0 1 0 1.5ZM3 12a.75.75 0 0 1 .75-.75h7.5a.75.75 0 0 1 0 1.5h-7.5A.75.75 0 0 1 3 12ZM9 3.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5ZM12.75 12a2.25 2.25 0 1 1 4.5 0 2.25 2.25 0 0 1-4.5 0ZM9 15.75a2.25 2.25 0 1 0 0 4.5 2.25 2.25 0 0 0 0-4.5Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<script nonce="{{script_nonce}}">
|
||||
// Use SetupPlugin class that is on static/js/plugins/setup.js
|
||||
const setPlugin = new SetupPlugin({
|
||||
info: {
|
||||
|
|
@ -131,6 +79,29 @@
|
|||
type: "text",
|
||||
},
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</script>
|
||||
{% else %}
|
||||
<div class="core-card">
|
||||
<div class="core-card-wrap">
|
||||
<h5 class="core-card-title">Deactivated</h5>
|
||||
<!-- icon -->
|
||||
<div role="img" class="core-card-svg-container">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="core-card-deactivated-svg">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z" />
|
||||
</svg>
|
||||
</div>
|
||||
<!-- end icon -->
|
||||
</div>
|
||||
<div class="core-card-text-container">
|
||||
<p data-info class="core-card-text">This plugin need to be activated to get metrics.</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- end info -->
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ SecAuditLog /var/log/bunkerweb/modsec_audit.log
|
|||
|
||||
# include OWASP CRS configurations
|
||||
{% if USE_MODSECURITY_CRS == "yes" %}
|
||||
include /usr/share/bunkerweb/core/modsecurity/files/crs-setup.conf
|
||||
include /usr/share/bunkerweb/core/modsecurity/files/crs-setup-v{{ MODSECURITY_CRS_VERSION }}.conf
|
||||
|
||||
# custom CRS configurations before loading rules (e.g. exclusions)
|
||||
{% if is_custom_conf("/etc/bunkerweb/configs/modsec-crs") %}
|
||||
|
|
@ -100,7 +100,7 @@ SecRule ENV:is_whitelisted "yes" "id:1000,phase:1,allow,nolog,ctl:ruleEngine=Off
|
|||
{% endif +%}
|
||||
|
||||
# include OWASP CRS rules
|
||||
include /usr/share/bunkerweb/core/modsecurity/files/coreruleset/rules/*.conf
|
||||
include /usr/share/bunkerweb/core/modsecurity/files/coreruleset-v{{ MODSECURITY_CRS_VERSION }}/rules/*.conf
|
||||
{% endif +%}
|
||||
|
||||
# custom rules after loading the CRS
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue