router-with-l2tp.nix (6891B)
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 longrun target; 37 inherit (pkgs.liminix) outputRef; 38 inherit (pkgs.pseudofile) dir symlink; 39 inherit (pkgs) serviceFns; 40 svc = config.system.service; 41 wirelessConfig = { 42 country_code = "GB"; 43 inherit (rsecrets) wpa_passphrase; 44 wmm_enabled = 1; 45 }; 46 in 47 rec { 48 boot = { 49 tftp = { 50 serverip = "10.0.0.1"; 51 ipaddr = "10.0.0.8"; 52 }; 53 }; 54 55 imports = [ 56 ../modules/wwan 57 ../modules/network 58 ../modules/ssh 59 ../modules/usb.nix 60 ../modules/ppp 61 ../modules/round-robin 62 ../modules/health-check 63 ../modules/secrets 64 ../modules/profiles/gateway.nix 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 { } 112 // lib.optionalAttrs (builtins.pathExists ./static-leases.nix) (import ./static-leases.nix); 113 localDomain = "lan"; 114 }; 115 }; 116 wan = 117 let 118 secret = outputRef config.services.secrets; 119 username = secret "ppp/username"; 120 password = secret "ppp/password"; 121 in 122 { 123 interface = 124 let 125 pppoe = svc.pppoe.build { 126 interface = config.hardware.networkInterfaces.wan; 127 debug = true; 128 inherit username password; 129 }; 130 131 l2tp = 132 let 133 check-address = oneshot rec { 134 name = "check-lns-address"; 135 up = "grep -Fx ${lns.address} $(output_path ${services.lns-address} addresses)"; 136 dependencies = [ services.lns-address ]; 137 }; 138 route = svc.network.route.build { 139 via = "$(output ${services.bootstrap-dhcpc} router)"; 140 target = lns.address; 141 dependencies = [ 142 services.bootstrap-dhcpc 143 check-address 144 ]; 145 }; 146 l2tpd = svc.l2tp.build { 147 lns = lns.address; 148 inherit username password; 149 dependencies = [ 150 config.services.lns-address 151 route 152 check-address 153 ]; 154 }; 155 in 156 svc.health-check.build { 157 service = l2tpd; 158 threshold = 3; 159 interval = 2; 160 healthCheck = pkgs.writeAshScript "ping-check" { } "ping 1.1.1.1"; 161 }; 162 in 163 svc.round-robin.build { 164 name = "wan"; 165 services = [ 166 pppoe 167 l2tp 168 ]; 169 }; 170 dhcp6.enable = true; 171 }; 172 173 wireless.networks = { 174 "${rsecrets.ssid}" = 175 { 176 interface = config.hardware.networkInterfaces.wlan; 177 hw_mode = "g"; 178 channel = "6"; 179 ieee80211n = 1; 180 } 181 // wirelessConfig 182 // { 183 wpa_passphrase = outputRef config.services.secrets "wpa_passphrase"; 184 }; 185 186 "${rsecrets.ssid}5" = 187 rec { 188 interface = config.hardware.networkInterfaces.wlan5; 189 hw_mode = "a"; 190 channel = 36; 191 ht_capab = "[HT40+]"; 192 vht_oper_chwidth = 1; 193 vht_oper_centr_freq_seg0_idx = channel + 6; 194 ieee80211n = 1; 195 ieee80211ac = 1; 196 } 197 // wirelessConfig 198 // { 199 wpa_passphrase = outputRef config.services.secrets "wpa_passphrase"; 200 }; 201 }; 202 }; 203 204 services.bootstrap-dhcpc = svc.network.dhcp.client.build { 205 interface = config.services.wwan; 206 dependencies = [ config.services.hostname ]; 207 }; 208 209 services.sshd = svc.ssh.build { 210 authorizedKeys = outputRef config.services.secrets "ssh/authorizedKeys"; 211 }; 212 213 services.lns-address = 214 let 215 ns = "$(output_word ${services.bootstrap-dhcpc} dns 1)"; 216 route-to-bootstrap-nameserver = svc.network.route.build { 217 via = "$(output ${services.bootstrap-dhcpc} router)"; 218 target = ns; 219 dependencies = [ services.bootstrap-dhcpc ]; 220 }; 221 in 222 oneshot rec { 223 name = "resolve-l2tp-server"; 224 dependencies = [ 225 services.bootstrap-dhcpc 226 route-to-bootstrap-nameserver 227 ]; 228 up = '' 229 (in_outputs ${name} 230 DNSCACHEIP="${ns}" ${pkgs.s6-dns}/bin/s6-dnsip4 ${lns.hostname} \ 231 > addresses 232 ) 233 ''; 234 }; 235 236 users.root = rsecrets.root; 237 238 programs.busybox.options = { 239 FEATURE_FANCY_TAIL = "y"; 240 }; 241 242 }