strongswan: migrate to swanctl configs
authorPhilip Prindeville <redacted>
Wed, 10 Feb 2021 05:49:30 +0000 (22:49 -0700)
committerPhilip Prindeville <redacted>
Sun, 4 Apr 2021 17:58:51 +0000 (11:58 -0600)
Derived from the ipsec initd script, with the following changes:

(1) various code improvements, corrections (get rid of left/right
    updown scripts, since there's only one), etc;
(2) add reauth and fragmentation parameters;
(3) add x.509 certificate-based authentication;

and other minor changes.

Signed-off-by: Philip Prindeville <redacted>
net/strongswan/Makefile
net/strongswan/files/swanctl.init [new file with mode: 0644]

index 06ec598baa28ecc203ec6f835b1b569157d752fe..7b8e215b73d25c833ebe89900b4cfe8bd3cd58fd 100644 (file)
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=strongswan
 PKG_VERSION:=5.9.1
-PKG_RELEASE:=7
+PKG_RELEASE:=8
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
 PKG_SOURCE_URL:=https://download.strongswan.org/ https://download2.strongswan.org/
@@ -537,11 +537,14 @@ define Package/strongswan-swanctl/conffiles
 endef
 
 define Package/strongswan-swanctl/install
+       $(INSTALL_DIR) $(1)/etc/init.d
        $(INSTALL_DIR) $(1)/etc/swanctl/{bliss,conf.d,ecdsa,pkcs{12,8},private,pubkey,rsa}
        $(INSTALL_DIR) $(1)/etc/swanctl/x509{,aa,ac,ca,crl,ocsp}
        $(CP) $(PKG_INSTALL_DIR)/etc/swanctl/swanctl.conf $(1)/etc/swanctl/
+       echo "include /var/swanctl/swanctl.conf" >> $(1)/etc/swanctl/swanctl.conf
        $(INSTALL_DIR) $(1)/usr/sbin
        $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/swanctl $(1)/usr/sbin/
+       $(INSTALL_BIN) ./files/swanctl.init $(1)/etc/init.d/swanctl
 endef
 
 define Package/strongswan-libtls/install
diff --git a/net/strongswan/files/swanctl.init b/net/strongswan/files/swanctl.init
new file mode 100644 (file)
index 0000000..21fc7e8
--- /dev/null
@@ -0,0 +1,601 @@
+#!/bin/sh /etc/rc.common
+
+START=90
+STOP=10
+
+USE_PROCD=1
+PROG=/usr/lib/ipsec/starter
+
+. $IPKG_INSTROOT/lib/functions.sh
+. $IPKG_INSTROOT/lib/functions/network.sh
+
+STRONGSWAN_CONF_FILE=/etc/strongswan.conf
+STRONGSWAN_VAR_CONF_FILE=/var/ipsec/strongswan.conf
+
+SWANCTL_CONF_FILE=/etc/swanctl/swanctl.conf
+SWANCTL_VAR_CONF_FILE=/var/swanctl/swanctl.conf
+
+WAIT_FOR_INTF=0
+
+time2seconds()
+{
+       local timestring="$1"
+       local multiplier number suffix
+
+       suffix="${timestring//[0-9 ]}"
+       number="${timestring%%$suffix}"
+       [ "$number$suffix" != "$timestring" ] && return 1
+       case "$suffix" in
+       ""|s)
+               multiplier=1 ;;
+       m)
+               multiplier=60 ;;
+       h)
+               multiplier=3600 ;;
+       d)
+               multiplier=86400 ;;
+       *)
+               return 1 ;;
+       esac
+       echo $(( number * multiplier ))
+}
+
+seconds2time()
+{
+       local seconds="$1"
+
+       if [ $seconds -eq 0 ]; then
+               echo "0s"
+       elif [ $((seconds % 86400)) -eq 0 ]; then
+               echo "$((seconds / 86400))d"
+       elif [ $((seconds % 3600)) -eq 0 ]; then
+               echo "$((seconds / 3600))h"
+       elif [ $((seconds % 60)) -eq 0 ]; then
+               echo "$((seconds / 60))m"
+       else
+               echo "${seconds}s"
+       fi
+}
+
+file_reset() {
+       : > "$1"
+}
+
+xappend() {
+       local file="$1"
+       shift
+
+       echo "$@" >> "$file"
+}
+
+swan_reset() {
+       file_reset "$STRONGSWAN_VAR_CONF_FILE"
+}
+
+swan_xappend() {
+       xappend "$STRONGSWAN_VAR_CONF_FILE" "$@"
+}
+
+swan_xappend0() {
+       swan_xappend "$@"
+}
+
+swan_xappend1() {
+       swan_xappend "  ""$@"
+}
+
+swan_xappend2() {
+       swan_xappend "    ""$@"
+}
+
+swan_xappend3() {
+       swan_xappend "      ""$@"
+}
+
+swan_xappend4() {
+       swan_xappend "        ""$@"
+}
+
+swanctl_reset() {
+       file_reset "$SWANCTL_VAR_CONF_FILE"
+}
+
+swanctl_xappend() {
+       xappend "$SWANCTL_VAR_CONF_FILE" "$@"
+}
+
+swanctl_xappend0() {
+       swanctl_xappend "$@"
+}
+
+swanctl_xappend1() {
+       swanctl_xappend "  ""$@"
+}
+
+swanctl_xappend2() {
+       swanctl_xappend "    ""$@"
+}
+
+swanctl_xappend3() {
+       swanctl_xappend "      ""$@"
+}
+
+swanctl_xappend4() {
+       swanctl_xappend "        ""$@"
+}
+
+warning() {
+       echo "WARNING: $@" >&2
+}
+
+is_aead() {
+       local cipher="$1"
+
+       case "$cipher" in
+       aes*gcm*|aes*ccm*|aes*gmac*)
+               return 0 ;;
+       esac
+
+       return 1
+}
+
+add_esp_proposal() {
+       local encryption_algorithm
+       local hash_algorithm
+       local dh_group
+
+       config_get encryption_algorithm "$1" encryption_algorithm
+       config_get hash_algorithm "$1" hash_algorithm
+       config_get dh_group "$1" dh_group
+
+       # check for AEAD and clobber hash_algorithm if set
+       if is_aead "$encryption_algorithm" && [ -n "$hash_algorithm" ]; then
+               warning "Can't have $hash_algorithm with $encryption_algorithm"
+               hash_algorithm=
+       fi
+
+       [ -n "$encryption_algorithm" ] && \
+               crypto="${crypto:+${crypto},}${encryption_algorithm}${hash_algorithm:+-${hash_algorithm}}${dh_group:+-${dh_group}}"
+}
+
+parse_esp_proposal() {
+       local conf="$1"
+       local crypto=""
+
+       config_list_foreach "$conf" crypto_proposal add_esp_proposal
+
+       echo "$crypto"
+}
+
+add_ike_proposal() {
+       local encryption_algorithm
+       local hash_algorithm
+       local dh_group
+       local prf_algorithm
+
+       config_get encryption_algorithm "$1" encryption_algorithm
+       config_get hash_algorithm "$1" hash_algorithm
+       config_get dh_group "$1" dh_group
+       config_get prf_algorithm "$1" prf_algorithm
+
+       # check for AEAD and clobber hash_algorithm if set
+       if is_aead "$encryption_algorithm" && [ -n "$hash_algorithm" ]; then
+               warning "Can't have $hash_algorithm with $encryption_algorithm"
+               hash_algorithm=
+       fi
+
+       [ -n "$encryption_algorithm" ] && \
+               crypto="${crypto:+${crypto},}${encryption_algorithm}${hash_algorithm:+-${hash_algorithm}}${prf_algorithm:+-${prf_algorithm}}${dh_group:+-${dh_group}}"
+}
+
+parse_ike_proposal() {
+       local conf="$1"
+       local crypto=""
+
+       config_list_foreach "$conf" crypto_proposal add_ike_proposal
+
+       echo "$crypto"
+}
+
+config_conn() {
+       # Generic ipsec conn section shared by tunnel and transport
+       local config_name="$1"
+       local mode="$2"
+
+       local local_subnet
+       local local_nat
+       local updown
+       local firewall
+       local remote_subnet
+       local remote_sourceip
+       local lifetime
+       local dpdaction
+       local closeaction
+       local startaction
+       local if_id
+       local rekeytime
+
+       config_get startaction "$1" startaction "route"
+       config_get local_subnet "$1" local_subnet ""
+       config_get local_nat "$1" local_nat ""
+       config_get updown "$1" updown ""
+       config_get firewall "$1" firewall ""
+       config_get remote_subnet "$1" remote_subnet ""
+       config_get remote_sourceip "$1" remote_sourceip ""
+       config_get lifetime "$1" lifetime ""
+       config_get dpdaction "$1" dpdaction "none"
+       config_get closeaction "$1" closeaction "none"
+       config_get if_id "$1" if_id ""
+       config_get rekeytime "$1" rekeytime ""
+
+       local esp_proposal="$(parse_esp_proposal "$1")"
+
+       # translate from ipsec to swanctl
+       case "$startaction" in
+       add)
+               startaction="none" ;;
+       route)
+               startaction="trap" ;;
+       start|none|trap)
+               # already using new syntax
+               ;;
+       *)
+               warning "Startaction $startaction unknown"
+               startaction=
+               ;;
+       esac
+
+       case "$closeaction" in
+       none|clear)
+               closeaction="none" ;;
+       hold)
+               closeaction="trap" ;;
+       restart)
+               closeaction="start" ;;
+       trap|start)
+               # already using new syntax
+               ;;
+       *)
+               warning "Closeaction $closeaction unknown"
+               closeaction=
+               ;;
+       esac
+
+       [ -n "$closeaction" -a "$closeaction" != "none" ] && warning "Closeaction $closeaction can cause instability"
+
+       case "$dpdaction" in
+       none)
+               dpddelay="0s"
+               dpdaction=
+               ;;
+       clear)
+               ;;
+       hold)
+               dpdaction="trap" ;;
+       restart)
+               dpdaction="start" ;;
+       trap|start)
+               # already using new syntax
+               ;;
+       *)
+               warning "Dpdaction $dpdaction unknown"
+               dpdaction=
+               ;;
+       esac
+
+       [ -n "$local_nat" ] && local_subnet="$local_nat"
+
+       swanctl_xappend3 "$config_name {"
+
+       [ -n "$local_subnet" ] && swanctl_xappend4 "local_ts = $local_subnet"
+       [ -n "$remote_subnet" ] && swanctl_xappend4 "remote_ts = $remote_subnet"
+       [ -n "$if_id" ] && { swanctl_xappend4 "if_id_in = $if_id" ; swanctl_xappend4 "if_id_out = $if_id" ; }
+       [ -n "$startaction" -a "$startaction" != "none" ] && swanctl_xappend4 "start_action = $startaction"
+       [ -n "$closeaction" -a "$closeaction" != "none" ] && swanctl_xappend4 "close_action = $closeaction"
+       swanctl_xappend4 "esp_proposals = $esp_proposal"
+       swanctl_xappend4 "mode = $mode"
+
+       if [ -n "$lifetime" ]; then
+               swanctl_xappend4 "life_time = $lifetime"
+       elif [ -n "$rekeytime" ]; then
+               swanctl_xappend4 "life_time = $(seconds2time $(((110 * $(time2seconds $rekeytime)) / 100)))"
+       fi
+       [ -n "$rekeytime" ] && swanctl_xappend4 "rekey_time = $rekeytime"
+
+       [ -n "$updown" ] && swanctl_xappend4 "updown = $updown"
+       [ -n "$dpdaction" ] && swanctl_xappend4 "dpd_action = $dpdaction"
+
+        swanctl_xappend3 "}"
+}
+
+config_tunnel() {
+       config_conn "$1" "tunnel"
+}
+
+config_transport() {
+       config_conn "$1" "transport"
+}
+
+config_remote() {
+       local config_name="$1"
+
+       local enabled
+       local gateway
+       local local_gateway
+       local local_sourceip
+       local local_leftip
+       local remote_gateway
+       local pre_shared_key
+       local auth_method
+       local keyingtries
+       local dpddelay
+       local inactivity
+       local keyexchange
+       local reqid
+       local packet_marker
+       local fragmentation
+       local mobike
+       local local_cert
+       local local_key
+       local ca_cert
+       local rekeytime
+
+       config_get_bool enabled "$1" enabled 0
+       [ $enabled -eq 0 ] && return
+
+       config_get gateway "$1" gateway
+       config_get pre_shared_key "$1" pre_shared_key
+       config_get auth_method "$1" authentication_method
+       config_get local_identifier "$1" local_identifier ""
+       config_get remote_identifier "$1" remote_identifier ""
+       config_get local_sourceip "$1" local_sourceip ""
+       config_get local_leftip "$1" local_leftip "%any"
+       config_get keyingtries "$1" keyingtries "3"
+       config_get dpddelay "$1" dpddelay "30s"
+       config_get inactivity "$1" inactivity
+       config_get keyexchange "$1" keyexchange "ikev2"
+       config_get reqid "$1" reqid
+       config_get packet_marker "$1" packet_marker
+       config_get fragmentation "$1" fragmentation "yes"
+       config_get_bool mobike "$1" mobike 1
+       config_get local_cert "$1" local_cert ""
+       config_get local_key "$1" local_key ""
+       config_get ca_cert "$1" ca_cert ""
+       config_get rekeytime "$1" rekeytime
+       config_get overtime "$1" overtime
+
+       case "$fragmentation" in
+       0)
+               fragmentation="no" ;;
+       1)
+               fragmentation="yes" ;;
+       yes|accept|force|no)
+               # already using new syntax
+               ;;
+       *)
+               warning "Fragmentation $fragmentation not supported"
+               fragmentation=
+               ;;
+       esac
+
+       [ "$gateway" = "any" ] && remote_gateway="%any" || remote_gateway="$gateway"
+
+       [ -z "$local_gateway" ] && {
+               local ipdest
+
+               [ "$remote_gateway" = "%any" ] && ipdest="1.1.1.1" || ipdest="$remote_gateway"
+               local_gateway=`ip -o route get $ipdest | awk '/ src / { gsub(/^.* src /,""); gsub(/ .*$/, ""); print $0}'`
+       }
+
+       local ike_proposal="$(parse_ike_proposal "$1")"
+
+       [ -n "$firewall" ] && warning "Firewall not supported"
+
+       swanctl_xappend0 "# config for $config_name"
+       swanctl_xappend0 "connections {"
+       swanctl_xappend1 "$config_name {"
+       swanctl_xappend2 "local_addrs = $local_leftip"
+       swanctl_xappend2 "remote_addrs = $remote_gateway"
+
+       [ -n "$local_sourceip" ] && swanctl_xappend2 "vips = $local_sourceip"
+       [ -n "$fragmentation" ] && swanctl_xappend2 "fragmentation = $fragmentation"
+
+       swanctl_xappend2 "local {"
+       swanctl_xappend3 "auth = $auth_method"
+
+       [ -n "$local_identifier" ] && swanctl_xappend3 "id = \"$local_identifier\""
+       [ "$auth_method" = pubkey ] && swanctl_xappend3 "certs = $local_cert"
+       swanctl_xappend2 "}"
+
+       swanctl_xappend2 "remote {"
+       swanctl_xappend3 "auth = $auth_method"
+       [ -n "$remote_identifier" ] && swanctl_xappend3 "id = \"$remote_identifier\""
+       swanctl_xappend2 "}"
+
+       swanctl_xappend2 "children {"
+
+       config_list_foreach "$1" tunnel config_tunnel
+
+       config_list_foreach "$1" transport config_transport
+
+       swanctl_xappend2 "}"
+
+       case "$keyexchange" in
+       ike)
+               ;;
+       ikev1)
+               swanctl_xappend2 "version = 1" ;;
+       ikev2)
+               swanctl_xappend2 "version = 2" ;;
+       *)
+               warning "Keyexchange $keyexchange not supported"
+               keyexchange=
+               ;;
+       esac
+
+       [ $mobike -eq 1 ] && swanctl_xappend2 "mobike = yes" || swanctl_xappend2 "mobike = no"
+
+       if [ -n "$rekeytime" ]; then
+               swanctl_xappend2 "rekey_time = $rekeytime"
+
+               if [ -z "$overtime" ]; then
+                       overtime=$(seconds2time $(($(time2seconds $rekeytime) / 10)))
+               fi
+       fi
+       [ -n "$overtime" ] && swanctl_xappend2 "over_time = $overtime"
+
+       swanctl_xappend2 "proposals = $ike_proposal"
+       [ -n "$dpddelay" ] && swanctl_xappend2 "dpd_delay = $dpddelay"
+       [ "$keyingtries" = "%forever" ] && swanctl_xappend2 "keyingtries = 0" || swanctl_xappend2 "keyingtries = $keyingtries"
+
+       swanctl_xappend1 "}"
+       swanctl_xappend0 "}"
+
+       if [ "$auth_method" = pubkey ]; then
+               swanctl_xappend0 ""
+
+               swanctl_xappend0 "secrets {"
+               swanctl_xappend1 "rsa {"
+               swanctl_xappend2 "filename = $local_key"
+               swanctl_xappend1 "}"
+               swanctl_xappend0 "}"
+
+               swanctl_xappend0 ""
+
+               if [ -n "$ca_cert" ]; then
+                       swanctl_xappend0 "authorities {"
+                       swanctl_xappend1 "$config_name {"
+                       swanctl_xappend2 "cacert = $ca_cert"
+                       swanctl_xappend1 "}"
+                       swanctl_xappend0 "}"
+               fi
+
+       elif [ "$auth_method" = psk ]; then
+               swanctl_xappend0 ""
+
+               swanctl_xappend0 "secrets {"
+               swanctl_xappend1 "ike {"
+               swanctl_xappend2 "secret = $pre_shared_key"
+               if [ -z "$local_id" ]; then
+                       swanctl_xappend2 "id1 = $local_id"
+                       if [ -z "$remote_id" ]; then
+                               swanctl_xappend2 "id2 = $remote_id"
+                       fi
+               fi
+       else
+               warning "AuthenticationMode $auth_mode not supported"
+       fi
+
+       swanctl_xappend0 ""
+}
+
+do_preamble() {
+       swanctl_xappend0 "# generated by /etc/init.d/swanctl"
+}
+
+config_ipsec() {
+       local debug
+       local rtinstall_enabled
+       local routing_tables_ignored
+       local routing_table
+       local routing_table_id
+       local interface
+       local device_list
+
+       swan_reset
+       swanctl_reset
+       do_preamble
+
+       config_get debug "$1" debug 0
+       config_get_bool rtinstall_enabled "$1" rtinstall_enabled 1
+       [ $rtinstall_enabled -eq 1 ] && install_routes=yes || install_routes=no
+
+       # prepare extra charon config option ignore_routing_tables
+       for routing_table in $(config_get "$1" "ignore_routing_tables"); do
+               if [ "$routing_table" -ge 0 ] 2>/dev/null; then
+                       routing_table_id=$routing_table
+               else
+                       routing_table_id=$(sed -n '/[ \t]*[0-9]\+[ \t]\+'$routing_table'[ \t]*$/s/[ \t]*\([0-9]\+\).*/\1/p' /etc/iproute2/rt_tables)
+               fi
+
+               [ -n "$routing_table_id" ] && append routing_tables_ignored "$routing_table_id"
+       done
+
+       local interface_list=$(config_get "$1" "interface")
+       if [ -z "$interface_list" ]; then
+               WAIT_FOR_INTF=0
+       else
+               for interface in $interface_list; do
+                       network_get_device device $interface
+                       [ -n "$device" ] && append device_list "$device" ","
+               done
+               [ -n "$device_list" ] && WAIT_FOR_INTF=0 || WAIT_FOR_INTF=1
+       fi
+
+       swan_xappend0 "# generated by /etc/init.d/swanctl"
+       swan_xappend0 "charon {"
+       swan_xappend1 "install_routes = $install_routes"
+       [ -n "$routing_tables_ignored" ] && swan_xappend1 "ignore_routing_tables = $routing_tables_ignored"
+       [ -n "$device_list" ] && swan_xappend1 "interfaces_use = $device_list"
+       swan_xappend1 "start-scripts {"
+       swan_xappend2 "load-all = /usr/sbin/swanctl --load-all --noprompt"
+       swan_xappend1 "}"
+       swan_xappend1 "syslog {"
+       swan_xappend2 "identifier = ipsec"
+       swan_xappend2 "daemon {"
+       swan_xappend3 "default = $debug"
+       swan_xappend2 "}"
+       swan_xappend1 "}"
+       swan_xappend0 "}"
+}
+
+prepare_env() {
+       mkdir -p /var/ipsec /var/swanctl
+       config_load ipsec
+       config_foreach config_ipsec ipsec
+       config_foreach config_remote remote
+}
+
+service_running() {
+       swanctl --stats > /dev/null 2>&1
+}
+
+reload_service() {
+       running && {
+               prepare_env
+               [ $WAIT_FOR_INTF -eq 0 ] && {
+                       swanctl --load-all --noprompt
+                       return
+               }
+       }
+
+       start
+}
+
+stop_service() {
+       swan_reset
+       swanctl_reset
+}
+
+service_triggers() {
+       procd_add_reload_trigger "ipsec"
+       config load "ipsec"
+}
+
+start_service() {
+       prepare_env
+
+       [ $WAIT_FOR_INTF -eq 1 ] && return
+
+       procd_open_instance
+
+       procd_set_param command $PROG --daemon charon --nofork
+
+       procd_set_param file $SWANCTL_CONF_FILE
+       procd_append_param file /etc/swanctl/conf.d/*.conf
+       procd_append_param file $STRONGSWAN_CONF_FILE
+
+       procd_set_param respawn
+
+       procd_close_instance
+}
git clone https://git.99rst.org/PROJECT