Nginx and Certbot with Docker

I have this repository that will basically automatically create SSL certificates for your domains using Nginx and Certbot to handler this.

The main script (project/ssl.sh inside repository) basically:

  1. Create temporary certificates to be able to up the Nginx container:
docker compose run --rm --entrypoint " \
	openssl req -x509 -nodes -newkey rsa:$RSA_KEY_SIZE -days 1 \
		-keyout '$DOMAIN_PATH/privkey.pem' \
		-out '$DOMAIN_PATH/fullchain.pem' \
		-subj '/CN=#!COMMONNAME!#' \
" certbot
  1. Start the Nginx container:
docker compose up -d --build --force-recreate nginx
  1. Remove the temporary certificate folders:
rm -rf "$CERTBOT_PATH/live/$domain"
rm -rf "$CERTBOT_PATH/archive/$domain"
rm -rf "$CERTBOT_PATH/renewal/$domain.conf"
  1. Now with Nginx up and running, request Let's Encrypt certificates:
docker compose run --rm --entrypoint " \
	certbot certonly --webroot -w /var/www/certbot \
		--rsa-key-size $RSA_KEY_SIZE \
		--agree-tos \
		--force-renewal \
		$DOMAIN_ARGS \
		${IS_STAGING:+--staging} \
		`[ "$EMAIL" ] && echo "--email $EMAIL" || echo '--register-unsafely-without-email'` \
" certbot

However, step 4 of this walkthrough fails and Let's Encrypt is unable to recover the challenge file.

After a LONG time trying to understand and debug the problem I understood that what is happening is: although Nginx can see the certificate files (that's why the container is up) it cannot read them... what it does is return permission error (as the certificates are from root):

[error] 20#20: *73 cannot load certificate key "/etc/letsencrypt/live/domain.xyz/privkey.pem": BIO_new_file() failed (SSL: error:8000000D:system library::Permission denied:calling fopen(/etc/letsencrypt/live/domain.xyz/privkey.pem, r) error:10080002:BIO routines::system lib) while SSL handshaking, client: 123.456.789.010, server: 0.0.0.0:443

From what I understand, this occurs because the certificates generated in step 1 and step 4 are generated by certbot which runs from root.

In fact, if I log into the container that is running Nginx, as I am normally root, in addition to seeing, I can read the contents of the certificates, however, when I log in with the nginx user, I see but do not read the files.

Someone have some ideia to help me?


PS: Guys, I really don't know if this is the right forum to ask, so if it's not, I'm sorry!

PPS: My project is inspired by several forums but mainly from this tutorial.

Looks like your ssl.sh is a ripoff of init-letsencrypt.sh which has a terrible design.

Please delete your ssl.sh, forget about it and rebuild it from the bottom up with a better design.

[edit]Ghe, looking at your Medium.com link I was correct. I recognise that piece of )(()#$ anywhere.. Which is not meant as an offence to you personally, as you simply have used that script without knowing better.

1 Like

I feel like we don't have enough info to diagnose this.

Also, that's a pretty messy way to set up nginx and certbot with docker. It can work but I wouldn't do that.

I think nginx starts because it can read the snakeoil certificates you generate, but something is interfering with the challenge, and you never get an actual Let's Encrypt certificate. We need more info on that.

2 Likes

I really can't understand the reason for your statements, could you explain them?

The design of init-letsencrypt.sh is simply flawed. It often deletes perfectly fine certificates issued by Let's Encrypt. It uses --force-renewal which can lead to rate limits. We've seen nothing but trouble by that script on this Community, but that's biassed of course :slight_smile:

1 Like

Could you suggest something better to do this setup?

As I mentioned in my explanation, in fact if I log into the container that Nginx runs (which by default I log in as user root) I can see and read the file, however if I specify the user nginx I can see but not read the file. So much so that if I stop the script right after step 2 and before step 3, when Nginx loads, if I try to make a request like curl -iL domain.xyz I get the return: curl: (35) OpenSSL /3.1.5: error:0A000438:SSL routines::tlsv1 alert internal error

Regarding not being able to fulfill the challenge (step 4), from what I analyzed, the logic is the same... As the files that certbot generates are owned by the root group, Nginx cannot access them, as the Let's Encrypt cannot recover them.

Does this make sense or am I talking nonsense?

PS: What extra information could I provide?

As for the --force-renewal issue, wouldn't simply removing it resolve the rate limits issue?

And why and under what conditions does it exclude good certificates?

try with curl -k

1 Like

We often saw that users had some problem with their setup, which was unrelated to the actual issuance of the certificate. E.g. problems with nginx. But the script overwrites perfectly fine certificates when it's ran a second time, which the user would often do. And a third time. And fourth. And fifth. Et voila, hitting the duplicate rate limit.

And with regard to the --force-renewal: it's a sign the script writer doesn't know what he/she's doing. Because there's nothing to force anyway, as the "dummy" (or perfectly fine certificates) were deleted earlier anyway!

Also, the script is outdated, as Certbot currently defaults to ECDSA instead of RSA.. It hasn't been updated for 4 years. Although I would recommend the original writer not to update it, but to delete it from the face of the Earth entirely.

2 Likes

Same return :confused:

I understand all the points you said but... All these things are adjustable within the script... So I don't understand why you say to "delete it from the face of the Earth entirely". It seems more personal than actually constructive.

The problem is there's no active development on a script which is often causing trouble. Thus it's better to not use it IMO, but as the Github repo isn't active, users can't be warned somehow either. It's just not a good situation I'd say.

2 Likes

It seems that you forget the detail that I said that this script was an inspiration for me, that is, I am not using the author's original script, because although the current flow is similar, I have already applied my own changes.

So in fact all the things you said that can't happen, in fact, can, due to the fact that I'm NOT just copying and pasting the original script, but like I said, making my own version... Which is even open for suggestions.

Personally: I agree with his statement.
There is very very little redeeming value in that script [and this ripped-off/sliced version of it].
The world would be better off without them.

2 Likes

There's some other issue, then.

I would read this: Should Caddy and Traefik Replace Certbot? | Electronic Frontier Foundation

4 Likes

I certainly have no problem receiving constructive criticism, so much so that I said above that my modified script is open for improvements. But what really bothers me is that most of those who are more advanced in the subject, forget that the vast majority of everyone who is asking for help are new to the subject and that that script that they say is horrible, was the only solution we found. and it seemed to work.

It's okay to speak politely and friendly about the negative points of something as long as you also talk about how to improve, however, what I see is everyone criticizing but I don't see anyone writing a better script or giving tips on how we could do it differently .

I certainly can't defend those who are simply running random commands without understanding what they're doing and not even caring about it... But I, and many others, really want to understand what's going on and not only that, but the reason too.

PS: An example of this is that after @Osiris words, I modified the script and removed the --force-renewal flag because from what he explained, it really didn't make sense for it to be there and I changed the type of the **RSA algorithms ** for ECDSA in both step 1 and step 4.

To be frank, I don't work with Docker that much so I'm probably not the right person to improve Docker-ish scripts.

Also accept my apologies if my remarks regarding init-letsencrypt.sh were offending you. That was not my intention. I'm just frustrated that script keeps turning up time after time.

Okay I understand your frustration, I appreciate the apology and I'm glad you realized that I may have been a little rude... And despite having said that you don't work much with Docker, in your previous explanations you have already managed to contribute to I can improve the script, thanks!

Once again, I appreciate the apology!

1 Like

@Osiris I know you said you don't work much with Docker but you are certainly a very knowledgeable user and I wanted to ask that if you have in mind a more intelligent flow of how you imagine it could be good, don't hesitate to share, I would be very grateful.

Who knows, we might be able to create something good and new!

Then you've been looking in all the wrong places.
We would be glad to point you in much better directions.

That is a step in the right direction.
The part where it completely removes all the subfolders is atrocious:

It removes far more then just the:

It literally removes it completely [taking whatever working certs may already be there with it].

2 Likes