at 25.11-pre 4.9 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7 8with lib; 9 10let 11 cfg = config.services.pppd; 12in 13{ 14 meta = { 15 maintainers = [ ]; 16 }; 17 18 options = { 19 services.pppd = { 20 enable = mkEnableOption "pppd"; 21 22 package = mkPackageOption pkgs "ppp" { }; 23 24 peers = mkOption { 25 default = { }; 26 description = "pppd peers."; 27 type = types.attrsOf ( 28 types.submodule ( 29 { name, ... }: 30 { 31 options = { 32 name = mkOption { 33 type = types.str; 34 default = name; 35 example = "dialup"; 36 description = "Name of the PPP peer."; 37 }; 38 39 enable = mkOption { 40 type = types.bool; 41 default = true; 42 example = false; 43 description = "Whether to enable this PPP peer."; 44 }; 45 46 autostart = mkOption { 47 type = types.bool; 48 default = true; 49 example = false; 50 description = "Whether the PPP session is automatically started at boot time."; 51 }; 52 53 config = mkOption { 54 type = types.lines; 55 default = ""; 56 description = "pppd configuration for this peer, see the {manpage}`pppd(8)` man page."; 57 }; 58 }; 59 } 60 ) 61 ); 62 }; 63 }; 64 }; 65 66 config = 67 let 68 enabledConfigs = filter (f: f.enable) (attrValues cfg.peers); 69 70 mkEtc = peerCfg: { 71 name = "ppp/peers/${peerCfg.name}"; 72 value.text = peerCfg.config; 73 }; 74 75 mkSystemd = peerCfg: { 76 name = "pppd-${peerCfg.name}"; 77 value = { 78 restartTriggers = [ config.environment.etc."ppp/peers/${peerCfg.name}".source ]; 79 before = [ "network.target" ]; 80 wants = [ "network.target" ]; 81 after = [ "network-pre.target" ]; 82 environment = { 83 # pppd likes to write directly into /var/run. This is rude 84 # on a modern system, so we use libredirect to transparently 85 # move those files into /run/pppd. 86 LD_PRELOAD = "${pkgs.libredirect}/lib/libredirect.so"; 87 NIX_REDIRECTS = "/var/run=/run/pppd"; 88 }; 89 serviceConfig = 90 let 91 capabilities = [ 92 "CAP_BPF" 93 "CAP_SYS_TTY_CONFIG" 94 "CAP_NET_ADMIN" 95 "CAP_NET_RAW" 96 ]; 97 in 98 { 99 ExecStart = "${getBin cfg.package}/sbin/pppd call ${peerCfg.name} nodetach nolog"; 100 Restart = "always"; 101 RestartSec = 5; 102 103 AmbientCapabilities = capabilities; 104 CapabilityBoundingSet = capabilities; 105 KeyringMode = "private"; 106 LockPersonality = true; 107 MemoryDenyWriteExecute = true; 108 NoNewPrivileges = true; 109 PrivateMounts = true; 110 PrivateTmp = true; 111 ProtectControlGroups = true; 112 ProtectHome = true; 113 ProtectHostname = true; 114 ProtectKernelModules = true; 115 # pppd can be configured to tweak kernel settings. 116 ProtectKernelTunables = false; 117 ProtectSystem = "strict"; 118 RemoveIPC = true; 119 RestrictAddressFamilies = [ 120 "AF_ATMPVC" 121 "AF_ATMSVC" 122 "AF_INET" 123 "AF_INET6" 124 "AF_IPX" 125 "AF_NETLINK" 126 "AF_PACKET" 127 "AF_PPPOX" 128 "AF_UNIX" 129 ]; 130 RestrictNamespaces = true; 131 RestrictRealtime = true; 132 RestrictSUIDSGID = true; 133 SecureBits = "no-setuid-fixup-locked noroot-locked"; 134 SystemCallFilter = "@system-service"; 135 SystemCallArchitectures = "native"; 136 137 # All pppd instances on a system must share a runtime 138 # directory in order for PPP multilink to work correctly. So 139 # we give all instances the same /run/pppd directory to store 140 # things in. 141 # 142 # For the same reason, we can't set PrivateUsers=true, because 143 # all instances need to run as the same user to access the 144 # multilink database. 145 RuntimeDirectory = "pppd"; 146 RuntimeDirectoryPreserve = true; 147 }; 148 wantedBy = mkIf peerCfg.autostart [ "multi-user.target" ]; 149 }; 150 }; 151 152 etcFiles = listToAttrs (map mkEtc enabledConfigs); 153 systemdConfigs = listToAttrs (map mkSystemd enabledConfigs); 154 155 in 156 mkIf cfg.enable { 157 environment.etc = etcFiles; 158 systemd.services = systemdConfigs; 159 }; 160}