linuxserver / docker-unifi-controller

GNU General Public License v3.0
899 stars 125 forks source link

installing my own ssl cert #12

Closed glc650 closed 5 years ago

glc650 commented 5 years ago

macOS 10.14.5 (on a 2018 Mac Mini)

docker create \ --name=unifi-controller \ --network ubnt \ -e PUID=501 \ -e PGID=20 \ -p 10.0.0.3:3478:3478/udp \ -p 10.0.0.3:10001:10001/udp \ -p 10.0.0.3:8080:8080 \ -p 10.0.0.3:8081:8081 \ -p 10.0.0.3:8443:8443 \ -p 10.0.0.3:8843:8843 \ -p 10.0.0.3:8880:8880 \ -p 10.0.0.3:6789:6789 \ -v ~/Documents/docker/config/unifi:/config \ --restart unless-stopped \ linuxserver/unifi-controller

I migrated from a Cloud Key where I was using my own cert and I'm not sure how to install that same cert in the container.

CHBMB commented 5 years ago

The way we advise this is to stick this container behind our preconfigured LetsEncrypt container as a reverse proxy.

We have preconfigured config for the unifi controller that are pulled into our LetsEncrypt container.

You can read about this on our blog.

haydonryan commented 4 years ago

Hopefully, this helps other people but got it to work with not too many issues.

- Running on a Raspberry Pi with docker. - I have a CA, (signed by my own root cert), key, and wildcard server cert that covers this and other test servers at home. (I'm not using lets-encrypt)

I have the unifi controller running the linuxserver/unifi-controller image(as of the current date). Got this to work by running the commands outside of the script from here.

I avoided installing the keytool command-line tool on the pi, so I used the one inside the container.

Copy the Server Cert, CA, and Key files to the pi/unifi/data directory.

Firstly backup the pi/unifi/data/keystore file

docker exec -it unifi-controller /bin/bash
cd data
keytool -list -keystore keystore # Note the dates/stamp of the key that's in there- it should change    

service unifi stop
openssl pkcs12 -export \
-in "CA.crt" \
-in "SERVER.crt" \
-inkey "SERVER-KEY.key" \
-out "temp" -passout pass:"aircontrolenterprise" \
-name "unifi"

keytool -delete -alias unifi -keystore keystore -deststorepass aircontrolenterprise

keytool -list -keystore keystore # should have nothing in the store

keytool -importkeystore \
-srckeystore "temp" -srcstoretype PKCS12 \
-srcstorepass "aircontrolenterprise" \
-destkeystore "keystore" \
-deststorepass "aircontrolenterprise" \
-destkeypass "aircontrolenterprise" \
-alias "unifi" -trustcacerts    

keytool -list -keystore keystore # New cert should be in there.
service unifi start

At this point, the controller will continue to use the old cert ... but if you exit out of the container and run

docker container restart unifi-controller

Mine worked. Good Luck!

ZericE commented 3 years ago

Hopefully, this helps other people but got it to work with not too many issues.

- Running on a Raspberry Pi with docker. - I have a CA, (signed by my own root cert), key, and wildcard server cert that covers this and other test servers at home. (I'm not using lets-encrypt)

I have the unifi controller running the linuxserver/unifi-controller image(as of the current date). Got this to work by running the commands outside of the script from here.

I avoided installing the keytool command-line tool on the pi, so I used the one inside the container.

I just used this process on a linuxserver.io/unifi 6.0.45 container running on ubuntu 20.04 and it seems to be working fine. If someone is using a certificate that expires regularly (ie. lets encrypt), a fully automated process would be needed, but for a locally created/trusted root CA and server certificates with a long life, this works well. It would be easy to put it in a script and add a little error checking. The keystore is outside the container so it should be preserved across version upgrades and container re-builds.

Thanks for posting this!

haydonryan commented 3 years ago

Thanks for posting this! You're welcome glad it worked.

clobber88 commented 3 years ago

Thanks for this writeup, it really helps - but Im not there yet.

I'm using Traefik to generate acme.json, which I have successfully converted to a x509 pem file (crt extension). This crt file contains 3 certificates. The first is R3 signing for my server. The second is LetsEncrypt R3. The third is Root X1 cross signed by CA X3. This is all herehttps://letsencrypt.org/certificates/.

I also have two keys. One for my server and one for letsencrypt.

I'm assuming in your command that: SERVER.crt is the first cert? CA.crt is the second cert? SERVER-KEY.key is the key for my server

And that There is no need for the 3rd cert or other key?

Thanks

haydonryan commented 3 years ago

I'm assuming in your command that: SERVER.crt is the first cert? CA.crt is the second cert? SERVER-KEY.key is the key for my server

Yes, that's correct. That's what I did in my case. But I do explicitly have my intermediate CA cert as a trusted certificate in all my browsers.

The letsencrypt root certificate - you can paste after your CA cert in the ca file to form a certificate chain.

I haven't tested this for this specific use case but that's how I have other devices set up.(I went through a process to give all my local https servers proper certificates).

It should mean that as long as the letsencrypt root certificate is trusted, and your CA is signed with it- and then your server cert is signed with the CA it will form a nice chain of trust (along with your server key).

Worst case you can always repeat the process again(take an offline backup of your files) - it won't hurt anything as you're just replacing the cert/key combo in the keystone.

Let me know how you go. :)

maxirus commented 2 years ago

@haydonryan 's solution works.

I think @CHBMB prematurely closed this issue by not accounting for Custom CA use-cases. The Unifi Controller supports custom certs out-of-the-box; we just need an easy way of injecting this. Please re-open.

I suspect given that the LinuxServer.io images looks for and executes custom init scripts upon startup, I believe this could be easily achieved by mounting a script along with the certs. This script IMO should be pre-baked into the image. A simple implementation could be scanning a /certs directory for specific files (CA.crt, Server.crt, Server.key) and if found, update the keystore. Perhaps copy to sub-dir in /config so that subsequent runs can do a diff to check for changes.

Alternatively, set ENV vars to their respective paths (ie. CA_CERT_PATH=/some/path/CA.crt).

j0nnymoe commented 2 years ago

@maxirus bare in mind, this issue is from 2019 - unifi has changed a lot since then. There is nothing stopping users setting up a SSL cert within our container of they know what they're doing but we have no plans to change how our image works. We will continue to recommend putting it behind a reverse proxy.

maxirus commented 2 years ago

@j0nnymoe yea; thought I'd try to get this re-opened instead of creating a new one. You've been able to set custom SSL certs for many years.

we have no plans to change how our image works

Not sure I follow... This wouldn't be a breaking change, it'd be an added feature to unlock additional functionality. Is your stance that you'd reject any community contribution to implement this feature? Maybe I missed something?

A reverse proxy isn't for everyone and is sub-optimal for this use-case. It adds an additional layer of complexity, another container to maintain & monitor, another service to debug when things go wrong, and adds a number of additional attack vectors. Unless I overlooked them, you can't automate the trust between NGINX & Unifi during deployment as you don't have the certs - they're initialized at first start. It's much simpler for your average user to create their own certs and let automation take over inside the container.

j0nnymoe commented 2 years ago

If a prove of concept mod was built for the image, it might be something we consider adding to https://mods.linuxserver.io - My personal concern would be support overhead. I don't believe anyone currently uses their own CA let a lone with Unifi so if something broke, it's not something we would be able to test.

Is your stance that you'd reject any community contribution to implement this feature?

No, we don't reject any community contribution. Just went something is suggested/PR'ed - we have to take into consideration not only if it causes any future problems with the container but also any support overhead we might get from this.

Mod seems like the best way to go for this imo, providing you're happy with maintaining it if any issues come up.

Brewer7 commented 1 year ago

Hopefully, this helps other people but got it to work with not too many issues.

- Running on a Raspberry Pi with docker. - I have a CA, (signed by my own root cert), key, and wildcard server cert that covers this and other test servers at home. (I'm not using lets-encrypt)

I have the unifi controller running the linuxserver/unifi-controller image(as of the current date). Got this to work by running the commands outside of the script from here.

I avoided installing the keytool command-line tool on the pi, so I used the one inside the container.

Copy the Server Cert, CA, and Key files to the pi/unifi/data directory.

Firstly backup the pi/unifi/data/keystore file

docker exec -it unifi-controller /bin/bash
cd data
keytool -list -keystore keystore # Note the dates/stamp of the key that's in there- it should change    

service unifi stop
openssl pkcs12 -export \
-in "CA.crt" \
-in "SERVER.crt" \
-inkey "SERVER-KEY.key" \
-out "temp" -passout pass:"aircontrolenterprise" \
-name "unifi"

keytool -delete -alias unifi -keystore keystore -deststorepass aircontrolenterprise

keytool -list -keystore keystore # should have nothing in the store

keytool -importkeystore \
-srckeystore "temp" -srcstoretype PKCS12 \
-srcstorepass "aircontrolenterprise" \
-destkeystore "keystore" \
-deststorepass "aircontrolenterprise" \
-destkeypass "aircontrolenterprise" \
-alias "unifi" -trustcacerts    

keytool -list -keystore keystore # New cert should be in there.
service unifi start

At this point, the controller will continue to use the old cert ... but if you exit out of the container and run

docker container restart unifi-controller

Mine worked. Good Luck!

Thanks! Works well

haydonryan commented 1 year ago

The other thing you could do is put it behind a reverse proxy like Traefik. I've done that for some services, and it's centralized where I need to update the cert. If you're using lets encrypt it can also auto renew. The yearly expiry I'm on (to satisfy iOS requirements) it gets a bit frustrating going to each device and service....

Would love to see an API endpoint and client side script to perform the update, but that's feedback for unifi and not the dockerimage.

asheroto commented 1 year ago

@haydonryan 's solution works.

I think @CHBMB prematurely closed this issue by not accounting for Custom CA use-cases. The Unifi Controller supports custom certs out-of-the-box; we just need an easy way of injecting this. Please re-open.

I suspect given that the LinuxServer.io images looks for and executes custom init scripts upon startup, I believe this could be easily achieved by mounting a script along with the certs. This script IMO should be pre-baked into the image. A simple implementation could be scanning a /certs directory for specific files (CA.crt, Server.crt, Server.key) and if found, update the keystore. Perhaps copy to sub-dir in /config so that subsequent runs can do a diff to check for changes.

Alternatively, set ENV vars to their respective paths (ie. CA_CERT_PATH=/some/path/CA.crt).

Yes, this would be ideal! There are situations where this would be much easier and faster, such as when you have your own SSL cert through a CA like Sectigo/DigiCert and don't want to use Let's Encrypt to maintain infrastructure consistency.

@haydonryan thanks for that info, saw you commented on the UniFi forums about this.

Your instructions worked well and I didn't get any errors, saw that my new certificate fingerprint made it in the keystore, but after restarting the certificate was not applied. Looking at the UniFi SSL certificate it just has the default cert again. Looking at the keystore again it just has the default cert. Any idea what I should adjust with the container?

Just reverts... image

haydonryan commented 1 year ago

I would check the logs for the controller - it's likely that it didn't like your cert chain for some reason. Unfortunately in my experience this can be numerous issues, eg incorrect certpath, dates, etc.