10 cfg = config.networking.openconnect;
11 openconnect = cfg.package;
12 pkcs11 = types.strMatching "pkcs11:.+" // {
14 description = "PKCS#11 URI";
18 autoStart = mkOption {
20 description = "Whether this VPN connection should be started automatically.";
25 description = "Gateway server to connect to.";
26 example = "gateway.example.com";
31 description = "Protocol to use.";
32 example = "anyconnect";
45 description = "Username to authenticate with.";
46 example = "example-user";
47 type = types.nullOr types.str;
51 # Note: It does not make sense to provide a way to declaratively
52 # set an authentication cookie, because they have to be requested
53 # for every new connection and would only work once.
54 passwordFile = mkOption {
56 File containing the password to authenticate with. This
57 is passed to `openconnect` via the
58 `--passwd-on-stdin` option.
61 example = "/var/lib/secrets/openconnect-passwd";
62 type = types.nullOr types.path;
65 certificate = mkOption {
66 description = "Certificate to authenticate with.";
68 example = "/var/lib/secrets/openconnect_certificate.pem";
69 type = with types; nullOr (either path pkcs11);
72 privateKey = mkOption {
73 description = "Private key to authenticate with.";
74 example = "/var/lib/secrets/openconnect_private_key.pem";
76 type = with types; nullOr (either path pkcs11);
79 extraOptions = mkOption {
81 Extra config to be appended to the interface config. It should
82 contain long-format options as would be accepted on the command
84 (see https://www.infradead.org/openconnect/manual.html).
85 Non-key-value options like `deflate` can be used by
86 declaring them as booleans, i. e. `deflate = true;`.
90 compression = "stateless";
92 no-http-keepalive = true;
95 type = with types; attrsOf (either str bool);
101 strings.concatStringsSep "\n" (
102 attrsets.mapAttrsToList (name: value: if (value == true) then name else "${name}=${value}") (
103 attrsets.filterAttrs (_: value: value != false) extra_cfg
108 pkgs.writeText "config" ''
110 ${optionalString (icfg.protocol != null) "protocol=${icfg.protocol}"}
111 ${optionalString (icfg.user != null) "user=${icfg.user}"}
112 ${optionalString (icfg.passwordFile != null) "passwd-on-stdin"}
113 ${optionalString (icfg.certificate != null) "certificate=${icfg.certificate}"}
114 ${optionalString (icfg.privateKey != null) "sslkey=${icfg.privateKey}"}
116 ${generateExtraConfig icfg.extraOptions}
118 generateUnit = name: icfg: {
119 description = "OpenConnect Interface - ${name}";
120 requires = [ "network-online.target" ];
123 "network-online.target"
125 wantedBy = optional icfg.autoStart "multi-user.target";
129 ExecStart = "${openconnect}/bin/openconnect --config=${generateConfig name icfg} ${icfg.gateway}";
130 StandardInput = lib.mkIf (icfg.passwordFile != null) "file:${icfg.passwordFile}";
137 options.networking.openconnect = {
138 package = mkPackageOption pkgs "openconnect" { };
140 interfaces = mkOption {
141 description = "OpenConnect interfaces.";
145 gateway = "gateway.example.com";
146 protocol = "anyconnect";
147 user = "example-user";
148 passwordFile = "/var/lib/secrets/openconnect-passwd";
151 type = with types; attrsOf (submodule interfaceOptions);
156 systemd.services = mapAttrs' (name: value: {
157 name = "openconnect-${name}";
158 value = generateUnit name value;
162 meta.maintainers = with maintainers; [ alyaeanyx ];