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