diff --git a/Dockerfile b/Dockerfile index 16811e7..08c365d 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 -FROM ghcr.io/linuxserver/baseimage-alpine-nginx:3.20 +FROM ghcr.io/linuxserver/baseimage-alpine-nginx:3.21 # set version label ARG BUILD_DATE @@ -88,7 +88,7 @@ RUN \ pip install -U --no-cache-dir \ pip \ wheel && \ - pip install -U --no-cache-dir --find-links https://wheel-index.linuxserver.io/alpine-3.20/ \ + pip install -U --no-cache-dir --find-links https://wheel-index.linuxserver.io/alpine-3.21/ \ certbot==${CERTBOT_VERSION} \ certbot-dns-acmedns \ certbot-dns-aliyun \ @@ -150,9 +150,9 @@ RUN \ rm -f /etc/nginx/conf.d/stream.conf && \ echo "**** correct ip6tables legacy issue ****" && \ rm \ - /sbin/ip6tables && \ + /usr/sbin/ip6tables && \ ln -s \ - /sbin/ip6tables-nft /sbin/ip6tables && \ + /usr/sbin/ip6tables-nft /usr/sbin/ip6tables && \ echo "**** remove unnecessary fail2ban filters ****" && \ rm \ /etc/fail2ban/jail.d/alpine-ssh.conf && \ diff --git a/Dockerfile.aarch64 b/Dockerfile.aarch64 index 2aa2823..9bc2aed 100755 --- a/Dockerfile.aarch64 +++ b/Dockerfile.aarch64 @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 -FROM ghcr.io/linuxserver/baseimage-alpine-nginx:arm64v8-3.20 +FROM ghcr.io/linuxserver/baseimage-alpine-nginx:arm64v8-3.21 # set version label ARG BUILD_DATE @@ -88,7 +88,7 @@ RUN \ pip install -U --no-cache-dir \ pip \ wheel && \ - pip install -U --no-cache-dir --find-links https://wheel-index.linuxserver.io/alpine-3.20/ \ + pip install -U --no-cache-dir --find-links https://wheel-index.linuxserver.io/alpine-3.21/ \ certbot==${CERTBOT_VERSION} \ certbot-dns-acmedns \ certbot-dns-aliyun \ @@ -150,9 +150,9 @@ RUN \ rm -f /etc/nginx/conf.d/stream.conf && \ echo "**** correct ip6tables legacy issue ****" && \ rm \ - /sbin/ip6tables && \ + /usr/sbin/ip6tables && \ ln -s \ - /sbin/ip6tables-nft /sbin/ip6tables && \ + /usr/sbin/ip6tables-nft /usr/sbin/ip6tables && \ echo "**** remove unnecessary fail2ban filters ****" && \ rm \ /etc/fail2ban/jail.d/alpine-ssh.conf && \ diff --git a/README.md b/README.md index ebcf8f3..b5d22a5 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,15 @@ This will *ask* Google et al not to index and list your site. Be careful with th Please follow the instructions [on this blog post](https://www.linuxserver.io/blog/2020-08-21-introducing-swag#migrate). +## Read-Only Operation + +This image can be run with a read-only container filesystem. For details please [read the docs](https://docs.linuxserver.io/misc/read-only/). + +### Caveats + +* `/tmp` must be mounted to tmpfs +* fail2ban will not be available + ## Usage To help you get started creating a container from this image you can either use docker-compose or the docker cli. @@ -180,6 +189,7 @@ services: - ONLY_SUBDOMAINS=false #optional - EXTRA_DOMAINS= #optional - STAGING=false #optional + - DISABLE_F2B= #optional volumes: - /path/to/swag/config:/config ports: @@ -207,6 +217,7 @@ docker run -d \ -e ONLY_SUBDOMAINS=false `#optional` \ -e EXTRA_DOMAINS= `#optional` \ -e STAGING=false `#optional` \ + -e DISABLE_F2B= `#optional` \ -p 443:443 \ -p 80:80 `#optional` \ -v /path/to/swag/config:/config \ @@ -235,7 +246,9 @@ Containers are configured using parameters passed at runtime (such as those abov | `-e ONLY_SUBDOMAINS=false` | If you wish to get certs only for certain subdomains, but not the main domain (main domain may be hosted on another machine and cannot be validated), set this to `true` | | `-e EXTRA_DOMAINS=` | Additional fully qualified domain names (comma separated, no spaces) ie. `example.net,subdomain.example.net,*.example.org` | | `-e STAGING=false` | Set to `true` to retrieve certs in staging mode. Rate limits will be much higher, but the resulting cert will not pass the browser's security test. Only to be used for testing purposes. | +| `-e DISABLE_F2B=` | Set to `true` to disable the Fail2ban service in the container, if you're already running it elsewhere or using a different IPS. | | `-v /config` | Persistent config files | +| `--read-only=true` | Run container with a read-only filesystem. Please [read the docs](https://docs.linuxserver.io/misc/read-only/). | | `--cap-add=NET_ADMIN` | Required for fail2Ban to be able to modify iptables rules. | ### Portainer notice @@ -404,6 +417,7 @@ Once registered you can define the dockerfile to use with `-f Dockerfile.aarch64 ## Versions +* **17.12.24:** - Rebase to Alpine 3.21. * **21.10.24:** - Fix naming issue with Dynu plugin. If you are using Dynu, please make sure your credentials are set in /config/dns-conf/dynu.ini and your DNSPLUGIN variable is set to dynu (not dynudns). * **30.08.24:** - Fix zerossl cert revocation. * **24.07.14:** - Rebase to Alpine 3.20. Remove deprecated Google Domains certbot plugin. Existing users should update their nginx confs to avoid http2 deprecation warnings. diff --git a/readme-vars.yml b/readme-vars.yml index 2a9e9d6..3329d0f 100644 --- a/readme-vars.yml +++ b/readme-vars.yml @@ -37,9 +37,14 @@ opt_param_env_vars: - {env_var: "ONLY_SUBDOMAINS", env_value: "false", desc: "If you wish to get certs only for certain subdomains, but not the main domain (main domain may be hosted on another machine and cannot be validated), set this to `true`"} - {env_var: "EXTRA_DOMAINS", env_value: "", desc: "Additional fully qualified domain names (comma separated, no spaces) ie. `example.net,subdomain.example.net,*.example.org`"} - {env_var: "STAGING", env_value: "false", desc: "Set to `true` to retrieve certs in staging mode. Rate limits will be much higher, but the resulting cert will not pass the browser's security test. Only to be used for testing purposes."} + - {env_var: "DISABLE_F2B", env_value: "", desc: "Set to `true` to disable the Fail2ban service in the container, if you're already running it elsewhere or using a different IPS."} opt_param_usage_include_ports: true opt_param_ports: - {external_port: "80", internal_port: "80", port_desc: "HTTP port (required for HTTP validation and HTTP -> HTTPS redirect)"} +readonly_supported: true +readonly_message: | + * `/tmp` must be mounted to tmpfs + * fail2ban will not be available # application setup block app_setup_block_enabled: true app_setup_block: | @@ -200,6 +205,7 @@ init_diagram: | "swag:latest" <- Base Images # changelog changelogs: + - {date: "17.12.24:", desc: "Rebase to Alpine 3.21."} - {date: "21.10.24:", desc: "Fix naming issue with Dynu plugin. If you are using Dynu, please make sure your credentials are set in /config/dns-conf/dynu.ini and your DNSPLUGIN variable is set to dynu (not dynudns)."} - {date: "30.08.24:", desc: "Fix zerossl cert revocation."} - {date: "24.07.14:", desc: "Rebase to Alpine 3.20. Remove deprecated Google Domains certbot plugin. Existing users should update their nginx confs to avoid http2 deprecation warnings."} diff --git a/root/app/le-renew.sh b/root/app/le-renew.sh index 7f2137a..c597f35 100755 --- a/root/app/le-renew.sh +++ b/root/app/le-renew.sh @@ -6,4 +6,4 @@ echo echo "<------------------------------------------------->" echo "cronjob running on $(date)" echo "Running certbot renew" -certbot renew --non-interactive +certbot renew --non-interactive --config-dir /config/etc/letsencrypt --logs-dir /config/log/letsencrypt --work-dir /tmp/letsencrypt --config /config/etc/letsencrypt/cli.ini diff --git a/root/defaults/etc/letsencrypt/renewal-hooks/deploy/10-default b/root/defaults/etc/letsencrypt/renewal-hooks/deploy/10-default old mode 100644 new mode 100755 diff --git a/root/defaults/etc/letsencrypt/renewal-hooks/post/10-nginx b/root/defaults/etc/letsencrypt/renewal-hooks/post/10-nginx old mode 100644 new mode 100755 diff --git a/root/defaults/etc/letsencrypt/renewal-hooks/pre/10-nginx b/root/defaults/etc/letsencrypt/renewal-hooks/pre/10-nginx old mode 100644 new mode 100755 diff --git a/root/defaults/nginx/site-confs/default.conf.sample b/root/defaults/nginx/site-confs/default.conf.sample index ce0d65e..8613f1e 100644 --- a/root/defaults/nginx/site-confs/default.conf.sample +++ b/root/defaults/nginx/site-confs/default.conf.sample @@ -1,4 +1,4 @@ -## Version 2024/07/16 - Changelog: https://github.com/linuxserver/docker-swag/commits/master/root/defaults/nginx/site-confs/default.conf.sample +## Version 2024/12/17 - Changelog: https://github.com/linuxserver/docker-swag/commits/master/root/defaults/nginx/site-confs/default.conf.sample # redirect all traffic to https server { @@ -80,5 +80,3 @@ server { # enable subdomain method reverse proxy confs include /config/nginx/proxy-confs/*.subdomain.conf; -# enable proxy cache for auth -proxy_cache_path cache/ keys_zone=auth_cache:10m; diff --git a/root/etc/crontabs/root b/root/etc/crontabs/root index 15de5f7..c848b3f 100644 --- a/root/etc/crontabs/root +++ b/root/etc/crontabs/root @@ -5,4 +5,4 @@ 0 3 * * 6 run-parts /etc/periodic/weekly 0 5 1 * * run-parts /etc/periodic/monthly -8 2 * * * /app/le-renew.sh >> /config/log/letsencrypt/letsencrypt.log 2>&1 +8 2 * * * /app/le-renew.sh >> /config/log/letsencrypt/renewal.log 2>&1 diff --git a/root/etc/s6-overlay/s6-rc.d/init-certbot-config/run b/root/etc/s6-overlay/s6-rc.d/init-certbot-config/run index 3283510..51e8e5e 100755 --- a/root/etc/s6-overlay/s6-rc.d/init-certbot-config/run +++ b/root/etc/s6-overlay/s6-rc.d/init-certbot-config/run @@ -38,12 +38,6 @@ if [[ "${VALIDATION}" = "dns" ]] && ! echo "${CERTBOT_DNS_AUTHENTICATORS}" | gre sleep infinity fi -# set owner of certbot's CONFIG_DIR, WORK_DIR, and LOGS_DIR to abc -lsiown -R abc:abc \ - /etc/letsencrypt \ - /var/lib/letsencrypt \ - /var/log/letsencrypt - # set_ini_value logic: # - if the name is not found in the file, append the name=value to the end of the file # - if the name is found in the file, replace the value @@ -67,7 +61,6 @@ cp -n /defaults/dns-conf/* /config/dns-conf/ 2> >(grep -v 'cp: not replacing') lsiown -R abc:abc /config/dns-conf # copy default renewal hooks -chmod -R +x /defaults/etc/letsencrypt/renewal-hooks cp -nR /defaults/etc/letsencrypt/renewal-hooks/* /config/etc/letsencrypt/renewal-hooks/ 2> >(grep -v 'cp: not replacing') lsiown -R abc:abc /config/etc/letsencrypt/renewal-hooks @@ -169,14 +162,14 @@ fi rm -rf /config/keys/letsencrypt if [[ "${ONLY_SUBDOMAINS}" = "true" ]] && [[ ! "${SUBDOMAINS}" = "wildcard" ]]; then DOMAIN="$(echo "${SUBDOMAINS}" | tr ',' ' ' | awk '{print $1}').${URL}" - ln -s ../etc/letsencrypt/live/"${DOMAIN}" /config/keys/letsencrypt + ln -s /config/etc/letsencrypt/live/"${DOMAIN}" /config/keys/letsencrypt else - ln -s ../etc/letsencrypt/live/"${URL}" /config/keys/letsencrypt + ln -s /config/etc/letsencrypt/live/"${URL}" /config/keys/letsencrypt fi # cleanup unused csr and keys folders -rm -rf /etc/letsencrypt/csr -rm -rf /etc/letsencrypt/keys +rm -rf /config/etc/letsencrypt/csr +rm -rf /config/etc/letsencrypt/keys # checking for changes in cert variables, revoking certs if necessary if [[ ! "${URL}" = "${ORIGURL}" ]] || @@ -197,9 +190,9 @@ if [[ ! "${URL}" = "${ORIGURL}" ]] || REV_ACMESERVER=("https://acme-v02.api.letsencrypt.org/directory") fi if [[ -f /config/etc/letsencrypt/live/"${ORIGDOMAIN}"/fullchain.pem ]]; then - certbot revoke --non-interactive --cert-path /config/etc/letsencrypt/live/"${ORIGDOMAIN}"/fullchain.pem --key-path /config/etc/letsencrypt/live/"${ORIGDOMAIN}"/privkey.pem --server "${REV_ACMESERVER[@]}" || true + certbot revoke --config-dir /config/etc/letsencrypt --logs-dir /config/log/letsencrypt --work-dir /tmp/letsencrypt --config /config/etc/letsencrypt/cli.ini --non-interactive --cert-path /config/etc/letsencrypt/live/"${ORIGDOMAIN}"/fullchain.pem --key-path /config/etc/letsencrypt/live/"${ORIGDOMAIN}"/privkey.pem --server "${REV_ACMESERVER[@]}" || true else - certbot revoke --non-interactive --cert-name "${ORIGDOMAIN}" --server "${REV_ACMESERVER[@]}" || true + certbot revoke --config-dir /config/etc/letsencrypt --logs-dir /config/log/letsencrypt --work-dir /tmp/letsencrypt --config /config/etc/letsencrypt/cli.ini --non-interactive --cert-name "${ORIGDOMAIN}" --server "${REV_ACMESERVER[@]}" || true fi rm -rf /config/etc/letsencrypt/{accounts,archive,live,renewal} fi @@ -212,9 +205,9 @@ if [[ -f "/config/keys/letsencrypt/chain.pem" ]] && { [[ "${CERTPROVIDER}" == "l echo "The cert seems to be using the old LE root cert, which is no longer valid. Deleting and revoking." REV_ACMESERVER=("https://acme-v02.api.letsencrypt.org/directory") if [[ -f /config/etc/letsencrypt/live/"${ORIGDOMAIN}"/fullchain.pem ]]; then - certbot revoke --non-interactive --cert-path /config/etc/letsencrypt/live/"${ORIGDOMAIN}"/fullchain.pem --server "${REV_ACMESERVER[@]}" || true + certbot revoke --config-dir /config/etc/letsencrypt --logs-dir /config/log/letsencrypt --work-dir /tmp/letsencrypt --config /config/etc/letsencrypt/cli.ini --non-interactive --cert-path /config/etc/letsencrypt/live/"${ORIGDOMAIN}"/fullchain.pem --server "${REV_ACMESERVER[@]}" || true else - certbot revoke --non-interactive --cert-name "${ORIGDOMAIN}" --server "${REV_ACMESERVER[@]}" || true + certbot revoke --config-dir /config/etc/letsencrypt --logs-dir /config/log/letsencrypt --work-dir /tmp/letsencrypt --config /config/etc/letsencrypt/cli.ini --non-interactive --cert-name "${ORIGDOMAIN}" --server "${REV_ACMESERVER[@]}" || true fi rm -rf /config/etc/letsencrypt/{accounts,archive,live,renewal} fi @@ -347,7 +340,7 @@ if [[ ! -f "/config/keys/letsencrypt/fullchain.pem" ]]; then set_ini_value "eab-hmac-key" "${ZEROSSL_EAB_HMAC_KEY}" /config/etc/letsencrypt/cli.ini fi echo "Generating new certificate" - certbot certonly --non-interactive --renew-by-default + certbot certonly --config-dir /config/etc/letsencrypt --logs-dir /config/log/letsencrypt --work-dir /tmp/letsencrypt --config /config/etc/letsencrypt/cli.ini --non-interactive --renew-by-default if [[ ! -d /config/keys/letsencrypt ]]; then if [[ "${VALIDATION}" = "dns" ]]; then echo "ERROR: Cert does not exist! Please see the validation error above. Make sure you entered correct credentials into the ${DNSCREDENTIALFILE} file." diff --git a/root/etc/s6-overlay/s6-rc.d/init-fail2ban-config/run b/root/etc/s6-overlay/s6-rc.d/init-fail2ban-config/run index 968043a..d254a6a 100755 --- a/root/etc/s6-overlay/s6-rc.d/init-fail2ban-config/run +++ b/root/etc/s6-overlay/s6-rc.d/init-fail2ban-config/run @@ -1,38 +1,40 @@ #!/usr/bin/with-contenv bash # shellcheck shell=bash -if ! iptables -L &> /dev/null; then - ln -sf /sbin/xtables-legacy-multi /sbin/iptables - ln -sf /sbin/xtables-legacy-multi /sbin/iptables-save - ln -sf /sbin/xtables-legacy-multi /sbin/iptables-restore - ln -sf /sbin/xtables-legacy-multi /sbin/ip6tables - ln -sf /sbin/xtables-legacy-multi /sbin/ip6tables-save - ln -sf /sbin/xtables-legacy-multi /sbin/ip6tables-restore -fi +if [[ -z ${LSIO_READ_ONLY_FS} ]] && [[ -z ${LSIO_NON_ROOT_USER} ]] && [[ "${DISABLE_F2B,,}" != "true" ]]; then + if ! iptables -L &> /dev/null; then + ln -sf /usr/sbin/xtables-legacy-multi /usr/sbin/iptables + ln -sf /usr/sbin/xtables-legacy-multi /usr/sbin/iptables-save + ln -sf /usr/sbin/xtables-legacy-multi /usr/sbin/iptables-restore + ln -sf /usr/sbin/xtables-legacy-multi /usr/sbin/ip6tables + ln -sf /usr/sbin/xtables-legacy-multi /usr/sbin/ip6tables-save + ln -sf /usr/sbin/xtables-legacy-multi /usr/sbin/ip6tables-restore + fi -# copy/update the fail2ban config defaults to/in /config -cp -R /defaults/fail2ban/filter.d /config/fail2ban/ -cp -R /defaults/fail2ban/action.d /config/fail2ban/ -# if jail.local is missing in /config, copy default -if [[ ! -f /config/fail2ban/jail.local ]]; then - cp /defaults/fail2ban/jail.local /config/fail2ban/jail.local -fi -# Replace fail2ban config with user config -if [[ -d /etc/fail2ban/filter.d ]]; then - rm -rf /etc/fail2ban/filter.d -fi -if [[ -d /etc/fail2ban/action.d ]]; then - rm -rf /etc/fail2ban/action.d -fi -cp -R /config/fail2ban/filter.d /etc/fail2ban/ -cp -R /config/fail2ban/action.d /etc/fail2ban/ -cp /defaults/fail2ban/fail2ban.local /etc/fail2ban/ -cp /config/fail2ban/jail.local /etc/fail2ban/jail.local + # copy/update the fail2ban config defaults to/in /config + cp -R /defaults/fail2ban/filter.d /config/fail2ban/ + cp -R /defaults/fail2ban/action.d /config/fail2ban/ + # if jail.local is missing in /config, copy default + if [[ ! -f /config/fail2ban/jail.local ]]; then + cp /defaults/fail2ban/jail.local /config/fail2ban/jail.local + fi + # Replace fail2ban config with user config + if [[ -d /etc/fail2ban/filter.d ]]; then + rm -rf /etc/fail2ban/filter.d + fi + if [[ -d /etc/fail2ban/action.d ]]; then + rm -rf /etc/fail2ban/action.d + fi + cp -R /config/fail2ban/filter.d /etc/fail2ban/ + cp -R /config/fail2ban/action.d /etc/fail2ban/ + cp /defaults/fail2ban/fail2ban.local /etc/fail2ban/ + cp /config/fail2ban/jail.local /etc/fail2ban/jail.local -# logfiles needed by fail2ban -if [[ ! -f /config/log/nginx/error.log ]]; then - touch /config/log/nginx/error.log -fi -if [[ ! -f /config/log/nginx/access.log ]]; then - touch /config/log/nginx/access.log + # logfiles needed by fail2ban + if [[ ! -f /config/log/nginx/error.log ]]; then + touch /config/log/nginx/error.log + fi + if [[ ! -f /config/log/nginx/access.log ]]; then + touch /config/log/nginx/access.log + fi fi diff --git a/root/etc/s6-overlay/s6-rc.d/init-permissions-config/run b/root/etc/s6-overlay/s6-rc.d/init-permissions-config/run index 6c91e71..4412726 100755 --- a/root/etc/s6-overlay/s6-rc.d/init-permissions-config/run +++ b/root/etc/s6-overlay/s6-rc.d/init-permissions-config/run @@ -4,7 +4,6 @@ # permissions lsiown -R abc:abc \ /config -chmod -R 0644 /etc/logrotate.d chmod -R +r /config/log # Workaround for systems with chmod errors diff --git a/root/etc/s6-overlay/s6-rc.d/init-swag-folders/run b/root/etc/s6-overlay/s6-rc.d/init-swag-folders/run index c18da5b..9de179d 100755 --- a/root/etc/s6-overlay/s6-rc.d/init-swag-folders/run +++ b/root/etc/s6-overlay/s6-rc.d/init-swag-folders/run @@ -7,6 +7,6 @@ mkdir -p \ /config/etc/letsencrypt/renewal-hooks \ /config/log/{fail2ban,letsencrypt,nginx} \ /config/nginx/proxy-confs \ - /run/fail2ban -rm -rf /etc/letsencrypt -ln -s /config/etc/letsencrypt /etc/letsencrypt + /run/fail2ban \ + /tmp/letsencrypt + diff --git a/root/etc/s6-overlay/s6-rc.d/svc-fail2ban/run b/root/etc/s6-overlay/s6-rc.d/svc-fail2ban/run index a06f3d0..f1d1b0f 100755 --- a/root/etc/s6-overlay/s6-rc.d/svc-fail2ban/run +++ b/root/etc/s6-overlay/s6-rc.d/svc-fail2ban/run @@ -1,5 +1,9 @@ #!/usr/bin/with-contenv bash # shellcheck shell=bash -exec \ - fail2ban-client -x -f start +if [[ -z ${LSIO_READ_ONLY_FS} ]] && [[ -z ${LSIO_NON_ROOT_USER} ]] && [[ "${DISABLE_F2B,,}" != "true" ]]; then + exec \ + fail2ban-client -x -f start +else + sleep infinity +fi