diff --git a/CHANGELOG.md b/CHANGELOG.md index 84674038f..6dc2e382c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,44 @@ # Changelog -## v1.4.3 - +## v1.4.6 + +- Fix error in the UI when a service have multiple domains +- Fix bwcli bans command +- Fix documentation about Linux Fedora install +- Fix DISABLE_DEFAULT_SERVER=yes not working with HTTPS +- Add INTERCEPTED_ERROR_CODES setting + +## v1.4.5 - 2022/11/26 + +- Fix bwcli syntax error +- Fix UI not working using Linux integration +- Fix missing openssl dep in autoconf +- Fix typo in selfsigned job + +## v1.4.4 - 2022/11/10 + +- Fix k8s controller not watching the events when there is an exception +- Fix python dependencies bug in CentOS and Fedora +- Fix incorrect log when reloading nginx using Linux integration +- Fix UI dev mode, production mode is now the default +- Fix wrong exposed port in the UI container +- Fix endless loading in the UI +- Fix \*_CUSTOM_CONF_\* dissapear when jobs are executed +- Fix various typos in documentation +- Fix warning about StartLimitIntervalSec directive when using Linux +- Fix incorrect log when issuing certbot renew +- Fix certbot renew error when using Linux or Docker integration +- Add greylist core feature +- Add BLACKLIST_IGNORE_\* settings +- Add automatic change of SecRequestBodyLimit modsec directive based on MAX_CLIENT_SIZE setting +- Add MODSECURITY_SEC_RULE_ENGINE and MODSECURITY_SEC_AUDIT_LOG_PARTS settings +- Add manual ban and get bans to the API/CLI +- Add Brawdunoir community example +- Improve core plugins order and add documentation about it +- Improve overall documentation +- Improve CI/CD + +## v1.4.3 - 2022/08/26 - Fix various documentation errors/typos and add various enhancements - Fix ui.env not read when using Linux integration diff --git a/README.md b/README.md index 0240b61be..dd370692d 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,12 @@

- BunkerWeb logo + BunkerWeb logo

- - - - + - - + +

@@ -30,7 +27,7 @@ > Make security by default great again ! -# Bunkerweb +# BunkerWeb

overview @@ -215,7 +212,7 @@ List of supported Linux distros : [Ansible](https://docs.ansible.com/ansible/latest/index.html) is an IT automation tool. It can configure systems, deploy software, and orchestrate more advanced IT tasks such as continuous deployments or zero downtime rolling updates. -A specific BunkerWeb Ansible role is available on [Ansible Galaxy](https://galaxy.ansible.com/fl0ppy_d1sk/bunkerweb) (source code is available [here](https://github.com/bunkerity/bunkerweb-ansible)). +A specific BunkerWeb Ansible role is available on [Ansible Galaxy](https://galaxy.ansible.com/bunkerity/bunkerweb) (source code is available [here](https://github.com/bunkerity/bunkerweb-ansible)). You will find more information in the [Ansible section](https://docs.bunkerweb.io/latest/integrations/#ansible) of the documentation. @@ -265,12 +262,12 @@ BunkerWeb comes with a plugin system to make it possible to easily add new featu Here is the list of "official" plugins that we maintain (see the [bunkerweb-plugins](https://github.com/bunkerity/bunkerweb-plugins) repository for more information) : -| Name | Version | Description | Link | -| :------------: | :-----: | :------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------: | +| Name | Version | Description | Link | +| :------------: | :-----: | :------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------: | | **ClamAV** | 0.1 | Automatically scans uploaded files with the ClamAV antivirus engine and denies the request when a file is detected as malicious. | [bunkerweb-plugins/clamav](https://github.com/bunkerity/bunkerweb-plugins/tree/main/clamav) | | **CrowdSec** | 0.1 | CrowdSec bouncer for BunkerWeb. | [bunkerweb-plugins/crowdsec](https://github.com/bunkerity/bunkerweb-plugins/tree/main/crowdsec) | -| **Discord** | 0.1 | Send security notifications to a Discord channel using a Webhook. | [bunkerweb-plugins/discord](https://github.com/bunkerity/bunkerweb-plugins/tree/main/discord) | -| **Slack** | 0.1 | Send security notifications to a Slack channel using a Webhook. | [bunkerweb-plugins/slack](https://github.com/bunkerity/bunkerweb-plugins/tree/main/slack) | +| **Discord** | 0.1 | Send security notifications to a Discord channel using a Webhook. | [bunkerweb-plugins/discord](https://github.com/bunkerity/bunkerweb-plugins/tree/main/discord) | +| **Slack** | 0.1 | Send security notifications to a Slack channel using a Webhook. | [bunkerweb-plugins/slack](https://github.com/bunkerity/bunkerweb-plugins/tree/main/slack) | | **VirusTotal** | 0.1 | Automatically scans uploaded files with the VirusTotal API and denies the request when a file is detected as malicious. | [bunkerweb-plugins/virustotal](https://github.com/bunkerity/bunkerweb-plugins/tree/main/virustotal) | You will find more information in the [plugins section](https://docs.bunkerweb.io/latest/plugins) of the documentation. @@ -309,4 +306,4 @@ If you would like to contribute to the plugins you can read the [contributing gu # Security policy -We take security bugs as serious issues and encourage responsible disclosure, see our [security policy](https://github.com/bunkerity/bunkerweb/tree/master/SECURITY.md) for more information. +We take security bugs as serious issues and encourage responsible disclosure, see our [security policy](https://github.com/bunkerity/bunkerweb/tree/master/SECURITY.md) for more information. \ No newline at end of file diff --git a/docs/backup-new-integrations.md b/docs/backup-new-integrations.md index 062900669..f8378c6c6 100644 --- a/docs/backup-new-integrations.md +++ b/docs/backup-new-integrations.md @@ -17,13 +17,13 @@ sudo dnf install nginx-1.20.2 ``` - And finally install BunkerWeb 1.4.4 : + And finally install BunkerWeb 1.4.6 : ```shell wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm && \ rpm -Uvh epel-release*rpm && \ curl -s https://packagecloud.io/install/repositories/bunkerity/bunkerweb/script.rpm.sh | sudo bash && \ sudo dnf check-update && \ - sudo dnf install -y bunkerweb-1.4.4 + sudo dnf install -y bunkerweb-1.4.6 ``` To prevent upgrading NGINX and/or BunkerWeb packages when executing `dnf upgrade`, you can use the following command : diff --git a/docs/integrations.md b/docs/integrations.md index 4ff7e9423..94105de6f 100644 --- a/docs/integrations.md +++ b/docs/integrations.md @@ -12,7 +12,7 @@ Using BunkerWeb as a [Docker](https://www.docker.com/) container is a quick and We provide ready-to-use prebuilt images for x64, x86 armv8 and armv7 architectures on [Docker Hub](https://hub.docker.com/r/bunkerity/bunkerweb) : ```shell -docker pull bunkerity/bunkerweb:1.4.4 +docker pull bunkerity/bunkerweb:1.4.6 ``` Alternatively, you can build the Docker images directly from the [source](https://github.com/bunkerity/bunkerweb) (and get a coffee ☕ because it may take a long time depending on your hardware) : @@ -39,7 +39,7 @@ docker run \ -e MY_SETTING=value \ -e "MY_OTHER_SETTING=value with spaces" \ ... - bunkerity/bunkerweb:1.4.4 + bunkerity/bunkerweb:1.4.6 ``` Here is the docker-compose equivalent : @@ -48,7 +48,7 @@ Here is the docker-compose equivalent : ... services: mybunker: - image: bunkerity/bunkerweb:1.4.4 + image: bunkerity/bunkerweb:1.4.6 environment: - MY_SETTING=value ``` @@ -73,7 +73,7 @@ docker run \ ... -v bw_data:/data \ ... - bunkerity/bunkerweb:1.4.4 + bunkerity/bunkerweb:1.4.6 ``` Here is the docker-compose equivalent : @@ -82,7 +82,7 @@ Here is the docker-compose equivalent : ... services: mybunker: - image: bunkerity/bunkerweb:1.4.4 + image: bunkerity/bunkerweb:1.4.6 volumes: - bw_data:/data ... @@ -152,7 +152,7 @@ docker run \ ... --network mynetwork \ ... - bunkerity/bunkerweb:1.4.4 + bunkerity/bunkerweb:1.4.6 ``` You will also need to do the same with your web application(s). Please note that the other containers are accessible using their name as the hostname. @@ -163,7 +163,7 @@ Here is the docker-compose equivalent : ... services: mybunker: - image: bunkerity/bunkerweb:1.4.4 + image: bunkerity/bunkerweb:1.4.6 networks: - bw-net ... @@ -218,7 +218,7 @@ docker run \ -e SERVER_NAME= \ -e "API_WHITELIST_IP=127.0.0.0/8 10.20.30.0/24" \ -l bunkerweb.AUTOCONF \ - bunkerity/bunkerweb:1.4.4 && \ + bunkerity/bunkerweb:1.4.6 && \ docker network connect bw-services mybunker ``` @@ -235,7 +235,7 @@ docker run \ --network bw-autoconf \ -v bw-data:/data \ -v /var/run/docker.sock:/var/run/docker.sock:ro \ - bunkerity/bunkerweb-autoconf:1.4.4 + bunkerity/bunkerweb-autoconf:1.4.6 ``` Here is the docker-compose equivalent for the BunkerWeb autoconf stack : @@ -246,7 +246,7 @@ version: '3.5' services: mybunker: - image: bunkerity/bunkerweb:1.4.4 + image: bunkerity/bunkerweb:1.4.6 ports: - 80:8080 - 443:8443 @@ -262,7 +262,7 @@ services: - bw-services myautoconf: - image: bunkerity/bunkerweb-autoconf:1.4.4 + image: bunkerity/bunkerweb-autoconf:1.4.6 volumes: - bw-data:/data - /var/run/docker.sock:/var/run/docker.sock:ro @@ -364,7 +364,7 @@ docker service create \ -e MULTISITE=yes \ -e "API_WHITELIST_IP=127.0.0.0/8 10.20.30.0/24" \ -l bunkerweb.AUTOCONF \ - bunkerity/bunkerweb:1.4.4 + bunkerity/bunkerweb:1.4.6 ``` And the autoconf one : @@ -378,7 +378,7 @@ docker service \ --mount type=bind,source=/var/run/docker.sock,destination=/var/run/docker.sock,ro \ --mount type=volume,source=bw-data,destination=/data \ -e SWARM_MODE=yes \ - bunkerity/bunkerweb-autoconf:1.4.4 + bunkerity/bunkerweb-autoconf:1.4.6 ``` Here is the docker-compose equivalent (using `docker stack deploy`) : @@ -389,7 +389,7 @@ version: '3.5' services: mybunker: - image: bunkerity/bunkerweb:1.4.4 + image: bunkerity/bunkerweb:1.4.6 ports: - published: 80 target: 8080 @@ -416,7 +416,7 @@ services: - "bunkerweb.AUTOCONF" myautoconf: - image: bunkerity/bunkerweb-autoconf:1.4.4 + image: bunkerity/bunkerweb-autoconf:1.4.6 environment: - SWARM_MODE=yes volumes: @@ -706,11 +706,11 @@ Repositories of Linux packages for BunkerWeb are available on [PackageCloud](htt sudo apt install -y nginx=1.20.2-1~$(lsb_release -cs) ``` - And finally install BunkerWeb 1.4.4 : + And finally install BunkerWeb 1.4.6 : ```shell curl -s https://packagecloud.io/install/repositories/bunkerity/bunkerweb/script.deb.sh | sudo bash && \ sudo apt update && \ - sudo apt install -y bunkerweb=1.4.4 + sudo apt install -y bunkerweb=1.4.6 ``` To prevent upgrading NGINX and/or BunkerWeb packages when executing `apt upgrade`, you can use the following command : @@ -736,11 +736,11 @@ Repositories of Linux packages for BunkerWeb are available on [PackageCloud](htt sudo apt install -y nginx=1.20.2-1~jammy ``` - And finally install BunkerWeb 1.4.4 : + And finally install BunkerWeb 1.4.6 : ```shell curl -s https://packagecloud.io/install/repositories/bunkerity/bunkerweb/script.deb.sh | sudo bash && \ sudo apt update && \ - sudo apt install -y bunkerweb=1.4.4 + sudo apt install -y bunkerweb=1.4.6 ``` To prevent upgrading NGINX and/or BunkerWeb packages when executing `apt upgrade`, you can use the following command : @@ -755,13 +755,13 @@ Repositories of Linux packages for BunkerWeb are available on [PackageCloud](htt sudo dnf install -y nginx-1.20.2 ``` - And finally install BunkerWeb 1.4.4 : + And finally install BunkerWeb 1.4.6 : ```shell curl -s https://packagecloud.io/install/repositories/bunkerity/bunkerweb/script.rpm.sh | \ sed 's/yum install -y pygpgme --disablerepo='\''bunkerity_bunkerweb'\''/yum install -y python-gnupg/g' | \ sed 's/pypgpme_check=`rpm -qa | grep -qw pygpgme`/python-gnupg_check=`rpm -qa | grep -qw python-gnupg`/g' | sudo bash && \ sudo dnf makecache && \ - sudo dnf install -y bunkerweb-1.4.4 + sudo dnf install -y bunkerweb-1.4.6 ``` To prevent upgrading NGINX and/or BunkerWeb packages when executing `dnf upgrade`, you can use the following command : @@ -788,12 +788,12 @@ Repositories of Linux packages for BunkerWeb are available on [PackageCloud](htt sudo dnf install nginx-1.20.2 ``` - And finally install BunkerWeb 1.4.4 : + And finally install BunkerWeb 1.4.6 : ```shell dnf install -y epel-release && \ curl -s https://packagecloud.io/install/repositories/bunkerity/bunkerweb/script.rpm.sh | sudo bash && \ sudo dnf check-update && \ - sudo dnf install -y bunkerweb-1.4.4 + sudo dnf install -y bunkerweb-1.4.6 ``` To prevent upgrading NGINX and/or BunkerWeb packages when executing `dnf upgrade`, you can use the following command : @@ -931,7 +931,7 @@ Configuration of BunkerWeb is done by using specific role variables : | Name | Type | Description | Default value | |:-----:|:-----:|--------------|----------------| -| `bunkerweb_version` | string | Version of BunkerWeb to install. | `1.4.4` | +| `bunkerweb_version` | string | Version of BunkerWeb to install. | `1.4.6` | | `nginx_version` | string | Version of NGINX to install. | `1.20.2` | | `freeze_versions` | boolean | Prevent upgrade of BunkerWeb and NGINX when performing packages upgrades. | `true` | | `variables_env` | string | Path of the variables.env file to configure BunkerWeb. | `files/variables.env` | diff --git a/docs/plugins.md b/docs/plugins.md index 90034f390..18100d102 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -53,13 +53,13 @@ The first step is to install the plugin by putting the plugin files inside the c ... -v "${PWD}/bw-data:/data" \ ... - bunkerity/bunkerweb:1.4.4 + bunkerity/bunkerweb:1.4.6 ``` Here is the docker-compose equivalent : ```yaml mybunker: - image: bunkerity/bunkerweb:1.4.4 + image: bunkerity/bunkerweb:1.4.6 volumes: - ./bw-data:/data ... diff --git a/docs/quickstart-guide.md b/docs/quickstart-guide.md index 65681b383..652d25498 100644 --- a/docs/quickstart-guide.md +++ b/docs/quickstart-guide.md @@ -54,7 +54,7 @@ You will find more settings about reverse proxy in the [settings section](/1.4/s -e USE_REVERSE_PROXY=yes \ -e REVERSE_PROXY_URL=/ \ -e REVERSE_PROXY_HOST=http://myapp \ - bunkerity/bunkerweb:1.4.4 + bunkerity/bunkerweb:1.4.6 ``` Here is the docker-compose equivalent : @@ -64,7 +64,7 @@ You will find more settings about reverse proxy in the [settings section](/1.4/s services: mybunker: - image: bunkerity/bunkerweb:1.4.4 + image: bunkerity/bunkerweb:1.4.6 ports: - 80:8080 - 443:8443 @@ -379,7 +379,7 @@ You will find more settings about reverse proxy in the [settings section](/1.4/s -e app1.example.com_REVERSE_PROXY_HOST=http://myapp1 \ -e app2.example.com_REVERSE_PROXY_HOST=http://myapp2 \ -e app3.example.com_REVERSE_PROXY_HOST=http://myapp3 \ - bunkerity/bunkerweb:1.4.4 + bunkerity/bunkerweb:1.4.6 ``` Here is the docker-compose equivalent : @@ -389,7 +389,7 @@ You will find more settings about reverse proxy in the [settings section](/1.4/s services: mybunker: - image: bunkerity/bunkerweb:1.4.4 + image: bunkerity/bunkerweb:1.4.6 ports: - 80:8080 - 443:8443 @@ -981,13 +981,13 @@ REAL_IP_HEADER=X-Forwarded-For -e "REAL_IP_FROM=1.2.3.0/24 100.64.0.0/16" \ -e REAL_IP_HEADER=X-Forwarded-For \ ... - bunkerity/bunkerweb:1.4.4 + bunkerity/bunkerweb:1.4.6 ``` Here is the docker-compose equivalent : ```yaml mybunker: - image: bunkerity/bunkerweb:1.4.4 + image: bunkerity/bunkerweb:1.4.6 ... environment: - USE_REAL_IP=yes @@ -1006,13 +1006,13 @@ REAL_IP_HEADER=X-Forwarded-For -e "REAL_IP_FROM=1.2.3.0/24 100.64.0.0/16" \ -e REAL_IP_HEADER=X-Forwarded-For \ ... - bunkerity/bunkerweb:1.4.4 + bunkerity/bunkerweb:1.4.6 ``` Here is the docker-compose equivalent : ```yaml mybunker: - image: bunkerity/bunkerweb:1.4.4 + image: bunkerity/bunkerweb:1.4.6 ... environment: - USE_REAL_IP=yes @@ -1031,13 +1031,13 @@ REAL_IP_HEADER=X-Forwarded-For -e "REAL_IP_FROM=1.2.3.0/24 100.64.0.0/16" \ -e REAL_IP_HEADER=X-Forwarded-For \ ... - bunkerity/bunkerweb:1.4.4 + bunkerity/bunkerweb:1.4.6 ``` Here is the docker-compose equivalent (using `docker stack deploy`) : ```yaml mybunker: - image: bunkerity/bunkerweb:1.4.4 + image: bunkerity/bunkerweb:1.4.6 ... environment: - USE_REAL_IP=yes @@ -1062,7 +1062,7 @@ REAL_IP_HEADER=X-Forwarded-For spec: containers: - name: bunkerweb - image: bunkerity/bunkerweb:1.4.4 + image: bunkerity/bunkerweb:1.4.6 ... env: - name: USE_REAL_IP @@ -1146,13 +1146,13 @@ REAL_IP_HEADER=proxy_protocol -e "REAL_IP_FROM=1.2.3.0/24 100.64.0.0/16" \ -e REAL_IP_HEADER=proxy_protocol \ ... - bunkerity/bunkerweb:1.4.4 + bunkerity/bunkerweb:1.4.6 ``` Here is the docker-compose equivalent : ```yaml mybunker: - image: bunkerity/bunkerweb:1.4.4 + image: bunkerity/bunkerweb:1.4.6 ... environment: - USE_REAL_IP=yes @@ -1173,13 +1173,13 @@ REAL_IP_HEADER=proxy_protocol -e "REAL_IP_FROM=1.2.3.0/24 100.64.0.0/16" \ -e REAL_IP_HEADER=proxy_protocol \ ... - bunkerity/bunkerweb:1.4.4 + bunkerity/bunkerweb:1.4.6 ``` Here is the docker-compose equivalent : ```yaml mybunker: - image: bunkerity/bunkerweb:1.4.4 + image: bunkerity/bunkerweb:1.4.6 ... environment: - USE_REAL_IP=yes @@ -1200,13 +1200,13 @@ REAL_IP_HEADER=proxy_protocol -e "REAL_IP_FROM=1.2.3.0/24 100.64.0.0/16" \ -e REAL_IP_HEADER=proxy_protocol \ ... - bunkerity/bunkerweb:1.4.4 + bunkerity/bunkerweb:1.4.6 ``` Here is the docker-compose equivalent (using `docker stack deploy`) : ```yaml mybunker: - image: bunkerity/bunkerweb:1.4.4 + image: bunkerity/bunkerweb:1.4.6 ... environment: - USE_REAL_IP=yes @@ -1232,7 +1232,7 @@ REAL_IP_HEADER=proxy_protocol spec: containers: - name: bunkerweb - image: bunkerity/bunkerweb:1.4.4 + image: bunkerity/bunkerweb:1.4.6 ... env: - name: USE_REAL_IP @@ -1327,7 +1327,7 @@ Some integrations offer a more convenient way of applying configurations such as Here is a dummy example using a docker-compose file : ```yaml mybunker: - image: bunkerity/bunkerweb:1.4.4 + image: bunkerity/bunkerweb:1.4.6 environment: - | CUSTOM_CONF_SERVER_HTTP_hello-world= @@ -1369,13 +1369,13 @@ Some integrations offer a more convenient way of applying configurations such as ... -v "${PWD}/bw-data:/data" \ ... - bunkerity/bunkerweb:1.4.4 + bunkerity/bunkerweb:1.4.6 ``` Here is the docker-compose equivalent : ```yaml mybunker: - image: bunkerity/bunkerweb:1.4.4 + image: bunkerity/bunkerweb:1.4.6 volumes: - ./bw-data:/data ... @@ -1436,13 +1436,13 @@ Some integrations offer a more convenient way of applying configurations such as ... -v "${PWD}/bw-data:/data" \ ... - bunkerity/bunkerweb-autoconf:1.4.4 + bunkerity/bunkerweb-autoconf:1.4.6 ``` Here is the docker-compose equivalent : ```yaml myautoconf: - image: bunkerity/bunkerweb-autoconf:1.4.4 + image: bunkerity/bunkerweb-autoconf:1.4.6 volumes: - ./bw-data:/data ... @@ -1622,7 +1622,7 @@ BunkerWeb supports PHP using external or remote [PHP-FPM](https://www.php.net/ma -e AUTO_LETS_ENCRYPT=yes \ -e REMOTE_PHP=myphp \ -e REMOTE_PHP_PATH=/app \ - bunkerity/bunkerweb:1.4.4 + bunkerity/bunkerweb:1.4.6 ``` Here is the docker-compose equivalent : @@ -1632,7 +1632,7 @@ BunkerWeb supports PHP using external or remote [PHP-FPM](https://www.php.net/ma services: mybunker: - image: bunkerity/bunkerweb:1.4.4 + image: bunkerity/bunkerweb:1.4.6 ports: - 80:8080 - 443:8443 @@ -1674,7 +1674,7 @@ BunkerWeb supports PHP using external or remote [PHP-FPM](https://www.php.net/ma ... -v "${PWD}/myapp:/app" \ ... - bunkerity/bunkerweb:1.4.4 + bunkerity/bunkerweb:1.4.6 ``` Once BunkerWeb and autoconf are ready, you will be able to create the PHP-FPM container, mount the application folder inside the container and configure it using specific labels : @@ -1738,7 +1738,7 @@ BunkerWeb supports PHP using external or remote [PHP-FPM](https://www.php.net/ma ... -v "/shared/myapp:/app" \ ... - bunkerity/bunkerweb:1.4.4 + bunkerity/bunkerweb:1.4.6 ``` Once BunkerWeb and autoconf are ready, you will be able to create the PHP-FPM service, mount the application folder inside the container and configure it using specific labels : @@ -1984,7 +1984,7 @@ BunkerWeb supports PHP using external or remote [PHP-FPM](https://www.php.net/ma -e app2.example.com_REMOTE_PHP_PATH=/app \ -e app3.example.com_REMOTE_PHP=myphp3 \ -e app3.example.com_REMOTE_PHP_PATH=/app \ - bunkerity/bunkerweb:1.4.4 + bunkerity/bunkerweb:1.4.6 ``` Here is the docker-compose equivalent : @@ -1994,7 +1994,7 @@ BunkerWeb supports PHP using external or remote [PHP-FPM](https://www.php.net/ma services: mybunker: - image: bunkerity/bunkerweb:1.4.4 + image: bunkerity/bunkerweb:1.4.6 ports: - 80:8080 - 443:8443 @@ -2055,7 +2055,7 @@ BunkerWeb supports PHP using external or remote [PHP-FPM](https://www.php.net/ma ... -v "${PWD}/myapps:/apps" \ ... - bunkerity/bunkerweb:1.4.4 + bunkerity/bunkerweb:1.4.6 ``` Once BunkerWeb and autoconf are ready, you will be able to create the PHP-FPM containers, mount the right application folder inside each container and configure them using specific labels : @@ -2179,7 +2179,7 @@ BunkerWeb supports PHP using external or remote [PHP-FPM](https://www.php.net/ma ... -v "/shared/myapps:/apps" \ ... - bunkerity/bunkerweb:1.4.4 + bunkerity/bunkerweb:1.4.6 ``` Once BunkerWeb and autoconf are ready, you will be able to create the PHP-FPM service, mount the application folder inside the container and configure it using specific labels : diff --git a/docs/web-ui.md b/docs/web-ui.md index 0bc6ac267..7851aea7e 100644 --- a/docs/web-ui.md +++ b/docs/web-ui.md @@ -76,7 +76,7 @@ Because the web UI is a web application, the recommended installation procedure -e "bwadm.example.com_REVERSE_PROXY_HEADERS=X-Script-Name /changeme" \ -e bwadm.example.com_REVERSE_PROXY_INTERCEPT_ERRORS=no \ -l bunkerweb.UI \ - bunkerity/bunkerweb:1.4.4 && \ + bunkerity/bunkerweb:1.4.6 && \ docker network connect bw-ui mybunker ``` @@ -115,7 +115,7 @@ Because the web UI is a web application, the recommended installation procedure -e ADMIN_USERNAME=admin \ -e ADMIN_PASSWORD=changeme \ -e ABSOLUTE_URI=http(s)://bwadm.example.com/changeme/ \ - bunkerity/bunkerweb-ui:1.4.4 && \ + bunkerity/bunkerweb-ui:1.4.6 && \ docker network connect bw-docker myui ``` @@ -131,7 +131,7 @@ Because the web UI is a web application, the recommended installation procedure services: mybunker: - image: bunkerity/bunkerweb:1.4.4 + image: bunkerity/bunkerweb:1.4.6 networks: - bw-services - bw-ui @@ -154,7 +154,7 @@ Because the web UI is a web application, the recommended installation procedure - "bunkerweb.UI" myui: - image: bunkerity/bunkerweb-ui:1.4.4 + image: bunkerity/bunkerweb-ui:1.4.6 depends_on: - mydocker networks: diff --git a/examples/autoconf-configs/tests.json b/examples/autoconf-configs/tests.json index 530e3d36e..5c4596e1f 100644 --- a/examples/autoconf-configs/tests.json +++ b/examples/autoconf-configs/tests.json @@ -1,6 +1,7 @@ { "name": "autoconf-configs", "kinds": ["autoconf"], + "delay": 60, "timeout": 60, "tests": [ { diff --git a/examples/drupal/setup-linux.sh b/examples/drupal/setup-linux.sh index 1f56b43bc..5b2e0f04c 100755 --- a/examples/drupal/setup-linux.sh +++ b/examples/drupal/setup-linux.sh @@ -13,7 +13,7 @@ else echo "❌ No PHP user found" exit 1 fi -curl https://www.drupal.org/download-latest/tar.gz -Lo /tmp/drupal.tar.gz +curl https://ftp.drupal.org/files/projects/drupal-9.5.3.tar.gz -Lo /tmp/drupal.tar.gz tar -xzf /tmp/drupal.tar.gz -C /tmp current_dir="$(pwd)" cd /tmp/drupal-* diff --git a/examples/ghost/tests.json b/examples/ghost/tests.json index 0fbaa943b..b96fecbf2 100644 --- a/examples/ghost/tests.json +++ b/examples/ghost/tests.json @@ -2,7 +2,7 @@ "name": "ghost", "kinds": ["docker", "autoconf", "swarm", "kubernetes"], "timeout": 60, - "delay": 180, + "delay": 240, "tests": [ { "type": "string", diff --git a/examples/nextcloud/kubernetes.yml b/examples/nextcloud/kubernetes.yml index 32841e724..89373ae7f 100644 --- a/examples/nextcloud/kubernetes.yml +++ b/examples/nextcloud/kubernetes.yml @@ -7,7 +7,7 @@ metadata: bunkerweb.io/www.example.com_MAX_CLIENT_SIZE: "10G" bunkerweb.io/www.example.com_ALLOWED_METHODS: "GET|POST|HEAD|COPY|DELETE|LOCK|MKCOL|MOVE|PROPFIND|PROPPATCH|PUT|UNLOCK|OPTIONS" bunkerweb.io/www.example.com_X_FRAME_OPTIONS: "SAMEORIGIN" - bunkerweb.io/www.example.com_BAD_BEHAVIOR_STATUS_CODES: "400 401.4.4 405 444" + bunkerweb.io/www.example.com_BAD_BEHAVIOR_STATUS_CODES: "400 401 405 444" bunkerweb.io/www.example.com_LIMIT_REQ_URL_1: "/apps" bunkerweb.io/www.example.com_LIMIT_REQ_RATE_1: "5r/s" bunkerweb.io/www.example.com_LIMIT_REQ_URL_2: "/apps/text/session/sync" diff --git a/src/common/confs/default-server-http.conf b/src/common/confs/default-server-http.conf index a9dc6630f..5e7b01012 100644 --- a/src/common/confs/default-server-http.conf +++ b/src/common/confs/default-server-http.conf @@ -10,6 +10,15 @@ server { listen 0.0.0.0:{{ HTTP_PORT }} default_server {% if USE_PROXY_PROTOCOL == "yes" %}proxy_protocol{% endif %}; {% endif %} + # HTTPS listen +{% set os = import("os") %} +{% if os.path.isfile("/var/cache/bunkerweb/default-server-cert/cert.pem") +%} + {% if has_variable(all, "USE_CUSTOM_HTTPS", "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 %}; + ssl_certificate /var/cache/bunkerweb/selfsigned/{{ SERVER_NAME.split(" ")[0] }}.pem; + ssl_certificate_key /var/cache/bunkerweb/selfsigned/{{ SERVER_NAME.split(" ")[0] }}.key; + {% endif %} +{% endif %} {% if IS_LOADING == "yes" +%} root /usr/share/bunkerweb/loading; diff --git a/src/common/core/errors/plugin.json b/src/common/core/errors/plugin.json index da6cc46bb..b4e3f976c 100644 --- a/src/common/core/errors/plugin.json +++ b/src/common/core/errors/plugin.json @@ -20,7 +20,7 @@ "help": "List of HTTP error code intercepted by Bunkerweb", "id": "intercepted-error-codes", "label": "Intercepted error codes", - "regex": "^.*$", + "regex": "^( *([1-5]\\d{2})(?!.*\\2) *)+$", "type": "text" } } diff --git a/src/common/core/misc/jobs/default-server-cert.py b/src/common/core/misc/jobs/default-server-cert.py new file mode 100644 index 000000000..abc4c51ce --- /dev/null +++ b/src/common/core/misc/jobs/default-server-cert.py @@ -0,0 +1,78 @@ +#!/usr/bin/python3 + +from os import getenv, makedirs +from os.path import isfile +from subprocess import DEVNULL, STDOUT, run +from sys import exit as sys_exit, path as sys_path +from traceback import format_exc + +sys_path.extend( + ( + "/usr/share/bunkerweb/deps/python", + "/usr/share/bunkerweb/utils", + ) +) + +from logger import setup_logger + +logger = setup_logger("DEFAULT-SERVER-CERT", getenv("LOG_LEVEL", "INFO")) +status = 0 + +try: + + # Check if we need to generate a self-signed default cert for non-SNI "clients" + need_default_cert = False + if getenv("MULTISITE", "no") == "yes": + for first_server in getenv("SERVER_NAME", "").split(" "): + for check_var in [ + "USE_CUSTOM_HTTPS", + "AUTO_LETS_ENCRYPT", + "GENERATE_SELF_SIGNED_SSL", + ]: + if ( + getenv(f"{first_server}_{check_var}", getenv(check_var, "no")) + == "yes" + ): + need_default_cert = True + break + if need_default_cert: + break + elif getenv("DISABLE_DEFAULT_SERVER", "no") == "yes" and ( + getenv("USE_CUSTOM_HTTPS", "no") == "yes" + or getenv("AUTO_LETS_ENCRYPT", "no") == "yes" + or getenv("GENERATE_SELF_SIGNED_SSL", "no") == "yes" + ): + need_default_cert = True + + # Generate the self-signed certificate + if need_default_cert: + makedirs("/var/cache/bunkerweb/default-server-cert", exist_ok=True) + if not isfile("/var/cache/bunkerweb/default-server-cert/cert.pem"): + cmd = "openssl req -nodes -x509 -newkey rsa:4096 -keyout /var/cache/bunkerweb/default-server-cert/cert.key -out /var/cache/bunkerweb/default-server-cert/cert.pem -days 3650".split( + " " + ) + cmd.extend(["-subj", "/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd/"]) + proc = run(cmd, stdin=DEVNULL, stderr=STDOUT) + if proc.returncode != 0: + logger.error( + "Self-signed certificate generation failed for default server", + ) + status = 2 + else: + logger.info( + "Successfully generated self-signed certificate for default server", + ) + else: + logger.info( + "Skipping generation of self-signed certificate for default server (already present)", + ) + else: + logger.info( + "Skipping generation of self-signed certificate for default server (not needed)", + ) + +except: + status = 2 + logger.error(f"Exception while running default-server-cert.py :\n{format_exc()}") + +sys_exit(status) diff --git a/src/common/core/misc/plugin.json b/src/common/core/misc/plugin.json index 7d52a3d6a..d08e1ebab 100644 --- a/src/common/core/misc/plugin.json +++ b/src/common/core/misc/plugin.json @@ -159,5 +159,13 @@ "type": "select", "select": ["403", "444"] } - } + }, + "jobs": [ + { + "name": "default-server-cert", + "file": "default-server-cert.py", + "every": "once", + "reload": false + } + ] } diff --git a/src/linux/scripts/bunkerweb-ui.sh b/src/linux/scripts/bunkerweb-ui.sh index ebfeea4ee..2c488a335 100755 --- a/src/linux/scripts/bunkerweb-ui.sh +++ b/src/linux/scripts/bunkerweb-ui.sh @@ -6,8 +6,8 @@ export PYTHONPATH=/usr/share/bunkerweb/deps/python # Create the ui.env file if it doesn't exist if [ ! -f /etc/bunkerweb/ui.env ]; then echo "ADMIN_USERNAME=admin" > /etc/bunkerweb/ui.env - echo "ADMIN_PASSWORD=PasswordChanged" >> /etc/bunkerweb/ui.env - echo "ABSOLUTE_URI=" >> /etc/bunkerweb/ui.env + echo "ADMIN_PASSWORD=changeme" >> /etc/bunkerweb/ui.env + echo "ABSOLUTE_URI=http://mydomain.ext/mypath/" >> /etc/bunkerweb/ui.env fi # Function to start the UI @@ -18,7 +18,7 @@ start() { fi source /etc/bunkerweb/ui.env export $(cat /etc/bunkerweb/ui.env) - python3 -m gunicorn --bind=127.0.0.1:7000 --chdir /usr/share/bunkerweb/ui/ --workers=1 --threads=2 main:app & + python3 -m gunicorn --graceful-timeout=0 --bind=127.0.0.1:7000 --chdir /usr/share/bunkerweb/ui/ --workers=1 --threads=2 main:app & echo $! > /var/tmp/bunkerweb/ui.pid } diff --git a/tests/AutoconfTest.py b/tests/AutoconfTest.py index b2d45be8d..deaa66540 100644 --- a/tests/AutoconfTest.py +++ b/tests/AutoconfTest.py @@ -48,7 +48,11 @@ class AutoconfTest(Test): "10.20.1.1:5000/bw-autoconf-tests:latest", ) Test.replace_in_file(compose, r"\./bw\-data:/", "/tmp/bw-data:/") - proc = run("docker-compose pull", cwd="/tmp/autoconf", shell=True) + proc = run( + "docker-compose pull --ignore-pull-failures", + cwd="/tmp/autoconf", + shell=True, + ) if proc.returncode != 0: raise (Exception("docker-compose pull failed (autoconf stack)")) proc = run("docker-compose up -d", cwd="/tmp/autoconf", shell=True) @@ -123,7 +127,11 @@ class AutoconfTest(Test): ) if proc.returncode != 0: raise (Exception("cp bw-data failed")) - proc = run("docker-compose -f autoconf.yml pull", shell=True, cwd=test) + proc = run( + "docker-compose -f autoconf.yml pull --ignore-pull-failures", + shell=True, + cwd=test, + ) if proc.returncode != 0: raise (Exception("docker-compose pull failed")) proc = run("docker-compose -f autoconf.yml up -d", shell=True, cwd=test) @@ -153,6 +161,10 @@ class AutoconfTest(Test): def _debug_fail(self): autoconf = "/tmp/autoconf" - run("docker-compose logs", shell=True, cwd=autoconf) + proc = run("docker-compose logs", shell=True, cwd=autoconf) + if proc.returncode != 0: + raise (Exception("docker-compose logs failed")) test = f"/tmp/tests/{self._name}" - run("docker-compose -f autoconf.yml logs", shell=True, cwd=test) + proc = run("docker-compose -f autoconf.yml logs", shell=True, cwd=test) + if proc.returncode != 0: + raise (Exception("docker-compose -f autoconf.yml logs failed")) diff --git a/tests/DockerTest.py b/tests/DockerTest.py index 66c0ca3a5..1848d3c3a 100644 --- a/tests/DockerTest.py +++ b/tests/DockerTest.py @@ -51,6 +51,11 @@ class DockerTest(Test): ) Test.replace_in_file(compose, r"\./bw\-data:/", "/tmp/bw-data:/") Test.replace_in_file(compose, r"\- bw_data:/", "- /tmp/bw-data:/") + Test.replace_in_file( + compose, + r"AUTO_LETS_ENCRYPT=yes", + "AUTO_LETS_ENCRYPT=yes\n - USE_LETS_ENCRYPT_STAGING=yes", + ) for ex_domain, test_domain in self._domains.items(): Test.replace_in_files(test, ex_domain, test_domain) Test.rename(test, ex_domain, test_domain) @@ -67,7 +72,9 @@ class DockerTest(Test): ) if proc.returncode != 0: raise (Exception("cp bw-data failed")) - proc = run("docker-compose pull", shell=True, cwd=test) + proc = run( + "docker-compose pull --ignore-pull-failures", shell=True, cwd=test + ) if proc.returncode != 0: raise (Exception("docker-compose pull failed")) proc = run("docker-compose up -d", shell=True, cwd=test) diff --git a/tests/KubernetesTest.py b/tests/KubernetesTest.py index 644945710..e4d881dbf 100644 --- a/tests/KubernetesTest.py +++ b/tests/KubernetesTest.py @@ -1,6 +1,6 @@ from Test import Test from os.path import isdir, isfile -from os import getenv +from os import getenv, mkdir from shutil import copytree, rmtree, copy from traceback import format_exc from subprocess import run @@ -25,35 +25,21 @@ class KubernetesTest(Test): try: if not Test.init(): return False - proc = run("sudo chown -R root:root /tmp/bw-data", shell=True) - if proc.returncode != 0: - raise (Exception("chown failed (k8s stack)")) - if isdir("/tmp/kubernetes"): - rmtree("/tmp/kubernetes") - copytree("./integrations/kubernetes", "/tmp/kubernetes") - copy("./tests/utils/k8s.yml", "/tmp/kubernetes") + mkdir("/tmp/kubernetes") + copy("./tests/utils/bunkerweb.yml", "/tmp/kubernetes") deploy = "/tmp/kubernetes/bunkerweb.yml" Test.replace_in_file( - deploy, r"bunkerity/bunkerweb:.*$", "10.20.1.1:5000/bw-tests:latest" + deploy, + r"bunkerity/bunkerweb:.*$", + f"{getenv('PRIVATE_REGISTRY')}/infra/bunkerweb-tests-amd64:{getenv('IMAGE_TAG')}", ) Test.replace_in_file( deploy, r"bunkerity/bunkerweb-autoconf:.*$", - "10.20.1.1:5000/bw-autoconf-tests:latest", + f"{getenv('PRIVATE_REGISTRY')}/infra/bunkerweb-autoconf-tests-amd64:{getenv('IMAGE_TAG')}", ) - Test.replace_in_file(deploy, r"ifNotPresent", "Always") proc = run( - "sudo kubectl apply -f k8s.yml", cwd="/tmp/kubernetes", shell=True - ) - if proc.returncode != 0: - raise (Exception("kubectl apply k8s failed (k8s stack)")) - proc = run( - "sudo kubectl apply -f rbac.yml", cwd="/tmp/kubernetes", shell=True - ) - if proc.returncode != 0: - raise (Exception("kubectl apply rbac failed (k8s stack)")) - proc = run( - "sudo kubectl apply -f bunkerweb.yml", cwd="/tmp/kubernetes", shell=True + "kubectl apply -f bunkerweb.yml", cwd="/tmp/kubernetes", shell=True ) if proc.returncode != 0: raise (Exception("kubectl apply bunkerweb failed (k8s stack)")) @@ -61,7 +47,7 @@ class KubernetesTest(Test): i = 0 while i < 30: proc = run( - "sudo kubectl get pods | grep bunkerweb | grep -v Running", + "kubectl get pods | grep bunkerweb | grep -v Running", shell=True, capture_output=True, ) @@ -87,20 +73,10 @@ class KubernetesTest(Test): if not Test.end(): return False proc = run( - "sudo kubectl delete -f bunkerweb.yml", + "kubectl delete -f bunkerweb.yml", cwd="/tmp/kubernetes", shell=True, ) - if proc.returncode != 0: - ret = False - proc = run( - "sudo kubectl delete -f rbac.yml", cwd="/tmp/kubernetes", shell=True - ) - if proc.returncode != 0: - ret = False - proc = run( - "sudo kubectl delete -f k8s.yml", cwd="/tmp/kubernetes", shell=True - ) if proc.returncode != 0: ret = False rmtree("/tmp/kubernetes") @@ -121,10 +97,10 @@ class KubernetesTest(Test): Test.replace_in_files(test, "example.com", getenv("ROOT_DOMAIN")) setup = f"{test}/setup-kubernetes.sh" if isfile(setup): - proc = run("sudo ./setup-kubernetes.sh", cwd=test, shell=True) + proc = run("kubectl./setup-kubernetes.sh", cwd=test, shell=True) if proc.returncode != 0: raise (Exception("setup-kubernetes failed")) - proc = run("sudo kubectl apply -f kubernetes.yml", shell=True, cwd=test) + proc = run("kubectl apply -f kubernetes.yml", shell=True, cwd=test) if proc.returncode != 0: raise (Exception("kubectl apply failed")) except: @@ -140,10 +116,10 @@ class KubernetesTest(Test): test = f"/tmp/tests/{self._name}" cleanup = f"{test}/cleanup-kubernetes.sh" if isfile(cleanup): - proc = run("sudo ./cleanup-kubernetes.sh", cwd=test, shell=True) + proc = run("kubectl./cleanup-kubernetes.sh", cwd=test, shell=True) if proc.returncode != 0: raise (Exception("cleanup-kubernetes failed")) - proc = run("sudo kubectl delete -f kubernetes.yml", shell=True, cwd=test) + proc = run("kubectl delete -f kubernetes.yml", shell=True, cwd=test) if proc.returncode != 0: raise (Exception("kubectl delete failed")) super()._cleanup_test() @@ -156,9 +132,9 @@ class KubernetesTest(Test): def _debug_fail(self): proc = run( - 'sudo kubectl get pods --no-headers -o custom-columns=":metadata.name"', + 'kubectl get pods --no-headers -o custom-columns=":metadata.name"', shell=True, capture_output=True, ) for pod in proc.stdout.decode().splitlines(): - run(f"sudo kubectl logs {pod}", shell=True) + run(f"kubectl logs {pod}", shell=True) diff --git a/tests/LinuxTest.py b/tests/LinuxTest.py index 6bdb16285..73528cd85 100644 --- a/tests/LinuxTest.py +++ b/tests/LinuxTest.py @@ -1,7 +1,6 @@ from Test import Test -from os.path import isdir, isfile -from os import getenv, mkdir, chmod -from shutil import rmtree +from os.path import isfile +from os import getenv from traceback import format_exc from subprocess import run from time import sleep @@ -28,15 +27,7 @@ class LinuxTest(Test): try: if not Test.init(): return False - # TODO : find the nginx uid/gid on Docker images - proc = run("sudo chown -R root:root /tmp/bw-data", shell=True) - if proc.returncode != 0: - raise Exception("chown failed (autoconf stack)") - if isdir("/tmp/linux"): - rmtree("/tmp/linux") - mkdir("/tmp/linux") - chmod("/tmp/linux", 0o0777) - cmd = f"docker run -p 80:80 -p 443:443 --rm --name linux-{distro} -d --tmpfs /tmp --tmpfs /run --tmpfs /run/lock -v /sys/fs/cgroup:/sys/fs/cgroup:ro bw-{distro}" + cmd = f"docker run -p 80:80 -p 443:443 --rm --name linux-{distro} -d --tmpfs /tmp --tmpfs /run --tmpfs /run/lock -v /sys/fs/cgroup:/sys/fs/cgroup:rw --cgroupns=host --tty local/bw-{distro}:latest" proc = run(cmd, shell=True) if proc.returncode != 0: raise Exception("docker run failed (linux stack)") @@ -50,20 +41,6 @@ class LinuxTest(Test): proc = LinuxTest.docker_exec(distro, "systemctl start bunkerweb") if proc.returncode != 0: raise Exception("docker exec systemctl start failed (linux stack)") - cp_dirs = { - "/tmp/bw-data/letsencrypt": "/etc/letsencrypt", - "/tmp/bw-data/cache": "/var/cache/bunkerweb", - } - for src, dst in cp_dirs.items(): - proc = LinuxTest.docker_cp(distro, src, dst) - if proc.returncode != 0: - raise Exception(f"docker cp failed for {src} (linux stack)") - proc = LinuxTest.docker_exec(distro, f"chown -R nginx:nginx {dst}/*") - if proc.returncode != 0: - raise Exception( - f"docker exec failed for directory {src} (linux stack)" - ) - if distro in ("ubuntu", "debian"): LinuxTest.docker_exec( distro, @@ -128,22 +105,28 @@ class LinuxTest(Test): Test.replace_in_files(test, ex_domain, test_domain) Test.rename(test, ex_domain, test_domain) Test.replace_in_files(test, "example.com", getenv("ROOT_DOMAIN")) - proc = LinuxTest.docker_cp(self.__distro, test, f"/opt/{self._name}") + proc = self.docker_cp(self.__distro, test, f"/opt/{self._name}") if proc.returncode != 0: raise Exception("docker cp failed (test)") setup = test + "/setup-linux.sh" if isfile(setup): - proc = LinuxTest.docker_exec( + proc = self.docker_exec( self.__distro, f"cd /opt/{self._name} && ./setup-linux.sh" ) if proc.returncode != 0: raise Exception("docker exec setup failed (test)") - proc = LinuxTest.docker_exec( + proc = self.docker_exec( self.__distro, f"cp /opt/{self._name}/variables.env /etc/bunkerweb/" ) if proc.returncode != 0: raise Exception("docker exec cp variables.env failed (test)") - proc = LinuxTest.docker_exec( + proc = self.docker_exec( + self.__distro, + "echo '' >> /opt/bunkerweb/variables.env ; echo 'USE_LETS_ENCRYPT_STAGING=yes' >> /opt/bunkerweb/variables.env", + ) + if proc.returncode != 0: + raise (Exception("docker exec append variables.env failed (test)")) + proc = self.docker_exec( self.__distro, "systemctl stop bunkerweb ; systemctl start bunkerweb" ) if proc.returncode != 0: @@ -159,7 +142,7 @@ class LinuxTest(Test): def _cleanup_test(self): try: - proc = LinuxTest.docker_exec( + proc = self.docker_exec( self.__distro, f"cd /opt/{self._name} ; ./cleanup-linux.sh ; rm -rf /etc/bunkerweb/configs/* ; rm -rf /etc/bunkerweb/plugins/*", ) @@ -174,16 +157,18 @@ class LinuxTest(Test): return True def _debug_fail(self): - LinuxTest.docker_exec( + self.docker_exec( self.__distro, "cat /var/log/nginx/access.log ; cat /var/log/nginx/error.log ; journalctl -u bunkerweb --no-pager", ) + @staticmethod def docker_exec(distro, cmd_linux): return run( f'docker exec linux-{distro} /bin/bash -c "{cmd_linux}"', shell=True, ) + @staticmethod def docker_cp(distro, src, dst): return run(f"sudo docker cp {src} linux-{distro}:{dst}", shell=True) diff --git a/tests/SwarmTest.py b/tests/SwarmTest.py index f86a4ed9e..deae302d9 100644 --- a/tests/SwarmTest.py +++ b/tests/SwarmTest.py @@ -33,12 +33,14 @@ class SwarmTest(Test): copytree("./integrations/swarm", "/tmp/swarm") compose = "/tmp/swarm/stack.yml" Test.replace_in_file( - compose, r"bunkerity/bunkerweb:.*$", "10.20.1.1:5000/bw-tests:latest" + compose, + r"bunkerity/bunkerweb:.*$", + "192.168.42.100:5000/bw-tests:latest", ) Test.replace_in_file( compose, r"bunkerity/bunkerweb-autoconf:.*$", - "10.20.1.1:5000/bw-autoconf-tests:latest", + "192.168.42.100:5000/bw-autoconf-tests:latest", ) Test.replace_in_file(compose, r"bw\-data:/", "/tmp/bw-data:/") proc = run( @@ -50,19 +52,28 @@ class SwarmTest(Test): raise (Exception("docker stack deploy failed (swarm stack)")) i = 0 healthy = False - while i < 45: + while i < 90: proc = run( 'docker stack ps --no-trunc --format "{{ .CurrentState }}" bunkerweb | grep -v "Running"', cwd="/tmp/swarm", shell=True, capture_output=True, ) - if "" == proc.stdout.decode(): + if not proc.stdout.decode(): healthy = True break sleep(1) i += 1 if not healthy: + proc = run( + "docker service logs bunkerweb_mybunker ; docker service logs bunkerweb_myautoconf", + cwd="/tmp/swarm", + shell=True, + capture_output=True, + ) + logger = setup_logger("Swarm_test", getenv("LOGLEVEL", "INFO")) + logger.error(f"stdout logs = {proc.stdout.decode()}") + logger.error(f"stderr logs = {proc.stderr.decode()}") raise (Exception("swarm stack is not healthy")) sleep(60) except: diff --git a/tests/Test.py b/tests/Test.py index 664e270cc..a49b4b096 100644 --- a/tests/Test.py +++ b/tests/Test.py @@ -19,6 +19,7 @@ class Test(ABC): self.__tests = tests self._no_copy_container = no_copy_container self.__delay = delay + self._domains = {} self.__logger = setup_logger("Test", getenv("LOG_LEVEL", "INFO")) self.__logger.info( f"instantiated with {len(tests)} tests and timeout of {timeout}s for {self._name}", @@ -143,11 +144,13 @@ class Test(ABC): f"Can't replace file {path}" ) + @staticmethod def replace_in_files(path, old, new): - for root, dirs, files in walk(path): + for root, _, files in walk(path): for name in files: Test.replace_in_file(join(root, name), old, new) + @staticmethod def rename(path, old, new): for root, dirs, files in walk(path): for name in dirs + files: diff --git a/tests/ansible/autoconf_playbook b/tests/ansible/autoconf_playbook new file mode 100644 index 000000000..39dd3ecc5 --- /dev/null +++ b/tests/ansible/autoconf_playbook @@ -0,0 +1,41 @@ +--- +- hosts: all + gather_facts: false + tasks: + - name: Wait 300 seconds for port 22 to become open and contain "OpenSSH" + wait_for: + port: 22 + host: '{{ (ansible_ssh_host|default(ansible_host))|default(inventory_hostname) }}' + search_regex: OpenSSH + delay: 10 + connection: local + +- hosts: all + name: Provisioning tasks + roles: + - common + - docker + +- hosts: all + name: Install GH runner + vars: + - runner_user: user + - access_token: "{{ lookup('env', 'GITHUB_TOKEN') }}" + - runner_extra_config_args: "--ephemeral" + - runner_name: "bw-autoconf-{{ ansible_date_time.iso8601_micro | to_uuid }}" + - github_account: "{{ lookup('env', 'GITHUB_ACCOUNT') }}" + - github_owner: bunkerity + - github_repo: bunkerweb + - runner_labels: + - bw-autoconf + roles: + - monolithprojects.github_actions_runner + +- hosts: all + name: Restart GH runner (dirty hack because sometimes the runner is in inactive state) + tasks: + - name: Wait 60 seconds just in case + ansible.builtin.pause: + seconds: 60 + - name: Restart GH runner + shell: systemctl restart actions.runner.* diff --git a/tests/ansible/docker_playbook b/tests/ansible/docker_playbook new file mode 100644 index 000000000..b297bd15c --- /dev/null +++ b/tests/ansible/docker_playbook @@ -0,0 +1,41 @@ +--- +- hosts: all + gather_facts: false + tasks: + - name: Wait 300 seconds for port 22 to become open and contain "OpenSSH" + wait_for: + port: 22 + host: '{{ (ansible_ssh_host|default(ansible_host))|default(inventory_hostname) }}' + search_regex: OpenSSH + delay: 10 + connection: local + +- hosts: all + name: Provisioning tasks + roles: + - common + - docker + +- hosts: all + name: Install GH runner + vars: + - runner_user: user + - access_token: "{{ lookup('env', 'GITHUB_TOKEN') }}" + - runner_extra_config_args: "--ephemeral" + - runner_name: "bw-docker-{{ ansible_date_time.iso8601_micro | to_uuid }}" + - github_account: "{{ lookup('env', 'GITHUB_ACCOUNT') }}" + - github_owner: bunkerity + - github_repo: bunkerweb + - runner_labels: + - bw-docker + roles: + - monolithprojects.github_actions_runner + +- hosts: all + name: Restart GH runner (dirty hack because sometimes the runner is in inactive state) + tasks: + - name: Wait 60 seconds just in case + ansible.builtin.pause: + seconds: 60 + - name: Restart GH runner + shell: systemctl restart actions.runner.* diff --git a/tests/ansible/linux_playbook b/tests/ansible/linux_playbook new file mode 100644 index 000000000..e5daa8394 --- /dev/null +++ b/tests/ansible/linux_playbook @@ -0,0 +1,41 @@ +--- +- hosts: all + gather_facts: false + tasks: + - name: Wait 300 seconds for port 22 to become open and contain "OpenSSH" + wait_for: + port: 22 + host: '{{ (ansible_ssh_host|default(ansible_host))|default(inventory_hostname) }}' + search_regex: OpenSSH + delay: 10 + connection: local + +- hosts: all + name: Provisioning tasks + roles: + - common + - docker + +- hosts: all + name: Install GH runner + vars: + - runner_user: user + - access_token: "{{ lookup('env', 'GITHUB_TOKEN') }}" + - runner_extra_config_args: "--ephemeral" + - runner_name: "bw-linux-{{ ansible_date_time.iso8601_micro | to_uuid }}" + - github_account: "{{ lookup('env', 'GITHUB_ACCOUNT') }}" + - github_owner: bunkerity + - github_repo: bunkerweb + - runner_labels: + - bw-linux + roles: + - monolithprojects.github_actions_runner + +- hosts: all + name: Restart GH runner (dirty hack because sometimes the runner is in inactive state) + tasks: + - name: Wait 60 seconds just in case + ansible.builtin.pause: + seconds: 60 + - name: Restart GH runner + shell: systemctl restart actions.runner.* diff --git a/tests/ansible/ovh_roles/common/files/20auto-upgrades b/tests/ansible/ovh_roles/common/files/20auto-upgrades new file mode 100644 index 000000000..8d6d7c82f --- /dev/null +++ b/tests/ansible/ovh_roles/common/files/20auto-upgrades @@ -0,0 +1,2 @@ +APT::Periodic::Update-Package-Lists "1"; +APT::Periodic::Unattended-Upgrade "1"; diff --git a/tests/ansible/ovh_roles/common/files/50unattended-upgrades b/tests/ansible/ovh_roles/common/files/50unattended-upgrades new file mode 100644 index 000000000..00aeb0ec7 --- /dev/null +++ b/tests/ansible/ovh_roles/common/files/50unattended-upgrades @@ -0,0 +1,3 @@ +Unattended-Upgrade::Origins-Pattern { + "origin=Debian,codename=${distro_codename},label=Debian-Security"; +}; diff --git a/tests/ansible/ovh_roles/common/files/99-disable-network-config.cfg b/tests/ansible/ovh_roles/common/files/99-disable-network-config.cfg new file mode 100644 index 000000000..f144451d1 --- /dev/null +++ b/tests/ansible/ovh_roles/common/files/99-disable-network-config.cfg @@ -0,0 +1 @@ +network: {config: disabled} diff --git a/tests/ansible/ovh_roles/common/files/defaults-debian.conf b/tests/ansible/ovh_roles/common/files/defaults-debian.conf new file mode 100644 index 000000000..db1d23422 --- /dev/null +++ b/tests/ansible/ovh_roles/common/files/defaults-debian.conf @@ -0,0 +1,6 @@ +[sshd] +enabled = true +port = 22 +findtime = 10m +bantime = 24h +maxretry = 3 diff --git a/tests/ansible/ovh_roles/common/files/ipv6.conf b/tests/ansible/ovh_roles/common/files/ipv6.conf new file mode 100644 index 000000000..1bcb45e59 --- /dev/null +++ b/tests/ansible/ovh_roles/common/files/ipv6.conf @@ -0,0 +1,5 @@ +net.ipv6.conf.all.disable_ipv6 = 1 +net.ipv6.conf.default.disable_ipv6 = 1 +net.ipv6.conf.lo.disable_ipv6 = 1 +net.ipv6.conf.ens3.disable_ipv6 = 1 +net.ipv6.conf.ens4.disable_ipv6 = 1 diff --git a/tests/ansible/ovh_roles/common/files/sources.list b/tests/ansible/ovh_roles/common/files/sources.list new file mode 100644 index 000000000..d846c1180 --- /dev/null +++ b/tests/ansible/ovh_roles/common/files/sources.list @@ -0,0 +1,3 @@ +deb http://deb.debian.org/debian bullseye main +deb http://deb.debian.org/debian-security/ bullseye-security main +deb http://deb.debian.org/debian bullseye-updates main diff --git a/tests/ansible/ovh_roles/common/handlers/main.yml b/tests/ansible/ovh_roles/common/handlers/main.yml new file mode 100644 index 000000000..21d39f587 --- /dev/null +++ b/tests/ansible/ovh_roles/common/handlers/main.yml @@ -0,0 +1,8 @@ +--- +- name: Restart networking + service: + name: networking + state: restarted + +- name: Reload sysctl + shell: sysctl -p -f /etc/sysctl.d/70-disable-ipv6.conf diff --git a/tests/ansible/ovh_roles/common/tasks/apt.yml b/tests/ansible/ovh_roles/common/tasks/apt.yml new file mode 100644 index 000000000..7ab809107 --- /dev/null +++ b/tests/ansible/ovh_roles/common/tasks/apt.yml @@ -0,0 +1,27 @@ +--- +- name: Update /etc/apt/sources.list + copy: + src: sources.list + dest: /etc/apt/sources.list + owner: root + group: root + mode: '0644' + +- name: Update APT cache and install dependencies + shell: apt update && apt autoclean && apt install -y unattended-upgrades python3-apt rename python3-pip + +- name: copy 50unattended-upgrades + copy: + src: 50unattended-upgrades + dest: /etc/apt/apt.conf.d/50unattended-upgrades + owner: root + group: root + mode: '0644' + +- name: copy 20auto-upgrades + copy: + src: 20auto-upgrades + dest: /etc/apt/apt.conf.d/20auto-upgrades + owner: root + group: root + mode: '0644' diff --git a/tests/ansible/ovh_roles/common/tasks/fail2ban.yml b/tests/ansible/ovh_roles/common/tasks/fail2ban.yml new file mode 100644 index 000000000..09233adc0 --- /dev/null +++ b/tests/ansible/ovh_roles/common/tasks/fail2ban.yml @@ -0,0 +1,13 @@ +--- +- name: Install fail2ban + apt: + name: fail2ban + state: present + +- name: Update /etc/fail2ban/jail.d/defaults-debian.conf + copy: + src: defaults-debian.conf + dest: /etc/fail2ban/jail.d/defaults-debian.conf + owner: root + group: root + mode: '0644' diff --git a/tests/ansible/ovh_roles/common/tasks/hostname.yml b/tests/ansible/ovh_roles/common/tasks/hostname.yml new file mode 100644 index 000000000..3c5dae836 --- /dev/null +++ b/tests/ansible/ovh_roles/common/tasks/hostname.yml @@ -0,0 +1,4 @@ +--- +- name: Set the hostname + hostname: + name: "{{ inventory_hostname }}" diff --git a/tests/ansible/ovh_roles/common/tasks/main.yml b/tests/ansible/ovh_roles/common/tasks/main.yml new file mode 100644 index 000000000..ad374824e --- /dev/null +++ b/tests/ansible/ovh_roles/common/tasks/main.yml @@ -0,0 +1,5 @@ +--- +- include_tasks: network.yml +- include_tasks: apt.yml +- include_tasks: hostname.yml +- include_tasks: fail2ban.yml diff --git a/tests/ansible/ovh_roles/common/tasks/network.yml b/tests/ansible/ovh_roles/common/tasks/network.yml new file mode 100644 index 000000000..2a0639b13 --- /dev/null +++ b/tests/ansible/ovh_roles/common/tasks/network.yml @@ -0,0 +1,29 @@ +--- +- name: Update /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg + copy: + src: 99-disable-network-config.cfg + dest: /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg + owner: root + group: root + mode: '0644' + +- name: Update /etc/network/interfaces.d/50-cloud-init + template: + src: 50-cloud-init + dest: /etc/network/interfaces.d/50-cloud-init + owner: root + group: root + mode: '0644' + notify: + - Restart networking + +- name: Update /etc/sysctl.d/70-disable-ipv6.conf + copy: + src: ipv6.conf + dest: /etc/sysctl.d/70-disable-ipv6.conf + owner: root + group: root + mode: '0644' + notify: + - Reload sysctl + diff --git a/tests/ansible/ovh_roles/common/templates/50-cloud-init b/tests/ansible/ovh_roles/common/templates/50-cloud-init new file mode 100644 index 000000000..1bbbd6ca0 --- /dev/null +++ b/tests/ansible/ovh_roles/common/templates/50-cloud-init @@ -0,0 +1,13 @@ +auto lo +iface lo inet loopback + dns-nameservers 213.186.33.99 0.0.0.0 + +auto ens3 +iface ens3 inet dhcp + accept_ra 0 + mtu 1500 + +auto ens3:0 +iface ens3:0 inet static + address {{ failover_ip }} + netmask 255.255.255.255 diff --git a/tests/ansible/ovh_roles/docker/files/docker.list b/tests/ansible/ovh_roles/docker/files/docker.list new file mode 100644 index 000000000..1ce77340a --- /dev/null +++ b/tests/ansible/ovh_roles/docker/files/docker.list @@ -0,0 +1 @@ +deb [arch=amd64] https://download.docker.com/linux/debian bullseye stable diff --git a/tests/ansible/ovh_roles/docker/tasks/main.yml b/tests/ansible/ovh_roles/docker/tasks/main.yml new file mode 100644 index 000000000..f73728792 --- /dev/null +++ b/tests/ansible/ovh_roles/docker/tasks/main.yml @@ -0,0 +1,38 @@ +--- +- name: Install docker dependencies + apt: + name: + - ca-certificates + - gnupg + update_cache: yes + state: present + +- name: Update /etc/apt/sources.list.d/docker.list + copy: + src: docker.list + dest: /etc/apt/sources.list.d/docker.list + owner: root + group: root + mode: '0644' + +- name: Trust docker key + apt_key: + url: https://download.docker.com/linux/debian/gpg + state: present + +- name: Install docker + apt: + name: + - docker-ce + - docker-ce-cli + update_cache: yes + state: present + +- name: Install /usr/local/bin/docker-compose + shell: curl -L https://github.com/docker/compose/releases/download/v2.12.2/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose + +- name: Add debian user to docker group + user: + name: debian + groups: docker + append: yes diff --git a/tests/ansible/ovh_roles/linux/tasks/main.yml b/tests/ansible/ovh_roles/linux/tasks/main.yml new file mode 100644 index 000000000..2dbe7d40e --- /dev/null +++ b/tests/ansible/ovh_roles/linux/tasks/main.yml @@ -0,0 +1,11 @@ +--- +- name: Install ruby + apt: + name: + - ruby-full + state: present + +- name: Install package_cloud package + community.general.gem: + name: package_cloud + state: present diff --git a/tests/ansible/ovh_roles/private_net/handlers/main.yml b/tests/ansible/ovh_roles/private_net/handlers/main.yml new file mode 100644 index 000000000..48c49cee5 --- /dev/null +++ b/tests/ansible/ovh_roles/private_net/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: Restart networking + service: + name: networking + state: restarted diff --git a/tests/ansible/ovh_roles/private_net/tasks/main.yml b/tests/ansible/ovh_roles/private_net/tasks/main.yml new file mode 100644 index 000000000..273039e9c --- /dev/null +++ b/tests/ansible/ovh_roles/private_net/tasks/main.yml @@ -0,0 +1,2 @@ +--- +- include_tasks: network.yml diff --git a/tests/ansible/ovh_roles/private_net/tasks/network.yml b/tests/ansible/ovh_roles/private_net/tasks/network.yml new file mode 100644 index 000000000..82c863fbd --- /dev/null +++ b/tests/ansible/ovh_roles/private_net/tasks/network.yml @@ -0,0 +1,10 @@ +--- +- name: Update /etc/network/interfaces.d/ens4 + template: + src: ens4 + dest: /etc/network/interfaces.d/ens4 + owner: root + group: root + mode: '0644' + notify: + - Restart networking diff --git a/tests/ansible/ovh_roles/private_net/templates/ens4 b/tests/ansible/ovh_roles/private_net/templates/ens4 new file mode 100644 index 000000000..3be5e9add --- /dev/null +++ b/tests/ansible/ovh_roles/private_net/templates/ens4 @@ -0,0 +1,5 @@ +auto ens4 +allow-hotplug ens4 +iface ens4 inet static + address {{ local_ip }}/24 + mtu 9000 diff --git a/tests/ansible/ovh_roles/swarm/tasks/main.yml b/tests/ansible/ovh_roles/swarm/tasks/main.yml new file mode 100644 index 000000000..02f45d6ed --- /dev/null +++ b/tests/ansible/ovh_roles/swarm/tasks/main.yml @@ -0,0 +1,64 @@ +--- +- name: Install pip + apt: + name: + - python3 + - python3-pip + - virtualenv + - python3-setuptools + - python + - python-setuptools + +- name: Upgrade pip3 + pip: + name: pip + state: latest + executable: pip3 + +- name: Install dockerpy for py3 + pip: + name: docker[tls] + state: forcereinstall + executable: pip3 + +- name: Init Docker Swarm + community.general.docker_swarm: + advertise_addr: "{{ local_ip }}" + listen_addr: "{{ local_ip }}" + ssl_version: "1.3" + validate_certs: yes + state: present + register: result + when: inventory_hostname == groups['managers'][0] + +- name: Get join-token for manager nodes + set_fact: + join_token_manager: "{{ hostvars[groups['managers'][0]].result.swarm_facts.JoinTokens.Manager }}" + +- name: Get join-token for worker nodes + set_fact: + join_token_worker: "{{ hostvars[groups['managers'][0]].result.swarm_facts.JoinTokens.Worker }}" + +- name: Join Swarm as managers + community.general.docker_swarm: + advertise_addr: "{{ local_ip }}" + listen_addr: "{{ local_ip }}" + ssl_version: "1.3" + validate_certs: yes + state: join + join_token: "{{ join_token_manager }}" + remote_addrs: ["{{ hostvars[groups['managers'][0]].local_ip }}:2377"] + when: + - inventory_hostname in groups['managers'] + - inventory_hostname != groups['managers'][0] + +- name: Join Swarm as workers + community.general.docker_swarm: + advertise_addr: "{{ local_ip }}" + listen_addr: "{{ local_ip }}" + ssl_version: 1.3 + validate_certs: yes + state: join + join_token: "{{ join_token_worker }}" + remote_addrs: ["{{ hostvars[groups['managers'][0]].local_ip }}:2377"] + when: inventory_hostname in groups['workers'] diff --git a/tests/ansible/roles/common/files/99-disable-network-config.cfg b/tests/ansible/roles/common/files/99-disable-network-config.cfg new file mode 100644 index 000000000..f144451d1 --- /dev/null +++ b/tests/ansible/roles/common/files/99-disable-network-config.cfg @@ -0,0 +1 @@ +network: {config: disabled} diff --git a/tests/ansible/roles/common/files/ipv6.conf b/tests/ansible/roles/common/files/ipv6.conf new file mode 100644 index 000000000..1bcb45e59 --- /dev/null +++ b/tests/ansible/roles/common/files/ipv6.conf @@ -0,0 +1,5 @@ +net.ipv6.conf.all.disable_ipv6 = 1 +net.ipv6.conf.default.disable_ipv6 = 1 +net.ipv6.conf.lo.disable_ipv6 = 1 +net.ipv6.conf.ens3.disable_ipv6 = 1 +net.ipv6.conf.ens4.disable_ipv6 = 1 diff --git a/tests/ansible/roles/common/files/sources.list b/tests/ansible/roles/common/files/sources.list new file mode 100644 index 000000000..d846c1180 --- /dev/null +++ b/tests/ansible/roles/common/files/sources.list @@ -0,0 +1,3 @@ +deb http://deb.debian.org/debian bullseye main +deb http://deb.debian.org/debian-security/ bullseye-security main +deb http://deb.debian.org/debian bullseye-updates main diff --git a/tests/ansible/roles/common/handlers/main.yml b/tests/ansible/roles/common/handlers/main.yml new file mode 100644 index 000000000..21d39f587 --- /dev/null +++ b/tests/ansible/roles/common/handlers/main.yml @@ -0,0 +1,8 @@ +--- +- name: Restart networking + service: + name: networking + state: restarted + +- name: Reload sysctl + shell: sysctl -p -f /etc/sysctl.d/70-disable-ipv6.conf diff --git a/tests/ansible/roles/common/tasks/apt.yml b/tests/ansible/roles/common/tasks/apt.yml new file mode 100644 index 000000000..8ea973bd9 --- /dev/null +++ b/tests/ansible/roles/common/tasks/apt.yml @@ -0,0 +1,11 @@ +--- +- name: Update /etc/apt/sources.list + copy: + src: sources.list + dest: /etc/apt/sources.list + owner: root + group: root + mode: '0644' + +- name: Update APT cache and install dependencies + shell: apt update && apt autoclean && apt install -y python3-apt rename python3-pip sudo diff --git a/tests/ansible/roles/common/tasks/hostname.yml b/tests/ansible/roles/common/tasks/hostname.yml new file mode 100644 index 000000000..3c5dae836 --- /dev/null +++ b/tests/ansible/roles/common/tasks/hostname.yml @@ -0,0 +1,4 @@ +--- +- name: Set the hostname + hostname: + name: "{{ inventory_hostname }}" diff --git a/tests/ansible/roles/common/tasks/main.yml b/tests/ansible/roles/common/tasks/main.yml new file mode 100644 index 000000000..d6866212d --- /dev/null +++ b/tests/ansible/roles/common/tasks/main.yml @@ -0,0 +1,5 @@ +--- +#- include_tasks: network.yml +- include_tasks: user.yml +- include_tasks: apt.yml +- include_tasks: hostname.yml diff --git a/tests/ansible/roles/common/tasks/network.yml b/tests/ansible/roles/common/tasks/network.yml new file mode 100644 index 000000000..2a0639b13 --- /dev/null +++ b/tests/ansible/roles/common/tasks/network.yml @@ -0,0 +1,29 @@ +--- +- name: Update /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg + copy: + src: 99-disable-network-config.cfg + dest: /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg + owner: root + group: root + mode: '0644' + +- name: Update /etc/network/interfaces.d/50-cloud-init + template: + src: 50-cloud-init + dest: /etc/network/interfaces.d/50-cloud-init + owner: root + group: root + mode: '0644' + notify: + - Restart networking + +- name: Update /etc/sysctl.d/70-disable-ipv6.conf + copy: + src: ipv6.conf + dest: /etc/sysctl.d/70-disable-ipv6.conf + owner: root + group: root + mode: '0644' + notify: + - Reload sysctl + diff --git a/tests/ansible/roles/common/tasks/user.yml b/tests/ansible/roles/common/tasks/user.yml new file mode 100644 index 000000000..dd2b29b12 --- /dev/null +++ b/tests/ansible/roles/common/tasks/user.yml @@ -0,0 +1,11 @@ +- name: Create user + user: + name: user + shell: /bin/bash +- name: Configuring sudoer access + community.general.sudoers: + name: allow-all-sudo + state: present + user: "user" + commands: ALL + nopassword: true \ No newline at end of file diff --git a/tests/ansible/roles/common/templates/50-cloud-init b/tests/ansible/roles/common/templates/50-cloud-init new file mode 100644 index 000000000..1bbbd6ca0 --- /dev/null +++ b/tests/ansible/roles/common/templates/50-cloud-init @@ -0,0 +1,13 @@ +auto lo +iface lo inet loopback + dns-nameservers 213.186.33.99 0.0.0.0 + +auto ens3 +iface ens3 inet dhcp + accept_ra 0 + mtu 1500 + +auto ens3:0 +iface ens3:0 inet static + address {{ failover_ip }} + netmask 255.255.255.255 diff --git a/tests/ansible/roles/docker/files/docker.list b/tests/ansible/roles/docker/files/docker.list new file mode 100644 index 000000000..1ce77340a --- /dev/null +++ b/tests/ansible/roles/docker/files/docker.list @@ -0,0 +1 @@ +deb [arch=amd64] https://download.docker.com/linux/debian bullseye stable diff --git a/tests/ansible/roles/docker/tasks/main.yml b/tests/ansible/roles/docker/tasks/main.yml new file mode 100644 index 000000000..da02e9520 --- /dev/null +++ b/tests/ansible/roles/docker/tasks/main.yml @@ -0,0 +1,38 @@ +--- +- name: Install docker dependencies + apt: + name: + - ca-certificates + - gnupg + update_cache: yes + state: present + +- name: Update /etc/apt/sources.list.d/docker.list + copy: + src: docker.list + dest: /etc/apt/sources.list.d/docker.list + owner: root + group: root + mode: '0644' + +- name: Trust docker key + apt_key: + url: https://download.docker.com/linux/debian/gpg + state: present + +- name: Install docker + apt: + name: + - docker-ce + - docker-ce-cli + update_cache: yes + state: present + +- name: Install /usr/local/bin/docker-compose + shell: curl -L https://github.com/docker/compose/releases/download/v2.12.2/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose && chmod +x /usr/local/bin/docker-compose + +- name: Add user to docker group + user: + name: user + groups: docker + append: yes diff --git a/tests/ansible/roles/linux/tasks/main.yml b/tests/ansible/roles/linux/tasks/main.yml new file mode 100644 index 000000000..2dbe7d40e --- /dev/null +++ b/tests/ansible/roles/linux/tasks/main.yml @@ -0,0 +1,11 @@ +--- +- name: Install ruby + apt: + name: + - ruby-full + state: present + +- name: Install package_cloud package + community.general.gem: + name: package_cloud + state: present diff --git a/tests/ansible/roles/private_net/handlers/main.yml b/tests/ansible/roles/private_net/handlers/main.yml new file mode 100644 index 000000000..48c49cee5 --- /dev/null +++ b/tests/ansible/roles/private_net/handlers/main.yml @@ -0,0 +1,5 @@ +--- +- name: Restart networking + service: + name: networking + state: restarted diff --git a/tests/ansible/roles/private_net/tasks/main.yml b/tests/ansible/roles/private_net/tasks/main.yml new file mode 100644 index 000000000..273039e9c --- /dev/null +++ b/tests/ansible/roles/private_net/tasks/main.yml @@ -0,0 +1,2 @@ +--- +- include_tasks: network.yml diff --git a/tests/ansible/roles/private_net/tasks/network.yml b/tests/ansible/roles/private_net/tasks/network.yml new file mode 100644 index 000000000..de00011b6 --- /dev/null +++ b/tests/ansible/roles/private_net/tasks/network.yml @@ -0,0 +1,10 @@ +--- +- name: Update /etc/network/interfaces.d/60-ens5-vpc + template: + src: 60-ens5-vpc + dest: /etc/network/interfaces.d/60-ens5-vpc + owner: root + group: root + mode: '0644' + notify: + - Restart networking diff --git a/tests/ansible/roles/private_net/templates/60-ens5-vpc b/tests/ansible/roles/private_net/templates/60-ens5-vpc new file mode 100644 index 000000000..5fa7855fb --- /dev/null +++ b/tests/ansible/roles/private_net/templates/60-ens5-vpc @@ -0,0 +1,4 @@ +auto ens5 +allow-hotplug ens5 +iface ens5 inet static + address {{ local_ip }}/24 diff --git a/tests/ansible/roles/registry/tasks/main.yml b/tests/ansible/roles/registry/tasks/main.yml new file mode 100644 index 000000000..4bcfa6a53 --- /dev/null +++ b/tests/ansible/roles/registry/tasks/main.yml @@ -0,0 +1,12 @@ +--- +- name: Create Docker Registry + docker_container: + name: registry + image: registry:2 + state: started + restart_policy: unless-stopped + volumes: + - /etc/docker/registry:/var/lib/registry + published_ports: + - "192.168.42.100:5000:5000" + when: inventory_hostname == groups['managers'][0] diff --git a/tests/ansible/roles/swarm/files/daemon.json b/tests/ansible/roles/swarm/files/daemon.json new file mode 100644 index 000000000..ddb37df3c --- /dev/null +++ b/tests/ansible/roles/swarm/files/daemon.json @@ -0,0 +1,3 @@ +{ + "insecure-registries" : ["192.168.42.100:5000"] +} diff --git a/tests/ansible/roles/swarm/tasks/main.yml b/tests/ansible/roles/swarm/tasks/main.yml new file mode 100644 index 000000000..c8559543e --- /dev/null +++ b/tests/ansible/roles/swarm/tasks/main.yml @@ -0,0 +1,78 @@ +--- +- name: Install pip + apt: + name: + - python3 + - python3-pip + - virtualenv + - python3-setuptools + - python + - python-setuptools + +- name: Upgrade pip3 + pip: + name: pip + state: latest + executable: pip3 + +- name: Install dockerpy for py3 + pip: + name: docker[tls] + state: forcereinstall + executable: pip3 + +- name: Init Docker Swarm + community.general.docker_swarm: + advertise_addr: "{{ local_ip }}" + listen_addr: "{{ local_ip }}" + ssl_version: "1.3" + validate_certs: yes + state: present + register: result + when: inventory_hostname == groups['managers'][0] + +- name: Get join-token for manager nodes + set_fact: + join_token_manager: "{{ hostvars[groups['managers'][0]].result.swarm_facts.JoinTokens.Manager }}" + +- name: Get join-token for worker nodes + set_fact: + join_token_worker: "{{ hostvars[groups['managers'][0]].result.swarm_facts.JoinTokens.Worker }}" + +- name: Join Swarm as managers + community.general.docker_swarm: + advertise_addr: "{{ local_ip }}" + listen_addr: "{{ local_ip }}" + ssl_version: "1.3" + validate_certs: yes + state: join + join_token: "{{ join_token_manager }}" + remote_addrs: ["{{ hostvars[groups['managers'][0]].local_ip }}:2377"] + when: + - inventory_hostname in groups['managers'] + - inventory_hostname != groups['managers'][0] + +- name: Join Swarm as workers + community.general.docker_swarm: + advertise_addr: "{{ local_ip }}" + listen_addr: "{{ local_ip }}" + ssl_version: 1.3 + validate_certs: yes + state: join + join_token: "{{ join_token_worker }}" + remote_addrs: ["{{ hostvars[groups['managers'][0]].local_ip }}:2377"] + when: inventory_hostname in groups['workers'] + +- name: Update daemon.json + copy: + src: daemon.json + dest: /etc/docker/daemon.json + owner: root + group: root + mode: '0644' + +- name: Reload docker + service: + name: docker + enabled: true + state: restarted diff --git a/tests/ansible/swarm_playbook b/tests/ansible/swarm_playbook new file mode 100644 index 000000000..03e86f713 --- /dev/null +++ b/tests/ansible/swarm_playbook @@ -0,0 +1,52 @@ +--- +- hosts: all + gather_facts: false + tasks: + - name: Wait 300 seconds for port 22 to become open and contain "OpenSSH" + wait_for: + port: 22 + host: '{{ (ansible_ssh_host|default(ansible_host))|default(inventory_hostname) }}' + search_regex: OpenSSH + delay: 10 + connection: local + +- hosts: all + name: Provisioning tasks + roles: + - common + - docker + - private_net + +- hosts: all + name: Setup swarm + roles: + - swarm + +- hosts: managers[0] + name: Setup local registry + roles: + - registry + +- hosts: managers[0] + name: Install GH runner + vars: + - runner_user: user + - access_token: "{{ lookup('env', 'GITHUB_TOKEN') }}" + - runner_extra_config_args: "--ephemeral" + - runner_name: "bw-swarm-{{ ansible_date_time.iso8601_micro | to_uuid }}" + - github_account: "{{ lookup('env', 'GITHUB_ACCOUNT') }}" + - github_owner: bunkerity + - github_repo: bunkerweb + - runner_labels: + - bw-swarm + roles: + - monolithprojects.github_actions_runner + +- hosts: managers[0] + name: Restart GH runner (dirty hack because sometimes the runner is in inactive state) + tasks: + - name: Wait 60 seconds just in case + ansible.builtin.pause: + seconds: 60 + - name: Restart GH runner + shell: systemctl restart actions.runner.* diff --git a/tests/linux/Dockerfile-centos b/tests/linux/Dockerfile-centos new file mode 100644 index 000000000..c17de1b99 --- /dev/null +++ b/tests/linux/Dockerfile-centos @@ -0,0 +1,25 @@ +FROM quay.io/centos/centos:stream8 + +RUN yum install -y initscripts # for old "service" + +ENV container=docker + +RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == systemd-tmpfiles-setup.service ] || rm -f $i; done); \ +rm -f /lib/systemd/system/multi-user.target.wants/*;\ +rm -f /etc/systemd/system/*.wants/*;\ +rm -f /lib/systemd/system/local-fs.target.wants/*; \ +rm -f /lib/systemd/system/sockets.target.wants/*udev*; \ +rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \ +rm -f /lib/systemd/system/basic.target.wants/*;\ +rm -f /lib/systemd/system/anaconda.target.wants/*; + +COPY linux/nginx.repo /etc/yum.repos.d/nginx.repo + +RUN dnf install php-fpm curl yum-utils epel-release -y && \ + dnf install nginx-1.20.2 -y + +COPY ./package-centos/*.rpm /opt + +VOLUME /run /tmp + +CMD /usr/sbin/init \ No newline at end of file diff --git a/tests/linux/Dockerfile-debian b/tests/linux/Dockerfile-debian new file mode 100644 index 000000000..e0bc6325a --- /dev/null +++ b/tests/linux/Dockerfile-debian @@ -0,0 +1,38 @@ +FROM debian:bullseye + +ENV container docker +ENV LC_ALL C +ENV DEBIAN_FRONTEND noninteractive +ENV NGINX_VERSION 1.20.2 + +RUN apt-get update \ + && apt-get install -y systemd systemd-sysv \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +RUN cd /lib/systemd/system/sysinit.target.wants/ \ + && rm $(ls | grep -v systemd-tmpfiles-setup) + +RUN rm -f /lib/systemd/system/multi-user.target.wants/* \ + /etc/systemd/system/*.wants/* \ + /lib/systemd/system/local-fs.target.wants/* \ + /lib/systemd/system/sockets.target.wants/*udev* \ + /lib/systemd/system/sockets.target.wants/*initctl* \ + /lib/systemd/system/basic.target.wants/* \ + /lib/systemd/system/anaconda.target.wants/* \ + /lib/systemd/system/plymouth* \ + /lib/systemd/system/systemd-update-utmp* + +RUN apt update && \ + apt-get install php-fpm curl gnupg2 ca-certificates python3-pip -y && \ + echo "deb https://nginx.org/packages/debian/ bullseye nginx" > /etc/apt/sources.list.d/nginx.list && \ + echo "deb-src https://nginx.org/packages/debian/ bullseye nginx" >> /etc/apt/sources.list.d/nginx.list && \ + apt-key adv --keyserver keyserver.ubuntu.com --recv-keys ABF5BD827BD9BF62 && \ + apt-get update && \ + apt-get install -y --no-install-recommends nginx=${NGINX_VERSION}-1~bullseye + +COPY ./package-debian/*.deb /opt + +VOLUME ["/sys/fs/cgroup"] + +CMD ["/lib/systemd/systemd"] diff --git a/tests/linux/Dockerfile-fedora b/tests/linux/Dockerfile-fedora new file mode 100644 index 000000000..05015e423 --- /dev/null +++ b/tests/linux/Dockerfile-fedora @@ -0,0 +1,29 @@ +FROM fedora:36 + +ENV container docker + +RUN dnf -y update \ + && dnf -y install systemd \ + && dnf clean all + +RUN cd /lib/systemd/system/sysinit.target.wants/; \ + for i in *; do [ $i = systemd-tmpfiles-setup.service ] || rm -f $i; done + +RUN rm -f /lib/systemd/system/multi-user.target.wants/* \ + /etc/systemd/system/*.wants/* \ + /lib/systemd/system/local-fs.target.wants/* \ + /lib/systemd/system/sockets.target.wants/*udev* \ + /lib/systemd/system/sockets.target.wants/*initctl* \ + /lib/systemd/system/basic.target.wants/* \ + /lib/systemd/system/anaconda.target.wants/* + +# Nginx +RUN dnf update -y && \ + dnf install -y php-fpm curl gnupg2 ca-certificates redhat-lsb-core python3-pip which && \ + dnf install nginx-1.20.2 -y + +COPY ./package-fedora/*.rpm /opt + +VOLUME ["/sys/fs/cgroup"] + +CMD ["/usr/sbin/init"] diff --git a/tests/linux/Dockerfile-ubuntu b/tests/linux/Dockerfile-ubuntu new file mode 100644 index 000000000..28afc7f03 --- /dev/null +++ b/tests/linux/Dockerfile-ubuntu @@ -0,0 +1,38 @@ +FROM ubuntu:22.04 + +ENV container docker +ENV LC_ALL C +ENV DEBIAN_FRONTEND noninteractive +ENV NGINX_VERSION 1.20.2 + +RUN apt-get update \ + && apt-get install -y systemd systemd-sysv \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +RUN cd /lib/systemd/system/sysinit.target.wants/ \ + && rm $(ls | grep -v systemd-tmpfiles-setup) + +RUN rm -f /lib/systemd/system/multi-user.target.wants/* \ + /etc/systemd/system/*.wants/* \ + /lib/systemd/system/local-fs.target.wants/* \ + /lib/systemd/system/sockets.target.wants/*udev* \ + /lib/systemd/system/sockets.target.wants/*initctl* \ + /lib/systemd/system/basic.target.wants/* \ + /lib/systemd/system/anaconda.target.wants/* \ + /lib/systemd/system/plymouth* \ + /lib/systemd/system/systemd-update-utmp* + +RUN apt update && \ + apt-get install php-fpm curl gnupg2 ca-certificates lsb-release ubuntu-keyring software-properties-common python3-pip -y && \ + echo "deb https://nginx.org/packages/ubuntu/ jammy nginx" > /etc/apt/sources.list.d/nginx.list && \ + echo "deb-src https://nginx.org/packages/ubuntu/ jammy nginx" >> /etc/apt/sources.list.d/nginx.list && \ + apt-key adv --keyserver keyserver.ubuntu.com --recv-keys ABF5BD827BD9BF62 && \ + apt-get update && \ + apt-get install -y --no-install-recommends nginx=${NGINX_VERSION}-1~jammy + +COPY ./package-ubuntu/*.deb /opt + +VOLUME ["/sys/fs/cgroup"] + +CMD ["/lib/systemd/systemd"] \ No newline at end of file diff --git a/tests/main.py b/tests/main.py index 1eae2260f..dd9d00136 100755 --- a/tests/main.py +++ b/tests/main.py @@ -11,7 +11,6 @@ from subprocess import run path.extend((f"{Path.cwd()}/utils", f"{Path.cwd()}/tests")) -from Test import Test from DockerTest import DockerTest from AutoconfTest import AutoconfTest from SwarmTest import SwarmTest diff --git a/tests/terraform/autoconf.tf b/tests/terraform/autoconf.tf new file mode 100644 index 000000000..76f3c2e25 --- /dev/null +++ b/tests/terraform/autoconf.tf @@ -0,0 +1,32 @@ +# Variables +variable "autoconf_ip" { + type = string + nullable = false +} +variable "autoconf_ip_id" { + type = string + nullable = false +} + +# Create cicd_bw_autoconf SSH key +resource "scaleway_account_ssh_key" "ssh_key" { + name = "cicd_bw_autoconf" + public_key = file("~/.ssh/id_rsa.pub") +} + +# Create cicd_bw_autoconf instance +resource "scaleway_instance_server" "instance" { + depends_on = [scaleway_account_ssh_key.ssh_key] + name = "cicd_bw_autoconf" + type = "DEV1-M" + image = "debian_bullseye" + ip_id = var.autoconf_ip_id +} + +# Create Ansible inventory file +resource "local_file" "ansible_inventory" { + content = templatefile("templates/autoconf_inventory.tftpl", { + public_ip = var.autoconf_ip + }) + filename = "/tmp/autoconf_inventory" +} diff --git a/tests/terraform/docker.tf b/tests/terraform/docker.tf new file mode 100644 index 000000000..44e05626b --- /dev/null +++ b/tests/terraform/docker.tf @@ -0,0 +1,32 @@ +# Variables +variable "docker_ip" { + type = string + nullable = false +} +variable "docker_ip_id" { + type = string + nullable = false +} + +# Create cicd_bw_docker SSH key +resource "scaleway_account_ssh_key" "ssh_key" { + name = "cicd_bw_docker" + public_key = file("~/.ssh/id_rsa.pub") +} + +# Create cicd_bw_docker instance +resource "scaleway_instance_server" "instance" { + depends_on = [scaleway_account_ssh_key.ssh_key] + name = "cicd_bw_docker" + type = "DEV1-M" + image = "debian_bullseye" + ip_id = var.docker_ip_id +} + +# Create Ansible inventory file +resource "local_file" "ansible_inventory" { + content = templatefile("templates/docker_inventory.tftpl", { + public_ip = var.docker_ip + }) + filename = "/tmp/docker_inventory" +} diff --git a/tests/terraform/k8s.tf b/tests/terraform/k8s.tf new file mode 100644 index 000000000..66672e64e --- /dev/null +++ b/tests/terraform/k8s.tf @@ -0,0 +1,62 @@ +# Variables +variable "k8s_ip" { + type = string + nullable = false +} +variable "k8s_dockerconfigjson" { + type = string + nullable = false +} + +# Create k8s cluster +resource "scaleway_k8s_cluster" "cluster" { + type = "kapsule" + name = "bw_k8s" + version = "1.24.7" + cni = "cilium" +} + +# Create k8s pool +resource "scaleway_k8s_pool" "pool" { + cluster_id = scaleway_k8s_cluster.cluster.id + name = "bw_k8s" + node_type = "DEV1-M" + size = 3 + wait_for_pool_ready = true +} + +# Get kubeconfig file +resource "local_file" "kubeconfig" { + depends_on = [scaleway_k8s_pool.pool] + content = scaleway_k8s_cluster.cluster.kubeconfig[0].config_file + filename = "/tmp/k8s/kubeconfig" +} +provider "kubectl" { + config_path = "${local_file.kubeconfig.filename}" +} + +# Setup LB +resource "local_file" "lb_yml" { + depends_on = [local_file.kubeconfig] + content = templatefile("templates/lb.yml.tftpl", { + lb_ip = var.k8s_ip + }) + filename = "/tmp/k8s/lb.yml" +} +resource "kubectl_manifest" "lb" { + depends_on = [local_file.lb_yml] + yaml_body = local_file.lb_yml.content +} + +# Setup registry +resource "local_file" "reg_yml" { + depends_on = [local_file.kubeconfig] + content = templatefile("templates/reg.yml.tftpl", { + dockerconfigjson = var.k8s_dockerconfigjson + }) + filename = "/tmp/k8s/reg.yml" +} +resource "kubectl_manifest" "reg" { + depends_on = [local_file.reg_yml] + yaml_body = local_file.reg_yml.content +} \ No newline at end of file diff --git a/tests/terraform/linux.tf b/tests/terraform/linux.tf new file mode 100644 index 000000000..2834164e8 --- /dev/null +++ b/tests/terraform/linux.tf @@ -0,0 +1,32 @@ +# Variables +variable "linux_ip" { + type = string + nullable = false +} +variable "linux_ip_id" { + type = string + nullable = false +} + +# Create cicd_bw_linux SSH key +resource "scaleway_account_ssh_key" "ssh_key" { + name = "cicd_bw_linux" + public_key = file("~/.ssh/id_rsa.pub") +} + +# Create cicd_bw_linux instance +resource "scaleway_instance_server" "instance" { + depends_on = [scaleway_account_ssh_key.ssh_key] + name = "cicd_bw_linux" + type = "DEV1-M" + image = "debian_bullseye" + ip_id = var.linux_ip_id +} + +# Create Ansible inventory file +resource "local_file" "ansible_inventory" { + content = templatefile("templates/linux_inventory.tftpl", { + public_ip = var.linux_ip + }) + filename = "/tmp/linux_inventory" +} diff --git a/tests/terraform/ovh/autoconf.tf b/tests/terraform/ovh/autoconf.tf new file mode 100644 index 000000000..0cc6e19b8 --- /dev/null +++ b/tests/terraform/ovh/autoconf.tf @@ -0,0 +1,41 @@ +# Variables +variable "autoconf_ip" { + type = string + nullable = false +} + +# Create cicd_bw_autoconf SSH key +resource "openstack_compute_keypair_v2" "ssh_key" { + provider = openstack.openstack + name = "cicd_bw_autoconf" + public_key = file("~/.ssh/id_rsa.pub") +} + +# Create cicd_bw_autoconf instance +resource "openstack_compute_instance_v2" "instance" { + provider = openstack.openstack + name = "cicd_bw_autoconf" + image_name = "Debian 11" + flavor_name = "d2-4" + region = "SBG5" + key_pair = openstack_compute_keypair_v2.ssh_key.name + network { + name = "Ext-Net" + } +} + +# Attach failover IP to cicd_bw_autoconf instance +#resource "ovh_cloud_project_failover_ip_attach" "failover_ip" { +# provider = ovh.ovh +# ip = var.autoconf_ip +# routed_to = openstack_compute_instance_v2.instance.name +#} + +# Create Ansible inventory file +resource "local_file" "ansible_inventory" { + content = templatefile("templates/autoconf_inventory.tftpl", { + public_ip = openstack_compute_instance_v2.instance.access_ip_v4, + failover_ip = var.autoconf_ip + }) + filename = "/tmp/autoconf_inventory" +} diff --git a/tests/terraform/ovh/docker.tf b/tests/terraform/ovh/docker.tf new file mode 100644 index 000000000..276e3eb8a --- /dev/null +++ b/tests/terraform/ovh/docker.tf @@ -0,0 +1,41 @@ +# Variables +variable "docker_ip" { + type = string + nullable = false +} + +# Create cicd_bw_docker SSH key +resource "openstack_compute_keypair_v2" "ssh_key" { + provider = openstack.openstack + name = "cicd_bw_docker" + public_key = file("~/.ssh/id_rsa.pub") +} + +# Create cicd_bw_docker instance +resource "openstack_compute_instance_v2" "instance" { + provider = openstack.openstack + name = "cicd_bw_docker" + image_name = "Debian 11" + flavor_name = "d2-4" + region = "SBG5" + key_pair = openstack_compute_keypair_v2.ssh_key.name + network { + name = "Ext-Net" + } +} + +# Attach failover IP to cicd_bw_docker instance +#resource "ovh_cloud_project_failover_ip_attach" "failover_ip" { +# provider = ovh.ovh +# ip = var.docker_ip +# routed_to = openstack_compute_instance_v2.instance.name +#} + +# Create Ansible inventory file +resource "local_file" "ansible_inventory" { + content = templatefile("templates/docker_inventory.tftpl", { + public_ip = openstack_compute_instance_v2.instance.access_ip_v4, + failover_ip = var.docker_ip + }) + filename = "/tmp/docker_inventory" +} diff --git a/tests/terraform/ovh/kubernetes.tf b/tests/terraform/ovh/kubernetes.tf new file mode 100644 index 000000000..cdc4b5c6c --- /dev/null +++ b/tests/terraform/ovh/kubernetes.tf @@ -0,0 +1,51 @@ +# Create cicd_bw_k8s network +resource "ovh_cloud_project_network_private" "network" { + provider = ovh.ovh + name = "cicd_bw_k8s" + regions = ["SBG5"] + vlan_id = 60 +} +resource "ovh_cloud_project_network_private_subnet" "subnet" { + provider = ovh.ovh + depends_on = [ovh_cloud_project_network_private.network] + network_id = ovh_cloud_project_network_private.network.id + start = "192.168.42.100" + end = "192.168.42.200" + network = "192.168.42.0/24" + region = "SBG5" + dhcp = true + no_gateway = false +} + +# Create k8s cluster +resource "ovh_cloud_project_kube" "cluster" { + provider = ovh.ovh + depends_on = [ovh_cloud_project_network_private_subnet.subnet] + name = "cicd_bw_k8s" + region = "SBG5" + version = "1.24" + private_network_id = tolist(ovh_cloud_project_network_private.network.regions_attributes[*].openstackid)[0] + private_network_configuration { + default_vrack_gateway = "" + private_network_routing_as_default = false + } +} + +# Create nodepool +resource "ovh_cloud_project_kube_nodepool" "pool" { + provider = ovh.ovh + kube_id = ovh_cloud_project_kube.cluster.id + name = "pool" + flavor_name = "d2-4" + desired_nodes = 3 + min_nodes = 3 + max_nodes = 3 + monthly_billed = false + autoscale = false +} + +# Get kubeconfig file +resource "local_file" "kubeconfig" { + content = ovh_cloud_project_kube.cluster.kubeconfig + filename = "/tmp/kubeconfig" +} diff --git a/tests/terraform/ovh/linux.tf b/tests/terraform/ovh/linux.tf new file mode 100644 index 000000000..f33f02a02 --- /dev/null +++ b/tests/terraform/ovh/linux.tf @@ -0,0 +1,41 @@ +# Variables +variable "linux_ip" { + type = string + nullable = false +} + +# Create cicd_bw_linux SSH key +resource "openstack_compute_keypair_v2" "ssh_key" { + provider = openstack.openstack + name = "cicd_bw_linux" + public_key = file("~/.ssh/id_rsa.pub") +} + +# Create cicd_bw_linux instance +resource "openstack_compute_instance_v2" "instance" { + provider = openstack.openstack + name = "cicd_bw_linux" + image_name = "Debian 11" + flavor_name = "d2-4" + region = "SBG5" + key_pair = openstack_compute_keypair_v2.ssh_key.name + network { + name = "Ext-Net" + } +} + +# Attach failover IP to cicd_bw_linux instance +#resource "ovh_cloud_project_failover_ip_attach" "failover_ip" { +# provider = ovh.ovh +# ip = var.linux_ip +# routed_to = openstack_compute_instance_v2.instance.name +#} + +# Create Ansible inventory file +resource "local_file" "ansible_inventory" { + content = templatefile("templates/linux_inventory.tftpl", { + public_ip = openstack_compute_instance_v2.instance.access_ip_v4, + failover_ip = var.linux_ip + }) + filename = "/tmp/linux_inventory" +} diff --git a/tests/terraform/ovh/providers.tf b/tests/terraform/ovh/providers.tf new file mode 100644 index 000000000..79bcc7cb9 --- /dev/null +++ b/tests/terraform/ovh/providers.tf @@ -0,0 +1,22 @@ +terraform { + required_version = ">= 0.14.0" + required_providers { + openstack = { + source = "terraform-provider-openstack/openstack" + version = "~> 1.48.0" + } + ovh = { + source = "ovh/ovh" + version = ">= 0.13.0" + } + } +} + +provider "openstack" { + alias = "openstack" +} + +provider "ovh" { + alias = "ovh" +} + diff --git a/tests/terraform/ovh/swarm.tf b/tests/terraform/ovh/swarm.tf new file mode 100644 index 000000000..8cbc67140 --- /dev/null +++ b/tests/terraform/ovh/swarm.tf @@ -0,0 +1,64 @@ +# Variables +variable "swarm_ips" { + type = list(string) + nullable = false +} + +# Create cicd_bw_swarm SSH key +resource "openstack_compute_keypair_v2" "ssh_key" { + provider = openstack.openstack + name = "cicd_bw_swarm" + public_key = file("~/.ssh/id_rsa.pub") +} + +# Create cicd_bw_swarm network +resource "ovh_cloud_project_network_private" "network" { + provider = ovh.ovh + name = "cicd_bw_swarm" + regions = ["SBG5"] + vlan_id = 50 +} +resource "ovh_cloud_project_network_private_subnet" "subnet" { + provider = ovh.ovh + network_id = ovh_cloud_project_network_private.network.id + start = "192.168.42.1" + end = "192.168.42.254" + network = "192.168.42.0/24" + region = "SBG5" + no_gateway = true +} + +# Create cicd_bw_swarm_[1-3] instances +resource "openstack_compute_instance_v2" "instances" { + provider = openstack.openstack + depends_on = [ovh_cloud_project_network_private_subnet.subnet] + count = 3 + name = "cicd_bw_swarm_${count.index}" + image_name = "Debian 11" + flavor_name = "d2-4" + region = "SBG5" + key_pair = openstack_compute_keypair_v2.ssh_key.name + network { + name = "Ext-Net" + } + network { + name = ovh_cloud_project_network_private.network.name + } +} + +# Attach failover IPs to cicd_bw_swarm_[1-3] instances +#resource "ovh_cloud_project_failover_ip_attach" "failover_ip" { +# provider = ovh.ovh +# count = 3 +# ip = var.swarm_ips[${count.index}] +# routed_to = openstack_compute_instance_v2.instances[${count.index}] +#} + +# Create Ansible inventory file +resource "local_file" "ansible_inventory" { + content = templatefile("templates/swarm_inventory.tftpl", { + instances = openstack_compute_instance_v2.instances, + failover_ips = var.swarm_ips + }) + filename = "/tmp/swarm_inventory" +} diff --git a/tests/terraform/ovh/templates/autoconf_inventory.tftpl b/tests/terraform/ovh/templates/autoconf_inventory.tftpl new file mode 100644 index 000000000..6e9bf5eac --- /dev/null +++ b/tests/terraform/ovh/templates/autoconf_inventory.tftpl @@ -0,0 +1,2 @@ +[autoconf] +autoconf ansible_host=${public_ip} ansible_user=debian failover_ip=${failover_ip} diff --git a/tests/terraform/ovh/templates/docker_inventory.tftpl b/tests/terraform/ovh/templates/docker_inventory.tftpl new file mode 100644 index 000000000..74c0d4764 --- /dev/null +++ b/tests/terraform/ovh/templates/docker_inventory.tftpl @@ -0,0 +1,2 @@ +[docker] +docker ansible_host=${public_ip} ansible_user=debian failover_ip=${failover_ip} diff --git a/tests/terraform/ovh/templates/linux_inventory.tftpl b/tests/terraform/ovh/templates/linux_inventory.tftpl new file mode 100644 index 000000000..5295c4ddf --- /dev/null +++ b/tests/terraform/ovh/templates/linux_inventory.tftpl @@ -0,0 +1,2 @@ +[linux] +linux ansible_host=${public_ip} ansible_user=debian failover_ip=${failover_ip} diff --git a/tests/terraform/ovh/templates/swarm_inventory.tftpl b/tests/terraform/ovh/templates/swarm_inventory.tftpl new file mode 100644 index 000000000..3d0d844b1 --- /dev/null +++ b/tests/terraform/ovh/templates/swarm_inventory.tftpl @@ -0,0 +1,6 @@ +[managers] +manager ansible_host=${instances[0].access_ip_v4} ansible_user=debian failover_ip=${failover_ips[0]} local_ip=192.168.42.100 + +[workers] +worker1 ansible_host=${instances[1].access_ip_v4} ansible_user=debian failover_ip=${failover_ips[1]} local_ip=192.168.42.101 +worker2 ansible_host=${instances[2].access_ip_v4} ansible_user=debian failover_ip=${failover_ips[2]} local_ip=192.168.42.102 diff --git a/tests/terraform/providers.tf b/tests/terraform/providers.tf new file mode 100644 index 000000000..f91c4f34e --- /dev/null +++ b/tests/terraform/providers.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + scaleway = { + source = "scaleway/scaleway" + version = "2.5.0" + } + kubectl = { + source = "gavinbunney/kubectl" + version = "1.14.0" + } + } +} \ No newline at end of file diff --git a/tests/terraform/swarm.tf b/tests/terraform/swarm.tf new file mode 100644 index 000000000..8aad3073d --- /dev/null +++ b/tests/terraform/swarm.tf @@ -0,0 +1,41 @@ +# Variables +variable "swarm_ips" { + type = list(string) + nullable = false +} +variable "swarm_ips_id" { + type = list(string) + nullable = false +} + +# Create cicd_bw_swarm SSH key +resource "scaleway_account_ssh_key" "ssh_key" { + name = "cicd_bw_swarm" + public_key = file("~/.ssh/id_rsa.pub") +} + +# Create cicd_bw_swarm private network +resource "scaleway_vpc_private_network" "pn" { + name = "cicd_bw_swarm" +} + +# Create cicd_bw_swarm_[1-3] instances +resource "scaleway_instance_server" "instances" { + count = 3 + depends_on = [scaleway_account_ssh_key.ssh_key] + name = "cicd_bw_swarm_${count.index}" + type = "DEV1-M" + image = "debian_bullseye" + ip_id = var.swarm_ips_id[count.index] + private_network { + pn_id = scaleway_vpc_private_network.pn.id + } +} + +# Create Ansible inventory file +resource "local_file" "ansible_inventory" { + content = templatefile("templates/swarm_inventory.tftpl", { + public_ips = var.swarm_ips + }) + filename = "/tmp/swarm_inventory" +} \ No newline at end of file diff --git a/tests/terraform/templates/autoconf_inventory.tftpl b/tests/terraform/templates/autoconf_inventory.tftpl new file mode 100644 index 000000000..fe438dbf5 --- /dev/null +++ b/tests/terraform/templates/autoconf_inventory.tftpl @@ -0,0 +1,2 @@ +[autoconf] +autoconf ansible_host=${public_ip} ansible_user=root diff --git a/tests/terraform/templates/docker_inventory.tftpl b/tests/terraform/templates/docker_inventory.tftpl new file mode 100644 index 000000000..51d69d810 --- /dev/null +++ b/tests/terraform/templates/docker_inventory.tftpl @@ -0,0 +1,2 @@ +[docker] +docker ansible_host=${public_ip} ansible_user=root diff --git a/tests/terraform/templates/lb.yml.tftpl b/tests/terraform/templates/lb.yml.tftpl new file mode 100644 index 000000000..49ba49c98 --- /dev/null +++ b/tests/terraform/templates/lb.yml.tftpl @@ -0,0 +1,18 @@ +kind: Service +apiVersion: v1 +metadata: + name: bw-lb + annotations: + service.beta.kubernetes.io/scw-loadbalancer-proxy-protocol-v2: "true" +spec: + selector: + app: bunkerweb + type: LoadBalancer + ports: + - name: http + port: 80 + targetPort: 8080 + - name: https + port: 443 + targetPort: 8443 + loadBalancerIP: ${lb_ip} diff --git a/tests/terraform/templates/linux_inventory.tftpl b/tests/terraform/templates/linux_inventory.tftpl new file mode 100644 index 000000000..c2590d2ba --- /dev/null +++ b/tests/terraform/templates/linux_inventory.tftpl @@ -0,0 +1,2 @@ +[linux] +linux ansible_host=${public_ip} ansible_user=root diff --git a/tests/terraform/templates/reg.yml.tftpl b/tests/terraform/templates/reg.yml.tftpl new file mode 100644 index 000000000..02d3fc73c --- /dev/null +++ b/tests/terraform/templates/reg.yml.tftpl @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Secret +metadata: + name: secret-registry +type: kubernetes.io/dockerconfigjson +data: + .dockerconfigjson: ${dockerconfigjson} \ No newline at end of file diff --git a/tests/terraform/templates/swarm_inventory.tftpl b/tests/terraform/templates/swarm_inventory.tftpl new file mode 100644 index 000000000..b6facb623 --- /dev/null +++ b/tests/terraform/templates/swarm_inventory.tftpl @@ -0,0 +1,6 @@ +[managers] +manager ansible_host=${public_ips[0]} ansible_user=root local_ip=192.168.42.100 + +[workers] +worker1 ansible_host=${public_ips[1]} ansible_user=root local_ip=192.168.42.101 +worker2 ansible_host=${public_ips[2]} ansible_user=root local_ip=192.168.42.102 diff --git a/tests/utils/bunkerweb.yml b/tests/utils/bunkerweb.yml new file mode 100644 index 000000000..2640cacb0 --- /dev/null +++ b/tests/utils/bunkerweb.yml @@ -0,0 +1,157 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: cr-bunkerweb +rules: +- apiGroups: [""] + resources: ["services", "pods", "configmaps"] + verbs: ["get", "watch", "list"] +- apiGroups: ["networking.k8s.io"] + resources: ["ingresses"] + verbs: ["get", "watch", "list"] +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: sa-bunkerweb +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: crb-bunkerweb +subjects: +- kind: ServiceAccount + name: sa-bunkerweb + namespace: default + apiGroup: "" +roleRef: + kind: ClusterRole + name: cr-bunkerweb + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: bunkerweb +spec: + selector: + matchLabels: + app: bunkerweb + template: + metadata: + labels: + app: bunkerweb + annotations: + bunkerweb.io/AUTOCONF: "yes" + spec: + containers: + - name: bunkerweb + image: bunkerity/bunkerweb:1.4.6 + imagePullPolicy: Always + securityContext: + runAsUser: 101 + runAsGroup: 101 + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + ports: + - containerPort: 8080 + - containerPort: 8443 + env: + - name: KUBERNETES_MODE + value: "yes" + # replace with your DNS resolvers + # e.g. : kube-dns.kube-system.svc.cluster.local + - name: DNS_RESOLVERS + value: "coredns.kube-system.svc.cluster.local" + - name: USE_API + value: "yes" + - name: API_WHITELIST_IP + value: "10.0.0.0/8 192.168.0.0/16 172.16.0.0/12 100.64.0.0/10" + - name: SERVER_NAME + value: "" + - name: MULTISITE + value: "yes" + - name: USE_REAL_IP + value: "yes" + - name: USE_PROXY_PROTOCOL + value: "yes" + - name: REAL_IP_HEADER + value: "proxy_protocol" + - name: REAL_IP_FROM + value: "10.0.0.0/8 192.168.0.0/16 172.16.0.0/12 100.64.0.0/10" + - name: USE_LETS_ENCRYPT_STAGING + value: "yes" + livenessProbe: + exec: + command: + - /opt/bunkerweb/helpers/healthcheck.sh + initialDelaySeconds: 30 + periodSeconds: 5 + timeoutSeconds: 1 + failureThreshold: 3 + readinessProbe: + exec: + command: + - /opt/bunkerweb/helpers/healthcheck.sh + initialDelaySeconds: 30 + periodSeconds: 1 + timeoutSeconds: 1 + failureThreshold: 3 + imagePullSecrets: + - name: secret-registry +--- +apiVersion: v1 +kind: Service +metadata: + name: svc-bunkerweb +spec: + clusterIP: None + selector: + app: bunkerweb +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: pvc-bunkerweb +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: bunkerweb-controller +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + app: bunkerweb-controller + template: + metadata: + labels: + app: bunkerweb-controller + spec: + serviceAccountName: sa-bunkerweb + volumes: + - name: vol-bunkerweb + persistentVolumeClaim: + claimName: pvc-bunkerweb + containers: + - name: bunkerweb-controller + image: bunkerity/bunkerweb-autoconf:1.4.6 + imagePullPolicy: Always + env: + - name: KUBERNETES_MODE + value: "yes" + volumeMounts: + - name: vol-bunkerweb + mountPath: /data + imagePullSecrets: + - name: secret-registry \ No newline at end of file diff --git a/tests/utils/k8s.yml b/tests/utils/k8s.yml deleted file mode 100644 index 0961bf2bd..000000000 --- a/tests/utils/k8s.yml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: PersistentVolume -metadata: - name: pv-bunkerweb -spec: - capacity: - storage: 5Gi - accessModes: - - ReadWriteOnce - hostPath: - path: "/tmp/bw-data"