Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTML volume required when using DNS challenge only #1159

Closed
shawb5 opened this issue Oct 26, 2024 · 2 comments · Fixed by #1160
Closed

HTML volume required when using DNS challenge only #1159

shawb5 opened this issue Oct 26, 2024 · 2 comments · Fixed by #1160
Labels
kind/bug Issue reporting a bug

Comments

@shawb5
Copy link

shawb5 commented Oct 26, 2024

Bug description

In contrevention to the documentation, acme-companion requires html volume to be mounted even if only DNS-01 challenge is used. Creating the html volume and mount resolves the issue.

acme-companion image version

Info: running acme-companion version v2.5.0

nginx-proxy's Docker configuration

services:
  nginx-proxy:
    image: 'nginxproxy/nginx-proxy:1.6'
    container_name: nginx-proxy
    network_mode: default_bridge
    ports:
      - "80:80"
      - "443:443"
    environment:
      ACME_HTTP_CHALLENGE_LOCATION: "false"
    volumes:
      - '/var/run/docker.sock:/tmp/docker.sock:ro'
      - 'certs:/etc/nginx/certs'
    restart: always

  acme-companion:
    image: 'nginxproxy/acme-companion:2.5'
    container_name: acme-companion
    network_mode: default_bridge
    volumes_from: 
      - 'nginx-proxy'
    volumes:
      - 'acme:/etc/acme.sh'
      - '/var/run/docker.sock:/var/run/docker.sock:ro'
    environment:
      DEFAULT_EMAIL: '[email protected]'
      ACME_CHALLENGE: DNS-01
      ACMESH_DNS_API_CONFIG: |-
        DNS_API: dns_cf
        CF_Token: 'DNS edit token'
        CF_Email: '[email protected]'
        CF_Account_ID: 'Cloudflare account ID'
    restart: always

  whoami:
    image: jwilder/whoami
    container_name: whoami
    network_mode: default_bridge
    expose:
      - "8000"
    environment:
      - VIRTUAL_HOST=subdomain.domain.tld
      - LETSENCRYPT_HOST=subdomain.domain.tld
      - VIRTUAL_PORT=8000

volumes:
    certs:
    acme:

rendered nginx configuration

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# configuration file /etc/nginx/nginx.conf:

user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


events {
    worker_connections  10240;
}
worker_rlimit_nofile 20480;


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;
}

include /etc/nginx/toplevel.conf.d/*.conf;

# configuration file /etc/nginx/mime.types:

types {
    text/html                                        html htm shtml;
    text/css                                         css;
    text/xml                                         xml;
    image/gif                                        gif;
    image/jpeg                                       jpeg jpg;
    application/javascript                           js;
    application/atom+xml                             atom;
    application/rss+xml                              rss;

    text/mathml                                      mml;
    text/plain                                       txt;
    text/vnd.sun.j2me.app-descriptor                 jad;
    text/vnd.wap.wml                                 wml;
    text/x-component                                 htc;

    image/avif                                       avif;
    image/png                                        png;
    image/svg+xml                                    svg svgz;
    image/tiff                                       tif tiff;
    image/vnd.wap.wbmp                               wbmp;
    image/webp                                       webp;
    image/x-icon                                     ico;
    image/x-jng                                      jng;
    image/x-ms-bmp                                   bmp;

    font/woff                                        woff;
    font/woff2                                       woff2;

    application/java-archive                         jar war ear;
    application/json                                 json;
    application/mac-binhex40                         hqx;
    application/msword                               doc;
    application/pdf                                  pdf;
    application/postscript                           ps eps ai;
    application/rtf                                  rtf;
    application/vnd.apple.mpegurl                    m3u8;
    application/vnd.google-earth.kml+xml             kml;
    application/vnd.google-earth.kmz                 kmz;
    application/vnd.ms-excel                         xls;
    application/vnd.ms-fontobject                    eot;
    application/vnd.ms-powerpoint                    ppt;
    application/vnd.oasis.opendocument.graphics      odg;
    application/vnd.oasis.opendocument.presentation  odp;
    application/vnd.oasis.opendocument.spreadsheet   ods;
    application/vnd.oasis.opendocument.text          odt;
    application/vnd.openxmlformats-officedocument.presentationml.presentation
                                                     pptx;
    application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
                                                     xlsx;
    application/vnd.openxmlformats-officedocument.wordprocessingml.document
                                                     docx;
    application/vnd.wap.wmlc                         wmlc;
    application/wasm                                 wasm;
    application/x-7z-compressed                      7z;
    application/x-cocoa                              cco;
    application/x-java-archive-diff                  jardiff;
    application/x-java-jnlp-file                     jnlp;
    application/x-makeself                           run;
    application/x-perl                               pl pm;
    application/x-pilot                              prc pdb;
    application/x-rar-compressed                     rar;
    application/x-redhat-package-manager             rpm;
    application/x-sea                                sea;
    application/x-shockwave-flash                    swf;
    application/x-stuffit                            sit;
    application/x-tcl                                tcl tk;
    application/x-x509-ca-cert                       der pem crt;
    application/x-xpinstall                          xpi;
    application/xhtml+xml                            xhtml;
    application/xspf+xml                             xspf;
    application/zip                                  zip;

    application/octet-stream                         bin exe dll;
    application/octet-stream                         deb;
    application/octet-stream                         dmg;
    application/octet-stream                         iso img;
    application/octet-stream                         msi msp msm;

    audio/midi                                       mid midi kar;
    audio/mpeg                                       mp3;
    audio/ogg                                        ogg;
    audio/x-m4a                                      m4a;
    audio/x-realaudio                                ra;

    video/3gpp                                       3gpp 3gp;
    video/mp2t                                       ts;
    video/mp4                                        mp4;
    video/mpeg                                       mpeg mpg;
    video/quicktime                                  mov;
    video/webm                                       webm;
    video/x-flv                                      flv;
    video/x-m4v                                      m4v;
    video/x-mng                                      mng;
    video/x-ms-asf                                   asx asf;
    video/x-ms-wmv                                   wmv;
    video/x-msvideo                                  avi;
}

# configuration file /etc/nginx/conf.d/default.conf:
# nginx-proxy version : 1.6.3
# Networks available to the container running docker-gen (which are assumed to
# match the networks available to the container running nginx):
#     default_bridge
# If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the
# scheme used to connect to this server
map $http_x_forwarded_proto $proxy_x_forwarded_proto {
    default $scheme;
    '' $scheme;
}
map $http_x_forwarded_host $proxy_x_forwarded_host {
    default $host;
    '' $host;
}
# If we receive X-Forwarded-Port, pass it through; otherwise, pass along the
# server port the client connected to
map $http_x_forwarded_port $proxy_x_forwarded_port {
    default $server_port;
    '' $server_port;
}
# Include the port in the Host header sent to the container if it is non-standard
map $server_port $host_port {
    default :$server_port;
    80 '';
    443 '';
}
# If the request from the downstream client has an "Upgrade:" header (set to any
# non-empty value), pass "Connection: upgrade" to the upstream (backend) server.
# Otherwise, the value for the "Connection" header depends on whether the user
# has enabled keepalive to the upstream server.
map $http_upgrade $proxy_connection {
    default upgrade;
    '' $proxy_connection_noupgrade;
}
map $upstream_keepalive $proxy_connection_noupgrade {
    # Preserve nginx's default behavior (send "Connection: close").
    default close;
    # Use an empty string to cancel nginx's default behavior.
    true '';
}
# Abuse the map directive (see <https://stackoverflow.com/q/14433309>) to ensure
# that $upstream_keepalive is always defined.  This is necessary because:
#   - The $proxy_connection variable is indirectly derived from
#     $upstream_keepalive, so $upstream_keepalive must be defined whenever
#     $proxy_connection is resolved.
#   - The $proxy_connection variable is used in a proxy_set_header directive in
#     the http block, so it is always fully resolved for every request -- even
#     those where proxy_pass is not used (e.g., unknown virtual host).
map "" $upstream_keepalive {
    # The value here should not matter because it should always be overridden in
    # a location block (see the "location" template) for all requests where the
    # value actually matters.
    default false;
}
# Apply fix for very long server names
server_names_hash_bucket_size 128;
# Default dhparam
ssl_dhparam /etc/nginx/dhparam/dhparam.pem;
# Set appropriate X-Forwarded-Ssl header based on $proxy_x_forwarded_proto
map $proxy_x_forwarded_proto $proxy_x_forwarded_ssl {
    default off;
    https on;
}
gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
log_format vhost escape=default '$host $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$upstream_addr"';
access_log off;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305';
    ssl_prefer_server_ciphers off;
error_log /dev/stderr;
# HTTP 1.1 support
proxy_http_version 1.1;
proxy_set_header Host $host$host_port;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $proxy_connection;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $proxy_x_forwarded_host;
proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto;
proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl;
proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port;
proxy_set_header X-Original-URI $request_uri;
# Mitigate httpoxy attack (see README for details)
proxy_set_header Proxy "";
server {
    server_name _; # This is just an invalid value which will never trigger on a real hostname.
    server_tokens off;
    access_log /var/log/nginx/access.log vhost;
    http2 on;
    listen 80;
    listen 443 ssl;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;
    ssl_certificate /etc/nginx/certs/default.crt;
    ssl_certificate_key /etc/nginx/certs/default.key;
    location ^~ / {
        return 503;
    }
}
# portainer.domain.tld/
upstream portainer.domain.tld {
    # Container: portainer
    #     networks:
    #         default_bridge (reachable)
    #     IP address: 172.18.0.2
    #     exposed ports (first ten): 8000/tcp 9000/tcp 9443/tcp
    #     default port: 80
    #     using port: 9000
    server 172.18.0.2:9000;
}
server {
    server_name portainer.domain.tld;
    access_log /var/log/nginx/access.log vhost;
    listen 80 ;
    location / {
        return 301 https://$host$request_uri;
    }
}
server {
    server_name portainer.domain.tld;
    access_log /var/log/nginx/access.log vhost;
    http2 on;
    listen 443 ssl ;
    ssl_session_timeout 5m;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;
    ssl_certificate /etc/nginx/certs/portainer.domain.tld.crt;
    ssl_certificate_key /etc/nginx/certs/portainer.domain.tld.key;
    ssl_dhparam /etc/nginx/certs/portainer.domain.tld.dhparam.pem;
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/nginx/certs/portainer.domain.tld.chain.pem;
    set $sts_header "";
    if ($https) {
        set $sts_header "max-age=31536000";
    }
    add_header Strict-Transport-Security $sts_header always;
    location / {
        proxy_pass http://portainer.domain.tld;
        set $upstream_keepalive false;
    }
}
# whoami.domain.tld/
upstream whoami.domain.tld {
    # Container: whoami
    #     networks:
    #         default_bridge (reachable)
    #     IP address: 172.18.0.3
    #     exposed ports (first ten): 8000/tcp
    #     default port: 8000
    #     using port: 8000
    server 172.18.0.3:8000;
}
server {
    server_name whoami.domain.tld;
    access_log /var/log/nginx/access.log vhost;
    listen 80 ;
    location / {
        return 301 https://$host$request_uri;
    }
}
server {
    server_name whoami.domain.tld;
    access_log /var/log/nginx/access.log vhost;
    http2 on;
    listen 443 ssl ;
    ssl_session_timeout 5m;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;
    ssl_certificate /etc/nginx/certs/whoami.domain.tld.crt;
    ssl_certificate_key /etc/nginx/certs/whoami.domain.tld.key;
    ssl_dhparam /etc/nginx/certs/whoami.domain.tld.dhparam.pem;
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/nginx/certs/whoami.domain.tld.chain.pem;
    set $sts_header "";
    if ($https) {
        set $sts_header "max-age=31536000";
    }
    add_header Strict-Transport-Security $sts_header always;
    location / {
        proxy_pass http://whoami.domain.tld;
        set $upstream_keepalive false;
    }
}

nginx: configuration file /etc/nginx/nginx.conf test is successful

Containers logs

acme-companion

Info: running acme-companion version v2.5.0
Warning: '/usr/share/nginx/html' does not appear to be a mounted volume.
Error: can't access to '/usr/share/nginx/html' directory !
Check that '/usr/share/nginx/html' directory is declared as a writable volume.
Info: running acme-companion version v2.5.0
Warning: '/usr/share/nginx/html' does not appear to be a mounted volume.
Error: can't access to '/usr/share/nginx/html' directory !
Check that '/usr/share/nginx/html' directory is declared as a writable volume.
Info: running acme-companion version v2.5.0
Warning: '/usr/share/nginx/html' does not appear to be a mounted volume.
Error: can't access to '/usr/share/nginx/html' directory !
Check that '/usr/share/nginx/html' directory is declared as a writable volume.
Info: running acme-companion version v2.5.0
Warning: '/usr/share/nginx/html' does not appear to be a mounted volume.
Error: can't access to '/usr/share/nginx/html' directory !
Check that '/usr/share/nginx/html' directory is declared as a writable volume.
Info: running acme-companion version v2.5.0
Warning: '/usr/share/nginx/html' does not appear to be a mounted volume.
Error: can't access to '/usr/share/nginx/html' directory !
Check that '/usr/share/nginx/html' directory is declared as a writable volume.
Info: running acme-companion version v2.5.0
Warning: '/usr/share/nginx/html' does not appear to be a mounted volume.
Error: can't access to '/usr/share/nginx/html' directory !
Check that '/usr/share/nginx/html' directory is declared as a writable volume.
Info: running acme-companion version v2.5.0
Warning: '/usr/share/nginx/html' does not appear to be a mounted volume.
Error: can't access to '/usr/share/nginx/html' directory !
Check that '/usr/share/nginx/html' directory is declared as a writable volume.
Info: running acme-companion version v2.5.0
Warning: '/usr/share/nginx/html' does not appear to be a mounted volume.
Error: can't access to '/usr/share/nginx/html' directory !
Check that '/usr/share/nginx/html' directory is declared as a writable volume.
Info: running acme-companion version v2.5.0
Warning: '/usr/share/nginx/html' does not appear to be a mounted volume.
Error: can't access to '/usr/share/nginx/html' directory !
Check that '/usr/share/nginx/html' directory is declared as a writable volume.

nginx-proxy

dockergen.1 | 2024/10/26 13:57:58 Received event start for container 497f061f0361
dockergen.1 | 2024/10/26 13:57:58 Contents of /etc/nginx/conf.d/default.conf did not change. Skipping notification 'nginx -s reload'
dockergen.1 | 2024/10/26 13:57:59 Received event die for container 497f061f0361
dockergen.1 | 2024/10/26 13:57:59 Received event start for container 497f061f0361
dockergen.1 | 2024/10/26 13:57:59 Contents of /etc/nginx/conf.d/default.conf did not change. Skipping notification 'nginx -s reload'
dockergen.1 | 2024/10/26 13:57:59 Contents of /etc/nginx/conf.d/default.conf did not change. Skipping notification 'nginx -s reload'
dockergen.1 | 2024/10/26 13:57:59 Received event die for container 497f061f0361
dockergen.1 | 2024/10/26 13:57:59 Contents of /etc/nginx/conf.d/default.conf did not change. Skipping notification 'nginx -s reload'
dockergen.1 | 2024/10/26 13:57:59 Received event start for container 497f061f0361
dockergen.1 | 2024/10/26 13:57:59 Contents of /etc/nginx/conf.d/default.conf did not change. Skipping notification 'nginx -s reload'
dockergen.1 | 2024/10/26 13:58:00 Received event die for container 497f061f0361
dockergen.1 | 2024/10/26 13:58:00 Contents of /etc/nginx/conf.d/default.conf did not change. Skipping notification 'nginx -s reload'
dockergen.1 | 2024/10/26 13:58:00 Received event start for container 497f061f0361
dockergen.1 | 2024/10/26 13:58:00 Contents of /etc/nginx/conf.d/default.conf did not change. Skipping notification 'nginx -s reload'
dockergen.1 | 2024/10/26 13:58:00 Received event die for container 497f061f0361
dockergen.1 | 2024/10/26 13:58:00 Contents of /etc/nginx/conf.d/default.conf did not change. Skipping notification 'nginx -s reload'
dockergen.1 | 2024/10/26 13:58:01 Received event start for container 497f061f0361
dockergen.1 | 2024/10/26 13:58:01 Contents of /etc/nginx/conf.d/default.conf did not change. Skipping notification 'nginx -s reload'
dockergen.1 | 2024/10/26 13:58:02 Received event die for container 497f061f0361
dockergen.1 | 2024/10/26 13:58:02 Contents of /etc/nginx/conf.d/default.conf did not change. Skipping notification 'nginx -s reload'
dockergen.1 | 2024/10/26 13:58:03 Received event start for container 497f061f0361
dockergen.1 | 2024/10/26 13:58:03 Contents of /etc/nginx/conf.d/default.conf did not change. Skipping notification 'nginx -s reload'
dockergen.1 | 2024/10/26 13:58:04 Received event die for container 497f061f0361
dockergen.1 | 2024/10/26 13:58:04 Contents of /etc/nginx/conf.d/default.conf did not change. Skipping notification 'nginx -s reload'
dockergen.1 | 2024/10/26 13:58:07 Received event start for container 497f061f0361
dockergen.1 | 2024/10/26 13:58:07 Contents of /etc/nginx/conf.d/default.conf did not change. Skipping notification 'nginx -s reload'
dockergen.1 | 2024/10/26 13:58:07 Received event die for container 497f061f0361
dockergen.1 | 2024/10/26 13:58:07 Contents of /etc/nginx/conf.d/default.conf did not change. Skipping notification 'nginx -s reload'
dockergen.1 | 2024/10/26 13:58:14 Received event start for container 497f061f0361
dockergen.1 | 2024/10/26 13:58:14 Contents of /etc/nginx/conf.d/default.conf did not change. Skipping notification 'nginx -s reload'
dockergen.1 | 2024/10/26 13:58:14 Received event die for container 497f061f0361
dockergen.1 | 2024/10/26 13:58:14 Contents of /etc/nginx/conf.d/default.conf did not change. Skipping notification 'nginx -s reload'

Docker host

  • OS: Rocky Linux 9.4 minimal
  • Docker version: Docker Engine 27.3.1
@buchdag buchdag added the kind/bug Issue reporting a bug label Oct 27, 2024
@buchdag
Copy link
Member

buchdag commented Oct 27, 2024

@shawb5 good catch, thank you.

Those check are in the entrypoint, and we have no easy way to know at the time when they're being made if the HTTP-01 challenge is going to be used or not. I think I'll lower the check to just print a warning.

diff --git a/app/entrypoint.sh b/app/entrypoint.sh
index 00f1ef2..5d9bab7 100755
--- a/app/entrypoint.sh
+++ b/app/entrypoint.sh
@@ -27,7 +27,7 @@ function check_docker_socket {
     fi
 }
 
-function check_writable_directory {
+function check_dir_is_mounted_volume {
     local dir="$1"
     if [[ $(get_self_cid) ]]; then
         if ! docker_api "/containers/$(get_self_cid)/json" | jq ".Mounts[].Destination" | grep -q "^\"$dir\"$"; then
@@ -36,6 +36,13 @@ function check_writable_directory {
     else
         echo "Warning: can't check if '$dir' is a mounted volume without self container ID."
     fi
+}
+
+function check_writable_directory {
+    local dir="$1"
+
+    check_dir_is_mounted_volume "$dir"
+
     if [[ ! -d "$dir" ]]; then
         echo "Error: can't access to '$dir' directory !" >&2
         echo "Check that '$dir' directory is declared as a writable volume." >&2
@@ -49,6 +56,18 @@ function check_writable_directory {
     rm -f "$dir/.check_writable"
 }
 
+function warn_html_directory {
+    local dir='/usr/share/nginx/html'
+    
+    check_dir_is_mounted_volume "$dir"
+
+    if [[ ! -d "$dir" ]] || ! touch "$dir/.check_writable" 2>/dev/null; then
+        echo "Warning: can't access or write to '$dir' directory. This will prevent HTML-01 challenges from working correctly."
+        echo "If you are only using DNS-01 challenges, you can ignore this warning, otherwise check that '$dir' is declared as a writable volume."
+    fi
+    rm -f "$dir/.check_writable"
+}
+
 function check_dh_group {
 	# DH params will be supplied for acme-companion here:
 	local DHPARAM_FILE='/etc/nginx/certs/dhparam.pem'
@@ -176,7 +195,7 @@ if [[ "$*" == "/bin/bash /app/start.sh" ]]; then
     check_writable_directory '/etc/nginx/certs'
     parse_true "${ACME_HTTP_CHALLENGE_LOCATION:=false}" && check_writable_directory '/etc/nginx/vhost.d'
     check_writable_directory '/etc/acme.sh'
-    check_writable_directory '/usr/share/nginx/html'
+    warn_html_directory
     if [[ -f /app/letsencrypt_user_data ]]; then
         check_writable_directory '/etc/nginx/vhost.d'
         check_writable_directory '/etc/nginx/conf.d'

Could you give the nginxproxy/acme-companion:1159 image a try before I open the PR ?

@shawb5
Copy link
Author

shawb5 commented Oct 27, 2024

@buchdag Thanks, this appears to be working now without the html volume

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/bug Issue reporting a bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants