summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
authorBas van Dijk <v.dijk.bas@gmail.com>2017-08-05 14:01:52 +0200
committerBas van Dijk <v.dijk.bas@gmail.com>2018-02-28 10:41:54 +0100
commitbd24b3addd46ff660d8ad6cc32a58aecd4715374 (patch)
tree844b6c0319732d03be366a1bf85af526d5e8f4c9 /nixos
parent04dd1987a3e9c28708d4cc69959b011f81d33a10 (diff)
downloadnixpkgs-bd24b3addd46ff660d8ad6cc32a58aecd4715374.tar
nixpkgs-bd24b3addd46ff660d8ad6cc32a58aecd4715374.tar.gz
nixpkgs-bd24b3addd46ff660d8ad6cc32a58aecd4715374.tar.bz2
nixpkgs-bd24b3addd46ff660d8ad6cc32a58aecd4715374.tar.lz
nixpkgs-bd24b3addd46ff660d8ad6cc32a58aecd4715374.tar.xz
nixpkgs-bd24b3addd46ff660d8ad6cc32a58aecd4715374.tar.zst
nixpkgs-bd24b3addd46ff660d8ad6cc32a58aecd4715374.zip
nixos: add the strongswan-swanctl service
The strongswan-swanctl systemd service starts charon-systemd. This implements a IKE daemon
very similar to charon, but it's specifically designed for use with systemd. It uses the
systemd libraries for a native integration.

Instead of using starter and an ipsec.conf based configuration, the daemon is directly
managed by systemd and configured with the swanctl configuration backend.

See: https://wiki.strongswan.org/projects/strongswan/wiki/Charon-systemd

Note that the strongswan.conf and swantctl.conf configuration files are automatically
generated based on NixOS options under services.strongswan-swanctl.strongswan and
services.strongswan-swanctl.swanctl respectively.
Diffstat (limited to 'nixos')
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/services/networking/strongswan-swanctl/module.nix80
-rw-r--r--nixos/modules/services/networking/strongswan-swanctl/param-constructors.nix162
-rw-r--r--nixos/modules/services/networking/strongswan-swanctl/param-lib.nix82
-rw-r--r--nixos/modules/services/networking/strongswan-swanctl/strongswan-charon-params.nix568
-rw-r--r--nixos/modules/services/networking/strongswan-swanctl/strongswan-charon-plugins-params.nix1070
-rw-r--r--nixos/modules/services/networking/strongswan-swanctl/strongswan-libimcv-params.nix291
-rw-r--r--nixos/modules/services/networking/strongswan-swanctl/strongswan-loglevel-params.nix29
-rw-r--r--nixos/modules/services/networking/strongswan-swanctl/strongswan-params.nix228
-rw-r--r--nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix1145
-rw-r--r--nixos/release.nix1
-rw-r--r--nixos/tests/strongswan-swanctl.nix154
12 files changed, 3811 insertions, 0 deletions
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 13a32b968dc..f9545740ade 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -560,6 +560,7 @@
   ./services/networking/ssh/lshd.nix
   ./services/networking/ssh/sshd.nix
   ./services/networking/strongswan.nix
+  ./services/networking/strongswan-swanctl/module.nix
   ./services/networking/stunnel.nix
   ./services/networking/supplicant.nix
   ./services/networking/supybot.nix
diff --git a/nixos/modules/services/networking/strongswan-swanctl/module.nix b/nixos/modules/services/networking/strongswan-swanctl/module.nix
new file mode 100644
index 00000000000..8bfb62e6b03
--- /dev/null
+++ b/nixos/modules/services/networking/strongswan-swanctl/module.nix
@@ -0,0 +1,80 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+with (import ./param-lib.nix lib);
+
+let
+  cfg = config.services.strongswan-swanctl;
+
+  # TODO: auto-generate these files using:
+  # https://github.com/strongswan/strongswan/tree/master/conf
+  # IDEA: extend the format-options.py script to output these Nix files.
+  strongswanParams = import ./strongswan-params.nix lib;
+  swanctlParams    = import ./swanctl-params.nix    lib;
+in  {
+  options.services.strongswan-swanctl = {
+    enable = mkEnableOption "strongswan-swanctl service";
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.strongswan;
+      defaultText = "pkgs.strongswan";
+      description = ''
+        The strongswan derivation to use.
+      '';
+    };
+
+    strongswan = paramsToOptions strongswanParams;
+    swanctl    = paramsToOptions swanctlParams;
+  };
+
+  config = mkIf cfg.enable {
+
+    assertions = [
+      { assertion = !config.services.strongswan.enable;
+        message = "cannot enable both services.strongswan and services.strongswan-swanctl. Choose either one.";
+      }
+    ];
+
+    environment.etc."swanctl/swanctl.conf".text =
+      paramsToConf cfg.swanctl swanctlParams;
+
+    # The swanctl command complains when the following directories don't exist:
+    # See: https://wiki.strongswan.org/projects/strongswan/wiki/Swanctldirectory
+    system.activationScripts.strongswan-swanctl-etc = stringAfter ["etc"] ''
+      mkdir -p '/etc/swanctl/x509'     # Trusted X.509 end entity certificates
+      mkdir -p '/etc/swanctl/x509ca'   # Trusted X.509 Certificate Authority certificates
+      mkdir -p '/etc/swanctl/x509ocsp'
+      mkdir -p '/etc/swanctl/x509aa'   # Trusted X.509 Attribute Authority certificates
+      mkdir -p '/etc/swanctl/x509ac'   # Attribute Certificates
+      mkdir -p '/etc/swanctl/x509crl'  # Certificate Revocation Lists
+      mkdir -p '/etc/swanctl/pubkey'   # Raw public keys
+      mkdir -p '/etc/swanctl/private'  # Private keys in any format
+      mkdir -p '/etc/swanctl/rsa'      # PKCS#1 encoded RSA private keys
+      mkdir -p '/etc/swanctl/ecdsa'    # Plain ECDSA private keys
+      mkdir -p '/etc/swanctl/bliss'
+      mkdir -p '/etc/swanctl/pkcs8'    # PKCS#8 encoded private keys of any type
+      mkdir -p '/etc/swanctl/pkcs12'   # PKCS#12 containers
+    '';
+
+    systemd.services.strongswan-swanctl = {
+      description = "strongSwan IPsec IKEv1/IKEv2 daemon using swanctl";
+      wantedBy = [ "multi-user.target" ];
+      after    = [ "network-online.target" "keys.target" ];
+      wants    = [ "keys.target" ];
+      path = with pkgs; [ kmod iproute iptables utillinux ];
+      environment.STRONGSWAN_CONF = pkgs.writeTextFile {
+        name = "strongswan.conf";
+        text = paramsToConf cfg.strongswan strongswanParams;
+      };
+      restartTriggers = [ config.environment.etc."swanctl/swanctl.conf".source ];
+      serviceConfig = {
+        ExecStart     = "${cfg.package}/sbin/charon-systemd";
+        Type          = "notify";
+        ExecStartPost = "${cfg.package}/sbin/swanctl --load-all --noprompt";
+        ExecReload    = "${cfg.package}/sbin/swanctl --reload";
+        Restart       = "on-abnormal";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/networking/strongswan-swanctl/param-constructors.nix b/nixos/modules/services/networking/strongswan-swanctl/param-constructors.nix
new file mode 100644
index 00000000000..5e74a96664f
--- /dev/null
+++ b/nixos/modules/services/networking/strongswan-swanctl/param-constructors.nix
@@ -0,0 +1,162 @@
+# In the following context a parameter is an attribute set that
+# contains a NixOS option and a render function. It also contains the
+# attribute: '_type = "param"' so we can distinguish it from other
+# sets.
+#
+# The render function is used to convert the value of the option to a
+# snippet of strongswan.conf. Most parameters simply render their
+# value to a string. For example, take the following parameter:
+#
+#   threads = mkIntParam 10 "Threads to use for request handling.";
+#
+# When a users defines the corresponding option as for example:
+#
+#   services.strongswan-swanctl.strongswan.threads = 32;
+#
+# It will get rendered to the following snippet in strongswan.conf:
+#
+#   threads = 32
+#
+# Some parameters however need to be able to change the attribute
+# name. For example, take the following parameter:
+#
+#   id = mkPrefixedAttrsOfParam (mkOptionalStrParam "") "...";
+#
+# A user can define the corresponding option as for example:
+#
+#   id = {
+#     "foo" = "bar";
+#     "baz" = "qux";
+#   };
+#
+# This will get rendered to the following snippet:
+#
+#   foo-id = bar
+#   baz-id = qux
+#
+# For this reason the render function is not simply a function from
+# value -> string but a function from a value to an attribute set:
+# { "${name}" = string }. This allows parameters to change the attribute
+# name like in the previous example.
+
+lib :
+
+with lib;
+with (import ./param-lib.nix lib);
+
+rec {
+  mkParamOfType = type : strongswanDefault : description : {
+    _type = "param";
+    option = mkOption {
+      type = types.nullOr type;
+      default = null;
+      description = documentDefault description strongswanDefault;
+    };
+    render = single toString;
+  };
+
+  documentDefault = description : strongswanDefault :
+    if isNull strongswanDefault
+    then description
+    else description + ''
+      </para><para>
+      StrongSwan default: <literal><![CDATA[${builtins.toJSON strongswanDefault}]]></literal>
+    '';
+
+  single = f: name: value: { "${name}" = f value; };
+
+  mkStrParam         = mkParamOfType types.str;
+  mkOptionalStrParam = mkStrParam null;
+
+  mkEnumParam = values : mkParamOfType (types.enum values);
+
+  mkIntParam         = mkParamOfType types.int;
+  mkOptionalIntParam = mkIntParam null;
+
+  # We should have floats in Nix...
+  mkFloatParam = mkStrParam;
+
+  # TODO: Check for hex format:
+  mkHexParam         = mkStrParam;
+  mkOptionalHexParam = mkOptionalStrParam;
+
+  # TODO: Check for duration format:
+  mkDurationParam         = mkStrParam;
+  mkOptionalDurationParam = mkOptionalStrParam;
+
+  mkYesNoParam = strongswanDefault : description : {
+    _type = "param";
+    option = mkOption {
+      type = types.nullOr types.bool;
+      default = null;
+      description = documentDefault description strongswanDefault;
+    };
+    render = single (b: if b then "yes" else "no");
+  };
+  yes = true;
+  no  = false;
+
+  mkSpaceSepListParam = mkSepListParam " ";
+  mkCommaSepListParam = mkSepListParam ",";
+
+  mkSepListParam = sep : strongswanDefault : description : {
+    _type = "param";
+    option = mkOption {
+      type = types.nullOr (types.listOf types.str);
+      default = null;
+      description = documentDefault description strongswanDefault;
+    };
+    render = single (value: concatStringsSep sep value);
+  };
+
+  mkAttrsOfParams = params :
+    mkAttrsOf params (types.submodule {options = paramsToOptions params;});
+
+  mkAttrsOfParam = param :
+    mkAttrsOf param param.option.type;
+
+  mkAttrsOf = param : option : description : {
+    _type = "param";
+    option = mkOption {
+      type = types.attrsOf option;
+      default = {};
+      inherit description;
+    };
+    render = single (attrs:
+      (paramsToRenderedStrings attrs
+        (mapAttrs (_n: _v: param) attrs)));
+  };
+
+  mkPrefixedAttrsOfParams = params :
+    mkPrefixedAttrsOf params (types.submodule {options = paramsToOptions params;});
+
+  mkPrefixedAttrsOfParam = param :
+    mkPrefixedAttrsOf param param.option.type;
+
+  mkPrefixedAttrsOf = p : option : description : {
+    _type = "param";
+    option = mkOption {
+      type = types.attrsOf option;
+      default = {};
+      inherit description;
+    };
+    render = prefix: attrs:
+      let prefixedAttrs = mapAttrs' (name: nameValuePair "${prefix}-${name}") attrs;
+      in paramsToRenderedStrings prefixedAttrs
+           (mapAttrs (_n: _v: p) prefixedAttrs);
+  };
+
+  mkPostfixedAttrsOfParams = params : description : {
+    _type = "param";
+    option = mkOption {
+      type = types.attrsOf (types.submodule {options = paramsToOptions params;});
+      default = {};
+      inherit description;
+    };
+    render = postfix: attrs:
+      let postfixedAttrs = mapAttrs' (name: nameValuePair "${name}-${postfix}") attrs;
+      in paramsToRenderedStrings postfixedAttrs
+           (mapAttrs (_n: _v: params) postfixedAttrs);
+  };
+
+}
diff --git a/nixos/modules/services/networking/strongswan-swanctl/param-lib.nix b/nixos/modules/services/networking/strongswan-swanctl/param-lib.nix
new file mode 100644
index 00000000000..fb87e81f321
--- /dev/null
+++ b/nixos/modules/services/networking/strongswan-swanctl/param-lib.nix
@@ -0,0 +1,82 @@
+lib :
+
+with lib;
+
+rec {
+  paramsToConf = cfg : ps : mkConf 0 (paramsToRenderedStrings cfg ps);
+
+  # mkConf takes an indentation level (which usually starts at 0) and a nested
+  # attribute set of strings and will render that set to a strongswan.conf style
+  # configuration format. For example:
+  #
+  #   mkConf 0 {a = "1"; b = { c = { "foo" = "2"; "bar" = "3"; }; d = "4";};}   =>   ''
+  #   a = 1
+  #   b {
+  #     c {
+  #       foo = 2
+  #       bar = 3
+  #     }
+  #     d = 4
+  #   }''
+  mkConf = indent : ps :
+    concatMapStringsSep "\n"
+      (name:
+        let value = ps."${name}";
+            indentation = replicate indent " ";
+        in
+        indentation + (
+          if isAttrs value
+          then "${name} {\n" +
+                 mkConf (indent + 2) value + "\n" +
+               indentation + "}"
+          else "${name} = ${value}"
+        )
+      )
+      (attrNames ps);
+
+  replicate = n : c : concatStrings (builtins.genList (_x : c) n);
+
+  # `paramsToRenderedStrings cfg ps` converts the NixOS configuration `cfg`
+  # (typically the "config" argument of a NixOS module) and the set of
+  # parameters `ps` (an attribute set where the values are constructed using the
+  # parameter constructors in ./param-constructors.nix) to a nested attribute
+  # set of strings (rendered parameters).
+  paramsToRenderedStrings = cfg : ps :
+    filterEmptySets (
+      (mapParamsRecursive (path: name: param:
+        let value = attrByPath path null cfg;
+        in optionalAttrs (!isNull value) (param.render name value)
+      ) ps));
+
+  filterEmptySets = set : filterAttrs (n: v: !(isNull v)) (mapAttrs (name: value:
+    if isAttrs value
+    then let value' = filterEmptySets value;
+         in if value' == {}
+            then null
+            else value'
+    else value
+  ) set);
+
+  # Recursively map over every parameter in the given attribute set.
+  mapParamsRecursive = mapAttrsRecursiveCond' (as: (!(as ? "_type" && as._type == "param")));
+
+  mapAttrsRecursiveCond' = cond: f: set:
+    let
+      recurse = path: set:
+        let
+          g =
+            name: value:
+            if isAttrs value && cond value
+              then { "${name}" = recurse (path ++ [name]) value; }
+              else f (path ++ [name]) name value;
+        in mapAttrs'' g set;
+    in recurse [] set;
+
+  mapAttrs'' = f: set:
+    foldl' (a: b: a // b) {} (map (attr: f attr set.${attr}) (attrNames set));
+
+  # Extract the options from the given set of parameters.
+  paramsToOptions = ps :
+    mapParamsRecursive (_path: name: param: { "${name}" = param.option; }) ps;
+
+}
diff --git a/nixos/modules/services/networking/strongswan-swanctl/strongswan-charon-params.nix b/nixos/modules/services/networking/strongswan-swanctl/strongswan-charon-params.nix
new file mode 100644
index 00000000000..3eec9886811
--- /dev/null
+++ b/nixos/modules/services/networking/strongswan-swanctl/strongswan-charon-params.nix
@@ -0,0 +1,568 @@
+lib: with (import ./param-constructors.nix lib);
+
+let loglevelParams = import ./strongswan-loglevel-params.nix lib;
+in {
+  accept_unencrypted_mainmode_messages = mkYesNoParam no ''
+    Accept unencrypted ID and HASH payloads in IKEv1 Main Mode. Some
+    implementations send the third Main Mode message unencrypted, probably
+    to find the PSKs for the specified ID for authentication. This is very
+    similar to Aggressive Mode, and has the same security implications: A
+    passive attacker can sniff the negotiated Identity, and start brute
+    forcing the PSK using the HASH payload. It is recommended to keep this
+    option to no, unless you know exactly what the implications are and
+    require compatibility to such devices (for example, some SonicWall
+    boxes).
+  '';
+
+  block_threshold = mkIntParam 5 ''
+    Maximum number of half-open IKE_SAs for a single peer IP.
+  '';
+
+  cache_crls = mkYesNoParam no ''
+    Whether Certicate Revocation Lists (CRLs) fetched via HTTP or LDAP
+    should be saved under a unique file name derived from the public
+    key of the Certification Authority (CA) to
+    <literal>/etc/ipsec.d/crls</literal> (stroke) or
+    <literal>/etc/swanctl/x509crl</literal> (vici), respectively.
+  '';
+
+  cert_cache = mkYesNoParam yes ''
+    Whether relations in validated certificate chains should be cached in memory.
+  '';
+
+  cisco_unity = mkYesNoParam no ''
+    Send Cisco Unity vendor ID payload (IKEv1 only), see unity plugin.
+  '';
+
+  close_ike_on_child_failure = mkYesNoParam no ''
+    Close the IKE_SA if setup of the CHILD_SA along with IKE_AUTH failed.
+  '';
+
+  cookie_threshold = mkIntParam 10 ''
+    Number of half-open IKE_SAs that activate the cookie mechanism.
+  '';
+
+  crypto_test.bench = mkYesNoParam no ''
+    Benchmark crypto algorithms and order them by efficiency.
+  '';
+
+  crypto_test.bench_size = mkIntParam 1024 ''
+    Buffer size used for crypto benchmark.
+  '';
+
+  crypto_test.bench_time = mkIntParam 50 ''
+    Number of iterations to test each algorithm.
+  '';
+
+  crypto_test.on_add = mkYesNoParam no ''
+    Test crypto algorithms during registration
+    (requires test vectors provided by the test-vectors plugin).
+  '';
+
+  crypto_test.on_create = mkYesNoParam no ''
+    Test crypto algorithms on each crypto primitive instantiation.
+  '';
+
+  crypto_test.required = mkYesNoParam no ''
+    Strictly require at least one test vector to enable an algorithm.
+  '';
+
+  crypto_test.rng_true = mkYesNoParam no ''
+    Whether to test RNG with TRUE quality; requires a lot of entropy.
+  '';
+
+  delete_rekeyed = mkYesNoParam no ''
+    Delete CHILD_SAs right after they got successfully rekeyed (IKEv1 only).
+    Reduces the number of stale CHILD_SAs in scenarios with a lot of rekeyings.
+    However, this might cause problems with implementations that continue
+    to use rekeyed SAs until they expire.
+  '';
+
+  delete_rekeyed_delay = mkIntParam 5 ''
+    Delay in seconds until inbound IPsec SAs are deleted after rekeyings
+    (IKEv2 only).
+    </para><para>
+    To process delayed packets the inbound part of a CHILD_SA is kept
+    installed up to the configured number of seconds after it got replaced
+    during a rekeying. If set to 0 the CHILD_SA will be kept installed until
+    it expires (if no lifetime is set it will be destroyed immediately).
+  '';
+
+  dh_exponent_ansi_x9_42 = mkYesNoParam yes ''
+    Use ANSI X9.42 DH exponent size or optimum size matched to
+    cryptographical strength.
+  '';
+
+  dlopen_use_rtld_now = mkYesNoParam no ''
+    Use RTLD_NOW with dlopen() when loading plugins and IMV/IMCs to reveal
+    missing symbols immediately. Useful during development of custom plugins.
+  '';
+
+  dns1 = mkOptionalStrParam ''
+    DNS server assigned to peer via configuration payload (CP), see attr plugin.
+  '';
+
+  dns2 = mkOptionalStrParam ''
+    DNS server assigned to peer via configuration payload (CP).
+  '';
+
+  dos_protection = mkYesNoParam yes ''
+    Enable Denial of Service protection using cookies and aggressiveness checks.
+  '';
+
+  ecp_x_coordinate_only = mkYesNoParam yes ''
+    Compliance with the errata for RFC 4753.
+  '';
+
+  filelog = mkAttrsOfParams ({
+    append = mkYesNoParam yes ''
+      If this option is enabled log entries are appended to the existing file.
+    '';
+
+    flush_line = mkYesNoParam no ''
+      Enabling this option disables block buffering and enables line
+      buffering. That is, a flush to disk is enforced for each logged line.
+    '';
+
+    ike_name = mkYesNoParam no ''
+      Prefix each log entry with the connection name and a unique numerical
+      identifier for each IKE_SA.
+    '';
+
+    time_format = mkOptionalStrParam ''
+      Prefix each log entry with a timestamp. The option accepts a format string
+      as passed to strftime(3).
+    '';
+
+    time_add_ms = mkYesNoParam no ''
+      Adds the milliseconds within the current second after the timestamp
+      (separated by a dot, so time_format should end with %S or %T)
+    '';
+  } // loglevelParams) ''Section to define file loggers, see LoggerConfiguration.'';
+
+  flush_auth_cfg = mkYesNoParam no ''
+    If enabled objects used during authentication (certificates, identities
+    etc.) are released to free memory once an IKE_SA is
+    established. Enabling this might conflict with plugins that later need
+    access to e.g. the used certificates.
+  '';
+
+  follow_redirects = mkYesNoParam yes ''
+    Whether to follow IKEv2 redirects (RFC 5685).
+  '';
+
+  fragment_size = mkIntParam 1280 ''
+    Maximum size (complete IP datagram size in bytes) of a sent IKE fragment
+    when using proprietary IKEv1 or standardized IKEv2 fragmentation,
+    defaults to 1280 (use 0 for address family specific default values,
+    which uses a lower value for IPv4). If specified this limit is used for
+    both IPv4 and IPv6.
+  '';
+
+  group = mkOptionalStrParam ''
+    Name of the group the daemon changes to after startup.
+  '';
+
+  half_open_timeout = mkIntParam 30 ''
+    Timeout in seconds for connecting IKE_SAs, also see IKE_SA_INIT dropping.
+  '';
+
+  hash_and_url = mkYesNoParam no ''
+    Enable hash and URL support.
+  '';
+
+  host_resolver.max_threads = mkIntParam 3 ''
+    Maximum number of concurrent resolver threads (they are terminated if unused).
+  '';
+
+  host_resolver.min_threads = mkIntParam 0 ''
+    Minimum number of resolver threads to keep around.
+  '';
+
+  i_dont_care_about_security_and_use_aggressive_mode_psk = mkYesNoParam no ''
+    If enabled responders are allowed to use IKEv1 Aggressive Mode with
+    pre-shared keys, which is discouraged due to security concerns (offline
+    attacks on the openly transmitted hash of the PSK).
+  '';
+
+  ignore_acquire_ts = mkYesNoParam no ''
+    If this is disabled the traffic selectors from the kernel's acquire
+    events, which are derived from the triggering packet, are prepended to
+    the traffic selectors from the configuration for IKEv2 connection. By
+    enabling this, such specific traffic selectors will be ignored and only
+    the ones in the config will be sent. This always happens for IKEv1
+    connections as the protocol only supports one set of traffic selectors
+    per CHILD_SA.
+  '';
+
+  ignore_routing_tables = mkSpaceSepListParam [] ''
+    A space-separated list of routing tables to be excluded from route lookup.
+  '';
+
+  ikesa_limit = mkIntParam 0 ''
+    Maximum number of IKE_SAs that can be established at the same time
+    before new connection attempts are blocked.
+  '';
+
+  ikesa_table_segments = mkIntParam 1 ''
+    Number of exclusively locked segments in the hash table, see IKE_SA
+    lookup tuning.
+  '';
+
+  ikesa_table_size = mkIntParam 1 ''
+    Size of the IKE_SA hash table, see IKE_SA lookup tuning.
+  '';
+
+  inactivity_close_ike = mkYesNoParam no ''
+    Whether to close IKE_SA if the only CHILD_SA closed due to inactivity.
+  '';
+
+  init_limit_half_open = mkIntParam 0 ''
+    Limit new connections based on the current number of half open IKE_SAs,
+    see IKE_SA_INIT dropping.
+  '';
+
+  init_limit_job_load = mkIntParam 0 ''
+    Limit new connections based on the number of jobs currently queued for
+    processing, see IKE_SA_INIT dropping.
+  '';
+
+  initiator_only = mkYesNoParam no ''
+    Causes charon daemon to ignore IKE initiation requests.
+  '';
+
+  install_routes = mkYesNoParam yes ''
+    Install routes into a separate routing table for established IPsec
+    tunnels. If disabled a more efficient lookup for source and next-hop
+    addresses is used since 5.5.2.
+  '';
+
+  install_virtual_ip = mkYesNoParam yes ''
+    Install virtual IP addresses.
+  '';
+
+  install_virtual_ip_on = mkOptionalStrParam ''
+    The name of the interface on which virtual IP addresses should be
+    installed. If not specified the addresses will be installed on the
+    outbound interface.
+  '';
+
+  integrity_test = mkYesNoParam no ''
+    Check daemon, libstrongswan and plugin integrity at startup.
+  '';
+
+  interfaces_ignore = mkCommaSepListParam [] ''
+    List of network interfaces that should be ignored, if
+    <option>interfaces_use</option> is specified this option has no effect.
+  '';
+
+  interfaces_use = mkCommaSepListParam [] ''
+    List of network interfaces that should be used by
+    charon. All other interfaces are ignored.
+  '';
+
+  keep_alive = mkIntParam 20 ''
+    NAT keep alive interval in seconds.
+  '';
+
+  leak_detective.detailed = mkYesNoParam yes ''
+    Includes source file names and line numbers in leak detective output.
+  '';
+
+  leak_detective.usage_threshold = mkIntParam 10240 ''
+    Threshold in bytes for leaks to be reported (0 to report all).
+  '';
+
+  leak_detective.usage_threshold_count = mkIntParam 0 ''
+    Threshold in number of allocations for leaks to be reported (0 to report
+    all).
+  '';
+
+  load = mkSpaceSepListParam [] ''
+    Plugins to load in IKEv2 charon daemon, see PluginLoad.
+  '';
+
+  load_modular = mkYesNoParam no ''
+    If enabled the list of plugins to load is determined by individual load
+    settings for each plugin, see PluginLoad.
+  '';
+
+  make_before_break = mkYesNoParam no ''
+    Initiate IKEv2 reauthentication with a make-before-break instead of a
+    break-before-make scheme. Make-before-break uses overlapping IKE and
+    CHILD_SA during reauthentication by first recreating all new SAs before
+    deleting the old ones. This behavior can be beneficial to avoid
+    connectivity gaps during reauthentication, but requires support for
+    overlapping SAs by the peer. strongSwan can handle such overlapping SAs
+    since 5.3.0.
+  '';
+
+  max_ikev1_exchanges = mkIntParam 3 ''
+    Maximum number of IKEv1 phase 2 exchanges per IKE_SA to keep state about
+    and track concurrently.
+  '';
+
+  max_packet = mkIntParam 10000 ''
+    Maximum packet size accepted by charon.
+  '';
+
+  multiple_authentication = mkYesNoParam yes ''
+    Enable multiple authentication exchanges (RFC 4739).
+  '';
+
+  nbns1 = mkOptionalStrParam ''
+    WINS server assigned to peer via configuration payload (CP), see attr
+    plugin.
+  '';
+
+  nbns2 = mkOptionalStrParam ''
+    WINS server assigned to peer via configuration payload (CP).
+  '';
+
+  port = mkIntParam 500 ''
+    UDP port used locally. If set to 0 a random port will be allocated.
+  '';
+
+  port_nat_t = mkIntParam 4500 ''
+    UDP port used locally in case of NAT-T. If set to 0 a random port will
+    be allocated. Has to be different from charon.port, otherwise a random
+    port will be allocated.
+  '';
+
+  prefer_best_path = mkYesNoParam no ''
+    By default, charon keeps SAs on the routing path with addresses it
+    previously used if that path is still usable. By enabling this option,
+    it tries more aggressively to update SAs with MOBIKE on routing priority
+    changes using the cheapest path. This adds more noise, but allows to
+    dynamically adapt SAs to routing priority changes. This option has no
+    effect if MOBIKE is not supported or disabled.
+  '';
+
+  prefer_configured_proposals = mkYesNoParam yes ''
+    Prefer locally configured proposals for IKE/IPsec over supplied ones as
+    responder (disabling this can avoid keying retries due to
+    INVALID_KE_PAYLOAD notifies).
+  '';
+
+  prefer_temporary_addrs = mkYesNoParam no ''
+    By default public IPv6 addresses are preferred over temporary ones
+    (according to RFC 4941), to make connections more stable. Enable this
+    option to reverse this.
+  '';
+
+  process_route = mkYesNoParam yes ''
+    Process RTM_NEWROUTE and RTM_DELROUTE events.
+  '';
+
+  processor.priority_threads = {
+    critical = mkIntParam 0 ''
+      Threads reserved for CRITICAL priority class jobs.
+    '';
+
+    high = mkIntParam 0 ''
+      Threads reserved for HIGH priority class jobs.
+    '';
+
+    medium = mkIntParam 0 ''
+      Threads reserved for MEDIUM priority class jobs.
+    '';
+
+    low = mkIntParam 0 ''
+      Threads reserved for LOW priority class jobs.
+    '';
+  };
+
+  receive_delay = mkIntParam 0 ''
+    Delay in ms for receiving packets, to simulate larger RTT.
+  '';
+
+  receive_delay_response = mkYesNoParam yes ''
+    Delay response messages.
+  '';
+
+  receive_delay_request = mkYesNoParam yes ''
+    Delay request messages.
+  '';
+
+  receive_delay_type = mkIntParam 0 ''
+    Specific IKEv2 message type to delay, 0 for any.
+  '';
+
+  replay_window = mkIntParam 32 ''
+    Size of the AH/ESP replay window, in packets.
+  '';
+
+  retransmit_base = mkFloatParam "1.8" ''
+    Base to use for calculating exponential back off, see Retransmission.
+  '';
+
+  retransmit_jitter = mkIntParam 0 ''
+    Maximum jitter in percent to apply randomly to calculated retransmission
+    timeout (0 to disable).
+  '';
+
+  retransmit_limit = mkIntParam 0 ''
+    Upper limit in seconds for calculated retransmission timeout (0 to
+    disable).
+  '';
+
+  retransmit_timeout = mkFloatParam "4.0" ''
+    Timeout in seconds before sending first retransmit.
+  '';
+
+  retransmit_tries = mkIntParam 5 ''
+    Number of times to retransmit a packet before giving up.
+  '';
+
+  retry_initiate_interval = mkIntParam 0 ''
+    Interval in seconds to use when retrying to initiate an IKE_SA (e.g. if
+    DNS resolution failed), 0 to disable retries.
+  '';
+
+  reuse_ikesa = mkYesNoParam yes ''
+    Initiate CHILD_SA within existing IKE_SAs (always enabled for IKEv1).
+  '';
+
+  routing_table = mkIntParam 220 ''
+    Numerical routing table to install routes to.
+  '';
+
+  routing_table_prio = mkIntParam 220 ''
+    Priority of the routing table.
+  '';
+
+  send_delay = mkIntParam 0 ''
+    Delay in ms for sending packets, to simulate larger RTT.
+  '';
+
+  send_delay_request = mkYesNoParam yes ''
+    Delay request messages.
+  '';
+
+  send_delay_response = mkYesNoParam yes ''
+    Delay response messages.
+  '';
+
+  send_delay_type = mkIntParam 0 ''
+    Specific IKEv2 message type to delay, 0 for any.
+  '';
+
+  send_vendor_id = mkYesNoParam no ''
+    Send strongSwan vendor ID payload.
+  '';
+
+  signature_authentication = mkYesNoParam yes ''
+    Whether to enable Signature Authentication as per RFC 7427.
+  '';
+
+  signature_authentication_constraints = mkYesNoParam yes ''
+    If enabled, signature schemes configured in rightauth, in addition to
+    getting used as constraints against signature schemes employed in the
+    certificate chain, are also used as constraints against the signature
+    scheme used by peers during IKEv2.
+  '';
+
+  spi_min = mkHexParam "0xc0000000" ''
+    The lower limit for SPIs requested from the kernel for IPsec SAs. Should
+    not be set lower than 0x00000100 (256), as SPIs between 1 and 255 are
+    reserved by IANA.
+  '';
+
+  spi_max = mkHexParam "0xcfffffff" ''
+    The upper limit for SPIs requested from the kernel for IPsec SAs.
+  '';
+
+  start-scripts = mkAttrsOfParam (mkStrParam "" "") ''
+    Section containing a list of scripts (name = path) that are executed
+    when the daemon is started.
+  '';
+
+  stop-scripts = mkAttrsOfParam (mkStrParam "" "") ''
+    Section containing a list of scripts (name = path) that are executed
+    when the daemon is terminated.
+  '';
+
+  syslog = loglevelParams // {
+    identifier = mkOptionalStrParam ''
+      Identifier for use with openlog(3).
+      </para><para>
+      Global identifier used for an openlog(3) call, prepended to each log
+      message by syslog.  If not configured, openlog(3) is not called, so
+      the value will depend on system defaults (often the program name).
+    '';
+
+    ike_name = mkYesNoParam no ''
+      Prefix each log entry with the connection name and a unique numerical
+      identifier for each IKE_SA.
+    '';
+  };
+
+  threads = mkIntParam 16 ''
+    Number of worker threads in charon. Several of these are reserved for
+    long running tasks in internal modules and plugins. Therefore, make sure
+    you don't set this value too low. The number of idle worker threads
+    listed in ipsec statusall might be used as indicator on the number of
+    reserved threads (JobPriority has more on this).
+  '';
+
+  user = mkOptionalStrParam ''
+    Name of the user the daemon changes to after startup.
+  '';
+
+  x509.enforce_critical = mkYesNoParam yes ''
+    Discard certificates with unsupported or unknown critical extensions.
+  '';
+
+  plugins = import ./strongswan-charon-plugins-params.nix lib;
+
+  imcv = {
+    assessment_result = mkYesNoParam yes ''
+      Whether IMVs send a standard IETF Assessment Result attribute.
+    '';
+
+    database = mkOptionalStrParam ''
+      Global IMV policy database URI. If it contains a password, make sure to
+      adjust the permissions of the config file accordingly.
+    '';
+
+    os_info.default_password_enabled = mkYesNoParam no ''
+      Manually set whether a default password is enabled.
+    '';
+
+    os_info.name = mkOptionalStrParam ''
+      Manually set the name of the client OS (e.g. <literal>NixOS</literal>).
+    '';
+
+    os_info.version = mkOptionalStrParam ''
+      Manually set the version of the client OS (e.g. <literal>17.09</literal>).
+    '';
+
+    policy_script = mkStrParam "ipsec _imv_policy" ''
+      Script called for each TNC connection to generate IMV policies.
+    '';
+  };
+
+  tls = {
+    cipher = mkSpaceSepListParam [] ''
+      List of TLS encryption ciphers.
+    '';
+
+    key_exchange = mkSpaceSepListParam [] ''
+      List of TLS key exchange methods.
+    '';
+
+    mac = mkSpaceSepListParam [] ''
+      List of TLS MAC algorithms.
+    '';
+
+    suites = mkSpaceSepListParam [] ''
+      List of TLS cipher suites.
+    '';
+  };
+
+  tnc = {
+    libtnccs.tnc_config = mkStrParam "/etc/tnc_config" ''
+      TNC IMC/IMV configuration file.
+    '';
+  };
+}
diff --git a/nixos/modules/services/networking/strongswan-swanctl/strongswan-charon-plugins-params.nix b/nixos/modules/services/networking/strongswan-swanctl/strongswan-charon-plugins-params.nix
new file mode 100644
index 00000000000..56a253d85d3
--- /dev/null
+++ b/nixos/modules/services/networking/strongswan-swanctl/strongswan-charon-plugins-params.nix
@@ -0,0 +1,1070 @@
+lib : with (import ./param-constructors.nix lib); {
+  addrblock.strict = mkYesNoParam yes ''
+    If enabled, a subject certificate without an RFC 3779 address block
+    extension is rejected if the issuer certificate has such an addrblock
+    extension. If disabled, subject certificates issued without addrblock
+    extension are accepted without any traffic selector checks and no policy
+    is enforced by the plugin.
+  '';
+
+  android_log.loglevel = mkIntParam 1 ''
+    Loglevel for logging to Android specific logger.
+  '';
+
+  attr = mkAttrsOfParam (mkCommaSepListParam [] "") ''
+    Section to specify arbitrary attributes that are assigned to a peer
+    via configuration payload, see attr plugin.
+    </para><para>
+    The attribute can be either
+    <literal>address</literal>,
+    <literal>netmask</literal>,
+    <literal>dns</literal>,
+    <literal>nbns</literal>,
+    <literal>dhcp</literal>,
+    <literal>subnet</literal>,
+    <literal>split-include</literal>,
+    <literal>split-exclude</literal>
+    or the numeric identifier of the attribute type. The assigned value can be
+    an IPv4/IPv6 address, a subnet in CIDR notation or an arbitrary value
+    depending on the attribute type. Since some attribute types accept multiple
+    values all values must be specified as a list.
+  '';
+
+  attr-sql.crash_recovery = mkYesNoParam yes ''
+    Release all online leases during startup. Disable this to share the DB
+    between multiple VPN gateways.
+  '';
+
+  attr-sql.database  = mkOptionalStrParam ''
+    Database URI for attr-sql plugin used by charon. If it contains a
+    password, make sure to adjust the permissions of the config file
+    accordingly.
+  '';
+
+  attr-sql.lease_history = mkYesNoParam yes ''
+    Enable logging of SQL IP pool leases.
+  '';
+
+  bliss.use_bliss_b = mkYesNoParam yes ''
+    Use the enhanced BLISS-B key generation and signature algorithm.
+  '';
+
+  bypass-lan.interfaces_ignore = mkCommaSepListParam [] ''
+    List of network interfaces for which connected subnets
+    should be ignored, if interfaces_use is specified this option has no
+    effect.
+  '';
+
+  bypass-lan.interfaces_use = mkCommaSepListParam [] ''
+    List of network interfaces for which connected subnets
+    should be considered. All other interfaces are ignored.
+  '';
+
+  certexpire.csv.cron = mkOptionalStrParam ''
+    Cron style string specifying CSV export times, see certexpire for
+    details.
+  '';
+
+  certexpire.csv.empty_string = mkOptionalStrParam ''
+    String to use in empty intermediate CA fields.
+  '';
+
+  certexpire.csv.fixed_fields = mkYesNoParam yes ''
+    Use a fixed intermediate CA field count.
+  '';
+
+  certexpire.csv.force = mkYesNoParam yes ''
+    Force export of all trustchains we have a private key for.
+  '';
+
+  certexpire.csv.format = mkStrParam "%d:%m:%Y" ''
+    strftime(3) format string to export expiration dates as.
+  '';
+
+  certexpire.csv.local = mkOptionalStrParam ''
+    strftime(3) format string for the CSV file name to export local
+    certificates to.
+  '';
+
+  certexpire.csv.remote = mkOptionalStrParam ''
+    strftime(3) format string for the CSV file name to export remote
+    certificates to.
+  '';
+
+  certexpire.csv.separator = mkStrParam "," ''
+    CSV field separator.
+  '';
+
+  coupling.file = mkOptionalStrParam ''
+    File to store coupling list to, see certcoupling plugin for details.
+  '';
+
+  coupling.hash = mkStrParam "sha1" ''
+    Hashing algorithm to fingerprint coupled certificates.
+  '';
+
+  coupling.max = mkIntParam 1 ''
+    Maximum number of coupling entries to create.
+  '';
+
+  curl.redir = mkIntParam (-1) ''
+    Maximum number of redirects followed by the plugin, set to 0 to disable
+    following redirects, set to -1 for no limit.
+  '';
+
+  dhcp.force_server_address = mkYesNoParam no ''
+    Always use the configured server address, see DHCP plugin for details.
+  '';
+
+  dhcp.identity_lease = mkYesNoParam no ''
+    Derive user-defined MAC address from hash of IKEv2 identity.
+  '';
+
+  dhcp.interface = mkOptionalStrParam ''
+    Interface name the plugin uses for address allocation. The default is to
+    bind to any and let the system decide which way to route the packets to
+    the DHCP server.
+  '';
+
+  dhcp.server = mkStrParam "255.255.255.255" ''
+    DHCP server unicast or broadcast IP address.
+  '';
+
+  dnscert.enable = mkYesNoParam no ''
+    Enable fetching of CERT RRs via DNS.
+  '';
+
+  duplicheck.enable = mkYesNoParam yes ''
+    Enable duplicheck plugin (if loaded).
+  '';
+
+  duplicheck.socket = mkStrParam "unix://\${piddir}/charon.dck" ''
+    Socket provided by the duplicheck plugin.
+  '';
+
+  eap-aka.request_identity = mkYesNoParam yes "";
+
+  eap-aka-3ggp2.seq_check = mkOptionalStrParam ''
+    Enable to activate sequence check of the AKA SQN values in order to trigger
+    resync cycles.
+  '';
+
+  eap-dynamic.prefer_user = mkYesNoParam no ''
+    If enabled, the eap-dynamic plugin will prefer the order of the EAP
+    methods in an EAP-Nak message sent by a client over the one configured
+    locally.
+  '';
+
+  eap-dynamic.preferred = mkCommaSepListParam [] ''
+    The preferred EAP method(s) to be used by the eap-dynamic plugin. If it is
+    not set, the first registered method will be used initially. The methods
+    are tried in the given order before trying the rest of the registered
+    methods.
+  '';
+
+  eap-gtc.backend = mkStrParam "pam" ''
+    XAuth backend to be used for credential verification, see EAP-GTC.
+  '';
+
+  eap-peap.fragment_size = mkIntParam 1024 ''
+    Maximum size of an EAP-PEAP packet.
+  '';
+
+  eap-peap.max_message_count = mkIntParam 32 ''
+    Maximum number of processed EAP-PEAP packets.
+  '';
+
+  eap-peap.include_length = mkYesNoParam no ''
+    Include length in non-fragmented EAP-PEAP packets.
+  '';
+
+  eap-peap.phase2_method = mkStrParam "mschapv2" ''
+    Phase2 EAP client authentication method.
+  '';
+
+  eap-peap.phase2_piggyback = mkYesNoParam no ''
+    Phase2 EAP Identity request piggybacked by server onto TLS Finished
+    message.
+  '';
+
+  eap-peap.phase2_tnc = mkYesNoParam no ''
+    Start phase2 EAP-TNC protocol after successful client authentication.
+  '';
+
+  eap-peap.request_peer_auth = mkYesNoParam no ''
+    Request peer authentication based on a client certificate.
+  '';
+
+  eap-radius.accounting = mkYesNoParam no ''
+    Enable EAP-RADIUS accounting.
+  '';
+
+  eap-radius.accounting_close_on_timeout = mkYesNoParam yes ''
+    Close the IKE_SA if there is a timeout during interim RADIUS accounting
+    updates.
+  '';
+
+  eap-radius.accounting_interval = mkIntParam 0 ''
+    Interval in seconds for interim RADIUS accounting updates, if not
+    specified by the RADIUS server in the Access-Accept message.
+  '';
+
+  eap-radius.accounting_requires_vip = mkYesNoParam no ''
+    If enabled, accounting is disabled unless an IKE_SA has at least one
+    virtual IP.
+  '';
+
+  eap-radius.class_group = mkYesNoParam no ''
+    Use the class attribute sent in the Access-Accept message as group
+    membership information, see EapRadius.
+  '';
+
+  eap-radius.close_all_on_timeout = mkYesNoParam no ''
+    Closes all IKE_SAs if communication with the RADIUS server times out. If
+    it is not set only the current IKE_SA is closed.
+  '';
+
+  eap-radius.dae.enable = mkYesNoParam no ''
+    Enables support for the Dynamic Authorization Extension (RFC 5176).
+  '';
+
+  eap-radius.dae.listen = mkStrParam "0.0.0.0" ''
+    Address to listen for DAE messages from the RADIUS server.
+  '';
+
+  eap-radius.dae.port = mkIntParam 3799 ''
+    Port to listen for DAE requests.
+  '';
+
+  eap-radius.dae.secret = mkOptionalStrParam ''
+    Shared secret used to verify/sign DAE messages.If set, make sure to
+    adjust the permissions of the config file accordingly.
+  '';
+
+  eap-radius.eap_start = mkYesNoParam no ''
+    Send EAP-Start instead of EAP-Identity to start RADIUS conversation.
+  '';
+
+  eap-radius.filter_id = mkYesNoParam no ''
+    Use the filter_id attribute sent in the RADIUS-Accept message as group
+    membership if the RADIUS tunnel_type attribute is set to ESP.
+  '';
+
+  eap-radius.forward.ike_to_radius = mkOptionalStrParam ''
+    RADIUS attributes to be forwarded from IKEv2 to RADIUS (can be defined
+    by name or attribute number, a colon can be used to specify
+    vendor-specific attributes, e.g. Reply-Message, or 11, or 36906:12).
+  '';
+
+  eap-radius.forward.radius_to_ike = mkOptionalStrParam ''
+    Same as above but from RADIUS to IKEv2, a strongSwan specific private
+    notify (40969) is used to transmit the attributes.
+  '';
+
+  eap-radius.id_prefix = mkOptionalStrParam ''
+    Prefix to EAP-Identity, some AAA servers use a IMSI prefix to select the
+    EAP method.
+  '';
+
+  eap-radius.nas_identifier = mkStrParam "strongSwan" ''
+    NAS-Identifier to include in RADIUS messages.
+  '';
+
+  eap-radius.port = mkIntParam 1812 ''
+    Port of RADIUS server (authentication).
+  '';
+
+  eap-radius.retransmit_base = mkFloatParam "1.4" ''
+    Base to use for calculating exponential back off.
+  '';
+
+  eap-radius.retransmit_timeout = mkFloatParam "2.0" ''
+    Timeout in seconds before sending first retransmit.
+  '';
+
+  eap-radius.retransmit_tries = mkIntParam 4 ''
+    Number of times to retransmit a packet before giving up.
+  '';
+
+  eap-radius.secret = mkOptionalStrParam ''
+    Shared secret between RADIUS and NAS. If set, make sure to adjust the
+    permissions of the config file accordingly.
+  '';
+
+  eap-radius.server = mkOptionalStrParam ''
+    IP/Hostname of RADIUS server.
+  '';
+
+  eap-radius.servers = mkAttrsOfParams {
+    nas_identifier = mkStrParam "strongSwan" ''
+      The nas_identifer (default: strongSwan) identifies the gateway against the
+      RADIUS server and allows it to enforce a policy, for example.
+    '';
+
+    secret = mkOptionalStrParam "";
+
+    sockets = mkIntParam 1 ''
+      The number of pre-allocated sockets to use. A value of 5 allows the
+      gateway to authentication 5 clients simultaneously over RADIUS.
+    '';
+
+    auth_port = mkIntParam 1812 ''
+      RADIUS UDP port
+    '';
+
+    address = mkOptionalStrParam ''
+      The server's IP/Hostname.
+    '';
+
+    acct_port = mkIntParam 1813 ''
+      Accounting port.
+    '';
+
+    preference = mkIntParam 0 ''
+      With the preference paramter of a server, priorities for specific servers
+      can be defined. This allows to use a secondary RADIUS server only if the
+      first gets unresponsive, or if it is overloaded.
+    '';
+  } ''Section to specify multiple RADIUS servers, see EapRadius.'';
+
+  eap-radius.sockets = mkIntParam 1 ''
+    Number of sockets (ports) to use, increase for high load.
+  '';
+
+  eap-radius.xauth = mkAttrsOfParams {
+    nextpin  = mkOptionalStrParam "";
+    password = mkOptionalStrParam "";
+    passcode = mkOptionalStrParam "";
+    answer   = mkOptionalStrParam "";
+  } ''
+    Section to configure multiple XAuth authentication rounds via RADIUS.
+  '';
+
+  eap-sim.request_identity = mkYesNoParam yes "";
+
+  eap-simaka-sql.database = mkOptionalStrParam "";
+
+  eap-simaka-sql.remove_used = mkOptionalStrParam "";
+
+  eap-tls.fragment_size = mkIntParam  1024 ''
+    Maximum size of an EAP-TLS packet.
+  '';
+
+  eap-tls.include_length = mkYesNoParam yes ''
+    Include length in non-fragmented EAP-TLS packets.
+  '';
+
+  eap-tls.max_message_count = mkIntParam 32 ''
+    Maximum number of processed EAP-TLS packets (0 = no limit).
+  '';
+
+  eap-tnc.max_message_count = mkIntParam 10 ''
+    Maximum number of processed EAP-TNC packets (0 = no limit).
+  '';
+
+  eap-tnc.protocol = mkStrParam "tnccs-2.0" ''
+    IF-TNCCS protocol version to be used (tnccs-1.1, tnccs-2.0,
+    tnccs-dynamic).
+  '';
+
+  eap-ttls.fragment_size = mkIntParam 1024 ''
+    Maximum size of an EAP-TTLS packet.
+  '';
+
+  eap-ttls.include_length = mkYesNoParam yes ''
+    Include length in non-fragmented EAP-TTLS packets.
+  '';
+
+  eap-ttls.max_message_count = mkIntParam 32 ''
+    Maximum number of processed EAP-TTLS packets (0 = no limit).
+  '';
+
+  eap-ttls.phase2_method = mkStrParam "md5" ''
+    Phase2 EAP client authentication method.
+  '';
+
+  eap-ttls.phase2_piggyback = mkYesNoParam no ''
+    Phase2 EAP Identity request piggybacked by server onto TLS Finished
+    message.
+  '';
+
+  eap-ttls.phase2_tnc = mkYesNoParam no ''
+    Start phase2 EAP TNC protocol after successful client authentication.
+  '';
+
+  eap-ttls-phase2_tnc_method = mkEnumParam ["pt" "legacy"] "pt" ''
+    Phase2 EAP TNC transport protocol (pt as IETF standard or legacy tnc)
+  '';
+
+  eap-ttls.request_peer_auth = mkYesNoParam no ''
+    Request peer authentication based on a client certificate.
+  '';
+
+  error-notify.socket = mkStrParam "unix://\${piddir}/charon.enfy" ''
+    Socket provided by the error-notify plugin.
+  '';
+
+  ext-auth.script = mkOptionalStrParam ''
+    Shell script to invoke for peer authorization (see ext-auth).
+  '';
+
+  gcrypt.quick_random = mkYesNoParam no ''
+    Use faster random numbers in gcrypt. For testing only, produces weak
+    keys!
+  '';
+
+  ha.autobalance = mkIntParam 0 ''
+    Interval in seconds to automatically balance handled segments between
+    nodes. Set to 0 to disable.
+  '';
+
+  ha.fifo_interface = mkYesNoParam yes "";
+
+  ha.heartbeat_delay = mkIntParam 1000 "";
+
+  ha.heartbeat_timeout = mkIntParam 2100 "";
+
+  ha.local = mkOptionalIntParam "";
+
+  ha.monitor = mkYesNoParam yes "";
+
+  ha.pools = mkOptionalStrParam "";
+
+  ha.remote = mkOptionalStrParam "";
+
+  ha.resync = mkYesNoParam yes "";
+
+  ha.secret = mkOptionalStrParam "";
+
+  ha.segment_count = mkIntParam 1 "";
+
+  ipseckey.enable = mkYesNoParam no ''
+    Enable fetching of IPSECKEY RRs via DNS.
+  '';
+
+  kernel-libipsec.allow_peer_ts = mkYesNoParam no ''
+    Allow that the remote traffic selector equals the IKE peer (see
+    kernel-libipsec for details).
+  '';
+
+  kernel-netlink.buflen = mkOptionalIntParam ''
+    Buffer size for received Netlink messages. Defaults to
+    <literal>min(PAGE_SIZE, 8192)</literal>.
+  '';
+
+  kernel-netlink.force_receive_buffer_size = mkYesNoParam no ''
+    If the maximum Netlink socket receive buffer in bytes set by
+    receive_buffer_size exceeds the system-wide maximum from
+    <literal>/proc/sys/net/core/rmem_max</literal>, this option can be used to
+    override the limit. Enabling this option requires special priviliges
+    (CAP_NET_ADMIN).
+  '';
+
+  kernel-netlink.fwmark = mkOptionalStrParam ''
+    Firewall mark to set on the routing rule that directs traffic to our own
+    routing table. The format is <literal>[!]mark[/mask]</literal>, where the
+    optional exclamation mark inverts the meaning (i.e. the rule only applies to
+    packets that don't match the mark). A possible use case are host-to-host
+    tunnels with kernel-libipsec. When set to !&#60;mark&#62; a more efficient
+    lookup for source and next-hop addresses may also be used since 5.3.3.
+  '';
+
+  kernel-netlink.mss = mkIntParam 0 ''
+    MSS to set on installed routes, 0 to disable.
+  '';
+
+  kernel-netlink.mtu = mkIntParam 0 ''
+    MTU to set on installed routes, 0 to disable.
+  '';
+
+  kernel-netlink.receive_buffer_size = mkIntParam 0 ''
+    Maximum Netlink socket receive buffer in bytes. This value controls how many
+    bytes of Netlink messages can be received on a Netlink socket. The default
+    value is set by <literal>/proc/sys/net/core/rmem_default</literal>. The
+    specified value cannot exceed the system-wide maximum from
+    <literal>/proc/sys/net/core/rmem_max</literal>, unless
+    <option>force_receive_buffer_size</option> is enabled.
+  '';
+
+  kernel-netlink.roam_events = mkYesNoParam yes ''
+    Whether to trigger roam events when interfaces, addresses or routes
+    change.
+  '';
+
+  kernel-netlink.set_proto_port_transport_sa = mkYesNoParam no ''
+    Whether to set protocol and ports in the selector installed on transport
+    mode IPsec SAs in the kernel. While doing so enforces policies for
+    inbound traffic, it also prevents the use of a single IPsec SA by more
+    than one traffic selector.
+  '';
+
+  kernel-netlink.spdh_thresh.ipv4.lbits = mkIntParam 32 ''
+    Local subnet XFRM policy hashing threshold for IPv4.
+  '';
+
+  kernel-netlink.spdh_thresh.ipv4.rbits = mkIntParam 32 ''
+    Remote subnet XFRM policy hashing threshold for IPv4.
+  '';
+
+  kernel-netlink.spdh_thresh.ipv6.lbits = mkIntParam 128 ''
+    Local subnet XFRM policy hashing threshold for IPv6.
+  '';
+
+  kernel-netlink.spdh_thresh.ipv6.rbits = mkIntParam 128 ''
+    Remote subnet XFRM policy hashing threshold for IPv6.
+  '';
+
+  kernel-netlink.xfrm_acq_expires = mkIntParam 165 ''
+    Lifetime of XFRM acquire state created by the kernel when traffic matches a
+    trap policy. The value gets written to
+    <literal>/proc/sys/net/core/xfrm_acq_expires</literal>. Indirectly controls
+    the delay between XFRM acquire messages triggered by the kernel for a trap
+    policy. The same value is used as timeout for SPIs allocated by the
+    kernel. The default value equals the default total retransmission timeout
+    for IKE messages (since 5.5.3 this value is determined dynamically based on
+    the configuration).
+  '';
+
+  kernel-pfkey.events_buffer_size = mkIntParam 0 ''
+    Size of the receive buffer for the event socket (0 for default
+    size). Because events are received asynchronously installing e.g. lots
+    of policies may require a larger buffer than the default on certain
+    platforms in order to receive all messages.
+  '';
+
+  kernel-pfroute.vip_wait = mkIntParam 1000 ''
+    Time in ms to wait until virtual IP addresses appear/disappear before
+    failing.
+  '';
+
+  led.activity_led = mkOptionalStrParam "";
+
+  led.blink_time = mkIntParam 50 "";
+
+  load-tester = {
+    addrs = mkAttrsOfParam (mkOptionalStrParam "") ''
+      Section that contains key/value pairs with address pools (in CIDR
+      notation) to use for a specific network interface e.g.
+      <literal>eth0 = 10.10.0.0/16</literal>.
+   '';
+
+    addrs_keep = mkYesNoParam no ''
+      Whether to keep dynamic addresses even after the associated SA got
+      terminated.
+    '';
+
+    addrs_prefix = mkIntParam 16 ''
+      Network prefix length to use when installing dynamic addresses.
+      If set to -1 the full address is used (i.e. 32 or 128).
+    '';
+
+    ca_dir = mkOptionalStrParam ''
+      Directory to load (intermediate) CA certificates from.
+    '';
+
+    child_rekey = mkIntParam 600 ''
+      Seconds to start CHILD_SA rekeying after setup.
+    '';
+
+    crl = mkOptionalStrParam ''
+      URI to a CRL to include as certificate distribution point in generated
+      certificates.
+    '';
+
+    delay = mkIntParam 0 ''
+      Delay between initiatons for each thread.
+    '';
+
+    delete_after_established = mkYesNoParam no ''
+      Delete an IKE_SA as soon as it has been established.
+    '';
+
+    digest = mkStrParam "sha1" ''
+      Digest algorithm used when issuing certificates.
+    '';
+
+    dpd_delay = mkIntParam 0 ''
+      DPD delay to use in load test.
+    '';
+
+    dynamic_port = mkIntParam 0 ''
+      Base port to be used for requests (each client uses a different port).
+    '';
+
+    eap_password = mkStrParam "default-pwd" ''
+      EAP secret to use in load test.
+    '';
+
+    enable = mkYesNoParam no ''
+      Enable the load testing plugin. **WARNING**: Never enable this plugin on
+      productive systems. It provides preconfigured credentials and allows an
+      attacker to authenticate as any user.
+    '';
+
+    esp = mkStrParam "aes128-sha1" ''
+      CHILD_SA proposal to use for load tests.
+    '';
+
+    fake_kernel = mkYesNoParam no ''
+      Fake the kernel interface to allow load-testing against self.
+    '';
+
+    ike_rekey = mkIntParam 0 ''
+      Seconds to start IKE_SA rekeying after setup.
+    '';
+
+    init_limit = mkIntParam 0 ''
+      Global limit of concurrently established SAs during load test.
+    '';
+
+    initiator = mkStrParam "0.0.0.0" ''
+      Address to initiate from.
+    '';
+
+    initiators = mkIntParam 0 ''
+      Number of concurrent initiator threads to use in load test.
+    '';
+
+    initiator_auth = mkStrParam "pubkey" ''
+      Authentication method(s) the intiator uses.
+    '';
+
+    initiator_id = mkOptionalStrParam ''
+      Initiator ID used in load test.
+    '';
+
+    initiator_match = mkOptionalStrParam ''
+      Initiator ID to match against as responder.
+    '';
+
+    initiator_tsi = mkOptionalStrParam ''
+      Traffic selector on initiator side, as proposed by initiator.
+    '';
+
+    initiator_tsr = mkOptionalStrParam ''
+      Traffic selector on responder side, as proposed by initiator.
+    '';
+
+    iterations = mkIntParam 1 ''
+      Number of IKE_SAs to initiate by each initiator in load test.
+    '';
+
+    issuer_cert = mkOptionalStrParam ''
+      Path to the issuer certificate (if not configured a hard-coded default
+      value is used).
+    '';
+
+    issuer_key = mkOptionalStrParam ''
+      Path to private key that is used to issue certificates (if not configured
+      a hard-coded default value is used).
+    '';
+
+    mode = mkEnumParam ["tunnel" "transport" "beet"] "tunnel" ''
+      IPsec mode to use.
+    '';
+
+    pool = mkOptionalStrParam ''
+      Provide INTERNAL_IPV4_ADDRs from a named pool.
+    '';
+
+    preshared_key = mkStrParam "<default-psk>" ''
+      Preshared key to use in load test.
+    '';
+
+    proposal = mkStrParam "aes128-sha1-modp768" ''
+      IKE proposal to use in load test.
+    '';
+
+    responder = mkStrParam "127.0.0.1" ''
+      Address to initiation connections to.
+    '';
+
+    responder_auth = mkStrParam "pubkey" ''
+      Authentication method(s) the responder uses.
+    '';
+
+    responder_id = mkOptionalStrParam ''
+      Responder ID used in load test.
+    '';
+
+    responder_tsi = mkStrParam "initiator_tsi" ''
+      Traffic selector on initiator side, as narrowed by responder.
+    '';
+
+    responder_tsr = mkStrParam "initiator_tsr" ''
+      Traffic selector on responder side, as narrowed by responder.
+    '';
+
+    request_virtual_ip = mkYesNoParam no ''
+      Request an INTERNAL_IPV4_ADDR from the server.
+    '';
+
+    shutdown_when_complete = mkYesNoParam no ''
+      Shutdown the daemon after all IKE_SAs have been established.
+    '';
+
+    socket = mkStrParam "unix://\\\${piddir}/charon.ldt" ''
+      Socket provided by the load-tester plugin.
+    '';
+
+    version = mkIntParam 0 ''
+      IKE version to use (0 means use IKEv2 as initiator and accept any version
+      as responder).
+    '';
+  };
+
+  lookip.socket = mkStrParam "unix://\\\${piddir}/charon.lkp" ''
+    Socket provided by the lookip plugin.
+  '';
+
+  ntru.max_drbg_requests = mkIntParam 4294967294 ''
+    Number of pseudo-random bit requests from the DRBG before an automatic
+    reseeding occurs.
+  '';
+
+  ntru.parameter_set =
+    mkEnumParam ["x9_98_speed" "x9_98_bandwidth" "x9_98_balance" "optimum"] "optimum" ''
+      The following parameter sets are available:
+      <literal>x9_98_speed</literal>, <literal>x9_98_bandwidth</literal>,
+      <literal>x9_98_balance</literal> and <literal>optimum</literal>, the last
+      set not being part of the X9.98 standard but having the best performance.
+    '';
+
+  openssl.engine_id = mkStrParam "pkcs11" ''
+    ENGINE ID to use in the OpenSSL plugin.
+  '';
+
+  openssl.fips_mode = mkIntParam 0 ''
+    Set OpenSSL FIPS mode:
+    <itemizedlist>
+    <listitem><para>disabled (0),</para></listitem>
+    <listitem><para>enabled (1),</para></listitem>
+    <listitem><para>Suite B enabled (2).</para></listitem>
+    </itemizedlist>
+    Defaults to the value configured with the
+    <literal>--with-fips-mode</literal> option.
+
+  '';
+
+  osx-attr.append = mkYesNoParam yes ''
+    Whether DNS servers are appended to existing entries, instead of
+    replacing them.
+  '';
+
+  pkcs11.load_certs = mkYesNoParam yes ''
+    Whether to load certificates from tokens.
+  '';
+
+  pkcs11.modules = mkAttrsOfParams {
+    path = mkOptionalStrParam ''
+      Full path to the shared object file of this PKCS#11 module
+    '';
+
+    os_locking = mkYesNoParam no ''
+      Whether OS locking should be enabled for this module
+    '';
+
+    load_certs = mkYesNoParam no ''
+      Whether the PKCS#11 modules should load certificates from tokens (since 5.0.2)
+    '';
+  } ''
+    List of available PKCS#11 modules, see SmartCardsIKEv2.
+  '';
+
+  pkcs11.reload_certs = mkYesNoParam no ''
+    Reload certificates from all tokens if charon receives a SIGHUP.
+  '';
+
+  pkcs11.use_dh = mkYesNoParam no ''
+    Whether the PKCS#11 modules should be used for DH and ECDH.
+  '';
+
+  pkcs11.use_ecc = mkYesNoParam no ''
+    Whether the PKCS#11 modules should be used for ECDH and ECDSA public key
+    operations. ECDSA private keys are used regardless of this option.
+  '';
+
+  pkcs11.use_hasher = mkYesNoParam no ''
+    Whether the PKCS#11 modules should be used to hash data.
+  '';
+
+  pkcs11.use_pubkey = mkYesNoParam no ''
+    Whether the PKCS#11 modules should be used for public key operations,
+    even for keys not stored on tokens.
+  '';
+
+  pkcs11.use_rng = mkYesNoParam no ''
+    Whether the PKCS#11 modules should be used as RNG.
+  '';
+
+  radattr.dir = mkOptionalStrParam ''
+    Directory where RADIUS attributes are stored in client-ID specific
+    files, see radattr.
+  '';
+
+  radattr.message_id = mkIntParam (-1) ''
+    RADIUS attributes are added to all IKE_AUTH messages by default (-1), or
+    only to the IKE_AUTH message with the given IKEv2 message ID.
+  '';
+
+  random.random = mkStrParam "/dev/random" ''
+    File to read random bytes from.
+  '';
+
+  random.urandom = mkStrParam "/dev/urandom" ''
+    File to read pseudo random bytes from.
+  '';
+
+  random.strong_equals_true = mkYesNoParam no ''
+    If enabled the RNG_STRONG class reads random bytes from the same source
+    as the RNG_TRUE class.
+  '';
+
+  resolve.file = mkStrParam "/etc/resolv.conf" ''
+    File used by the resolve plugin to write DNS server entries to.
+  '';
+
+  resolve.resolvconf.iface_prefix = mkStrParam "lo.inet.ipsec." ''
+    Prefix used by the resolve plugin for interface names sent to
+    resolvconf(8). The name server address is appended to this prefix to
+    make it unique. The result has to be a valid interface name according to
+    the rules defined by resolvconf. Also, it should have a high priority
+    according to the order defined in interface-order(5).
+  '';
+
+  revocation.enable_crl = mkYesNoParam yes ''
+    Whether CRL validation should be enabled.
+  '';
+
+  revocation.enable_ocsp = mkYesNoParam yes ''
+    Whether OCSP validation should be enabled.
+  '';
+
+  socket-default.fwmark = mkOptionalStrParam ''
+    Firewall mark to set on outbound packets (a possible use case are
+    host-to-host tunnels with kernel-libipsec).
+  '';
+
+  socket-default.set_source = mkYesNoParam yes ''
+    Set source address on outbound packets, if possible.
+  '';
+
+  socket-default.set_sourceif = mkYesNoParam no ''
+    Force sending interface on outbound packets, if possible. This allows
+    using IPv6 link-local addresses as tunnel endpoints.
+  '';
+
+  socket-default.use_ipv4 = mkYesNoParam yes ''
+    Listen on IPv4, if possible.
+  '';
+
+  socket-default.use_ipv6 = mkYesNoParam yes ''
+    Listen on IPv6, if possible.
+  '';
+
+  sql.database = mkOptionalStrParam ''
+    Database URI for charon's SQL plugin. If it contains a password, make
+    sure to adjust the permissions of the config file accordingly.
+  '';
+
+  sql.loglevel = mkIntParam (-1) ''
+    Loglevel for logging to SQL database.
+  '';
+
+  stroke.allow_swap = mkYesNoParam yes ''
+    Analyze addresses/hostnames in left/right to detect which side is local
+    and swap configuration options if necessary. If disabled left is always
+    local.
+  '';
+
+  stroke.ignore_missing_ca_basic_constraint = mkYesNoParam no ''
+    Treat certificates in ipsec.d/cacerts and ipsec.conf ca sections as CA
+    certificates even if they don't contain a CA basic constraint.
+  '';
+
+  stroke.max_concurrent = mkIntParam 4 ''
+    Maximum number of stroke messages handled concurrently.
+  '';
+
+  stroke.secrets_file = mkStrParam "\${sysconfdir}/ipsec.secrets" ''
+    Location of the ipsec.secrets file.
+  '';
+
+  stroke.socket = mkStrParam "unix://\${piddir}/charon.ctl" ''
+    Socket provided by the stroke plugin.
+  '';
+
+  stroke.timeout = mkIntParam 0 ''
+    Timeout in ms for any stroke command. Use 0 to disable the timeout.
+  '';
+
+  systime-fix.interval = mkIntParam 0 ''
+    Interval in seconds to check system time for validity. 0 disables the
+    check. See systime-fix plugin.
+  '';
+
+  systime-fix.reauth = mkYesNoParam no ''
+    Whether to use reauth or delete if an invalid cert lifetime is detected.
+  '';
+
+  systime-fix.threshold = mkOptionalStrParam ''
+    Threshold date where system time is considered valid. Disabled if not
+    specified.
+  '';
+
+  systime-fix.threshold_format = mkStrParam "%Y" ''
+    strptime(3) format used to parse threshold option.
+  '';
+
+  tnc-ifmap.client_cert = mkOptionalStrParam ''
+    Path to X.509 certificate file of IF-MAP client.
+  '';
+
+  tnc-ifmap.client_key = mkOptionalStrParam ''
+    Path to private key file of IF-MAP client.
+  '';
+
+  tnc-ifmap.device_name = mkOptionalStrParam ''
+    Unique name of strongSwan server as a PEP and/or PDP device.
+  '';
+
+  tnc-ifmap.renew_session_interval = mkIntParam 150 ''
+    Interval in seconds between periodic IF-MAP RenewSession requests.
+  '';
+
+  tnc-ifmap.server_cert = mkOptionalStrParam ''
+    Path to X.509 certificate file of IF-MAP server.
+  '';
+
+  tnc-ifmap.server_uri = mkStrParam "https://localhost:8444/imap" ''
+    URI of the form <literal>[https://]servername[:port][/path]</literal>.
+  '';
+
+  tnc-ifmap.username_password = mkOptionalStrParam ''
+    Credentials of IF-MAP client of the form
+    <literal>username:password</literal>. If set, make sure to adjust the
+    permissions of the config file accordingly.
+  '';
+
+  tnc-imc.dlcose = mkYesNoParam yes ''
+    Unload IMC after use.
+  '';
+
+  tnc-imc.preferred_language = mkStrParam "en" ''
+    Preferred language for TNC recommendations.
+  '';
+
+  tnc-imv.dlcose = mkYesNoParam yes ''
+    Unload IMV after use.
+  '';
+
+  tnc-imv.recommendation_policy = mkEnumParam ["default" "any" "all"] "default" ''
+    default TNC recommendation policy.
+  '';
+
+  tnc-pdp.pt_tls.enable = mkYesNoParam yes ''
+    Enable PT-TLS protocol on the strongSwan PDP.
+  '';
+
+  tnc-pdp.pt_tls.port = mkIntParam 271 ''
+    PT-TLS server port the strongSwan PDP is listening on.
+  '';
+
+  tnc-pdp.radius.enable = mkYesNoParam yes ''
+    Enable RADIUS protocol on the strongSwan PDP.
+  '';
+
+  tnc-pdp.radius.method = mkStrParam "ttls" ''
+    EAP tunnel method to be used.
+  '';
+
+  tnc-pdp.radius.port = mkIntParam 1812 ''
+    RADIUS server port the strongSwan PDP is listening on.
+  '';
+
+  tnc-pdp.radius.secret = mkOptionalStrParam ''
+    Shared RADIUS secret between strongSwan PDP and NAS. If set, make sure
+    to adjust the permissions of the config file accordingly.
+  '';
+
+  tnc-pdp.server = mkOptionalStrParam ''
+    Name of the strongSwan PDP as contained in the AAA certificate.
+  '';
+
+  tnc-pdp.timeout = mkOptionalIntParam ''
+    Timeout in seconds before closing incomplete connections.
+  '';
+
+  tnccs-11.max_message_size = mkIntParam 45000 ''
+    Maximum size of a PA-TNC message (XML &#38; Base64 encoding).
+  '';
+
+  tnccs-20.max_batch_size = mkIntParam 65522 ''
+    Maximum size of a PB-TNC batch (upper limit via PT-EAP = 65529).
+  '';
+
+  tnccs-20.max_message_size = mkIntParam 65490 ''
+    Maximum size of a PA-TNC message (upper limit via PT-EAP = 65497).
+  '';
+
+  tnccs-20.mutual = mkYesNoParam no ''
+    Enable PB-TNC mutual protocol.
+  '';
+
+  tpm.use_rng = mkYesNoParam no ''
+    Whether the TPM should be used as RNG.
+  '';
+
+  unbound.dlv_anchors = mkOptionalStrParam ''
+    File to read trusted keys for DLV from. It uses the same format as
+    <option>trust_anchors</option>. Only one DLV can be configured, which is
+    then used as a root trusted DLV, this means that it is a lookaside for the
+    root.
+  '';
+
+  unbound.resolv_conf = mkStrParam "/etc/resolv.conf" ''
+    File to read DNS resolver configuration from.
+  '';
+
+  unbound.trust_anchors = mkStrParam "/etc/ipsec.d/dnssec.keys" ''
+    File to read DNSSEC trust anchors from (usually root zone KSK). The
+    format of the file is the standard DNS Zone file format, anchors can be
+    stored as DS or DNSKEY entries in the file.
+  '';
+
+  updown.dns_handler = mkYesNoParam no ''
+    Whether the updown script should handle DNS servers assigned via IKEv1
+    Mode Config or IKEv2 Config Payloads (if enabled they can't be handled
+    by other plugins, like resolve).
+  '';
+
+  vici.socket = mkStrParam "unix://\${piddir}/charon.vici" ''
+    Socket the vici plugin serves clients.
+  '';
+
+  whitelist.enable = mkYesNoParam yes ''
+    Enable loaded whitelist plugin.
+  '';
+
+  whitelist.socket = mkStrParam "unix://\${piddir}/charon.wlst" ''
+    Socket provided by the whitelist plugin.
+  '';
+
+  xauth-eap.backend = mkStrParam "radius" ''
+    EAP plugin to be used as backend for XAuth credential verification, see
+    XAuthEAP.
+  '';
+
+  xauth-pam.pam_service = mkStrParam "login" ''
+    PAM service to be used for authentication, see XAuthPAM.
+  '';
+
+  xauth-pam.session = mkYesNoParam no ''
+    Open/close a PAM session for each active IKE_SA.
+  '';
+
+  xauth-pam.trim_email = mkYesNoParam yes ''
+    If an email address is given as an XAuth username, trim it to just the
+    username part.
+  '';
+}
diff --git a/nixos/modules/services/networking/strongswan-swanctl/strongswan-libimcv-params.nix b/nixos/modules/services/networking/strongswan-swanctl/strongswan-libimcv-params.nix
new file mode 100644
index 00000000000..2ca2c9c396e
--- /dev/null
+++ b/nixos/modules/services/networking/strongswan-swanctl/strongswan-libimcv-params.nix
@@ -0,0 +1,291 @@
+lib : with (import ./param-constructors.nix lib); {
+  debug_level = mkIntParam 1 ''
+    Debug level for a stand-alone libimcv library.
+  '';
+
+  load = mkSpaceSepListParam ["random" "nonce" "gmp" "pubkey" "x509"] ''
+    Plugins to load in IMC/IMVs with stand-alone libimcv library.
+  '';
+
+  stderr_quiet = mkYesNoParam no ''
+    Disable the output to stderr with a stand-alone libimcv library.
+  '';
+
+  swid_gen = {
+    command = mkStrParam "/usr/local/bin/swid_generator" ''
+      SWID generator command to be executed.
+    '';
+
+    tag_creator = {
+      name = mkStrParam "strongSwan Project" ''
+        Name of the tagCreator entity.
+      '';
+
+      regid = mkStrParam "strongswan.org" ''
+        regid of the tagCreator entity.
+      '';
+    };
+  };
+
+  plugins = {
+
+    imc-attestation = {
+      aik_blob = mkOptionalStrParam ''
+        AIK encrypted private key blob file.
+      '';
+
+      aik_cert = mkOptionalStrParam ''
+        AIK certificate file.
+      '';
+
+      aik_handle = mkOptionalStrParam ''
+        AIK object handle, e.g. 0x81010003.
+      '';
+
+      aik_pubkey = mkOptionalStrParam ''
+        AIK public key file.
+      '';
+
+      mandatory_dh_groups = mkYesNoParam yes ''
+        Enforce mandatory Diffie-Hellman groups
+      '';
+
+      nonce_len = mkIntParam 20 ''
+        DH nonce length.
+      '';
+
+      pcr_info = mkYesNoParam no ''
+        Whether to send pcr_before and pcr_after info.
+      '';
+
+      use_quote2 = mkYesNoParam yes ''
+        Use Quote2 AIK signature instead of Quote signature.
+      '';
+
+      use_version_info = mkYesNoParam no ''
+        Version Info is included in Quote2 signature.
+      '';
+    };
+
+    imc-hcd.push_info = mkYesNoParam yes ''
+      Send quadruple info without being prompted.
+    '';
+
+    imc-hcd.subtypes = let
+      imcHcdSubtypeParams = let
+        softwareParams = mkAttrsOfParams {
+          name = mkOptionalStrParam ''
+            Name of the software installed on the hardcopy device.
+          '';
+
+          patches = mkOptionalStrParam ''
+            String describing all patches applied to the given software on this
+            hardcopy device. The individual patches are separated by a newline
+            character '\\n'.
+          '';
+
+          string_version = mkOptionalStrParam ''
+            String describing the version of the given software on this hardcopy device.
+          '';
+
+          version = mkOptionalStrParam ''
+            Hex-encoded version string with a length of 16 octets consisting of
+            the fields major version number (4 octets), minor version number (4
+            octets), build number (4 octets), service pack major number (2
+            octets) and service pack minor number (2 octets).
+          '';
+        } ''
+          Defines a software section having an arbitrary name.
+        '';
+      in {
+        firmware             = softwareParams;
+        resident_application = softwareParams;
+        user_application     = softwareParams;
+        attributes_natural_language = mkStrParam "en" ''
+          Variable length natural language tag conforming to RFC 5646 specifies
+          the language to be used in the health assessment message of a given
+          subtype.
+        '';
+      };
+    in {
+      system = imcHcdSubtypeParams // {
+        certification_state = mkOptionalStrParam ''
+          Hex-encoded certification state.
+        '';
+
+        configuration_state = mkOptionalStrParam ''
+          Hex-encoded configuration state.
+        '';
+
+        machine_type_model = mkOptionalStrParam ''
+          String specifying the machine type and model of the hardcopy device.
+        '';
+
+        pstn_fax_enabled = mkYesNoParam no ''
+          Specifies if a PSTN facsimile interface is installed and enabled on the
+          hardcopy device.
+        '';
+
+        time_source = mkOptionalStrParam ''
+          String specifying the hostname of the network time server used by the
+          hardcopy device.
+        '';
+
+        user_application_enabled = mkYesNoParam no ''
+          Specifies if users can dynamically download and execute applications on
+          the hardcopy device.
+        '';
+
+        user_application_persistence_enabled = mkYesNoParam no ''
+          Specifies if user dynamically downloaded applications can persist outside
+          the boundaries of a single job on the hardcopy device.
+        '';
+
+        vendor_name = mkOptionalStrParam ''
+          String specifying the manufacturer of the hardcopy device.
+        '';
+
+        vendor_smi_code = mkOptionalIntParam ''
+          Integer specifying the globally unique 24-bit SMI code assigned to the
+          manufacturer of the hardcopy device.
+        '';
+      };
+      control   = imcHcdSubtypeParams;
+      marker    = imcHcdSubtypeParams;
+      finisher  = imcHcdSubtypeParams;
+      interface = imcHcdSubtypeParams;
+      scanner   = imcHcdSubtypeParams;
+    };
+
+    imc-os = {
+      device_cert = mkOptionalStrParam ''
+        Manually set the path to the client device certificate
+        (e.g. /etc/pts/aikCert.der)
+      '';
+
+      device_id = mkOptionalStrParam ''
+        Manually set the client device ID in hexadecimal format
+        (e.g. 1083f03988c9762703b1c1080c2e46f72b99cc31)
+      '';
+
+      device_pubkey = mkOptionalStrParam ''
+        Manually set the path to the client device public key
+        (e.g. /etc/pts/aikPub.der)
+      '';
+
+      push_info = mkYesNoParam yes ''
+        Send operating system info without being prompted.
+      '';
+    };
+
+    imc-scanner.push_info = mkYesNoParam yes ''
+      Send open listening ports without being prompted.
+    '';
+
+    imc-swid = {
+      swid_full = mkYesNoParam no ''
+        Include file information in the XML-encoded SWID tags.
+      '';
+
+      swid_pretty = mkYesNoParam no ''
+        Generate XML-encoded SWID tags with pretty indentation.
+      '';
+
+      swid_directory = mkStrParam "\${prefix}/share" ''
+        Directory where SWID tags are located.
+      '';
+    };
+
+    imc-swima = {
+      eid_epoch = mkHexParam "0x11223344" ''
+        Set 32 bit epoch value for event IDs manually if software collector
+        database is not available.
+      '';
+
+      swid_database = mkOptionalStrParam ''
+        URI to software collector database containing event timestamps, software
+        creation and deletion events and collected software identifiers.  If it
+        contains a password, make sure to adjust the permissions of the config
+        file accordingly.
+      '';
+
+      swid_directory = mkStrParam "\${prefix}/share" ''
+        Directory where SWID tags are located.
+      '';
+
+      swid_pretty = mkYesNoParam no ''
+        Generate XML-encoded SWID tags with pretty indentation.
+      '';
+
+      swid_full = mkYesNoParam no ''
+        Include file information in the XML-encoded SWID tags.
+      '';
+    };
+
+    imc-test = {
+      additional_ids = mkIntParam 0 ''
+        Number of additional IMC IDs.
+      '';
+
+      command = mkStrParam "none" ''
+        Command to be sent to the Test IMV.
+      '';
+
+      dummy_size = mkIntParam 0 ''
+        Size of dummy attribute to be sent to the Test IMV (0 = disabled).
+      '';
+
+      retry = mkYesNoParam no ''
+        Do a handshake retry.
+      '';
+
+      retry_command = mkOptionalStrParam ''
+        Command to be sent to the IMV Test in the handshake retry.
+      '';
+    };
+
+    imv-attestation = {
+      cadir = mkOptionalStrParam ''
+        Path to directory with AIK cacerts.
+      '';
+
+      dh_group = mkStrParam "ecp256" ''
+        Preferred Diffie-Hellman group.
+      '';
+
+      hash_algorithm = mkStrParam "sha256" ''
+        Preferred measurement hash algorithm.
+      '';
+
+      min_nonce_len = mkIntParam 0 ''
+        DH minimum nonce length.
+      '';
+
+      remediation_uri = mkOptionalStrParam ''
+        URI pointing to attestation remediation instructions.
+      '';
+    };
+
+    imv-os.remediation_uri = mkOptionalStrParam ''
+      URI pointing to operating system remediation instructions.
+    '';
+
+    imv-scanner.remediation_uri = mkOptionalStrParam ''
+      URI pointing to scanner remediation instructions.
+    '';
+
+    imv-swima.rest_api = {
+      uri = mkOptionalStrParam ''
+        HTTP URI of the SWID REST API.
+      '';
+
+      timeout = mkIntParam 120 ''
+        Timeout of SWID REST API HTTP POST transaction.
+      '';
+    };
+
+    imv-test.rounds = mkIntParam 0 ''
+      Number of IMC-IMV retry rounds.
+    '';
+  };
+}
diff --git a/nixos/modules/services/networking/strongswan-swanctl/strongswan-loglevel-params.nix b/nixos/modules/services/networking/strongswan-swanctl/strongswan-loglevel-params.nix
new file mode 100644
index 00000000000..0f517d8ead4
--- /dev/null
+++ b/nixos/modules/services/networking/strongswan-swanctl/strongswan-loglevel-params.nix
@@ -0,0 +1,29 @@
+lib : with (import ./param-constructors.nix lib);
+
+let mkJournalParam = description :
+      mkEnumParam [(-1) 0 1 2 3 4] 0 "Logging level for ${description}";
+in {
+  default = mkIntParam 1 ''
+    Specifies the default loglevel to be used for subsystems for which no
+    specific loglevel is defined.
+  '';
+
+  app = mkJournalParam "applications other than daemons.";
+  asn = mkJournalParam "low-level encoding/decoding (ASN.1, X.509 etc.)";
+  cfg = mkJournalParam "configuration management and plugins.";
+  chd = mkJournalParam "CHILD_SA/IPsec SA.";
+  dmn = mkJournalParam "main daemon setup/cleanup/signal handling.";
+  enc = mkJournalParam "packet encoding/decoding encryption/decryption operations.";
+  esp = mkJournalParam "libipsec library messages.";
+  ike = mkJournalParam "IKE_SA/ISAKMP SA.";
+  imc = mkJournalParam "integrity Measurement Collector.";
+  imv = mkJournalParam "integrity Measurement Verifier.";
+  job = mkJournalParam "jobs queuing/processing and thread pool management.";
+  knl = mkJournalParam "IPsec/Networking kernel interface.";
+  lib = mkJournalParam "libstrongwan library messages.";
+  mgr = mkJournalParam "IKE_SA manager, handling synchronization for IKE_SA access.";
+  net = mkJournalParam "IKE network communication.";
+  pts = mkJournalParam "platform Trust Service.";
+  tls = mkJournalParam "libtls library messages.";
+  tnc = mkJournalParam "trusted Network Connect.";
+}
diff --git a/nixos/modules/services/networking/strongswan-swanctl/strongswan-params.nix b/nixos/modules/services/networking/strongswan-swanctl/strongswan-params.nix
new file mode 100644
index 00000000000..ad805305370
--- /dev/null
+++ b/nixos/modules/services/networking/strongswan-swanctl/strongswan-params.nix
@@ -0,0 +1,228 @@
+# See: https://wiki.strongswan.org/projects/strongswan/wiki/StrongswanConf
+#
+# When strongSwan is upgraded please update the parameters in this file. You can
+# see which parameters should be deleted, changed or added by diffing
+# the strongswan conf directory:
+#
+#   git clone https://github.com/strongswan/strongswan.git
+#   cd strongswan
+#   git diff 5.5.3..5.6.0 conf/
+
+lib: with (import ./param-constructors.nix lib);
+
+let charonParams = import ./strongswan-charon-params.nix lib;
+in {
+  aikgen = {
+    load = mkSpaceSepListParam [] ''
+      Plugins to load in ipsec aikgen tool.
+    '';
+  };
+  attest = {
+    database = mkOptionalStrParam ''
+      File measurement information database URI. If it contains a password,
+      make sure to adjust the permissions of the config file accordingly.
+    '';
+
+    load = mkSpaceSepListParam [] ''
+      Plugins to load in ipsec attest tool.
+    '';
+  };
+
+  charon = charonParams;
+
+  charon-nm = {
+    ca_dir = mkStrParam "<default>" ''
+      Directory from which to load CA certificates if no certificate is
+      configured.
+    '';
+  };
+
+  charon-systemd = charonParams // {
+    journal = import ./strongswan-loglevel-params.nix lib;
+  };
+
+  imv_policy_manager = {
+    command_allow = mkOptionalStrParam ''
+      Shell command to be executed with recommendation allow.
+    '';
+
+    command_block = mkOptionalStrParam ''
+      Shell command to be executed with all other recommendations.
+    '';
+
+    database = mkOptionalStrParam ''
+      Database URI for the database that stores the package information. If it
+      contains a password, make sure to adjust permissions of the config file
+      accordingly.
+    '';
+
+    load = mkSpaceSepListParam ["sqlite"] ''
+      Plugins to load in IMV policy manager.
+    '';
+  };
+
+  libimcv = import ./strongswan-libimcv-params.nix lib;
+
+  manager = {
+    database = mkOptionalStrParam ''
+      Credential database URI for manager. If it contains a password, make
+      sure to adjust the permissions of the config file accordingly.
+    '';
+
+    debug = mkYesNoParam no ''
+      Enable debugging in manager.
+    '';
+
+    load = mkSpaceSepListParam [] ''
+      Plugins to load in manager.
+    '';
+
+    socket = mkOptionalStrParam ''
+      FastCGI socket of manager, to run it statically.
+    '';
+
+    threads = mkIntParam 10 ''
+      Threads to use for request handling.
+    '';
+
+    timeout = mkDurationParam "15m" ''
+      Session timeout for manager.
+    '';
+  };
+
+  medcli = {
+    database = mkOptionalStrParam ''
+      Mediation client database URI. If it contains a password, make sure to
+      adjust the permissions of the config file accordingly.
+    '';
+
+    dpd = mkDurationParam "5m" ''
+      DPD timeout to use in mediation client plugin.
+    '';
+
+    rekey = mkDurationParam "20m" ''
+      Rekeying time on mediation connections in mediation client plugin.
+    '';
+  };
+
+  medsrv = {
+    database = mkOptionalStrParam ''
+      Mediation server database URI. If it contains a password, make sure to
+      adjust the permissions of the config file accordingly.
+    '';
+
+    debug = mkYesNoParam no ''
+      Debugging in mediation server web application.
+    '';
+
+    dpd = mkDurationParam "5m" ''
+      DPD timeout to use in mediation server plugin.
+    '';
+
+    load = mkSpaceSepListParam [] ''
+      Plugins to load in mediation server plugin.
+    '';
+
+    password_length = mkIntParam 6 ''
+      Minimum password length required for mediation server user accounts.
+    '';
+
+    rekey = mkDurationParam "20m" ''
+      Rekeying time on mediation connections in mediation server plugin.
+    '';
+
+    socket = mkOptionalStrParam ''
+      Run Mediation server web application statically on socket.
+    '';
+
+    threads = mkIntParam 5 ''
+      Number of thread for mediation service web application.
+    '';
+
+    timeout = mkDurationParam "15m" ''
+      Session timeout for mediation service.
+    '';
+  };
+
+  pacman.database = mkOptionalStrParam ''
+    Database URI for the database that stores the package information. If it
+    contains a password, make sure to adjust the permissions of the config
+    file accordingly.
+  '';
+
+  pki.load = mkSpaceSepListParam [] ''
+    Plugins to load in ipsec pki tool.
+  '';
+
+  pool = {
+    database = mkOptionalStrParam ''
+      Database URI for the database that stores IP pools and configuration
+      attributes. If it contains a password, make sure to adjust the
+      permissions of the config file accordingly.
+    '';
+
+    load = mkSpaceSepListParam [] ''
+      Plugins to load in ipsec pool tool.
+    '';
+  };
+
+  pt-tls-client.load = mkSpaceSepListParam [] ''
+    Plugins to load in ipsec pt-tls-client tool.
+  '';
+
+  scepclient.load = mkSpaceSepListParam [] ''
+    Plugins to load in ipsec scepclient tool.
+  '';
+
+  starter = {
+    config_file = mkStrParam "\${sysconfdir}/ipsec.conf" ''
+      Location of the ipsec.conf file.
+    '';
+
+    load_warning = mkYesNoParam yes ''
+      Show charon.load setting warning, see
+      https://wiki.strongswan.org/projects/strongswan/wiki/PluginLoad
+    '';
+  };
+
+  sw-collector = {
+    database = mkOptionalStrParam ''
+      URI to software collector database containing event timestamps,
+      software creation and deletion events and collected software
+      identifiers. If it contains a password, make sure to adjust the
+      permissions of the config file accordingly.
+    '';
+
+    first_file = mkStrParam "/var/log/bootstrap.log" ''
+      Path pointing to file created when the Linux OS was installed.
+    '';
+
+    first_time = mkStrParam "0000-00-00T00:00:00Z" ''
+      Time in UTC when the Linux OS was installed.
+    '';
+
+    history = mkOptionalStrParam ''
+      Path pointing to apt history.log file.
+    '';
+
+    rest_api = {
+      uri = mkOptionalStrParam ''
+        HTTP URI of the central collector's REST API.
+      '';
+
+      timeout = mkIntParam 120 ''
+        Timeout of REST API HTTP POST transaction.
+      '';
+    };
+
+    load = mkSpaceSepListParam [] "Plugins to load in sw-collector tool.";
+  };
+
+  swanctl = {
+    load = mkSpaceSepListParam [] "Plugins to load in swanctl.";
+
+    socket = mkStrParam "unix://\${piddir}/charon.vici" ''
+      VICI socket to connect to by default.
+    '';
+  };
+}
diff --git a/nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix b/nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix
new file mode 100644
index 00000000000..095ae549730
--- /dev/null
+++ b/nixos/modules/services/networking/strongswan-swanctl/swanctl-params.nix
@@ -0,0 +1,1145 @@
+# See: https://wiki.strongswan.org/projects/strongswan/wiki/Swanctlconf
+#
+# When strongSwan is upgraded please update the parameters in this file. You can
+# see which parameters should be deleted, changed or added by diffing
+# swanctl.opt:
+#
+#   git clone https://github.com/strongswan/strongswan.git
+#   cd strongswan
+#   git diff 5.5.3..5.6.0 src/swanctl/swanctl.opt
+
+lib: with (import ./param-constructors.nix lib);
+
+let
+  certParams = {
+    file = mkOptionalStrParam ''
+      Absolute path to the certificate to load. Passed as-is to the daemon, so
+      it must be readable by it.
+      </para><para>
+      Configure either this or <option>handle</option>, but not both, in one section.
+    '';
+
+    handle = mkOptionalHexParam ''
+      Hex-encoded CKA_ID or handle of the certificate on a token or TPM,
+      respectively.
+      </para><para>
+      Configure either this or <option>file</option>, but not both, in one section.
+    '';
+
+    slot = mkOptionalIntParam ''
+      Optional slot number of the token that stores the certificate.
+    '';
+
+    module = mkOptionalStrParam ''
+      Optional PKCS#11 module name.
+    '';
+  };
+in {
+  authorities = mkAttrsOfParams ({
+
+    cacert = mkOptionalStrParam ''
+      The certificates may use a relative path from the swanctl
+      <literal>x509ca</literal> directory or an absolute path.
+      </para><para>
+      Configure one of <option>cacert</option>,
+      <option>file</option>, or
+      <option>handle</option> per section.
+    '';
+
+    cert_uri_base = mkOptionalStrParam ''
+      Defines the base URI for the Hash and URL feature supported by
+      IKEv2. Instead of exchanging complete certificates, IKEv2 allows one to
+      send an URI that resolves to the DER encoded certificate. The certificate
+      URIs are built by appending the SHA1 hash of the DER encoded certificates
+      to this base URI.
+    '';
+
+    crl_uris = mkCommaSepListParam [] ''
+      List of CRL distribution points (ldap, http, or file URI).
+    '';
+
+    ocsp_uris = mkCommaSepListParam [] ''
+      List of OCSP URIs.
+    '';
+
+  } // certParams) ''
+    Section defining complementary attributes of certification authorities, each
+    in its own subsection with an arbitrary yet unique name
+  '';
+
+  connections = mkAttrsOfParams {
+
+    version = mkIntParam 0 ''
+      IKE major version to use for connection.
+      <itemizedlist>
+      <listitem><para>1 uses IKEv1 aka ISAKMP,</para></listitem>
+      <listitem><para>2 uses IKEv2.</para></listitem>
+      <listitem><para>A connection using the default of 0 accepts both IKEv1 and IKEv2 as
+      responder, and initiates the connection actively with IKEv2.</para></listitem>
+      </itemizedlist>
+    '';
+
+    local_addrs	= mkCommaSepListParam [] ''
+      Local address(es) to use for IKE communication. Takes
+      single IPv4/IPv6 addresses, DNS names, CIDR subnets or IP address ranges.
+      </para><para>
+      As initiator, the first non-range/non-subnet is used to initiate the
+      connection from. As responder, the local destination address must match at
+      least to one of the specified addresses, subnets or ranges.
+      </para><para>
+      If FQDNs are assigned they are resolved every time a configuration lookup
+      is done. If DNS resolution times out, the lookup is delayed for that time.
+    '';
+
+    remote_addrs = mkCommaSepListParam [] ''
+      Remote address(es) to use for IKE communication. Takes
+      single IPv4/IPv6 addresses, DNS names, CIDR subnets or IP address ranges.
+      </para><para>
+      As initiator, the first non-range/non-subnet is used to initiate the
+      connection to. As responder, the initiator source address must match at
+      least to one of the specified addresses, subnets or ranges.
+      </para><para>
+      If FQDNs are assigned they are resolved every time a configuration lookup
+      is done. If DNS resolution times out, the lookup is delayed for that time.
+      To initiate a connection, at least one specific address or DNS name must
+      be specified.
+    '';
+
+    local_port = mkIntParam 500 ''
+      Local UDP port for IKE communication. By default the port of the socket
+      backend is used, which is usually <literal>500</literal>. If port
+      <literal>500</literal> is used, automatic IKE port floating to port
+      <literal>4500</literal> is used to work around NAT issues.
+      </para><para>
+      Using a non-default local IKE port requires support from the socket
+      backend in use (socket-dynamic).
+    '';
+
+    remote_port = mkIntParam 500 ''
+      Remote UDP port for IKE communication. If the default of port
+      <literal>500</literal> is used, automatic IKE port floating to port
+      <literal>4500</literal> is used to work around NAT issues.
+    '';
+
+    proposals = mkCommaSepListParam ["default"] ''
+      A proposal is a set of algorithms. For non-AEAD algorithms, this includes
+      for IKE an encryption algorithm, an integrity algorithm, a pseudo random
+      function and a Diffie-Hellman group. For AEAD algorithms, instead of
+      encryption and integrity algorithms, a combined algorithm is used.
+      </para><para>
+      In IKEv2, multiple algorithms of the same kind can be specified in a
+      single proposal, from which one gets selected. In IKEv1, only one
+      algorithm per kind is allowed per proposal, more algorithms get implicitly
+      stripped. Use multiple proposals to offer different algorithms
+      combinations in IKEv1.
+      </para><para>
+      Algorithm keywords get separated using dashes. Multiple proposals may be
+      specified in a list. The special value <literal>default</literal> forms a
+      default proposal of supported algorithms considered safe, and is usually a
+      good choice for interoperability.
+    '';
+
+    vips = mkCommaSepListParam [] ''
+      List of virtual IPs to request in IKEv2 configuration payloads or IKEv1
+      Mode Config. The wildcard addresses <literal>0.0.0.0</literal> and
+      <literal>::</literal> request an arbitrary address, specific addresses may
+      be defined. The responder may return a different address, though, or none
+      at all.
+    '';
+
+    aggressive = mkYesNoParam no ''
+      Enables Aggressive Mode instead of Main Mode with Identity
+      Protection. Aggressive Mode is considered less secure, because the ID and
+      HASH payloads are exchanged unprotected. This allows a passive attacker to
+      snoop peer identities, and even worse, start dictionary attacks on the
+      Preshared Key.
+    '';
+
+    pull = mkYesNoParam yes ''
+      If the default of yes is used, Mode Config works in pull mode, where the
+      initiator actively requests a virtual IP. With no, push mode is used,
+      where the responder pushes down a virtual IP to the initiating peer.
+      </para><para>
+      Push mode is currently supported for IKEv1, but not in IKEv2. It is used
+      by a few implementations only, pull mode is recommended.
+    '';
+
+    dscp = mkStrParam "000000" ''
+      Differentiated Services Field Codepoint to set on outgoing IKE packets for
+      this connection. The value is a six digit binary encoded string specifying
+      the Codepoint to set, as defined in RFC 2474.
+    '';
+
+    encap = mkYesNoParam no ''
+      To enforce UDP encapsulation of ESP packets, the IKE daemon can fake the
+      NAT detection payloads. This makes the peer believe that NAT takes place
+      on the path, forcing it to encapsulate ESP packets in UDP.
+      </para><para>
+      Usually this is not required, but it can help to work around connectivity
+      issues with too restrictive intermediary firewalls.
+    '';
+
+    mobike = mkYesNoParam yes ''
+      Enables MOBIKE on IKEv2 connections. MOBIKE is enabled by default on IKEv2
+      connections, and allows mobility of clients and multi-homing on servers by
+      migrating active IPsec tunnels.
+      </para><para>
+      Usually keeping MOBIKE enabled is unproblematic, as it is not used if the
+      peer does not indicate support for it. However, due to the design of
+      MOBIKE, IKEv2 always floats to port 4500 starting from the second
+      exchange. Some implementations don't like this behavior, hence it can be
+      disabled.
+    '';
+
+    dpd_delay = mkDurationParam "0s" ''
+      Interval to check the liveness of a peer actively using IKEv2
+      INFORMATIONAL exchanges or IKEv1 R_U_THERE messages. Active DPD checking
+      is only enforced if no IKE or ESP/AH packet has been received for the
+      configured DPD delay.
+    '';
+
+    dpd_timeout = mkDurationParam "0s" ''
+      Charon by default uses the normal retransmission mechanism and timeouts to
+      check the liveness of a peer, as all messages are used for liveness
+      checking. For compatibility reasons, with IKEv1 a custom interval may be
+      specified; this option has no effect on connections using IKEv2.
+    '';
+
+    fragmentation = mkEnumParam ["yes" "accept" "force" "no"] "yes" ''
+      Use IKE fragmentation (proprietary IKEv1 extension or RFC 7383 IKEv2
+      fragmentation). Acceptable values are <literal>yes</literal> (the default
+      since 5.5.1), <literal>accept</literal> (since versions:5.5.3),
+      <literal>force</literal> and <literal>no</literal>.
+      <itemizedlist>
+      <listitem><para>If set to <literal>yes</literal>, and the peer
+      supports it, oversized IKE messages will be sent in fragments.</para></listitem>
+      <listitem><para>If set to
+      <literal>accept</literal>, support for fragmentation is announced to the peer but the daemon
+      does not send its own messages in fragments.</para></listitem>
+      <listitem><para>If set to <literal>force</literal> (only
+      supported for IKEv1) the initial IKE message will already be fragmented if
+      required.</para></listitem>
+      <listitem><para>Finally, setting the option to <literal>no</literal> will disable announcing
+      support for this feature.</para></listitem>
+      </itemizedlist>
+      </para><para>
+      Note that fragmented IKE messages sent by a peer are always processed
+      irrespective of the value of this option (even when set to no).
+    '';
+
+    send_certreq = mkYesNoParam yes ''
+      Send certificate request payloads to offer trusted root CA certificates to
+      the peer. Certificate requests help the peer to choose an appropriate
+      certificate/private key for authentication and are enabled by default.
+      Disabling certificate requests can be useful if too many trusted root CA
+      certificates are installed, as each certificate request increases the size
+      of the initial IKE packets.
+   '';
+
+    send_cert = mkEnumParam ["always" "never" "ifasked" ] "ifasked" ''
+      Send certificate payloads when using certificate authentication.
+      <itemizedlist>
+      <listitem><para>With the default of <literal>ifasked</literal> the daemon sends
+      certificate payloads only if certificate requests have been received.</para></listitem>
+      <listitem><para><literal>never</literal> disables sending of certificate payloads
+      altogether,</para></listitem>
+      <listitem><para><literal>always</literal> causes certificate payloads to be sent
+      unconditionally whenever certificate authentication is used.</para></listitem>
+      </itemizedlist>
+    '';
+
+    keyingtries = mkIntParam 1 ''
+      Number of retransmission sequences to perform during initial
+      connect. Instead of giving up initiation after the first retransmission
+      sequence with the default value of <literal>1</literal>, additional
+      sequences may be started according to the configured value. A value of
+      <literal>0</literal> initiates a new sequence until the connection
+      establishes or fails with a permanent error.
+    '';
+
+    unique = mkEnumParam ["no" "never" "keep" "replace"] "no" ''
+      Connection uniqueness policy to enforce. To avoid multiple connections
+      from the same user, a uniqueness policy can be enforced.
+      </para><para>
+      <itemizedlist>
+      <listitem><para>
+      The value <literal>never</literal> does never enforce such a policy, even
+      if a peer included INITIAL_CONTACT notification messages,
+      </para></listitem>
+      <listitem><para>
+      whereas <literal>no</literal> replaces existing connections for the same
+      identity if a new one has the INITIAL_CONTACT notify.
+      </para></listitem>
+      <listitem><para>
+      <literal>keep</literal> rejects new connection attempts if the same user
+      already has an active connection,
+      </para></listitem>
+      <listitem><para>
+      <literal>replace</literal> deletes any existing connection if a new one
+      for the same user gets established.
+      </para></listitem>
+      </itemizedlist>
+      To compare connections for uniqueness, the remote IKE identity is used. If
+      EAP or XAuth authentication is involved, the EAP-Identity or XAuth
+      username is used to enforce the uniqueness policy instead.
+      </para><para>
+      On initiators this setting specifies whether an INITIAL_CONTACT notify is
+      sent during IKE_AUTH if no existing connection is found with the remote
+      peer (determined by the identities of the first authentication
+      round). Only if set to keep or replace will the client send a notify.
+    '';
+
+    reauth_time	= mkDurationParam "0s" ''
+      Time to schedule IKE reauthentication. IKE reauthentication recreates the
+      IKE/ISAKMP SA from scratch and re-evaluates the credentials. In asymmetric
+      configurations (with EAP or configuration payloads) it might not be
+      possible to actively reauthenticate as responder. The IKEv2
+      reauthentication lifetime negotiation can instruct the client to perform
+      reauthentication.
+      </para><para>
+      Reauthentication is disabled by default. Enabling it usually may lead to
+      small connection interruptions, as strongSwan uses a break-before-make
+      policy with IKEv2 to avoid any conflicts with associated tunnel resources.
+    '';
+
+    rekey_time = mkDurationParam "4h" ''
+      IKE rekeying refreshes key material using a Diffie-Hellman exchange, but
+      does not re-check associated credentials. It is supported in IKEv2 only,
+      IKEv1 performs a reauthentication procedure instead.
+      </para><para>
+      With the default value IKE rekeying is scheduled every 4 hours, minus the
+      configured rand_time. If a reauth_time is configured, rekey_time defaults
+      to zero, disabling rekeying; explicitly set both to enforce rekeying and
+      reauthentication.
+    '';
+
+    over_time = mkOptionalDurationParam ''
+      Hard IKE_SA lifetime if rekey/reauth does not complete, as time. To avoid
+      having an IKE/ISAKMP kept alive if IKE reauthentication or rekeying fails
+      perpetually, a maximum hard lifetime may be specified. If the IKE_SA fails
+      to rekey or reauthenticate within the specified time, the IKE_SA gets
+      closed.
+      </para><para>
+      In contrast to CHILD_SA rekeying, over_time is relative in time to the
+      rekey_time and reauth_time values, as it applies to both.
+      </para><para>
+      The default is 10% of the longer of <option>rekey_time</option> and
+      <option>reauth_time</option>.
+    '';
+
+    rand_time = mkOptionalDurationParam ''
+      Time range from which to choose a random value to subtract from
+      rekey/reauth times. To avoid having both peers initiating the rekey/reauth
+      procedure simultaneously, a random time gets subtracted from the
+      rekey/reauth times.
+      </para><para>
+      The default is equal to the configured <option>over_time</option>.
+    '';
+
+    pools = mkCommaSepListParam [] ''
+      List of named IP pools to allocate virtual IP addresses
+      and other configuration attributes from. Each name references a pool by
+      name from either the pools section or an external pool.
+    '';
+
+    mediation = mkYesNoParam no ''
+      Whether this connection is a mediation connection, that is, whether this
+      connection is used to mediate other connections using the IKEv2 Mediation
+      Extension. Mediation connections create no CHILD_SA.
+    '';
+
+    mediated_by = mkOptionalStrParam ''
+      The name of the connection to mediate this connection through. If given,
+      the connection will be mediated through the named mediation
+      connection. The mediation connection must have mediation enabled.
+    '';
+
+    mediation_peer = mkOptionalStrParam ''
+      Identity under which the peer is registered at the mediation server, that
+      is, the IKE identity the other end of this connection uses as its local
+      identity on its connection to the mediation server. This is the identity
+      we request the mediation server to mediate us with. Only relevant on
+      connections that set mediated_by. If it is not given, the remote IKE
+      identity of the first authentication round of this connection will be
+      used.
+    '';
+
+    local = mkPrefixedAttrsOfParams {
+
+      round = mkIntParam 0 ''
+        Optional numeric identifier by which authentication rounds are
+        sorted. If not specified rounds are ordered by their position in the
+        config file/vici message.
+      '';
+
+      certs = mkCommaSepListParam [] ''
+        List of certificate candidates to use for
+        authentication. The certificates may use a relative path from the
+        swanctl <literal>x509</literal> directory or an absolute path.
+        </para><para>
+        The certificate used for authentication is selected based on the
+        received certificate request payloads. If no appropriate CA can be
+        located, the first certificate is used.
+      '';
+
+      cert = mkPostfixedAttrsOfParams certParams ''
+        Section for a certificate candidate to use for
+        authentication. Certificates in certs are transmitted as binary blobs,
+        these sections offer more flexibility.
+      '';
+
+      pubkeys = mkCommaSepListParam [] ''
+        List of raw public key candidates to use for
+        authentication. The public keys may use a relative path from the swanctl
+        <literal>pubkey</literal> directory or an absolute path.
+        </para><para>
+        Even though multiple local public keys could be defined in principle,
+        only the first public key in the list is used for authentication.
+      '';
+
+      auth = mkStrParam "pubkey" ''
+        Authentication to perform locally.
+        <itemizedlist>
+        <listitem><para>
+        The default <literal>pubkey</literal> uses public key authentication
+        using a private key associated to a usable certificate.
+        </para></listitem>
+        <listitem><para>
+        <literal>psk</literal> uses pre-shared key authentication.
+        </para></listitem>
+        <listitem><para>
+        The IKEv1 specific <literal>xauth</literal> is used for XAuth or Hybrid
+        authentication,
+        </para></listitem>
+        <listitem><para>
+        while the IKEv2 specific <literal>eap</literal> keyword defines EAP
+        authentication.
+        </para></listitem>
+        <listitem><para>
+        For <literal>xauth</literal>, a specific backend name may be appended,
+        separated by a dash. The appropriate <literal>xauth</literal> backend is
+        selected to perform the XAuth exchange. For traditional XAuth, the
+        <literal>xauth</literal> method is usually defined in the second
+        authentication round following an initial <literal>pubkey</literal> (or
+        <literal>psk</literal>) round. Using <literal>xauth</literal> in the
+        first round performs Hybrid Mode client authentication.
+        </para></listitem>
+        <listitem><para>
+        For <literal>eap</literal>, a specific EAP method name may be appended, separated by a
+        dash. An EAP module implementing the appropriate method is selected to
+        perform the EAP conversation.
+        </para></listitem>
+        <listitem><para>
+        Since 5.4.0, if both peers support RFC 7427 ("Signature Authentication
+        in IKEv2") specific hash algorithms to be used during IKEv2
+        authentication may be configured. To do so use <literal>ike:</literal>
+        followed by a trust chain signature scheme constraint (see description
+        of the <option>remote</option> section's <option>auth</option>
+        keyword). For example, with <literal>ike:pubkey-sha384-sha256</literal>
+        a public key signature scheme with either SHA-384 or SHA-256 would get
+        used for authentication, in that order and depending on the hash
+        algorithms supported by the peer. If no specific hash algorithms are
+        configured, the default is to prefer an algorithm that matches or
+        exceeds the strength of the signature key. If no constraints with
+        <literal>ike:</literal> prefix are configured any signature scheme
+        constraint (without <literal>ike:</literal> prefix) will also apply to
+        IKEv2 authentication, unless this is disabled in
+        <literal>strongswan.conf</literal>.
+        </para></listitem>
+        </itemizedlist>
+      '';
+
+      id = mkOptionalStrParam ''
+        IKE identity to use for authentication round. When using certificate
+        authentication, the IKE identity must be contained in the certificate,
+        either as subject or as subjectAltName.
+      '';
+
+      eap_id = mkOptionalStrParam ''
+        Client EAP-Identity to use in EAP-Identity exchange and the EAP method.
+      '';
+
+      aaa_id = mkOptionalStrParam ''
+        Server side EAP-Identity to expect in the EAP method. Some EAP methods,
+        such as EAP-TLS, use an identity for the server to perform mutual
+        authentication. This identity may differ from the IKE identity,
+        especially when EAP authentication is delegated from the IKE responder
+        to an AAA backend.
+        </para><para>
+        For EAP-(T)TLS, this defines the identity for which the server must
+        provide a certificate in the TLS exchange.
+      '';
+
+      xauth_id = mkOptionalStrParam ''
+        Client XAuth username used in the XAuth exchange.
+      '';
+
+    } ''
+      Section for a local authentication round. A local authentication round
+      defines the rules how authentication is performed for the local
+      peer. Multiple rounds may be defined to use IKEv2 RFC 4739 Multiple
+      Authentication or IKEv1 XAuth.
+      </para><para>
+      Each round is defined in a section having <literal>local</literal> as
+      prefix, and an optional unique suffix. To define a single authentication
+      round, the suffix may be omitted.
+    '';
+
+    remote = mkPrefixedAttrsOfParams {
+
+      round = mkIntParam 0 ''
+        Optional numeric identifier by which authentication rounds are
+        sorted. If not specified rounds are ordered by their position in the
+        config file/vici message.
+      '';
+
+      id = mkStrParam "%any" ''
+        IKE identity to expect for authentication round. When using certificate
+        authentication, the IKE identity must be contained in the certificate,
+        either as subject or as subjectAltName.
+      '';
+
+      eap_id = mkOptionalStrParam ''
+        Identity to use as peer identity during EAP authentication. If set to
+        <literal>%any</literal> the EAP-Identity method will be used to ask the
+        client for an EAP identity.
+      '';
+
+      groups = mkCommaSepListParam [] ''
+        Authorization group memberships to require. The peer
+        must prove membership to at least one of the specified groups. Group
+        membership can be certified by different means, for example by
+        appropriate Attribute Certificates or by an AAA backend involved in the
+        authentication.
+      '';
+
+      cert_policy = mkCommaSepListParam [] ''
+        List of certificate policy OIDs the peer's certificate
+        must have. OIDs are specified using the numerical dotted representation.
+      '';
+
+      certs = mkCommaSepListParam [] ''
+        List of certificates to accept for authentication. The certificates may
+        use a relative path from the swanctl <literal>x509</literal> directory
+        or an absolute path.
+      '';
+
+      cert = mkPostfixedAttrsOfParams certParams ''
+        Section for a certificate candidate to use for
+        authentication. Certificates in certs are transmitted as binary blobs,
+        these sections offer more flexibility.
+      '';
+
+      cacerts = mkCommaSepListParam [] ''
+        List of CA certificates to accept for
+        authentication. The certificates may use a relative path from the
+        swanctl <literal>x509ca</literal> directory or an absolute path.
+      '';
+
+      cacert = mkPostfixedAttrsOfParams certParams ''
+        Section for a CA certificate to accept for authentication. Certificates
+        in cacerts are transmitted as binary blobs, these sections offer more
+        flexibility.
+      '';
+
+      pubkeys = mkCommaSepListParam [] ''
+        List of raw public keys to accept for
+        authentication. The public keys may use a relative path from the swanctl
+        <literal>pubkey</literal> directory or an absolute path.
+      '';
+
+      revocation = mkEnumParam ["strict" "ifuri" "relaxed"] "relaxed" ''
+        Certificate revocation policy for CRL or OCSP revocation.
+        <itemizedlist>
+        <listitem><para>
+        A <literal>strict</literal> revocation policy fails if no revocation information is
+        available, i.e. the certificate is not known to be unrevoked.
+        </para></listitem>
+        <listitem><para>
+        <literal>ifuri</literal> fails only if a CRL/OCSP URI is available, but certificate
+        revocation checking fails, i.e. there should be revocation information
+        available, but it could not be obtained.
+        </para></listitem>
+        <listitem><para>
+        The default revocation policy <literal>relaxed</literal> fails only if a certificate is
+        revoked, i.e. it is explicitly known that it is bad.
+        </para></listitem>
+        </itemizedlist>
+      '';
+
+      auth = mkStrParam "pubkey" ''
+        Authentication to expect from remote. See the <option>local</option>
+        section's <option>auth</option> keyword description about the details of
+        supported mechanisms.
+        </para><para>
+        Since 5.4.0, to require a trustchain public key strength for the remote
+        side, specify the key type followed by the minimum strength in bits (for
+        example <literal>ecdsa-384</literal> or
+        <literal>rsa-2048-ecdsa-256</literal>). To limit the acceptable set of
+        hashing algorithms for trustchain validation, append hash algorithms to
+        pubkey or a key strength definition (for example
+        <literal>pubkey-sha1-sha256</literal> or
+        <literal>rsa-2048-ecdsa-256-sha256-sha384-sha512</literal>). Unless
+        disabled in <literal>strongswan.conf</literal>, or explicit IKEv2
+        signature constraints are configured (refer to the description of the
+        <option>local</option> section's <option>auth</option> keyword for
+        details), such key types and hash algorithms are also applied as
+        constraints against IKEv2 signature authentication schemes used by the
+        remote side.
+        </para><para>
+        To specify trust chain constraints for EAP-(T)TLS, append a colon to the
+        EAP method, followed by the key type/size and hash algorithm as
+        discussed above (e.g. <literal>eap-tls:ecdsa-384-sha384</literal>).
+      '';
+
+    } ''
+      Section for a remote authentication round. A remote authentication round
+      defines the constraints how the peers must authenticate to use this
+      connection. Multiple rounds may be defined to use IKEv2 RFC 4739 Multiple
+      Authentication or IKEv1 XAuth.
+      </para><para>
+      Each round is defined in a section having <literal>remote</literal> as
+      prefix, and an optional unique suffix. To define a single authentication
+      round, the suffix may be omitted.
+    '';
+
+    children = mkAttrsOfParams {
+      ah_proposals = mkCommaSepListParam [] ''
+        AH proposals to offer for the CHILD_SA. A proposal is a set of
+        algorithms. For AH, this includes an integrity algorithm and an optional
+        Diffie-Hellman group. If a DH group is specified, CHILD_SA/Quick Mode
+        rekeying and initial negotiation uses a separate Diffie-Hellman exchange
+        using the specified group (refer to esp_proposals for details).
+        </para><para>
+        In IKEv2, multiple algorithms of the same kind can be specified in a
+        single proposal, from which one gets selected. In IKEv1, only one
+        algorithm per kind is allowed per proposal, more algorithms get
+        implicitly stripped. Use multiple proposals to offer different algorithms
+        combinations in IKEv1.
+        </para><para>
+        Algorithm keywords get separated using dashes. Multiple proposals may be
+        specified in a list. The special value <literal>default</literal> forms
+        a default proposal of supported algorithms considered safe, and is
+        usually a good choice for interoperability. By default no AH proposals
+        are included, instead ESP is proposed.
+     '';
+
+      esp_proposals = mkCommaSepListParam ["default"] ''
+        ESP proposals to offer for the CHILD_SA. A proposal is a set of
+        algorithms. For ESP non-AEAD proposals, this includes an integrity
+        algorithm, an encryption algorithm, an optional Diffie-Hellman group and
+        an optional Extended Sequence Number Mode indicator. For AEAD proposals,
+        a combined mode algorithm is used instead of the separate
+        encryption/integrity algorithms.
+        </para><para>
+        If a DH group is specified, CHILD_SA/Quick Mode rekeying and initial
+        negotiation use a separate Diffie-Hellman exchange using the specified
+        group. However, for IKEv2, the keys of the CHILD_SA created implicitly
+        with the IKE_SA will always be derived from the IKE_SA's key material. So
+        any DH group specified here will only apply when the CHILD_SA is later
+        rekeyed or is created with a separate CREATE_CHILD_SA exchange. A
+        proposal mismatch might, therefore, not immediately be noticed when the
+        SA is established, but may later cause rekeying to fail.
+        </para><para>
+        Extended Sequence Number support may be indicated with the
+        <literal>esn</literal> and <literal>noesn</literal> values, both may be
+        included to indicate support for both modes. If omitted,
+        <literal>noesn</literal> is assumed.
+        </para><para>
+        In IKEv2, multiple algorithms of the same kind can be specified in a
+        single proposal, from which one gets selected. In IKEv1, only one
+        algorithm per kind is allowed per proposal, more algorithms get
+        implicitly stripped. Use multiple proposals to offer different algorithms
+        combinations in IKEv1.
+        </para><para>
+        Algorithm keywords get separated using dashes. Multiple proposals may be
+        specified as a list. The special value <literal>default</literal> forms
+        a default proposal of supported algorithms considered safe, and is
+        usually a good choice for interoperability. If no algorithms are
+        specified for AH nor ESP, the default set of algorithms for ESP is
+        included.
+      '';
+
+      sha256_96 = mkYesNoParam no ''
+        HMAC-SHA-256 is used with 128-bit truncation with IPsec. For
+        compatibility with implementations that incorrectly use 96-bit truncation
+        this option may be enabled to configure the shorter truncation length in
+        the kernel. This is not negotiated, so this only works with peers that
+        use the incorrect truncation length (or have this option enabled).
+      '';
+
+      local_ts = mkCommaSepListParam ["dynamic"] ''
+        List of local traffic selectors to include in CHILD_SA. Each selector is
+        a CIDR subnet definition, followed by an optional proto/port
+        selector. The special value <literal>dynamic</literal> may be used
+        instead of a subnet definition, which gets replaced by the tunnel outer
+        address or the virtual IP, if negotiated. This is the default.
+        </para><para>
+        A protocol/port selector is surrounded by opening and closing square
+        brackets. Between these brackets, a numeric or getservent(3) protocol
+        name may be specified. After the optional protocol restriction, an
+        optional port restriction may be specified, separated by a slash. The
+        port restriction may be numeric, a getservent(3) service name, or the
+        special value <literal>opaque</literal> for RFC 4301 OPAQUE
+        selectors. Port ranges may be specified as well, none of the kernel
+        backends currently support port ranges, though.
+        </para><para>
+        When IKEv1 is used only the first selector is interpreted, except if the
+        Cisco Unity extension plugin is used. This is due to a limitation of the
+        IKEv1 protocol, which only allows a single pair of selectors per
+        CHILD_SA. So to tunnel traffic matched by several pairs of selectors when
+        using IKEv1 several children (CHILD_SAs) have to be defined that cover
+        the selectors.  The IKE daemon uses traffic selector narrowing for IKEv1,
+        the same way it is standardized and implemented for IKEv2. However, this
+        may lead to problems with other implementations. To avoid that, configure
+        identical selectors in such scenarios.
+      '';
+
+      remote_ts = mkCommaSepListParam ["dynamic"] ''
+        List of remote selectors to include in CHILD_SA. See
+        <option>local_ts</option> for a description of the selector syntax.
+      '';
+
+      rekey_time = mkDurationParam "1h" ''
+        Time to schedule CHILD_SA rekeying. CHILD_SA rekeying refreshes key
+        material, optionally using a Diffie-Hellman exchange if a group is
+        specified in the proposal.  To avoid rekey collisions initiated by both
+        ends simultaneously, a value in the range of <option>rand_time</option>
+        gets subtracted to form the effective soft lifetime.
+        </para><para>
+        By default CHILD_SA rekeying is scheduled every hour, minus
+        <option>rand_time</option>.
+      '';
+
+      life_time = mkOptionalDurationParam ''
+        Maximum lifetime before CHILD_SA gets closed. Usually this hard lifetime
+        is never reached, because the CHILD_SA gets rekeyed before. If that fails
+        for whatever reason, this limit closes the CHILD_SA.  The default is 10%
+        more than the <option>rekey_time</option>.
+      '';
+
+      rand_time = mkOptionalDurationParam ''
+        Time range from which to choose a random value to subtract from
+        <option>rekey_time</option>. The default is the difference between
+        <option>life_time</option> and <option>rekey_time</option>.
+      '';
+
+      rekey_bytes = mkIntParam 0 ''
+        Number of bytes processed before initiating CHILD_SA rekeying. CHILD_SA
+        rekeying refreshes key material, optionally using a Diffie-Hellman
+        exchange if a group is specified in the proposal.
+        </para><para>
+        To avoid rekey collisions initiated by both ends simultaneously, a value
+        in the range of <option>rand_bytes</option> gets subtracted to form the
+        effective soft volume limit.
+        </para><para>
+        Volume based CHILD_SA rekeying is disabled by default.
+      '';
+
+      life_bytes = mkOptionalIntParam ''
+        Maximum bytes processed before CHILD_SA gets closed. Usually this hard
+        volume limit is never reached, because the CHILD_SA gets rekeyed
+        before. If that fails for whatever reason, this limit closes the
+        CHILD_SA.  The default is 10% more than <option>rekey_bytes</option>.
+      '';
+
+      rand_bytes = mkOptionalIntParam ''
+        Byte range from which to choose a random value to subtract from
+        <option>rekey_bytes</option>. The default is the difference between
+        <option>life_bytes</option> and <option>rekey_bytes</option>.
+      '';
+
+      rekey_packets = mkIntParam 0 ''
+        Number of packets processed before initiating CHILD_SA rekeying. CHILD_SA
+        rekeying refreshes key material, optionally using a Diffie-Hellman
+        exchange if a group is specified in the proposal.
+        </para><para>
+        To avoid rekey collisions initiated by both ends simultaneously, a value
+        in the range of <option>rand_packets</option> gets subtracted to form
+        the effective soft packet count limit.
+        </para><para>
+        Packet count based CHILD_SA rekeying is disabled by default.
+      '';
+
+      life_packets = mkOptionalIntParam ''
+        Maximum number of packets processed before CHILD_SA gets closed. Usually
+        this hard packets limit is never reached, because the CHILD_SA gets
+        rekeyed before. If that fails for whatever reason, this limit closes the
+        CHILD_SA.
+        </para><para>
+        The default is 10% more than <option>rekey_bytes</option>.
+      '';
+
+      rand_packets = mkOptionalIntParam ''
+        Packet range from which to choose a random value to subtract from
+        <option>rekey_packets</option>. The default is the difference between
+        <option>life_packets</option> and <option>rekey_packets</option>.
+      '';
+
+      updown = mkOptionalStrParam ''
+        Updown script to invoke on CHILD_SA up and down events.
+      '';
+
+      hostaccess = mkYesNoParam yes ''
+        Hostaccess variable to pass to <literal>updown</literal> script.
+      '';
+
+      mode = mkEnumParam [ "tunnel"
+                           "transport"
+                           "transport_proxy"
+                           "beet"
+                           "pass"
+                           "drop"
+                         ] "tunnel" ''
+        IPsec Mode to establish CHILD_SA with.
+        <itemizedlist>
+        <listitem><para>
+        <literal>tunnel</literal> negotiates the CHILD_SA in IPsec Tunnel Mode,
+        </para></listitem>
+        <listitem><para>
+        whereas <literal>transport</literal> uses IPsec Transport Mode.
+        </para></listitem>
+        <listitem><para>
+        <literal>transport_proxy</literal> signifying the special Mobile IPv6
+        Transport Proxy Mode.
+        </para></listitem>
+        <listitem><para>
+        <literal>beet</literal> is the Bound End to End Tunnel mixture mode,
+        working with fixed inner addresses without the need to include them in
+        each packet.
+        </para></listitem>
+        <listitem><para>
+        Both <literal>transport</literal> and <literal>beet</literal> modes are
+        subject to mode negotiation; <literal>tunnel</literal> mode is
+        negotiated if the preferred mode is not available.
+        </para></listitem>
+        <listitem><para>
+        <literal>pass</literal> and <literal>drop</literal> are used to install
+        shunt policies which explicitly bypass the defined traffic from IPsec
+        processing or drop it, respectively.
+        </para></listitem>
+        </itemizedlist>
+      '';
+
+      policies = mkYesNoParam yes ''
+        Whether to install IPsec policies or not. Disabling this can be useful in
+        some scenarios e.g. MIPv6, where policies are not managed by the IKE
+        daemon. Since 5.3.3.
+      '';
+
+      policies_fwd_out = mkYesNoParam no ''
+        Whether to install outbound FWD IPsec policies or not. Enabling this is
+        required in case there is a drop policy that would match and block
+        forwarded traffic for this CHILD_SA. Since 5.5.1.
+      '';
+
+      dpd_action = mkEnumParam ["clear" "trap" "restart"] "clear" ''
+        Action to perform for this CHILD_SA on DPD timeout. The default clear
+        closes the CHILD_SA and does not take further action. trap installs a
+        trap policy, which will catch matching traffic and tries to re-negotiate
+        the tunnel on-demand. restart immediately tries to re-negotiate the
+        CHILD_SA under a fresh IKE_SA.
+      '';
+
+      ipcomp = mkYesNoParam no ''
+        Enable IPComp compression before encryption. If enabled, IKE tries to
+        negotiate IPComp compression to compress ESP payload data prior to
+        encryption.
+      '';
+
+      inactivity = mkDurationParam "0s" ''
+        Timeout before closing CHILD_SA after inactivity. If no traffic has been
+        processed in either direction for the configured timeout, the CHILD_SA
+        gets closed due to inactivity. The default value of 0 disables inactivity
+        checks.
+      '';
+
+      reqid = mkIntParam 0 ''
+        Fixed reqid to use for this CHILD_SA. This might be helpful in some
+        scenarios, but works only if each CHILD_SA configuration is instantiated
+        not more than once. The default of 0 uses dynamic reqids, allocated
+        incrementally.
+      '';
+
+      priority = mkIntParam 0 ''
+        Optional fixed priority for IPsec policies. This could be useful to
+        install high-priority drop policies. The default of 0 uses dynamically
+        calculated priorities based on the size of the traffic selectors.
+      '';
+
+      interface = mkOptionalStrParam ''
+        Optional interface name to restrict outbound IPsec policies.
+      '';
+
+      mark_in = mkStrParam "0/0x00000000" ''
+        Netfilter mark and mask for input traffic. On Linux Netfilter may
+        require marks on each packet to match an SA having that option set. This
+        allows Netfilter rules to select specific tunnels for incoming
+        traffic. The special value <literal>%unique</literal> sets a unique mark
+        on each CHILD_SA instance, beyond that the value
+        <literal>%unique-dir</literal> assigns a different unique mark for each
+        CHILD_SA direction (in/out).
+        </para><para>
+        An additional mask may be appended to the mark, separated by
+        <literal>/</literal>. The default mask if omitted is
+        <literal>0xffffffff</literal>.
+      '';
+
+      mark_out = mkStrParam "0/0x00000000" ''
+        Netfilter mark and mask for output traffic. On Linux Netfilter may
+        require marks on each packet to match a policy having that option
+        set. This allows Netfilter rules to select specific tunnels for outgoing
+        traffic. The special value <literal>%unique</literal> sets a unique mark
+        on each CHILD_SA instance, beyond that the value
+        <literal>%unique-dir</literal> assigns a different unique mark for each
+        CHILD_SA direction (in/out).
+        </para><para>
+        An additional mask may be appended to the mark, separated by
+        <literal>/</literal>. The default mask if omitted is
+        <literal>0xffffffff</literal>.
+      '';
+
+      tfc_padding = mkParamOfType (with lib.types; either int (enum ["mtu"])) 0 ''
+        Pads ESP packets with additional data to have a consistent ESP packet
+        size for improved Traffic Flow Confidentiality. The padding defines the
+        minimum size of all ESP packets sent.  The default value of
+        <literal>0</literal> disables TFC padding, the special value
+        <literal>mtu</literal> adds TFC padding to create a packet size equal to
+        the Path Maximum Transfer Unit.
+      '';
+
+      replay_window = mkIntParam 32 ''
+        IPsec replay window to configure for this CHILD_SA. Larger values than
+        the default of <literal>32</literal> are supported using the Netlink
+        backend only, a value of <literal>0</literal> disables IPsec replay
+        protection.
+      '';
+
+      hw_offload = mkYesNoParam no ''
+        Enable hardware offload for this CHILD_SA, if supported by the IPsec
+        implementation.
+      '';
+
+      start_action = mkEnumParam ["none" "trap" "start"] "none" ''
+        Action to perform after loading the configuration.
+        <itemizedlist>
+        <listitem><para>
+        The default of <literal>none</literal> loads the connection only, which
+        then can be manually initiated or used as a responder configuration.
+        </para></listitem>
+        <listitem><para>
+        The value <literal>trap</literal> installs a trap policy, which triggers
+        the tunnel as soon as matching traffic has been detected.
+        </para></listitem>
+        <listitem><para>
+        The value <literal>start</literal> initiates the connection actively.
+        </para></listitem>
+        </itemizedlist>
+        When unloading or replacing a CHILD_SA configuration having a
+        <option>start_action</option> different from <literal>none</literal>,
+        the inverse action is performed. Configurations with
+        <literal>start</literal> get closed, while such with
+        <literal>trap</literal> get uninstalled.
+      '';
+
+      close_action = mkEnumParam ["none" "trap" "start"] "none" ''
+        Action to perform after a CHILD_SA gets closed by the peer.
+        <itemizedlist>
+        <listitem><para>
+        The default of <literal>none</literal> does not take any action,
+        </para></listitem>
+        <listitem><para>
+        <literal>trap</literal> installs a trap policy for the CHILD_SA.
+        </para></listitem>
+        <listitem><para>
+        <literal>start</literal> tries to re-create the CHILD_SA.
+        </para></listitem>
+        </itemizedlist>
+        </para><para>
+        <option>close_action</option> does not provide any guarantee that the
+        CHILD_SA is kept alive. It acts on explicit close messages only, but not
+        on negotiation failures. Use trap policies to reliably re-create failed
+        CHILD_SAs.
+      '';
+
+    } ''
+      CHILD_SA configuration sub-section. Each connection definition may have
+      one or more sections in its <option>children</option> subsection. The
+      section name defines the name of the CHILD_SA configuration, which must be
+      unique within the connection (denoted &#60;child&#62; below).
+    '';
+  } ''
+    Section defining IKE connection configurations, each in its own subsection
+    with an arbitrary yet unique name
+  '';
+
+  secrets = let
+    mkEapXauthParams = mkPrefixedAttrsOfParams {
+      secret = mkOptionalStrParam ''
+        Value of the EAP/XAuth secret. It may either be an ASCII string, a hex
+        encoded string if it has a 0x prefix or a Base64 encoded string if it
+        has a 0s prefix in its value.
+      '';
+
+      id = mkPrefixedAttrsOfParam (mkOptionalStrParam "") ''
+        Identity the EAP/XAuth secret belongs to. Multiple unique identities may
+        be specified, each having an <literal>id</literal> prefix, if a secret
+        is shared between multiple users.
+      '';
+
+    } ''
+      EAP secret section for a specific secret. Each EAP secret is defined in a
+      unique section having the <literal>eap</literal> prefix. EAP secrets are
+      used for XAuth authentication as well.
+    '';
+
+  in {
+
+    eap   = mkEapXauthParams;
+    xauth = mkEapXauthParams;
+
+    ntlm = mkPrefixedAttrsOfParams {
+      secret = mkOptionalStrParam ''
+        Value of the NTLM secret, which is the NT Hash of the actual secret,
+        that is, MD4(UTF-16LE(secret)). The resulting 16-byte value may either
+        be given as a hex encoded string with a 0x prefix or as a Base64 encoded
+        string with a 0s prefix.
+      '';
+
+      id = mkPrefixedAttrsOfParam (mkOptionalStrParam "") ''
+        Identity the NTLM secret belongs to. Multiple unique identities may be
+        specified, each having an id prefix, if a secret is shared between
+        multiple users.
+      '';
+    } ''
+      NTLM secret section for a specific secret. Each NTLM secret is defined in
+      a unique section having the <literal>ntlm</literal> prefix. NTLM secrets
+      may only be used for EAP-MSCHAPv2 authentication.
+    '';
+
+    ike = mkPrefixedAttrsOfParams {
+      secret = mkOptionalStrParam ''
+        Value of the IKE preshared secret. It may either be an ASCII string, a
+        hex encoded string if it has a 0x prefix or a Base64 encoded string if
+        it has a 0s prefix in its value.
+      '';
+
+      id = mkPrefixedAttrsOfParam (mkOptionalStrParam "") ''
+        IKE identity the IKE preshared secret belongs to. Multiple unique
+        identities may be specified, each having an <literal>id</literal>
+        prefix, if a secret is shared between multiple peers.
+      '';
+    } ''
+      IKE preshared secret section for a specific secret. Each IKE PSK is
+      defined in a unique section having the <literal>ike</literal> prefix.
+    '';
+
+    private = mkPrefixedAttrsOfParams {
+      file = mkPrefixedAttrsOfParam (mkOptionalStrParam "") ''
+        File name in the private folder for which this passphrase should be used.
+      '';
+
+      secret = mkOptionalStrParam ''
+        Value of decryption passphrase for private key.
+      '';
+    } ''
+      Private key decryption passphrase for a key in the
+      <literal>private</literal> folder.
+    '';
+
+    rsa = mkPrefixedAttrsOfParams {
+      file = mkPrefixedAttrsOfParam (mkOptionalStrParam "") ''
+        File name in the <literal>rsa</literal> folder for which this passphrase
+        should be used.
+      '';
+      secret = mkOptionalStrParam ''
+        Value of decryption passphrase for RSA key.
+      '';
+    } ''
+      Private key decryption passphrase for a key in the <literal>rsa</literal>
+      folder.
+    '';
+
+    ecdsa = mkPrefixedAttrsOfParams {
+      file = mkPrefixedAttrsOfParam (mkOptionalStrParam "") ''
+        File name in the <literal>ecdsa</literal> folder for which this
+        passphrase should be used.
+      '';
+      secret = mkOptionalStrParam ''
+        Value of decryption passphrase for ECDSA key.
+      '';
+    } ''
+      Private key decryption passphrase for a key in the
+      <literal>ecdsa</literal> folder.
+    '';
+
+    pkcs8 = mkPrefixedAttrsOfParams {
+      file = mkPrefixedAttrsOfParam (mkOptionalStrParam "") ''
+        File name in the <literal>pkcs8</literal> folder for which this
+        passphrase should be used.
+      '';
+      secret = mkOptionalStrParam ''
+        Value of decryption passphrase for PKCS#8 key.
+      '';
+    } ''
+      Private key decryption passphrase for a key in the
+      <literal>pkcs8</literal> folder.
+    '';
+
+    pkcs12 = mkPrefixedAttrsOfParams {
+      file = mkPrefixedAttrsOfParam (mkOptionalStrParam "") ''
+        File name in the <literal>pkcs12</literal> folder for which this
+        passphrase should be used.
+      '';
+      secret = mkOptionalStrParam ''
+        Value of decryption passphrase for PKCS#12 container.
+      '';
+    } ''
+      PKCS#12 decryption passphrase for a container in the
+      <literal>pkcs12</literal> folder.
+    '';
+
+    token = mkPrefixedAttrsOfParams {
+      handle = mkOptionalHexParam ''
+        Hex-encoded CKA_ID or handle of the private key on the token or TPM,
+        respectively.
+      '';
+
+      slot = mkOptionalIntParam ''
+        Optional slot number to access the token.
+      '';
+
+      module = mkOptionalStrParam ''
+        Optional PKCS#11 module name to access the token.
+      '';
+
+      pin = mkOptionalStrParam ''
+        Optional PIN required to access the key on the token. If none is
+        provided the user is prompted during an interactive
+        <literal>--load-creds</literal> call.
+      '';
+    } ''Definition for a private key that's stored on a token/smartcard/TPM.'';
+
+  };
+
+  pools = mkAttrsOfParams {
+    addrs = mkOptionalStrParam ''
+      Subnet or range defining addresses allocated in pool. Accepts a single
+      CIDR subnet defining the pool to allocate addresses from or an address
+      range (&#60;from&#62;-&#60;to&#62;). Pools must be unique and non-overlapping.
+    '';
+
+    dns           = mkCommaSepListParam [] "Address or CIDR subnets";
+    nbns          = mkCommaSepListParam [] "Address or CIDR subnets";
+    dhcp          = mkCommaSepListParam [] "Address or CIDR subnets";
+    netmask       = mkCommaSepListParam [] "Address or CIDR subnets";
+    server        = mkCommaSepListParam [] "Address or CIDR subnets";
+    subnet        = mkCommaSepListParam [] "Address or CIDR subnets";
+    split_include = mkCommaSepListParam [] "Address or CIDR subnets";
+    split_exclude = mkCommaSepListParam [] "Address or CIDR subnets";
+  } ''
+    Section defining named pools. Named pools may be referenced by connections
+    with the pools option to assign virtual IPs and other configuration
+    attributes. Each pool must have a unique name (denoted &#60;name&#62; below).
+  '';
+}
diff --git a/nixos/release.nix b/nixos/release.nix
index 23f050367d6..c3a3fa34338 100644
--- a/nixos/release.nix
+++ b/nixos/release.nix
@@ -352,6 +352,7 @@ in rec {
   tests.smokeping = callTest tests/smokeping.nix {};
   tests.snapper = callTest tests/snapper.nix {};
   tests.statsd = callTest tests/statsd.nix {};
+  tests.strongswan-swanctl = callTest tests/strongswan-swanctl.nix {};
   tests.sudo = callTest tests/sudo.nix {};
   tests.switchTest = callTest tests/switch-test.nix {};
   tests.taskserver = callTest tests/taskserver.nix {};
diff --git a/nixos/tests/strongswan-swanctl.nix b/nixos/tests/strongswan-swanctl.nix
new file mode 100644
index 00000000000..14be2b9f220
--- /dev/null
+++ b/nixos/tests/strongswan-swanctl.nix
@@ -0,0 +1,154 @@
+# This strongswan-swanctl test is based on:
+# https://www.strongswan.org/testing/testresults/swanctl/rw-psk-ipv4/index.html
+# https://github.com/strongswan/strongswan/tree/master/testing/tests/swanctl/rw-psk-ipv4
+#
+# The roadwarrior carol sets up a connection to gateway moon. The authentication
+# is based on pre-shared keys and IPv4 addresses. Upon the successful
+# establishment of the IPsec tunnels, the specified updown script automatically
+# inserts iptables-based firewall rules that let pass the tunneled traffic. In
+# order to test both tunnel and firewall, carol pings the client alice behind
+# the gateway moon.
+#
+#     alice                       moon                        carol
+#      eth1------vlan_0------eth1        eth2------vlan_1------eth1
+#   192.168.0.1         192.168.0.3  192.168.1.3           192.168.1.2
+#
+# See the NixOS manual for how to run this test:
+# https://nixos.org/nixos/manual/index.html#sec-running-nixos-tests-interactively
+
+import ./make-test.nix ({ pkgs, ...} :
+
+let
+  ifAddr = node: iface: (pkgs.lib.head node.config.networking.interfaces.${iface}.ip4).address;
+
+  allowESP = "iptables --insert INPUT --protocol ESP --jump ACCEPT";
+
+  # Shared VPN settings:
+  vlan0         = "192.168.0.0/24";
+  version       = 2;
+  secret        = "0sFpZAZqEN6Ti9sqt4ZP5EWcqx";
+  esp_proposals = [ "aes128gcm128-x25519" ];
+  proposals     = [ "aes128-sha256-x25519" ];
+in {
+  name = "strongswan-swanctl";
+  meta.maintainers = with pkgs.stdenv.lib.maintainers; [ basvandijk ];
+  nodes = {
+
+    alice = { nodes, ... } : {
+      virtualisation.vlans = [ 0 ];
+      networking = {
+        dhcpcd.enable = false;
+        defaultGateway = ifAddr nodes.moon "eth1";
+      };
+    };
+
+    moon = {pkgs, config, nodes, ...} :
+      let
+        carolIp = ifAddr nodes.carol "eth1";
+        moonIp  = ifAddr nodes.moon  "eth2";
+        strongswan = config.services.strongswan-swanctl.package;
+      in {
+        virtualisation.vlans = [ 0 1 ];
+        networking = {
+          dhcpcd.enable = false;
+          firewall = {
+            allowedUDPPorts = [ 4500 500 ];
+            extraCommands = allowESP;
+          };
+          nat = {
+            enable             = true;
+            internalIPs        = [ vlan0 ];
+            internalInterfaces = [ "eth1" ];
+            externalIP         = moonIp;
+            externalInterface  = "eth2";
+          };
+        };
+        environment.systemPackages = [ strongswan ];
+        services.strongswan-swanctl = {
+          enable = true;
+          swanctl = {
+            connections = {
+              "rw" = {
+                local_addrs = [ moonIp ];
+                local."main" = {
+                  auth = "psk";
+                };
+                remote."main" = {
+                  auth = "psk";
+                };
+                children = {
+                  "net" = {
+                    local_ts = [ vlan0 ];
+                    updown = "${strongswan}/libexec/ipsec/_updown iptables";
+                    inherit esp_proposals;
+                  };
+                };
+                inherit version;
+                inherit proposals;
+              };
+            };
+            secrets = {
+              ike."carol" = {
+                id."main" = carolIp;
+                inherit secret;
+              };
+            };
+          };
+        };
+      };
+
+    carol = {pkgs, config, nodes, ...} :
+      let
+        carolIp = ifAddr nodes.carol "eth1";
+        moonIp  = ifAddr nodes.moon  "eth2";
+        strongswan = config.services.strongswan-swanctl.package;
+      in {
+        virtualisation.vlans = [ 1 ];
+        networking = {
+          dhcpcd.enable = false;
+          firewall.extraCommands = allowESP;
+        };
+        environment.systemPackages = [ strongswan ];
+        services.strongswan-swanctl = {
+          enable = true;
+          swanctl = {
+            connections = {
+              "home" = {
+                local_addrs = [ carolIp ];
+                remote_addrs = [ moonIp ];
+                local."main" = {
+                  auth = "psk";
+                  id = carolIp;
+                };
+                remote."main" = {
+                  auth = "psk";
+                  id = moonIp;
+                };
+                children = {
+                  "home" = {
+                    remote_ts = [ vlan0 ];
+                    start_action = "trap";
+                    updown = "${strongswan}/libexec/ipsec/_updown iptables";
+                    inherit esp_proposals;
+                  };
+                };
+                inherit version;
+                inherit proposals;
+              };
+            };
+            secrets = {
+              ike."moon" = {
+                id."main" = moonIp;
+                inherit secret;
+              };
+            };
+          };
+        };
+      };
+
+  };
+  testScript = ''
+    startAll();
+    $carol->waitUntilSucceeds("ping -c 1 alice");
+  '';
+})