From: Ruoyu Date: Tue, 9 Jun 2020 00:37:18 +0000 (+0800) Subject: Add CRD for IpsecHost X-Git-Tag: v1.0~24^2 X-Git-Url: https://gerrit.akraino.org/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F53%2F3553%2F8;p=icn%2Fsdwan.git Add CRD for IpsecHost * 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 --- diff --git a/platform/cnf/src/Dockerfile_1806_mwan3.tpl b/platform/cnf/src/Dockerfile_1806_mwan3.tpl index b62e627..658f463 100644 --- a/platform/cnf/src/Dockerfile_1806_mwan3.tpl +++ b/platform/cnf/src/Dockerfile_1806_mwan3.tpl @@ -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= diff --git a/platform/cnf/src/Dockerfile_1806_mwan3_noproxy.tpl b/platform/cnf/src/Dockerfile_1806_mwan3_noproxy.tpl index 1163ed4..0fe098b 100644 --- a/platform/cnf/src/Dockerfile_1806_mwan3_noproxy.tpl +++ b/platform/cnf/src/Dockerfile_1806_mwan3_noproxy.tpl @@ -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 index 0000000..6b906a3 --- /dev/null +++ b/platform/cnf/src/ipsec_exec @@ -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 +} diff --git a/platform/cnf/src/rest_v1/ipsec_rest.lua b/platform/cnf/src/rest_v1/ipsec_rest.lua index 837b5e7..5f0e7a0 100644 --- a/platform/cnf/src/rest_v1/ipsec_rest.lua +++ b/platform/cnf/src/rest_v1/ipsec_rest.lua @@ -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 diff --git a/platform/crd-ctrlr/README.md b/platform/crd-ctrlr/README.md index 5699c92..78267eb 100644 --- a/platform/crd-ctrlr/README.md +++ b/platform/crd-ctrlr/README.md @@ -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 diff --git a/platform/crd-ctrlr/examples/sdewan-controller.yaml b/platform/crd-ctrlr/examples/sdewan-controller.yaml index 78c0662..af0694b 100644 --- a/platform/crd-ctrlr/examples/sdewan-controller.yaml +++ b/platform/crd-ctrlr/examples/sdewan-controller.yaml @@ -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 diff --git a/platform/crd-ctrlr/src/PROJECT b/platform/crd-ctrlr/src/PROJECT index 34a442e..e282bff 100644 --- a/platform/crd-ctrlr/src/PROJECT +++ b/platform/crd-ctrlr/src/PROJECT @@ -25,4 +25,7 @@ resources: - group: batch kind: IpsecProposal version: v1alpha1 +- group: batch + kind: IpsecHost + version: v1alpha1 version: "2" diff --git a/platform/crd-ctrlr/src/api/v1alpha1/bucket_permission_webhook.go b/platform/crd-ctrlr/src/api/v1alpha1/bucket_permission_webhook.go index 722ef99..adf9c11 100644 --- a/platform/crd-ctrlr/src/api/v1alpha1/bucket_permission_webhook.go +++ b/platform/crd-ctrlr/src/api/v1alpha1/bucket_permission_webhook.go @@ -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 index 0000000..a51d685 --- /dev/null +++ b/platform/crd-ctrlr/src/api/v1alpha1/ipsechost_types.go @@ -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{}) +} diff --git a/platform/crd-ctrlr/src/api/v1alpha1/zz_generated.deepcopy.go b/platform/crd-ctrlr/src/api/v1alpha1/zz_generated.deepcopy.go index 44dd03b..3e01a17 100644 --- a/platform/crd-ctrlr/src/api/v1alpha1/zz_generated.deepcopy.go +++ b/platform/crd-ctrlr/src/api/v1alpha1/zz_generated.deepcopy.go @@ -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 index 0000000..da2c63d --- /dev/null +++ b/platform/crd-ctrlr/src/config/crd/bases/batch.sdewan.akraino.org_ipsechosts.yaml @@ -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: [] diff --git a/platform/crd-ctrlr/src/config/crd/kustomization.yaml b/platform/crd-ctrlr/src/config/crd/kustomization.yaml index f3138ae..1a470d7 100644 --- a/platform/crd-ctrlr/src/config/crd/kustomization.yaml +++ b/platform/crd-ctrlr/src/config/crd/kustomization.yaml @@ -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 index 0000000..e2f2a8d --- /dev/null +++ b/platform/crd-ctrlr/src/config/crd/patches/cainjection_in_ipsechosts.yaml @@ -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 index 0000000..b5d2c56 --- /dev/null +++ b/platform/crd-ctrlr/src/config/crd/patches/webhook_in_ipsechosts.yaml @@ -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 diff --git a/platform/crd-ctrlr/src/config/local/webhook_config.yaml b/platform/crd-ctrlr/src/config/local/webhook_config.yaml index 17651a7..cefecce 100644 --- a/platform/crd-ctrlr/src/config/local/webhook_config.yaml +++ b/platform/crd-ctrlr/src/config/local/webhook_config.yaml @@ -28,6 +28,8 @@ webhooks: - firewallrules - firewallsnats - firewallzones + - ipsechosts + - ipsecproposals scope: '*' sideEffects: Unknown timeoutSeconds: 30 diff --git a/platform/crd-ctrlr/src/config/rbac/role.yaml b/platform/crd-ctrlr/src/config/rbac/role.yaml index 9c48bc5..3cb2ecf 100644 --- a/platform/crd-ctrlr/src/config/rbac/role.yaml +++ b/platform/crd-ctrlr/src/config/rbac/role.yaml @@ -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 index 0000000..bfde59b --- /dev/null +++ b/platform/crd-ctrlr/src/config/samples/batch_v1alpha1_ipsechost.yaml @@ -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 + diff --git a/platform/crd-ctrlr/src/config/webhook/manifests.yaml b/platform/crd-ctrlr/src/config/webhook/manifests.yaml index 99354b0..9374f80 100644 --- a/platform/crd-ctrlr/src/config/webhook/manifests.yaml +++ b/platform/crd-ctrlr/src/config/webhook/manifests.yaml @@ -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 index 0000000..2c3fbf1 --- /dev/null +++ b/platform/crd-ctrlr/src/controllers/ipsechost_controller.go @@ -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) +} diff --git a/platform/crd-ctrlr/src/controllers/suite_test.go b/platform/crd-ctrlr/src/controllers/suite_test.go index 176be3c..500a585 100644 --- a/platform/crd-ctrlr/src/controllers/suite_test.go +++ b/platform/crd-ctrlr/src/controllers/suite_test.go @@ -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}) diff --git a/platform/crd-ctrlr/src/main.go b/platform/crd-ctrlr/src/main.go index 04cfa65..e2f903d 100644 --- a/platform/crd-ctrlr/src/main.go +++ b/platform/crd-ctrlr/src/main.go @@ -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") diff --git a/platform/crd-ctrlr/src/openwrt/ipsec.go b/platform/crd-ctrlr/src/openwrt/ipsec.go index 788e909..b60fbba 100644 --- a/platform/crd-ctrlr/src/openwrt/ipsec.go +++ b/platform/crd-ctrlr/src/openwrt/ipsec.go @@ -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 }