Skip to content

Latest commit

 

History

History
191 lines (127 loc) · 10.6 KB

README.md

File metadata and controls

191 lines (127 loc) · 10.6 KB

Docker Home Server

docker-based server using custom subdomains over https

Initial installation

Clone this repo, or fork it first if you want to! Be sure to install the pre-commit hook following the instructions in that file. It will strip out the values from .env and generate a env.sample file with only the keys. It also will strip emails and the pilot token from traefik/traefik.yml and generate a traefik/traefik.yml.sample.

Motivation

  • host each service as a subdomain of a personal domain with cloudflare/letsencrypt
  • run public maintained images with no modifications
  • require minimal configuration and setup

Containers

  • Netdata - Troubleshoot slowdowns and anomalies in your infrastructure with thousands of metrics, interactive visualizations, and insightful health alarms.
  • Traefik - modern HTTP reverse proxy and load balancer that makes deploying microservices easy.
  • ESPHome - system to control your ESP8266/ESP32 by simple yet powerful configuration files and control them remotely through Home Automation systems.
  • code-server - Run VS Code on any machine anywhere and access it in the browser.
  • personal-site - my personal site, built with Phoenix LiveView.
  • AdGuard Home - network-wide software for blocking ads & tracking.
  • Watchtower - container-based solution for automating Docker container base image updates.
  • Portainer CE - lightweight ‘universal’ management GUI that can be used to easily manage Docker, Swarm, Kubernetes and ACI environments.
  • Unifi controller - powerful, enterprise wireless software engine ideal for high-density client deployments requiring low latency and high uptime performance.
  • Bookstack - simple, self-hosted, easy-to-use platform for organising and storing information.
  • Wireguard - extremely simple yet fast and modern VPN that utilizes state-of-the-art cryptography.
  • fail2ban - scans log files and bans IPs that show the malicious signs -- too many password failures, seeking for exploits, etc. *
  • mosquitto - an open source (EPL/EDL licensed) message broker that implements the MQTT protocol versions 5.0, 3.1.1 and 3.1. *
  • adguardhome-sync- Synchronize AdGuardHome config to a replica instance. *
  • dashy - 🚀 A self-hostable personal dashboard built for you. Includes status-checking, widgets, themes, icon packs, a UI editor and tons more!
  • whoogle - A self-hosted, ad-free, privacy-respecting metasearch engine
  • edison - a little discord bot I run for some random tasks *
  • lemmy - The official web app for lemmy **

*not exposed

** including the lemmy, lemmy-ui, postgres and pictrs containers

Requirements

  • dedicated server or PC
  • docker and docker-compose
  • personal domain with configurable sub-domains (eg. netdata.example.com)

Configuration (exposed to internet)

Copy env.sample to .env and populate all fields in the COMMON and EXTERNAL sections.

Copy traefik/traefik.yml.sample to traefik/traefik.yml and fill in the ACME_EMAIL and PILOT_TOKEN variables (the pilot token is an optional property for Traefik Pilot, feel free to remove the section all-together).

Be sure to port forward :443 on your router to get access externally.

It is recommended to use the staging cert resolver initially to avoid any potential rate limits from Let's Encrypt for any misconfigured services.

Other configuration / Notes

Wireguard

You will need to forward port 51820/udp, and be sure that it is not proxied through Cloudflare, as the CF proxy only allows HTTP traffic.

On initial startup, the container should generate 5 peer QR codes. You can view them with:

docker-compose logs -tf wireguard

AdGuard

On startup for the intial configuration the web interface will bind to port 3000. If you have local access to the server, you can access it there (ie. 10.0.0.10:3000), and then be sure that it does not bind to port 80, but instead 3333 (defined in external labels for loadbalancer).

If you do not have direct network access to the server, you can launch the first time with the loadbalancer port to :3000, configure it to bind to 3333, then restart with the original port in the label configuration.

I set up a replica instance on a Raspberry Pi, so that I can have a backup DNS in case I need to take the server down for some reason. Just use the default config file (${CONFIG_DIR}/adguard/adguardhome-sync.yaml) from the adguardhome-sync repo entering in the username and password for each, and you should be good to go! (Note: I turn off the stats and query log syncing because I find it useful to see what hits the fallback server)

DNS/Networks

I have set up only the single default network, and assigned it a subnet so that I can assign an IP to AdGuard for containers to reference as DNS. This allows more granular insight into container network activity, as they will no longer be agregated at the host level.

In order to see container names in AdGuard, set [/69.20.172.in-addr.arpa/]127.0.0.11 in the "Private DNS servers" field in AdGuard. This tells AdGuard to send PTR requests in the docker network to the internal docker DNS resolver. The main downside here is that any containers that run in "host" mode (in this case, just Home Assistant) will show up in AdGuard as the subnet gateway address for the internal network.

I have a couple of containers in "host" networking mode, this is to mainly make a few things work a little cleaner (UPnP, mDNS, DNS over IPv6). You can turn these off if you don't want any of these.

Home assistant

This stack utilizes Home Assistant via a VM managed in proxmox. I set it up using this script from tteck. The files in traefik/conf_example with the info filled in should get you going to route to the external host.

Set the following into ${CONFIG_DIR}/home/configuration.yaml: Note: for running HA at a different IP, you will need to include the server IP running this compose stack where the <YOUR SERVER IP> is.

http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 127.0.0.1
    - <YOUR SERVER IP>
    # cloudflare IPs for skipping in X-Forwarded-For header
    - 103.21.244.0/22
    - 103.22.200.0/22
    - 103.31.4.0/22
    - 104.16.0.0/13
    - 104.24.0.0/14
    - 108.162.192.0/18
    - 131.0.72.0/22
    - 141.101.64.0/18
    - 162.158.0.0/15
    - 172.64.0.0/13
    - 173.245.48.0/20
    - 188.114.96.0/20
    - 190.93.240.0/20
    - 197.234.240.0/22
    - 198.41.128.0/17

If you have any devices (in my case a Xiaomi Air Purifier 3H) that don't seem to be able to discover, you may want to put them on the same VLAN. I did this by running the following commands:

echo -e '[keyfile]\nunmanaged-devices=none' | sudo tee -a /etc/NetworkManager/conf.d/10-globally-managed-devices.conf
sudo nmcli con add type vlan con-name enp0s25@vlan10 dev enp0s25 id 10
sudo service network-manager restart

I had previously set up a VLAN 10 tagged port on the switch for my server, with the base network as the secure network.

This should allow device discovery with the devices being in separate VLANs

Fail2Ban

This is set up to ban the real client IP in cloudflare, as well as the local iptables just for funsies (though my firewall does that job already). It is set up for all traefik applications by reading the access log, but home assistant for some reason decided to return a 200 even on a failed login. So there is another jail that reads the home assistant logs as well.

Copy fail2ban/cloudflare.conf.example to fail2ban/cloudflare.conf and fill in the cftoken and cfuser fields with the api key and email you use for cloudflare login.

Deployment

Pull and deploy containers with docker-compose.

docker-compose pull
docker-compose up -d

Cloudflare

This configuration is set up for a domain proxied through Cloudflare. You can remove this by removing the CF_API_KEY and CF_API_EMAIL environment cariables, as well as the entrypoints.websecure.forwardedHeaders.trustedIPs config in traefik/traefik.yml.

Middlewares

ipallowlist

If you are using mediaserver locally and are not exposing any ports to the Internet, you can skip this section or set IPALLOWLIST=0.0.0.0/0,::/0 in your .env file.

Set the IPALLOWLIST to only IP ranges that we want to explictly allow access.

This functionality can be enabled/disabled per service in docker-compose.external.yml with the ipallowlist middleware.

traefik-forward-auth

Uses traefik-forward-auth to handle OAuth through google. Just follow the setup instructions there, and fill in the relevant fields in env.

This stack uses the TFA host mode, which is hosted at AUTH_HOST. Thus, the redirect URI you enter into google will be https://<AUTH_HOST>/_oauth. This way you don't need to make an individual entry for every subdomain.

basicauth

This functionality can be enabled/disabled per service in docker-compose.external.yml with the basicauth middleware.

Users can be added to basic auth in 2 ways. If both methods are used they are merged and the htpasswd file takes priority.

  1. Add users in your .env file with the BASICAUTH_USERS variable.

  2. Add users via htpasswd file in the traefik service.

The first user added requires htpasswd -c in order to create the password file. Subsequent users should only use htpasswd to avoid overwriting the file.

docker-compose exec traefik apk add --no-cache apache2-utils
docker-compose exec traefik htpasswd -c /etc/traefik/.htpasswd <user1>
docker-compose exec traefik htpasswd /etc/traefik/.htpasswd <user2>

Acknowledgments

Started with setup from klutchell's mediaserver

Buy them a beer

Special thanks to brettinternet for the help and inspiration along the way.