liminix

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }