1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7let
8 cfg = config.services.newt;
9 type =
10 with lib.types;
11 attrsOf (
12 nullOr (oneOf [
13 bool
14 int
15 float
16 str
17 path
18 (listOf type)
19 ])
20 )
21 // {
22 description = "value coercible to CLI argument";
23 };
24in
25{
26 imports = [
27 (lib.mkRenamedOptionModule [ "services" "newt" "id" ] [ "services" "newt" "settings" "id" ])
28 (lib.mkRenamedOptionModule
29 [ "services" "newt" "logLevel" ]
30 [ "services" "newt" "settings" "log-level" ]
31 )
32 (lib.mkRenamedOptionModule
33 [ "services" "newt" "endpoint" ]
34 [ "services" "newt" "settings" "endpoint" ]
35 )
36 ];
37
38 options = {
39 services.newt = {
40 enable = lib.mkEnableOption "Newt, user space tunnel client for Pangolin";
41 package = lib.mkPackageOption pkgs "fosrl-newt" { };
42 settings = lib.mkOption {
43 inherit type;
44 default = { };
45 example = {
46 endpoint = "pangolin.example.com";
47 id = "8yfsghj438a20ol";
48 };
49 description = "Settings for Newt module, see [Newt CLI docs](https://github.com/fosrl/newt?tab=readme-ov-file#cli-args) for more information.";
50 };
51
52 # provide path to file to keep secrets out of the nix store
53 environmentFile = lib.mkOption {
54 type = with lib.types; nullOr path;
55 default = null;
56 description = ''
57 Path to a file containing sensitive environment variables for Newt. See <https://docs.fossorial.io/Newt/overview#cli-args>
58 These will overwrite anything defined in the config.
59 The file should contain environment-variable assignments like:
60 NEWT_ID=2ix2t8xk22ubpfy
61 NEWT_SECRET=nnisrfsdfc7prqsp9ewo1dvtvci50j5uiqotez00dgap0ii2
62 '';
63 };
64 };
65 };
66
67 config = lib.mkIf cfg.enable {
68
69 assertions = [
70 {
71 assertion = cfg.environmentFile != null;
72 message = "services.newt.environmentFile must be provided when Newt is enabled.";
73 }
74 ];
75
76 systemd.services.newt = {
77 description = "Newt, user space tunnel client for Pangolin";
78 wantedBy = [ "multi-user.target" ];
79 after = [ "network.target" ];
80
81 environment = {
82 HOME = "/var/lib/private/newt";
83 };
84 # the flag values will all be overwritten if also defined in the env file
85 serviceConfig = {
86 ExecStart = "${lib.getExe cfg.package} ${lib.cli.toGNUCommandLineShell { } cfg.settings}";
87 DynamicUser = true;
88 StateDirectory = "newt";
89 StateDirectoryMode = "0700";
90 Restart = "always";
91 RestartSec = "10s";
92 EnvironmentFile = cfg.environmentFile;
93 # hardening
94 ProtectSystem = "strict";
95 ProtectHome = true;
96 PrivateTmp = "disconnected";
97 PrivateDevices = true;
98 PrivateUsers = true;
99 PrivateMounts = true;
100 ProtectKernelTunables = true;
101 ProtectKernelModules = true;
102 ProtectKernelLogs = true;
103 ProtectControlGroups = true;
104 LockPersonality = true;
105 RestrictRealtime = true;
106 ProtectClock = true;
107 ProtectProc = "noaccess";
108 ProtectHostname = true;
109 RemoveIPC = true;
110 NoNewPrivileges = true;
111 RestrictSUIDSGID = true;
112 MemoryDenyWriteExecute = true;
113 SystemCallArchitectures = "native";
114 UMask = "0077";
115 RestrictAddressFamilies = [
116 "AF_INET"
117 "AF_INET6"
118 "AF_NETLINK"
119 "AF_UNIX"
120 ];
121 CapabilityBoundingSet = [
122 "~CAP_BLOCK_SUSPEND"
123 "~CAP_BPF"
124 "~CAP_CHOWN"
125 "~CAP_MKNOD"
126 "~CAP_NET_RAW"
127 "~CAP_PERFMON"
128 "~CAP_SYS_BOOT"
129 "~CAP_SYS_CHROOT"
130 "~CAP_SYS_MODULE"
131 "~CAP_SYS_NICE"
132 "~CAP_SYS_PACCT"
133 "~CAP_SYS_PTRACE"
134 "~CAP_SYS_TIME"
135 "~CAP_SYS_TTY_CONFIG"
136 "~CAP_SYSLOG"
137 "~CAP_WAKE_ALARM"
138 ];
139 SystemCallFilter = [
140 "~@aio:EPERM"
141 "~@chown:EPERM"
142 "~@clock:EPERM"
143 "~@cpu-emulation:EPERM"
144 "~@debug:EPERM"
145 "~@keyring:EPERM"
146 "~@memlock:EPERM"
147 "~@module:EPERM"
148 "~@mount:EPERM"
149 "~@obsolete:EPERM"
150 "~@pkey:EPERM"
151 "~@privileged:EPERM"
152 "~@raw-io:EPERM"
153 "~@reboot:EPERM"
154 "~@resources:EPERM"
155 "~@sandbox:EPERM"
156 "~@setuid:EPERM"
157 "~@swap:EPERM"
158 "~@sync:EPERM"
159 "~@timer:EPERM"
160 ];
161 };
162 };
163 };
164
165 meta.maintainers = with lib.maintainers; [ jackr ];
166}