1 { config, lib, pkgs, ... }:
19 mainCfg = config.services.ghostunnel;
21 module = { config, name, ... }:
26 description = lib.mdDoc ''
27 Address and port to listen on (can be HOST:PORT, unix:PATH).
33 description = lib.mdDoc ''
34 Address to forward connections to (can be HOST:PORT or unix:PATH).
40 description = lib.mdDoc ''
41 Path to keystore (combined PEM with cert/key, or PKCS12 keystore).
43 NB: storepass is not supported because it would expose credentials via `/proc/*/cmdline`.
45 Specify this or `cert` and `key`.
47 type = types.nullOr types.str;
52 description = lib.mdDoc ''
53 Path to certificate (PEM with certificate chain).
55 Not required if `keystore` is set.
57 type = types.nullOr types.str;
62 description = lib.mdDoc ''
63 Path to certificate private key (PEM with private key).
65 Not required if `keystore` is set.
67 type = types.nullOr types.str;
72 description = lib.mdDoc ''
73 Path to CA bundle file (PEM/X509). Uses system trust store if `null`.
75 type = types.nullOr types.str;
78 disableAuthentication = mkOption {
79 description = lib.mdDoc ''
80 Disable client authentication, no client certificate will be required.
87 description = lib.mdDoc ''
88 If true, allow all clients, do not check client cert subject.
95 description = lib.mdDoc ''
96 Allow client if common name appears in the list.
98 type = types.listOf types.str;
103 description = lib.mdDoc ''
104 Allow client if organizational unit name appears in the list.
106 type = types.listOf types.str;
110 allowDNS = mkOption {
111 description = lib.mdDoc ''
112 Allow client if DNS subject alternative name appears in the list.
114 type = types.listOf types.str;
118 allowURI = mkOption {
119 description = lib.mdDoc ''
120 Allow client if URI subject alternative name appears in the list.
122 type = types.listOf types.str;
126 extraArguments = mkOption {
127 description = lib.mdDoc "Extra arguments to pass to `ghostunnel server`";
128 type = types.separatedString " ";
132 unsafeTarget = mkOption {
133 description = lib.mdDoc ''
134 If set, does not limit target to localhost, 127.0.0.1, [::1], or UNIX sockets.
136 This is meant to protect against accidental unencrypted traffic on
143 # Definitions to apply at the root of the NixOS configuration.
149 # Clients should not be authenticated with the public root certificates
150 # (afaict, it doesn't make sense), so we only provide that default when
151 # client cert auth is disabled.
152 config.cacert = mkIf config.disableAuthentication (mkDefault null);
157 services.ghostunnel.servers.${name}: At least one access control flag is required.
159 - services.ghostunnel.servers.${name}.disableAuthentication
160 - services.ghostunnel.servers.${name}.allowAll
161 - services.ghostunnel.servers.${name}.allowCN
162 - services.ghostunnel.servers.${name}.allowOU
163 - services.ghostunnel.servers.${name}.allowDNS
164 - services.ghostunnel.servers.${name}.allowURI
166 assertion = config.disableAuthentication
168 || config.allowCN != []
169 || config.allowOU != []
170 || config.allowDNS != []
171 || config.allowURI != []
176 systemd.services."ghostunnel-server-${name}" = {
177 after = [ "network.target" ];
178 wants = [ "network.target" ];
179 wantedBy = [ "multi-user.target" ];
182 AmbientCapabilities = ["CAP_NET_BIND_SERVICE"];
184 LoadCredential = optional (config.keystore != null) "keystore:${config.keystore}"
185 ++ optional (config.cert != null) "cert:${config.cert}"
186 ++ optional (config.key != null) "key:${config.key}"
187 ++ optional (config.cacert != null) "cacert:${config.cacert}";
189 script = concatStringsSep " " (
190 [ "${mainCfg.package}/bin/ghostunnel" ]
191 ++ optional (config.keystore != null) "--keystore=$CREDENTIALS_DIRECTORY/keystore"
192 ++ optional (config.cert != null) "--cert=$CREDENTIALS_DIRECTORY/cert"
193 ++ optional (config.key != null) "--key=$CREDENTIALS_DIRECTORY/key"
194 ++ optional (config.cacert != null) "--cacert=$CREDENTIALS_DIRECTORY/cacert"
197 "--listen ${config.listen}"
198 "--target ${config.target}"
199 ] ++ optional config.allowAll "--allow-all"
200 ++ map (v: "--allow-cn=${escapeShellArg v}") config.allowCN
201 ++ map (v: "--allow-ou=${escapeShellArg v}") config.allowOU
202 ++ map (v: "--allow-dns=${escapeShellArg v}") config.allowDNS
203 ++ map (v: "--allow-uri=${escapeShellArg v}") config.allowURI
204 ++ optional config.disableAuthentication "--disable-authentication"
205 ++ optional config.unsafeTarget "--unsafe-target"
206 ++ [ config.extraArguments ]
216 services.ghostunnel.enable = mkEnableOption (lib.mdDoc "ghostunnel");
218 services.ghostunnel.package = mkOption {
219 description = lib.mdDoc "The ghostunnel package to use.";
220 type = types.package;
221 default = pkgs.ghostunnel;
222 defaultText = literalExpression "pkgs.ghostunnel";
225 services.ghostunnel.servers = mkOption {
226 description = lib.mdDoc ''
227 Server mode ghostunnels (TLS listener -> plain TCP/UNIX target)
229 type = types.attrsOf (types.submodule module);
234 config = mkIf mainCfg.enable {
235 assertions = lib.mkMerge (map (v: v.atRoot.assertions) (attrValues mainCfg.servers));
236 systemd = lib.mkMerge (map (v: v.atRoot.systemd) (attrValues mainCfg.servers));
239 meta.maintainers = with lib.maintainers; [