So the idea is to have a home router listen on port 443 for HTTPS and SSH connection simultanously, route HTTPS traffic to a local homeassistant instance and SSH traffic to a local SSH server:

Port 443 is choosen as SSH port, because it shows the fewest problems in some network scenarios anything else beside standard web ports are blocked. The connection to home assistant is TLS secured. As TLS indicates the server name in the unencrypted protocol part (server name indication, SNI) its possible to add more https endpoints to the picture.
As SSH does not use the TLS protocol there is no way for Traefik to differentiate several SSH hosts or differentiate SSH traffic from any other non TLS traffic. The whole thing is possible by use of the HostSNI(*) matching. If you need more advanced traffic handling you might be interested in HAProxy which is able to guess traffic by inspecting the first few protocol bytes.
Setup Traefik
The Traefik setup consists of three files:
docker-compose.yml
: We use a dockerized Traefiktraefik.yml
: Static configurationconfig/service.yml
: Dynamic configuration via files
docker-compose.yml
First create a docker compose file:
version: '3'
services:
reverse-proxy:
image: traefik:v2.2
command: --configFile=/etc/traefik.yml
restart: always
ports:
- "80:80"
- "443:443"
# The Web UI (enabled by api.insecure=true)
- "8080:8080"
volumes:
# Access to docker is disabled, we use file provider, no docker provider
#- /var/run/docker.sock:/var/run/docker.sock
- ./config/:/config/:ro
- ./traefik.yml:/etc/traefik.yml:ro
- ./traefik_letsencrypt/:/etc/traefik_letsencrypt/
The configuration is readonly as it is not going to be changed from within docker.
As we don’t use the docker config provider the socket access is disabled. traefik_letsencrypt
is a folder which needs to be created on the local host before starting the container. In this folder traefik stores its letsencrypt certificates. Its mounted to the host to be able to backup the letsencrypt files when updating the conatiner. Alternatively you may want to use a volume.
Please note that I used versioned image names and not the latest tag as adviced in the article Dockerfile best practices.
traefik.yml
traefik.yml is the static configuration:
entryPoints:
web:
address: ":80"
web-secure:
address: ":443"
accessLog: {}
log:
level: INFO
providers:
file:
directory: /config
watch: true
api:
insecure: true
certificatesResolvers:
letsencrypt:
acme:
email: "add_your_email_here"
storage: "/etc/traefik_letsencrypt/traefik_letsencrypt.json"
#caServer: "https://acme-staging-v02.api.letsencrypt.org/directory"
httpChallenge:
entryPoint: web
To test the setup first use the staging acme server. The non staging server has a rate limit.
We need to listen to port 80 to be able to serve the http challenge.
config/service.yml
tcp:
routers:
router-ssh:
entryPoints:
- web-secure
rule: HostSNI(`*`)
service: service-ssh
services:
service-ssh:
loadBalancer:
servers:
- address: add_ssh_hostname_here:22
http:
routers:
router-homeassistant:
rule: Host(`internal-homeassistant-hostname`, `homeassistant.example.com`)
tls:
certResolver: letsencrypt
domains:
- main: ".example.com"
service: service-homeassistant
entryPoints:
- web
router-homeassistant-https:
rule: Host(`internal-homeassistant-hostname`, `homeassistant.example.com`)
tls:
certResolver: letsencrypt
domains:
# Explicitely set the domain name so that letsencrypt does
# try to request certificate for internal hostname accidently
- main: "homeassistant.example.com"
service: service-homeassistant
entryPoints:
- web-secure
tls: {}
services:
service-homeassistant:
loadBalancer:
servers:
- url: http://internal-homeassistant-hostname:8123
Things to adapt:
add_ssh_hostname_here
: The internal hostname for the SSH serverinternal-homeassistant-hostname
: The internal hostname for the homeassistant installation, so you can test the setup in the local networkhomeassistant.example.com
: The external hostname for the homeassistant installation
At this point you should be able to start the Traefic container:
mkdir traefik_letsencrypt
docker-compose up -d
Setup Home Assistant
As final step home assistant needs to be configured to accept the X_Forwarded_For
HTTP header. I add the whole docker default IP range here:
http:
use_x_forwarded_for: true
trusted_proxies:
- 127.0.0.1
# docker IP range:
- 172.0.0.0/8
Please not that the setting base_url
is deprecated since homeassistant release 0.110. You don’t need to use the new settings external_url
and internal_url
as homeassistant will automatically guess, my setup is working so far without.