Add CRD for IpsecHost 53/3553/8
authorRuoyu <ruoyu.ying@intel.com>
Tue, 9 Jun 2020 00:37:18 +0000 (08:37 +0800)
committerRuoyu <ruoyu.ying@intel.com>
Mon, 15 Jun 2020 06:57:40 +0000 (14:57 +0800)
* Contains changes on IpsecHost
  - Add CR for IpsecHost
  - Add support for 'mark' in /etc/init.d/ipsec
  - Change the 'Site' to 'Remote' in rest api calls
Issue-ID: ICN-289
Change-Id: I1f07f1f8f5fdf62f082829fdedf09a7504414611
Signed-off-by: Ruoyu <ruoyu.ying@intel.com>
22 files changed:
platform/cnf/src/Dockerfile_1806_mwan3.tpl
platform/cnf/src/Dockerfile_1806_mwan3_noproxy.tpl
platform/cnf/src/ipsec_exec [new file with mode: 0755]
platform/cnf/src/rest_v1/ipsec_rest.lua
platform/crd-ctrlr/README.md
platform/crd-ctrlr/examples/sdewan-controller.yaml
platform/crd-ctrlr/src/PROJECT
platform/crd-ctrlr/src/api/v1alpha1/bucket_permission_webhook.go
platform/crd-ctrlr/src/api/v1alpha1/ipsechost_types.go [new file with mode: 0644]
platform/crd-ctrlr/src/api/v1alpha1/zz_generated.deepcopy.go
platform/crd-ctrlr/src/config/crd/bases/batch.sdewan.akraino.org_ipsechosts.yaml [new file with mode: 0644]
platform/crd-ctrlr/src/config/crd/kustomization.yaml
platform/crd-ctrlr/src/config/crd/patches/cainjection_in_ipsechosts.yaml [new file with mode: 0644]
platform/crd-ctrlr/src/config/crd/patches/webhook_in_ipsechosts.yaml [new file with mode: 0644]
platform/crd-ctrlr/src/config/local/webhook_config.yaml
platform/crd-ctrlr/src/config/rbac/role.yaml
platform/crd-ctrlr/src/config/samples/batch_v1alpha1_ipsechost.yaml [new file with mode: 0644]
platform/crd-ctrlr/src/config/webhook/manifests.yaml
platform/crd-ctrlr/src/controllers/ipsechost_controller.go [new file with mode: 0644]
platform/crd-ctrlr/src/controllers/suite_test.go
platform/crd-ctrlr/src/main.go
platform/crd-ctrlr/src/openwrt/ipsec.go

index b62e627..658f463 100644 (file)
@@ -16,6 +16,7 @@ RUN mkdir /var/lock && \
 
 COPY system /etc/config/system
 COPY ipsec /etc/config/ipsec
+COPY ipsec_exec /etc/init.d/ipsec
 COPY rest_v1 /usr/lib/lua/luci/controller/rest_v1
 
 ENV http_proxy=
index 1163ed4..0fe098b 100644 (file)
@@ -13,6 +13,7 @@ RUN mkdir /var/lock && \
 
 COPY system /etc/config/system
 COPY ipsec /etc/config/ipsec
+COPY ipsec_exec /etc/init.d/ipsec
 COPY rest_v1 /usr/lib/lua/luci/controller/rest_v1
 
 USER root
diff --git a/platform/cnf/src/ipsec_exec b/platform/cnf/src/ipsec_exec
new file mode 100755 (executable)
index 0000000..6b906a3
--- /dev/null
@@ -0,0 +1,399 @@
+#!/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
+
+IPSEC_SECRETS_FILE=/etc/ipsec.secrets
+IPSEC_CONN_FILE=/etc/ipsec.conf
+STRONGSWAN_CONF_FILE=/etc/strongswan.conf
+
+IPSEC_VAR_SECRETS_FILE=/var/ipsec/ipsec.secrets
+IPSEC_VAR_CONN_FILE=/var/ipsec/ipsec.conf
+STRONGSWAN_VAR_CONF_FILE=/var/ipsec/strongswan.conf
+
+WAIT_FOR_INTF=0
+
+file_reset() {
+       : > "$1"
+}
+
+xappend() {
+       local file="$1"
+       shift
+
+       echo "${@}" >> "${file}"
+}
+
+remove_include() {
+       local file="$1"
+       local include="$2"
+
+       sed -i "\_${include}_d" "${file}"
+}
+
+remove_includes() {
+       remove_include "${IPSEC_CONN_FILE}" "${IPSEC_VAR_CONN_FILE}"
+       remove_include "${IPSEC_SECRETS_FILE}" "${IPSEC_VAR_SECRETS_FILE}"
+       remove_include "${STRONGSWAN_CONF_FILE}" "${STRONGSWAN_VAR_CONF_FILE}"
+}
+
+do_include() {
+       local conf="$1"
+       local uciconf="$2"
+       local backup=`mktemp -t -p /tmp/ ipsec-init-XXXXXX`
+
+       [ ! -f "${conf}" ] && rm -rf "${conf}"
+       touch "${conf}"
+
+       cat "${conf}" | grep -v "${uciconf}" > "${backup}"
+       mv "${backup}" "${conf}"
+       xappend "${conf}" "include ${uciconf}"
+       file_reset "${uciconf}"
+}
+
+ipsec_reset() {
+       do_include "${IPSEC_CONN_FILE}" "${IPSEC_VAR_CONN_FILE}"
+}
+
+ipsec_xappend() {
+       xappend "${IPSEC_VAR_CONN_FILE}" "$@"
+}
+
+swan_reset() {
+       do_include "${STRONGSWAN_CONF_FILE}" "${STRONGSWAN_VAR_CONF_FILE}"
+}
+
+swan_xappend() {
+       xappend "${STRONGSWAN_VAR_CONF_FILE}" "$@"
+}
+
+secret_reset() {
+       do_include "${IPSEC_SECRETS_FILE}" "${IPSEC_VAR_SECRETS_FILE}"
+}
+
+secret_xappend() {
+       xappend "${IPSEC_VAR_SECRETS_FILE}" "$@"
+}
+
+warning() {
+       echo "WARNING: $@" >&2
+}
+
+add_crypto_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
+
+       [ -n "${encryption_algorithm}" ] && \
+               crypto="${crypto:+${crypto},}${encryption_algorithm}${hash_algorithm:+-${hash_algorithm}}${dh_group:+-${dh_group}}"
+}
+
+set_crypto_proposal() {
+       local conf="$1"
+       local proposal
+
+       crypto=""
+
+       config_get crypto_proposal "$conf" crypto_proposal ""
+       for proposal in $crypto_proposal; do
+               add_crypto_proposal "$proposal"
+       done
+
+       [ -n "${crypto}" ] && {
+               local force_crypto_proposal
+
+               config_get_bool force_crypto_proposal "$conf" force_crypto_proposal
+
+               [ "${force_crypto_proposal}" = "1" ] && crypto="${crypto}!"
+       }
+
+       crypto_proposal="${crypto}"
+}
+
+config_conn() {
+       # Generic ipsec conn section shared by tunnel and transport
+       local mode
+       local mark
+       local local_subnet
+       local local_nat
+       local local_sourceip
+       local local_updown
+       local local_firewall
+       local remote_subnet
+       local remote_sourceip
+       local remote_updown
+       local remote_firewall
+       local ikelifetime
+       local lifetime
+       local margintime
+       local keyingtries
+       local dpdaction
+       local dpddelay
+       local inactivity
+       local keyexchange
+
+       config_get mode                     "$1"           mode "route"
+       config_get local_subnet             "$1"           local_subnet ""
+       config_get local_nat                "$1"           local_nat ""
+       config_get local_sourceip           "$1"           local_sourceip ""
+       config_get local_updown             "$1"           local_updown ""
+       config_get local_firewall           "$1"           local_firewall ""
+       config_get remote_subnet            "$1"           remote_subnet ""
+       config_get remote_sourceip          "$1"           remote_sourceip ""
+       config_get remote_updown            "$1"           remote_updown ""
+       config_get remote_firewall          "$1"           remote_firewall ""
+       config_get ikelifetime              "$1"           ikelifetime "3h"
+       config_get lifetime                 "$1"           lifetime "1h"
+       config_get margintime               "$1"           margintime "9m"
+       config_get keyingtries              "$1"           keyingtries "3"
+       config_get dpdaction                "$1"           dpdaction "none"
+       config_get dpddelay                 "$1"           dpddelay "30s"
+       config_get inactivity               "$1"           inactivity
+       config_get keyexchange              "$1"           keyexchange "ikev2"
+       config_get mark                     "$1"           mark ""
+
+       [ -n "$local_nat" ] && local_subnet=$local_nat
+
+       ipsec_xappend "conn $config_name-$1"
+       ipsec_xappend "  left=%any"
+       ipsec_xappend "  right=$remote_gateway"
+
+       [ -n "$local_sourceip" ] && ipsec_xappend "  leftsourceip=$local_sourceip"
+       [ -n "$local_subnet" ] && ipsec_xappend "  leftsubnet=$local_subnet"
+
+       [ -n "$local_firewall" ] && ipsec_xappend "  leftfirewall=$local_firewall"
+       [ -n "$remote_firewall" ] && ipsec_xappend "  rightfirewall=$remote_firewall"
+
+       ipsec_xappend "  ikelifetime=$ikelifetime"
+       ipsec_xappend "  lifetime=$lifetime"
+       ipsec_xappend "  margintime=$margintime"
+       ipsec_xappend "  keyingtries=$keyingtries"
+       ipsec_xappend "  dpdaction=$dpdaction"
+       ipsec_xappend "  dpddelay=$dpddelay"
+
+       [ -n "$inactivity" ] && ipsec_xappend "  inactivity=$inactivity"
+
+       if [ "$auth_method" = "psk" ]; then
+               ipsec_xappend "  leftauth=psk"
+               ipsec_xappend "  rightauth=psk"
+
+               [ "$remote_sourceip" != "" ] && ipsec_xappend "  rightsourceip=$remote_sourceip"
+               [ "$remote_subnet" != "" ] && ipsec_xappend "  rightsubnet=$remote_subnet"
+
+               ipsec_xappend "  auto=$mode"
+       elif [ "$auth_method" = "pubkey" ]; then
+               ipsec_xappend "  leftauth=pubkey"
+               ipsec_xappend "  rightauth=pubkey"
+
+               [ "$remote_sourceip" != "" ] && ipsec_xappend "  rightsourceip=$remote_sourceip"
+               [ "$remote_subnet" != "" ] && ipsec_xappend "  rightsubnet=$remote_subnet"
+
+               ipsec_xappend "  auto=$mode"
+       else
+               warning "AuthenticationMethod $auth_method not supported"
+       fi
+
+       [ -n "$local_identifier" ] && ipsec_xappend "  leftid=$local_identifier"
+       [ -n "$remote_identifier" ] && ipsec_xappend "  rightid=$remote_identifier"
+       [ -n "$local_updown" ] && ipsec_xappend "  leftupdown=$local_updown"
+       [ -n "$remote_updown" ] && ipsec_xappend "  rightupdown=$remote_updown"
+       ipsec_xappend "  keyexchange=$keyexchange"
+
+       [ "$type" = "VTI-based" ] && ipsec_xappend "  mark=$mark"
+
+       set_crypto_proposal "$1"
+       [ -n "${crypto_proposal}" ] && ipsec_xappend "  esp=$crypto_proposal"
+       [ -n "${ike_proposal}" ] && ipsec_xappend "  ike=$ike_proposal"
+}
+
+config_tunnel() {
+       config_conn "$1"
+
+       # Specific for the tunnel part
+       ipsec_xappend "  type=tunnel"
+}
+
+config_transport() {
+       config_conn "$1"
+
+       # Specific for the transport part
+       ipsec_xappend "  type=transport"
+}
+
+config_remote() {
+       local enabled
+       local gateway
+       local pre_shared_key
+       local auth_method
+       local type
+
+       config_name=$1
+
+       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 type              "$1" type "policy-based"
+
+       [ "$gateway" = "any" ] && remote_gateway="%any" || remote_gateway="$gateway"
+
+       [ -z "$local_identifier" ] && {
+               local ipdest
+
+               [ "$remote_gateway" = "%any" ] && ipdest="1.1.1.1" || ipdest="$remote_gateway"
+               local_gateway=`ip route get $ipdest | awk -F"src" '/src/{gsub(/ /,"");print $2}'`
+       }
+
+       [ -n "$local_identifier" ] && secret_xappend -n "$local_identifier " || secret_xappend -n "$local_gateway "
+       [ -n "$remote_identifier" ] && secret_xappend -n "$remote_identifier " || secret_xappend -n "$remote_gateway "
+
+       secret_xappend ": PSK \"$pre_shared_key\""
+
+       set_crypto_proposal "$1"
+       ike_proposal="$crypto_proposal"
+
+       config_list_foreach "$1" tunnel config_tunnel
+
+       config_list_foreach "$1" transport config_transport
+
+       ipsec_xappend ""
+}
+
+config_ipsec() {
+       local debug
+       local rtinstall_enabled
+       local routing_tables_ignored
+       local routing_table
+       local routing_table_id
+       local interface
+       local device_list
+
+       ipsec_reset
+       secret_reset
+       swan_reset
+
+       ipsec_xappend "# generated by /etc/init.d/ipsec"
+       ipsec_xappend "version 2"
+       ipsec_xappend ""
+
+       secret_xappend "# generated by /etc/init.d/ipsec"
+
+       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_xappend "# generated by /etc/init.d/ipsec"
+       swan_xappend "charon {"
+       swan_xappend "  load_modular = yes"
+       swan_xappend "  install_routes = $install_routes"
+       [ -n "$routing_tables_ignored" ] && swan_xappend "  ignore_routing_tables = $routing_tables_ignored"
+       [ -n "$device_list" ] && swan_xappend "  interfaces_use = $device_list"
+       swan_xappend "    plugins {"
+       swan_xappend "      include /etc/strongswan.d/charon/*.conf"
+       swan_xappend "    }"
+       swan_xappend "  syslog {"
+       swan_xappend "    identifier = ipsec"
+       swan_xappend "    daemon {"
+       swan_xappend "      default = $debug"
+       swan_xappend "    }"
+       swan_xappend "    auth {"
+       swan_xappend "      default = $debug"
+       swan_xappend "    }"
+       swan_xappend "  }"
+       swan_xappend "}"
+}
+
+prepare_env() {
+       mkdir -p /var/ipsec
+       remove_includes
+       config_load ipsec
+       config_foreach config_ipsec ipsec
+       config_foreach config_remote remote
+}
+
+service_running() {
+       ipsec status > /dev/null 2>&1
+}
+
+reload_service() {
+       running && {
+               prepare_env
+               [ $WAIT_FOR_INTF -eq 0 ] && {
+                       ipsec rereadall
+                       ipsec reload
+                       return
+               }
+       }
+
+       start
+}
+
+check_ipsec_interface() {
+       local intf
+
+       for intf in $(config_get "$1" interface); do
+               procd_add_interface_trigger "interface.*" "$intf" /etc/init.d/ipsec reload
+       done
+}
+
+service_triggers() {
+       procd_add_reload_trigger "ipsec"
+       config load "ipsec"
+       config_foreach check_ipsec_interface 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 $IPSEC_CONN_FILE
+       procd_append_param file $IPSEC_SECRETS_FILE
+       procd_append_param file $STRONGSWAN_CONF_FILE
+       procd_append_param file /etc/strongswan.d/*.conf
+       procd_append_param file /etc/strongswan.d/charon/*.conf
+
+       procd_set_param respawn
+
+       procd_close_instance
+}
index 837b5e7..5f0e7a0 100644 (file)
@@ -25,9 +25,9 @@ proposal_validator = {
 }
 
 connection_validator = {
-config_type=function(value) return value["type"] end,
+config_type=function(value) return value["conn_type"] end,
     {name="name", required=true},
-    {name="type", required=true, validator=function(value) return utils.in_array(value, {"tunnel", "transport"}) end, load_func=function(value) return value[".type"] end, save_func=function(value) return true, "" end, message="invalid type"},
+    {name="conn_type", required=true, validator=function(value) return utils.in_array(value, {"tunnel", "transport"}) end, load_func=function(value) return value[".type"] end, save_func=function(value) return true, "" end, message="invalid type"},
     {name="mode", required=true, validator=function(value) return utils.in_array(value, {"start", "add", "route"}) end, message="invalid connection mode"},
     {name="local_subnet"},
     {name="local_nat"},
@@ -39,13 +39,15 @@ config_type=function(value) return value["type"] end,
     {name="remote_updown"},
     {name="remote_firewall", validator=function(value) return utils.in_array(value, {"yes", "no"}) end},
     {name="crypto_proposal", is_list=true, item_validator=function(value) return is_proposal_available(value) end, message="invalid crypto_proposal"},
+    {name="mark"},
 
 }
 
-site_validator = {
+remote_validator = {
     config_type="remote",
     object_validator=function(value) return check_auth_method(value) end,
     {name="name"},
+    {name="type"},
     {name="gateway", required=true},
     {name="enabled", default="1"},
     {name="authentication_method", required=true, validator=function(value) return utils.in_array(value, {"psk", "pubkey"}) end},
@@ -73,7 +75,7 @@ site_validator = {
 
 ipsec_processor = {
     proposal={update="update_proposal", delete="delete_proposal", validator=proposal_validator},
-    site={validator=site_validator},
+    remote={validator=remote_validator},
     configuration=uci_conf
 }
 
@@ -87,7 +89,7 @@ function index()
     ver = "v1"
     configuration = "ipsec"
     entry({"sdewan", configuration, ver, "proposals"}, call("handle_request")).leaf = true
-    entry({"sdewan", configuration, ver, "sites"}, call("handle_request")).leaf = true
+    entry({"sdewan", configuration, ver, "remotes"}, call("handle_request")).leaf = true
 end
 
 -- Validate authentication method and secrets
@@ -198,7 +200,7 @@ function save_connection(value)
     local connections = value["connections"]
     local ret_value = {_standard_format=true}
     for i=1, #connections do
-        add_to_key_list(ret_value, connections[i]["type"], connections[i])
+        add_to_key_list(ret_value, connections[i]["conn_type"], connections[i])
     end
 
     return true, ret_value
index 5699c92..78267eb 100644 (file)
@@ -22,7 +22,7 @@ Sample deployment of CNF:
   ```
   kubectl apply -f examples/attach-network-ovn.yaml
   kubectl apply -f examples/ovn-net1.yaml
-  kubectl apply -f examples/ovn-net2.yml
+  kubectl apply -f examples/ovn-net2.yaml
   ```
 2. Launch CNF deployment. **NOTE:** CNF deployment is supposed to bind to a Node.
   For the sample cnf yaml, we bind it to master node. You can bind to other node by modifying the `nodeSelector`.
@@ -114,6 +114,8 @@ make gen-yaml IMG="integratedcloudnative/sdewan-controller:dev"
   - FirewallRule
   - FirewallDNAT
   - FirewallSNAT
+  - IpsecProposal
+  - IpsecHost
 
 ### What we don't have yet
 
index 78c0662..af0694b 100644 (file)
@@ -483,6 +483,139 @@ status:
 ---
 apiVersion: apiextensions.k8s.io/v1beta1
 kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.2.5
+  creationTimestamp: null
+  name: ipsechosts.batch.sdewan.akraino.org
+spec:
+  group: batch.sdewan.akraino.org
+  names:
+    kind: IpsecHost
+    listKind: IpsecHostList
+    plural: ipsechosts
+    singular: ipsechost
+  scope: Namespaced
+  subresources:
+    status: {}
+  validation:
+    openAPIV3Schema:
+      description: IpsecHost is the Schema for the ipsechosts API
+      properties:
+        apiVersion:
+          description: 'APIVersion defines the versioned schema of this representation
+            of an object. Servers should convert recognized schemas to the latest
+            internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+          type: string
+        kind:
+          description: 'Kind is a string value representing the REST resource this
+            object represents. Servers may infer this from the endpoint the client
+            submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+          type: string
+        metadata:
+          type: object
+        spec:
+          properties:
+            authentication_method:
+              type: string
+            connections:
+              items:
+                properties:
+                  conn_type:
+                    type: string
+                  crypto_proposal:
+                    items:
+                      type: string
+                    type: array
+                  if_id:
+                    type: string
+                  local_firewall:
+                    type: string
+                  local_sourceip:
+                    type: string
+                  local_updown:
+                    type: string
+                  mark:
+                    type: string
+                  mode:
+                    type: string
+                  name:
+                    type: string
+                  remote_firewall:
+                    type: string
+                  remote_sourceip:
+                    type: string
+                  remote_subnet:
+                    type: string
+                  remote_updown:
+                    type: string
+                required:
+                - conn_type
+                - mode
+                - name
+                type: object
+              type: array
+            crypto_proposal:
+              items:
+                type: string
+              type: array
+            force_crypto_proposal:
+              type: string
+            local_identifier:
+              type: string
+            local_private_cert:
+              type: string
+            local_public_cert:
+              type: string
+            name:
+              type: string
+            pre_shared_key:
+              type: string
+            remote:
+              type: string
+            remote_identifier:
+              type: string
+            shared_ca:
+              type: string
+            type:
+              type: string
+          required:
+          - authentication_method
+          - connections
+          - crypto_proposal
+          - remote
+          type: object
+        status:
+          description: status subsource used for Sdewan rule CRDs
+          properties:
+            appliedGeneration:
+              format: int64
+              type: integer
+            appliedTime:
+              format: date-time
+              type: string
+            message:
+              type: string
+            state:
+              type: string
+          required:
+          - state
+          type: object
+      type: object
+  version: v1alpha1
+  versions:
+  - name: v1alpha1
+    served: true
+    storage: true
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
+---
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
 metadata:
   annotations:
     controller-gen.kubebuilder.io/version: v0.2.5
@@ -895,6 +1028,26 @@ rules:
   - get
   - patch
   - update
+- apiGroups:
+  - batch.sdewan.akraino.org
+  resources:
+  - ipsechosts
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - batch.sdewan.akraino.org
+  resources:
+  - ipsechosts/status
+  verbs:
+  - get
+  - patch
+  - update
 - apiGroups:
   - batch.sdewan.akraino.org
   resources:
@@ -1164,4 +1317,5 @@ webhooks:
     - firewallrules
     - firewallsnats
     - firewalldnats
-    - ipsecproposal
+    - ipsecproposals
+    - ipsechosts
index 34a442e..e282bff 100644 (file)
@@ -25,4 +25,7 @@ resources:
 - group: batch
   kind: IpsecProposal
   version: v1alpha1
+- group: batch
+  kind: IpsecHost
+  version: v1alpha1
 version: "2"
index 722ef99..adf9c11 100644 (file)
@@ -45,7 +45,7 @@ func SetupBucketPermissionWebhookWithManager(mgr ctrl.Manager) error {
        return nil
 }
 
-// +kubebuilder:webhook:path=/validate-sdewan-bucket-permission,mutating=false,failurePolicy=fail,groups="batch.sdewan.akraino.org",resources=mwan3policies;mwan3rules;firewallzones;firewallforwardings;firewallrules;firewallsnats;firewalldnats;ipsecproposal,verbs=create;update;delete,versions=v1alpha1,name=validate-sdewan-bucket.akraino.org
+// +kubebuilder:webhook:path=/validate-sdewan-bucket-permission,mutating=false,failurePolicy=fail,groups="batch.sdewan.akraino.org",resources=mwan3policies;mwan3rules;firewallzones;firewallforwardings;firewallrules;firewallsnats;firewalldnats;ipsecproposals;ipsechosts,verbs=create;update;delete,versions=v1alpha1,name=validate-sdewan-bucket.akraino.org
 
 // bucketPermissionValidator validates Pods
 type bucketPermissionValidator struct {
@@ -59,7 +59,7 @@ type BucketPermission map[string][]string
 
 // +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=clusterroles;roles;rolebindings;clusterrolebindings,verbs=get;list;watch
 
-// bucketPermissionValidator admits a pod iff a specific annotation exists.
+// bucketPermissionValidator admits a pod if a specific annotation exists.
 func (v *bucketPermissionValidator) Handle(ctx context.Context, req admission.Request) admission.Response {
        if req.Kind.Group != "batch.sdewan.akraino.org" {
                return admission.Errored(
@@ -97,6 +97,10 @@ func (v *bucketPermissionValidator) Handle(ctx context.Context, req admission.Re
                obj = &FirewallDNAT{}
        case "FirewallSNAT":
                obj = &FirewallSNAT{}
+       case "IpsecProposal":
+               obj = &IpsecProposal{}
+       case "IpsecHost":
+               obj = &IpsecHost{}
        default:
                return admission.Errored(
                        http.StatusBadRequest,
diff --git a/platform/crd-ctrlr/src/api/v1alpha1/ipsechost_types.go b/platform/crd-ctrlr/src/api/v1alpha1/ipsechost_types.go
new file mode 100644 (file)
index 0000000..a51d685
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+       metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+type Connection struct {
+       Name           string   `json:"name"`
+       ConnectionType string   `json:"conn_type"`
+       Mode           string   `json:"mode"`
+       LocalSourceIp  string   `json:"local_sourceip,omitempty"`
+       LocalUpDown    string   `json:"local_updown,omitempty"`
+       LocalFirewall  string   `json:"local_firewall,omitempty"`
+       RemoteSubnet   string   `json:"remote_subnet,omitempty"`
+       RemoteSourceIp string   `json:"remote_sourceip,omitempty"`
+       RemoteUpDown   string   `json:"remote_updown,omitempty"`
+       RemoteFirewall string   `json:"remote_firewall,omitempty"`
+       CryptoProposal []string `json:"crypto_proposal,omitempty"`
+       Mark           string   `json:"mark,omitempty"`
+       IfId           string   `json:"if_id,omitempty"`
+}
+
+type IpsecHostSpec struct {
+       Name                 string       `json:"name,omitempty"`
+       Type                 string       `json:"type,omitempty"`
+       Remote               string       `json:"remote"`
+       AuthenticationMethod string       `json:"authentication_method"`
+       CryptoProposal       []string     `json:"crypto_proposal"`
+       LocalIdentifier      string       `json:"local_identifier,omitempty"`
+       RemoteIdentifier     string       `json:"remote_identifier,omitempty"`
+       ForceCryptoProposal  string       `json:"force_crypto_proposal,omitempty"`
+       PresharedKey         string       `json:"pre_shared_key,omitempty"`
+       LocalPublicCert      string       `json:"local_public_cert,omitempty"`
+       LocalPrivateCert     string       `json:"local_private_cert,omitempty"`
+       SharedCA             string       `json:"shared_ca,omitempty"`
+       Connections          []Connection `json:"connections"`
+}
+
+// +kubebuilder:object:root=true
+// +kubebuilder:subresource:status
+
+// IpsecHost is the Schema for the ipsechosts API
+type IpsecHost struct {
+       metav1.TypeMeta   `json:",inline"`
+       metav1.ObjectMeta `json:"metadata,omitempty"`
+
+       Spec   IpsecHostSpec `json:"spec,omitempty"`
+       Status SdewanStatus  `json:"status,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+
+// IpsecHostList contains a list of IpsecHost
+type IpsecHostList struct {
+       metav1.TypeMeta `json:",inline"`
+       metav1.ListMeta `json:"metadata,omitempty"`
+       Items           []IpsecHost `json:"items"`
+}
+
+func init() {
+       SchemeBuilder.Register(&IpsecHost{}, &IpsecHostList{})
+}
index 44dd03b..3e01a17 100644 (file)
@@ -52,6 +52,26 @@ func (in BucketPermission) DeepCopy() BucketPermission {
        return *out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Connection) DeepCopyInto(out *Connection) {
+       *out = *in
+       if in.CryptoProposal != nil {
+               in, out := &in.CryptoProposal, &out.CryptoProposal
+               *out = make([]string, len(*in))
+               copy(*out, *in)
+       }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Connection.
+func (in *Connection) DeepCopy() *Connection {
+       if in == nil {
+               return nil
+       }
+       out := new(Connection)
+       in.DeepCopyInto(out)
+       return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *FirewallDNAT) DeepCopyInto(out *FirewallDNAT) {
        *out = *in
@@ -466,6 +486,25 @@ func (in *IpsecProposal) DeepCopy() *IpsecProposal {
        return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *IpsecHost) DeepCopyInto(out *IpsecHost) {
+       *out = *in
+       out.TypeMeta = in.TypeMeta
+       in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+       in.Spec.DeepCopyInto(&out.Spec)
+       in.Status.DeepCopyInto(&out.Status)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IpsecHost.
+func (in *IpsecHost) DeepCopy() *IpsecHost {
+       if in == nil {
+               return nil
+       }
+       out := new(IpsecHost)
+       in.DeepCopyInto(out)
+       return out
+}
+
 // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
 func (in *IpsecProposal) DeepCopyObject() runtime.Object {
        if c := in.DeepCopy(); c != nil {
@@ -474,6 +513,14 @@ func (in *IpsecProposal) DeepCopyObject() runtime.Object {
        return nil
 }
 
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *IpsecHost) DeepCopyObject() runtime.Object {
+       if c := in.DeepCopy(); c != nil {
+               return c
+       }
+       return nil
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *IpsecProposalList) DeepCopyInto(out *IpsecProposalList) {
        *out = *in
@@ -488,6 +535,19 @@ func (in *IpsecProposalList) DeepCopyInto(out *IpsecProposalList) {
        }
 }
 
+func (in *IpsecHostList) DeepCopyInto(out *IpsecHostList) {
+       *out = *in
+       out.TypeMeta = in.TypeMeta
+       in.ListMeta.DeepCopyInto(&out.ListMeta)
+       if in.Items != nil {
+               in, out := &in.Items, &out.Items
+               *out = make([]IpsecHost, len(*in))
+               for i := range *in {
+                       (*in)[i].DeepCopyInto(&(*out)[i])
+               }
+       }
+}
+
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IpsecProposalList.
 func (in *IpsecProposalList) DeepCopy() *IpsecProposalList {
        if in == nil {
@@ -498,6 +558,16 @@ func (in *IpsecProposalList) DeepCopy() *IpsecProposalList {
        return out
 }
 
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IpsecHostList.
+func (in *IpsecHostList) DeepCopy() *IpsecHostList {
+       if in == nil {
+               return nil
+       }
+       out := new(IpsecHostList)
+       in.DeepCopyInto(out)
+       return out
+}
+
 // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
 func (in *IpsecProposalList) DeepCopyObject() runtime.Object {
        if c := in.DeepCopy(); c != nil {
@@ -506,6 +576,14 @@ func (in *IpsecProposalList) DeepCopyObject() runtime.Object {
        return nil
 }
 
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *IpsecHostList) DeepCopyObject() runtime.Object {
+       if c := in.DeepCopy(); c != nil {
+               return c
+       }
+       return nil
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *IpsecProposalSpec) DeepCopyInto(out *IpsecProposalSpec) {
        *out = *in
@@ -521,6 +599,32 @@ func (in *IpsecProposalSpec) DeepCopy() *IpsecProposalSpec {
        return out
 }
 
+func (in *IpsecHostSpec) DeepCopyInto(out *IpsecHostSpec) {
+       *out = *in
+       if in.CryptoProposal != nil {
+               in, out := &in.CryptoProposal, &out.CryptoProposal
+               *out = make([]string, len(*in))
+               copy(*out, *in)
+       }
+       if in.Connections != nil {
+               in, out := &in.Connections, &out.Connections
+               *out = make([]Connection, len(*in))
+               for i := range *in {
+                       (*in)[i].DeepCopyInto(&(*out)[i])
+               }
+       }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IpsecHostSpec.
+func (in *IpsecHostSpec) DeepCopy() *IpsecHostSpec {
+       if in == nil {
+               return nil
+       }
+       out := new(IpsecHostSpec)
+       in.DeepCopyInto(out)
+       return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Mwan3Policy) DeepCopyInto(out *Mwan3Policy) {
        *out = *in
diff --git a/platform/crd-ctrlr/src/config/crd/bases/batch.sdewan.akraino.org_ipsechosts.yaml b/platform/crd-ctrlr/src/config/crd/bases/batch.sdewan.akraino.org_ipsechosts.yaml
new file mode 100644 (file)
index 0000000..da2c63d
--- /dev/null
@@ -0,0 +1,132 @@
+
+---
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.2.5
+  creationTimestamp: null
+  name: ipsechosts.batch.sdewan.akraino.org
+spec:
+  group: batch.sdewan.akraino.org
+  names:
+    kind: IpsecHost
+    listKind: IpsecHostList
+    plural: ipsechosts
+    singular: ipsechost
+  scope: Namespaced
+  subresources:
+    status: {}
+  validation:
+    openAPIV3Schema:
+      description: IpsecHost is the Schema for the ipsechosts API
+      properties:
+        apiVersion:
+          description: 'APIVersion defines the versioned schema of this representation
+            of an object. Servers should convert recognized schemas to the latest
+            internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+          type: string
+        kind:
+          description: 'Kind is a string value representing the REST resource this
+            object represents. Servers may infer this from the endpoint the client
+            submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+          type: string
+        metadata:
+          type: object
+        spec:
+          properties:
+            authentication_method:
+              type: string
+            connections:
+              items:
+                properties:
+                  crypto_proposal:
+                    items:
+                      type: string
+                    type: array
+                  if_id:
+                    type: string
+                  local_firewall:
+                    type: string
+                  local_sourceip:
+                    type: string
+                  local_updown:
+                    type: string
+                  mark:
+                    type: string
+                  mode:
+                    type: string
+                  name:
+                    type: string
+                  remote_firewall:
+                    type: string
+                  remote_sourceip:
+                    type: string
+                  remote_subnet:
+                    type: string
+                  remote_updown:
+                    type: string
+                  type:
+                    type: string
+                required:
+                - mode
+                - name
+                - type
+                type: object
+              type: array
+            crypto_proposal:
+              items:
+                type: string
+              type: array
+            force_crypto_proposal:
+              type: string
+            local_identifier:
+              type: string
+            local_private_cert:
+              type: string
+            local_public_cert:
+              type: string
+            name:
+              type: string
+            pre_shared_key:
+              type: string
+            remote:
+              type: string
+            remote_identifier:
+              type: string
+            shared_ca:
+              type: string
+          required:
+          - authentication_method
+          - connections
+          - crypto_proposal
+          - remote
+          type: object
+        status:
+          description: status subsource used for Sdewan rule CRDs
+          properties:
+            appliedGeneration:
+              format: int64
+              type: integer
+            appliedTime:
+              format: date-time
+              type: string
+            message:
+              type: string
+            state:
+              type: string
+          required:
+          - state
+          type: object
+      type: object
+  version: v1alpha1
+  versions:
+  - name: v1alpha1
+    served: true
+    storage: true
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
index f3138ae..1a470d7 100644 (file)
@@ -10,6 +10,7 @@ resources:
 - bases/batch.sdewan.akraino.org_firewalldnats.yaml
 - bases/batch.sdewan.akraino.org_firewallforwardings.yaml
 - bases/batch.sdewan.akraino.org_ipsecproposals.yaml
+- bases/batch.sdewan.akraino.org_ipsechosts.yaml
 # +kubebuilder:scaffold:crdkustomizeresource
 
 patchesStrategicMerge:
@@ -23,6 +24,7 @@ patchesStrategicMerge:
 #- patches/webhook_in_firewalldnats.yaml
 #- patches/webhook_in_firewallforwardings.yaml
 #- patches/webhook_in_ipsecproposals.yaml
+#- patches/webhook_in_ipsechosts.yaml
 # +kubebuilder:scaffold:crdkustomizewebhookpatch
 
 # [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix.
@@ -35,6 +37,7 @@ patchesStrategicMerge:
 #- patches/cainjection_in_firewalldnats.yaml
 #- patches/cainjection_in_firewallforwardings.yaml
 #- patches/cainjection_in_ipsecproposals.yaml
+#- patches/cainjection_in_ipsechosts.yaml
 # +kubebuilder:scaffold:crdkustomizecainjectionpatch
 
 # the following config is for teaching kustomize how to do kustomization for CRDs.
diff --git a/platform/crd-ctrlr/src/config/crd/patches/cainjection_in_ipsechosts.yaml b/platform/crd-ctrlr/src/config/crd/patches/cainjection_in_ipsechosts.yaml
new file mode 100644 (file)
index 0000000..e2f2a8d
--- /dev/null
@@ -0,0 +1,8 @@
+# The following patch adds a directive for certmanager to inject CA into the CRD
+# CRD conversion requires k8s 1.13 or later.
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
+  name: ipsechosts.batch.sdewan.akraino.org
diff --git a/platform/crd-ctrlr/src/config/crd/patches/webhook_in_ipsechosts.yaml b/platform/crd-ctrlr/src/config/crd/patches/webhook_in_ipsechosts.yaml
new file mode 100644 (file)
index 0000000..b5d2c56
--- /dev/null
@@ -0,0 +1,15 @@
+# The following patch enables conversion webhook for CRD
+# CRD conversion requires k8s 1.13 or later.
+apiVersion: apiextensions.k8s.io/v1beta1
+kind: CustomResourceDefinition
+metadata:
+  name: ipsechosts.batch.sdewan.akraino.org
+spec:
+  conversion:
+    strategy: Webhook
+    webhookClientConfig:
+      caBundle: Cg==
+      service:
+        namespace: system
+        name: webhook-service
+        path: /convert
index 17651a7..cefecce 100644 (file)
@@ -28,6 +28,8 @@ webhooks:
     - firewallrules
     - firewallsnats
     - firewallzones
+    - ipsechosts
+    - ipsecproposals
     scope: '*'
   sideEffects: Unknown
   timeoutSeconds: 30
index 9c48bc5..3cb2ecf 100644 (file)
@@ -142,6 +142,26 @@ rules:
   - get
   - patch
   - update
+- apiGroups:
+  - batch.sdewan.akraino.org
+  resources:
+  - ipsechosts
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - batch.sdewan.akraino.org
+  resources:
+  - ipsechosts/status
+  verbs:
+  - get
+  - patch
+  - update
 - apiGroups:
   - batch.sdewan.akraino.org
   resources:
diff --git a/platform/crd-ctrlr/src/config/samples/batch_v1alpha1_ipsechost.yaml b/platform/crd-ctrlr/src/config/samples/batch_v1alpha1_ipsechost.yaml
new file mode 100644 (file)
index 0000000..bfde59b
--- /dev/null
@@ -0,0 +1,28 @@
+apiVersion: batch.sdewan.akraino.org/v1alpha1
+kind: IpsecHost
+metadata:
+  name: ipsechost
+  namespace: default
+  labels:
+    sdewanPurpose: cnf1
+spec:
+    name: hostA
+    type: "VTI-based"
+    remote: 10.10.10.35
+    pre_shared_key: test_key
+    authentication_method: psk
+    local_identifier: host
+    remote_identifier: Hub
+    crypto_proposal:
+      - ipsecproposal
+    force_crypto_proposal: "0"
+    connections:
+    - name: connA
+      conn_type: tunnel
+      mode: start
+      mark: "42"
+      local_sourceip: "%config"
+      remote_subnet: 192.168.1.1/24,10.10.10.35/32
+      crypto_proposal:
+        - ipsecproposal
+
index 99354b0..9374f80 100644 (file)
@@ -31,4 +31,5 @@ webhooks:
     - firewallrules
     - firewallsnats
     - firewalldnats
-    - ipsecproposal
+    - ipsecproposals
+    - ipsechosts
diff --git a/platform/crd-ctrlr/src/controllers/ipsechost_controller.go b/platform/crd-ctrlr/src/controllers/ipsechost_controller.go
new file mode 100644 (file)
index 0000000..2c3fbf1
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package controllers
+
+import (
+       "context"
+       "reflect"
+
+       "github.com/go-logr/logr"
+       appsv1 "k8s.io/api/apps/v1"
+       "k8s.io/apimachinery/pkg/runtime"
+       ctrl "sigs.k8s.io/controller-runtime"
+       "sigs.k8s.io/controller-runtime/pkg/builder"
+       "sigs.k8s.io/controller-runtime/pkg/client"
+       "sigs.k8s.io/controller-runtime/pkg/predicate"
+
+       batchv1alpha1 "sdewan.akraino.org/sdewan/api/v1alpha1"
+       "sdewan.akraino.org/sdewan/openwrt"
+)
+
+var ipsecHostHandler = new(IpsecHostHandler)
+
+type IpsecHostHandler struct {
+}
+
+func (m *IpsecHostHandler) GetType() string {
+       return "IpsecHost"
+}
+
+func (m *IpsecHostHandler) GetName(instance runtime.Object) string {
+       host := instance.(*batchv1alpha1.IpsecHost)
+       return host.Name
+}
+
+func (m *IpsecHostHandler) GetFinalizer() string {
+       return "ipsec.host.finalizers.sdewan.akraino.org"
+}
+
+func (m *IpsecHostHandler) GetInstance(r client.Client, ctx context.Context, req ctrl.Request) (runtime.Object, error) {
+       instance := &batchv1alpha1.IpsecHost{}
+       err := r.Get(ctx, req.NamespacedName, instance)
+       return instance, err
+}
+
+func (m *IpsecHostHandler) Convert(instance runtime.Object, deployment appsv1.Deployment) (openwrt.IOpenWrtObject, error) {
+       host := instance.(*batchv1alpha1.IpsecHost)
+       numOfConn := len(host.Spec.Connections)
+       conn := host.Spec.Connections
+       openwrtConn := make([]openwrt.SdewanIpsecConnection, numOfConn)
+       for i := 0; i < numOfConn; i++ {
+               openwrtConn[i] = openwrt.SdewanIpsecConnection{
+                       Name:           conn[i].Name,
+                       ConnType:       conn[i].ConnectionType,
+                       Mode:           conn[i].Mode,
+                       LocalSourceip:  conn[i].LocalSourceIp,
+                       LocalUpdown:    conn[i].LocalUpDown,
+                       LocalFirewall:  conn[i].LocalFirewall,
+                       RemoteSubnet:   conn[i].RemoteSubnet,
+                       RemoteSourceip: conn[i].RemoteSourceIp,
+                       RemoteUpdown:   conn[i].RemoteUpDown,
+                       RemoteFirewall: conn[i].RemoteFirewall,
+                       CryptoProposal: conn[i].CryptoProposal,
+                       Mark:           conn[i].Mark,
+                       IfId:           conn[i].IfId,
+               }
+       }
+       hostObject := openwrt.SdewanIpsecRemote{
+               Name:                 host.Name,
+               Gateway:              host.Spec.Remote,
+               Type:                 host.Spec.Type,
+               AuthenticationMethod: host.Spec.AuthenticationMethod,
+               PreSharedKey:         host.Spec.PresharedKey,
+               LocalIdentifier:      host.Spec.LocalIdentifier,
+               RemoteIdentifier:     host.Spec.RemoteIdentifier,
+               CryptoProposal:       host.Spec.CryptoProposal,
+               ForceCryptoProposal:  host.Spec.ForceCryptoProposal,
+               LocalPublicCert:      host.Spec.LocalPublicCert,
+               LocalPrivateCert:     host.Spec.LocalPrivateCert,
+               SharedCa:             host.Spec.SharedCA,
+               Connections:          openwrtConn,
+       }
+       return &hostObject, nil
+}
+
+func (m *IpsecHostHandler) IsEqual(instance1 openwrt.IOpenWrtObject, instance2 openwrt.IOpenWrtObject) bool {
+       host1 := instance1.(*openwrt.SdewanIpsecRemote)
+       host2 := instance2.(*openwrt.SdewanIpsecRemote)
+       return reflect.DeepEqual(*host1, *host2)
+}
+
+func (m *IpsecHostHandler) GetObject(clientInfo *openwrt.OpenwrtClientInfo, name string) (openwrt.IOpenWrtObject, error) {
+       openwrtClient := openwrt.GetOpenwrtClient(*clientInfo)
+       ipsec := openwrt.IpsecClient{OpenwrtClient: openwrtClient}
+       ret, err := ipsec.GetRemote(name)
+       return ret, err
+}
+
+func (m *IpsecHostHandler) CreateObject(clientInfo *openwrt.OpenwrtClientInfo, instance openwrt.IOpenWrtObject) (openwrt.IOpenWrtObject, error) {
+       openwrtClient := openwrt.GetOpenwrtClient(*clientInfo)
+       ipsec := openwrt.IpsecClient{OpenwrtClient: openwrtClient}
+       host := instance.(*openwrt.SdewanIpsecRemote)
+       return ipsec.CreateRemote(*host)
+}
+
+func (m *IpsecHostHandler) UpdateObject(clientInfo *openwrt.OpenwrtClientInfo, instance openwrt.IOpenWrtObject) (openwrt.IOpenWrtObject, error) {
+       openwrtClient := openwrt.GetOpenwrtClient(*clientInfo)
+       ipsec := openwrt.IpsecClient{OpenwrtClient: openwrtClient}
+       host := instance.(*openwrt.SdewanIpsecRemote)
+       return ipsec.UpdateRemote(*host)
+}
+
+func (m *IpsecHostHandler) DeleteObject(clientInfo *openwrt.OpenwrtClientInfo, name string) error {
+       openwrtClient := openwrt.GetOpenwrtClient(*clientInfo)
+       ipsec := openwrt.IpsecClient{OpenwrtClient: openwrtClient}
+       return ipsec.DeleteRemote(name)
+}
+
+func (m *IpsecHostHandler) Restart(clientInfo *openwrt.OpenwrtClientInfo) (bool, error) {
+       openwrtClient := openwrt.GetOpenwrtClient(*clientInfo)
+       service := openwrt.ServiceClient{OpenwrtClient: openwrtClient}
+       return service.ExecuteService("ipsec", "restart")
+}
+
+// IpsecHostReconciler reconciles a IpsecHost object
+type IpsecHostReconciler struct {
+       client.Client
+       Log    logr.Logger
+       Scheme *runtime.Scheme
+}
+
+// +kubebuilder:rbac:groups=batch.sdewan.akraino.org,resources=ipsechosts,verbs=get;list;watch;create;update;patch;delete
+// +kubebuilder:rbac:groups=batch.sdewan.akraino.org,resources=ipsechosts/status,verbs=get;update;patch
+
+func (r *IpsecHostReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
+       return ProcessReconcile(r, r.Log, req, ipsecHostHandler)
+}
+
+func (r *IpsecHostReconciler) SetupWithManager(mgr ctrl.Manager) error {
+       ps := builder.WithPredicates(predicate.GenerationChangedPredicate{})
+       return ctrl.NewControllerManagedBy(mgr).
+               For(&batchv1alpha1.IpsecHost{}, ps).
+               Complete(r)
+}
index 176be3c..500a585 100644 (file)
@@ -83,6 +83,9 @@ var _ = BeforeSuite(func(done Done) {
        err = batchv1alpha1.AddToScheme(scheme.Scheme)
        Expect(err).NotTo(HaveOccurred())
 
+       err = batchv1alpha1.AddToScheme(scheme.Scheme)
+       Expect(err).NotTo(HaveOccurred())
+
        // +kubebuilder:scaffold:scheme
 
        k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
index 04cfa65..e2f903d 100644 (file)
@@ -159,6 +159,7 @@ func main() {
                setupLog.Error(err, "unable to create controller", "controller", "FirewallForwarding")
                os.Exit(1)
        }
+
        if err = (&controllers.IpsecProposalReconciler{
                Client: mgr.GetClient(),
                Log:    ctrl.Log.WithName("controllers").WithName("IpsecProposal"),
@@ -167,6 +168,15 @@ func main() {
                setupLog.Error(err, "unable to create controller", "controller", "IpsecProposal")
                os.Exit(1)
        }
+
+       if err = (&controllers.IpsecHostReconciler{
+               Client: mgr.GetClient(),
+               Log:    ctrl.Log.WithName("controllers").WithName("IpsecHost"),
+               Scheme: mgr.GetScheme(),
+       }).SetupWithManager(mgr); err != nil {
+               setupLog.Error(err, "unable to create controller", "controller", "IpsecHost")
+               os.Exit(1)
+       }
        // +kubebuilder:scaffold:builder
 
        setupLog.Info("starting manager")
index 788e909..b60fbba 100644 (file)
@@ -28,10 +28,10 @@ func (o *SdewanIpsecProposal) GetName() string {
        return o.Name
 }
 
-// Sites
+// Remotes
 type SdewanIpsecConnection struct {
        Name           string   `json:"name"`
-       Type           string   `json:"type"`
+       ConnType       string   `json:"conn_type"`
        Mode           string   `json:"mode"`
        LocalSubnet    string   `json:"local_subnet"`
        LocalNat       string   `json:"local_nat"`
@@ -43,10 +43,13 @@ type SdewanIpsecConnection struct {
        RemoteUpdown   string   `json:"remote_updown"`
        RemoteFirewall string   `json:"remote_firewall"`
        CryptoProposal []string `json:"crypto_proposal"`
+       Mark           string   `json:"mark"`
+       IfId           string   `json:"if_id"`
 }
 
-type SdewanIpsecSite struct {
+type SdewanIpsecRemote struct {
        Name                 string                  `json:"name"`
+       Type                 string                  `json:"type"`
        Gateway              string                  `json:"gateway"`
        PreSharedKey         string                  `json:"pre_shared_key"`
        AuthenticationMethod string                  `json:"authentication_method"`
@@ -60,8 +63,12 @@ type SdewanIpsecSite struct {
        Connections          []SdewanIpsecConnection `json:"connections"`
 }
 
-type SdewanIpsecSites struct {
-       Sites []SdewanIpsecSite `json:"sites"`
+type SdewanIpsecRemotes struct {
+       Remotes []SdewanIpsecRemote `json:"remotes"`
+}
+
+func (o *SdewanIpsecRemote) GetName() string {
+       return o.Name
 }
 
 // Proposal APIs
@@ -150,65 +157,65 @@ func (m *IpsecClient) UpdateProposal(proposal SdewanIpsecProposal) (*SdewanIpsec
        return &sdewanIpsecProposal, nil
 }
 
-// Site APIs
-// get sites
-func (f *IpsecClient) GetSites() (*SdewanIpsecSites, error) {
+// Remote APIs
+// get remotes
+func (f *IpsecClient) GetRemotes() (*SdewanIpsecRemotes, error) {
        var response string
        var err error
-       response, err = f.OpenwrtClient.Get(ipsecBaseURL + "sites")
+       response, err = f.OpenwrtClient.Get(ipsecBaseURL + "remotes")
        if err != nil {
                return nil, err
        }
 
-       var sdewanIpsecSites SdewanIpsecSites
-       err = json.Unmarshal([]byte(response), &sdewanIpsecSites)
+       var sdewanIpsecRemotes SdewanIpsecRemotes
+       err = json.Unmarshal([]byte(response), &sdewanIpsecRemotes)
        if err != nil {
                return nil, err
        }
 
-       return &sdewanIpsecSites, nil
+       return &sdewanIpsecRemotes, nil
 }
 
-// get site
-func (m *IpsecClient) GetSite(site string) (*SdewanIpsecSite, error) {
+// get remote
+func (m *IpsecClient) GetRemote(remote string) (*SdewanIpsecRemote, error) {
        var response string
        var err error
-       response, err = m.OpenwrtClient.Get(ipsecBaseURL + "sites/" + site)
+       response, err = m.OpenwrtClient.Get(ipsecBaseURL + "remotes/" + remote)
        if err != nil {
                return nil, err
        }
 
-       var sdewanIpsecSite SdewanIpsecSite
-       err = json.Unmarshal([]byte(response), &sdewanIpsecSite)
+       var sdewanIpsecRemote SdewanIpsecRemote
+       err = json.Unmarshal([]byte(response), &sdewanIpsecRemote)
        if err != nil {
                return nil, err
        }
 
-       return &sdewanIpsecSite, nil
+       return &sdewanIpsecRemote, nil
 }
 
-// create site
-func (m *IpsecClient) CreateSite(site SdewanIpsecSite) (*SdewanIpsecSite, error) {
+// create remote
+func (m *IpsecClient) CreateRemote(remote SdewanIpsecRemote) (*SdewanIpsecRemote, error) {
        var response string
        var err error
-       site_obj, _ := json.Marshal(site)
-       response, err = m.OpenwrtClient.Post(ipsecBaseURL+"sites", string(site_obj))
+       remote_obj, _ := json.Marshal(remote)
+       response, err = m.OpenwrtClient.Post(ipsecBaseURL+"remotes", string(remote_obj))
        if err != nil {
                return nil, err
        }
 
-       var sdewanIpsecSite SdewanIpsecSite
-       err = json.Unmarshal([]byte(response), &sdewanIpsecSite)
+       var sdewanIpsecRemote SdewanIpsecRemote
+       err = json.Unmarshal([]byte(response), &sdewanIpsecRemote)
        if err != nil {
                return nil, err
        }
 
-       return &sdewanIpsecSite, nil
+       return &sdewanIpsecRemote, nil
 }
 
-// delete site
-func (m *IpsecClient) DeleteSite(site_name string) error {
-       _, err := m.OpenwrtClient.Delete(ipsecBaseURL + "sites/" + site_name)
+// delete remote
+func (m *IpsecClient) DeleteRemote(remote_name string) error {
+       _, err := m.OpenwrtClient.Delete(ipsecBaseURL + "remotes/" + remote_name)
        if err != nil {
                return err
        }
@@ -216,22 +223,22 @@ func (m *IpsecClient) DeleteSite(site_name string) error {
        return nil
 }
 
-// update site
-func (m *IpsecClient) UpdateSite(site SdewanIpsecSite) (*SdewanIpsecSite, error) {
+// update remote
+func (m *IpsecClient) UpdateRemote(remote SdewanIpsecRemote) (*SdewanIpsecRemote, error) {
        var response string
        var err error
-       site_obj, _ := json.Marshal(site)
-       site_name := site.Name
-       response, err = m.OpenwrtClient.Put(ipsecBaseURL+"sites/"+site_name, string(site_obj))
+       remote_obj, _ := json.Marshal(remote)
+       remote_name := remote.Name
+       response, err = m.OpenwrtClient.Put(ipsecBaseURL+"remotes/"+remote_name, string(remote_obj))
        if err != nil {
                return nil, err
        }
 
-       var sdewanIpsecSite SdewanIpsecSite
-       err = json.Unmarshal([]byte(response), &sdewanIpsecSite)
+       var sdewanIpsecRemote SdewanIpsecRemote
+       err = json.Unmarshal([]byte(response), &sdewanIpsecRemote)
        if err != nil {
                return nil, err
        }
 
-       return &sdewanIpsecSite, nil
+       return &sdewanIpsecRemote, nil
 }