STRONGSWAN.CONF(5)

💖 strongswan.conf(5) — the strongSwan config file, bestie edition 💖

🌸 NAME 🌸

strongswan.conf — strongSwan configuration file. That's the name, that's the vibe, and the lore starts right below. 💅

✨ DESCRIPTION ✨

okay so ipsec.conf(5) is genuinely great for defining IPsec-related configuration parameters, but it is not useful for other strongSwan applications to read options from this file. It's hard to parse, and only ipsec starter is capable of doing so. Since the number of components in the strongSwan project is continually growing, a more flexible configuration file was needed — one that's easy to extend and can be used by all components. With strongSwan 4.2.1, strongswan.conf(5) was introduced, and it meets these requirements. slay. ✨

🎀 SYNTAX 🎀

The format of the strongswan.conf file consists of hierarchical sections and a list of key/value pairs in each section. Each section has a name, followed by C-Style curly brackets defining the section body. Each section body contains a set of subsections and key/value pairs:

settings := (section|keyvalue)*
section  := name { settings }
keyvalue := key = value\n

Values must be terminated by a newline.

Comments are possible using the #-character.

Section names and keys may contain any printable character except:

. , : { } = " # \n \t space

An example file in this format might look like this:

a = b
section-one {
     somevalue = asdf
     subsection {
          othervalue = xxx
     }
     # yei, a comment
     yetanother = zz
}
section-two {
     x = 12
}

Indentation is optional, you may use tabs or spaces.

🔢 NUMBER FORMATS 🔢

Options that define an integer value can be specified as decimal (the default) or hexadecimal (0x prefix, upper- or lowercase letters are accepted). Locale-dependent strings (e.g. the thousands separator of the current locale) may also be accepted in locales other than "C".

Options that define a floating-point value can be specified as decimal (the default) or hexadecimal (0x prefix, upper- or lowercase letters are accepted). The radix character (decimal separator) in either case is locale-dependent, usually ..

⏰ TIME FORMATS ⏰

Unless stated otherwise, options that define a time are specified in seconds. The "s", "m", "h" and "d" suffixes may be used to automatically convert values given in seconds, minutes, hours or days (for instance, instead of configuring a rekey time of 4 hours as "14400" seconds, "4h" may be used).

There are some global options that don't accept these suffixes as they are configured as integer values in seconds or milliseconds, or even as floating-point numbers (e.g. the retransmission timeout). Options that accept the suffixes have a corresponding default value.

🧚 REFERENCING OTHER SECTIONS 🧚

It is possible to inherit settings and sections from another section. This feature is mainly useful in swanctl.conf (which uses the same file format). The syntax is as follows:

section    := name : references { settings }
references := absname[, absname]*
absname    := name[.name]*

All key/value pairs and all subsections of the referenced sections will be inherited by the section that references them via their absolute name. Values may be overridden in the section or any of its sub-sections (use an empty assignment to clear a value so its default value, if any, will apply). It is currently not possible to limit the inclusion level or clear/remove inherited sub-sections.

If the order is important (e.g. for auth rounds in a connection, if round is not used), it should be noted that inherited settings/sections will follow those defined in the current section (if multiple sections are referenced, their settings are enumerated left to right).

References are evaluated dynamically at runtime, so referring to sections later in the config file or included via other files is no problem.

Here is an example of how this might look like:

conn-defaults {
     # default settings for all conns (e.g. a cert, or IP pools)
}
eap-defaults {
     # defaults if eap is used (e.g. a remote auth round)
}
child-defaults {
     # defaults for child configs (e.g. traffic selectors)
}
connections {
     conn-a : conn-defaults, eap-defaults {
          # set/override stuff specific to this connection
          children {
               child-a : child-defaults {
                    # set/override stuff specific to this child
               }
          }
     }
     conn-b : conn-defaults {
          # set/override stuff specific to this connection
          children {
               child-b : child-defaults {
                    # set/override stuff specific to this child
               }
          }
     }
     conn-c : connections.conn-a {
          # everything is inherited, including everything conn-a
          # already inherits from the sections it and its
          # sub-section reference
     }
}

🦋 INCLUDING FILES 🦋

Using the include statement it is possible to include other files into strongswan.conf, e.g.

include /some/path/*.conf

If the file name is not an absolute path, it is considered to be relative to the directory of the file containing the include statement. The file name may include shell wildcards (see sh(1)). Also, such inclusions can be nested.

Sections loaded from included files extend previously loaded sections; already existing values are replaced. It is important to note that settings are added relative to the section the include statement is in.

As an example, the following three files result in the same final config as the one given above:

a = b
section-one {
     somevalue = before include
     include include.conf
}
include other.conf

include.conf:

# settings loaded from this file are added to section-one
# the following replaces the previous value
somevalue = asdf
subsection {
     othervalue = yyy
}
yetanother = zz

other.conf:

# this extends section-one and subsection
section-one {
     subsection {
          # this replaces the previous value
          othervalue = xxx
     }
}
section-two {
     x = 12
}

🔍 READING VALUES 🔍

Values are accessed using a dot-separated section list and a key. With reference to the example above, accessing section-one.subsection.othervalue will return xxx.

👑 DEFINED KEYS 👑

The following keys are currently defined (using dot notation). The default value (if any) is listed in brackets after the key.

  • aikgen.load [] — Plugins to load in the ipsec aikgen tool.
  • attest.database [] — File measurement information database URI. If it contains a password, make sure to adjust the permissions of the config file accordingly.
  • attest.load [] — Plugins to load in the ipsec attest tool.
  • charon — Options for the charon IKE daemon. Note: Many of the options in this section also apply to charon-cmd and other charon derivatives. Just use their respective name (e.g. charon-cmd instead of charon). For many options defaults can be defined in the libstrongswan section.
  • charon.accept_private_algs [no] — Deliberately violate the IKE standard's requirement and allow the use of private algorithm identifiers, even if the peer implementation is unknown.
  • charon.accept_unencrypted_mainmode_messages [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).
  • charon.block_threshold [5] — Maximum number of half-open IKE_SAs (including unprocessed IKE_SA_INITs) for a single peer IP.
  • charon.cache_crls [no] — Whether Certificate 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 ${sysconfdir}/ipsec.d/crls (stroke) or ${sysconfdir}/swanctl/x509crl (vici), respectively.
  • charon.cert_cache [yes] — Whether relations in validated certificate chains should be cached in memory.
  • charon.check_current_path [no] — By default, after detecting any changes to interfaces and/or addresses no action is taken if the current path to the remote peer still looks usable. Enabling this option will use DPD to check if the path actually still works, or, for instance, the peer removed the state after a longer phase without connectivity. It will also trigger a MOBIKE update if NAT mappings were removed during the downtime.
  • charon.cisco_flexvpn [no] — Send the Cisco FlexVPN vendor ID payload, which is required in order to make Cisco brand devices allow negotiating a local traffic selector (from strongSwan's point of view) that is not the assigned virtual IP address if such an address is requested by strongSwan. Sending the Cisco FlexVPN vendor ID prevents the peer from narrowing the initiator's local traffic selector and allows it to e.g. negotiate a TS of 0.0.0.0/0 == 0.0.0.0/0 instead. This has been tested with a "tunnel mode ipsec ipv4" Cisco template but should also work for GRE encapsulation.
  • charon.cisco_unity [no] — Send Cisco Unity vendor ID payload (IKEv1 only).
  • charon.close_ike_on_child_failure [no] — Close the IKE_SA if setup of the CHILD_SA along with IKE_AUTH failed.
  • charon.cookie_threshold [30] — Number of half-open IKE_SAs (including unprocessed IKE_SA_INITs) that activate the cookie mechanism.
  • charon.cookie_threshold_ip [3] — Number of half-open IKE_SAs (including unprocessed IKE_SA_INITs) for a single peer IP that activate the cookie mechanism.
  • charon.crypto_test.bench [default: no] — makes charon run a whole lil' benchmark of the crypto algorithms and rank them by efficiency. cute, right? 💅
  • charon.crypto_test.bench_size [default: 1024] — the buffer size used for said crypto benchmark, bestie.
  • charon.crypto_test.bench_time [default: 50] — how long the performance of each crypto algorithm gets measured, in ms.
  • charon.crypto_test.on_add [default: no] — test crypto algorithms during registration. heads up: this one needs test vectors, which the test-vectors plugin provides. 🧪
  • charon.crypto_test.on_create [default: no] — test the crypto algorithms every single time a crypto primitive gets instantiated. yeah, every time. 😭
  • charon.crypto_test.required [default: no] — if you flip this on, at least one test vector is strictly required to enable an algorithm. no vector, no algorithm, periodt.
  • charon.crypto_test.rng_true [default: no] — whether to test the RNG with TRUE quality. warning: this one requires a lot of entropy, so maybe don't be surprised when it gets greedy. 🌀
  • charon.delete_rekeyed [default: no] — delete CHILD_SAs right after they got successfully rekeyed (IKEv1 only). it slashes the number of stale CHILD_SAs in scenarios with a ton of rekeyings. but lowkey it might cause problems with implementations that keep using those rekeyed SAs until they expire. so, yk, vibes check first. 🙈
  • charon.delete_rekeyed_delay [default: 5] — delay, in seconds, before inbound IPsec SAs get deleted after rekeyings (IKEv2 only). to process delayed packets, the inbound part of a CHILD_SA stays installed up to the configured number of seconds after it got replaced during a rekeying. set it to 0 and the CHILD_SA is kept installed until it expires (and if no lifetime is set, it gets destroyed immediately — brutal). 💀
  • charon.dh_exponent_ansi_x9_42 [default: yes] — use the ANSI X9.42 DH exponent size, or the optimum size matched to cryptographic strength. your call, bestie.
  • charon.dlopen_use_rtld_now [default: no] — use RTLD_NOW with dlopen when loading plugins and IMV/IMCs, so any missing symbols get exposed immediately. no more surprises later. ✨
  • charon.dns1 [default: empty] — DNS server assigned to the peer via configuration payload (CP).
  • charon.dns2 [default: empty] — the second DNS server assigned to the peer via configuration payload (CP). yeah, there's two. 🎀
  • charon.dos_protection [default: yes] — enables Denial of Service protection using cookies and aggressiveness checks. keep the haters out. 🛡️
  • charon.filelog — this is the section where you define file loggers. see LOGGER CONFIGURATION in strongswan.conf(5) for the deets. 📓
  • charon.filelog.<name> — <name> may be the full path to the log file, but only if it contains characters that are permitted in section names. it gets ignored if path is specified. iykyk.
  • charon.filelog.<name>.<subsystem> [default: <default>] — the loglevel for a specific subsystem.
  • charon.filelog.<name>.append [default: yes] — when this is enabled, log entries get appended to the existing file. no overwriting your faves. 💖
  • charon.filelog.<name>.default [default: 1] — the default loglevel used for subsystems that don't have a specific loglevel defined.
  • charon.filelog.<name>.flush_line [default: no] — enabling this disables block buffering and enables line buffering. very "immediate", very main character. ⚡
  • charon.filelog.<name>.ike_name [default: no] — adds the connection name and a unique numerical identifier for the current IKE_SA to each log entry, if available. slay, now you know who did it.
  • charon.filelog.<name>.json [default: no] — enables writing each log entry as a JSON object to the file. the properties are "time" (if time_format is set), "thread", "group", "level" and "msg". newlines, double quotes and backslashes are escaped in the latter. if ike_name is enabled, "ikesa-uniqueid" and "ikesa-name" get added to the object if available. bestie, note this: the log_level option does not apply if this is enabled. 🫶
  • charon.filelog.<name>.log_level [default: no] — adds the log level of each message after the subsystem (e.g. [IKE2]).
  • charon.filelog.<name>.path [default: empty] — optional path to the log file. it overrides the section name. you must use this if the path contains characters that aren't allowed in section names. no cap.
  • charon.filelog.<name>.time_format [default: empty] — prefix each log entry with a timestamp. accepts a format string as passed to strftime(3). 🕰️
  • charon.filelog.<name>.time_precision [default: empty] — adds the milliseconds (ms) or microseconds (us) within the current second after the timestamp, separated by a dot — so time_format should end with %S or %T. by default, nothing is added. ✨
  • charon.flush_auth_cfg [default: no] — if enabled, objects used during authentication (certificates, identities etc.) get released to free memory once an IKE_SA is established. but heads up: enabling this might conflict with plugins that later need access to e.g. the used certificates. so it's giving "trade-off". ⚖️
  • charon.follow_redirects [default: yes] — whether to follow IKEv2 redirects (RFC 5685). 🦋
  • charon.force_eap_only_authentication [default: no] — violates RFC 5998 and uses EAP-only authentication even if the peer did not send an EAP_ONLY_AUTHENTICATION notify during IKE_AUTH. yes, it literally says violate. 🙈
  • charon.fragment_size [default: 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). unless overridden, this limit is used for both IPv4 and IPv6 if specified. 📦
  • charon.fragment_size_v4 [default: charon.fragment_size] — maximum size (complete IPv4 datagram size in bytes) of a sent IKE fragment when using proprietary IKEv1 or standardized IKEv2 fragmentation.
  • charon.fragment_size_v6 [default: charon.fragment_size] — maximum size (complete IPv6 datagram size in bytes) of a sent IKE fragment when using proprietary IKEv1 or standardized IKEv2 fragmentation. 🌈
  • charon.group [default: empty] — name of the group the daemon changes to after startup. very "new identity, who dis". 👑
  • charon.half_open_timeout [default: 30] — timeout in seconds for connecting IKE_SAs (also see IKE_SA_INIT DROPPING). ⏳
  • charon.hash_and_url [default: no] — enable hash and URL support.
  • charon.host_resolver.max_threads [default: 3] — maximum number of concurrent resolver threads (they get terminated if unused). 🧵
  • charon.host_resolver.min_threads [default: 0] — minimum number of resolver threads to keep around.
  • charon.i_dont_care_about_security_and_use_aggressive_mode_psk [default: no] — bestie the option name is literally a confession 😭. 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). like, don't. but if you must, it's right there.
  • charon.ignore_acquire_ts [default: no] — if this is disabled, the traffic selectors from the kernel's acquire events (derived from the triggering packet) are prepended to the traffic selectors from the configuration for IKEv2 connections. enabling this means those specific traffic selectors get ignored, and only the ones in the config get sent. this always happens for IKEv1 connections, since the protocol only supports one set of traffic selectors per CHILD_SA. fr fr. 🔍
  • charon.ignore_routing_tables [default: empty] — a space-separated list of routing tables to be excluded from route lookups. 🗺️
  • charon.ikesa_limit [default: 0] — maximum number of IKE_SAs that can be established at the same time before new connection attempts are blocked. the velvet rope, basically. 🚧
  • charon.ikesa_table_segments [default: 1] — number of exclusively locked segments in the hash table.
  • charon.ikesa_table_size [default: 1] — size of the IKE_SA hash table.
  • charon.imcv — defaults for options in this section can be configured in the libimcv section. 🧬
  • charon.imcv.assessment_result [default: yes] — whether IMVs send a standard IETF Assessment Result attribute. 📋
  • charon.imcv.database [default: empty] — global IMV policy database URI. if it contains a password, make sure to adjust the permissions of the config file accordingly. seriously, protect that file. 🔐
  • charon.imcv.os_info.default_password_enabled [default: no] — manually set whether a default password is enabled. just… manually. by hand. you decide. 💅
  • charon.imcv.os_info.name [default: empty] — manually set the name of the client OS (e.g. Ubuntu). 🐧
  • charon.imcv.os_info.version [default: empty] — manually set the version of the client OS (e.g. 12.04 i686).
  • charon.imcv.policy_script [default: ipsec _imv_policy] — script called for each TNC connection to generate IMV policies. 📜
  • charon.inactivity_close_ike [default: no] — whether to close the IKE_SA if the only CHILD_SA closed due to inactivity. if the child leaves, does the parent too? you decide. 🚪
  • charon.init_limit_half_open [default: 0] — limit new connections based on the current number of half open IKE_SAs, see IKE_SA_INIT DROPPING in strongswan.conf(5).
  • charon.init_limit_job_load [default: 0] — limit new connections based on the number of jobs currently queued for processing (see IKE_SA_INIT DROPPING). 🧺
  • charon.initiator_only [default: no] — causes the charon daemon to ignore IKE initiation requests. it's giving "i don't text first". 📵
  • charon.install_routes [default: yes] — install routes into a separate routing table for established IPsec tunnels. 🛣️
  • charon.install_virtual_ip [default: yes] — install virtual IP addresses.
  • charon.install_virtual_ip_on [default: empty] — the name of the interface on which virtual IP addresses should be installed. if not specified, the addresses get installed on the outbound interface. 📍
  • charon.integrity_test [default: no] — check daemon, libstrongswan and plugin integrity at startup. a whole vibe check for your binaries. 💯
  • charon.interfaces_ignore [default: empty] — a comma-separated list of network interfaces that should be ignored. note: if interfaces_use is specified, this option has no effect. 🙅‍♀️
  • charon.interfaces_use [default: empty] — a comma-separated list of network interfaces that should be used by charon. all other interfaces are ignored. choose your faves. 💖
  • charon.iptfs — global settings for IP-TFS (RFC 9347). the Linux kernel supports this mode since 6.14. however, it currently only supports aggregation/fragmentation of tunneled IP packets in ESP/AGGFRAG packets. it doesn't yet support other IP-TFS features like sending packets at a constant rate or congestion control. so like, half the slay, but the slay is real. 🧚
  • charon.iptfs.accept_fragments [default: yes] — whether fragments of inner packets across multiple AGGFRAG payloads are accepted. this is an IKEv2 option, so if the peer doesn't adhere to this request and still sends such fragments, they get processed by the kernel. the kernel said "i'll allow it". 😭
  • charon.iptfs.dont_frag [default: no] — force disabling fragmenting inner packets across multiple AGGFRAG payloads when sending outbound traffic. heads up: fragmentation is automatically disabled if the peer indicates it doesn't support handling such packets. so sometimes it's not even your choice. 🦋
  • charon.iptfs.drop_time [default: 1000000] — time in microseconds to wait for out-of-order packets when processing inbound traffic. patience is a virtue, especially in microseconds. ⏱️
  • charon.iptfs.init_delay [0] — how long, in microseconds, to sit and wait for more packets to show up so they can aggregate together before you send outbound traffic 💅. Lowkey only matters when there's literally nothing already queued up to send.
  • charon.iptfs.max_queue_size [1048576] — the ceiling on how many bytes can pile up waiting to be sent on the tunnel (default is 1 MiB, bestie, that's 1048576 bytes exactly). If the queue hits the limit, packets just get dropped. No queue, no mercy 🫠.
  • charon.iptfs.packet_size [0] — max outer packet size (layer 3) when sending. The default 0 means "just use the PMTU as the packet size," iconic lazy-girl behavior and honestly valid. Note that the kernel currently doesn't pad smaller packets — so don't @ me about padding 🌸.
  • charon.iptfs.reorder_window [3] — how many packets are allowed to arrive out of order while processing inbound traffic. Give the network a little chaos budget ✨.
  • charon.keep_alive [20s] — the NAT keep alive interval. That's the little heartbeat so the NAT doesn't forget you exist 😭.
  • charon.keep_alive_dpd_margin [0s] — how many seconds the keep alive interval is allowed to be exceeded before a DPD gets sent instead of a NAT keep alive (0 disables this whole thing). It's only useful if you're using a clock that counts time spent suspended, e.g. CLOCK_BOOTTIME ⏰.
  • charon.leak_detective.detailed [yes] — includes source file names and line numbers in leak detective output. Yes it's snitching, but make it detailed snitching 🔍.
  • charon.leak_detective.usage_threshold [10240] — threshold in bytes for which allocations make it into usage reports (0 = include all of them, no gatekeeping).
  • charon.leak_detective.usage_threshold_count [0] — threshold in number of allocations for which allocations make it into usage reports (0 = include all).
  • charon.load [] — the plugins to load in the IKE daemon charon. That's the whole gig 🎀.
  • charon.load_modular [no] — if you enable this, the plugin list gets determined by the value of the charon.plugins.<name>.load options instead. And it's not just a plain boolean flag — that option may take an integer value that means the priority of a plugin, which influences the order of a plugin in the plugin list (the default priority is 1). If two plugins have the same priority, their order in the default plugin list is preserved. Enabled plugins that aren't found in that list get ordered alphabetically before the other plugins with the same priority. Very main-character-of-the-sorting-algorithm energy 👑.
  • charon.make_before_break [yes] — initiate IKEv2 reauthentication with a make-before-break scheme instead of a break-before-make one. Make-before-break uses overlapping IKE and CHILD_SA during reauthentication by first recreating all the new SAs before deleting the old ones. This is genuinely a glow-up because it avoids connectivity gaps during reauthentication — but it requires the peer to support overlapping SAs. strongSwan can handle such overlapping SAs since version 5.3.0, so you're covered 🫶.
  • charon.max_ikev1_exchanges [3] — the maximum number of IKEv1 phase 2 exchanges per IKE_SA to keep state about and track concurrently. That's the cap, bestie ✋.
  • charon.max_packet [10000] — the maximum packet size accepted by charon.
  • charon.multiple_authentication [yes] — enable multiple authentication exchanges (RFC 4739). Yes queen, log in twice, it's allowed 💅.
  • charon.nbns1 [] — WINS servers assigned to the peer via configuration payload (CP).
  • charon.nbns2 [] — the second WINS servers list assigned to the peer via configuration payload (CP). Serving the same iconic purpose, just slot number two ✨.
  • charon.ocsp_nonce_len [32] — the length of nonces in OCSP requests. According to RFC 8954, valid values are between 1 and 32, and new clients are required to use 32. Some servers might not support that, so lowering the value to e.g. 16 might be necessary. Know when to fold 'em 🙈.
  • charon.plugins.addrblock.depth [-1] — RFC3779 requires that all addrblocks claimed by a certificate must be contained in the addrblock extension of the issuer certificate, all the way up to the root CA. The default depth of -1 enforces exactly this.

In practice though, third party (root) CAs may not contain the extension, which makes the addrblock extension unusable under those CAs 😭. By limiting the validation depth, only a certain level of issuer certificates gets validated for proper addrblock extensions: a depth of 0 doesn't check any issuer certificate extensions, a depth of 1 checks only the direct issuer of the end entity certificate, and so on. It's giving "how far up the family tree do we go" 🌳.

  • charon.plugins.addrblock.strict [yes] — if set to yes, a subject certificate without an addrblock extension is rejected if the issuer certificate has such an addrblock extension. If set to no, subject certificates issued without the addrblock extension are accepted without any traffic selector checks and no policy is enforced by the plugin. Choose your vibe deliberately, bestie 💀.
  • charon.plugins.android_log.loglevel [1] — the loglevel for logging to the Android specific logger. Android girlies, this one's for you 🧚.
  • charon.plugins.attr — this is the section where you specify arbitrary attributes that get assigned to a peer via configuration payload (CP). A whole little section, not a single value, okay? 🎀
  • charon.plugins.attr.<attr> [] — <attr> can be either address, netmask, dns, nbns, dhcp, subnet, split-include, split-exclude, 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. For some attribute types, multiple values may be specified as a comma separated list. Lots of range, slay 🌈.
  • charon.plugins.attr-sql.crash_recovery [yes] — release all online leases during startup. Disable this if you want to share the DB between multiple VPN gateways. Sharing is caring but the leases are mine 😤.
  • charon.plugins.attr-sql.database [] — the database URI for the attr-sql plugin used by charon. If it contains a password, make sure to adjust the permissions of the config file accordingly. Genuinely, lock that file down — do not leave your password out here looking like a public story 🔐.
  • charon.plugins.attr-sql.lease_history [yes] — enable logging of SQL IP pool leases. Receipts, retained 📝.
  • charon.plugins.botan.internal_rng_only [no] — if enabled, only Botan's internal RNG gets used throughout the plugin. Otherwise (and if it's supported by Botan), the rng_t implementations provided by other loaded plugins are used as the RNG. Sharing is a choice 🌀.
  • charon.plugins.botan.rng.strong [user-threadsafe] — the name of the Botan RNG instance to use for RNG_STRONG and RNG_WEAK quality (e.g. user, user-threadsafe or system).
  • charon.plugins.botan.rng.true [system] — the name of the Botan RNG instance to use for RNG_TRUE quality (e.g. user, user-threadsafe or system). Different quality tier, different pick 💅.
  • charon.plugins.bypass-lan.interfaces_ignore [] — a comma-separated list of network interfaces for which the connected subnets should be ignored. If interfaces_use is specified, this option has no effect. Can't have both, pick a lane 🚫.
  • charon.plugins.bypass-lan.interfaces_use [] — a comma-separated list of network interfaces for which the connected subnets should be considered. All other interfaces are ignored. This is the guest list, everyone else is outside ✨.
  • charon.plugins.certexpire.csv.cron [] — a cron style string specifying the CSV export times. Schedule your exports like the organized icon you are 🗓️.
  • charon.plugins.certexpire.csv.empty_string [] — the string to use in empty intermediate CA fields. Fill the void with a string of your choosing 🕳️.
  • charon.plugins.certexpire.csv.fixed_fields [yes] — use a fixed intermediate CA field count. Consistency is (checks notes) slay.
  • charon.plugins.certexpire.csv.force [yes] — force export of all trustchains we have a private key for. No skipping, we're exporting everybody 💪.
  • charon.plugins.certexpire.csv.format [%d:%m:%Y] — the strftime(3) format string used to export expiration dates as. Formatting is a personality 🎀.
  • charon.plugins.certexpire.csv.local [] — the strftime(3) format string for the CSV file name to export local certificates to.
  • charon.plugins.certexpire.csv.remote [] — the strftime(3) format string for the CSV file name to export remote certificates to. Two lists, two files, no mixing 🦋.
  • charon.plugins.certexpire.csv.separator [,] — the CSV field separator. A humble comma doing the most.
  • charon.plugins.coupling.file [] — the file to store the coupling list to. It's where the coupling list lives, that's the whole sentence 💖.
  • charon.plugins.coupling.hash [sha1] — the hashing algorithm used to fingerprint coupled certificates. sha1 is the default, bestie, don't come for me, that's just what the manpage says 🌸.
  • charon.plugins.coupling.max [1] — the maximum number of coupling entries to create. Just 1, unless you say otherwise ✨.
  • charon.plugins.curl.redir [-1] — the maximum number of redirects the plugin follows, set to 0 to disable following redirects entirely, or set to -1 for no limit at all. Go wherever the internet takes you 🌀.
  • charon.plugins.curl.tls_backend [] — the SSL/TLS backend to configure in curl if multiple are available (requires libcurl 7.56 or newer). A list of available options gets logged on level 2 if nothing is configured. Similar, but on level 1, if the selected backend isn't available. Very much a "if you know you know" situation 👀.
  • charon.plugins.dhcp.force_server_address [no] — always use the configured server address. This might be helpful if the DHCP server runs on the same host as strongSwan and the DHCP daemon doesn't listen on the loopback interface. In that case the server can't be reached via unicast (or even 255.255.255.255) since that would be routed via loopback 😭. Setting this option to yes and configuring the local broadcast address (e.g. 192.168.0.255) as the server address might work. It's giving "route your way around the problem" 💅.
  • charon.plugins.dhcp.identity_lease [no] — derive a user-defined MAC address from a hash of the IKE identity and send the client identity DHCP option. Identity but make it a lease 🪪.
  • charon.plugins.dhcp.interface [] — the interface name the plugin uses for address allocation. The default is to bind to any (0.0.0.0) and let the system decide which way to route the packets to the DHCP server. Chillest possible default, honestly ✨.
  • charon.plugins.dhcp.interface_receive [charon.plugins.dhcp.interface] — the interface name the plugin uses to bind its receive socket. The default is to use the same interface as the send socket. Set it to the empty string to avoid binding the receive socket to any interface while the send socket is bound to one. If the server runs on the same host and the send socket is bound to an interface, it might be necessary to set this to lo or the empty string 🫶.
  • charon.plugins.dhcp.server [255.255.255.255] — the DHCP server unicast or broadcast IP address.
  • charon.plugins.dhcp.use_server_port [no] — use the DHCP server port (67) as the source port, instead of the DHCP client port (68), when a unicast server address is configured and the plugin acts as a relay agent. When replying in this mode, the DHCP server will always send packets to the DHCP server port, and if no process binds that port, ICMP port unreachables get sent back, which might be problematic for some DHCP servers 💀. To avoid that, enabling this option causes the plugin to bind the DHCP server port to send its requests when acting as a relay agent. This is not necessary if a DHCP server is already running on the same host — and it might even cause conflicts (though since the server port is already bound, ICMPs should not be an issue). Read that whole thing twice, bestie, it's load-bearing 📚.
  • charon.plugins.dnscert.enable [no] — turn this on and charon will go fetch CERT RRs over DNS for you ✨
  • charon.plugins.drbg.max_drbg_requests [4294967294] — how many pseudo-random bit requests the DRBG hands out before it automatically reseeds itself 🎲 that number is huge, bestie, like aggressively huge
  • charon.plugins.eap-aka.request_identity [yes] — default is yes, and the page literally gives us nothing else, so that's the whole tea 🍵
  • charon.plugins.eap-aka-3gpp.seq_check [[]] — enable this and you activate a sequence check of the AKA SQN values so resync cycles can be triggered 🔁
  • charon.plugins.eap-aka-3gpp2.seq_check [[]] — same vibe, different plugin: enable it to activate the sequence check of the AKA SQN values so resync cycles get triggered 💫
  • charon.plugins.eap-dynamic.prefer_user [no] — flip it on and the EAP methods proposed in an EAP-Nak message from the peer win out over the ones registered locally 👑 peer picks, basically
  • charon.plugins.eap-dynamic.preferred [[]] — the preferred EAP method(s). Leave it empty and the first registered method gets used initially. Give it a comma separated list and those methods get tried in the order you wrote them before it moves on to the rest of the registered methods 📋
  • charon.plugins.eap-gtc.backend [pam] — which XAuth backend does the credential verification 🎀
  • charon.plugins.eap-peap.fragment_size [1024] — max size of an EAP-PEAP packet, no cap on the drama 📦
  • charon.plugins.eap-peap.include_length [no] — include the length in non-fragmented EAP-PEAP packets
  • charon.plugins.eap-peap.max_message_count [32] — max number of processed EAP-PEAP packets (0 = no limit, go feral) 💀
  • charon.plugins.eap-peap.phase2_method [mschapv2] — the phase2 EAP client authentication method 🔐
  • charon.plugins.eap-peap.phase2_piggyback [no] — phase2 EAP Identity request piggybacked by the server onto the TLS Finished message — relevant only if TLS 1.2 or earlier was negotiated, iykyk 🐷
  • charon.plugins.eap-peap.phase2_tnc [no] — start the phase2 EAP TNC protocol once client authentication has succeeded ✅
  • charon.plugins.eap-peap.request_peer_auth [no] — request peer authentication based on a client certificate 🪪
  • charon.plugins.eap-radius.accounting [no] — send RADIUS accounting info to the RADIUS servers 🧾
  • charon.plugins.eap-radius.accounting_close_on_timeout [yes] — close the IKE_SA if there's a timeout during interim RADIUS accounting updates ⏰
  • charon.plugins.eap-radius.accounting_interval [0s] — interval in seconds for interim RADIUS accounting updates, unless the RADIUS server already said otherwise in the Access-Accept message 🌸
  • charon.plugins.eap-radius.accounting_requires_vip [no] — if enabled, accounting is disabled unless an IKE_SA has at least one virtual IP. Only for IKEv2 — for IKEv1 a virtual IP is strictly necessary anyway 💅
  • charon.plugins.eap-radius.accounting_send_class [no] — if enabled, it adds the Class attributes received in the Access-Accept message to the RADIUS accounting messages 🏷️
  • charon.plugins.eap-radius.class_group [no] — use the class attribute sent in the RADIUS-Accept message as group membership info, compared against the groups in the rightgroups option in ipsec.conf(5) 👯
  • charon.plugins.eap-radius.close_all_on_timeout [no] — closes all IKE_SAs if talking to the RADIUS server times out. If it's not set, only the current IKE_SA gets closed 🙈
  • charon.plugins.eap-radius.dae.enable [no] — enables support for the Dynamic Authorization Extension (RFC 5176) ✨
  • charon.plugins.eap-radius.dae.listen [0.0.0.0] — address to listen on for DAE messages coming from the RADIUS server 👂
  • charon.plugins.eap-radius.dae.port [3799] — port to listen for DAE requests 🔌
  • charon.plugins.eap-radius.dae.secret [[]] — shared secret used to verify/sign DAE messages. > If you set this, make sure to adjust the permissions of the config file accordingly 🔒
  • charon.plugins.eap-radius.eap_start [no] — send EAP-Start instead of EAP-Identity to kick off the RADIUS conversation 🚀
  • charon.plugins.eap-radius.filter_id [no] — if the RADIUS tunnel_type attribute with value ESP is received, use the filter_id attribute sent in the RADIUS-Accept message as group membership info, compared against the groups in the rightgroups option in ipsec.conf(5) 🎯
  • charon.plugins.eap-radius.forward.ike_to_radius [[]] — RADIUS attributes to forward from IKEv2 to RADIUS. You can define them by name or by attribute number, and a colon specifies vendor-specific attributes — e.g. Reply-Message, or 11, or 36906:12 📨
  • charon.plugins.eap-radius.forward.radius_to_ike [[]] — same as charon.plugins.eap-radius.forward.ike_to_radius but the other direction (RADIUS to IKEv2); a strongSwan specific private notify (40969) is used to transmit the attributes 🔄
  • charon.plugins.eap-radius.id_prefix [[]] — prefix to EAP-Identity, because some AAA servers use an IMSI prefix to pick the EAP method ✏️
  • charon.plugins.eap-radius.nas_identifier [strongSwan] — the NAS-Identifier to include in RADIUS messages 🪪
  • charon.plugins.eap-radius.port [1812] — port of the RADIUS server (authentication) 🔢
  • charon.plugins.eap-radius.retransmit_base [1.4] — the base used for calculating exponential back off 📈
  • charon.plugins.eap-radius.retransmit_timeout [2.0] — timeout in seconds before the first retransmit gets sent ⏳
  • charon.plugins.eap-radius.retransmit_tries [4] — how many times to retransmit a packet before giving up 😤
  • charon.plugins.eap-radius.secret [[]] — shared secret between RADIUS and NAS. > If set, make sure to adjust the permissions of the config file accordingly 🔐
  • charon.plugins.eap-radius.server [[]] — IP/Hostname of the RADIUS server 🖥️
  • charon.plugins.eap-radius.servers — section for specifying multiple RADIUS servers. The source, nas_identifier, secret, sockets and port (or auth_port) options can be specified for each server. A server's IP/Hostname goes in the address option. The acct_port [1813] option specifies the port used for RADIUS accounting. For each RADIUS server you can set a priority with the preference [0] option. And the retransmission time for each server can be set with retransmit_base, retransmit_timeout and retransmit_tries 🫶
  • charon.plugins.eap-radius.sockets [1] — number of sockets (ports) to use — crank it up for high load 🔥
  • charon.plugins.eap-radius.source [[]] — optional specific source IP to use 📍
  • charon.plugins.eap-radius.station_id_with_port [yes] — whether to include the UDP port in the Called- and Calling-Station-Id RADIUS attributes 🔢
  • charon.plugins.eap-radius.xauth — section to configure multiple XAuth authentication rounds via RADIUS. The subsections define so called authentication profiles with arbitrary names. In each profile section you can configure one or more XAuth types, each with an assigned message. For each type a separate XAuth exchange gets initiated and all the replies get concatenated into the User-Password attribute, which is then verified over RADIUS 🎀

The available XAuth types are password, passcode, nextpin, and answer. This type is not relevant to strongSwan or the AAA server, but the client may show a different dialog (along with the configured message) 💬

To actually use the profiles you configured, they have to be set up in the respective connection in ipsec.conf(5) by appending the profile name, separated by a colon, to the xauth-radius backend configuration in rightauth or rightauth2 — for instance, rightauth2=xauth-radius:profile ⭐

  • charon.plugins.eap-sim.request_identity [yes] — default yes, and the page doesn't say anything more about it, that's it, that's the entry 🌸
  • charon.plugins.eap-simaka-sql.database [[]] — empty by default; the page gives no further description 🗄️
  • charon.plugins.eap-simaka-sql.remove_used [no] — no by default; again, no extra description here 🧹
  • charon.plugins.eap-tls.fragment_size [1024] — max size of an EAP-TLS packet 📦
  • charon.plugins.eap-tls.include_length [yes] — include the length in non-fragmented EAP-TLS packets 📏
  • charon.plugins.eap-tls.max_message_count [32] — max number of processed EAP-TLS packets (0 = no limit) 💀
  • charon.plugins.eap-tnc.max_message_count [10] — max number of processed EAP-TNC packets (0 = no limit) 💀
  • charon.plugins.eap-tnc.protocol [tnccs-2.0] — the IF-TNCCS protocol version to use (tnccs-2.0 is the only option, so… choices were made) 🎀
  • charon.plugins.eap-ttls.fragment_size [1024] — max size of an EAP-TTLS packet 📦
  • charon.plugins.eap-ttls.include_length [yes] — include the length in non-fragmented EAP-TTLS packets 📏
  • charon.plugins.eap-ttls.max_message_count [32] — max number of processed EAP-TTLS packets (0 = no limit) 💀
  • charon.plugins.eap-ttls.phase2_method [md5] — the phase2 EAP client authentication method 🔐
  • charon.plugins.eap-ttls.phase2_piggyback [no] — phase2 EAP Identity request piggybacked by the server onto the TLS Finished message 🐷
  • charon.plugins.eap-ttls.phase2_tnc [no] — start the phase2 EAP TNC protocol once client authentication has succeeded ✅
  • charon.plugins.eap-ttls.phase2_tnc_method [pt] — phase2 EAP TNC transport protocol (pt as IETF standard, or legacy tnc) 🚚
  • charon.plugins.eap-ttls.request_peer_auth [no] — request peer authentication based on a client certificate 🪪
  • charon.plugins.error-notify.socket [unix://${piddir}/charon.enfy] — the socket provided by the error-notify plugin 🧚
  • charon.plugins.ext-auth.script [[]] — command to pass to the system shell for peer authorization. Authorization counts as successful if the command executes normally with an exit code of zero. For all other exit codes, IKE_SA authorization gets rejected ❌

The following environment variables get passed to the script: IKE_UNIQUE_ID: the IKE_SA numerical unique identifier. IKE_NAME: the peer configuration connection name. IKE_LOCAL_HOST: local IKE IP address. IKE_REMOTE_HOST: remote IKE IP address. IKE_LOCAL_ID: local IKE identity. IKE_REMOTE_ID: remote IKE identity. IKE_REMOTE_EAP_ID: remote EAP or XAuth identity, if used 🌈

  • charon.plugins.forecast.groups [224.0.0.1,224.0.0.22,224.0.0.251,224.0.0.252,239.255.255.250] — comma separated list of multicast groups to join locally. The local host receives and forwards packets in the local LAN only for joined multicast groups. Packets that match the list of multicast groups get forwarded to connected clients. The default group includes host multicasts, IGMP, mDNS, LLMNR and SSDP/WS-Discovery, and is usually a good choice for Windows clients 🪟✨
  • charon.plugins.forecast.interface [] — the name of the local interface where we listen for broadcast messages that need forwarding. If you don't configure any interface, the first usable one gets picked, and honestly that's usually just fine for single-homed hosts. 💅 But if your host has multiple interfaces, set this to the local LAN interface you want to forward broadcasts from/to, bestie.
  • charon.plugins.forecast.reinject [] — a comma separated list of CHILD_SA configuration names for which to perform multi/broadcast reinjection. For clients connecting over such a configuration, any multi/broadcast received over the tunnel gets reinjected to all active tunnels. This makes the broadcasts visible to other peers, and for example allows clients to see others shares. ✨ If disabled, multi/broadcast messages received over a tunnel are injected to the local network only, but not to other IPsec clients.
  • charon.plugins.ha.autobalance [0] — interval in seconds to automatically balance handled segments between nodes. Set to 0 to disable. ⚖️
  • charon.plugins.ha.buflen [2048] — buffer size for received HA messages. For IKEv1 the public DH factors are also transmitted, so depending on the DH group the HA messages can get quite big — the default should be fine up to modp4096, no cap. 🔥
  • charon.plugins.ha.fifo_interface [yes] — yes, this one's just vibes, the page gives no extra lore. 🎀
  • charon.plugins.ha.heartbeat_delay [1000] — heartbeat delay, in all its unexplained glory. 💓
  • charon.plugins.ha.heartbeat_timeout [2100] — heartbeat timeout, likewise keeping it mysterious. ⏱️
  • charon.plugins.ha.local [] — local HA address, left empty by default. 🫶
  • charon.plugins.ha.monitor [yes] — monitoring, on by default. 👀
  • charon.plugins.ha.pools [] — pools, empty by default. 🏊
  • charon.plugins.ha.receive_buffer_size [8388608] — maximum receive buffer size for the HA socket in bytes. This value controls how many bytes of HA messages can be queued to the socket. If set to 0, the default from /proc/sys/net/core/rmem_default will apply. Note that the kernel doubles the configured value to account for overhead. To exceed the system-wide maximum from /proc/sys/net/core/rmem_max, special privileges (CAP_NET_ADMIN) are necessary — otherwise, the kernel silently caps the value. 😤
  • charon.plugins.ha.remote [] — remote HA address, empty by default. 🛰️
  • charon.plugins.ha.resync [yes] — resync, enabled by default. 🔄
  • charon.plugins.ha.secret [] — the secret, empty by default (put something in there, bestie 🙈).
  • charon.plugins.ha.segment_count [1] — how many segments, 1 by default. 🍰
  • charon.plugins.ipseckey.enable [no] — enable fetching of IPSECKEY RRs via DNS. 🌐
  • charon.plugins.kernel-libipsec.allow_peer_ts [no] — allow that the remote traffic selector equals the IKE peer. The route installed for such traffic (via TUN device) usually prevents further IKE traffic, which is giving unhinged behavior tbh. The fwmark options for the kernel-netlink and socket-default plugins can be used to circumvent that problem. 🚧
  • charon.plugins.kernel-libipsec.fwmark [charon.plugins.socket-default.fwmark] — firewall mark to set on outbound raw ESP packets.
  • charon.plugins.kernel-libipsec.raw_esp [no] — whether to send and receive ESP packets without UDP encapsulation if supported on this platform and no NAT is detected. 🔍
  • charon.plugins.kernel-netlink.buflen [<min(PAGE_SIZE, 8192)>] — buffer size for received Netlink messages. Literally that's it, the default is that little expression.
  • charon.plugins.kernel-netlink.fwmark [] — firewall mark to set on the routing rule that directs traffic to our routing table. The format is [!]mark[/mask], where the optional exclamation mark inverts the meaning (i.e. the rule only applies to packets that don't match the mark). Sneaky. 🙃
  • charon.plugins.kernel-netlink.hw_offload_feature_interface [lo] — if the kernel supports hardware offloading, the plugin needs to find the feature flag which represents hardware offloading support for network devices. Using the loopback device for this purpose is usually fine, since it should always be present. For rare cases in which the loopback device cannot be used to obtain the appropriate feature flag, this option can be used to specify an alternative interface for offload feature detection. 🧚
  • charon.plugins.kernel-netlink.ignore_retransmit_errors [no] — whether to ignore errors potentially resulting from a retransmission. 🩹
  • charon.plugins.kernel-netlink.install_routes_xfrmi [no] — whether routes via XFRM interfaces are automatically installed for SAs that reference such an interface via if_id_out. If the traffic selectors include the IKE traffic to the peer, this requires special care (e.g. installing bypass policies and/or routes, or setting a mark on the IKE socket and excluding such packets from the configured routing table via fwmark option). ⚠️
  • charon.plugins.kernel-netlink.mss [0] — MSS to set on installed routes, 0 to disable. 📏
  • charon.plugins.kernel-netlink.mtu [0] — MTU to set on installed routes, 0 to disable. 📐
  • charon.plugins.kernel-netlink.parallel_route [no] — whether to perform concurrent Netlink ROUTE queries on a single socket. While parallel queries can improve throughput, it has more overhead. On vanilla Linux, DUMP queries fail with EBUSY and must be retried, further decreasing performance — so like, highkey not always the glow-up it sounds like. 💀
  • charon.plugins.kernel-netlink.parallel_xfrm [no] — whether to perform concurrent Netlink XFRM queries on a single socket.
  • charon.plugins.kernel-netlink.policy_update [no] — whether to always use XFRM_MSG_UPDPOLICY to install policies.
  • charon.plugins.kernel-netlink.port_bypass [no] — whether to use port or socket based IKE XFRM bypass policies. IKE bypass policies are used to exempt IKE traffic from XFRM processing. The default socket based policies are directly tied to the IKE UDP sockets, port based policies use global XFRM bypass policies for the used IKE UDP ports. 🚪
  • charon.plugins.kernel-netlink.process_rules [no] — whether to process changes in routing rules to trigger roam events. This is currently only useful if the kernel based route lookup is used (i.e. if route installation is disabled or an inverted fwmark match is configured). 🧭
  • charon.plugins.kernel-netlink.receive_buffer_size [8388608] — maximum Netlink socket receive buffer in bytes. This value controls how many bytes of Netlink messages can be queued to a Netlink socket. If set to 0, the default from /proc/sys/net/core/rmem_default will apply. Note that the kernel doubles the configured value to account for overhead. To exceed the system-wide maximum from /proc/sys/net/core/rmem_max, special privileges (CAP_NET_ADMIN) are necessary — otherwise, the kernel silently caps the value. Same energy as the HA one, fr fr. 😮‍💨
  • charon.plugins.kernel-netlink.retries [0] — number of Netlink message retransmissions to send on timeout. 🔁
  • charon.plugins.kernel-netlink.roam_events [yes] — whether to trigger roam events when interfaces, addresses or routes change.
  • charon.plugins.kernel-netlink.set_proto_port_transport_sa [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. Trade-offs, bestie. ⚖️

XFRM policy hashing threshold configuration for IPv4 and IPv6.

The section defines hashing thresholds to configure in the kernel during daemon startup. Each address family takes a threshold for the local subnet of an IPsec policy (src in out-policies, dst in in- and forward-policies) and the remote subnet (dst in out-policies, src in in- and forward-policies). 🧠

If the subnet has more or equal net bits than the threshold, the first threshold bits are used to calculate a hash to lookup the policy.

Policy hashing thresholds are not supported before Linux 3.18 and might conflict with socket policies before Linux 4.8. So if you're on ancient kernel vibes, beware. 💀
  • charon.plugins.kernel-netlink.spdh_thresh.ipv4.lbits [32] — local subnet XFRM policy hashing threshold for IPv4.
  • charon.plugins.kernel-netlink.spdh_thresh.ipv4.rbits [32] — remote subnet XFRM policy hashing threshold for IPv4.
  • charon.plugins.kernel-netlink.spdh_thresh.ipv6.lbits [128] — local subnet XFRM policy hashing threshold for IPv6.
  • charon.plugins.kernel-netlink.spdh_thresh.ipv6.rbits [128] — remote subnet XFRM policy hashing threshold for IPv6.
  • charon.plugins.kernel-netlink.timeout [0] — Netlink message retransmission timeout, 0 to disable retransmissions. ⏳
  • charon.plugins.kernel-netlink.xfrm_acq_expires [165] — lifetime of XFRM acquire state created by the kernel when traffic matches a trap policy. The value gets written to /proc/sys/net/core/xfrm_acq_expires. 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 total retransmission timeout for IKE messages, see IKEv2 RETRANSMISSION in strongswan.conf(5). 👑
  • charon.plugins.kernel-pfkey.events_buffer_size [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. 📬
  • charon.plugins.kernel-pfkey.route_via_internal [no] — whether to use the internal or external interface in installed routes. The internal interface is the one where the IP address contained in the local traffic selector is located, the external interface is the one over which the destination address of the IPsec tunnel can be reached. This is not relevant if virtual IPs are used, for which a TUN device is created that's used in the routes. 🛣️
  • charon.plugins.kernel-pfroute.mtu [1400] — MTU to set on TUN devices created for virtual IPs.
  • charon.plugins.kernel-pfroute.vip_wait [1000] — time in ms to wait until virtual IP addresses appear/disappear before failing. 🕰️
  • charon.plugins.load-tester — section to configure the load-tester plugin, see LOAD TESTS in strongswan.conf(5) for details. 📚
  • charon.plugins.load-tester.addrs — section that contains key/value pairs with address pools (in CIDR notation) to use for a specific network interface e.g. eth0 = 10.10.0.0/16. 🗺️
  • charon.plugins.load-tester.addrs_keep [no] — whether to keep dynamic addresses even after the associated SA got terminated. 👛
  • charon.plugins.load-tester.addrs_prefix [16] — the network prefix length used when it installs those dynamic addresses. Set it to -1 and you get the whole address instead (so, 32 or 128). no cap 💅
  • charon.plugins.load-tester.ca_dir [] — the directory it loads (intermediate) CA certs from. 🎀
  • charon.plugins.load-tester.child_rekey [600] — how many seconds after setup before CHILD_SA rekeying kicks off. ⏱️
  • charon.plugins.load-tester.crl [] — a URI to a CRL that gets included as the certificate distribution point in the generated certs. 🔥
  • charon.plugins.load-tester.delay [0] — the delay between initiations, per thread. 🦋
  • charon.plugins.load-tester.delete_after_established [no] — delete the IKE_SA the literal second it's been established. bye bestie 👋
  • charon.plugins.load-tester.digest [sha1] — which digest algorithm gets used when issuing certificates. ✨
  • charon.plugins.load-tester.dpd_delay [0] — the DPD delay to use in the load test.
  • charon.plugins.load-tester.dynamic_port [0] — the base port used for requests, and yes each client gets its own different port. 🌈
  • charon.plugins.load-tester.eap_password [default-pwd] — the EAP secret used in the load test. 🔑
  • charon.plugins.load-tester.enable [no] — turns the load testing plugin on.
⚠️ WARNING, and I mean it: never enable this plugin on productive systems. It ships with preconfigured credentials and lets an attacker authenticate as literally any user. That's not a vibe, that's a breach. 💀
  • charon.plugins.load-tester.esp [aes128-sha1] — the CHILD_SA proposal used for load tests.
  • charon.plugins.load-tester.fake_kernel [no] — fakes the kernel interface so you can load-test against yourself. main character energy fr 🪞
  • charon.plugins.load-tester.ike_rekey [0] — seconds after setup before IKE_SA rekeying starts.
  • charon.plugins.load-tester.init_limit [0] — a global cap on how many SAs can be concurrently established during the load test. 🛑
  • charon.plugins.load-tester.initiator [0.0.0.0] — the address to initiate from.
  • charon.plugins.load-tester.initiator_auth [pubkey] — the authentication method(s) the initiator uses.
  • charon.plugins.load-tester.initiator_id [] — the initiator ID used in the load test. 🪪
  • charon.plugins.load-tester.initiator_match [] — the initiator ID to match against when acting as responder.
  • charon.plugins.load-tester.initiator_tsi [] — the traffic selector on the initiator side, as proposed by the initiator. 🦋
  • charon.plugins.load-tester.initiator_tsr [] — the traffic selector on the responder side, as proposed by the initiator.
  • charon.plugins.load-tester.initiators [0] — how many concurrent initiator threads the load test runs. 🧵
  • charon.plugins.load-tester.issuer_cert [] — path to the issuer certificate. If you don't configure it, a hard-coded default value gets used. 🎀
  • charon.plugins.load-tester.issuer_key [] — path to the private key used to issue certificates. Same deal: if it's not configured, a hard-coded default value is used.
  • charon.plugins.load-tester.iterations [1] — how many IKE_SAs each initiator initiates during the load test. 🔁
  • charon.plugins.load-tester.mode [tunnel] — the IPsec mode to use: one of tunnel, transport, or beet. ✨
  • charon.plugins.load-tester.pool [] — hand out INTERNAL_IPV4_ADDRs from a named pool. 🏊
  • charon.plugins.load-tester.preshared_key [<default-psk>] — the preshared key used in the load test. 🔐
  • charon.plugins.load-tester.proposal [aes128-sha1-modp768] — the IKE proposal used in the load test.
  • charon.plugins.load-tester.request_virtual_ip [no] — request an INTERNAL_IPV4_ADDR and an INTERNAL_IPV6_ADDR from the server. 🌐
  • charon.plugins.load-tester.responder [127.0.0.1] — the address it initiates connections to. lo vibes 💜
  • charon.plugins.load-tester.responder_auth [pubkey] — the authentication method(s) the responder uses.
  • charon.plugins.load-tester.responder_id [] — the responder ID used in the load test.
  • charon.plugins.load-tester.responder_tsi [initiator_tsi] — the traffic selector on the initiator side, as narrowed by the responder.
  • charon.plugins.load-tester.responder_tsr [initiator_tsr] — the traffic selector on the responder side, as narrowed by the responder.
  • charon.plugins.load-tester.shutdown_when_complete [no] — shut the daemon down once all IKE_SAs have been established. 👋
  • charon.plugins.load-tester.socket [unix://${piddir}/charon.ldt] — the socket the load-tester plugin provides. 🔌
  • charon.plugins.load-tester.version [0] — the IKE version to use. 0 means "use IKEv2 as initiator and accept any version as responder". 🦋
  • charon.plugins.lookip.socket [unix://${piddir}/charon.lkp] — the socket the lookip plugin provides.
  • charon.plugins.openssl.engine_id [pkcs11] — the ENGINE ID used in the OpenSSL plugin. 🔧
  • charon.plugins.openssl.fips_mode [0] — set the OpenSSL FIPS mode. With OpenSSL before 3.0, the supported values are disabled(0), enabled(1) and Suite B enabled(2). With OpenSSL 3+, any value other than 0 will explicitly load the fips and base providers (load_legacy will be ignored). The latter still requires the config in fipsmodule.cnf (e.g. for the module's MAC), but allows explicitly loading the provider if it's not activated in that config. 😮‍💨 that's a mouthful but it's all true
  • charon.plugins.openssl.load_legacy [yes] — load the legacy provider in OpenSSL 3+ for algorithms like MD4, DES, or Blowfish (heads up: the first two are required for EAP-MSCHAPv2). Disable it and the default provider gets loaded instead, or whichever ones are configured in the OpenSSL config (e.g. the fips provider). 🧚
  • charon.plugins.openxpki.database [] — the database URI connecting to the OpenXPKI certificate database. If it contains a password, make sure you adjust the permissions of the config file accordingly. 🔒
  • charon.plugins.osx-attr.append [yes] — whether DNS servers get appended to existing entries instead of straight-up replacing them. 🍓
  • charon.plugins.p-cscf.enable — the section that enables requesting P-CSCF server addresses for individual connections.
  • charon.plugins.p-cscf.enable.<conn> [no] — here <conn> is the name of a connection with an ePDG to request P-CSCF server addresses from. Requests get sent for addresses of the same families for which internal IPs are requested. 💅
  • charon.plugins.pkcs11.modules — the list of available PKCS#11 modules. 🗂️
  • charon.plugins.pkcs11.modules.<name>.load_certs [yes] — whether to automatically load certificates from tokens.
  • charon.plugins.pkcs11.modules.<name>.os_locking [no] — whether OS locking should be enabled for this module. 🔐
  • charon.plugins.pkcs11.modules.<name>.path [] — the full path to the shared object file of this PKCS#11 module. 📍
  • charon.plugins.pkcs11.reload_certs [no] — reload certificates from all tokens whenever charon receives a SIGHUP. 🔄
  • charon.plugins.pkcs11.use_dh [no] — whether the PKCS#11 modules should be used for DH and ECDH (peep the use_ecc option).
  • charon.plugins.pkcs11.use_ecc [no] — whether the PKCS#11 modules should be used for ECDH and ECDSA public key operations. Important detail: ECDSA private keys can be used regardless of this option. 🗝️
  • charon.plugins.pkcs11.use_hasher [no] — whether the PKCS#11 modules should be used to hash data.
  • charon.plugins.pkcs11.use_pubkey [no] — whether the PKCS#11 modules should be used for public key operations, even for keys that are not stored on tokens. 👑
  • charon.plugins.pkcs11.use_rng [no] — whether the PKCS#11 modules should be used as RNG. 🎲
  • charon.plugins.pkcs11.use_rsa_pss_hashers [no] — whether the PKCS#11 modules should try to use internal hashing for RSA-PSS signatures. (iykyk: some PKCS#11 libraries don't implement internal hashing in conjunction with RSA-PSS correctly.) 😭
  • charon.plugins.radattr.dir [] — the directory where RADIUS attributes are stored in client-ID specific files. 📁
  • charon.plugins.radattr.message_id [-1] — attributes get added to all IKE_AUTH messages by default (-1), or only to the IKE_AUTH message with the given IKEv2 message ID.
  • charon.plugins.random.random [${random_device}] — the file to read random bytes from. 🎀
  • charon.plugins.random.strong_equals_true [no] — set this to yes and the RNG_STRONG class reads random bytes from the same source as the RNG_TRUE class.
  • charon.plugins.random.urandom [${urandom_device}] — the file to read pseudo random bytes from. 🌀
  • charon.plugins.resolve.file [/etc/resolv.conf] — the file where DNS server entries get added, if you're not using resolvconf(8). 📝
  • charon.plugins.resolve.resolvconf.iface [lo.ipsec] — the interface name and protocol sent to resolvconf(8). It 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) if that's relevant on the system. ⚡
  • charon.plugins.resolve.resolvconf.path [/sbin/resolvconf] — path/command for resolvconf(8). The command gets executed by a shell, so "resolvconf" will work if it's in the daemon's $PATH.

If it's not configured, resolvconf(8) will be used if found at the default location. Otherwise, the file in charon.plugins.resolve.file gets modified directly. 🛠️

  • charon.plugins.revocation.enable_crl [yes] — whether CRL validation should be enabled. ✅
  • charon.plugins.revocation.enable_ocsp [yes] — whether OCSP validation should be enabled. ✅
  • charon.plugins.revocation.timeout [10s] — the timeout used when fetching OCSP/CRL. ⏳
  • charon.plugins.save-keys.esp [no] — whether to save ESP keys. 🔑
  • charon.plugins.save-keys.ike [no] — whether to save IKE keys. 🔑
  • charon.plugins.save-keys.load [no] — whether to load the plugin.
  • charon.plugins.save-keys.wireshark_keys [] — the directory where keys are stored in the format supported by Wireshark. IKEv1 keys land in the ikev1_decryption_table file. IKEv2 keys land in the ikev2_decryption_table file. Keys for ESP CHILD_SAs land in the esp_sa file. 🦈✨
  • charon.plugins.socket-default.fwmark [] — the firewall mark to set on outbound packets. 🔥
  • charon.plugins.socket-default.set_source [yes] — set the source address on outbound packets, if possible.
  • charon.plugins.socket-default.set_sourceif [no] — force the sending interface on outbound packets, if possible. This is what lets you use IPv6 link-local addresses as tunnel endpoints. 🔗
  • charon.plugins.socket-default.use_ipv4 [yes] — listen on IPv4, if possible. 🌈
  • charon.plugins.socket-default.use_ipv6 [yes] — listen on IPv6, if possible. 🌈
  • charon.plugins.sql.database [empty] — the Database URI for charon's SQL plugin. 🗄️ Heads up bestie: if it's got a password in there, go fix the permissions on the config file right now, because that secret is just sitting out in the open.
  • charon.plugins.sql.loglevel [-1] — the loglevel used for logging into the SQL database.
  • charon.plugins.stroke.allow_swap [yes] — analyzes the addresses/hostnames in left|right to figure out which side is local, and swaps config options around if it needs to. If you disable this, left is always the local one, no arguments.
  • charon.plugins.stroke.ignore_missing_ca_basic_constraint [no] — treats certs in ipsec.d/cacerts and ipsec.conf ca sections as CA certificates even when they don't actually contain a CA basic constraint. 🙈
  • charon.plugins.stroke.max_concurrent [4] — the maximum number of stroke messages that get handled at the same time.
  • charon.plugins.stroke.prevent_loglevel_changes [no] — if you enable this, changing the loglevel through the stroke socket is simply not allowed. locked down 🔒
  • charon.plugins.stroke.secrets_file [${sysconfdir}/ipsec.secrets] — where the ipsec.secrets file lives.
  • charon.plugins.stroke.socket [unix://${piddir}/charon.ctl] — the socket that the stroke plugin hands out.
  • charon.plugins.stroke.timeout [0] — timeout in ms for any stroke command. 0 means no timeout at all, it just vibes forever.
  • charon.plugins.systime-fix.interval [0] — interval in seconds for checking whether the system time is actually valid. 0 disables the check entirely.
  • charon.plugins.systime-fix.reauth [no] — whether to use reauth or delete when an invalid cert lifetime gets detected. pick your fighter ⚔️
  • charon.plugins.systime-fix.threshold [empty] — the threshold date where system time counts as valid. disabled if you don't specify it.
  • charon.plugins.systime-fix.threshold_format [%Y] — the strptime(3) format used to parse the threshold option.
  • charon.plugins.systime-fix.timeout [0s] — how long to wait for a valid system time when an interval is configured. 0 means recheck indefinitely, bestie is patient.

  • charon.plugins.tnc-imc.dlclose [yes] — unload the IMC after use.
  • charon.plugins.tnc-imc.preferred_language [en] — the preferred language for TNC recommendations. 🌍
  • charon.plugins.tnc-imv.dlclose [yes] — unload the IMV after use.
  • charon.plugins.tnc-imv.recommendation_policy [default] — the TNC recommendation policy. valid values are default, any, or all.
  • charon.plugins.tnc-pdp.pt_tls.enable [yes] — enables the PT-TLS protocol on the strongSwan PDP.
  • charon.plugins.tnc-pdp.pt_tls.port [271] — the PT-TLS server port the strongSwan PDP is listening on.
  • charon.plugins.tnc-pdp.radius.enable [yes] — enables the RADIUS protocol on the strongSwan PDP.
  • charon.plugins.tnc-pdp.radius.method [ttls] — the EAP tunnel method to be used.
  • charon.plugins.tnc-pdp.radius.port [1812] — the RADIUS server port the strongSwan PDP is listening on.
  • charon.plugins.tnc-pdp.radius.secret [empty] — the shared RADIUS secret between the strongSwan PDP and the NAS. 🔐 If you set this, please go adjust the permissions of the config file accordingly, iykyk.
  • charon.plugins.tnc-pdp.server [empty] — the name of the strongSwan PDP as it's contained in the AAA certificate.
  • charon.plugins.tnc-pdp.timeout [empty] — timeout in seconds before incomplete connections get closed.
  • charon.plugins.tnccs-20.max_batch_size [65522] — the max size of a PB-TNC batch (the upper limit via PT-EAP is 65529).
  • charon.plugins.tnccs-20.max_message_size [65490] — the max size of a PA-TNC message (the upper limit via PT-EAP is 65497).
  • charon.plugins.tnccs-20.mutual [no] — enable the PB-TNC mutual protocol.
  • charon.plugins.tnccs-20.tests.pb_tnc_noskip [no] — sends an unsupported PB-TNC message type with the NOSKIP flag set. yes, this is a test knob, it's giving chaos 🧪
  • charon.plugins.tnccs-20.tests.pb_tnc_version [2] — sends a PB-TNC batch with a modified PB-TNC version.

  • charon.plugins.tpm.ek_handle [empty] — the handle of the RSA or ECC Endorsement Key (EK) used to set up an authenticated session with a TPM 2.0 (e.g. 0x81010001).
  • charon.plugins.tpm.fips_186_4 [no] — is the TPM 2.0 FIPS-186-4 compliant? enabling this forces stuff like using the default salt length instead of the maximum salt length with RSAPSS padding.
  • charon.plugins.tpm.tcti.name [device|tabrmd] — the name of the TPM 2.0 TCTI library. valid values: tabrmd, device or mssim. The defaults are device if the /dev/tpmrm0 in-kernel TPM 2.0 resource manager device exists, and tabrmd otherwise — which requires the d-bus based TPM 2.0 access broker and resource manager to be available.
  • charon.plugins.tpm.tcti.opts [/dev/tpmrm0|<none>] — options for the TPM 2.0 TCTI library. The defaults are /dev/tpmrm0 if the TCTI library name is device, and no options otherwise.
  • charon.plugins.tpm.use_rng [no] — whether the TPM should be used as RNG. For security reasons, only enable this if an authenticated session can be set up (see the ek_handle option). 🔥

  • charon.plugins.unbound.dlv_anchors [empty] — the file to read trusted keys for DLV (DNSSEC Lookaside Validation) from. It uses the same format as trust_anchors. Only one DLV can be configured, and it's then used as a root trusted DLV — meaning it's a lookaside for the root.
  • charon.plugins.unbound.resolv_conf [/etc/resolv.conf] — the file to read the DNS resolver configuration from.
  • charon.plugins.unbound.trust_anchors [${sysconfdir}/ipsec.d/dnssec.keys] — the file to read DNSSEC trust anchors from (usually the root zone KSK). The format of the file is the standard DNS Zone file format, and anchors can be stored as DS or DNSKEY entries in it.
  • charon.plugins.updown.dns_handler [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 — so pick wisely 💅)
  • charon.plugins.vici.socket [unix://${piddir}/charon.vici] — the socket the vici plugin serves clients on.
  • charon.plugins.whitelist.enable [yes] — enable the loaded whitelist plugin.
  • charon.plugins.whitelist.socket [unix://${piddir}/charon.wlst] — the socket provided by the whitelist plugin.
  • charon.plugins.wolfssl.fips_mode [no] — enable this to prevent loading the plugin if wolfSSL isn't in FIPS mode.
  • charon.plugins.xauth-eap.backend [radius] — the EAP plugin used as the backend for XAuth credential verification.
  • charon.plugins.xauth-pam.pam_service [login] — the PAM service used for authentication.
  • charon.plugins.xauth-pam.session [no] — open/close a PAM session for each active IKE_SA.
  • charon.plugins.xauth-pam.trim_email [yes] — if an email address comes in as the XAuth username, trim it down to just the username part. ✂️

  • charon.port [500] — the UDP port used locally. If you set it to 0, a random port gets allocated.
  • charon.port_nat_t [4500] — the UDP port used locally in the case of NAT-T. If you set it to 0, a random port gets allocated. It has to be different from charon.port — otherwise a random port will be allocated.
  • charon.prefer_best_path [no] — by default, charon keeps SAs on the routing path with addresses it previously used, as long as that path is still usable. Set this to yes and it tries way more aggressively to update SAs with MOBIKE on routing priority changes, using the cheapest path. Yeah it adds more noise, but it lets you dynamically adapt SAs to routing priority changes. This option has no effect if MOBIKE isn't supported or is disabled.
  • charon.prefer_configured_proposals [yes] — prefer locally configured proposals for IKE/IPsec over the supplied ones when acting as responder. (disabling this can avoid keying retries caused by INVALID_KE_PAYLOAD notifies.)
  • charon.prefer_temporary_addrs [no] — by default, permanent IPv6 source addresses are preferred over temporary ones (RFC 4941), so connections stay more stable. Enable this option to flip that around.

It also affects which IPv6 addresses get announced as additional addresses if MOBIKE is used. If the option is disabled, only permanent addresses are sent — and only temporary ones if it's enabled.

  • charon.process_route [yes] — process RTM_NEWROUTE and RTM_DELROUTE events.
  • charon.processor.priority_threads — a whole section for configuring the number of reserved threads per priority class. see JOB PRIORITY MANAGEMENT in strongswan.conf(5). 📚
  • charon.rdn_matching [strict] — how the RDNs in subject DNs of certificates get matched against configured identities. Possible values are strict (the default), reordered, and relaxed.

With strict, the number, type and order of all RDNs has to match — wildcards (*) for the values of RDNs are allowed (and that's true for all three variants, fyi).

Using reordered also matches DNs if the RDNs appear in a different order, but the number and type still have to match.

Finally, relaxed also allows matches of DNs that contain more RDNs than the configured identity (missing RDNs are treated like a wildcard match).

⚠️ Note that reordered and relaxed impose a considerable overhead on memory usage and runtime — in particular for mismatches — compared to strict. so like, don't just turn it on for funsies.
  • charon.receive_delay [0] — delay in ms for receiving packets, so you can simulate a larger RTT.
  • charon.receive_delay_request [yes] — delay request messages.
  • charon.receive_delay_response [yes] — delay response messages.
  • charon.receive_delay_type [0] — a specific IKEv2 message type to delay, or 0 for any.
  • charon.reject_trusted_end_entity [no] — reject peers that use trusted end-entity certificates (i.e. local certificates). 🚫
  • charon.replay_window [32] — the size of the AH/ESP replay window, in packets.
  • charon.reqid_base [1] — the value of the first reqid automatically assigned to a CHILD_SA.
  • charon.retransmit_base [1.8] — the base used for calculating exponential back off, see IKEv2 RETRANSMISSION in strongswan.conf(5).
  • charon.retransmit_jitter [0] — maximum jitter in percent, applied randomly to the calculated retransmission timeout (0 to disable).
  • charon.retransmit_limit [0] — upper limit in seconds for the calculated retransmission timeout (0 to disable).
  • charon.retransmit_timeout [4.0] — timeout in seconds before sending the first retransmit.
  • charon.retransmit_tries [5] — how many times to retransmit a packet before giving up. 😤
  • charon.retry_initiate_interval [0s] — the interval in seconds to use when retrying to initiate an IKE_SA (e.g. if DNS resolution failed), 0 to disable retries.
  • charon.reuse_ikesa [yes] — initiate the CHILD_SA inside existing IKE_SAs (this is always enabled for IKEv1, bestie 💅).
  • charon.routing_table [] — the numerical routing table where routes get installed.
  • charon.routing_table_prio [] — priority of that routing table.
  • charon.rsa_pss [no] — whether to use RSA with PSS padding instead of PKCS#1 padding by default.
  • charon.rsa_pss_trailerfield [no] — whether to encode an explicit trailerField value of 0x01 in the RSA-PSS algorithmIdentifier (CONTEXT3), or use the DEFAULT value by omitting it.
  • charon.send_delay [0] — delay in ms for sending packets, to simulate a larger RTT.
  • charon.send_delay_request [yes] — delay request messages.
  • charon.send_delay_response [yes] — delay response messages.
  • charon.send_delay_type [0] — specific IKEv2 message type to delay; 0 means any.
  • charon.send_vendor_id [no] — send the strongSwan vendor ID payload.
  • charon.signature_authentication [yes] — whether to enable Signature Authentication as per RFC 7427.
  • charon.signature_authentication_constraints [yes] — if enabled, signature schemes configured in rightauth are used as constraints against signature schemes in the certificate chain, and also as constraints against the signature scheme peers use during IKEv2. Iconic little double-duty situation ✨.
  • charon.spi_label [0x0000000000000000] — value mixed into the local IKE SPIs after applying spi_mask.
  • charon.spi_mask [0x0000000000000000] — mask applied to local IKE SPIs before mixing in spi_label (bits set will be replaced with spi_label).
  • charon.spi_max [0xcfffffff] — the upper limit for SPIs requested from the kernel for IPsec SAs.
  • charon.spi_min [0xc0000000] — the lower limit for SPIs requested from the kernel for IPsec SAs. Should not be set lower than 0x00000100 (256), because SPIs between 1 and 255 are reserved by IANA. Don't do it, bestie 😭.
  • charon.start-scripts — section containing a list of scripts (name = path) that are executed when the daemon is started.
  • charon.stop-scripts — section containing a list of scripts (name = path) that are executed when the daemon is terminated.
  • charon.syslog — section to define syslog loggers, see LOGGER CONFIGURATION in strongswan.conf(5).
  • charon.syslog.<facility> — <facility> is one of the supported syslog facilities, see LOGGER CONFIGURATION in strongswan.conf(5).
  • charon.syslog.<facility>.<subsystem> [<default>] — loglevel for a specific subsystem.
  • charon.syslog.<facility>.default [1] — specifies the default loglevel to be used for subsystems for which no specific loglevel is defined.
  • charon.syslog.<facility>.ike_name [no] — prefix each log entry with the connection name and a unique numerical identifier for each IKE_SA.
  • charon.syslog.<facility>.log_level [no] — add the log level of each message after the subsystem (e.g. [IKE2]).
  • charon.syslog.<facility>.map_level [-1] — the default setting of -1 passes all messages to syslog using a log level of LOG_INFO. A non-negative value maps the strongSwan specific loglevels (0..4) to the syslog level starting at the specified number. For example, a value of 5 (LOG_NOTICE) maps strongSwan loglevel 0 to LOG_NOTICE, level 1 to LOG_INFO, and levels 2, 3 and 4 to LOG_DEBUG. This allows (additional) filtering of log messages on the syslog server. Cute little mapping moment 🎀.
  • charon.syslog.identifier [] — 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).
  • charon.threads [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 an indicator of the number of reserved threads.
  • charon.tls.cipher [] — list of TLS encryption ciphers.
  • charon.tls.ke_group [] — list of TLS key exchange groups.
  • charon.tls.key_exchange [] — list of TLS key exchange methods.
  • charon.tls.mac [] — list of TLS MAC algorithms.
  • charon.tls.send_certreq_authorities [yes] — whether to include CAs in a server's CertificateRequest message. May be disabled if clients can't handle a long list of CAs.
  • charon.tls.signature [] — list of TLS signature schemes.
  • charon.tls.suites [] — list of TLS cipher suites.
  • charon.tls.version_max [1.2] — maximum TLS version to negotiate.
  • charon.tls.version_min [1.2] — minimum TLS version to negotiate.
  • charon.tnc.tnc_config [/etc/tnc_config] — TNC IMC/IMV configuration file.
  • charon.user [] — name of the user the daemon changes to after startup.
  • charon.x509.enforce_critical [yes] — discard certificates with unsupported or unknown critical extensions.
  • charon-cmd — section with settings specific to the CLI client charon-cmd. Settings from the charon section are not inherited, but many can be used here as well.
  • charon-cmd.load_modular [yes] — load only explicitly enabled plugins.
  • charon-nm — section with settings specific to the NetworkManager backend charon-nm. Settings from the charon section are not inherited, but many can be used here as well. Defaults for some settings are chosen very deliberately and should only be changed in case of conflicts. Respect the intentional choices, bestie 🫶.
  • charon-nm.ca_dir [<default>] — directory from which to load CA certificates if no certificate is configured.
  • charon-nm.install_virtual_ip_on [lo] — interface on which virtual IP addresses are installed. Note that NM also installs the virtual IPs on the XFRM interface.
  • charon-nm.load_modular [yes] — load only explicitly enabled plugins.
  • charon-nm.mtu [1400] — MTU for XFRM interfaces created by the NM plugin.
  • charon-nm.plugins.kernel-netlink.fwmark [!210] — make packets with this mark ignore the routing table. Must be the same mark set in charon-nm.plugins.socket-default.fwmark.
  • charon-nm.plugins.socket-default.fwmark [210] — mark applied to IKE and ESP packets to ignore the routing table and avoid routing loops when using XFRM interfaces.
  • charon-nm.port [0] — source port when sending packets to port 500. Defaults to an ephemeral port. May be set to 500 if firewall rules require a static port.
  • charon-nm.port_nat_t [0] — source port when sending packets to port 4500 or a custom server port. Defaults to an ephemeral port. May be set to e.g. 4500 if firewall rules require a static port.
  • charon-nm.retransmit_base [1.4] — base to use for calculating exponential back off, see IKEv2 RETRANSMISSION in strongswan.conf(5). Default retransmission settings for charon-nm are deliberately lower to fail and possibly reestablish SAs more quickly.
  • charon-nm.retransmit_timeout [2.0] — timeout in seconds before sending the first retransmit.
  • charon-nm.retransmit_tries [3] — number of times to retransmit a packet before giving up.
  • charon-nm.routing_table [210] — table where routes via the XFRM interface are installed. Should be different from the table used for the regular IKE daemon due to the mark.
  • charon-nm.routing_table_prio [210] — priority of the routing table. Higher than the default priority used for the regular IKE daemon.
  • charon-nm.syslog.daemon.default [1] — default to logging via syslog's daemon facility on level 1.
  • charon-systemd.journal — section to configure the native systemd journal logger, very similar to the syslog logger as described in LOGGER CONFIGURATION in strongswan.conf(5).
  • charon-systemd.journal.<subsystem> [<default>] — loglevel for a specific subsystem.
  • charon-systemd.journal.default [1] — specifies the default loglevel to be used for subsystems for which no specific loglevel is defined.
  • imv_policy_manager.command_allow [] — shell command to be executed with recommendation allow.
  • imv_policy_manager.command_block [] — shell command to be executed with all other recommendations.
  • imv_policy_manager.database [] — 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. Security girlie reminder 🔥.
  • imv_policy_manager.load [sqlite] — plugins to load in IMV policy manager.
  • libimcv.debug_level [1] — debug level for a stand-alone libimcv library.
  • libimcv.load [random nonce gmp pubkey x509] — plugins to load in IMC/IMVs with the stand-alone libimcv library.
  • libimcv.plugins.imc-attestation.aik_blob [] — AIK encrypted private key blob file.
  • libimcv.plugins.imc-attestation.aik_cert [] — AIK certificate file.
  • libimcv.plugins.imc-attestation.aik_handle [] — AIK object handle.
  • libimcv.plugins.imc-attestation.aik_pubkey [] — AIK public key file.
  • libimcv.plugins.imc-attestation.hash_algorithm [sha384] — preferred measurement hash algorithm.
  • libimcv.plugins.imc-attestation.mandatory_dh_groups [yes] — enforce mandatory Diffie-Hellman groups.
  • libimcv.plugins.imc-attestation.nonce_len [20] — DH nonce length.
  • libimcv.plugins.imc-attestation.pcr17_after [] — PCR17 value after measurement.
  • libimcv.plugins.imc-attestation.pcr17_before [] — PCR17 value before measurement.
  • libimcv.plugins.imc-attestation.pcr17_meas [] — dummy measurement value extended into PCR17 if the TBOOT log is not available.
  • libimcv.plugins.imc-attestation.pcr18_after [] — PCR18 value after measurement.
  • libimcv.plugins.imc-attestation.pcr18_before [] — PCR18 value before measurement.
  • libimcv.plugins.imc-attestation.pcr18_meas [] — dummy measurement value extended into PCR17 if the TBOOT log is not available. Yes, it says PCR17 here too — copy-paste lore preserved 💀.
  • libimcv.plugins.imc-attestation.pcr_info [no] — whether to send pcr_before and pcr_after info.
  • libimcv.plugins.imc-attestation.pcr_padding [no] — whether to pad IMA SHA1 measurements values when extending into the SHA256 PCR bank.
  • libimcv.plugins.imc-attestation.use_quote2 [yes] — bestie, use the Quote2 AIK signature instead of the plain Quote signature. glow-up fr 💅
  • libimcv.plugins.imc-attestation.use_version_info [no] — when this is on, Version Info gets included in the Quote2 signature ✨
  • libimcv.plugins.imc-hcd.push_info [yes] — send that quadruple info without anyone having to prompt you. main character energy, no waiting to be asked 👑
  • libimcv.plugins.imc-hcd.subtypes [] — the section where you define PWG HCD PA subtypes 🎀
  • libimcv.plugins.imc-hcd.subtypes.<section> [] — defines a PWG HCD PA subtype section. the recognized subtype section names are literally system, control, marker, finisher, interface and scanner 🖨️
  • libimcv.plugins.imc-hcd.subtypes.<section>.<sw_type> [] — defines a software type section. recognized software type section names are firmware, resident_application and user_application 💾
  • libimcv.plugins.imc-hcd.subtypes.<section>.<sw_type>.<software> [] — defines a software section, and the name is arbitrary, pick whatever 🦋
  • libimcv.plugins.imc-hcd.subtypes.<section>.<sw_type>.<software>.name [] — name of the software installed on the hardcopy device 🏷️
  • libimcv.plugins.imc-hcd.subtypes.<section>.<sw_type>.<software>.patches [] — a string describing all the patches applied to that given software on this hardcopy device. the individual patches are separated by a newline character '\n' 📝
  • libimcv.plugins.imc-hcd.subtypes.<section>.<sw_type>.<software>.string_version [] — a string describing the version of the given software on this hardcopy device 🍓
  • libimcv.plugins.imc-hcd.subtypes.<section>.<sw_type>.<software>.version [] — hex-encoded version string, 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). very precise, we love that 🔢
  • libimcv.plugins.imc-hcd.subtypes.<section>.attributes_natural_language [en] — a variable length natural language tag conforming to RFC 5646, specifying the language to be used in the health assessment message of a given subtype 🌈
  • libimcv.plugins.imc-hcd.subtypes.system.certification_state [] — hex-encoded certification state ✅
  • libimcv.plugins.imc-hcd.subtypes.system.configuration_state [] — hex-encoded configuration state ⚙️
  • libimcv.plugins.imc-hcd.subtypes.system.machine_type_model [] — string specifying the machine type and model of the hardcopy device 🖨️
  • libimcv.plugins.imc-hcd.subtypes.system.pstn_fax_enabled [no] — specifies if a PSTN facsimile interface is installed and enabled on the hardcopy device 📠
  • libimcv.plugins.imc-hcd.subtypes.system.time_source [] — string specifying the hostname of the network time server used by the hardcopy device ⏰
  • libimcv.plugins.imc-hcd.subtypes.system.user_application_enabled [no] — specifies if users can dynamically download and execute applications on the hardcopy device 😳
  • libimcv.plugins.imc-hcd.subtypes.system.user_application_persistence_enabled [no] — specifies if user dynamically downloaded applications can persist outside the boundaries of a single job on the hardcopy device 👀
  • libimcv.plugins.imc-hcd.subtypes.system.vendor_name [] — string specifying the manufacturer of the hardcopy device 🏭
  • libimcv.plugins.imc-hcd.subtypes.system.vendor_smi_code [] — integer specifying the globally unique 24-bit SMI code assigned to the manufacturer of the hardcopy device 🔐
  • libimcv.plugins.imc-os.device_cert [] — manually set the path to the client device certificate (e.g. /etc/pts/aikCert.der) 📜
  • libimcv.plugins.imc-os.device_handle [] — manually set handle to a private key bound to a smartcard or TPM (e.g. 0x81010004) 🔑
  • libimcv.plugins.imc-os.device_id [] — manually set the client device ID in hexadecimal format (e.g. 1083f03988c9762703b1c1080c2e46f72b99cc31) 🆔
  • libimcv.plugins.imc-os.device_pubkey [] — manually set the path to the client device public key (e.g. /etc/pts/aikPub.der) 🗝️
  • libimcv.plugins.imc-os.push_info [yes] — send operating system info without being prompted, so unbothered 💅
  • libimcv.plugins.imc-scanner.push_info [yes] — send open listening ports without being prompted ✨
  • libimcv.plugins.imc-swima.eid_epoch [0x11223344] — set the 32 bit epoch value for event IDs manually, in case the software collector database isn't available 🕰️
  • libimcv.plugins.imc-swima.subscriptions [no] — accept SW Inventory or SW Events subscriptions 📬
  • libimcv.plugins.imc-swima.swid_database [] — URI to the software collector database containing event timestamps, software creation and deletion events and collected software identifiers 💾
ok so if it contains a password, make sure to adjust the permissions of the config file accordingly. do not leave that lying around bestie 😭
  • libimcv.plugins.imc-swima.swid_directory [${prefix}/share] — the directory where SWID tags are located 📁
  • libimcv.plugins.imc-swima.swid_full [no] — include file information in the XML-encoded SWID tags 🧾
  • libimcv.plugins.imc-swima.swid_pretty [no] — generate XML-encoded SWID tags with pretty indentation. aesthetic ✨
  • libimcv.plugins.imc-test.additional_ids [0] — number of additional IMC IDs 🔢
  • libimcv.plugins.imc-test.command [none] — command to be sent to the Test IMV 📨
  • libimcv.plugins.imc-test.dummy_size [0] — size of dummy attribute to be sent to the Test IMV (0 = disabled) 🫧
  • libimcv.plugins.imc-test.retry [no] — do a handshake retry 🔁
  • libimcv.plugins.imc-test.retry_command [] — command to be sent to the Test IMV in the handshake retry 📤
  • libimcv.plugins.imv-attestation.cadir [] — path to directory with AIK cacerts 📂
  • libimcv.plugins.imv-attestation.dh_group [ecp256] — preferred Diffie-Hellman group 🤝
  • libimcv.plugins.imv-attestation.hash_algorithm [sha384] — preferred measurement hash algorithm #️⃣
  • libimcv.plugins.imv-attestation.mandatory_dh_groups [yes] — enforce mandatory Diffie-Hellman groups, no skipping 🚨
  • libimcv.plugins.imv-attestation.min_nonce_len [0] — DH minimum nonce length 📏
  • libimcv.plugins.imv-os.remediation_uri [] — URI pointing to operating system remediation instructions 🛠️
  • libimcv.plugins.imv-scanner.remediation_uri [] — URI pointing to scanner remediation instructions 🧹
  • libimcv.plugins.imv-swima.rest_api.timeout [120] — timeout of the SWID REST API HTTP POST transaction ⏳
  • libimcv.plugins.imv-swima.rest_api.uri [] — HTTP URI of the SWID REST API 🌐
  • libimcv.plugins.imv-test.rounds [0] — number of IMC-IMV retry rounds 🔄
  • libimcv.stderr_quiet [no] — disable output to stderr with a stand-alone libimcv library 🤫
  • libimcv.swid_gen.command [/usr/local/bin/swid_generator] — SWID generator command to be executed 🏃‍♀️
  • libimcv.swid_gen.tag_creator.name [strongSwan Project] — name of the tagCreator entity 🏷️
  • libimcv.swid_gen.tag_creator.regid [strongswan.org] — regid of the tagCreator entity 🆔
  • pki.load [] — plugins to load in the pki tool 🔌
  • pki.scep.http_bind [] — source IP address to bind for HTTP operations 📌
  • pki.scep.http_timeout [30s] — timeout for HTTP operations ⏱️
  • pki.scep.renewal_via_pkcs_req [no] — some SCEP servers (e.g. openxpki) are incorrectly doing certificate renewal via messageType PKCSReq (19) instead of RenewalReq (17) 😬 so this exists for them
  • pool.database [] — database URI for the database that stores IP pools and configuration attributes 🗄️
same deal as before: if it contains a password, make sure to adjust the permissions of the config file accordingly 🔒
  • pool.load [] — plugins to load in ipsec pool tool 🔌
  • sec-updater — options for the sec-updater tool 🧰
  • sec-updater.database [] — global IMV policy database URI 🗃️
and yes, if it contains a password, make sure to adjust the permissions of the config file accordingly, please and thank you 🙏
  • sec-updater.load [] — plugins to load in sec-updater tool 🔌
  • sec-updater.swid_gen.command [/usr/local/bin/swid_generator] — SWID generator command to be executed 🏃‍♀️
  • sec-updater.swid_gen.tag_creator.name [strongSwan Project] — name of the tagCreator entity 🏷️
  • sec-updater.swid_gen.tag_creator.regid [strongswan.org] — regid of the tagCreator entity 🆔
  • sec-updater.tmp.deb_file [/tmp/sec-updater.deb] — temporary storage for downloaded deb package file 📦
  • sec-updater.tmp.tag_file [/tmp/sec-updater.tag] — temporary storage for generated SWID tags 🏷️
  • sec-updater.tnc_manage_command [/var/www/tnc/manage.py] — strongTNC manage.py command used to import SWID tags 🐍
  • starter.config_file [${sysconfdir}/ipsec.conf] — location of the ipsec.conf file 📍
  • starter.load_warning [yes] — disable charon plugin load option warning 🔕
  • sw-collector — options for the sw-collector tool 🧰
  • sw-collector.database [] — 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, iykyk 🔐
  • sw-collector.first_file [/var/log/bootstrap.log] — path pointing to the file created when the Linux OS was installed 🐧
  • sw-collector.first_time [0000-00-00T00:00:00Z] — time in UTC when the Linux OS was installed 🕛
  • sw-collector.history [] — path pointing to apt history.log file 📜
  • sw-collector.load [] — plugins to load in sw-collector tool 🔌
  • sw-collector.rest_api.timeout [120] — timeout of REST API HTTP POST transaction ⏳
  • sw-collector.rest_api.uri [] — HTTP URI of the central collector's REST API 🌐
  • swanctl.load [] — plugins to load in swanctl 🔌
  • swanctl.socket [unix://${piddir}/charon.vici] — VICI socket to connect to by default 🦋

✨ LOGGER CONFIGURATION ✨

Okay so the options in strongswan.conf(5) give you a much more flexible way to set up loggers for the IKE daemon charon than the charondebug option in ipsec.conf(5). Like, way more flexible. It's giving glow-up.

Note bestie: if you specify any loggers in strongswan.conf, then charondebug literally does not have any effect. Zero. Nada. She's not invited to the party anymore. 💀

There are currently two types of loggers:

  • File loggers — these log directly to a file, and you define one by specifying an arbitrarily named subsection in the charon.filelog section. The full path to the file goes in the path setting of that subsection — however, if the name only contains characters permitted in section names, you can just omit the setting entirely and use the subsection name as the path. And to log to the console, there are two special filenames you can use: stdout and stderr. 🎀
  • Syslog loggers — these log into a syslog facility, and you define one by specifying the facility to log to as the name of a subsection in the charon.syslog section. The following facilities are currently supported: daemon and auth. 💅

You can define multiple loggers of each type, each with different log verbosity for the different subsystems of the daemon. Obsessed with the granularity honestly.


Subsystems 🦋

  • dmn — main daemon setup/cleanup/signal handling
  • mgr — IKE_SA manager, handling synchronization for IKE_SA access
  • ike — IKE_SA
  • chd — CHILD_SA
  • job — jobs queueing/processing and thread pool management
  • cfg — configuration management and plugins
  • knl — IPsec/Networking kernel interface
  • net — IKE network communication
  • asn — low-level encoding/decoding (ASN.1, X.509 etc.)
  • enc — packet encoding/decoding encryption/decryption operations
  • tls — libtls library messages
  • esp — libipsec library messages
  • lib — libstrongswan library messages
  • tnc — Trusted Network Connect
  • imc — Integrity Measurement Collector
  • imv — Integrity Measurement Verifier
  • pts — Platform Trust Service

Loglevels 🌈

  • -1 — absolutely silent. not a peep. 🤫
  • 0 — very basic auditing logs, (e.g. SA up/SA down)
  • 1 — generic control flow with errors, a good default to see what's going on
  • 2 — more detailed debugging control flow
  • 3 — including RAW data dumps in Hex
  • 4 — also include sensitive material in dumps, e.g. keys. ⚠️ bestie, sensitive means sensitive, keys in your logs is a whole vibe you probably don't want.

Example 🍓

charon {
     filelog {
          charon {
               path = /var/log/charon.log
               time_format = %b %e %T
               append = no
               default = 1
          }
          stderr {
               ike = 2
               knl = 3
               ike_name = yes
          }
     }
     syslog {
          # enable logging to LOG_DAEMON, use defaults
          daemon {
          }
          # minimalistic IKE auditing logging to LOG_AUTHPRIV
          auth {
               default = -1
               ike = 0
          }
     }
}

🔥 JOB PRIORITY MANAGEMENT 🔥

So some operations in the IKEv2 daemon charon are currently implemented synchronously and blocking. Two examples of such operations are communication with a RADIUS server via EAP-RADIUS, or fetching CRL/OCSP information during certificate chain verification. Under high load conditions, the thread pool may run out of available threads, and some more important jobs — like liveness checking — may not get executed in time. And that's a whole mess. 😭

To prevent thread starvation in such situations, job priorities were introduced. The job processor will reserve some threads for higher priority jobs, and these threads are not available for lower priority, locking jobs. Reserved seats, basically. 👑


Implementation 🧚

Currently 4 priorities have been defined, and they're used in charon as follows:

  • CRITICAL — priority for long-running dispatcher jobs.
  • HIGH — INFORMATIONAL exchanges, as used by liveness checking (DPD).
  • MEDIUM — everything not HIGH/LOW, including IKE_SA_INIT processing.
  • LOW — IKE_AUTH message processing. RADIUS and CRL fetching block here. 😬

Even though IKE_SA_INIT processing is computationally expensive, it is explicitly assigned to the MEDIUM class. This allows charon to do the DH exchange while other threads are blocked in IKE_AUTH. To prevent the daemon from accepting more IKE_SA_INIT requests than it can handle, use IKE_SA_INIT DROPPING (coming up next, bestie, don't worry).

The thread pool processes jobs strictly by priority, meaning it will consume all higher priority jobs before looking for ones with lower priority. Further, it reserves threads for certain priorities. A priority class having reserved n threads will always have n threads available for this class (either currently processing a job, or waiting for one). No cap, it's reserved. ✨


Configuration 🌸

To ensure that there are always enough threads available for higher priority tasks, threads must be reserved for each priority class.

  • charon.processor.priority_threads.critical [0] — threads reserved for CRITICAL priority class jobs
  • charon.processor.priority_threads.high [0] — threads reserved for HIGH priority class jobs
  • charon.processor.priority_threads.medium [0] — threads reserved for MEDIUM priority class jobs
  • charon.processor.priority_threads.low [0] — threads reserved for LOW priority class jobs

Let's consider the following configuration:

charon {
     processor {
          priority_threads {
               high = 1
               medium = 4
          }
     }
}

With this configuration, one thread is reserved for HIGH priority tasks. Since currently only liveness checking and stroke message processing is done with high priority, one or two threads should be sufficient.

The MEDIUM class mostly processes non-blocking jobs. Unless your setup is experiencing many blocks in locks while accessing shared resources, threads for one or two times the number of CPU cores is fine. 💖

It is usually not required to reserve threads for CRITICAL jobs. Jobs in this class rarely return and do not release their thread to the pool. (Iconic of them, honestly.)

The remaining threads are available for LOW priority jobs. Reserving threads does not make sense (until we have an even lower priority).


Monitoring 👀

To see what the threads are actually doing, invoke ipsec statusall. Under high load, something like this will show up:

worker threads: 2 or 32 idle, 5/1/2/22 working,
     job queue: 0/0/1/149, scheduled: 198

From 32 worker threads:

  • 2 — are currently idle.
  • 5 — are running CRITICAL priority jobs (dispatching from sockets, etc.).
  • 1 — is currently handling a HIGH priority job. This is actually the thread currently providing this information via stroke. 🫶
  • 2 — are handling MEDIUM priority jobs, likely IKE_SA_INIT or CREATE_CHILD_SA messages.
  • 22 — are handling LOW priority jobs, probably waiting for an EAP-RADIUS response while processing IKE_AUTH messages. 😭

The job queue load shows how many jobs are queued for each priority, ready for execution. The single MEDIUM priority job will get executed immediately, as we have two spare threads reserved for MEDIUM class jobs.


🌈 IKE_SA_INIT DROPPING 🌈

If a responder receives more connection requests per second than it can handle, it does not make sense to accept more IKE_SA_INIT messages. And if they're queued but can't get processed in time, an answer might be sent after the client has already given up and restarted its connection setup — which additionally increases the load on the responder. Literally a spiral. 🌀

To limit the responder load resulting from new connection attempts, the daemon can drop IKE_SA_INIT messages just after reception. There are two mechanisms to decide if this should happen, configured with the following options:

  • charon.init_limit_half_open [0] — limit based on the number of half open IKE_SAs. Half open IKE_SAs are SAs in connecting state, but not yet established.
  • charon.init_limit_job_load [0] — limit based on the number of jobs currently queued for processing (sum over all job priorities).

The second limit includes load from other jobs, such as rekeying. Choosing a good value is difficult and depends on the hardware and expected load. ngl, good luck with that one.

The first limit is simpler to calculate, but includes the load from new connections only. If your responder is capable of negotiating 100 tunnels/s, you might set this limit to 1000. The daemon will then drop new connection attempts if generating a response would require more than 10 seconds. If you're allowing for a maximum response time of more than 30 seconds, consider adjusting the timeout for connecting IKE_SAs (charon.half_open_timeout). A responder, by default, deletes an IKE_SA if the initiator does not establish it within 30 seconds. Under high load, a higher value might be required. ⭐

🔥 LOAD TESTS 🔥

Okay so for stability testing and performance optimizations, the IKE daemon charon ships with the load-tester plugin. This plugin lets you set up thousands of tunnels concurrently against the daemon itself or against a remote host. Very stress-test core, no cap. ✨

WARNING: Never enable the load-testing plugin on productive systems. It provides preconfigured credentials and allows an attacker to authenticate as any user. That is a huge red flag, bestie — do not do it on anything real. 💀

🧪 Configuration details 🧪

For public key authentication, the responder uses the identity "CN=srv, OU=load-test, O=strongSwan". For the initiator, each connection attempt uses a different identity in the form "CN=c1-r1, OU=load-test, O=strongSwan", where the first number is the client number and the second is the authentication round — this matters if multiple authentication rounds are used.

For PSK authentication, FQDN identities are used. The server uses srv.strongswan.org, and the client uses an identity in the form c1-r1.strongswan.org.

For EAP authentication, the client uses a NAI in the form 100000000010001@strongswan.org.

To configure multiple authentication rounds, concatenate multiple methods, like this:

initiator_auth = pubkey|psk|eap-md5|eap-aka

The responder uses a hardcoded certificate based on a 1024-bit RSA key. This certificate additionally serves as CA certificate. A peer uses the same private key, but generates client certificates on demand signed by the CA certificate. Install the Responder/CA certificate on the remote host to authenticate all clients.

To speed up testing, the load tester plugin implements a special Diffie-Hellman implementation called modpnull. By setting this:

proposal = aes128-sha1-modpnull

you get this wicked fast DH implementation. It does not provide any security at all, but it allows you to run tests without DH calculation overhead. Iconic for testing, absolutely not for real life. 💅

🎀 Examples 🎀

In the simplest case, the daemon initiates IKE_SAs against itself using the loopback interface. This will actually establish double the number of IKE_SAs, because the daemon is initiator and responder for each IKE_SA at the same time. Installation of IPsec SAs would fail, since each SA gets installed twice. To simulate the correct behavior, a fake kernel interface can be enabled which does not install the IPsec SAs at the kernel level.

A simple loopback configuration might look like this:

charon {
     # create new IKE_SAs for each CHILD_SA to simulate
     # different clients
     reuse_ikesa = no
     # turn off denial of service protection
     dos_protection = no

     plugins {
          load-tester {
               # enable the plugin
               enable = yes
               # use 4 threads to initiate connections
               # simultaneously
               initiators = 4
               # each thread initiates 1000 connections
               iterations = 1000
               # delay each initiation in each thread by 20ms
               delay = 20
               # enable the fake kernel interface to
               # avoid SA conflicts
               fake_kernel = yes
          }
     }
}

This will initiate 4000 IKE_SAs within 20 seconds. You may increase the delay value if your box can not handle that much load, or decrease it to put more load on it. If the daemon starts retransmitting messages, your box probably can not handle all connection attempts.

The plugin also allows you to test against a remote host. This might help to test against a real world configuration. A connection setup to do stress testing of a gateway might look like this:

charon {
     reuse_ikesa = no
     threads = 32

     plugins {
          load-tester {
               enable = yes
               # 10000 connections, ten in parallel
               initiators = 10
               iterations = 1000
               # use a delay of 100ms, overall time is:
               # iterations * delay = 100s
               delay = 100
               # address of the gateway
               remote = 1.2.3.4
               # IKE-proposal to use
               proposal = aes128-sha1-modp1024
               # use faster PSK authentication instead
               # of 1024bit RSA
               initiator_auth = psk
               responder_auth = psk
               # request a virtual IP using configuration
               # payloads
               request_virtual_ip = yes
               # enable CHILD_SA every 60s
               child_rekey = 60
          }
     }
}

⏱️ IKEv2 RETRANSMISSION ⏱️

Retransmission timeouts in the IKEv2 daemon charon can be configured globally using the keys listed below:

charon.retransmit_base [1.8]
charon.retransmit_timeout [4.0]
charon.retransmit_tries [5]
charon.retransmit_jitter [0]
charon.retransmit_limit [0]

The following algorithm is used to calculate the timeout:

relative timeout = retransmit_timeout * retransmit_base ^ (n-1)

Where n is the current retransmission count. The calculated timeout can't exceed the configured retransmit_limit (if any), which is useful if the number of retries is high.

If a jitter in percent is configured, the timeout is modified as follows:

relative timeout -= random(0, retransmit_jitter * relative timeout)

Using the default values, packets are retransmitted in:

  • Retransmission 1 — relative timeout 4s, absolute timeout 4s
  • Retransmission 2 — relative timeout 7s, absolute timeout 11s
  • Retransmission 3 — relative timeout 13s, absolute timeout 24s
  • Retransmission 4 — relative timeout 23s, absolute timeout 47s
  • Retransmission 5 — relative timeout 42s, absolute timeout 89s
  • giving up — relative timeout 76s, absolute timeout 165s

✨ VARIABLES ✨

The variables used above are configured as follows:

  • ${piddir} — /var/run
  • ${prefix} — /usr
  • ${sysconfdir} — /etc
  • ${random_device} — /dev/random
  • ${urandom_device} — /dev/urandom

🗂️ FILES 🗂️

  • /etc/strongswan.conf — configuration file
  • /etc/strongswan.d/ — directory containing included config snippets
  • /etc/strongswan.d/charon/ — plugin specific config snippets

👀 SEE ALSO 👀

🌸 HISTORY 🌸

Written for the strongSwan project by Tobias Brunner, Andreas Steffen and Martin Willi.


That's the whole STRONGSWAN.CONF(5) page, bestie — strongSwan 6.1.0, manual page footer and its date included right where they belong. You absolutely ate this read 💖✨

📦 where this page comes from