feat: add Let's Encrypt SSL certificates and Tailscale instructions

This commit is contained in:
Adrien Poupa 2022-12-30 19:05:22 -05:00
parent 02d3208c51
commit 3e1e94817a
6 changed files with 91 additions and 10 deletions

View File

@ -7,3 +7,8 @@ PIA_LOCATION=ca
PIA_USER= PIA_USER=
PIA_PASS= PIA_PASS=
PIA_LOCAL_NETWORK="192.168.0.0/16" PIA_LOCAL_NETWORK="192.168.0.0/16"
HOSTNAME=
LETS_ENCRYPT_EMAIL=
CLOUDFLARE_EMAIL=
CLOUDFLARE_DNS_API_TOKEN=
CLOUDFLARE_ZONE_API_TOKEN=

2
.gitignore vendored
View File

@ -14,3 +14,5 @@
!/pia/.gitkeep !/pia/.gitkeep
/pia-shared /pia-shared
!/pia-shared/.gitkeep !/pia-shared/.gitkeep
/letsencrypt
!/letsencrypt/.gitkeep

View File

@ -69,7 +69,7 @@ place in the VPN container, the hostname for qBittorrent is the hostname of the
The indexers are configured through Prowlarr. They synchronize automatically to Radarr and Sonarr. The indexers are configured through Prowlarr. They synchronize automatically to Radarr and Sonarr.
Radarr and Sonarr may then be added via Settongs > Apps. The Prowlarr server is `http://prowlarr:9696/prowlarr`, the Radarr server Radarr and Sonarr may then be added via Settings > Apps. The Prowlarr server is `http://prowlarr:9696/prowlarr`, the Radarr server
is `http://radarr:7878/radarr` and Sonarr `http://sonarr:8989/sonarr`: is `http://radarr:7878/radarr` and Sonarr `http://sonarr:8989/sonarr`:
![](https://cdn.poupa.net/uploads/2022/03/sonarr.png) ![](https://cdn.poupa.net/uploads/2022/03/sonarr.png)
@ -100,3 +100,60 @@ Applications can be added in Items > Add. The URLs should be the static IP, ie:
for example. for example.
![](https://cdn.poupa.net/uploads/2022/03/homepage.png) ![](https://cdn.poupa.net/uploads/2022/03/homepage.png)
## Traefik and SSL Certificates
While you can use the private IP to access your NAS, how cool would it be for it to be accessible through a subdomain
with a valid SSL certificate?
Traefik makes this trivial by using Let's Encrypt and one of its
[supported ACME challenge providers](https://doc.traefik.io/traefik/https/acme/).
Let's assume we are using `nas.domain.com` as custom subdomain.
The idea is to create an A record pointing to the private IP of the NAS, `192.168.0.10` for example:
```
nas.domain.com. 1 IN A 192.168.0.10
```
The record will be publicly exposed but not resolve given this is a private IP.
Given the NAS is not accessible from the internet, we need to do a dnsChallenge.
Here we will be using CloudFlare, but the mechanism will be the same for all DNS providers
baring environment variable changes, see the Traefik documentation above and [Lego's documentation](https://go-acme.github.io/lego/dns/).
Then, we need to fill the `.env` entries:
- `HOSTNAME`: the subdomain used, `nas.domain.com` for example
- `LETS_ENCRYPT_EMAIL`: e-mail address used to send expiration notifications
- `CLOUDFLARE_EMAIL`: Account email
- `CLOUDFLARE_DNS_API_TOKEN`: API token with DNS:Edit permission
- `CLOUDFLARE_ZONE_API_TOKEN`: API token with Zone:Read permission
If you want to test your configuration first, use the Let's Encrypt staging server by uncommenting this:
```
#- --certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
```
If it worked, you will see the staging certificate at https://nas.domain.com.
You may remove the `./letsencrypt/acme.json` file and restart the services to issue the real certificate.
### Accessing from the outside
If we want to make it reachable from outside the network without opening ports or exposing it to the internet, I found
[Tailscale](https://tailscale.com/) to be a great solution: create a network, run the client on both the NAS and the device
you are connecting from, and they will see each other.
In this case, the A record should point to the IP Tailscale assigned to the NAS, eg `100.xxx.xxx.xxx`:
```
nas.domain.com. 1 IN A 100.xxx.xxx.xxx
```
See [here](https://tailscale.com/kb/installation/) for installation instructions.
However, this means you will always need to be connected to Tailscale to access your NAS, even locally.
This can be remedied by overriding the DNS entry for the NAS domain like `192.168.0.10 nas.domain.com`
in your local DNS resolver such as Pi-Hole.
This way, when connected to the local network, the NAS is accessible directly from the private IP,
and from the outside you need to connect to Tailscale first, then the NAS domain will be accessible.

View File

@ -3,6 +3,7 @@
After searching for the perfect NAS solution, I realized what I wanted could be achieved After searching for the perfect NAS solution, I realized what I wanted could be achieved
with some Docker containers on a vanilla Linux box. The result is an opinionated Docker Compose configuration capable of with some Docker containers on a vanilla Linux box. The result is an opinionated Docker Compose configuration capable of
browsing indexers to retrieve media resources and downloading them through a Wireguard VPN with port forwarding. browsing indexers to retrieve media resources and downloading them through a Wireguard VPN with port forwarding.
SSL certificates and remote access through Tailscale are supported.
## Applications ## Applications
@ -54,6 +55,5 @@ for some indexers in Prowlarr
- [Jackett](https://github.com/Jackett/Jackett): API Support for your favorite torrent trackers, as a Prowlarr replacement - [Jackett](https://github.com/Jackett/Jackett): API Support for your favorite torrent trackers, as a Prowlarr replacement
- [Plex](https://www.plex.tv/): Plex Media Server - [Plex](https://www.plex.tv/): Plex Media Server
- [Pi-hole](https://pi-hole.net/): DNS that blocks ads - [Pi-hole](https://pi-hole.net/): DNS that blocks ads
- Use a domain name and Let's Encrypt certificate to get SSL - Expose services with CloudFlare Tunnel if Tailscale is not enough
- Expose services with CloudFlare Tunnel
- you tell me! - you tell me!

View File

@ -4,6 +4,11 @@ services:
image: traefik:v2.9 image: traefik:v2.9
container_name: traefik container_name: traefik
restart: always restart: always
environment:
- CLOUDFLARE_EMAIL=${CLOUDFLARE_EMAIL}
- CLOUDFLARE_DNS_API_TOKEN=${CLOUDFLARE_DNS_API_TOKEN}
- CLOUDFLARE_ZONE_API_TOKEN=${CLOUDFLARE_ZONE_API_TOKEN}
- LETS_ENCRYPT_EMAIL=${LETS_ENCRYPT_EMAIL}
command: command:
- --providers.docker=true - --providers.docker=true
- --providers.docker.exposedbydefault=false - --providers.docker.exposedbydefault=false
@ -12,10 +17,17 @@ services:
- --entrypoints.web.http.redirections.entryPoint.to=web-secure - --entrypoints.web.http.redirections.entryPoint.to=web-secure
- --entrypoints.web.http.redirections.entryPoint.scheme=https - --entrypoints.web.http.redirections.entryPoint.scheme=https
- --entrypoints.web.http.redirections.entrypoint.permanent=true - --entrypoints.web.http.redirections.entrypoint.permanent=true
- --certificatesresolvers.myresolver.acme.dnschallenge=true
- --certificatesresolvers.myresolver.acme.dnschallenge.provider=cloudflare
# Uncomment to test your configuration by using Let's Encrypt staging certificates
#- --certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
- --certificatesresolvers.myresolver.acme.email=${LETS_ENCRYPT_EMAIL}
- --certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json
ports: ports:
- "80:80" - "80:80"
- "443:443" - "443:443"
volumes: volumes:
- ./letsencrypt:/letsencrypt
- "/var/run/docker.sock:/var/run/docker.sock:ro" - "/var/run/docker.sock:/var/run/docker.sock:ro"
sonarr: sonarr:
image: lscr.io/linuxserver/sonarr image: lscr.io/linuxserver/sonarr
@ -29,9 +41,10 @@ services:
restart: always restart: always
labels: labels:
- traefik.enable=true - traefik.enable=true
- traefik.http.routers.sonarr.rule=PathPrefix(`/sonarr`) - traefik.http.routers.sonarr.rule=(Host(`${HOSTNAME}`) && PathPrefix(`/sonarr`) || PathPrefix(`/sonarr`))
- traefik.http.services.sonarr.loadbalancer.server.port=8989
- traefik.http.routers.sonarr.tls=true - traefik.http.routers.sonarr.tls=true
- traefik.http.routers.sonarr.tls.certresolver=myresolver
- traefik.http.services.sonarr.loadbalancer.server.port=8989
radarr: radarr:
image: lscr.io/linuxserver/radarr image: lscr.io/linuxserver/radarr
container_name: radarr container_name: radarr
@ -44,8 +57,9 @@ services:
restart: always restart: always
labels: labels:
- traefik.enable=true - traefik.enable=true
- traefik.http.routers.radarr.rule=PathPrefix(`/radarr`) - traefik.http.routers.radarr.rule=(Host(`${HOSTNAME}`) && PathPrefix(`/radarr`) || PathPrefix(`/radarr`))
- traefik.http.routers.radarr.tls=true - traefik.http.routers.radarr.tls=true
- traefik.http.routers.radarr.tls.certresolver=myresolver
- traefik.http.services.radarr.loadbalancer.server.port=7878 - traefik.http.services.radarr.loadbalancer.server.port=7878
prowlarr: prowlarr:
image: lscr.io/linuxserver/prowlarr:develop image: lscr.io/linuxserver/prowlarr:develop
@ -58,8 +72,9 @@ services:
restart: always restart: always
labels: labels:
- traefik.enable=true - traefik.enable=true
- traefik.http.routers.prowlarr.rule=PathPrefix(`/prowlarr`) - traefik.http.routers.prowlarr.rule=(Host(`${HOSTNAME}`) && PathPrefix(`/prowlarr`) || PathPrefix(`/prowlarr`))
- traefik.http.routers.prowlarr.tls=true - traefik.http.routers.prowlarr.tls=true
- traefik.http.routers.prowlarr.tls.certresolver=myresolver
- traefik.http.services.prowlarr.loadbalancer.server.port=9696 - traefik.http.services.prowlarr.loadbalancer.server.port=9696
qbittorrent: qbittorrent:
image: lscr.io/linuxserver/qbittorrent:4.5.0-libtorrentv1 image: lscr.io/linuxserver/qbittorrent:4.5.0-libtorrentv1
@ -78,8 +93,9 @@ services:
- vpn - vpn
labels: labels:
- traefik.enable=true - traefik.enable=true
- traefik.http.routers.qbittorrent.rule=PathPrefix(`/qbittorrent`) - traefik.http.routers.qbittorrent.rule=(Host(`${HOSTNAME}`) && PathPrefix(`/qbittorrent`) || PathPrefix(`/qbittorrent`))
- traefik.http.routers.qbittorrent.tls=true - traefik.http.routers.qbittorrent.tls=true
- traefik.http.routers.qbittorrent.tls.certresolver=myresolver
- traefik.http.services.qbittorrent.loadbalancer.server.port=8080 - traefik.http.services.qbittorrent.loadbalancer.server.port=8080
- traefik.http.routers.qbittorrent.middlewares=qbittorrent-strip-slash,qbittorrent-stripprefix - traefik.http.routers.qbittorrent.middlewares=qbittorrent-strip-slash,qbittorrent-stripprefix
# https://github.com/qbittorrent/qBittorrent/issues/5693#issuecomment-552146296 # https://github.com/qbittorrent/qBittorrent/issues/5693#issuecomment-552146296
@ -130,9 +146,10 @@ services:
restart: always restart: always
labels: labels:
- traefik.enable=true - traefik.enable=true
- traefik.http.routers.heimdall.rule=PathPrefix(`/`) - traefik.http.routers.heimdall.rule=(Host(`${HOSTNAME}`) && PathPrefix(`/`) || PathPrefix(`/`))
- traefik.http.services.heimdall.loadbalancer.server.port=80
- traefik.http.routers.heimdall.tls=true - traefik.http.routers.heimdall.tls=true
- traefik.http.routers.heimdall.tls.certresolver=myresolver
- traefik.http.services.heimdall.loadbalancer.server.port=80
watchtower: watchtower:
image: containrrr/watchtower image: containrrr/watchtower
container_name: watchtower container_name: watchtower

0
letsencrypt/.gitkeep Normal file
View File