Refactor nftables, vpn and transmission configuration

This commit is contained in:
sonny 2025-03-09 20:54:41 +01:00
parent 465a5d2887
commit dcbdfdc422
24 changed files with 292 additions and 192 deletions

View file

@ -5,6 +5,7 @@
{{ lan_ip }} {{ domain_name }} {{ hostname }}
{{ vpn_listen_address }} {{ vpn_domain }}
{{ vpn_media_listen_address }} {{ vpn_media_domain }}
{{ transmission_nginx_ip }} {{ transmission_domain }}
# The following lines are desirable for IPv6 capable hosts
#::1 localhost ip6-localhost ip6-loopback

View file

@ -4,7 +4,7 @@
Name={{ network_interface }}
[Network]
Address={{ lan_ip }}/24
Address={{ lan_ip }}/{{ lan_prefix }}
Gateway={{ lan_gateway }}
DNS={{ lan_dns }}
IgnoreCarrierLoss=true

View file

@ -1,12 +1,14 @@
# {{ ansible_managed }}
[Interface]
Address={{ vpn_peers.mobile.ip }}/24
Address={{ vpn_peers.mobile.ip }}/{{ vpn_prefix }}
DNS={{ vpn_listen_address }}
PrivateKey={{ lookup("file", vpn_peers.mobile.private_key_source_path) }}
[Peer]
PublicKey={{ lookup("file", vpn_server_public_key_source_path) }}
PresharedKey={{ lookup("file", vpn_peers.mobile.preshared_key_source_path) }}
AllowedIPs={{ vpn_listen_address }}/32
{% for ip in vpn_peers.mobile.allowed_ips %}
AllowedIPs={{ ip }}
{% endfor %}
Endpoint={{ domain_name }}:{{ vpn_port }}

View file

@ -13,7 +13,7 @@ PrivateKeyFile={{ vpn_server_key_path }}
[WireGuardPeer]
PublicKey={{ properties.public_key }}
PresharedKeyFile={{ properties.preshared_key_path }}
AllowedIPs={{ properties.ip }}/32
AllowedIPs={{ properties.ip }}
{% if not loop.last %}
{% endif %}

View file

@ -4,4 +4,4 @@
Name={{ vpn_interface }}
[Network]
Address={{ vpn_listen_address }}/{{ vpn_subnet }}
Address={{ vpn_listen_address }}/{{ vpn_prefix }}

View file

@ -1,12 +1,14 @@
# {{ ansible_managed }}
[Interface]
Address={{ vpn_media_peers.mobile_peer_1.ip }}/24
Address={{ vpn_media_peers.mobile_peer_1.ip }}/{{ vpn_media_prefix }}
DNS={{ vpn_media_listen_address }}
PrivateKey={{ lookup('file', vpn_media_peers.mobile_peer_1.private_key_source_path) }}
[Peer]
PublicKey={{ lookup('file', vpn_media_server_public_key_source_path) }}
PresharedKey={{ lookup('file', vpn_media_peers.mobile_peer_1.preshared_key_source_path) }}
AllowedIPs={{ vpn_media_listen_address }}/32
{% for ip in vpn_media_peers.mobile_peer_1.allowed_ips %}
AllowedIPs={{ ip }}
{% endfor %}
Endpoint={{ domain_name }}:{{ vpn_media_port }}

View file

@ -1,12 +1,14 @@
# {{ ansible_managed }}
[Interface]
Address={{ vpn_media_peers.mobile_peer_2.ip }}/24
Address={{ vpn_media_peers.mobile_peer_2.ip }}/{{ vpn_media_prefix }}
DNS={{ vpn_media_listen_address }}
PrivateKey={{ lookup('file', vpn_media_peers.mobile_peer_2.private_key_source_path) }}
[Peer]
PublicKey={{ lookup('file', vpn_media_server_public_key_source_path) }}
PresharedKey={{ lookup('file', vpn_media_peers.mobile_peer_2.preshared_key_source_path) }}
AllowedIPs={{ vpn_media_listen_address }}/32
{% for ip in vpn_media_peers.mobile_peer_2.allowed_ips %}
AllowedIPs={{ ip }}
{% endfor %}
Endpoint={{ domain_name }}:{{ vpn_media_port }}

View file

@ -1,12 +1,14 @@
# {{ ansible_managed }}
[Interface]
Address={{ vpn_media_peers.tv.ip }}/24
Address={{ vpn_media_peers.tv.ip }}/{{ vpn_media_prefix }}
DNS={{ vpn_media_listen_address }}
PrivateKey={{ lookup('file', vpn_media_peers.tv.private_key_source_path) }}
[Peer]
PublicKey={{ lookup('file', vpn_media_server_public_key_source_path) }}
PresharedKey={{ lookup('file', vpn_media_peers.tv.preshared_key_source_path) }}
AllowedIPs={{ vpn_media_listen_address }}/32
{% for ip in vpn_media_peers.tv.allowed_ips %}
AllowedIPs={{ ip }}
{% endfor %}
Endpoint={{ domain_name }}:{{ vpn_media_port }}

View file

@ -13,7 +13,7 @@ PrivateKeyFile={{ vpn_media_server_key_path }}
[WireGuardPeer]
PublicKey={{ properties.public_key }}
PresharedKeyFile={{ properties.preshared_key_path }}
AllowedIPs={{ properties.ip }}/32
AllowedIPs={{ properties.ip }}
{% if not loop.last %}
{% endif %}

View file

@ -4,4 +4,4 @@
Name={{ vpn_media_interface }}
[Network]
Address={{ vpn_media_listen_address }}/{{ vpn_media_subnet }}
Address={{ vpn_media_listen_address }}/{{ vpn_media_prefix }}

View file

@ -4,6 +4,7 @@
flush ruleset
table ip filter {
chain input {
type filter hook input priority 0; policy drop;
@ -19,32 +20,53 @@ table ip filter {
# allow icmp
ip protocol icmp accept
iifname "{{ network_interface }}" tcp dport {{ ssh_port }} accept comment "SSH"
iifname "{{ network_interface }}" tcp dport {{ forgejo_ssh_port }} accept comment "Forgejo SSH"
iifname "{{ network_interface }}" tcp dport { {{ http_port }}, {{ https_port }} } accept comment "HTTP/HTTPS"
iifname "{{ network_interface }}" tcp dport {{ transmission_port }} accept comment "Transmission"
iifname "{{ network_interface }}" udp dport {{ vpn_port }} accept comment "Wireguard"
iifname "{{ network_interface }}" udp dport {{ vpn_media_port }} accept comment "Wireguard media"
# TODO: create combined rule
iifname "{{ vpn_interface }}" tcp dport 53 ip saddr {{ vpn_source_range }} ip daddr {{ vpn_destination_range }} accept comment "DNS TCP"
iifname "{{ vpn_interface }}" udp dport 53 ip saddr {{ vpn_source_range }} ip daddr {{ vpn_destination_range }} accept comment "DNS UDP"
iifname "{{ vpn_interface }}" tcp dport { {{ http_port }}, {{ https_port }} } ip saddr {{ vpn_source_range }} ip daddr {{ vpn_destination_range }} accept comment "HTTP/HTTPS"
iifname "{{ vpn_interface }}" tcp dport {{ transmission_web_port }} ip saddr {{ vpn_source_range }} ip daddr {{ vpn_destination_range }} accept comment "Transmission Web"
iifname "{{ vpn_interface }}" tcp dport { {{ syncthing_gui_port }}, {{ syncthing_protocol_port }} } ip saddr {{ vpn_source_range }} ip daddr {{ vpn_destination_range }} accept comment "Syncthing"
iifname "{{ vpn_interface }}" tcp dport {{ mpd_port }} ip saddr {{ vpn_source_range }} ip daddr {{ vpn_destination_range }} accept comment "MPD"
iifname "{{ vpn_interface }}" tcp dport {{ mpd_http_stream_port }} ip saddr {{ vpn_source_range }} ip daddr {{ vpn_destination_range }} accept comment "MPD HTTP stream"
iifname "{{ vpn_interface }}" tcp dport {{ mpd_http_mobile_stream_port }} ip saddr {{ vpn_source_range }} ip daddr {{ vpn_destination_range }} accept comment "MPD HTTP mobile stream"
# TODO: create combined rule
iifname "{{ vpn_media_interface }}" tcp dport 53 ip saddr {{ vpn_media_source_range }} ip daddr {{ vpn_media_destination_range }} accept comment "DNS TCP"
iifname "{{ vpn_media_interface }}" udp dport 53 ip saddr {{ vpn_media_source_range }} ip daddr {{ vpn_media_destination_range }} accept comment "DNS UDP"
iifname "{{ vpn_media_interface }}" tcp dport {{ jellyfin_http_port }} ip saddr {{ vpn_media_source_range }} ip daddr {{ vpn_media_destination_range }} accept comment "Jellyfin HTTP"
iifname vmap {
{{ network_interface }} : goto wlan-chain,
{{ vpn_interface }} : goto vpn-chain,
{{ vpn_media_interface }} : goto media-vpn-chain
}
log
}
chain wlan-chain {
tcp dport {{ ssh_port }} accept comment "SSH"
tcp dport {{ forgejo_ssh_port }} accept comment "Forgejo SSH"
tcp dport { {{ http_port }}, {{ https_port }} } accept comment "HTTP/HTTPS"
udp dport {{ vpn_port }} accept comment "Wireguard"
udp dport {{ vpn_media_port }} accept comment "Wireguard media"
}
set vpn_set {
typeof ip saddr . ip daddr
flags interval
elements = { {{ vpn_subnet }} . {{ vpn_listen_address }}/{{ vpn_prefix }} }
}
chain vpn-chain {
meta l4proto { tcp, udp } th dport 53 ip saddr . ip daddr @vpn_set accept comment "DNS"
tcp dport { {{ http_port }}, {{ https_port }} } ip saddr . ip daddr @vpn_set accept comment "HTTP/HTTPS"
tcp dport { 80, 443 } ip saddr {{ vpn_subnet }} ip daddr {{ transmission_nginx_ip }} accept comment "Transmission Web"
tcp dport { {{ syncthing_gui_port }}, {{ syncthing_protocol_port }} } ip saddr . ip daddr @vpn_set accept comment "Syncthing"
tcp dport {{ mpd_port }} ip saddr . ip daddr @vpn_set accept comment "MPD"
tcp dport {{ mpd_http_stream_port }} ip saddr . ip daddr @vpn_set accept comment "MPD HTTP stream"
tcp dport {{ mpd_http_mobile_stream_port }} ip saddr . ip daddr @vpn_set accept comment "MPD HTTP mobile stream"
}
set vpn_media_set {
typeof ip saddr . ip daddr
flags interval
elements = { {{ vpn_media_subnet }} . {{ vpn_media_listen_address }}/{{ vpn_media_prefix }} }
}
chain media-vpn-chain {
meta l4proto { tcp, udp } th dport 53 ip saddr . ip daddr @vpn_media_set accept comment "DNS"
tcp dport {{ jellyfin_http_port }} ip saddr . ip daddr @vpn_media_set accept comment "Jellyfin HTTP"
}
}

View file

@ -1,73 +0,0 @@
{
"alt-speed-down": 50,
"alt-speed-enabled": false,
"alt-speed-time-begin": 540,
"alt-speed-time-day": 127,
"alt-speed-time-enabled": false,
"alt-speed-time-end": 1020,
"alt-speed-up": 50,
"bind-address-ipv4": "",
"bind-address-ipv6": "",
"blocklist-enabled": false,
"blocklist-url": "http://www.example.com/blocklist",
"cache-size-mb": 4,
"dht-enabled": true,
"download-dir": "{{ transmission_download_folder }}",
"download-limit": 100,
"download-limit-enabled": 0,
"download-queue-enabled": true,
"download-queue-size": 5,
"encryption": 1,
"idle-seeding-limit": 30,
"idle-seeding-limit-enabled": false,
"incomplete-dir": "{{ transmission_incomplete_folder }}",
"incomplete-dir-enabled": true,
"lpd-enabled": true,
"max-peers-global": 200,
"message-level": 1,
"peer-congestion-algorithm": "",
"peer-id-ttl-hours": 6,
"peer-limit-global": 200,
"peer-limit-per-torrent": 50,
"peer-port": {{ transmission_port }},
"peer-port-random-high": 65535,
"peer-port-random-low": 49152,
"peer-port-random-on-start": false,
"peer-socket-tos": "default",
"pex-enabled": true,
"port-forwarding-enabled": true,
"preallocation": 1,
"prefetch-enabled": true,
"queue-stalled-enabled": true,
"queue-stalled-minutes": 30,
"ratio-limit": {{ transmission_ratelimit_ratio }},
"ratio-limit-enabled": false,
"rename-partial-files": true,
"rpc-authentication-required": false,
"rpc-bind-address": "{{ vpn_listen_address }}",
"rpc-enabled": true,
"rpc-host-whitelist": "",
"rpc-host-whitelist-enabled": false,
"rpc-password": "{6d8c6eafffb8ae980db6f2d7e2c36dbf8d111479Z/5l3mfq",
"rpc-port": {{ transmission_web_port }},
"rpc-url": "/transmission/",
"rpc-username": "transmission",
"rpc-whitelist": "127.0.0.1, {{ vpn_listen_address[:-1] }}*",
"rpc-whitelist-enabled": true,
"scrape-paused-torrents-enabled": true,
"script-torrent-done-enabled": false,
"script-torrent-done-filename": "",
"seed-queue-enabled": false,
"seed-queue-size": 10,
"speed-limit-down": 100,
"speed-limit-down-enabled": false,
"speed-limit-up": 5,
"speed-limit-up-enabled": true,
"start-added-torrents": true,
"trash-original-torrent-files": false,
"umask": 18,
"upload-limit": 1,
"upload-limit-enabled": 1,
"upload-slots-per-torrent": 14,
"utp-enabled": true
}

View file

@ -0,0 +1,13 @@
{
"download-dir": "/app/downloads",
"incomplete-dir": "/app/incomplete_downloads",
"incomplete-dir-enabled": true,
"peer-port": {{ transmission_peer_port }},
"rpc-port": {{ transmission_web_port }},
"rpc-host-whitelist-enabled": false,
"rpc-whitelist-enabled": false,
"ratio-limit": {{ transmission_ratelimit_ratio }},
"ratio-limit-enabled": true,
"speed-limit-up": 5,
"speed-limit-up-enabled": true
}

View file

@ -0,0 +1,32 @@
# {{ ansible_managed }}
networks:
transmission-net:
ipam:
config:
- subnet: '{{ transmission_subnet }}'
services:
transmission:
image: alpine:latest
build:
context: .
dockerfile: Dockerfile
restart: always
networks:
transmission-net:
volumes:
- {{ transmission_download_dir }}:/app/downloads
- {{ transmission_incomplete_dir }}:/app/incomplete_downloads
- {{ transmission_app_dir }}/config:/app/config
nginx:
image: nginx:mainline-alpine
depends_on:
- transmission
restart: always
networks:
transmission-net:
ipv4_address: '{{ transmission_nginx_ip }}'
volumes:
- '{{ transmission_app_dir }}/nginx.conf.d:/etc/nginx/conf.d'

View file

@ -0,0 +1,20 @@
# {{ ansible_managed }}
upstream transmission-upstream {
server transmission:9091;
}
server {
listen 80;
server_name {{ transmission_domain }};
location / {
proxy_read_timeout 300;
proxy_pass_header X-Transmission-Session-Id;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://transmission-upstream;
}
}