commit d4d8093f97f11dbd398f4fbd758cac6de2e11aaf
parent 7c9c801afc03b4838153f795c1d1ead4a6e0b067
Author: Daniel Barlow <dan@telent.net>
Date: Thu, 20 Jun 2024 10:15:54 +0100
working l2tp-over-wwan stick example
Diffstat:
3 files changed, 84 insertions(+), 22 deletions(-)
diff --git a/examples/l2tp.nix b/examples/l2tp.nix
@@ -6,7 +6,26 @@
}: let
secrets = import ./extneder-secrets.nix;
rsecrets = import ./rotuer-secrets.nix;
- lns = "l2tp.aaisp.net.uk";
+
+ # https://support.aa.net.uk/Category:Incoming_L2TP says:
+ # "Please use the DNS name (l2tp.aa.net.uk) instead of hardcoding an
+ # IP address; IP addresses can and do change. If you have to use an
+ # IP, use 194.4.172.12, but do check the DNS for l2tp.aa.net.uk in
+ # case it changes."
+
+ # but (1) we don't want to use the wwan stick's dns as our main
+ # resolver: it's provided by some mobile ISP and they aren't
+ # necessarily the best at providing unfettered services without
+ # deciding to do something weird; (2) it's not simple to arrange
+ # that xl2tpd gets a different resolver than every other process;
+ # (3) there's no way to specify an lns address to xl2tpd at runtime
+ # except by rewriting its config file. So what we will do is lookup
+ # the lns hostname using the mobile ISP's dns server and then refuse
+ # to start l2tp unless the expected lns address is one of the
+ # addresses returned. I think this satisfies "do check the DNS"
+
+ lns = { hostname = "l2tp.aaisp.net.uk"; address = "194.4.172.12"; };
+
inherit (pkgs.liminix.services) oneshot longrun bundle target;
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs) writeText dropbear ifwait serviceFns;
@@ -46,46 +65,75 @@ in rec {
services.sshd = svc.ssh.build { };
services.resolvconf = oneshot rec {
- dependencies = [ services.dhcpc ];
+ dependencies = [ services.l2tp ];
name = "resolvconf";
up = ''
. ${serviceFns}
- ( in_outputs ${name}
- for i in $(output ${services.dhcpc} dns); do
- echo "nameserver $i" > resolv.conf
- done
- )
+ ( in_outputs ${name}
+ for i in ns1 ns2 ; do
+ ns=$(output ${services.l2tp} $i)
+ echo "nameserver $ns" >> resolv.conf
+ done
+ )
'';
};
filesystem = dir {
etc = dir {
"resolv.conf" = symlink "${services.resolvconf}/.outputs/resolv.conf";
};
- srv = dir {};
};
- services.lnsroute = svc.network.route.build {
- via = "$(output ${services.dhcpc} router)";
- target = lns;
- dependencies = [services.dhcpc];
+ services.lns-address = let
+ ns = "$(output_word ${services.dhcpc} dns 1)";
+ route-to-bootstrap-nameserver = svc.network.route.build {
+ via = "$(output ${services.dhcpc} router)";
+ target = ns;
+ dependencies = [services.dhcpc];
+ };
+ in oneshot rec {
+ name = "resolve-l2tp-server";
+ dependencies = [ services.dhcpc route-to-bootstrap-nameserver ];
+ up = ''
+ (in_outputs ${name}
+ DNSCACHEIP="${ns}" ${pkgs.s6-dns}/bin/s6-dnsip4 ${lns.hostname} \
+ > addresses
+ )
+ '';
};
- services.l2tp = svc.l2tp.build {
- inherit lns;
- ppp-options = [
- "debug" "+ipv6" "noauth"
- "name" rsecrets.l2tp.name
- "password" rsecrets.l2tp.password
- ];
- dependencies = [ services.lnsroute ];
+ services.l2tp =
+ let
+ check-address = oneshot rec {
+ name = "check-lns-address";
+ up = ''
+ grep -Fx ${lns.address} $(output_path ${services.lns-address} addresses)
+ '';
+ dependencies = [ services.lns-address ];
+ };
+ route = svc.network.route.build {
+ via = "$(output ${services.dhcpc} router)";
+ target = lns.address;
+ dependencies = [services.dhcpc check-address];
+ };
+ in svc.l2tp.build {
+ lns = lns.address;
+ ppp-options = [
+ "debug" "+ipv6" "noauth"
+ "name" rsecrets.l2tp.name
+ "connect-delay" "5000"
+ "password" rsecrets.l2tp.password
+ ];
+ dependencies = [config.services.lns-address route check-address];
};
services.defaultroute4 = svc.network.route.build {
- via = "$(output ${services.l2tp} router)";
+ via = "$(output ${services.l2tp} peer-address)";
target = "default";
dependencies = [services.l2tp];
};
+# defaultProfile.packages = [ pkgs.go-l2tp ];
+
users.root = {
passwd = lib.mkForce secrets.root.passwd;
openssh.authorizedKeys.keys = secrets.root.keys;
diff --git a/modules/network/route.nix b/modules/network/route.nix
@@ -8,8 +8,10 @@
let
inherit (liminix.services) oneshot;
with_dev = if interface != null then "dev $(output ${interface} ifname)" else "";
+ target_hash = builtins.substring 0 12 (builtins.hashString "sha256" target);
+ via_hash = builtins.substring 0 12 (builtins.hashString "sha256" via);
in oneshot {
- name = "route-${target}-${builtins.substring 0 12 (builtins.hashString "sha256" "${via}-${if interface!=null then interface.name else ""}")}";
+ name = "route-${target_hash}-${builtins.substring 0 12 (builtins.hashString "sha256" "${via_hash}-${if interface!=null then interface.name else ""}")}";
up = ''
ip route add ${target} via ${via} metric ${toString metric} ${with_dev}
'';
diff --git a/pkgs/service-fns/default.nix b/pkgs/service-fns/default.nix
@@ -1,6 +1,18 @@
{writeText}:
writeText "service-fns.sh" ''
output() { cat $1/.outputs/$2; }
+ output_word() {
+ set -f
+ local i=1
+ for var in $(cat $1/.outputs/$2); do
+ if test "$i" == "$3" ; then
+ echo $var
+ fi
+ i=$(expr $i + 1)
+ done
+ set +f
+ }
+
output_path() { echo $(realpath $1/.outputs)/$2; }
SERVICE_OUTPUTS=/run/services/outputs
SERVICE_STATE=/run/services/state