nginxdocker-composehttps

Nginx and docker error: Cannot load certificate no such file


I'm trying to convert my HTTP web application to use HTTPS. I've used this site to request a cert using the certbot linux snap. I opened port 80 on my router to forward to my home server on port 80.

>sudo certbot certonly --standalone -d footeware.ca
Certificate is saved at: /etc/letsencrypt/live/footeware.ca/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/footeware.ca/privkey.pem
This certificate expires on 2025-01-18.

That seemed to work so I'd like to let the certbot snap automatically take care of refreshing the cert. I think it's a cron job that activates the snap to use port 80 to update certs in the above folder.

My angular app sits in a docker container with nginx. Now I need to make the cert available to that nginx and have it listen on 443.

# nginx.conf

http {
    server {
        listen 443 default_server ssl http2;
        listen [::]:443 ssl http2;
        server_name footeware.ca;
        ssl_certificate /etc/letsencrypt/live/footeware.ca/fullchain.pem;
        ssl_certificate_key  /etc/letsencrypt/live/footeware.ca/privkey.pem;
        location / {
            EDIT # proxy_pass http://footeware.ca; # would forward to http & error
            try_files $uri $uri/ =404;
        }
    }
}
# dockerfile

FROM nginx:latest
# use my conf
COPY ./nginx.conf /etc/nginx/nginx.conf
# copy angular app to container
COPY ./dist/ca.footeware.ng.web/browser /usr/share/nginx/html/browser
# not sure this is needed
RUN mkdir -p /etc/letsencrypt/live/footeware.ca
EXPOSE 443

I'm using docker-compose to start the image as a container:

# compose.yaml

services:
  ng_web:
    image: craigfoote/ng.web:latest
    container_name: ng.web
    restart: unless-stopped
    ports:
      - 443:443
    volumes:
      - /etc/letsencrypt/live/footeware.ca:/etc/letsencrypt/live/footeware.ca

But docker-compose up yields the following error repeatedly as the ng.web container fails to stay up:

Cannot load certificate "/etc/letsencrypt/live/footeware.ca/fullchain.pem" - no such file

Because the container fails to start, I can't attach to it and check anything. I'm stuck.

Update

I edited the nginx.conf after realizing it was a proxy type directive where it was forwarding to another http server. I changed:

location / {
            # proxy_pass http://footeware.ca; # would forward to http & error
            try_files $uri $uri/ =404;
        }

But I'm getting the same error:

2024/10/21 14:32:38 [emerg] 1#1: cannot load certificate "/etc/letsencrypt/live/footeware.ca/fullchain.pem": BIO_new_file() failed (SSL: error:80000002:system library::No such file or directory:calling fopen(/etc/letsencrypt/live/footeware.ca/fullchain.pem, r) error:10000080:BIO routines::no such file)
ng.web          | nginx: [emerg] cannot load certificate "/etc/letsencrypt/live/footeware.ca/fullchain.pem": BIO_new_file() failed (SSL: error:80000002:system library::No such file or directory:calling fopen(/etc/letsencrypt/live/footeware.ca/fullchain.pem, r) error:10000080:BIO routines::no such file)

I deleted and re-requested the cert using CertBot. It again indicated success. I can't list the files within /etc/letsencrypt but I think that's correct:

craig@server:/etc/letsencrypt$ ls -Ahl
total 20K
drwx------ 4 root root 4.0K Oct 20 19:38 accounts
drwx------ 3 root root 4.0K Oct 20 16:37 archive
drwx------ 3 root root 4.0K Oct 20 16:37 live
drwxr-xr-x 2 root root 4.0K Oct 20 16:37 renewal
drwxr-xr-x 5 root root 4.0K Oct 20 16:37 renewal-hooks
craig@server:/etc/letsencrypt$ tree
.  [error opening dir]

0 directories, 1 file

I still don't know why nginx can't see my cert but I guess it has to do with permissions. 🤔


Solution

  • Got it working (mostly)! The certificate could not be seen using the volume specified in the compose.yaml:

    # compose.yaml
    
    services:
      ng_web:
        image: craigfoote/ng.web:latest
        container_name: ng.web
        restart: unless-stopped
        ports:
          - 443:443
        volumes:
          - /etc/letsencrypt/live/footeware.ca:/etc/letsencrypt/live/footeware.ca
    
    

    I changed it to:

    # compose.yaml
    
    services:
     ng_web:
        image: craigfoote/ng.web:latest
        container_name: ng.web
        restart: unless-stopped
        ports:
          - 443:443
        depends_on:
          - rest_galleries
        volumes:
          - /etc/letsencrypt/live/footeware.ca/fullchain.pem:/etc/letsencrypt/live/footeware.ca/fullchain.pem
          - /etc/letsencrypt/live/footeware.ca/privkey.pem:/etc/letsencrypt/live/footeware.ca/privkey.pem
    
    

    There's now two volumes, one each for the certificate and key.

    Along the way I made some changes to the nginx.conf too:

    events {
    }
    
    http {
        server {
            listen 80;
            server_name footeware.ca www.footeware.ca;
            return 301 https://$host$request_uri;
        }
    
        server {
            listen 443 ssl;
            server_name footeware.ca www.footeware.ca;
    
            ssl_certificate /etc/letsencrypt/live/footeware.ca/fullchain.pem;
            ssl_certificate_key  /etc/letsencrypt/live/footeware.ca/privkey.pem;
            
            ssl_protocols TLSv1.2 TLSv1.3;
            ssl_ciphers HIGH:!aNULL:!MD5;
    
            include mime.types;
            gzip_static on;
    
            location / {
                root /usr/share/nginx/html/browser;
                index index.html;
                try_files $uri $uri/ =404;
            }
        }
    }
    
    

    That got most of the site working but I've now got some mixed content, requests to other http services and images and the like. Their http scheme gets changed to https somehow and fails to resolve. Now to make all of them https...