Skip to content

Commit

Permalink
Merge pull request #134 from metal-stack/dns-based-egress-with-egress…
Browse files Browse the repository at this point in the history
…-IPs

Escape dns queries from snat when dns proxy is in effect
  • Loading branch information
mreiger authored Dec 23, 2022
2 parents c6afe2a + a249c46 commit 72f34ec
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 3 deletions.
13 changes: 12 additions & 1 deletion pkg/nftables/snat.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func snatRules(f *Firewall) (nftablesRules, error) {
if f.primaryPrivateNet == nil {
return nil, fmt.Errorf("no primary private network found")
}
sourceNetworks := strings.Join(f.primaryPrivateNet.Prefixes, ", ")

rules := nftablesRules{}
for _, s := range f.firewall.Spec.EgressRules {
Expand Down Expand Up @@ -73,11 +74,21 @@ func snatRules(f *Firewall) (nftablesRules, error) {

snatRule := snatRule{
comment: fmt.Sprintf("snat for %s", s.NetworkID),
sourceNetworks: strings.Join(f.primaryPrivateNet.Prefixes, ", "),
sourceNetworks: sourceNetworks,
oifname: fmt.Sprintf("vlan%d", *n.Vrf),
to: to,
}
rules = append(rules, snatRule.String())
}

enableDNS := len(f.clusterwideNetworkPolicies.GetFQDNs()) > 0
if enableDNS {
escapeDNSRules := []string{
fmt.Sprintf(`ip saddr { %s } tcp dport { 53 } accept comment "escape snat for dns proxy tcp"`, sourceNetworks),
fmt.Sprintf(`ip saddr { %s } udp dport { 53 } accept comment "escape snat for dns proxy udp"`, sourceNetworks),
}
return append(escapeDNSRules, uniqueSorted(rules)...), nil
}

return uniqueSorted(rules), nil
}
86 changes: 84 additions & 2 deletions pkg/nftables/snat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ import (
"github.com/go-logr/logr"
"github.com/google/go-cmp/cmp"
mn "github.com/metal-stack/metal-lib/pkg/net"
corev1 "k8s.io/api/core/v1"
networking "k8s.io/api/networking/v1"

firewallv1 "github.com/metal-stack/firewall-controller/api/v1"
)

func TestSnatRules(t *testing.T) {
tcp := corev1.ProtocolTCP
udp := corev1.ProtocolUDP
private := "private"
internet := "internet"
mpls := "mpls"
Expand All @@ -24,6 +28,7 @@ func TestSnatRules(t *testing.T) {
tests := []struct {
name string
input firewallv1.FirewallSpec
cwnps firewallv1.ClusterwideNetworkPolicyList
want nftablesRules
wantErr bool
err error
Expand Down Expand Up @@ -65,11 +70,86 @@ func TestSnatRules(t *testing.T) {
},
},
},
cwnps: firewallv1.ClusterwideNetworkPolicyList{},
want: nftablesRules{
`ip saddr { 10.0.1.0/24 } oifname "vlan1" counter snat to jhash ip daddr . tcp sport mod 2 map { 0 : 185.0.0.2, 1 : 185.0.0.3 } comment "snat for internet"`,
`ip saddr { 10.0.1.0/24 } oifname "vlan2" counter snat 100.0.0.2 comment "snat for mpls"`,
},
},
{
name: "escape DNS for dns-based CWNPs",
input: firewallv1.FirewallSpec{
Data: firewallv1.Data{
FirewallNetworks: []firewallv1.FirewallNetwork{
{
Networkid: &private,
Prefixes: []string{"10.0.1.0/24"},
Ips: []string{"10.0.1.1"},
Networktype: &privatePrimary,
},
{
Networkid: &internet,
Prefixes: []string{"185.0.0.0/24"},
Ips: []string{"185.0.0.1"},
Vrf: &vrf1,
Networktype: &external,
},
{
Networkid: &mpls,
Prefixes: []string{"100.0.0.0/24"},
Ips: []string{"100.0.0.1"},
Vrf: &vrf2,
Networktype: &external,
},
},
EgressRules: []firewallv1.EgressRuleSNAT{
{
NetworkID: "internet",
IPs: []string{"185.0.0.2", "185.0.0.3"},
}, {
NetworkID: "mpls",
IPs: []string{"100.0.0.2"},
},
},
},
},
cwnps: firewallv1.ClusterwideNetworkPolicyList{
Items: []firewallv1.ClusterwideNetworkPolicy{
{
Spec: firewallv1.PolicySpec{
Egress: []firewallv1.EgressRule{
{
ToFQDNs: []firewallv1.FQDNSelector{
{
MatchName: "test.com",
},
{
MatchPattern: "*.test.com",
},
},
Ports: []networking.NetworkPolicyPort{
{
Protocol: &tcp,
Port: port(53),
},
{
Protocol: &udp,
Port: port(53),
},
},
},
},
},
},
},
},
want: nftablesRules{
`ip saddr { 10.0.1.0/24 } tcp dport { 53 } accept comment "escape snat for dns proxy tcp"`,
`ip saddr { 10.0.1.0/24 } udp dport { 53 } accept comment "escape snat for dns proxy udp"`,
`ip saddr { 10.0.1.0/24 } oifname "vlan1" counter snat to jhash ip daddr . tcp sport mod 2 map { 0 : 185.0.0.2, 1 : 185.0.0.3 } comment "snat for internet"`,
`ip saddr { 10.0.1.0/24 } oifname "vlan2" counter snat 100.0.0.2 comment "snat for mpls"`,
},
},
{
name: "empty snat rules",
input: firewallv1.FirewallSpec{
Expand All @@ -86,7 +166,8 @@ func TestSnatRules(t *testing.T) {
EgressRules: []firewallv1.EgressRuleSNAT{},
},
},
want: nftablesRules{},
cwnps: firewallv1.ClusterwideNetworkPolicyList{},
want: nftablesRules{},
},
{
name: "no primary network",
Expand All @@ -102,14 +183,15 @@ func TestSnatRules(t *testing.T) {
},
},
},
cwnps: firewallv1.ClusterwideNetworkPolicyList{},
wantErr: true,
err: errors.New("no primary private network found"),
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
f := NewFirewall(firewallv1.Firewall{Spec: tt.input}, &firewallv1.ClusterwideNetworkPolicyList{}, nil, nil, logr.Discard())
f := NewFirewall(firewallv1.Firewall{Spec: tt.input}, &tt.cwnps, nil, nil, logr.Discard())
got, err := snatRules(f)
if (err != nil) != tt.wantErr {
t.Errorf("snatRules() error = %v, wantErr %v", err, tt.err)
Expand Down

0 comments on commit 72f34ec

Please sign in to comment.