Add support for IPV6 in dns resolvers
This commit is contained in:
parent
8e90fc0290
commit
99209ad33d
10 changed files with 153 additions and 16 deletions
|
@ -227,9 +227,6 @@ http://nginx.org/en/docs/http/ngx_http_core_module.html#keepalive_timeout
|
|||
**proxy-buffer-size:** Sets the size of the buffer used for [reading the first part of the response](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffer_size) received from the proxied server. This part usually contains a small response header.`
|
||||
|
||||
|
||||
**resolver:** Configures name servers used to [resolve](http://nginx.org/en/docs/http/ngx_http_core_module.html#resolver) names of upstream servers into addresses
|
||||
|
||||
|
||||
**server-name-hash-max-size:** Sets the maximum size of the [server names hash tables](http://nginx.org/en/docs/http/ngx_http_core_module.html#server_names_hash_max_size) used in server names, map directive’s values, MIME types, names of request header strings, etc.
|
||||
http://nginx.org/en/docs/hash.html
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import (
|
|||
|
||||
"k8s.io/ingress/controllers/nginx/pkg/config"
|
||||
"k8s.io/ingress/core/pkg/ingress/defaults"
|
||||
"k8s.io/ingress/core/pkg/net/dns"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -97,6 +98,13 @@ func ReadConfig(conf *api.ConfigMap) config.Configuration {
|
|||
if err != nil {
|
||||
glog.Infof("%v", err)
|
||||
}
|
||||
|
||||
nss, err := dns.GetSystemNameServers()
|
||||
if err != nil {
|
||||
glog.Infof("unexpected error reading /etc/resolv.conf file: %v", err)
|
||||
}
|
||||
to.Resolver = nss
|
||||
|
||||
return to
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os/exec"
|
||||
"strings"
|
||||
text_template "text/template"
|
||||
|
@ -29,6 +30,7 @@ import (
|
|||
|
||||
"k8s.io/ingress/controllers/nginx/pkg/config"
|
||||
"k8s.io/ingress/core/pkg/ingress"
|
||||
ing_net "k8s.io/ingress/core/pkg/net"
|
||||
"k8s.io/ingress/core/pkg/watch"
|
||||
)
|
||||
|
||||
|
@ -134,6 +136,7 @@ var (
|
|||
"buildRateLimitZones": buildRateLimitZones,
|
||||
"buildRateLimit": buildRateLimit,
|
||||
"buildSSPassthroughUpstreams": buildSSPassthroughUpstreams,
|
||||
"buildResolvers": buildResolvers,
|
||||
|
||||
"contains": strings.Contains,
|
||||
"hasPrefix": strings.HasPrefix,
|
||||
|
@ -143,6 +146,27 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
// buildResolvers returns the resolvers reading the /etc/resolv.conf file
|
||||
func buildResolvers(a interface{}) string {
|
||||
// NGINX need IPV6 addresses to be surrounded by brakets
|
||||
nss := a.([]net.IP)
|
||||
if len(nss) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
r := []string{"resolver"}
|
||||
for _, ns := range nss {
|
||||
if ing_net.IsIPV6(ns) {
|
||||
r = append(r, fmt.Sprintf("[%v]", ns))
|
||||
} else {
|
||||
r = append(r, fmt.Sprintf("%v", ns))
|
||||
}
|
||||
}
|
||||
r = append(r, "valid=30s;")
|
||||
|
||||
return strings.Join(r, " ")
|
||||
}
|
||||
|
||||
func buildSSPassthroughUpstreams(b interface{}, sslb interface{}) string {
|
||||
backends := b.([]*ingress.Backend)
|
||||
sslBackends := sslb.([]*ingress.SSLPassthroughBackend)
|
||||
|
|
|
@ -85,10 +85,7 @@ http {
|
|||
access_log /var/log/nginx/access.log upstreaminfo if=$loggable;
|
||||
error_log /var/log/nginx/error.log {{ $cfg.ErrorLogLevel }};
|
||||
|
||||
{{ if not (empty $cfg.Resolver) }}# Custom dns resolver.
|
||||
resolver {{ $cfg.Resolver }} valid=30s;
|
||||
resolver_timeout 10s;
|
||||
{{ end }}
|
||||
{{ buildResolvers $cfg.Resolver }}
|
||||
|
||||
{{/* Whenever nginx proxies a request without a "Connection" header, the "Connection" header is set to "close" */}}
|
||||
{{/* when making the target request. This means that you cannot simply use */}}
|
||||
|
|
|
@ -992,7 +992,7 @@ func (ic *GenericController) getEndpoints(
|
|||
}
|
||||
|
||||
// check for invalid port value
|
||||
if targetPort <=0 {
|
||||
if targetPort <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package defaults
|
||||
|
||||
import "net"
|
||||
|
||||
// Backend defines the mandatory configuration that an Ingress controller must provide
|
||||
// The reason of this requirements is the annotations are generic. If some implementation do not supports
|
||||
// one or more annotations it just can provides defaults
|
||||
|
@ -30,9 +32,9 @@ type Backend struct {
|
|||
// http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffer_size)
|
||||
ProxyBufferSize string `structs:"proxy-buffer-size"`
|
||||
|
||||
// Configures name servers used to resolve names of upstream servers into addresses
|
||||
// http://nginx.org/en/docs/http/ngx_http_core_module.html#resolver
|
||||
Resolver string `structs:"resolver"`
|
||||
// Name server/s used to resolve names of upstream servers into IP addresses.
|
||||
// The file /etc/resolv.conf is used as DNS resolution configuration.
|
||||
Resolver []net.IP
|
||||
|
||||
// SkipAccessLogURLs sets a list of URLs that should not appear in the NGINX access log
|
||||
// This is useful with urls like `/health` or `health-check` that make "complex" reading the logs
|
||||
|
|
|
@ -18,15 +18,18 @@ package dns
|
|||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
var defResolvConf = "/etc/resolv.conf"
|
||||
|
||||
// GetSystemNameServers returns the list of nameservers located in the file /etc/resolv.conf
|
||||
func GetSystemNameServers() ([]string, error) {
|
||||
var nameservers []string
|
||||
file, err := ioutil.ReadFile("/etc/resolv.conf")
|
||||
func GetSystemNameServers() ([]net.IP, error) {
|
||||
var nameservers []net.IP
|
||||
file, err := ioutil.ReadFile(defResolvConf)
|
||||
if err != nil {
|
||||
return nameservers, err
|
||||
}
|
||||
|
@ -43,10 +46,13 @@ func GetSystemNameServers() ([]string, error) {
|
|||
continue
|
||||
}
|
||||
if fields[0] == "nameserver" {
|
||||
nameservers = append(nameservers, fields[1:]...)
|
||||
ip := net.ParseIP(fields[1])
|
||||
if ip != nil {
|
||||
nameservers = append(nameservers, ip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glog.V(3).Infof("nameservers to use: %v", nameservers)
|
||||
glog.V(3).Infof("nameservers IP address/es to use: %v", nameservers)
|
||||
return nameservers, nil
|
||||
}
|
||||
|
|
|
@ -17,6 +17,9 @@ limitations under the License.
|
|||
package dns
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -28,4 +31,31 @@ func TestGetDNSServers(t *testing.T) {
|
|||
if len(s) < 1 {
|
||||
t.Error("expected at least 1 nameserver in /etc/resolv.conf")
|
||||
}
|
||||
|
||||
file, err := ioutil.TempFile("", "fw")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
defer os.Remove(file.Name())
|
||||
|
||||
ioutil.WriteFile(file.Name(), []byte(`
|
||||
nameserver 2001:4860:4860::8844
|
||||
nameserver 2001:4860:4860::8888
|
||||
nameserver 8.8.8.8
|
||||
`), 0644)
|
||||
|
||||
defResolvConf = file.Name()
|
||||
s, err = GetSystemNameServers()
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error reading /etc/resolv.conf file: %v", err)
|
||||
}
|
||||
if len(s) < 3 {
|
||||
t.Errorf("expected at 3 nameservers but %v returned", len(s))
|
||||
}
|
||||
|
||||
eip := net.ParseIP("2001:4860:4860::8844")
|
||||
if !s[0].Equal(eip) {
|
||||
t.Errorf("expected %v as nameservers but %v returned", eip, s[0])
|
||||
}
|
||||
}
|
||||
|
|
30
core/pkg/net/net.go
Normal file
30
core/pkg/net/net.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
_net "net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// IsIPV6 checks if the input contains a valid IPV6 address
|
||||
func IsIPV6(ip _net.IP) bool {
|
||||
if dp := strings.Index(ip.String(), ":"); dp != -1 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
43
core/pkg/net/net_test.go
Normal file
43
core/pkg/net/net_test.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIsIPV6(t *testing.T) {
|
||||
tests := []struct {
|
||||
in net.IP
|
||||
isIPV6 bool
|
||||
}{
|
||||
{net.ParseIP("2001:4860:4860::8844"), true},
|
||||
{net.ParseIP("2001:4860:4860::8888"), true},
|
||||
{net.ParseIP("0:0:0:0:0:ffff:c868:8165"), true},
|
||||
{net.ParseIP("2001:db8:85a3::8a2e:370:7334"), true},
|
||||
{net.ParseIP("::1"), true},
|
||||
{net.ParseIP("8.8.8.8"), false},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
isIPV6 := IsIPV6(test.in)
|
||||
if isIPV6 && !test.isIPV6 {
|
||||
t.Fatalf("%v expected %v but returned %v", test.in, test.isIPV6, isIPV6)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue