SSL certificate and HTTPS protocol for OpenVPNAdmin
karabelnikov opened this issue · 25 comments
Scott, I would like to touch on another important topic when using the OpenVPN Admin project in production. This is a connection to the web interface via an SSL certificate using an HTTPS connection. How difficult is it to implement in a container? Many use their own certificate authority to issue certificates for use in the organization projects, such as OpenVPN Admin.
This would not be difficult to implement, either as a code change, or by updating the app.conf file, as referenced in this PR from the original project: adamwalach/openvpn-web-ui#9
The app.conf change seems like the way to go, but I don't know much about the whole certificate generation process. We'd want users to have a self-signed certificate initially, with the ability to get a "real" certificate I would think -- but as I say I'm not really tuned-in to this process.
Personally, I would continue to use http since I only use the interface on my local network or over a VPN. The other reason I use http for most of my internal server stuff is Organizr, which I love, particularly when my various WebUIs work in iFrames. I've been known to go to some lengths to disable https, for this very reason. :-)
Oh, and one other thing worth mentioning:
If you notice in the comment attached to the PR, the path for the certificates has "dehydrated" in it, which I'm sure refers to this project: https://github.com/dehydrated-io/dehydrated
My suggestion is to use the code editing path. HTTPS should be an alternative to HTTP, but not a complete replacement. I threw some thoughts, I think you get the point. This is how I see it:
beego.BConfig.Listen.EnableHTTPS = true
beego.BConfig.Listen.HTTPSPort = 8443
beego.BConfig.Listen.HTTPSCertFile = "$SSLFolder" + "$SSLCert"
beego.BConfig.Listen.HTTPSKeyFile = "$SSLFolder" + "$SSLPrivateKey"
$SSLFolder = "/opt/openvpn-admin/ssl/"
$SSLCert = {{ .SSLCert }}
$SSLPrivateKey = {{ .SSLKey }}
settings.html
<div class="form-group">
<label for="name">SSL Server Certificate</label>
<input type="text" class="form-control" id="SSLCert" name="SSLCert" placeholder="Enter certificate name"
value="{{ .Settings.SSLCert }}">
<span class="help-block">File name of the certificate to enable the https protocol. In the folder: /opt/openvpn-admin/ssl/</span>
</div>
<div class="form-group">
<label for="name">SSL Server Private Key</label>
<input type="text" class="form-control" id="SSLKey" name="SSLKey" placeholder="Enter private key name"
value="{{ .Settings.SSLKey }}">
<span class="help-block">File name of the private key to enable the https protocol. In the folder: /opt/openvpn-admin/ssl/</span>
</div>
Add lines to settings.html file, write that you need to specify the filenames name.crt and name.key to enable the HTTPS protocol.
Administrator:
Write file names in the lines on the settings.html page, saves and restarts the container. The https protocol should work.
This is how you create folders:
package main
import (
"os"
)
func main() {
err = os.MkdirAll("openvpn-admin/ssl", 0777)
if err != nil {
panic(err)
}
}
I've added support for https, including the optional use of a custom certificate and key. You'll need to use a custom docker-compose to test it until we determine that it's ready for prime time:
version: '3'
services:
gui:
image: bnhf/pivpn-tap-web-ui:beta
container_name: openvpn-gui-tap
environment:
- OPENVPN_ADMIN_USERNAME=admin
- OPENVPN_ADMIN_PASSWORD=b3secure
- COUNTRY=${COUNTRY}
- PROVINCE=${PROVINCE}
- CITY=${CITY}
- ORG=${ORG}
- EMAIL=${EMAIL}
- OU=${OU}
- PIVPN_SERVER=${PIVPN_SERVER}
- PIVPN_CONF=${PIVPN_CONF}
- TZ=${TZ}
- ENABLEHTTPS=${ENABLEHTTPS} # Optional (default=false)
- HTTPSPORT=${HTTPSPORT} # Optional (default=8443)
- HTTPSCERT=${HTTPSCERT} # Optional (default=/etc/openvpn/easy-rsa/pki/issued/${PIVPN_SERVER}.crt
- HTTPSKEY=${HTTPSKEY} # Optional (default=/etc/openvpn/easy-rsa/pki/private/${PIVPN_SERVER}.key
ports:
- "8080:8080/tcp"
- "8443:8443/tcp" # Optional (required if ENABLEHTTPS=true)
restart: always
volumes:
- /etc/openvpn:/etc/openvpn
- ./openvpn-data/db:/opt/openvpn-gui-tap/db
Then, of course, you'd need to actually supply the environment variables in that section of the Portainer Stack.
Hi, Scott! I have checked the functionality of the https protocol and can confirm that everything is working as it should. Great!
It turns out we have two scenarios:
-
OpenVPN Admin uses the existing certificate and private key of PIVPN Server in the standard way. It turns out that you only need to install the CA root certificate on the client machine and use the hostname as the name of the PIVPN server and everything works.
-
When creating a container, set in variables:
HTTPSCERT=/etc/ssl/certs/server.crt
HTTPSKEY=/etc/ssl/private/server.key
specify your way to the server certificate and private key and install the certificate authority certificate on the client machine and you can access the browser by the host name.
Scott, for some reason, after the first restart of the container or after it is stopped and turned on, immediately after the initial deployment, OpenVPN Admin is no longer available over http and https. If you look at the container, then the PORT CONFIGURATION disappears from it
Not sure if this is the reason, but you can't map your own certificates into whatever location you want. Since /etc/openvpn is bound from the host to the container, you could create an ssl directory there, i.e. /etc/openvpn/ssl. Alternatively, if /etc/ssl doesn't already exist in the container (check first please), you could add an additional volume mapping to the docker-compose.
Scott, made the container as in the screenshots. This works as it should, until the container is restarted for the first time or stopped and started. I don't specify my user certificates, I haven't tested such a scenario yet. But in the standard version, the container crashes after the following launches.
Comment out the 3 variables you're not using currently in the docker-compose and delete those same 3 from the environment variables section of Portainer -- then restart the container.
Scott, if I do as you wrote, then everything works. Deleting variables, restarting and everything works. But if I leave variables and specify their values, then the problem remains. After the first restart, the container crashes.
If we want to specify variables, then it doesn't work.
I see what you are saying, and I'm working on it now.
Something I didn't realize, is that when a container gets restarted rather than redeployed, the app.conf file doesn't get recreated. The start.sh script is run again though, leading to the .crt file name being concatenated to the environment variable. I need a slightly different sed
command in the script. For, now re-deploy the container in Portainer - Stacks (no need to re-pull, just re-deploy) rather than restarting. Shouldn't take long to fix.
Scott, I've tried it in different ways, but it doesn't work fine for me, as it should. If I specify variable data, then the container crashes. Alas!
I'm building a new development container now. I'll do some testing, and if this problem appears solved, I'll build a new multi-arch beta and post back here to let you know.
There's more to this issue than I first thought, so don't use these variables for now. I'll have to look at this more after I'm back.
I made my changes, did the commit, but then forgot to push them to Github -- little wonder it seemed like nothing was fixed when I built my dev container. :-/ Anyway, it appears my tweaks were successful, so I'm building a new multi-arch container now, which should be live very shortly.
Also, I had no idea there were so many configuration options available through the Beego framework and its app.conf file. Makes sense to me for us to map this .conf file (and maybe the whole conf directory) to the host so that advanced users can edit it directly. We'll make this change part of a future update.
https://github.com/beego/beedoc/blob/master/en-US/mvc/controller/config.md
Scott, I take it you haven't solved the problem completely yet? I tested it and got the following results:
1. If you do not specify variables:
HTTPSPORT=${HTTPSPORT}
HTTPSCERT=${HTTPSCERT}
HTTPSKEY=${HTTPSKEY}
Then everything works, including restarting the container or stopping and restarting.
2. If you specify variables as default values forcibly:
HTTPSPORT=${HTTPSPORT}
HTTPSCERT=${HTTPSCERT}
HTTPSKEY=${HTTPSKEY}
Then everything works, including restarting the container or stopping and restarting.
3. If you specify the port and values with absolute paths in the variables:
HTTPSPORT=8443
HTTPSCERT=/etc/openvpn/easy-rsa/pki/issued/debian_34c93990-1e2e-49af-a768-f221fe898792.crt
HTTPSKEY=/etc/openvpn/easy-rsa/pki/private/debian_34c93990-1e2e-49af-a768-f221fe898792.key
Then nothing works. So setting your values causes the container to crash.
You're a good tester Shura -- thanks. My modified sed
was working for two of the variables, but not the other two. Your testing strategy of using the default variables, as though they were optional, was helpful.
It's working for me now on an arm64 platform, with all four variables specifically set. The new beta is available now, so give it a try when you have a moment.
If you look at the Portainer logs for the container (you'll want to set Lines=500 and then turn Auto-refresh off), you should see something like this (I'm posting just the beginning and end of the log:
TERM environment variable not set.
OpenVPN directory set to /etc/openvpn
Working Directory set to /opt
PiVPN Server set to raspberrypi10_ca8554c5-6025-442f-9d06-25a5cab85a3c
Working directory set to /opt/openvpn-gui-tap
HTTPS enabled set to "true"
HTTPS port set to: "8443"
HTTPS Certificate path set to: "/etc/openvpn/easy-rsa/pki/issued/raspberrypi10_ca8554c5-6025-442f-9d06-25a5cab85a3c.crt"
HTTPS key path set to: "/etc/openvpn/easy-rsa/pki/private/raspberrypi10_ca8554c5-6025-442f-9d06-25a5cab85a3c.key"
...
2022/12/19 11:43:17.208 [I] [asm_arm64.s:1133] http server Running on http://:8080
2022/12/19 11:43:17.209 [I] [asm_arm64.s:1133] https server Running on https://:8443
If you don't, please post what you do see from similar log snippets.
Scott, I tested and I can safely say everything works.
Option 1. We just enable "ENABLEHTTPS=true"
support and don't specify any other variables. Used by default. After starting and 3 reboots of the container, everything works.
Option 2. We enable "ENABLEHTTPS=true"
support and specify variables, but do not specify their values. Leaving the values as variables themselves for the default parameters in order to specify your data in the future.
HTTPSPORT=${HTTPSPORT}
HTTPSCERT=${HTTPSCERT}
HTTPSKEY=${HTTPSKEY}
After starting and 3 reboots of the container, everything works.
Option 3. We enable "ENABLEHTTPS=true" support and specify our data:
HTTPSPORT=8443
HTTPSCERT=/etc/openvpn/ssl/debian_34c93990-1e2e-49af-a768-f221fe898792.crt
HTTPSKEY=/etc/openvpn/ssl/debian_34c93990-1e2e-49af-a768-f221fe898792.key
After starting and 3 reboots of the container, everything works.
However, an important note! The path must be relative to the /etc/openvpn directory. Since this path is mounted in the container options at startup. If you specify a path other than /etc/openvpn, then you will have to add this path to the mount options, which is redundant.
Let's summarize. HTTPS support is fully functional.
Если вы посмотрите журналы Portainer для контейнера (вы захотите установить Lines=500, а затем отключить автоматическое обновление), вы должны увидеть что-то вроде этого (я публикую только начало и конец журнала:
TERM environment variable not set. OpenVPN directory set to /etc/openvpn Working Directory set to /opt PiVPN Server set to raspberrypi10_ca8554c5-6025-442f-9d06-25a5cab85a3c Working directory set to /opt/openvpn-gui-tap HTTPS enabled set to "true" HTTPS port set to: "8443" HTTPS Certificate path set to: "/etc/openvpn/easy-rsa/pki/issued/raspberrypi10_ca8554c5-6025-442f-9d06-25a5cab85a3c.crt" HTTPS key path set to: "/etc/openvpn/easy-rsa/pki/private/raspberrypi10_ca8554c5-6025-442f-9d06-25a5cab85a3c.key" ... 2022/12/19 11:43:17.208 [I] [asm_arm64.s:1133] http server Running on http://:8080 2022/12/19 11:43:17.209 [I] [asm_arm64.s:1133] https server Running on https://:8443
Если вы этого не сделаете, пожалуйста, опубликуйте то, что вы видите из аналогичных фрагментов журнала.
I didn't quite understand why to make the value 500?
Now in the magazine I have so:
OpenVPN directory set to /etc/openvpn
Working Directory set to /opt
TERM environment variable not set.
PiVPN Server set to debian_34c93990-1e2e-49af-a768-f221fe898792
Working directory set to /opt/openvpn-gui-tap
HTTPS enabled set to "true"
HTTPS Certificate set to default: "debian_34c93990-1e2e-49af-a768-f221fe898792".crt
HTTPS key set to default: "debian_34c93990-1e2e-49af-a768-f221fe898792".key
2022/12/20 02:48:48.271 [I] [asm_amd64.s:1581] http server Running on http://:8080
2022/12/20 02:48:48.273 [I] [asm_amd64.s:1581] https server Running on https://:8443
Excellent news! Sorry it took multiple attempts to get it right. sed
is nice utility but it can be fussy, and isn't implemented exactly the same across versions of Linux.
Log lines add up surprisingly quickly, so if you login to the application and then look at the Portainer log, 100 lines won't get you back to the first boot line. It doesn't hurt to set it large when you want to be sure to be able to scroll back. I often just add a zero and make it 1000 before I turn off Auto-refresh.