go.ligato.io/vpp-agent/v3@v3.5.0/tests/e2e/120_dns_test.go (about) 1 // Copyright (c) 2019 Cisco and/or its affiliates. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at: 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package e2e 16 17 import ( 18 "context" 19 "fmt" 20 "net" 21 "testing" 22 "time" 23 24 docker "github.com/fsouza/go-dockerclient" 25 . "github.com/onsi/gomega" 26 27 linux_interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/linux/interfaces" 28 linux_iptables "go.ligato.io/vpp-agent/v3/proto/ligato/linux/iptables" 29 vpp_dns "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/dns" 30 vpp_interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces" 31 vpp_l3 "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/l3" 32 . "go.ligato.io/vpp-agent/v3/tests/e2e/e2etest" 33 ) 34 35 // TestDnsCache tests ability of VPP to act as DNS server with cache capabilities (cache info from upstream DNS server) 36 func TestDnsCache(t *testing.T) { 37 dnsIP4Result := net.ParseIP("10.100.0.1") 38 dnsIP6Result := net.ParseIP("fc::1") // fc::/7 is ipv6 private range (like 10.0.0.0/8 for ipv4) 39 40 cases := []struct { 41 Name string 42 PublicUpstreamDNSServer net.IP 43 QueryDomainName string 44 UnreachabilityVerificationDomainName string 45 SetupModifiers []SetupOptModifier 46 ExpectedResolvedIPv4Address net.IP 47 ExpectedResolvedIPv6Address net.IP 48 SkipAAAARecordCheck bool 49 SkipAll bool 50 SkipReason string 51 }{ 52 { 53 Name: "Test VPP DNS Cache with google DNS as upstream DNS server", 54 PublicUpstreamDNSServer: net.ParseIP("8.8.8.8"), 55 QueryDomainName: "www.google.com", 56 UnreachabilityVerificationDomainName: "www.sme.sk", 57 SkipAAAARecordCheck: true, // TODO remove skipping when VPP bug resolved 58 SkipReason: "VPP bug https://jira.fd.io/browse/VPP-1963", 59 }, { 60 Name: "Test VPP DNS Cache with cloudflare DNS as upstream DNS server", 61 PublicUpstreamDNSServer: net.ParseIP("1.1.1.1"), 62 QueryDomainName: "www.google.com", 63 UnreachabilityVerificationDomainName: "ubuntu.com", 64 SkipAll: true, // TODO remove skipping when VPP bug resolved 65 SkipReason: "VPP bug https://jira.fd.io/browse/VPP-1963", 66 }, { 67 Name: "Test VPP DNS Cache with coredns container as upstream DNS server", 68 QueryDomainName: "dnscache." + LigatoDNSHostNameSuffix, 69 UnreachabilityVerificationDomainName: "unresolvable." + LigatoDNSHostNameSuffix, 70 SetupModifiers: []SetupOptModifier{ 71 WithDNSServer(WithZonedStaticEntries(LigatoDNSHostNameSuffix, 72 fmt.Sprintf("%s %s", dnsIP4Result, "dnscache."+LigatoDNSHostNameSuffix), 73 fmt.Sprintf("%s %s", dnsIP6Result, "dnscache."+LigatoDNSHostNameSuffix), 74 )), 75 }, 76 ExpectedResolvedIPv4Address: dnsIP4Result, 77 ExpectedResolvedIPv6Address: dnsIP6Result, 78 SkipAll: true, // TODO remove skipping when VPP bug resolved 79 SkipReason: "VPP bug https://jira.fd.io/browse/VPP-1963", 80 }, 81 } 82 83 // Run all cases 84 for _, td := range cases { 85 t.Run(td.Name, func(t *testing.T) { 86 if td.SkipAll { 87 t.Skipf("Skipped due to %s", td.SkipReason) 88 } 89 90 // test setup 91 td.SetupModifiers = append(td.SetupModifiers, WithCustomVPPAgent()) // need iptables to be installed 92 ctx := Setup(t, td.SetupModifiers...) 93 defer ctx.Teardown() 94 95 // start microservice 96 ms := ctx.StartMicroservice("microservice1", useMicroserviceWithDig()) 97 98 // configure VPP-Agent container as DNS server 99 vppDNSServer := net.ParseIP(ctx.Agent.IPAddress()) 100 ctx.Expect(vppDNSServer).ShouldNot(BeNil(), "VPP DNS Server container has no IP address") 101 upstreamDNSServer := td.PublicUpstreamDNSServer 102 if td.PublicUpstreamDNSServer == nil { 103 upstreamDNSServer = net.ParseIP(ctx.DNSServer.IPAddress()) 104 ctx.Expect(upstreamDNSServer).ShouldNot(BeNil(), 105 "Local upstream DNS Server container (CoreDNS) has no IP address") 106 } 107 err := configureVPPAgentAsDNSServer(ctx, vppDNSServer, upstreamDNSServer) 108 ctx.Expect(err).ShouldNot(HaveOccurred(), "Configuring changes to VPP-Agent failed with err") 109 110 // Testing resolving DNS query by VPP (it should make request to upstream DNS server) 111 //// Testing A (IPv4) record 112 resolvedIPAddresses, err := ms.Dig(vppDNSServer, td.QueryDomainName, A) 113 ctx.Expect(err).ToNot(HaveOccurred()) 114 if td.ExpectedResolvedIPv4Address != nil { 115 ctx.Expect(resolvedIPAddresses).To(ConsistOf([]net.IP{td.ExpectedResolvedIPv4Address})) 116 } else { // external domain have loadbalancers -> can't tell what IP address we get from upstream DNS server 117 ctx.Expect(resolvedIPAddresses).NotTo(BeEmpty()) 118 ctx.Expect(resolvedIPAddresses[0].To4() != nil).Should(BeTrue(), "is not ipv4 address") 119 } 120 121 //// Testing AAAA (IPv6) record 122 if !td.SkipAAAARecordCheck { 123 resolvedIPAddresses, err = ms.Dig(vppDNSServer, td.QueryDomainName, AAAA) 124 ctx.Expect(err).ToNot(HaveOccurred()) 125 if td.ExpectedResolvedIPv6Address != nil { 126 ctx.Expect(resolvedIPAddresses).To(ConsistOf([]net.IP{td.ExpectedResolvedIPv6Address})) 127 } else { // external domain have loadbalancers -> can't tell what IP address we get from upstream DNS server 128 ctx.Expect(resolvedIPAddresses).NotTo(BeEmpty()) 129 ctx.Expect(resolvedIPAddresses[0].To4() == nil).Should(BeTrue(), "is not ipv6 address") 130 } 131 } 132 133 // block additional request to upstream DNS server (VPP should have the info 134 // already cached and should not need the upstream DNS server anymore) 135 if td.PublicUpstreamDNSServer != nil { 136 // block request to upstream DNS server 137 blockUpstreamDNSServer := &linux_iptables.RuleChain{ 138 Name: "blockUpstreamDNSServer", 139 Protocol: linux_iptables.RuleChain_IPV4, 140 Table: linux_iptables.RuleChain_FILTER, 141 ChainType: linux_iptables.RuleChain_FORWARD, 142 Rules: []string{ 143 fmt.Sprintf("-j DROP -d %s", upstreamDNSServer.String()), 144 }, 145 } 146 ctx.Expect(ctx.GenericClient().ChangeRequest().Update(blockUpstreamDNSServer). 147 Send(context.Background())).To(Succeed()) 148 } else { 149 // using local container as DNS server -> the easy way how to block it is to kill it 150 ctx.Expect(ctx.DNSServer.Stop()).To(Succeed()) 151 } 152 153 // verify the upstream DNS blocking (without it some mild test/container changes could introduce 154 // silent error when VPP will still access upstream DNS server due to ineffective(or still not 155 // effectively applied) blocking and therefore test of VPP caching will not test the cache at all) 156 ctx.Eventually(func() error { 157 _, err := ms.Dig(vppDNSServer, td.UnreachabilityVerificationDomainName, A) 158 return err 159 }, 3*time.Second).Should(HaveOccurred(), 160 "The connection to upstream DNS server is still not severed.") 161 162 // Testing resolving DNS query by VPP from its cache (upstream DNS server requests are blocked) 163 //// Testing A (IPv4) record 164 resolvedIPAddresses, err = ms.Dig(vppDNSServer, td.QueryDomainName, A) 165 ctx.Expect(err).ToNot(HaveOccurred()) 166 if td.ExpectedResolvedIPv4Address != nil { 167 ctx.Expect(resolvedIPAddresses).To(ConsistOf([]net.IP{td.ExpectedResolvedIPv4Address})) 168 } else { // external domain have loadbalancers -> can't tell what IP address we get from upstream DNS server 169 ctx.Expect(resolvedIPAddresses).NotTo(BeEmpty()) 170 ctx.Expect(resolvedIPAddresses[0].To4() != nil).Should(BeTrue(), "is not ipv4 address") 171 } 172 173 //// Testing AAAA (IPv6) record 174 if !td.SkipAAAARecordCheck { 175 resolvedIPAddresses, err = ms.Dig(vppDNSServer, td.QueryDomainName, AAAA) 176 ctx.Expect(err).ToNot(HaveOccurred()) 177 if td.ExpectedResolvedIPv6Address != nil { 178 ctx.Expect(resolvedIPAddresses).To(ConsistOf([]net.IP{td.ExpectedResolvedIPv6Address})) 179 } else { // external domain have loadbalancers -> can't tell what IP address we get from upstream DNS server 180 ctx.Expect(resolvedIPAddresses).NotTo(BeEmpty()) 181 ctx.Expect(resolvedIPAddresses[0].To4() == nil).Should(BeTrue(), "is not ipv6 address") 182 } 183 } 184 }) 185 } 186 } 187 188 // configureVPPAgentAsDNSServer configures VPP-Agent container to act as DNS server. 189 func configureVPPAgentAsDNSServer(ctx *TestCtx, vppDNSServer, upstreamDNSServer net.IP) error { 190 /* VPP-Agent as DNS Server topology: 191 192 +--------------------------------------------+ 193 | VPP-Agent container | 194 | | 195 | +----------------+ +----------------+ | 196 | | VPP-Agent | | VPP | | 197 | | | | | | 198 | | | | | | 199 | | | | + vppTap | | 200 | | | | | | | 201 | +------+---------+ +----------------+ | 202 | | | | 203 | | + linuxTap | 204 | | | | 205 | +------------------------------------+ | 206 | | | Linux Kernel | | | 207 | | +---+--------------------+------+ | | 208 | | | NAT | | | 209 | | +--------------+----------------+ | | 210 | | | | | 211 | +------------------------------------+ | 212 | | | 213 +--------------------------------------------+ 214 | 215 + Default container interface 216 */ 217 218 const ( 219 vppTapName = "tap1" 220 linuxTapName = "tap1-host" 221 vppTapIP = "10.10.0.2" 222 linuxTapIP = "10.10.0.3" 223 vppTapIPWithMask = vppTapIP + "/24" 224 linuxTapIPWithMask = linuxTapIP + "/24" 225 ) 226 227 // configure VPP to act as DNS cache server 228 dnsCacheServer := &vpp_dns.DNSCache{ 229 UpstreamDnsServers: []string{upstreamDNSServer.String()}, 230 } 231 232 // tap tunnel from VPP to container linux environment 233 vppTap := &vpp_interfaces.Interface{ 234 Name: vppTapName, 235 Type: vpp_interfaces.Interface_TAP, 236 Enabled: true, 237 IpAddresses: []string{vppTapIPWithMask}, 238 Link: &vpp_interfaces.Interface_Tap{ 239 Tap: &vpp_interfaces.TapLink{ 240 Version: 2, 241 HostIfName: linuxTapName, 242 //ToMicroservice: MsNamePrefix + msName, 243 }, 244 }, 245 } 246 linuxTap := &linux_interfaces.Interface{ 247 Name: linuxTapName, 248 Type: linux_interfaces.Interface_TAP_TO_VPP, 249 Enabled: true, 250 HostIfName: linuxTapName, 251 IpAddresses: []string{linuxTapIPWithMask}, 252 Link: &linux_interfaces.Interface_Tap{ 253 Tap: &linux_interfaces.TapLink{ 254 VppTapIfName: vppTapName, 255 }, 256 }, 257 } 258 259 // routing all packets out of vpp using tap tunnel 260 // (requesting upstream DNS server + responding to clients of VPP in role of DNS server) 261 vppRouteOut := &vpp_l3.Route{ 262 DstNetwork: "0.0.0.0/0", 263 NextHopAddr: linuxTapIP, 264 OutgoingInterface: vppTapName, 265 } 266 267 // NAT translation so that VPP-Agent container IP address with standard DNS port (53) acts as DNS Server service. 268 // The VPP handles DNS packets from its inner tap tunnel end, but packets arrive at default container interface. 269 // So additional path to join these 2 places is done (tap + linux routing), but that is not enough due to 270 // using container ip address as DNS Server service address. The DNS packet must be forwarded to VPP, but it 271 // thinks that it has already arrived where it is supposed to be (external IP of container), so the destination 272 // address must be translated to arrive in VPP. This happens by configuring linux kernel's prerouting chain of 273 // NAT table. The answer to DNS request must be also translated (changed source IP) by using postrouting chain 274 // of NAT table. The DNS traffic should normally stay only on port 53, but VPP contacts upstream DNS server 275 // (to consult unknown DNS domain names) with source port 53053 and that makes trouble when answer return 276 // from these upstream DNS servers. Hence, the NAT also for 53053 port. 277 vppDNSCommunicationPrerouting := &linux_iptables.RuleChain{ 278 Name: "VPPDNSCommunicationPrerouting", 279 Protocol: linux_iptables.RuleChain_IPV4, 280 Table: linux_iptables.RuleChain_NAT, 281 ChainType: linux_iptables.RuleChain_PREROUTING, 282 Rules: []string{ 283 // NAT for communication between client and VPP DNS server 284 fmt.Sprintf("-p tcp -d %s --dport 53 -j DNAT --to-destination %s", vppDNSServer.String(), vppTapIP), 285 fmt.Sprintf("-p udp -d %s --dport 53 -j DNAT --to-destination %s", vppDNSServer.String(), vppTapIP), 286 // NAT for communication with upstream DNS server 287 fmt.Sprintf("-p tcp -d %s --dport 53053 -j DNAT --to-destination %s", vppDNSServer.String(), vppTapIP), 288 fmt.Sprintf("-p udp -d %s --dport 53053 -j DNAT --to-destination %s", vppDNSServer.String(), vppTapIP), 289 }, 290 } 291 vppDNSCommunicationPostrouting := &linux_iptables.RuleChain{ 292 Name: "VPPDNSCommunicationPostrouting", 293 Protocol: linux_iptables.RuleChain_IPV4, 294 Table: linux_iptables.RuleChain_NAT, 295 ChainType: linux_iptables.RuleChain_POSTROUTING, 296 Rules: []string{ 297 // NAT for communication between client and VPP DNS server 298 fmt.Sprintf("-p tcp -s %s --sport 53 -j SNAT --to-source %s", vppTapIP, vppDNSServer.String()), 299 fmt.Sprintf("-p udp -s %s --sport 53 -j SNAT --to-source %s", vppTapIP, vppDNSServer.String()), 300 // NAT for communication with upstream DNS server 301 fmt.Sprintf("-p tcp -s %s --sport 53053 -j SNAT --to-source %s", vppTapIP, vppDNSServer.String()), 302 fmt.Sprintf("-p udp -s %s --sport 53053 -j SNAT --to-source %s", vppTapIP, vppDNSServer.String()), 303 }, 304 } 305 306 // apply the configuration 307 req := ctx.GenericClient().ChangeRequest() 308 err := req.Update( 309 dnsCacheServer, 310 vppTap, 311 linuxTap, 312 vppRouteOut, 313 vppDNSCommunicationPrerouting, 314 vppDNSCommunicationPostrouting, 315 ).Send(context.Background()) 316 return err 317 } 318 319 // useMicroserviceWithDig provides modifier for using specialized microservice image for using linux dig tool 320 func useMicroserviceWithDig() MicroserviceOptModifier { 321 return WithMSContainerStartHook(func(opts *docker.CreateContainerOptions) { 322 // use different image (+ entrypoint usage in image needs changes in container start) 323 opts.Config.Image = "itsthenetwork/alpine-dig" 324 opts.Config.Entrypoint = []string{"tail", "-f", "/dev/null"} 325 opts.Config.Cmd = []string{} 326 opts.HostConfig.NetworkMode = "bridge" // return back to default docker networking 327 }) 328 }