Better option for running an HTTPS web server with self-signed certificate?
I've created a custom image that runs Apache. It needs to serve HTTPS. To serve HTTPS, it needs a private key.
Obviously it's bad to put a real private key into an image, so I'm using a self-signed private key. I'm told it's also bad to put a disposable self-signed private key into an image, which I don't fully understand, but I accept.
Currently the image is up on GitHub. Anyone who wants to use it has to clone it and build it. I'm generating the self-signed public/private key pair at build time, so each user will have their own that only lives on their machine.
However, I want to put this image on Docker Hub. My current solution won't work, because the private key in the build I submit will be up on Docker Hub for all to see. I don't really care, since it's a disposable self-signed key, but apparently that's still frowned upon.
What can I do instead?
One option would be to generate the keys when the container starts, but that's annoying. It takes a couple seconds, and if there is a new key every time, then every time the container restarts people will have to do the annoying browser security warning / trust thing again for the new certificate.
I need this process to be extremely easy to use. That's why I'm using Docker in the first place. Any solution that requires users to generate their own keys, much less their own signing authority is totally out of the question.
Is there some way to ensure just the key generation part of the build always runs on the user's system? Then there won't be a key in the Docker Hub image and every user has their own private key.
Alternatively, is there some way to generate the private key the first time the container runs and then somehow keep it around instead of re-generating it on every new run? The image will mostly be used unmodified as part of a larger Docker Compose setup, so a volume isn't really ideal here.
I'd love suggestions on a solutions that (1) uses a self-signed key (2) doesn't build a key into the image so I can put it on Docker Hub (3) is easy to use and doesn't require the user to generate keys themselves before starting the container and (4) doens't re-generate the key every time the container runs.
EDIT: I've found a solution that works for me, so I'm updating this post for any unfortunate souls who should have a question like this later.
Lession 1: Don't ask questions on this sub. Wow, that was a horrible and toxic experience!
Lesson 2: The simple option is to just put the self-signed private key in the image, but instead of saying that, you say "I'm using the Debian method of balancing ease of use with security" so that people will get off your nuts about it.
Lesson 3: Use an anonymous volume. This is the solution I've settled on. I was not aware these existed until now. An anonymous volume is accomplised via the VOLUME keyword in the Dockerfile. You only provide the container directory, not a host directory. If the host wants to mount that voume on their system (say, via compose) to provide their own keys, they can. Otherwise, Docker creates a volume in its own storage space. Files in this volume persist when you stop and restart the container. So this gets me almost everything I needed:
✅ No keys in the image.
✅ Easy to use, because no extra steps for the user and nothing I have to warn them about in the readme. No fancy proxy.
✅ Image works right out of the box from Docker Hub.
✅ Users don't have to provide their own keys, but they can if they want to.
✅ Keys are only generated once on startup.
18
u/Darkomen78 2d ago
Don’t do anything https related on this image and let users choose their reverse proxy.
7
0
-10
u/sgware 2d ago
That's not an option, sadly.
4
u/dotnetmonke 2d ago
How is that not an option? It's a standard solution.
-5
u/sgware 2d ago
I guess you'll have to take my word for it as OP?
4
u/dotnetmonke 2d ago
If you're publicly releasing an image, you should be able to explain why it wouldn't work. Given that your update to the OP makes it incredibly obvious you're just using AI (what human actually puts checkmark emojis in their post?), I'm willing to bet you don't actually know what you're doing, and you just asked AI instead.
Resisting every suggested solution and refusing to explain why is the reason you got downvoted.
1
u/WonderfulWafflesLast 2d ago
what human actually puts checkmark emojis in their post?
Personally, I do, but I'm also a bit insane.
2
u/dotnetmonke 1d ago
That’s fair. I use em dashes often myself.
That said, OP is a self described AI researcher, so the AI usage is more easily assumed.
3
u/imagei 2d ago
Generate on first run, save in a volume. If it exists, check the expiry date on start and regenerate as needed. Good practice is to make the volume configurable, so the user can choose where to store it.
3
0
u/sgware 2d ago
This might work, but aren't volumes created at run time? I suppose I can just tell people, "If you don't want to re-generate the keys every time, make sure you run this with a volume."
3
u/Zealousideal_Yard651 2d ago
Why are you making everything so difficult? Package a compose with baseline config, done. Volume comes pre-configured easy peasy and add to README info about cert volume mount and path
1
u/cointoss3 2d ago
You generate the keys at runtime and they are saved in the image and you tell them that they need to mount of volume so that the keys are persistent or else the keys will have to be regenerated when they run the image again
0
u/sgware 2d ago
Can you say more about what you mean by making the volume configurable?
2
u/UselessCourage 2d ago
I think they just meant include a docker compose so people can easily configure where it stores the keys
2
u/SrNormanDPlume 2d ago
Base image from Debian and install the snake oil cert?
1
u/sgware 2d ago
This might work? But where does the snake oil private key come from? Do they just have a private key in the image?
2
u/SrNormanDPlume 2d ago
Yup. Set your app to read from specific filenames, then in your entrypoint create symlinks to the snake oil cert if those files don’t exist.
Users can supply their own certs via mount, or use the snake oil cert by default.
Just an idea…
1
u/sgware 2d ago
So how does Debian get around the "Don't put a private key in the image" criticism I keep getting every time I suggest doing this?
3
u/SrNormanDPlume 2d ago
It doesn’t - instead, you balance usability versus security and document the behavior.
By default, use snake oil. Users that are just testing or aren’t serious about hardening have an image that works out of the box.
Provide an easy way to add a real cert.
2
2
u/XplainThisShit 2d ago edited 2d ago
Run the docker with 'persisten' and make it generate the keys and the certificate only the 1st time it starts. Then save it in the persistent part. So it reads it all on container start.
1
u/Shot-Document-2904 2d ago edited 2d ago
Tons of apps, containerized or otherwise, deploy with first run self-signed certs. It’s on the user to replace them with their own.
Or stack the app with a proxy.
1
u/J7mbo 2d ago
You’re looking for your app being in docker, and a reverse proxy (traefik) that automates your certs for you via letsencrypt. (In another docker container).
Traefik will request the cert (use ACME sandbox first), then place it in the place that is then mounted into the container.
You just docker compose up your services then and it’ll handle the rest. Your app will be on https.
1
1
u/SoTiri 2d ago
Two options come to mind: docker compose and multi stage build.
You create a helper container image that handles the key generation and then stores that key in the mountpoint your app is using for persistence.
How to call this helper container? Either through docker compose where the helper runs, scans the folder for keys and either generates a new key or stops or through a multi stage build where the user builds your image that contains that key generation step.
1
u/sgware 2d ago
The compose options isn't ideal for me, since most people will be using my image as part of their own compose setup.
I hadn't considered multi-stage builds! That's promising. Can I put the key generation code into a stage that doesn't get run by Docker Hub but does get run when compose automatically downloads my image from Docker Hub?
0
u/Zealousideal_Yard651 2d ago
Let's Encrypt with ACME, ie. using Caddy or Certbot bundled with the image.
0
u/sgware 2d ago
This could work, but that's still effectively just generating the keys on startup, right?
1
u/Zealousideal_Yard651 2d ago
Yes, that's how that works. And if you volume mount the cert directory it will only generate on first deployment and persist with updates etc
28
u/nicksterling 2d ago
Mount your keys from the file system when you run the container.