cns/middlewares/k8sSwiftV2_windows.go (188 lines of code) (raw):

package middlewares import ( "context" "encoding/json" "fmt" "net/netip" "github.com/Azure/azure-container-networking/cns" "github.com/Azure/azure-container-networking/cns/logger" "github.com/Azure/azure-container-networking/cns/middlewares/utils" "github.com/Azure/azure-container-networking/cns/types" "github.com/Azure/azure-container-networking/crd/multitenancy/api/v1alpha1" "github.com/Azure/azure-container-networking/network/policy" "github.com/pkg/errors" ) var defaultDenyEgressPolicy policy.Policy = mustGetEndpointPolicy(cns.DirectionTypeOut) var defaultDenyIngressPolicy policy.Policy = mustGetEndpointPolicy(cns.DirectionTypeIn) const ( defaultGateway = "0.0.0.0" ) // for AKS L1VH, do not set default route on infraNIC to avoid customer pod reaching all infra vnet services // default route is set for secondary interface NIC(i.e,delegatedNIC) func (k *K8sSWIFTv2Middleware) setRoutes(podIPInfo *cns.PodIpInfo) error { if podIPInfo.NICType == cns.InfraNIC { // as a workaround, HNS will not set this dummy default route(0.0.0.0/0, nexthop:0.0.0.0) on infraVnet interface eth0 // the only usage for this dummy default is to bypass HNS setting default route on eth0 // TODO: Remove this once HNS fix is ready route := cns.Route{ IPAddress: "0.0.0.0/0", GatewayIPAddress: defaultGateway, } podIPInfo.Routes = append(podIPInfo.Routes, route) // Windows CNS middleware sets the infra routes(infravnet and service cidrs) to infraNIC interface for the podIPInfo used in SWIFT V2 Windows scenario infraRoutes, err := k.getInfraRoutes(podIPInfo) if err != nil { return errors.Wrap(err, "failed to set routes for infraNIC interface") } podIPInfo.Routes = append(podIPInfo.Routes, infraRoutes...) podIPInfo.SkipDefaultRoutes = true } return nil } // assignSubnetPrefixLengthFields will assign the subnet-prefix length to some fields of podipinfo // this is required for the windows scenario so that HNS programming is successful for pods func (k *K8sSWIFTv2Middleware) assignSubnetPrefixLengthFields(podIPInfo *cns.PodIpInfo, interfaceInfo v1alpha1.InterfaceInfo, ip string) error { // Parse MTPNC SubnetAddressSpace to get the subnet prefix length subnet, subnetPrefix, err := utils.ParseIPAndPrefix(interfaceInfo.SubnetAddressSpace) if err != nil { return errors.Wrap(err, "failed to parse mtpnc subnetAddressSpace prefix") } // assign the subnet-prefix length to all fields in podipinfo podIPInfo.PodIPConfig.PrefixLength = uint8(subnetPrefix) podIPInfo.HostPrimaryIPInfo = cns.HostIPInfo{ Gateway: interfaceInfo.GatewayIP, PrimaryIP: ip, Subnet: interfaceInfo.SubnetAddressSpace, } podIPInfo.NetworkContainerPrimaryIPConfig = cns.IPConfiguration{ IPSubnet: cns.IPSubnet{ IPAddress: subnet, PrefixLength: uint8(subnetPrefix), }, GatewayIPAddress: interfaceInfo.GatewayIP, } return nil } // add default route with gateway IP to podIPInfo func (k *K8sSWIFTv2Middleware) addDefaultRoute(podIPInfo *cns.PodIpInfo, gwIP string) { route := cns.Route{ IPAddress: "0.0.0.0/0", GatewayIPAddress: gwIP, } podIPInfo.Routes = append(podIPInfo.Routes, route) } func mustGetEndpointPolicy(direction string) policy.Policy { endpointPolicy, err := getEndpointPolicy(direction) if err != nil { panic(err) } return endpointPolicy } // get policy of type endpoint policy given the params func getEndpointPolicy(direction string) (policy.Policy, error) { endpointPolicy, err := createEndpointPolicy(direction) if err != nil { return policy.Policy{}, fmt.Errorf("error creating endpoint policy: %w", err) } additionalArgs := policy.Policy{ Type: policy.EndpointPolicy, Data: endpointPolicy, } return additionalArgs, nil } // create policy given the params func createEndpointPolicy(direction string) ([]byte, error) { endpointPolicy := struct { Type string `json:"Type"` Action string `json:"Action"` Direction string `json:"Direction"` Priority int `json:"Priority"` }{ Type: string(policy.ACLPolicy), Action: cns.ActionTypeBlock, Direction: direction, Priority: 10_000, } rawPolicy, err := json.Marshal(endpointPolicy) if err != nil { return nil, fmt.Errorf("error marshalling policy to json, err is: %w", err) } return rawPolicy, nil } // IPConfigsRequestHandlerWrapper is the middleware function for handling SWIFT v2 IP configs requests for AKS-SWIFT. This function wrapped the default SWIFT request // and release IP configs handlers. func (k *K8sSWIFTv2Middleware) IPConfigsRequestHandlerWrapper(defaultHandler, failureHandler cns.IPConfigsHandlerFunc) cns.IPConfigsHandlerFunc { return func(ctx context.Context, req cns.IPConfigsRequest) (*cns.IPConfigsResponse, error) { podInfo, respCode, message := k.GetPodInfoForIPConfigsRequest(ctx, &req) if respCode != types.Success { return &cns.IPConfigsResponse{ Response: cns.Response{ ReturnCode: respCode, Message: message, }, }, errors.New("failed to validate IP configs request") } ipConfigsResp, err := defaultHandler(ctx, req) // If the pod is not v2, return the response from the handler if !req.SecondaryInterfacesExist { return ipConfigsResp, err } // Get MTPNC mtpnc, respCode, message := k.getMTPNC(ctx, podInfo) if respCode != types.Success { return &cns.IPConfigsResponse{ Response: cns.Response{ ReturnCode: respCode, Message: message, }, }, errors.New("failed to validate IP configs request") } // GetDefaultDenyBool takes in mtpnc and returns the value of defaultDenyACLBool from it defaultDenyACLBool := GetDefaultDenyBool(mtpnc) // ipConfigsResp has infra IP configs -> if defaultDenyACLbool is enabled, add the default deny endpoint policies as a property in PodIpInfo for i := range ipConfigsResp.PodIPInfo { ipInfo := &ipConfigsResp.PodIPInfo[i] // there will be no pod connectivity to and from those pods if defaultDenyACLBool && ipInfo.NICType == cns.InfraNIC { ipInfo.EndpointPolicies = append(ipInfo.EndpointPolicies, defaultDenyEgressPolicy, defaultDenyIngressPolicy) break } } // If the pod is v2, get the infra IP configs from the handler first and then add the SWIFTv2 IP config defer func() { // Release the default IP config if there is an error if err != nil { _, err = failureHandler(ctx, req) if err != nil { logger.Errorf("failed to release default IP config : %v", err) } } }() if err != nil { return ipConfigsResp, err } SWIFTv2PodIPInfos, err := k.getIPConfig(ctx, podInfo) if err != nil { return &cns.IPConfigsResponse{ Response: cns.Response{ ReturnCode: types.FailedToAllocateIPConfig, Message: fmt.Sprintf("AllocateIPConfig failed: %v, IP config request is %v", err, req), }, PodIPInfo: []cns.PodIpInfo{}, }, errors.Wrapf(err, "failed to get SWIFTv2 IP config : %v", req) } ipConfigsResp.PodIPInfo = append(ipConfigsResp.PodIPInfo, SWIFTv2PodIPInfos...) // Set routes for the pod for i := range ipConfigsResp.PodIPInfo { ipInfo := &ipConfigsResp.PodIPInfo[i] // Backend nics doesn't need routes to be set if ipInfo.NICType != cns.BackendNIC { err = k.setRoutes(ipInfo) if err != nil { return &cns.IPConfigsResponse{ Response: cns.Response{ ReturnCode: types.FailedToAllocateIPConfig, Message: fmt.Sprintf("AllocateIPConfig failed: %v, IP config request is %v", err, req), }, PodIPInfo: []cns.PodIpInfo{}, }, errors.Wrapf(err, "failed to set routes for pod %s", podInfo.Name()) } } } return ipConfigsResp, nil } } func GetDefaultDenyBool(mtpnc v1alpha1.MultitenantPodNetworkConfig) bool { // returns the value of DefaultDenyACL from mtpnc return mtpnc.Status.DefaultDenyACL } // getInfraRoutes() returns the infra routes including infravnet/ and service cidrs for the podIPInfo used in SWIFT V2 Windows scenario // Windows uses default route 0.0.0.0 as the gateway IP for containerd to configure; // For example, containerd would set route like: ip route 10.0.0.0/16 via 0.0.0.0 dev eth0 func (k *K8sSWIFTv2Middleware) getInfraRoutes(podIPInfo *cns.PodIpInfo) ([]cns.Route, error) { var routes []cns.Route ip, err := netip.ParseAddr(podIPInfo.PodIPConfig.IPAddress) if err != nil { return nil, errors.Wrapf(err, "failed to parse podIPConfig IP address %s", podIPInfo.PodIPConfig.IPAddress) } // TODO: add ipv6 when supported v4IPs, _, err := k.GetInfravnetAndServiceCidrs() if err != nil { return nil, errors.Wrap(err, "failed to get infravnet and service CIDRs") } if ip.Is4() { routes = append(routes, k.AddRoutes(v4IPs, defaultGateway)...) } return routes, nil }