diff --git a/cns/NetworkContainerContract.go b/cns/NetworkContainerContract.go index b3617415ac..f25cd05a12 100644 --- a/cns/NetworkContainerContract.go +++ b/cns/NetworkContainerContract.go @@ -2,6 +2,8 @@ package cns import ( "encoding/json" + "fmt" + "strings" ) // Container Network Service DNC Contract @@ -63,6 +65,14 @@ type CreateNetworkContainerRequest struct { Routes []Route AllowHostToNCCommunication bool AllowNCToHostCommunication bool + EndpointPolicies []NetworkContainerRequestPolicies +} + +// NetworkContainerRequestPolicies - specifies policies associated with create network request +type NetworkContainerRequestPolicies struct { + Type string + EndpointType string + Settings json.RawMessage } // ConfigureContainerNetworkingRequest - specifies request to attach/detach container to network. @@ -220,3 +230,51 @@ type UnpublishNetworkContainerResponse struct { UnpublishStatusCode int UnpublishResponseBody []byte } + +// ValidAclPolicySetting - Used to validate ACL policy +type ValidAclPolicySetting struct { + Protocols string `json:","` + Action string `json:","` + Direction string `json:","` + LocalAddresses string `json:","` + RemoteAddresses string `json:","` + LocalPorts string `json:","` + RemotePorts string `json:","` + RuleType string `json:","` + Priority uint16 `json:","` +} + +// Validate - Validates network container request policies +func (networkContainerRequestPolicy *NetworkContainerRequestPolicies) Validate() error { + // validate ACL policy + if networkContainerRequestPolicy != nil { + if strings.EqualFold(networkContainerRequestPolicy.Type, "ACLPolicy") && strings.EqualFold(networkContainerRequestPolicy.EndpointType, "APIPA") { + var requestedAclPolicy ValidAclPolicySetting + if err := json.Unmarshal(networkContainerRequestPolicy.Settings, &requestedAclPolicy); err != nil { + return fmt.Errorf("ACL policy failed to pass validation with error: %+v ", err) + } + //Deny request if ACL Action is empty + if len(strings.TrimSpace(string(requestedAclPolicy.Action))) == 0 { + return fmt.Errorf("Action field cannot be empty in ACL Policy") + } + //Deny request if ACL Action is not Allow or Deny + if !strings.EqualFold(requestedAclPolicy.Action, "Allow") && !strings.EqualFold(requestedAclPolicy.Action, "Deny") { + return fmt.Errorf("Only Allow or Deny is supported in Action field") + } + //Deny request if ACL Direction is empty + if len(strings.TrimSpace(string(requestedAclPolicy.Direction))) == 0 { + return fmt.Errorf("Direction field cannot be empty in ACL Policy") + } + //Deny request if ACL direction is not In or Out + if !strings.EqualFold(requestedAclPolicy.Direction, "In") && !strings.EqualFold(requestedAclPolicy.Direction, "Out") { + return fmt.Errorf("Only In or Out is supported in Direction field") + } + if requestedAclPolicy.Priority == 0 { + return fmt.Errorf("Priority field cannot be empty in ACL Policy") + } + } else { + return fmt.Errorf("Only ACL Policies on APIPA endpoint supported") + } + } + return nil +} diff --git a/cns/hnsclient/hnsclient_linux.go b/cns/hnsclient/hnsclient_linux.go index a78acb9fb8..0a9d62c3c7 100644 --- a/cns/hnsclient/hnsclient_linux.go +++ b/cns/hnsclient/hnsclient_linux.go @@ -38,7 +38,8 @@ func CreateHostNCApipaEndpoint( networkContainerID string, localIPConfiguration cns.IPConfiguration, allowNCToHostCommunication bool, - allowHostToNCCommunication bool) (string, error) { + allowHostToNCCommunication bool, + ncPolicies []cns.NetworkContainerRequestPolicies) (string, error) { return "", nil } diff --git a/cns/hnsclient/hnsclient_windows.go b/cns/hnsclient/hnsclient_windows.go index 49fcc22edc..306236b8ed 100644 --- a/cns/hnsclient/hnsclient_windows.go +++ b/cns/hnsclient/hnsclient_windows.go @@ -66,6 +66,12 @@ const ( // aclPriority200 indicates the ACL priority of 200 aclPriority200 = 200 + + // aclPolicyType indicates a ACL policy + aclPolicyType = "ACLPolicy" + + //signals a APIPA endpoint type + apipaEndpointType = "APIPA" ) var ( @@ -347,7 +353,8 @@ func configureAclSettingHostNCApipaEndpoint( networkContainerApipaIP string, hostApipaIP string, allowNCToHostCommunication bool, - allowHostToNCCommunication bool) ([]hcn.EndpointPolicy, error) { + allowHostToNCCommunication bool, + ncRequestedPolicies []cns.NetworkContainerRequestPolicies) ([]hcn.EndpointPolicy, error) { var ( err error endpointPolicies []hcn.EndpointPolicy @@ -426,8 +433,33 @@ func configureAclSettingHostNCApipaEndpoint( return nil, err } } + } + if ncRequestedPolicies != nil { + // Iterate thru the requested endpoint policies where policy type is ACL, endpoint type is APIPA + // include the raw json message in the endpoint policies + for _, requestedPolicy := range ncRequestedPolicies { + if strings.EqualFold(requestedPolicy.Type, aclPolicyType) && strings.EqualFold(requestedPolicy.EndpointType, apipaEndpointType) { + var requestedAclPolicy hcn.AclPolicySetting + if err = json.Unmarshal(requestedPolicy.Settings, &requestedAclPolicy); err != nil { + return nil, fmt.Errorf("Failed to Unmarshal requested ACL policy: %+v with error: %S", requestedPolicy.Settings, err) + } + //Using {NetworkContainerIP} as a placeholder to signal using Network Container IP + if strings.EqualFold(requestedAclPolicy.LocalAddresses, "{NetworkContainerIP}") { + requestedAclPolicy.LocalAddresses = networkContainerApipaIP + } + //Using {HostApipaIP} as a placeholder to signal using Host Apipa IP + if strings.EqualFold(requestedAclPolicy.RemoteAddresses, "{HostApipaIP}") { + requestedAclPolicy.RemoteAddresses = hostApipaIP + } + logger.Printf("ACL Policy requested in NcGoalState %+v", requestedAclPolicy) + if err = addAclToEndpointPolicy(requestedAclPolicy, &endpointPolicies); err != nil { + return nil, err + } + } + } + } return endpointPolicies, nil } @@ -436,7 +468,8 @@ func configureHostNCApipaEndpoint( networkID string, localIPConfiguration cns.IPConfiguration, allowNCToHostCommunication bool, - allowHostToNCCommunication bool) (*hcn.HostComputeEndpoint, error) { + allowHostToNCCommunication bool, + ncPolicies []cns.NetworkContainerRequestPolicies) (*hcn.HostComputeEndpoint, error) { endpoint := &hcn.HostComputeEndpoint{ Name: endpointName, HostComputeNetwork: networkID, @@ -455,7 +488,8 @@ func configureHostNCApipaEndpoint( networkContainerApipaIP, hostApipaIP, allowNCToHostCommunication, - allowHostToNCCommunication) + allowHostToNCCommunication, + ncPolicies) if err != nil { logger.Errorf("[Azure CNS] Failed to configure ACL for HostNCApipaEndpoint. Error: %v", err) @@ -490,7 +524,8 @@ func CreateHostNCApipaEndpoint( networkContainerID string, localIPConfiguration cns.IPConfiguration, allowNCToHostCommunication bool, - allowHostToNCCommunication bool) (string, error) { + allowHostToNCCommunication bool, + ncPolicies []cns.NetworkContainerRequestPolicies) (string, error) { var ( network *hcn.HostComputeNetwork endpoint *hcn.HostComputeEndpoint @@ -528,7 +563,8 @@ func CreateHostNCApipaEndpoint( network.Id, localIPConfiguration, allowNCToHostCommunication, - allowHostToNCCommunication); err != nil { + allowHostToNCCommunication, + ncPolicies); err != nil { logger.Errorf("[Azure CNS] Failed to configure HostNCApipaEndpoint: %s. Error: %v", endpointName, err) return "", err } diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index 0366c0149f..97beff3368 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -1697,7 +1697,8 @@ func (service *HTTPRestService) createHostNCApipaEndpoint(w http.ResponseWriter, req.NetworkContainerID, networkContainerDetails.CreateNetworkContainerRequest.LocalIPConfiguration, networkContainerDetails.CreateNetworkContainerRequest.AllowNCToHostCommunication, - networkContainerDetails.CreateNetworkContainerRequest.AllowHostToNCCommunication); err != nil { + networkContainerDetails.CreateNetworkContainerRequest.AllowHostToNCCommunication, + networkContainerDetails.CreateNetworkContainerRequest.EndpointPolicies); err != nil { returnMessage = fmt.Sprintf("CreateHostNCApipaEndpoint failed with error: %v", err) returnCode = UnexpectedError }