Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v1.14] fqdn: Add Protocol to DNS Proxy Cache #31801

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion daemon/cmd/fqdn.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ func (d *Daemon) bootstrapFQDN(possibleEndpoints map[uint16]*endpoint.Endpoint,
// Restore old rules
for _, possibleEP := range possibleEndpoints {
// Upgrades from old ciliums have this nil
if possibleEP.DNSRules != nil {
if possibleEP.DNSRules != nil || possibleEP.DNSRulesV2 != nil {
proxy.DefaultDNSProxy.RestoreRules(possibleEP)
}
}
Expand Down
38 changes: 33 additions & 5 deletions pkg/endpoint/bpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,13 @@ func (e *Endpoint) writeHeaderfile(prefix string) error {
}
defer f.Cleanup()

if e.DNSRules != nil {
// Note: e.DNSRules is updated by syncEndpointHeaderFile and regenerateBPF
if e.DNSRulesV2 != nil {
// Note: e.DNSRulesV2 is updated by syncEndpointHeaderFile and regenerateBPF
// before they call into writeHeaderfile, because GetDNSRules must not be
// called with endpoint.mutex held.
e.getLogger().WithFields(logrus.Fields{
logfields.Path: headerPath,
"DNSRules": e.DNSRules,
"DNSRulesV2": e.DNSRulesV2,
}).Debug("writing header file with DNSRules")
}

Expand All @@ -169,6 +169,31 @@ func (e *Endpoint) writeHeaderfile(prefix string) error {
return f.CloseAtomicallyReplace()
}

// proxyPolicy implements policy.ProxyPolicy interface, and passes most of the calls
// to policy.L4Filter, but re-implements GetPort() to return the resolved named port,
// instead of returning a 0 port number.
type proxyPolicy struct {
*policy.L4Filter
port uint16
protocol uint8
}

// newProxyPolicy returns a new instance of proxyPolicy by value
func (e *Endpoint) newProxyPolicy(l4 *policy.L4Filter, port uint16, proto uint8) *proxyPolicy {
return &proxyPolicy{L4Filter: l4, port: port, protocol: proto}
}

// GetPort returns the destination port number on which the proxy policy applies
// This version properly returns the port resolved from a named port, if any.
func (p *proxyPolicy) GetPort() uint16 {
return p.port
}

// GetProtocol returns the destination protocol number on which the proxy policy applies
func (p *proxyPolicy) GetProtocol() uint8 {
return p.protocol
}

// addNewRedirectsFromDesiredPolicy must be called while holding the endpoint lock for
// writing. On success, returns nil; otherwise, returns an error indicating the
// problem that occurred while adding an l7 redirect for the specified policy.
Expand Down Expand Up @@ -200,7 +225,9 @@ func (e *Endpoint) addNewRedirectsFromDesiredPolicy(ingress bool, desiredRedirec
var finalizeFunc revert.FinalizeFunc
var revertFunc revert.RevertFunc

proxyID := e.proxyID(l4)
// proxyID() returns also the destination port for the policy,
// which may be resolved from a named port
proxyID, dstPort, dstProto := e.proxyID(l4)
if proxyID == "" {
// Skip redirects for which a proxyID cannot be created.
// This may happen due to the named port mapping not
Expand All @@ -212,7 +239,8 @@ func (e *Endpoint) addNewRedirectsFromDesiredPolicy(ingress bool, desiredRedirec
}

var err error
redirectPort, err, finalizeFunc, revertFunc = e.proxy.CreateOrUpdateRedirect(e.aliveCtx, l4, proxyID, e, proxyWaitGroup)
pp := e.newProxyPolicy(l4, dstPort, dstProto)
redirectPort, err, finalizeFunc, revertFunc = e.proxy.CreateOrUpdateRedirect(e.aliveCtx, pp, proxyID, e, proxyWaitGroup)
if err != nil {
// Skip redirects that can not be created or updated. This
// can happen when a listener is missing, for example when
Expand Down
23 changes: 21 additions & 2 deletions pkg/endpoint/endpoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import (
"github.com/cilium/cilium/pkg/proxy/accesslog"
"github.com/cilium/cilium/pkg/trigger"
"github.com/cilium/cilium/pkg/types"
"github.com/cilium/cilium/pkg/u8proto"
)

const (
Expand Down Expand Up @@ -207,9 +208,17 @@ type Endpoint struct {
status *EndpointStatus

// DNSRules is the collection of current endpoint-specific DNS proxy
// rules. These can be restored during Cilium restart.
// rules that conform to using restore.PortProto V1 (that is, they do
// **not** take protocol into account). These can be restored during
// Cilium restart.
// TODO: This can be removed when 1.16 is deprecated.
DNSRules restore.DNSRules

// DNSRulesV2 is the collection of current endpoint-specific DNS proxy
// rules that conform to using restore.PortProto V2 (that is, they take
// protocol into account). These can be restored during Cilium restart.
DNSRulesV2 restore.DNSRules

// DNSHistory is the collection of still-valid DNS responses intercepted for
// this endpoint.
DNSHistory *fqdn.DNSCache
Expand Down Expand Up @@ -471,6 +480,7 @@ func createEndpoint(owner regeneration.Owner, policyGetter policyRepoGetter, nam
ifName: ifName,
OpLabels: labels.NewOpLabels(),
DNSRules: nil,
DNSRulesV2: nil,
DNSHistory: fqdn.NewDNSCacheWithLimit(option.Config.ToFQDNsMinTTL, option.Config.ToFQDNsMaxIPsPerHost),
DNSZombies: fqdn.NewDNSZombieMappings(option.Config.ToFQDNsMaxDeferredConnectionDeletes, option.Config.ToFQDNsMaxIPsPerHost),
state: "",
Expand Down Expand Up @@ -1467,7 +1477,16 @@ func (e *Endpoint) OnProxyPolicyUpdate(revision uint64) {

// OnDNSPolicyUpdateLocked is called when the Endpoint's DNS policy has been updated
func (e *Endpoint) OnDNSPolicyUpdateLocked(rules restore.DNSRules) {
e.DNSRules = rules
e.DNSRulesV2 = rules
// Keep V1 in tact in case of a downgrade.
e.DNSRules = make(restore.DNSRules)
for pp, rules := range rules {
proto := pp.Protocol()
// Filter out non-UDP/TCP protocol
if proto == uint8(u8proto.TCP) || proto == uint8(u8proto.UDP) {
e.DNSRules[pp.ToV1()] = rules
}
}
}

// getProxyStatisticsLocked gets the ProxyStatistics for the flows with the
Expand Down
16 changes: 15 additions & 1 deletion pkg/endpoint/endpoint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -554,14 +554,28 @@ func (s *EndpointSuite) TestWaitForPolicyRevision(c *C) {
func (s *EndpointSuite) TestProxyID(c *C) {
e := &Endpoint{ID: 123, policyRevision: 0}

id := e.proxyID(&policy.L4Filter{Port: 8080, Protocol: api.ProtoTCP, Ingress: true})
id, port, proto := e.proxyID(&policy.L4Filter{Port: 8080, Protocol: api.ProtoTCP, Ingress: true})
c.Assert(id, Not(Equals), "")
c.Assert(port, Equals, uint16(8080))
c.Assert(proto, Equals, uint8(6))

endpointID, ingress, protocol, port, err := policy.ParseProxyID(id)
c.Assert(endpointID, Equals, uint16(123))
c.Assert(ingress, Equals, true)
c.Assert(protocol, Equals, "TCP")
c.Assert(port, Equals, uint16(8080))
c.Assert(err, IsNil)

id, port, proto = e.proxyID(&policy.L4Filter{Port: 8080, Protocol: api.ProtoTCP, Ingress: true, L7Parser: policy.ParserTypeCRD})
c.Assert(id, Not(Equals), "")
c.Assert(port, Equals, uint16(8080))
c.Assert(proto, Equals, uint8(6))
endpointID, ingress, protocol, port, err = policy.ParseProxyID(id)
c.Assert(endpointID, Equals, uint16(123))
c.Assert(ingress, Equals, true)
c.Assert(protocol, Equals, "TCP")
c.Assert(port, Equals, uint16(8080))
c.Assert(err, IsNil)
}

func TestEndpoint_GetK8sPodLabels(t *testing.T) {
Expand Down
19 changes: 14 additions & 5 deletions pkg/endpoint/policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,26 @@ func (e *Endpoint) getNamedPortEgress(npMap types.NamedPortMultiMap, name string
return port
}

// proxyID returns a unique string to identify a proxy mapping.
// proxyID returns a unique string to identify a proxy mapping,
// and the resolved destination port and protocol numbers, if any.
// Must be called with e.mutex held.
func (e *Endpoint) proxyID(l4 *policy.L4Filter) string {
func (e *Endpoint) proxyID(l4 *policy.L4Filter) (string, uint16, uint8) {
port := uint16(l4.Port)
protocol := uint8(l4.U8Proto)
// Calculate protocol if it is 0 (default) and
// is not "ANY" (that is, it was not calculated).
if protocol == 0 && !l4.Protocol.IsAny() {
proto, _ := u8proto.ParseProtocol(string(l4.Protocol))
protocol = uint8(proto)
}
if port == 0 && l4.PortName != "" {
port = e.GetNamedPort(l4.Ingress, l4.PortName, uint8(l4.U8Proto))
port = e.GetNamedPort(l4.Ingress, l4.PortName, protocol)
if port == 0 {
return ""
return "", 0, 0
}
}
return policy.ProxyID(e.ID, l4.Ingress, string(l4.Protocol), port)

return policy.ProxyID(e.ID, l4.Ingress, string(l4.Protocol), port), port, protocol
}

// lookupRedirectPort returns the redirect L4 proxy port for the given L4
Expand Down
6 changes: 6 additions & 0 deletions pkg/endpoint/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ func (e *Endpoint) toSerializedEndpoint() *serializableEndpoint {
SecurityIdentity: e.SecurityIdentity,
Options: e.Options,
DNSRules: e.DNSRules,
DNSRulesV2: e.DNSRulesV2,
DNSHistory: e.DNSHistory,
DNSZombies: e.DNSZombies,
K8sPodName: e.K8sPodName,
Expand Down Expand Up @@ -471,6 +472,10 @@ type serializableEndpoint struct {
// DNSRules is the collection of current DNS rules for this endpoint.
DNSRules restore.DNSRules

// DNSRulesV2 is the collection of current DNS rules for this endpoint,
// that conform to using V2 of the PortProto key.
DNSRulesV2 restore.DNSRules

// DNSHistory is the collection of still-valid DNS responses intercepted for
// this endpoint.
DNSHistory *fqdn.DNSCache
Expand Down Expand Up @@ -539,6 +544,7 @@ func (ep *Endpoint) fromSerializedEndpoint(r *serializableEndpoint) {
ep.nodeMAC = r.NodeMAC
ep.SecurityIdentity = r.SecurityIdentity
ep.DNSRules = r.DNSRules
ep.DNSRulesV2 = r.DNSRulesV2
ep.DNSHistory = r.DNSHistory
ep.DNSZombies = r.DNSZombies
ep.K8sPodName = r.K8sPodName
Expand Down
11 changes: 7 additions & 4 deletions pkg/fqdn/dnsproxy/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,20 @@ import (
"net"

"github.com/cilium/dns"

"github.com/cilium/cilium/pkg/fqdn/restore"
"github.com/cilium/cilium/pkg/u8proto"
)

// lookupTargetDNSServer finds the intended DNS target server for a specific
// request (passed in via ServeDNS). The IP:port combination is
// request (passed in via ServeDNS). The IP:port:protocol combination is
// returned.
func lookupTargetDNSServer(w dns.ResponseWriter) (serverIP net.IP, serverPort uint16, addrStr string, err error) {
func lookupTargetDNSServer(w dns.ResponseWriter) (serverIP net.IP, serverPortProto restore.PortProto, addrStr string, err error) {
switch addr := (w.LocalAddr()).(type) {
case *net.UDPAddr:
return addr.IP, uint16(addr.Port), addr.String(), nil
return addr.IP, restore.MakeV2PortProto(uint16(addr.Port), uint8(u8proto.UDP)), addr.String(), nil
case *net.TCPAddr:
return addr.IP, uint16(addr.Port), addr.String(), nil
return addr.IP, restore.MakeV2PortProto(uint16(addr.Port), uint8(u8proto.TCP)), addr.String(), nil
default:
return nil, 0, addr.String(), fmt.Errorf("Cannot extract address information for type %T: %+v", addr, addr)
}
Expand Down
34 changes: 23 additions & 11 deletions pkg/fqdn/dnsproxy/helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,16 @@ import (
"github.com/cilium/cilium/pkg/defaults"
"github.com/cilium/cilium/pkg/fqdn/dns"
"github.com/cilium/cilium/pkg/fqdn/re"
"github.com/cilium/cilium/pkg/fqdn/restore"
"github.com/cilium/cilium/pkg/identity"
"github.com/cilium/cilium/pkg/policy"
"github.com/cilium/cilium/pkg/policy/api"
"github.com/cilium/cilium/pkg/u8proto"
)

const (
udpProto = uint8(u8proto.UDP)
tcpProto = uint8(u8proto.TCP)
)

type DNSProxyHelperTestSuite struct{}
Expand All @@ -33,6 +40,8 @@ func (s *DNSProxyHelperTestSuite) TestSetPortRulesForID(c *C) {
epID := uint64(1)
pea := perEPAllow{}
cache := make(regexCache)
udpProtoPort8053 := restore.MakeV2PortProto(8053, udpProto)

rules[new(MockCachedSelector)] = &policy.PerSelectorPolicy{
L7Rules: api.L7Rules{
DNS: []api.PortRuleDNS{
Expand All @@ -41,7 +50,8 @@ func (s *DNSProxyHelperTestSuite) TestSetPortRulesForID(c *C) {
},
},
}
err := pea.setPortRulesForID(cache, epID, 8053, rules)

err := pea.setPortRulesForID(cache, epID, udpProtoPort8053, rules)
c.Assert(err, Equals, nil)
c.Assert(len(cache), Equals, 1)

Expand All @@ -55,16 +65,17 @@ func (s *DNSProxyHelperTestSuite) TestSetPortRulesForID(c *C) {
},
},
}
err = pea.setPortRulesForID(cache, epID, 8053, rules)

err = pea.setPortRulesForID(cache, epID, udpProtoPort8053, rules)
c.Assert(err, Equals, nil)
c.Assert(len(cache), Equals, 2)

delete(rules, selector2)
err = pea.setPortRulesForID(cache, epID, 8053, rules)
err = pea.setPortRulesForID(cache, epID, udpProtoPort8053, rules)
c.Assert(err, Equals, nil)
c.Assert(len(cache), Equals, 1)

err = pea.setPortRulesForID(cache, epID, 8053, nil)
err = pea.setPortRulesForID(cache, epID, udpProtoPort8053, nil)
c.Assert(err, Equals, nil)
c.Assert(len(cache), Equals, 0)

Expand All @@ -78,7 +89,7 @@ func (s *DNSProxyHelperTestSuite) TestSetPortRulesForID(c *C) {
},
},
}
err = pea.setPortRulesForID(cache, epID, 8053, rules)
err = pea.setPortRulesForID(cache, epID, udpProtoPort8053, rules)

c.Assert(err, NotNil)
c.Assert(len(cache), Equals, 0)
Expand All @@ -90,34 +101,35 @@ func (s *DNSProxyHelperTestSuite) TestSetPortRulesForIDFromUnifiedFormat(c *C) {
epID := uint64(1)
pea := perEPAllow{}
cache := make(regexCache)
udpProtoPort8053 := restore.MakeV2PortProto(8053, udpProto)
rules[new(MockCachedSelector)] = regexp.MustCompile("^.*[.]cilium[.]io$")
rules[new(MockCachedSelector)] = regexp.MustCompile("^.*[.]cilium[.]io$")

err := pea.setPortRulesForIDFromUnifiedFormat(cache, epID, 8053, rules)
err := pea.setPortRulesForIDFromUnifiedFormat(cache, epID, udpProtoPort8053, rules)
c.Assert(err, Equals, nil)
c.Assert(len(cache), Equals, 1)

selector2 := new(MockCachedSelector)
rules[selector2] = regexp.MustCompile("^sub[.]cilium[.]io")
err = pea.setPortRulesForIDFromUnifiedFormat(cache, epID, 8053, rules)
err = pea.setPortRulesForIDFromUnifiedFormat(cache, epID, udpProtoPort8053, rules)
c.Assert(err, Equals, nil)
c.Assert(len(cache), Equals, 2)

delete(rules, selector2)
err = pea.setPortRulesForIDFromUnifiedFormat(cache, epID, 8053, rules)
err = pea.setPortRulesForIDFromUnifiedFormat(cache, epID, udpProtoPort8053, rules)
c.Assert(err, Equals, nil)
c.Assert(len(cache), Equals, 1)

err = pea.setPortRulesForIDFromUnifiedFormat(cache, epID, 8053, nil)
err = pea.setPortRulesForIDFromUnifiedFormat(cache, epID, udpProtoPort8053, nil)
c.Assert(err, Equals, nil)
c.Assert(len(cache), Equals, 0)

delete(rules, selector2)
err = pea.setPortRulesForIDFromUnifiedFormat(cache, epID, 8053, rules)
err = pea.setPortRulesForIDFromUnifiedFormat(cache, epID, udpProtoPort8053, rules)
c.Assert(err, Equals, nil)
c.Assert(len(cache), Equals, 1)

err = pea.setPortRulesForIDFromUnifiedFormat(cache, epID, 8053, nil)
err = pea.setPortRulesForIDFromUnifiedFormat(cache, epID, udpProtoPort8053, nil)
c.Assert(err, Equals, nil)
c.Assert(len(cache), Equals, 0)
}
Expand Down