diff --git a/Dockerfile b/Dockerfile index b3c0007..1f9603f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,11 +1,9 @@ -FROM debian:11 -# Update/upgrade -RUN apt-get update -RUN apt-get upgrade -y -RUN apt-get install --no-install-recommends --yes rsyslog openvpn ssh less vim iputils-ping net-tools -RUN mkdir /root/.ssh -RUN chmod 700 /root/.ssh -RUN touch /root/.ssh/authorized_keys +FROM alpine +RUN apk add rsyslog openvpn openssh haproxy curl +RUN mkdir /root/.ssh && chmod 700 /root/.ssh && touch /root/.ssh/authorized_keys +RUN mkdir /etc/ssh/sshd_config.d/ /etc/rsyslog.d/ /var/lib/haproxy/dev +RUN echo 'Include /etc/ssh/sshd_config.d/*.conf' >> /etc/ssh/sshd_config +RUN echo '$IncludeConfig /etc/rsyslog.d/*.conf' >> /etc/rsyslog.conf COPY entrypoint.sh /usr/local/sbin/entrypoint.sh RUN chmod 755 /usr/local/sbin/entrypoint.sh ENTRYPOINT /usr/local/sbin/entrypoint.sh diff --git a/README.md b/README.md new file mode 100644 index 0000000..46afcb5 --- /dev/null +++ b/README.md @@ -0,0 +1,55 @@ +# Docker image to get access to your Home Assistant via OpenVPN and a reverse proxy + +This Alpine Linux based image allow you to mount a tunnel to your external host that have to run OpenVPN server and a reverse proxy to access to your Home Assistant. In this container, you have: + +- a Rsyslog (for logging) +- a SSH service with root access (if you declare your SSH pub key) +- a OpenVPN client +- a Haproxy configured to get access to your Home Assistant + +## Installation + +```bash +git clone https://gitea.zionetrix.net/bn8/ha-remote-vpn /srv/ha-remote-vpn +docker pull brenard/ha-remote-vpn +``` + +## Configuration + +### On the container + +You have to: + +- put your external host IP address or domain name in `srv/openvpn/client.conf` (on the `remote` line at the begining of the file) +- put your Home Assistant IP address in `srv/haproxy/haproxy.cfg` (on the `server` line at the end of the file) +- pur your SSH public key in `srv/ssh/authorized_keys` + +## On your external host + +You have to: + +- install and configure OpenVPN using the provide `srv/openvpn/server.conf` and the `secret.key` file that will be generated by the client container on its first start +- install and configure the reverse proxy of your choice, for instance, Apache2: on a Debian host : + - Install it : `apt install apache2` + - Copy `apache2.conf` in `/etc/apache2/sites-available/home.conf` and ajust it for your needs + - Enable required modules and the site : `a2enmod proxy_http proxy_wstunnel rewrite ssl && a2ensite home && service apache2 restart` + +### On your Home Assistant + +You have to authorized access via your reverse proxy by adding the following lines in your `configuration.yaml` file: + +```yaml +http: + use_x_forwarded_for: true + trusted_proxies: + - 192.168.1.160 +``` + +**Note:** Adjust your docker container IP address in the list `trusted_proxies`. + +## Start the container + +```bash +cd /srv/ha-remote-vpn +docker run -it --rm -v "$( realpath srv ):/srv" --cap-add=NET_ADMIN brenard/ha-remote-vpn +``` diff --git a/apache2.conf b/apache2.conf new file mode 100644 index 0000000..2a23db8 --- /dev/null +++ b/apache2.conf @@ -0,0 +1,42 @@ + + ServerName ha.example.com + + DocumentRoot /var/www/empty + + RewriteEngine on + RewriteCond %{REQUEST_URI} !(^/\.well-known/.*$) + RewriteRule ^(.*)$ https://%{SERVER_NAME}$1 [R=307] + + ErrorLog /var/log/apache2/ha.example.com.error.log + CustomLog /var/log/apache2/ha.example.com.access.log combined + + + + ServerName ha.example.com + + SSLEngine On + #SSLCertificateFile /etc/letsencrypt/live/ha.example.com/cert.pem + #SSLCertificateKeyFile /etc/letsencrypt/live/ha.example.com/privkey.pem + #SSLCACertificateFile /etc/letsencrypt/live/ha.example.com/chain.pem + SSLCertificateFile /etc/ssl/private/ssl-cert-snakeoil.key + SSLCertificateKeyFile /etc/ssl/certs/ssl-cert-snakeoil.pem + + DocumentRoot /var/www/html + + # Home-Assistant + ProxyPreserveHost On + ProxyRequests off + ProxyPass /api/websocket ws://172.16.88.2:80/api/websocket + ProxyPassReverse /api/websocket ws://172.16.88.2:80/api/websocket + ProxyPass / http://172.16.88.2:80/ + ProxyPassReverse / http://172.16.88.2:80/ + + RewriteEngine on + RewriteCond %{HTTP:Upgrade} =websocket [NC] + RewriteRule /(.*) ws://172.16.88.2:80/$1 [P,L] + RewriteCond %{HTTP:Upgrade} !=websocket [NC] + RewriteRule /(.*) http://172.16.88.2:80/$1 [P,L] + + ErrorLog /var/log/apache2/ha.example.com.error.log + CustomLog /var/log/apache2/ha.example.com.access.log combined + diff --git a/entrypoint.sh b/entrypoint.sh index ebeffff..914e05a 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,40 +1,107 @@ -#!/bin/bash -e +#!/bin/sh -e -echo "Start rsyslog service..." -service rsyslog start -echo done. +[ "$1" == "--stop" ] && { killall haproxy openvpn sshd rsyslogd; exit; } + +if [ -d /srv/rsyslog ] +then + if [ -n "$( ls /srv/rsyslog/*.conf )" ] + then + echo -n "Install Rsyslog configuration... " + cp -p /srv/rsyslog/*.conf /etc/rsyslog.d/ + echo done. + fi + + echo "Start rsyslog service..." + /usr/sbin/rsyslogd + echo done. +else + echo "Rsyslog configuration directory not found (/srv/rsyslog)" +fi if [ -d /srv/ssh ] then + # Generate key if missing + if [ -z "$( ls /srv/ssh/*_key 2> /dev/null )" ] + then + echo "Generate SSH host keys..." + ssh-keygen -A + cp -p /etc/ssh/*_key* /srv/ssh/ + echo done. + else + echo "Existing SSH host keys present, reuse it" + + # Install host keys + echo -n "Install SSH host keys... " + cp -p /srv/ssh/*_key /srv/ssh/*_key.pub /etc/ssh/ + chown root: /etc/ssh/*_key* + chmod 600 /etc/ssh/*_key + chmod 644 /etc/ssh/*_key.pub + echo done. + fi + + # Install configuration if [ -n "$( ls /srv/ssh/*.conf 2> /dev/null )" ] then - echo "Install custom SSH configuration files..." - cp /srv/ssh/*.conf /etc/ssh/sshd_config.d/ + echo -n "Install custom SSH configuration files... " + cp -p /srv/ssh/*.conf /etc/ssh/sshd_config.d/ echo done. else echo "No custom SSH configuration files found. Put it in /srv/ssh if need (with .conf extension)." fi + # Install authorized_keys file if [ -e /srv/ssh/authorized_keys ] then - echo "Install SSH authorized keys (from /srv/ssh/authorized_keys file)" + echo -n "Install SSH authorized keys (from /srv/ssh/authorized_keys file)... " cat /srv/ssh/authorized_keys > /root/.ssh/authorized_keys + chmod 644 /root/.ssh/authorized_keys + echo done. else echo "No SSH authorized keys to install. Put it in /srv/ssh/authorized_keys file." fi - echo "Start SSH service..." - service ssh start + # Start SSH + echo -n "Start SSH service... " + /usr/sbin/sshd -f /etc/ssh/sshd_config echo done. +else + echo "SSH configuration directory not found (/srv/ssh)" fi if [ -d /srv/openvpn ] then - cp /srv/openvpn/* /etc/openvpn - service openvpn start + # Generate secret on first start + if [ ! -e /srv/openvpn/secret.key ] + then + echo -n "Generate missing share secret key file... " + openvpn --genkey secret /srv/openvpn/secret.key + chmod 400 /srv/openvpn/secret.key + echo done. + fi + + # Ensure /dev/net/tun is present + mkdir -p /dev/net + if [ ! -c /dev/net/tun ]; then + mknod /dev/net/tun c 10 200 + fi + + # Start OpenVPN + echo -n "Start OpenVPN ... " + /usr/sbin/openvpn --daemon --config /srv/openvpn/client.conf + echo done. else - echo "OpenVPN configuration directory not mount (/srv/openvpn)" + echo "OpenVPN configuration directory not found (/srv/openvpn)" fi -echo "Run BASH shell" -bash -l +if [ -d /srv/haproxy ] +then + # Start Haproxy + echo -n "Start OpenVPN ... " + /usr/sbin/haproxy -f /srv/haproxy/haproxy.cfg + echo done. +else + echo "Haproxy configuration directory not mount (/srv/haproxy)" +fi + +echo "Run interactive shell" +sh diff --git a/srv/haproxy/blacklist b/srv/haproxy/blacklist new file mode 100644 index 0000000..000340d --- /dev/null +++ b/srv/haproxy/blacklist @@ -0,0 +1,2 @@ +# Access blacklist +# 123.123.123.123 diff --git a/srv/haproxy/error.http b/srv/haproxy/error.http new file mode 100644 index 0000000..e7eea07 --- /dev/null +++ b/srv/haproxy/error.http @@ -0,0 +1,44 @@ +HTTP/1.0 500 Server Error +Cache-Control: no-cache +Connection: close +Content-Type: text/html + + + +Home Assistant + + + + + + + +
+ +

Your Home Assistant seem not reacheable for the moment.

+

Please check your installation or retry later.

+
+ + + diff --git a/srv/haproxy/haproxy.cfg b/srv/haproxy/haproxy.cfg new file mode 100644 index 0000000..dc441a9 --- /dev/null +++ b/srv/haproxy/haproxy.cfg @@ -0,0 +1,75 @@ +global + log /dev/log local0 + log /dev/log local1 notice + chroot /var/lib/haproxy + stats timeout 30s + user haproxy + group haproxy + daemon + + # Default SSL material locations + ca-base /etc/ssl/certs + crt-base /etc/ssl/private + + # See: https://ssl-config.mozilla.org/#server=haproxy&server-version=2.0.3&config=intermediate + ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 + ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256 + ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets + +defaults + log global + mode http + option httplog + option dontlognull + option log-health-checks + option log-separate-errors + option logasap + option contstats + option abortonclose + #option forwardfor except 172.16.81.0/24 + + timeout connect 3s + timeout client 60s + timeout server 60s + timeout http-request 5s + timeout check 2s + + retries 3 + + option splice-auto + option tcp-smart-connect + + errorfile 400 /srv/haproxy/error.http + errorfile 403 /srv/haproxy/error.http + errorfile 408 /srv/haproxy/error.http + errorfile 500 /srv/haproxy/error.http + errorfile 502 /srv/haproxy/error.http + errorfile 503 /srv/haproxy/error.http + errorfile 504 /srv/haproxy/error.http + + # Force source IP address to connect to HA + #source 192.168.8.161 + +frontend ha_front + bind 0.0.0.0:80 + #bind 0.0.0.0:443 ssl crt /srv/haproxy/bundle.pem + mode http + maxconn 10000 + + # Get user ip behind uppon reverse proxy + capture request header X-Forwarded-For len 15 + + # Blacklist + acl blacklist hdr(x-forwarded-for) -f /srv/haproxy/blacklist + http-request deny if blacklist + + default_backend ha_back + +backend ha_back + mode http + balance roundrobin + option httpchk GET / HTTP/1.0 + + timeout server 60s + + server ha-host 192.168.8.2:8123 check observe layer4 diff --git a/srv/openvpn/.gitignore b/srv/openvpn/.gitignore new file mode 100644 index 0000000..c996e50 --- /dev/null +++ b/srv/openvpn/.gitignore @@ -0,0 +1 @@ +*.key diff --git a/srv/openvpn/client.conf b/srv/openvpn/client.conf new file mode 100644 index 0000000..25c4c42 --- /dev/null +++ b/srv/openvpn/client.conf @@ -0,0 +1,53 @@ +# Remote host +remote remote.fqdn.tdl 1188 + +# Protocol & port +proto udp +port 1188 + +# Interface +dev vpn-ha +dev-type tap + +# MTU +tun-mtu 1500 + +# Secret shared key (generated on first client start) +# Note: to manually generate it, run: +# openvpn --genkey secret /srv/openvpn/secret.key +# cp /srv/openvpn/secret.key /etc/openvpn/secret.key +# chmod 400 /srv/openvpn/secret.key /etc/openvpn/secret.key +secret /srv/openvpn/secret.key +cipher AES-256-CBC + +# Keepalive +ping 30 +ping-restart 60 + +# Allow remote address changed +float + +# IP address inside VPN +ifconfig 172.16.88.2 255.255.255.0 +route-gateway 172.16.88.1 + +# Optional routes recheable througt the remote host +# route 192.168.8.0 255.255.255.0 + +# Run openvpn using this specified user & group +user nobody +group nogroup + +persist-key +persist-tun + +## Logging + +# Log level (0-9) +verb 3 + +# Max repeat count for logged messages +mute 10 + +# Managing interface +# management 127.0.0.1 7588 diff --git a/srv/openvpn/server.conf b/srv/openvpn/server.conf new file mode 100644 index 0000000..6c923ef --- /dev/null +++ b/srv/openvpn/server.conf @@ -0,0 +1,57 @@ +# Listen on specific IP address (optional, default: all) +# local 192.168.1.8 + +# Protocol & port +proto udp +port 1188 + +# Interface +dev vpn-ha +dev-type tap + +# MTU +tun-mtu 1500 + +# Secret shared key (generated on first client start) +# Note: to manually generate it, run: +# openvpn --genkey secret /srv/openvpn/secret.key +# cp /srv/openvpn/secret.key /etc/openvpn/secret.key +# chmod 400 /srv/openvpn/secret.key /etc/openvpn/secret.key +secret secret.key + +# Keepalive +ping 30 + +# Allow remote address changed +float + +# IP address inside VPN +ifconfig 172.16.88.1 255.255.255.0 +route-gateway 172.16.88.2 + +# Optional routes recheable througt the remote host +# route 192.168.9.0 255.255.255.0 + +# Run openvpn using this specified user & group +user nobody +group nogroup + +persist-key +persist-tun + +## Logging + +# Log level (0-9) +verb 3 + +# Max repeat count for logged messages +mute 10 + +# Daemon log +log /var/log/openvpn/homeassistant.log + +# Daemon status file +status /var/log/openvpn/homeassistant.status + +# Managing interface +# management 127.0.0.1 7588 diff --git a/srv/rsyslog/haproxy.conf b/srv/rsyslog/haproxy.conf new file mode 100644 index 0000000..36a1261 --- /dev/null +++ b/srv/rsyslog/haproxy.conf @@ -0,0 +1,9 @@ +# Create an additional socket in haproxy's chroot in order to allow logging via +# /dev/log to chroot'ed HAProxy processes +$AddUnixListenSocket /var/lib/haproxy/dev/log + +# Send HAProxy messages to a dedicated logfile +:programname, startswith, "haproxy" { + /var/log/haproxy.log + stop +} diff --git a/srv/rsyslog/openvpn.conf b/srv/rsyslog/openvpn.conf new file mode 100644 index 0000000..8030311 --- /dev/null +++ b/srv/rsyslog/openvpn.conf @@ -0,0 +1,5 @@ +# Send OpenVPN messages to a dedicated logfile +:programname, startswith, "openvpn" { + /var/log/openvpn.log + stop +} diff --git a/srv/ssh/.gitignore b/srv/ssh/.gitignore new file mode 100644 index 0000000..80b29eb --- /dev/null +++ b/srv/ssh/.gitignore @@ -0,0 +1,2 @@ +*_key +*_key.pub diff --git a/srv/ssh/authorized_keys b/srv/ssh/authorized_keys new file mode 100644 index 0000000..7c92cb9 --- /dev/null +++ b/srv/ssh/authorized_keys @@ -0,0 +1 @@ +# Put your SSH key here to get access to your container (as root) diff --git a/srv/ssh/permit_root_login.conf b/srv/ssh/permit_root_login.conf new file mode 100644 index 0000000..7a3ebbb --- /dev/null +++ b/srv/ssh/permit_root_login.conf @@ -0,0 +1 @@ +PermitRootLogin prohibit-password