diff --git a/apis/networking/v1alpha1/firewall/common_types.go b/apis/networking/v1alpha1/firewall/common_types.go index 9b5f10f35f..32986841e2 100644 --- a/apis/networking/v1alpha1/firewall/common_types.go +++ b/apis/networking/v1alpha1/firewall/common_types.go @@ -27,25 +27,25 @@ const ( IPValueTypeIP IPValueType = "ip" // IPValueTypeSubnet is a string representing a subnet (eg. 10.0.0.0/24). IPValueTypeSubnet IPValueType = "subnet" - // IPValueTypeInvalid is an invalid match value. - IPValueTypeInvalid IPValueType = "invalid" + // IPValueTypeVoid is a void match value. + IPValueTypeVoid IPValueType = "void" ) // GetIPValueType parses the match value and returns the type of the value. -func GetIPValueType(value string) (IPValueType, error) { - if value == "" { - return IPValueTypeInvalid, fmt.Errorf("match value cannot be empty") +func GetIPValueType(value *string) (IPValueType, error) { + if value == nil { + return IPValueTypeVoid, nil } // Check if the value is a pool subnet. - if _, _, err := net.ParseCIDR(value); err == nil { + if _, _, err := net.ParseCIDR(*value); err == nil { return IPValueTypeSubnet, nil } // Check if the value is an IP. - if net.ParseIP(value) != nil { + if net.ParseIP(*value) != nil { return IPValueTypeIP, nil } - return IPValueTypeInvalid, fmt.Errorf("invalid match value %s", value) + return IPValueTypeVoid, fmt.Errorf("invalid match value %s", *value) } diff --git a/apis/networking/v1alpha1/firewall/match.go b/apis/networking/v1alpha1/firewall/match.go index 8ad02b265c..dbfce8656f 100644 --- a/apis/networking/v1alpha1/firewall/match.go +++ b/apis/networking/v1alpha1/firewall/match.go @@ -38,7 +38,7 @@ func applyMatch(m *Match, rule *nftables.Rule) error { } func applyMatchIP(m *Match, rule *nftables.Rule, op expr.CmpOp) error { - matchIPValueType, err := GetIPValueType(m.IP.Value) + matchIPValueType, err := GetIPValueType(&m.IP.Value) if err != nil { return err } diff --git a/apis/networking/v1alpha1/firewall/natrule.go b/apis/networking/v1alpha1/firewall/natrule.go index 4abfbda48c..f20ca186c8 100644 --- a/apis/networking/v1alpha1/firewall/natrule.go +++ b/apis/networking/v1alpha1/firewall/natrule.go @@ -108,15 +108,19 @@ func applyNatRule(nr *NatRule, rule *nftables.Rule) error { return applyNatIP(nr.To, natType, rule) case IPValueTypeSubnet: return applyNatSubnet(nr.To, natType, rule) - default: - return fmt.Errorf("invalid ip value %s", nr.To) + case IPValueTypeVoid: + return applyNatVoid(rule) } + return nil } -func applyNatIP(ip string, natType expr.NATType, rule *nftables.Rule) error { - ipNet := net.ParseIP(ip) +func applyNatIP(ip *string, natType expr.NATType, rule *nftables.Rule) error { + if ip == nil { + return fmt.Errorf("\"to\" argument cannot be nil for nat type snat/dnat") + } + ipNet := net.ParseIP(*ip) if ipNet == nil { - return fmt.Errorf("invalid ip %s", ip) + return fmt.Errorf("invalid ip %s", *ip) } rule.Exprs = append(rule.Exprs, @@ -132,8 +136,16 @@ func applyNatIP(ip string, natType expr.NATType, rule *nftables.Rule) error { return nil } -func applyNatSubnet(ip string, natType expr.NATType, rule *nftables.Rule) error { - _, subnet, err := net.ParseCIDR(ip) +func applyNatVoid(rule *nftables.Rule) error { + rule.Exprs = append(rule.Exprs, &expr.Masq{}) + return nil +} + +func applyNatSubnet(ip *string, natType expr.NATType, rule *nftables.Rule) error { + if ip == nil { + return fmt.Errorf("\"to\" argument cannot be nil for nat type snat/dnat") + } + _, subnet, err := net.ParseCIDR(*ip) if err != nil { return err } @@ -169,7 +181,7 @@ func getNatRuleType(natrule *NatRule) (expr.NATType, error) { switch natrule.NatType { case NatTypeDestination: return expr.NATTypeDestNAT, nil - case NatTypeSource: + case NatTypeSource, NatTypeMasquerade: return expr.NATTypeSourceNAT, nil default: return expr.NATType(0), fmt.Errorf("invalid nat type %s", natrule.NatType) diff --git a/apis/networking/v1alpha1/firewall/natrule_types.go b/apis/networking/v1alpha1/firewall/natrule_types.go index 55479c2fca..a19f1e002b 100644 --- a/apis/networking/v1alpha1/firewall/natrule_types.go +++ b/apis/networking/v1alpha1/firewall/natrule_types.go @@ -22,6 +22,8 @@ const ( NatTypeDestination NatType = "dnat" // NatTypeSource is the type of the NAT rule. NatTypeSource NatType = "snat" + // NatTypeMasquerade is the type of the NAT rule. + NatTypeMasquerade NatType = "masquerade" ) var _ Rule = &NatRule{} @@ -36,8 +38,8 @@ type NatRule struct { // Using multiple ip matches with same position or Match []Match `json:"match"` // NatType is the type of the NAT rule. - // +kubebuilder:validation:Enum=dnat;snat + // +kubebuilder:validation:Enum=dnat;snat;masquerade NatType NatType `json:"natType"` // To is the IP to be used for the NAT translation. - To string `json:"to"` + To *string `json:"to,omitempty"` } diff --git a/apis/networking/v1alpha1/firewall/zz_generated.deepcopy.go b/apis/networking/v1alpha1/firewall/zz_generated.deepcopy.go index 0cd8940ace..74a84faad1 100644 --- a/apis/networking/v1alpha1/firewall/zz_generated.deepcopy.go +++ b/apis/networking/v1alpha1/firewall/zz_generated.deepcopy.go @@ -151,6 +151,11 @@ func (in *NatRule) DeepCopyInto(out *NatRule) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.To != nil { + in, out := &in.To, &out.To + *out = new(string) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NatRule. diff --git a/deployments/liqo/charts/liqo-crds/crds/networking.liqo.io_firewallconfigurations.yaml b/deployments/liqo/charts/liqo-crds/crds/networking.liqo.io_firewallconfigurations.yaml index f3bf65f114..1ff522926d 100644 --- a/deployments/liqo/charts/liqo-crds/crds/networking.liqo.io_firewallconfigurations.yaml +++ b/deployments/liqo/charts/liqo-crds/crds/networking.liqo.io_firewallconfigurations.yaml @@ -172,6 +172,7 @@ spec: enum: - dnat - snat + - masquerade type: string to: description: To is the IP to be used for the NAT @@ -180,7 +181,6 @@ spec: required: - match - natType - - to type: object type: array routeRules: diff --git a/go.sum b/go.sum index 88417f0dc7..e36982f841 100644 --- a/go.sum +++ b/go.sum @@ -423,8 +423,6 @@ github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/nftables v0.1.0 h1:T6lS4qudrMufcNIZ8wSRrL+iuwhsKxpN+zFLxhUWOqk= -github.com/google/nftables v0.1.0/go.mod h1:b97ulCCFipUC+kSin+zygkvUVpx0vyIAwxXFdY3PlNc= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= diff --git a/pkg/liqo-controller-manager/webhooks/firewallconfiguration/natrule.go b/pkg/liqo-controller-manager/webhooks/firewallconfiguration/natrule.go index 42b631f668..0cb0cc011e 100644 --- a/pkg/liqo-controller-manager/webhooks/firewallconfiguration/natrule.go +++ b/pkg/liqo-controller-manager/webhooks/firewallconfiguration/natrule.go @@ -29,6 +29,23 @@ func checkNatRulesInChain(chain *firewallapi.Chain) error { if err := checkNatRuleChainHook(*chain.Hook, &natrules[i]); err != nil { return err } + if err := checkNatRuleTo(&natrules[i]); err != nil { + return err + } + } + return nil +} + +func checkNatRuleTo(natrule *firewallapi.NatRule) error { + switch natrule.NatType { + case firewallapi.NatTypeDestination, firewallapi.NatTypeSource: + if natrule.To == nil { + return fmt.Errorf("natrule %s is %s but has no To field", *natrule.GetName(), natrule.NatType) + } + case firewallapi.NatTypeMasquerade: + if natrule.To != nil { + return fmt.Errorf("natrule %s is Masquerade or masquerade but has a To field", *natrule.GetName()) + } } return nil } @@ -39,28 +56,28 @@ func checkNatRuleChainHook(hook firewallapi.ChainHook, rule *firewallapi.NatRule switch rule.NatType { case firewallapi.NatTypeDestination: return fmt.Errorf("natrule %s is DNAT that is incompatible with postrouting", *rule.GetName()) - case firewallapi.NatTypeSource: + case firewallapi.NatTypeSource, firewallapi.NatTypeMasquerade: return nil } case firewallapi.ChainHookPrerouting: switch rule.NatType { case firewallapi.NatTypeDestination: return nil - case firewallapi.NatTypeSource: + case firewallapi.NatTypeSource, firewallapi.NatTypeMasquerade: return fmt.Errorf("natrule %s is SNAT that is incompatible with prerouting", *rule.GetName()) } case firewallapi.ChainHookInput: switch rule.NatType { case firewallapi.NatTypeDestination: return fmt.Errorf("natrule %s is DNAT that is incompatible with input", *rule.GetName()) - case firewallapi.NatTypeSource: + case firewallapi.NatTypeSource, firewallapi.NatTypeMasquerade: return nil } case firewallapi.ChainHookOutput: switch rule.NatType { case firewallapi.NatTypeDestination: return nil - case firewallapi.NatTypeSource: + case firewallapi.NatTypeSource, firewallapi.NatTypeMasquerade: return fmt.Errorf("natrule %s is SNAT that is incompatible with output", *rule.GetName()) } default: