router-with-l2tp.nix (6760B)
1 # A demonstration config for a home/soho router with PPPoE upstream 2 # and fallback to an L2TP tunnel over a USB WWAN device 3 4 { 5 config, 6 pkgs, 7 lib, 8 ... 9 }: 10 let 11 secrets = import ./extneder-secrets.nix; 12 rsecrets = import ./rotuer-secrets.nix; 13 14 # https://support.aa.net.uk/Category:Incoming_L2TP says: 15 # "Please use the DNS name (l2tp.aa.net.uk) instead of hardcoding an 16 # IP address; IP addresses can and do change. If you have to use an 17 # IP, use 194.4.172.12, but do check the DNS for l2tp.aa.net.uk in 18 # case it changes." 19 20 # but (1) we don't want to use the wwan stick's dns as our main 21 # resolver: it's provided by some mobile ISP and they aren't 22 # necessarily the best at providing unfettered services without 23 # deciding to do something weird; (2) it's not simple to arrange 24 # that xl2tpd gets a different resolver than every other process; 25 # (3) there's no way to specify an lns address to xl2tpd at runtime 26 # except by rewriting its config file. So what we will do is lookup 27 # the lns hostname using the mobile ISP's dns server and then refuse 28 # to start l2tp unless the expected lns address is one of the 29 # addresses returned. I think this satisfies "do check the DNS" 30 31 lns = { 32 hostname = "l2tp.aaisp.net.uk"; 33 address = "194.4.172.12"; 34 }; 35 36 inherit (pkgs.liminix.services) oneshot target; 37 inherit (pkgs.liminix) outputRef; 38 svc = config.system.service; 39 wirelessConfig = { 40 country_code = "GB"; 41 inherit (rsecrets) wpa_passphrase; 42 wmm_enabled = 1; 43 }; 44 in 45 rec { 46 boot = { 47 tftp = { 48 serverip = "10.0.0.1"; 49 ipaddr = "10.0.0.8"; 50 }; 51 }; 52 53 imports = [ 54 ../modules/wwan 55 ../modules/network 56 ../modules/ssh 57 ../modules/usb.nix 58 ../modules/ppp 59 ../modules/round-robin 60 ../modules/health-check 61 ../modules/secrets 62 ../modules/profiles/gateway.nix 63 ../modules/dhcp4c 64 65 ]; 66 hostname = "thing"; 67 68 services.wan-address-for-secrets = svc.network.address.build { 69 interface = config.hardware.networkInterfaces.wan; 70 family = "inet"; 71 address = "10.0.0.10"; 72 prefixLength = 24; 73 }; 74 75 services.secrets = svc.secrets.outboard.build { 76 name = "secret-service"; 77 url = "http://10.0.0.1/liminix/examples/real-secrets.json"; 78 username = "demo"; 79 password = "demo"; 80 interval = 5; 81 dependencies = [ services.wan-address-for-secrets ]; 82 }; 83 84 services.wwan = svc.wwan.huawei-e3372.build { 85 apn = "data.uk"; 86 username = "user"; 87 password = "one2one"; 88 authType = "chap"; 89 }; 90 91 profile.gateway = { 92 lan = { 93 interfaces = with config.hardware.networkInterfaces; [ 94 # EDIT: these are the interfaces exposed by the gl.inet gl-ar750: 95 # if your device has more or differently named lan interfaces, 96 # specify them here 97 wlan 98 wlan5 99 lan 100 ]; 101 inherit (rsecrets.lan) prefix; 102 address = { 103 family = "inet"; 104 address = "${rsecrets.lan.prefix}.1"; 105 prefixLength = 24; 106 }; 107 dhcp = { 108 start = 10; 109 end = 240; 110 hosts = 111 { } // lib.optionalAttrs (builtins.pathExists ./static-leases.nix) (import ./static-leases.nix); 112 localDomain = "lan"; 113 }; 114 }; 115 wan = 116 let 117 secret = outputRef config.services.secrets; 118 username = secret "ppp/username"; 119 password = secret "ppp/password"; 120 in 121 { 122 interface = 123 let 124 pppoe = svc.pppoe.build { 125 interface = config.hardware.networkInterfaces.wan; 126 debug = true; 127 inherit username password; 128 }; 129 130 l2tp = 131 let 132 check-address = oneshot rec { 133 name = "check-lns-address"; 134 up = "grep -Fx ${lns.address} $(output_path ${services.lns-address} addresses)"; 135 dependencies = [ services.lns-address ]; 136 }; 137 route = svc.network.route.build { 138 via = "$(output ${services.bootstrap-dhcpc} router)"; 139 target = lns.address; 140 dependencies = [ 141 services.bootstrap-dhcpc 142 check-address 143 ]; 144 }; 145 l2tpd = svc.l2tp.build { 146 lns = lns.address; 147 inherit username password; 148 dependencies = [ 149 config.services.lns-address 150 route 151 check-address 152 ]; 153 }; 154 in 155 svc.health-check.build { 156 service = l2tpd; 157 threshold = 3; 158 interval = 2; 159 healthCheck = pkgs.writeAshScript "ping-check" { } "ping 1.1.1.1"; 160 }; 161 in 162 svc.round-robin.build { 163 name = "wan"; 164 services = [ 165 pppoe 166 l2tp 167 ]; 168 }; 169 dhcp6.enable = true; 170 }; 171 172 wireless.networks = { 173 "${rsecrets.ssid}" = { 174 interface = config.hardware.networkInterfaces.wlan; 175 hw_mode = "g"; 176 channel = "6"; 177 ieee80211n = 1; 178 } 179 // wirelessConfig 180 // { 181 wpa_passphrase = outputRef config.services.secrets "wpa_passphrase"; 182 }; 183 184 "${rsecrets.ssid}5" = rec { 185 interface = config.hardware.networkInterfaces.wlan5; 186 hw_mode = "a"; 187 channel = 36; 188 ht_capab = "[HT40+]"; 189 vht_oper_chwidth = 1; 190 vht_oper_centr_freq_seg0_idx = channel + 6; 191 ieee80211n = 1; 192 ieee80211ac = 1; 193 } 194 // wirelessConfig 195 // { 196 wpa_passphrase = outputRef config.services.secrets "wpa_passphrase"; 197 }; 198 }; 199 }; 200 201 services.bootstrap-dhcpc = svc.dhcp4c.client.build { 202 interface = config.services.wwan; 203 dependencies = [ config.services.hostname ]; 204 }; 205 206 services.sshd = svc.ssh.build { 207 authorizedKeys = outputRef config.services.secrets "ssh/authorizedKeys"; 208 }; 209 210 services.lns-address = 211 let 212 ns = "$(output_word ${services.bootstrap-dhcpc} dns 1)"; 213 route-to-bootstrap-nameserver = svc.network.route.build { 214 via = "$(output ${services.bootstrap-dhcpc} router)"; 215 target = ns; 216 dependencies = [ services.bootstrap-dhcpc ]; 217 }; 218 in 219 oneshot rec { 220 name = "resolve-l2tp-server"; 221 dependencies = [ 222 services.bootstrap-dhcpc 223 route-to-bootstrap-nameserver 224 ]; 225 up = '' 226 (in_outputs ${name} 227 DNSCACHEIP="${ns}" ${pkgs.s6-dns}/bin/s6-dnsip4 ${lns.hostname} \ 228 > addresses 229 ) 230 ''; 231 }; 232 233 users.root = rsecrets.root; 234 235 programs.busybox.options = { 236 FEATURE_FANCY_TAIL = "y"; 237 }; 238 239 }