summaryrefslogtreecommitdiff
path: root/hosts/poweredge/transmission.nix
blob: cded95dd40a297fdd63e9a2a7ea19e31a66c244f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
{ config, pkgs, ... }: {
	# Secrets
	sops.secrets.transmission-ovpn-config = { sopsFile = ./resources/secrets/transmission.yaml; key = "ovpn-config"; };
	sops.secrets.transmission-ovpn-auth   = { sopsFile = ./resources/secrets/transmission.yaml; key = "ovpn-auth"; };

	# Container
	containers.transmission = let
		home = "/var/lib/transmission";
		download-dir = "${home}/complete";
		incomplete-dir = "${home}/incomplete";
	in {
		autoStart = true;
		privateNetwork = true;
		enableTun = true;  # OpenVPN requires
		hostBridge = "br-lan0";
		localMacAddress = "02:00:00:00:00:07";

		# Download dirs
		bindMounts = {
			"${download-dir}" = {
				hostPath = "/media/ingens/media/.incomplete";
				isReadOnly = false;
			};
			"${incomplete-dir}" = {
				hostPath = "/media/ingens/media/.complete";
				isReadOnly = false;
			};
		};

		# Bind secrets
		bindMounts."/run/secrets/ovpn-config.ovpn" = {
			hostPath = config.sops.secrets.transmission-ovpn-config.path;
			isReadOnly = true;
		};
		bindMounts."/run/secrets/ovpn-auth" = {
			hostPath = config.sops.secrets.transmission-ovpn-auth.path;
			isReadOnly = true;
		};

		config = { lib, config, ... }: {
			# Network
			networking.enableIPv6 = false;  # Prevent ip leaks
			networking.interfaces.eth0.useDHCP = true;
			networking.firewall.interfaces = {
				eth0.allowedTCPPorts = [ 80 ];  # RPC interface
				# Torrent ports
				tun0 = {
					allowedTCPPorts = [ 51413 ];
					allowedUDPPorts = [ 51413 ];
				};
			};

			# Transmission
			services.transmission = {
				inherit home;
				enable = true;

				settings = {
					inherit download-dir incomplete-dir;
				};
			};

			# TODO remove (#258793)
			systemd.services.transmission.serviceConfig = {
				RootDirectoryStartOnly = lib.mkForce null;
				RootDirectory = lib.mkForce null;
			};

			# Reverse proxy
			services.caddy = {
				enable = true;
				virtualHosts.":80".extraConfig = ''
					reverse_proxy localhost:9091
				'';
			};

			# OpenVPN
			services.openvpn.servers.main = {
				config = ''
					config /run/secrets/ovpn-config.ovpn
					auth-user-pass /run/secrets/ovpn-auth
				'';
				autoStart = true;
				updateResolvConf = true;
			};

			# VPN killswitch
			networking.firewall.extraCommands = ''
				# Get domain name host and port from ovpn config
				SERVER_HOST=$(${pkgs.gawk}/bin/awk '/^remote /{print $2;exit}' /run/secrets/ovpn-config.ovpn)
				SERVER_PORT=$(${pkgs.gawk}/bin/awk '/^remote /{print $3;exit}' /run/secrets/ovpn-config.ovpn)

				# Resolve server ip from host
				while [ -z "$SERVER_IP" ]; do
					sleep 3
					SERVER_IP=$(${pkgs.getent}/bin/getent hosts "$SERVER_HOST" 2>/dev/null | ${pkgs.gawk}/bin/awk '{print $1}')
					echo "SERVER_IP: $SERVER_IP"
				done

				# Only allow out traffic from tun0
				${pkgs.iptables}/bin/iptables -P OUTPUT DROP
				${pkgs.iptables}/bin/iptables -A OUTPUT -o lo -j ACCEPT
				${pkgs.iptables}/bin/iptables -A OUTPUT -o tun0 -j ACCEPT
				${pkgs.iptables}/bin/iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT  # Exception: allow established connections
				${pkgs.iptables}/bin/iptables -A OUTPUT -p udp -d "$SERVER_IP" --dport "$SERVER_PORT" -j ACCEPT

				# Allow DNS
				DNS_IP=$(${pkgs.gawk}/bin/awk '/^nameserver /{print $2; exit}' /etc/resolv.conf)
				${pkgs.iptables}/bin/iptables -A OUTPUT -o eth0 -p udp -d "$DNS_IP" --dport 53 -j ACCEPT
				${pkgs.iptables}/bin/iptables -A OUTPUT -o eth0 -p tcp -d "$DNS_IP" --dport 53 -j ACCEPT

				# Allow transmission RPC
			'';

			system.stateVersion = "26.05";
		};
	};
}