summaryrefslogtreecommitdiff
path: root/data/templates/ipsec
diff options
context:
space:
mode:
Diffstat (limited to 'data/templates/ipsec')
-rw-r--r--data/templates/ipsec/charon.j2352
-rw-r--r--data/templates/ipsec/charon/dhcp.conf.j220
-rw-r--r--data/templates/ipsec/charon/eap-radius.conf.j2117
-rw-r--r--data/templates/ipsec/interfaces_use.conf.j25
-rw-r--r--data/templates/ipsec/ios_profile.j2104
-rw-r--r--data/templates/ipsec/swanctl.conf.j2131
-rw-r--r--data/templates/ipsec/swanctl/l2tp.j230
-rw-r--r--data/templates/ipsec/swanctl/peer.j2166
-rw-r--r--data/templates/ipsec/swanctl/profile.j243
-rw-r--r--data/templates/ipsec/swanctl/remote_access.j261
-rw-r--r--data/templates/ipsec/windows_profile.j24
11 files changed, 1033 insertions, 0 deletions
diff --git a/data/templates/ipsec/charon.j2 b/data/templates/ipsec/charon.j2
new file mode 100644
index 0000000..388559a
--- /dev/null
+++ b/data/templates/ipsec/charon.j2
@@ -0,0 +1,352 @@
+# Options for the charon IKE daemon.
+charon {
+ # Accept unencrypted ID and HASH payloads in IKEv1 Main Mode.
+ # accept_unencrypted_mainmode_messages = no
+
+ # Maximum number of half-open IKE_SAs for a single peer IP.
+ # block_threshold = 5
+
+ # Whether Certicate Revocation Lists (CRLs) fetched via HTTP or LDAP should
+ # be saved under a unique file name derived from the public key of the
+ # Certification Authority (CA) to /etc/ipsec.d/crls (stroke) or
+ # /etc/swanctl/x509crl (vici), respectively.
+ # cache_crls = no
+
+ # Whether relations in validated certificate chains should be cached in
+ # memory.
+ # cert_cache = yes
+
+ # Send Cisco Unity vendor ID payload (IKEv1 only).
+ # cisco_unity = no
+
+ # Cisco FlexVPN
+{% if options is vyos_defined %}
+ cisco_flexvpn = {{ 'yes' if options.flexvpn is vyos_defined else 'no' }}
+{% if options.virtual_ip is vyos_defined %}
+ install_virtual_ip = yes
+{% endif %}
+{% if options.interface is vyos_defined %}
+ install_virtual_ip_on = {{ options.interface }}
+{% endif %}
+{% endif %}
+
+ # Close the IKE_SA if setup of the CHILD_SA along with IKE_AUTH failed.
+ # close_ike_on_child_failure = no
+
+ # Number of half-open IKE_SAs that activate the cookie mechanism.
+ # cookie_threshold = 10
+
+ # Delete CHILD_SAs right after they got successfully rekeyed (IKEv1 only).
+ # delete_rekeyed = no
+
+ # Use ANSI X9.42 DH exponent size or optimum size matched to cryptographic
+ # strength.
+ # dh_exponent_ansi_x9_42 = yes
+
+ # Use RTLD_NOW with dlopen when loading plugins and IMV/IMCs to reveal
+ # missing symbols immediately.
+ # dlopen_use_rtld_now = no
+
+ # DNS server assigned to peer via configuration payload (CP).
+ # dns1 =
+
+ # DNS server assigned to peer via configuration payload (CP).
+ # dns2 =
+
+ # Enable Denial of Service protection using cookies and aggressiveness
+ # checks.
+ # dos_protection = yes
+
+ # Compliance with the errata for RFC 4753.
+ # ecp_x_coordinate_only = yes
+
+ # Free objects during authentication (might conflict with plugins).
+ # flush_auth_cfg = no
+
+ # Whether to follow IKEv2 redirects (RFC 5685).
+ # follow_redirects = yes
+
+ # Maximum size (complete IP datagram size in bytes) of a sent IKE fragment
+ # when using proprietary IKEv1 or standardized IKEv2 fragmentation, defaults
+ # to 1280 (use 0 for address family specific default values, which uses a
+ # lower value for IPv4). If specified this limit is used for both IPv4 and
+ # IPv6.
+ # fragment_size = 1280
+
+ # Name of the group the daemon changes to after startup.
+ # group =
+
+ # Timeout in seconds for connecting IKE_SAs (also see IKE_SA_INIT DROPPING).
+ # half_open_timeout = 30
+
+ # Enable hash and URL support.
+ # hash_and_url = no
+
+ # Allow IKEv1 Aggressive Mode with pre-shared keys as responder.
+ # i_dont_care_about_security_and_use_aggressive_mode_psk = no
+
+ # Whether to ignore the traffic selectors from the kernel's acquire events
+ # for IKEv2 connections (they are not used for IKEv1).
+ # ignore_acquire_ts = no
+
+ # A space-separated list of routing tables to be excluded from route
+ # lookups.
+ # ignore_routing_tables =
+
+ # Maximum number of IKE_SAs that can be established at the same time before
+ # new connection attempts are blocked.
+ # ikesa_limit = 0
+
+ # Number of exclusively locked segments in the hash table.
+ # ikesa_table_segments = 1
+
+ # Size of the IKE_SA hash table.
+ # ikesa_table_size = 1
+
+ # Whether to close IKE_SA if the only CHILD_SA closed due to inactivity.
+ # inactivity_close_ike = no
+
+ # Limit new connections based on the current number of half open IKE_SAs,
+ # see IKE_SA_INIT DROPPING in strongswan.conf(5).
+ # init_limit_half_open = 0
+
+ # Limit new connections based on the number of queued jobs.
+ # init_limit_job_load = 0
+
+ # Causes charon daemon to ignore IKE initiation requests.
+ # initiator_only = no
+
+ # Install routes into a separate routing table for established IPsec
+ # tunnels.
+ install_routes = {{ install_routes }}
+
+ # Install virtual IP addresses.
+ # install_virtual_ip = yes
+
+ # The name of the interface on which virtual IP addresses should be
+ # installed.
+ # install_virtual_ip_on =
+
+ # Check daemon, libstrongswan and plugin integrity at startup.
+ # integrity_test = no
+
+ # A comma-separated list of network interfaces that should be ignored, if
+ # interfaces_use is specified this option has no effect.
+ # interfaces_ignore =
+
+ # A comma-separated list of network interfaces that should be used by
+ # charon. All other interfaces are ignored.
+ # interfaces_use =
+
+ # NAT keep alive interval.
+ # keep_alive = 20s
+
+ # Plugins to load in the IKE daemon charon.
+ # load =
+
+ # Determine plugins to load via each plugin's load option.
+ # load_modular = no
+
+ # Initiate IKEv2 reauthentication with a make-before-break scheme.
+ # make_before_break = no
+
+ # Maximum number of IKEv1 phase 2 exchanges per IKE_SA to keep state about
+ # and track concurrently.
+ # max_ikev1_exchanges = 3
+
+ # Maximum packet size accepted by charon.
+ # max_packet = 10000
+
+ # Enable multiple authentication exchanges (RFC 4739).
+ # multiple_authentication = yes
+
+ # WINS servers assigned to peer via configuration payload (CP).
+ # nbns1 =
+
+ # WINS servers assigned to peer via configuration payload (CP).
+ # nbns2 =
+
+ # UDP port used locally. If set to 0 a random port will be allocated.
+ # port = 500
+
+ # UDP port used locally in case of NAT-T. If set to 0 a random port will be
+ # allocated. Has to be different from charon.port, otherwise a random port
+ # will be allocated.
+ # port_nat_t = 4500
+
+ # Prefer locally configured proposals for IKE/IPsec over supplied ones as
+ # responder (disabling this can avoid keying retries due to
+ # INVALID_KE_PAYLOAD notifies).
+ # prefer_configured_proposals = yes
+
+ # By default public IPv6 addresses are preferred over temporary ones (RFC
+ # 4941), to make connections more stable. Enable this option to reverse
+ # this.
+ # prefer_temporary_addrs = no
+
+ # Process RTM_NEWROUTE and RTM_DELROUTE events.
+ # process_route = yes
+
+ # Delay in ms for receiving packets, to simulate larger RTT.
+ # receive_delay = 0
+
+ # Delay request messages.
+ # receive_delay_request = yes
+
+ # Delay response messages.
+ # receive_delay_response = yes
+
+ # Specific IKEv2 message type to delay, 0 for any.
+ # receive_delay_type = 0
+
+ # Size of the AH/ESP replay window, in packets.
+ # replay_window = 32
+
+ # Base to use for calculating exponential back off, see IKEv2 RETRANSMISSION
+ # in strongswan.conf(5).
+ # retransmit_base = 1.8
+
+ # Timeout in seconds before sending first retransmit.
+ # retransmit_timeout = 4.0
+
+ # Number of times to retransmit a packet before giving up.
+ # retransmit_tries = 5
+
+ # Interval in seconds to use when retrying to initiate an IKE_SA (e.g. if
+ # DNS resolution failed), 0 to disable retries.
+ # retry_initiate_interval = 0
+
+ # Initiate CHILD_SA within existing IKE_SAs (always enabled for IKEv1).
+ # reuse_ikesa = yes
+
+ # Numerical routing table to install routes to.
+ # routing_table =
+
+ # Priority of the routing table.
+ # routing_table_prio =
+
+ # Delay in ms for sending packets, to simulate larger RTT.
+ # send_delay = 0
+
+ # Delay request messages.
+ # send_delay_request = yes
+
+ # Delay response messages.
+ # send_delay_response = yes
+
+ # Specific IKEv2 message type to delay, 0 for any.
+ # send_delay_type = 0
+
+ # Send strongSwan vendor ID payload
+ # send_vendor_id = no
+
+ # Whether to enable Signature Authentication as per RFC 7427.
+ # signature_authentication = yes
+
+ # Whether to enable constraints against IKEv2 signature schemes.
+ # signature_authentication_constraints = yes
+
+ # Number of worker threads in charon.
+ # threads = 16
+
+ # Name of the user the daemon changes to after startup.
+ # user =
+
+ crypto_test {
+
+ # Benchmark crypto algorithms and order them by efficiency.
+ # bench = no
+
+ # Buffer size used for crypto benchmark.
+ # bench_size = 1024
+
+ # Number of iterations to test each algorithm.
+ # bench_time = 50
+
+ # Test crypto algorithms during registration (requires test vectors
+ # provided by the test-vectors plugin).
+ # on_add = no
+
+ # Test crypto algorithms on each crypto primitive instantiation.
+ # on_create = no
+
+ # Strictly require at least one test vector to enable an algorithm.
+ # required = no
+
+ # Whether to test RNG with TRUE quality; requires a lot of entropy.
+ # rng_true = no
+
+ }
+
+ host_resolver {
+
+ # Maximum number of concurrent resolver threads (they are terminated if
+ # unused).
+ # max_threads = 3
+
+ # Minimum number of resolver threads to keep around.
+ # min_threads = 0
+
+ }
+
+ leak_detective {
+
+ # Includes source file names and line numbers in leak detective output.
+ # detailed = yes
+
+ # Threshold in bytes for leaks to be reported (0 to report all).
+ # usage_threshold = 10240
+
+ # Threshold in number of allocations for leaks to be reported (0 to
+ # report all).
+ # usage_threshold_count = 0
+
+ }
+
+ processor {
+
+ # Section to configure the number of reserved threads per priority class
+ # see JOB PRIORITY MANAGEMENT in strongswan.conf(5).
+ priority_threads {
+
+ }
+
+ }
+
+ # Section containing a list of scripts (name = path) that are executed when
+ # the daemon is started.
+ start-scripts {
+
+ }
+
+ # Section containing a list of scripts (name = path) that are executed when
+ # the daemon is terminated.
+ stop-scripts {
+
+ }
+
+ tls {
+
+ # List of TLS encryption ciphers.
+ # cipher =
+
+ # List of TLS key exchange methods.
+ # key_exchange =
+
+ # List of TLS MAC algorithms.
+ # mac =
+
+ # List of TLS cipher suites.
+ # suites =
+
+ }
+
+ x509 {
+
+ # Discard certificates with unsupported or unknown critical extensions.
+ # enforce_critical = yes
+
+ }
+
+}
+
diff --git a/data/templates/ipsec/charon/dhcp.conf.j2 b/data/templates/ipsec/charon/dhcp.conf.j2
new file mode 100644
index 0000000..aaa5613
--- /dev/null
+++ b/data/templates/ipsec/charon/dhcp.conf.j2
@@ -0,0 +1,20 @@
+dhcp {
+ load = yes
+{% if remote_access.dhcp.interface is vyos_defined %}
+ interface = {{ remote_access.dhcp.interface }}
+{% endif %}
+{% if remote_access.dhcp.server is vyos_defined %}
+ server = {{ remote_access.dhcp.server }}
+{% endif %}
+
+ # Always use the configured server address.
+ # force_server_address = no
+
+ # Derive user-defined MAC address from hash of IKE identity and send client
+ # identity DHCP option.
+ # identity_lease = no
+
+ # Use the DHCP server port (67) as source port when a unicast server address
+ # is configured.
+ # use_server_port = no
+}
diff --git a/data/templates/ipsec/charon/eap-radius.conf.j2 b/data/templates/ipsec/charon/eap-radius.conf.j2
new file mode 100644
index 0000000..3643774
--- /dev/null
+++ b/data/templates/ipsec/charon/eap-radius.conf.j2
@@ -0,0 +1,117 @@
+eap-radius {
+ # Send RADIUS accounting information to RADIUS servers.
+ # accounting = no
+
+ # Close the IKE_SA if there is a timeout during interim RADIUS accounting
+ # updates.
+ # accounting_close_on_timeout = yes
+
+ # Interval in seconds for interim RADIUS accounting updates, if not
+ # specified by the RADIUS server in the Access-Accept message.
+ # accounting_interval = 0
+
+ # 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.
+ # accounting_requires_vip = no
+
+ # If enabled, adds the Class attributes received in Access-Accept message to
+ # the RADIUS accounting messages.
+ # accounting_send_class = no
+
+ # Use class attributes in Access-Accept messages as group membership
+ # information.
+ # class_group = no
+
+ # Closes all IKE_SAs if communication with the RADIUS server times out. If
+ # it is not set only the current IKE_SA is closed.
+ # close_all_on_timeout = no
+
+ # Send EAP-Start instead of EAP-Identity to start RADIUS conversation.
+ # eap_start = no
+
+ # Use filter_id attribute as group membership information.
+ # filter_id = no
+
+ # Prefix to EAP-Identity, some AAA servers use a IMSI prefix to select the
+ # EAP method.
+ # id_prefix =
+
+ # Whether to load the plugin. Can also be an integer to increase the
+ # priority of this plugin.
+ load = yes
+
+ # NAS-Identifier to include in RADIUS messages.
+ nas_identifier = {{ remote_access.radius.nas_identifier if remote_access.radius.nas_identifier is vyos_defined else 'strongSwan' }}
+
+ # Port of RADIUS server (authentication).
+ # port = 1812
+
+ # Base to use for calculating exponential back off.
+ # retransmit_base = 1.4
+
+{% if remote_access.radius.timeout is vyos_defined %}
+ # Timeout in seconds before sending first retransmit.
+ retransmit_timeout = {{ remote_access.radius.timeout | float }}
+{% endif %}
+
+ # Number of times to retransmit a packet before giving up.
+ # retransmit_tries = 4
+
+ # Shared secret between RADIUS and NAS. If set, make sure to adjust the
+ # permissions of the config file accordingly.
+ # secret =
+
+ # IP/Hostname of RADIUS server.
+ # server =
+
+ # Number of sockets (ports) to use, increase for high load.
+ # sockets = 1
+
+ # Whether to include the UDP port in the Called- and Calling-Station-Id
+ # RADIUS attributes.
+ # station_id_with_port = yes
+
+ dae {
+ # Enables support for the Dynamic Authorization Extension (RFC 5176).
+ # enable = no
+
+ # Address to listen for DAE messages from the RADIUS server.
+ # listen = 0.0.0.0
+
+ # Port to listen for DAE requests.
+ # port = 3799
+
+ # Shared secret used to verify/sign DAE messages. If set, make sure to
+ # adjust the permissions of the config file accordingly.
+ # secret =
+ }
+
+ forward {
+ # RADIUS attributes to be forwarded from IKEv2 to RADIUS.
+ # ike_to_radius =
+
+ # Same as ike_to_radius but from RADIUS to IKEv2.
+ # radius_to_ike =
+ }
+
+ # Section to specify multiple RADIUS servers.
+ servers {
+{% if remote_access.radius.server is vyos_defined %}
+{% for server, server_options in remote_access.radius.server.items() if server_options.disable is not vyos_defined %}
+ {{ server | replace('.', '-') }} {
+ address = {{ server }}
+ secret = {{ server_options.key }}
+ auth_port = {{ server_options.port }}
+{% if server_options.disable_accounting is not vyos_defined %}
+ acct_port = {{ server_options.port | int + 1 }}
+{% endif %}
+ sockets = 20
+ }
+{% endfor %}
+{% endif %}
+ }
+
+ # Section to configure multiple XAuth authentication rounds via RADIUS.
+ xauth {
+ }
+}
diff --git a/data/templates/ipsec/interfaces_use.conf.j2 b/data/templates/ipsec/interfaces_use.conf.j2
new file mode 100644
index 0000000..c1bf827
--- /dev/null
+++ b/data/templates/ipsec/interfaces_use.conf.j2
@@ -0,0 +1,5 @@
+{% if interface is vyos_defined %}
+charon {
+ interfaces_use = {{ ', '.join(interface) }}
+}
+{% endif %} \ No newline at end of file
diff --git a/data/templates/ipsec/ios_profile.j2 b/data/templates/ipsec/ios_profile.j2
new file mode 100644
index 0000000..eb74924
--- /dev/null
+++ b/data/templates/ipsec/ios_profile.j2
@@ -0,0 +1,104 @@
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <!-- Set the name to whatever you like, it is used in the profile list on the device -->
+ <key>PayloadDisplayName</key>
+ <string>{{ profile_name }}</string>
+ <!-- This is a reverse-DNS style unique identifier used to detect duplicate profiles -->
+ <key>PayloadIdentifier</key>
+ <string>{{ rfqdn }}</string>
+ <!-- A globally unique identifier, use uuidgen on Linux/Mac OS X to generate it -->
+ <key>PayloadUUID</key>
+ <string>{{ '' | get_uuid }}</string>
+ <key>PayloadType</key>
+ <string>Configuration</string>
+ <key>PayloadVersion</key>
+ <integer>1</integer>
+ <key>PayloadContent</key>
+ <array>
+ <!-- It is possible to add multiple VPN payloads with different identifiers/UUIDs and names -->
+ <dict>
+ <!-- This is an extension of the identifier given above -->
+ <key>PayloadIdentifier</key>
+ <string>{{ rfqdn }}.conf1</string>
+ <!-- A globally unique identifier for this payload -->
+ <key>PayloadUUID</key>
+ <string>{{ '' | get_uuid }}</string>
+ <key>PayloadType</key>
+ <string>com.apple.vpn.managed</string>
+ <key>PayloadVersion</key>
+ <integer>1</integer>
+ <!-- This is the name of the VPN connection as seen in the VPN application later -->
+ <key>UserDefinedName</key>
+ <string>{{ vpn_name }}</string>
+ <key>VPNType</key>
+ <string>IKEv2</string>
+ <key>IKEv2</key>
+ <dict>
+ <!-- Hostname or IP address of the VPN server -->
+ <key>RemoteAddress</key>
+ <string>{{ remote }}</string>
+ <!-- Remote identity, can be a FQDN, a userFQDN, an IP or (theoretically) a certificate's subject DN. Can't be empty.
+ IMPORTANT: DNs are currently not handled correctly, they are always sent as identities of type FQDN -->
+ <key>RemoteIdentifier</key>
+ <string>{{ authentication.local_id if authentication.local_id is vyos_defined else 'VyOS' }}</string>
+ <!-- Local IKE identity, same restrictions as above. If it is empty the client's IP address will be used -->
+ <key>LocalIdentifier</key>
+ <string></string>
+ <!-- Optional, if it matches the CN of the root CA certificate (not the full subject DN) a certificate request will be sent
+ NOTE: If this is not configured make sure to configure leftsendcert=always on the server, otherwise it won't send its certificate -->
+ <key>ServerCertificateIssuerCommonName</key>
+ <string>{{ ca_cn }}</string>
+ <!-- Optional, the CN or one of the subjectAltNames of the server certificate to verify it, if not set RemoteIdentifier will be used -->
+ <key>ServerCertificateCommonName</key>
+ <string>{{ cert_cn }}</string>
+ <!-- The server is authenticated using a certificate -->
+ <key>AuthenticationMethod</key>
+ <string>Certificate</string>
+ <!-- The client uses EAP to authenticate -->
+ <key>ExtendedAuthEnabled</key>
+ <integer>1</integer>
+ <!-- The next two dictionaries are optional (as are the keys in them), but it is recommended to specify them as the default is to use 3DES.
+ IMPORTANT: Because only one proposal is sent (even if nothing is configured here) it must match the server configuration -->
+ <key>IKESecurityAssociationParameters</key>
+ <dict>
+ <!-- @see https://developer.apple.com/documentation/networkextension/nevpnikev2encryptionalgorithm -->
+ <key>EncryptionAlgorithm</key>
+ <string>{{ ike_encryption.encryption }}</string>
+ <!-- @see https://developer.apple.com/documentation/networkextension/nevpnikev2integrityalgorithm -->
+ <key>IntegrityAlgorithm</key>
+ <string>{{ ike_encryption.hash }}</string>
+ <!-- @see https://developer.apple.com/documentation/networkextension/nevpnikev2diffiehellmangroup -->
+ <key>DiffieHellmanGroup</key>
+ <integer>{{ ike_encryption.dh_group }}</integer>
+ </dict>
+ <key>ChildSecurityAssociationParameters</key>
+ <dict>
+ <key>EncryptionAlgorithm</key>
+ <string>{{ esp_encryption.encryption }}</string>
+ <key>IntegrityAlgorithm</key>
+ <string>{{ esp_encryption.hash }}</string>
+ <key>DiffieHellmanGroup</key>
+ <integer>{{ ike_encryption.dh_group }}</integer>
+ </dict>
+ </dict>
+ </dict>
+ <!-- This payload is optional but it provides an easy way to install the CA certificate together with the configuration -->
+ <dict>
+ <key>PayloadIdentifier</key>
+ <string>org.example.ca</string>
+ <key>PayloadUUID</key>
+ <string>{{ '' | get_uuid }}</string>
+ <key>PayloadType</key>
+ <string>com.apple.security.root</string>
+ <key>PayloadVersion</key>
+ <integer>1</integer>
+ <!-- This is the Base64 (PEM) encoded CA certificate -->
+ <key>PayloadContent</key>
+ <data>
+ {{ ca_cert }}
+ </data>
+ </dict>
+ </array>
+</dict>
+</plist>
diff --git a/data/templates/ipsec/swanctl.conf.j2 b/data/templates/ipsec/swanctl.conf.j2
new file mode 100644
index 0000000..d44d0f5
--- /dev/null
+++ b/data/templates/ipsec/swanctl.conf.j2
@@ -0,0 +1,131 @@
+### Autogenerated by vpn_ipsec.py ###
+{% import 'ipsec/swanctl/l2tp.j2' as l2tp_tmpl %}
+{% import 'ipsec/swanctl/profile.j2' as profile_tmpl %}
+{% import 'ipsec/swanctl/peer.j2' as peer_tmpl %}
+{% import 'ipsec/swanctl/remote_access.j2' as remote_access_tmpl %}
+
+connections {
+{% if profile is vyos_defined %}
+{% for name, profile_conf in profile.items() if profile_conf.disable is not vyos_defined and profile_conf.bind.tunnel is vyos_defined %}
+{{ profile_tmpl.conn(name, profile_conf, ike_group, esp_group) }}
+{% endfor %}
+{% endif %}
+{% if site_to_site.peer is vyos_defined %}
+{% for peer, peer_conf in site_to_site.peer.items() if peer not in dhcp_no_address and peer_conf.disable is not vyos_defined %}
+{{ peer_tmpl.conn(peer, peer_conf, ike_group, esp_group) }}
+{% endfor %}
+{% endif %}
+{% if remote_access.connection is vyos_defined %}
+{% for rw, rw_conf in remote_access.connection.items() if rw_conf.disable is not vyos_defined %}
+{{ remote_access_tmpl.conn(rw, rw_conf, ike_group, esp_group) }}
+{% endfor %}
+{% endif %}
+{% if l2tp %}
+{{ l2tp_tmpl.conn(l2tp, l2tp_outside_address, l2tp_ike_default, l2tp_esp_default, ike_group, esp_group) }}
+{% endif %}
+}
+
+pools {
+{% if remote_access.pool is vyos_defined %}
+{% for pool, pool_config in remote_access.pool.items() %}
+ {{ pool }} {
+{% if pool_config.prefix is vyos_defined %}
+ addrs = {{ pool_config.prefix }}
+{% endif %}
+{% if pool_config.name_server is vyos_defined %}
+ dns = {{ pool_config.name_server | join(',') }}
+{% endif %}
+{% if pool_config.exclude is vyos_defined %}
+ split_exclude = {{ pool_config.exclude | join(',') }}
+{% endif %}
+ }
+{% endfor %}
+{% endif %}
+}
+
+secrets {
+{% if profile is vyos_defined %}
+{% for name, profile_conf in profile.items() if profile_conf.disable is not vyos_defined and profile_conf.bind.tunnel is vyos_defined %}
+{% if profile_conf.authentication.mode is vyos_defined('pre-shared-secret') %}
+{% for interface in profile_conf.bind.tunnel %}
+ ike-dmvpn-{{ interface }} {
+ secret = {{ profile_conf.authentication.pre_shared_secret }}
+ }
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if site_to_site.peer is vyos_defined %}
+{% for peer, peer_conf in site_to_site.peer.items() if peer not in dhcp_no_address and peer_conf.disable is not vyos_defined %}
+{% set peer_name = peer.replace("@", "") | dot_colon_to_dash %}
+{% if peer_conf.authentication.mode is vyos_defined('x509') %}
+ private_{{ peer_name }} {
+ file = {{ peer_conf.authentication.x509.certificate }}.pem
+{% if peer_conf.authentication.x509.passphrase is vyos_defined %}
+ secret = "{{ peer_conf.authentication.x509.passphrase }}"
+{% endif %}
+ }
+{% elif peer_conf.authentication.mode is vyos_defined('rsa') %}
+ rsa_{{ peer_name }}_local {
+ file = {{ peer_conf.authentication.rsa.local_key }}.pem
+{% if peer_conf.authentication.rsa.passphrase is vyos_defined %}
+ secret = "{{ peer_conf.authentication.rsa.passphrase }}"
+{% endif %}
+ }
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if authentication.psk is vyos_defined %}
+{% for psk, psk_config in authentication.psk.items() %}
+ ike-{{ psk }} {
+{% if psk_config.id is vyos_defined %}
+ # ID's from auth psk <tag> id xxx
+{% for id in psk_config.id %}
+{% set gen_uuid = '' | generate_uuid4 %}
+ id-{{ gen_uuid }} = "{{ id }}"
+{% endfor %}
+{% endif %}
+ secret = "{{ psk_config.secret }}"
+ }
+{% endfor %}
+{% endif %}
+
+{% if remote_access.connection is vyos_defined %}
+{% for ra, ra_conf in remote_access.connection.items() if ra_conf.disable is not vyos_defined %}
+{% if ra_conf.authentication.server_mode is vyos_defined('pre-shared-secret') %}
+ ike_{{ ra }} {
+{% if ra_conf.authentication.local_id is vyos_defined %}
+ id = "{{ ra_conf.authentication.local_id }}"
+{% elif ra_conf.local_address is vyos_defined %}
+ id = "{{ ra_conf.local_address }}"
+{% endif %}
+ secret = "{{ ra_conf.authentication.pre_shared_secret }}"
+ }
+{% endif %}
+{% if ra_conf.authentication.client_mode is vyos_defined('eap-mschapv2') and ra_conf.authentication.local_users.username is vyos_defined %}
+{% for user, user_conf in ra_conf.authentication.local_users.username.items() if user_conf.disable is not vyos_defined %}
+ eap-{{ ra }}-{{ user }} {
+ secret = "{{ user_conf.password }}"
+ id-{{ ra }}-{{ user }} = "{{ user }}"
+ }
+{% endfor %}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% if l2tp %}
+{% if l2tp.authentication.mode is vyos_defined('pre-shared-secret') %}
+ ike_l2tp_remote_access {
+ id = "{{ l2tp_outside_address }}"
+ secret = "{{ l2tp.authentication.pre_shared_secret }}"
+ }
+{% elif l2tp.authentication.mode is vyos_defined('x509') %}
+ private_l2tp_remote_access {
+ id = "{{ l2tp_outside_address }}"
+ file = {{ l2tp.authentication.x509.certificate }}.pem
+{% if l2tp.authentication.x509.passphrase is vyos_defined %}
+ secret = "{{ l2tp.authentication.x509.passphrase }}"
+{% endif %}
+ }
+{% endif %}
+{% endif %}
+}
diff --git a/data/templates/ipsec/swanctl/l2tp.j2 b/data/templates/ipsec/swanctl/l2tp.j2
new file mode 100644
index 0000000..7e63865
--- /dev/null
+++ b/data/templates/ipsec/swanctl/l2tp.j2
@@ -0,0 +1,30 @@
+{% macro conn(l2tp, l2tp_outside_address, l2tp_ike_default, l2tp_esp_default, ike_group, esp_group) %}
+{% set l2tp_ike = ike_group[l2tp.ike_group] if l2tp.ike_group is vyos_defined else None %}
+{% set l2tp_esp = esp_group[l2tp.esp_group] if l2tp.esp_group is vyos_defined else None %}
+ l2tp_remote_access {
+ proposals = {{ l2tp_ike | get_esp_ike_cipher | join(',') if l2tp_ike else l2tp_ike_default }}
+ local_addrs = {{ l2tp_outside_address }}
+ dpd_delay = 15s
+ dpd_timeout = 45s
+ rekey_time = {{ l2tp_ike.lifetime if l2tp_ike else l2tp.ike_lifetime }}s
+ reauth_time = 0
+ local {
+ auth = {{ 'psk' if l2tp.authentication.mode == 'pre-shared-secret' else 'pubkey' }}
+{% if l2tp.authentication.mode == 'x509' %}
+ certs = {{ l2tp.authentication.x509.certificate }}.pem
+{% endif %}
+ }
+ remote {
+ auth = {{ 'psk' if l2tp.authentication.mode == 'pre-shared-secret' else 'pubkey' }}
+ }
+ children {
+ l2tp_remote_access_esp {
+ mode = transport
+ esp_proposals = {{ l2tp_esp | get_esp_ike_cipher(l2tp_ike) | join(',') if l2tp_esp else l2tp_esp_default }}
+ life_time = {{ l2tp_esp.lifetime if l2tp_esp else l2tp.lifetime }}s
+ local_ts = dynamic[/1701]
+ remote_ts = dynamic
+ }
+ }
+ }
+{% endmacro %}
diff --git a/data/templates/ipsec/swanctl/peer.j2 b/data/templates/ipsec/swanctl/peer.j2
new file mode 100644
index 0000000..58f0199
--- /dev/null
+++ b/data/templates/ipsec/swanctl/peer.j2
@@ -0,0 +1,166 @@
+{% macro conn(peer, peer_conf, ike_group, esp_group) %}
+{% set name = peer.replace("@", "") | dot_colon_to_dash %}
+{# peer needs to reference the global IKE configuration for certain values #}
+{% set ike = ike_group[peer_conf.ike_group] %}
+ {{ name }} {
+ proposals = {{ ike | get_esp_ike_cipher | join(',') }}
+ version = {{ ike.key_exchange[4:] if ike.key_exchange is vyos_defined else "0" }}
+{% if peer_conf.virtual_address is vyos_defined %}
+ vips = {{ peer_conf.virtual_address | join(', ') }}
+{% endif %}
+ local_addrs = {{ peer_conf.local_address if peer_conf.local_address != 'any' else '%any' }} # dhcp:{{ peer_conf.dhcp_interface if 'dhcp_interface' in peer_conf else 'no' }}
+ remote_addrs = {{ peer_conf.remote_address | join(",") if peer_conf.remote_address is vyos_defined and 'any' not in peer_conf.remote_address else '%any' }}
+{% if peer_conf.authentication.mode is vyos_defined('x509') %}
+ send_cert = always
+{% endif %}
+{% if ike.dead_peer_detection is vyos_defined %}
+ dpd_timeout = {{ ike.dead_peer_detection.timeout }}
+ dpd_delay = {{ ike.dead_peer_detection.interval }}
+{% endif %}
+{% if ike.key_exchange is vyos_defined('ikev1') and ike.mode is vyos_defined('aggressive') %}
+ aggressive = yes
+{% endif %}
+ rekey_time = {{ ike.lifetime }}s
+ mobike = {{ "no" if ike.disable_mobike is defined else "yes" }}
+{% if peer[0:1] == '@' %}
+ keyingtries = 0
+ reauth_time = 0
+{% elif peer_conf.connection_type is not vyos_defined or peer_conf.connection_type is vyos_defined('initiate') %}
+ keyingtries = 0
+{% elif peer_conf.connection_type is vyos_defined('respond') %}
+ keyingtries = 1
+{% endif %}
+{% if peer_conf.force_udp_encapsulation is vyos_defined %}
+ encap = yes
+{% endif %}
+ local {
+{% if peer_conf.authentication.local_id is vyos_defined %}
+ id = "{{ peer_conf.authentication.local_id }}"
+{% endif %}
+ auth = {{ 'psk' if peer_conf.authentication.mode == 'pre-shared-secret' else 'pubkey' }}
+{% if peer_conf.authentication.mode == 'x509' %}
+ certs = {{ peer_conf.authentication.x509.certificate }}.pem
+{% elif peer_conf.authentication.mode == 'rsa' %}
+ pubkeys = {{ peer_conf.authentication.rsa.local_key }}.pem
+{% endif %}
+ }
+ remote {
+ id = "{{ peer_conf.authentication.remote_id }}"
+ auth = {{ 'psk' if peer_conf.authentication.mode == 'pre-shared-secret' else 'pubkey' }}
+{% if peer_conf.authentication.mode == 'rsa' %}
+ pubkeys = {{ peer_conf.authentication.rsa.remote_key }}.pem
+{% endif %}
+ }
+ children {
+{% if peer_conf.vti.bind is vyos_defined and peer_conf.tunnel is not vyos_defined %}
+{% set vti_esp = esp_group[ peer_conf.vti.esp_group ] if peer_conf.vti.esp_group is vyos_defined else esp_group[ peer_conf.default_esp_group ] %}
+ {{ name }}-vti {
+ esp_proposals = {{ vti_esp | get_esp_ike_cipher(ike) | join(',') }}
+{% if vti_esp.life_bytes is vyos_defined %}
+ life_bytes = {{ vti_esp.life_bytes }}
+{% endif %}
+{% if vti_esp.life_packets is vyos_defined %}
+ life_packets = {{ vti_esp.life_packets }}
+{% endif %}
+ life_time = {{ vti_esp.lifetime }}s
+ local_ts = 0.0.0.0/0,::/0
+ remote_ts = 0.0.0.0/0,::/0
+ updown = "/etc/ipsec.d/vti-up-down {{ peer_conf.vti.bind }}"
+{# The key defaults to 0 and will match any policies which similarly do not have a lookup key configuration. #}
+{# Thus we simply shift the key by one to also support a vti0 interface #}
+{% set if_id = peer_conf.vti.bind | replace('vti', '') | int + 1 %}
+ if_id_in = {{ if_id }}
+ if_id_out = {{ if_id }}
+ ipcomp = {{ 'yes' if vti_esp.compression is vyos_defined else 'no' }}
+ mode = {{ vti_esp.mode }}
+{% if peer[0:1] == '@' %}
+ start_action = none
+{% elif peer_conf.connection_type is not vyos_defined or peer_conf.connection_type is vyos_defined('initiate') %}
+ start_action = start
+{% elif peer_conf.connection_type is vyos_defined('respond') %}
+ start_action = trap
+{% elif peer_conf.connection_type is vyos_defined('none') %}
+ start_action = none
+{% endif %}
+{% if ike.dead_peer_detection is vyos_defined %}
+ dpd_action = {{ ike.dead_peer_detection.action }}
+{% endif %}
+ close_action = {{ ike.close_action }}
+{% if peer_conf.replay_window is vyos_defined %}
+ replay_window = {{ peer_conf.replay_window }}
+{% endif %}
+ }
+{% elif peer_conf.tunnel is vyos_defined %}
+{% for tunnel_id, tunnel_conf in peer_conf.tunnel.items() if tunnel_conf.disable is not defined %}
+{% set tunnel_esp_name = tunnel_conf.esp_group if tunnel_conf.esp_group is vyos_defined else peer_conf.default_esp_group %}
+{% set tunnel_esp = esp_group[tunnel_esp_name] %}
+{% set proto = tunnel_conf.protocol if tunnel_conf.protocol is vyos_defined else '' %}
+{% set local_port = tunnel_conf.local.port if tunnel_conf.local.port is vyos_defined else '' %}
+{% set local_suffix = '[{0}/{1}]'.format(proto, local_port) if proto or local_port else '' %}
+{% set remote_port = tunnel_conf.remote.port if tunnel_conf.remote.port is vyos_defined else '' %}
+{% set remote_suffix = '[{0}/{1}]'.format(proto, remote_port) if proto or remote_port else '' %}
+ {{ name }}-tunnel-{{ tunnel_id }} {
+ esp_proposals = {{ tunnel_esp | get_esp_ike_cipher(ike) | join(',') }}
+{% if tunnel_esp.life_bytes is vyos_defined %}
+ life_bytes = {{ tunnel_esp.life_bytes }}
+{% endif %}
+{% if tunnel_esp.life_packets is vyos_defined %}
+ life_packets = {{ tunnel_esp.life_packets }}
+{% endif %}
+ life_time = {{ tunnel_esp.lifetime }}s
+{% if tunnel_esp.mode is not defined or tunnel_esp.mode == 'tunnel' %}
+{% if tunnel_conf.local.prefix is vyos_defined %}
+{% set local_prefix = tunnel_conf.local.prefix if 'any' not in tunnel_conf.local.prefix else ['0.0.0.0/0', '::/0'] %}
+ local_ts = {{ local_prefix | join(local_suffix + ",") }}{{ local_suffix }}
+{% endif %}
+{% if tunnel_conf.remote.prefix is vyos_defined %}
+{% set remote_prefix = tunnel_conf.remote.prefix if 'any' not in tunnel_conf.remote.prefix else ['0.0.0.0/0', '::/0'] %}
+ remote_ts = {{ remote_prefix | join(remote_suffix + ",") }}{{ remote_suffix }}
+{% endif %}
+{% if tunnel_conf.priority is vyos_defined %}
+ priority = {{ tunnel_conf.priority }}
+{% endif %}
+{% elif tunnel_esp.mode == 'transport' %}
+ local_ts = {{ peer_conf.local_address }}{{ local_suffix }}
+ remote_ts = {{ peer_conf.remote_address | join(",") }}{{ remote_suffix }}
+{% endif %}
+ ipcomp = {{ 'yes' if tunnel_esp.compression is vyos_defined else 'no' }}
+ mode = {{ tunnel_esp.mode }}
+{% if peer[0:1] == '@' %}
+ start_action = none
+{% elif peer_conf.connection_type is not vyos_defined or peer_conf.connection_type is vyos_defined('initiate') %}
+ start_action = start
+{% elif peer_conf.connection_type is vyos_defined('respond') %}
+ start_action = trap
+{% elif peer_conf.connection_type is vyos_defined('none') %}
+ start_action = none
+{% endif %}
+{% if ike.dead_peer_detection is vyos_defined %}
+ dpd_action = {{ ike.dead_peer_detection.action }}
+{% endif %}
+ close_action = {{ ike.close_action }}
+{% if peer_conf.replay_window is vyos_defined %}
+ replay_window = {{ peer_conf.replay_window }}
+{% endif %}
+{% if peer_conf.vti.bind is vyos_defined %}
+{# The key defaults to 0 and will match any policies which similarly do not have a lookup key configuration. #}
+{# Thus we simply shift the key by one to also support a vti0 interface #}
+{% set if_id = peer_conf.vti.bind | replace('vti', '') | int + 1 %}
+ updown = "/etc/ipsec.d/vti-up-down {{ peer_conf.vti.bind }}"
+ if_id_in = {{ if_id }}
+ if_id_out = {{ if_id }}
+{% endif %}
+ }
+{% if tunnel_conf.passthrough is vyos_defined %}
+ {{ name }}-tunnel-{{ tunnel_id }}-passthrough {
+ local_ts = {{ tunnel_conf.passthrough | join(",") }}
+ remote_ts = {{ tunnel_conf.passthrough | join(",") }}
+ start_action = trap
+ mode = pass
+ }
+{% endif %}
+{% endfor %}
+{% endif %}
+ }
+ }
+{% endmacro %}
diff --git a/data/templates/ipsec/swanctl/profile.j2 b/data/templates/ipsec/swanctl/profile.j2
new file mode 100644
index 0000000..8519a84
--- /dev/null
+++ b/data/templates/ipsec/swanctl/profile.j2
@@ -0,0 +1,43 @@
+{% macro conn(name, profile_conf, ike_group, esp_group) %}
+{# peer needs to reference the global IKE configuration for certain values #}
+{% set ike = ike_group[profile_conf.ike_group] %}
+{% set esp = esp_group[profile_conf.esp_group] %}
+{% if profile_conf.bind.tunnel is vyos_defined %}
+{% for interface in profile_conf.bind.tunnel %}
+ dmvpn-{{ name }}-{{ interface }} {
+ proposals = {{ ike_group[profile_conf.ike_group] | get_esp_ike_cipher | join(',') }}
+ version = {{ ike.key_exchange[4:] if ike.key_exchange is vyos_defined else "0" }}
+ rekey_time = {{ ike.lifetime }}s
+ keyingtries = 0
+{% if ike.dead_peer_detection is vyos_defined %}
+ dpd_timeout = {{ ike.dead_peer_detection.timeout }}
+ dpd_delay = {{ ike.dead_peer_detection.interval }}
+{% endif %}
+{% if profile_conf.authentication.mode is vyos_defined('pre-shared-secret') %}
+ local {
+ auth = psk
+ }
+ remote {
+ auth = psk
+ }
+{% endif %}
+ children {
+ dmvpn {
+ esp_proposals = {{ esp | get_esp_ike_cipher(ike) | join(',') }}
+ rekey_time = {{ esp.lifetime }}s
+ rand_time = 540s
+ local_ts = dynamic[gre]
+ remote_ts = dynamic[gre]
+ mode = {{ esp.mode }}
+{% if ike.dead_peer_detection.action is vyos_defined %}
+ dpd_action = {{ ike.dead_peer_detection.action }}
+{% endif %}
+{% if esp.compression is vyos_defined('enable') %}
+ ipcomp = yes
+{% endif %}
+ }
+ }
+ }
+{% endfor %}
+{% endif %}
+{% endmacro %}
diff --git a/data/templates/ipsec/swanctl/remote_access.j2 b/data/templates/ipsec/swanctl/remote_access.j2
new file mode 100644
index 0000000..6bced88
--- /dev/null
+++ b/data/templates/ipsec/swanctl/remote_access.j2
@@ -0,0 +1,61 @@
+{% macro conn(name, rw_conf, ike_group, esp_group) %}
+{# peer needs to reference the global IKE configuration for certain values #}
+{% set ike = ike_group[rw_conf.ike_group] %}
+{% set esp = esp_group[rw_conf.esp_group] %}
+ ra-{{ name }} {
+ remote_addrs = %any
+ local_addrs = {{ rw_conf.local_address if rw_conf.local_address is not vyos_defined('any') else '%any' }} # dhcp:{{ rw_conf.dhcp_interface if rw_conf.dhcp_interface is vyos_defined else 'no' }}
+ proposals = {{ ike_group[rw_conf.ike_group] | get_esp_ike_cipher | join(',') }}
+ version = {{ ike.key_exchange[4:] if ike.key_exchange is vyos_defined else "0" }}
+ send_certreq = no
+ rekey_time = {{ ike.lifetime }}s
+ keyingtries = 0
+{% if rw_conf.unique is vyos_defined %}
+ unique = {{ rw_conf.unique }}
+{% endif %}
+{% if rw_conf.pool is vyos_defined %}
+ pools = {{ rw_conf.pool | join(',') }}
+{% endif %}
+ local {
+{% if rw_conf.authentication.local_id is vyos_defined and rw_conf.authentication.use_x509_id is not vyos_defined %}
+{# please use " quotes - else Apple iOS goes crazy #}
+ id = "{{ rw_conf.authentication.local_id }}"
+{% endif %}
+{% if rw_conf.authentication.server_mode == 'x509' %}
+ auth = pubkey
+ certs = {{ rw_conf.authentication.x509.certificate }}.pem
+{% elif rw_conf.authentication.server_mode == 'pre-shared-secret' %}
+ auth = psk
+{% endif %}
+ }
+ remote {
+{% if rw_conf.authentication.client_mode == 'x509' %}
+ auth = pubkey
+{% elif rw_conf.authentication.client_mode.startswith("eap") %}
+ auth = {{ rw_conf.authentication.client_mode }}
+ eap_id = {{ '%any' if rw_conf.authentication.eap_id == 'any' else rw_conf.authentication.eap_id }}
+{% endif %}
+{% if rw_conf.authentication.client_mode is vyos_defined('eap-tls') or rw_conf.authentication.client_mode is vyos_defined('x509') %}
+{# pass all configured CAs as filenames, separated by commas #}
+{# this will produce a string like "MyCA1.pem,MyCA2.pem" #}
+ cacerts = {{ '.pem,'.join(rw_conf.authentication.x509.ca_certificate) ~ '.pem' }}
+{% endif %}
+ }
+ children {
+ ikev2-vpn {
+ esp_proposals = {{ esp | get_esp_ike_cipher(ike) | join(',') }}
+ rekey_time = {{ esp.lifetime }}s
+ rand_time = 540s
+ dpd_action = clear
+ inactivity = {{ rw_conf.timeout }}
+{% if rw_conf.replay_window is vyos_defined %}
+ replay_window = {{ rw_conf.replay_window }}
+{% endif %}
+{% set local_prefix = rw_conf.local.prefix if rw_conf.local.prefix is vyos_defined else ['0.0.0.0/0', '::/0'] %}
+{% set local_port = rw_conf.local.port if rw_conf.local.port is vyos_defined else '' %}
+{% set local_suffix = '[%any/{1}]'.format(local_port) if local_port else '' %}
+ local_ts = {{ local_prefix | join(local_suffix + ",") }}{{ local_suffix }}
+ }
+ }
+ }
+{% endmacro %}
diff --git a/data/templates/ipsec/windows_profile.j2 b/data/templates/ipsec/windows_profile.j2
new file mode 100644
index 0000000..8c26944
--- /dev/null
+++ b/data/templates/ipsec/windows_profile.j2
@@ -0,0 +1,4 @@
+Remove-VpnConnection -Name "{{ vpn_name }}" -Force -PassThru
+
+Add-VpnConnection -Name "{{ vpn_name }}" -ServerAddress "{{ remote }}" -TunnelType "Ikev2"
+Set-VpnConnectionIPsecConfiguration -ConnectionName "{{ vpn_name }}" -AuthenticationTransformConstants {{ ike_encryption.encryption }} -CipherTransformConstants {{ ike_encryption.encryption }} -EncryptionMethod {{ esp_encryption.encryption }} -IntegrityCheckMethod {{ esp_encryption.hash }} -PfsGroup None -DHGroup "Group{{ ike_encryption.dh_group }}" -PassThru -Force