From 0313925948895e5cb829b1aa1e44652ec887f128 Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Wed, 4 Sep 2024 16:00:32 +0100 Subject: [PATCH] ci: docker compose configuration for production --- cSpell.json | 4 + droplet/.env.production | 1 + droplet/.gitignore | 2 + droplet/README.md | 191 ++++++++++++++++++ droplet/bin/install.sh | 27 +++ droplet/bin/ssl_renew.sh | 15 ++ droplet/compose.yaml | 62 ++++++ .../share/container/default/config/nginx.conf | 75 +++++++ 8 files changed, 377 insertions(+) create mode 100644 droplet/.env.production create mode 100644 droplet/.gitignore create mode 100644 droplet/README.md create mode 100755 droplet/bin/install.sh create mode 100755 droplet/bin/ssl_renew.sh create mode 100644 droplet/compose.yaml create mode 100644 droplet/share/container/default/config/nginx.conf diff --git a/cSpell.json b/cSpell.json index 025afaa..3cad346 100644 --- a/cSpell.json +++ b/cSpell.json @@ -9,11 +9,14 @@ "bitvec", "btih", "camino", + "certbot", + "certonly", "comms", "Containerfile", "distroless", "ikatson", "infohash", + "letsencrypt", "librqbit", "libz", "metainfo", @@ -26,6 +29,7 @@ "thiserror", "tlsv", "urlencoding", + "webroot", "Werror" ], "enableFiletypes": [ diff --git a/droplet/.env.production b/droplet/.env.production new file mode 100644 index 0000000..be84eaf --- /dev/null +++ b/droplet/.env.production @@ -0,0 +1 @@ +USER_ID=1000 diff --git a/droplet/.gitignore b/droplet/.gitignore new file mode 100644 index 0000000..1a532c9 --- /dev/null +++ b/droplet/.gitignore @@ -0,0 +1,2 @@ +.env +storage/ diff --git a/droplet/README.md b/droplet/README.md new file mode 100644 index 0000000..21604f8 --- /dev/null +++ b/droplet/README.md @@ -0,0 +1,191 @@ +# Droplet configuration + +It's a sample production configuration to deploy the webapp on a Digital Ocean droplet. + +## Requirements + +- Docker version 24.0.7, build afdd53b. +- Docker Compose version v2.3.3. +- GNU bash, version 5.2.15(1)-release (x86_64-pc-linux-gnu). + +## Install + +```console +cd \ + && mkdir -p github/torrust \ + && cd torrust/ \ + && git clone --single-branch --branch main https://github.com/torrust/torrust-hash2torrent.git \ + && cd torrust-hash2torrent/ \ + && git status \ + && cd droplet/ \ + && ./bin/install.sh +``` + +### HTTPS + +Get certificates: + +Log into the `certbot` container: + +```console +docker com1pose run --entrypoint /bin/sh certbot +``` + +Get staging certificates: + +```console +certbot certonly --webroot --webroot-path=/var/www/html --email your@email.com --agree-tos --no-eff-email --staging -d hash2torrent.com +``` + +Get production certificates: + +```console +certbot certonly --webroot --webroot-path=/var/www/html --email your@email.com --agree-tos --no-eff-email --force-renewal -d hash2torrent.com +``` + +Check that the proxy can see the certificates: + +```console +docker compose exec proxy ls -la /etc/letsencrypt/live +``` + +Generate your key with the openssl command: + +```console +sudo openssl dhparam -out /home/torrust/github/torrust/torrust-hash2torrent/droplet/storage/dhparam/dhparam-2048.pem 2048 +``` + +Edit the Nginx config file: + +```console +vim ./storage/proxy/etc/nginx-conf/nginx.conf +``` + +Uncomment the lines for HTTPS servers and recreate the proxy with: + +```console +docker compose up -d --force-recreate --no-deps proxy +``` + +Add the following cronjob with `sudo crontab -e` to auto-renew certificates: + +```text +0 12 * * * /home/torrust/github/torrust/torrust-hash2torrent/droplet/bin/ssl_renew.sh >> /var/log/cron.log 2>&1 +``` + +You can check the cronjob output with `tail -n 200 /var/log/cron.log`. + +### Storage + +This is how the storage folder is configured after installation (including HTTPS). + +```console +$ sudo tree storage +storage +├── certbot +│   ├── etc +│   │   ├── accounts +│   │   │   ├── acme-staging-v02.api.letsencrypt.org +│   │   │   │   └── directory +│   │   │   │   └── 2cedba87be07faf5da056edf65b8939f +│   │   │   │   ├── meta.json +│   │   │   │   ├── private_key.json +│   │   │   │   └── regr.json +│   │   │   └── acme-v02.api.letsencrypt.org +│   │   │   └── directory +│   │   │   └── 08ee268c3eef6bf23301b1300bda96e5 +│   │   │   ├── meta.json +│   │   │   ├── private_key.json +│   │   │   └── regr.json +│   │   ├── archive +│   │   │   └── hash2torrent.com +│   │   │   ├── cert1.pem +│   │   │   ├── cert2.pem +│   │   │   ├── chain1.pem +│   │   │   ├── chain2.pem +│   │   │   ├── fullchain1.pem +│   │   │   ├── fullchain2.pem +│   │   │   ├── privkey1.pem +│   │   │   └── privkey2.pem +│   │   ├── live +│   │   │   ├── README +│   │   │   └── hash2torrent.com +│   │   │   ├── README +│   │   │   ├── cert.pem -> ../../archive/hash2torrent.com/cert2.pem +│   │   │   ├── chain.pem -> ../../archive/hash2torrent.com/chain2.pem +│   │   │   ├── fullchain.pem -> ../../archive/hash2torrent.com/fullchain2.pem +│   │   │   └── privkey.pem -> ../../archive/hash2torrent.com/privkey2.pem +│   │   ├── renewal +│   │   │   └── hash2torrent.com.conf +│   │   └── renewal-hooks +│   │   ├── deploy +│   │   ├── post +│   │   └── pre +│   └── lib +├── dhparam +│   └── dhparam-2048.pem +├── hash2torrent +│   ├── etc +│   ├── lib +│   │   ├── session +│   │   └── torrents +│   │   └── 443c7602b4fde83d1154d6d9da48808418b181b6.torrent +│   └── log +└── proxy + ├── etc + │   └── nginx-conf + │   └── nginx.conf + └── webroot + +30 directories, 24 files +``` + +## Usage + +To start the application: + +```s +docker compose up --build --detach +``` + +To stop the application: + +```s +docker compose down +``` + +By default, the application will: + +- Be available at . +- Use the `./storage` directory to store the data. + +After starting the application you should see these running containers: + +```s +$ docker ps +CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +03e2f0a66512 nginx:mainline-alpine "/docker-entrypoint.…" 3 hours ago Up 8 minutes 0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp proxy +5b30ef8ddcd1 torrust/hash2torrent:main "/usr/local/bin/entr…" 3 hours ago Up 3 hours (healthy) 0.0.0.0:3000->3000/tcp, :::3000->3000/tcp, 51000-51010/tcp hash2torrent +``` + +Other commands are: + +Restart all (reloading env vars from `.env` file by forcing recreation): + +```console +docker compose up -d --force-recreate +``` + +Restart proxy (to reload Nginx configuration): + +```console +docker compose --ansi never restart proxy +``` + +Update container images (to upgrade the services): + +```console +docker compose down +docker compose pull +docker compose up --build --detach +``` diff --git a/droplet/bin/install.sh b/droplet/bin/install.sh new file mode 100755 index 0000000..0e412de --- /dev/null +++ b/droplet/bin/install.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +if ! [ -f "./.env" ]; then + echo "Creating compose .env './.env'" + cp .env.production .env +fi + +## Proxy + +mkdir -p ./storage/proxy/etc/nginx-conf +mkdir -p ./storage/proxy/webroot +mkdir -p ./storage/dhparam + +if ! [ -f "./storage/proxy/etc/nginx-conf/nginx.conf" ]; then + echo "Creating proxy config file: './storage/proxy/etc/nginx-conf/nginx.conf'" + cp ./share/container/default/config/nginx.conf ./storage/proxy/etc/nginx-conf/nginx.conf +fi + +## Certbot + +mkdir -p ./storage/certbot/etc +mkdir -p ./storage/certbot/lib + +## hash2torrent + +mkdir -p ./storage/hash2torrent/lib/session +mkdir -p ./storage/hash2torrent/lib/torrents diff --git a/droplet/bin/ssl_renew.sh b/droplet/bin/ssl_renew.sh new file mode 100755 index 0000000..dcd3d98 --- /dev/null +++ b/droplet/bin/ssl_renew.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +COMPOSE="/usr/bin/docker compose --ansi never" +DOCKER="/usr/bin/docker" + +cd /home/torrust/github/torrust/hash2torrent/droplet || exit + +# Run this just for testing purposes +#$COMPOSE run certbot renew --dry-run && $COMPOSE kill -s SIGHUP proxy + +# Renew certificates that are close to expiring and restart proxy server +# to reload Nginx configuration. +$COMPOSE run certbot renew && $COMPOSE --ansi never restart proxy + +$DOCKER system prune -af diff --git a/droplet/compose.yaml b/droplet/compose.yaml new file mode 100644 index 0000000..9b1fe6f --- /dev/null +++ b/droplet/compose.yaml @@ -0,0 +1,62 @@ +--- +name: torrust +services: + certbot: + image: certbot/certbot + container_name: certbot + volumes: + - ./storage/proxy/webroot:/var/www/html + - ./storage/certbot/etc:/etc/letsencrypt + - ./storage/certbot/lib:/var/lib/letsencrypt + logging: + options: + max-size: "10m" + max-file: "10" + depends_on: + - proxy + + proxy: + image: nginx:mainline-alpine + container_name: proxy + restart: unless-stopped + networks: + - backend_network + ports: + - "80:80" + - "443:443" + volumes: + - ./storage/proxy/webroot:/var/www/html + - ./storage/proxy/etc/nginx-conf:/etc/nginx/conf.d + - ./storage/certbot/etc:/etc/letsencrypt + - ./storage/certbot/lib:/var/lib/letsencrypt + - ./storage/dhparam:/etc/ssl/certs + logging: + options: + max-size: "10m" + max-file: "10" + depends_on: + - hash2torrent + + hash2torrent: + image: torrust/hash2torrent:main + container_name: hash2torrent + tty: true + restart: unless-stopped + environment: + - USER_ID=${USER_ID} + networks: + - backend_network + ports: + - "3000:3000" + - "51000-51010" + volumes: + - ./storage/hash2torrent/lib:/var/lib/torrust/hash2torrent:Z + - ./storage/hash2torrent/log:/var/log/torrust/hash2torrent:Z + - ./storage/hash2torrent/etc:/etc/torrust/hash2torrent:Z + logging: + options: + max-size: "10m" + max-file: "10" + +networks: + backend_network: {} diff --git a/droplet/share/container/default/config/nginx.conf b/droplet/share/container/default/config/nginx.conf new file mode 100644 index 0000000..3012676 --- /dev/null +++ b/droplet/share/container/default/config/nginx.conf @@ -0,0 +1,75 @@ +server +{ + listen 80; + listen [::]:80; + + root /var/www/html; + index index.html index.htm index.nginx-debian.html; + + server_name hash2torrent.com; + + error_log /var/log/nginx/error.log debug; + + location / + { + proxy_pass http://hash2torrent:3000; + } + + location ~ /.well-known/acme-challenge + { + allow all; + root /var/www/html; + } +} + +#server +#{ +# listen 443 ssl http2; +# listen [::]:443 ssl http2; +# server_name hash2torrent.com; +# +# error_log /var/log/nginx/error.log debug; +# merge_slashes off; +# server_tokens off; +# +# ssl_certificate /etc/letsencrypt/live/hash2torrent.com/fullchain.pem; +# ssl_certificate_key /etc/letsencrypt/live/hash2torrent.com/privkey.pem; +# +# ssl_buffer_size 8k; +# +# ssl_dhparam /etc/ssl/certs/dhparam-2048.pem; +# +# ssl_protocols TLSv1.2; +# ssl_prefer_server_ciphers on; +# +# ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5; +# +# ssl_ecdh_curve secp384r1; +# ssl_session_tickets off; +# +# ssl_stapling on; +# ssl_stapling_verify on; +# resolver 8.8.8.8; +# +# error_log /var/log/nginx/error.log debug; +# +# location / +# { +# try_files $uri @hash2torrent; +# } +# +# location @hash2torrent +# { +# proxy_pass http://hash2torrent:3000; +# add_header X-Frame-Options "SAMEORIGIN" always; +# add_header X-XSS-Protection "1; mode=block" always; +# add_header X-Content-Type-Options "nosniff" always; +# add_header Referrer-Policy "no-referrer-when-downgrade" always; +# add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always; +# #add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; +# # enable strict transport security only if you understand the implications +# } +# +# root /var/www/html; +# index index.html index.htm index.nginx-debian.html; +#}