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