commit
6b80ddaada
6 changed files with 22 additions and 89 deletions
|
@ -14,6 +14,7 @@ This is an nginx Ingress controller that uses [ConfigMap](https://kubernetes.io/
|
||||||
* [HTTPS enforcement](#server-side-https-enforcement)
|
* [HTTPS enforcement](#server-side-https-enforcement)
|
||||||
* [HSTS](#http-strict-transport-security)
|
* [HSTS](#http-strict-transport-security)
|
||||||
* [Kube-Lego](#automated-certificate-management-with-kube-lego)
|
* [Kube-Lego](#automated-certificate-management-with-kube-lego)
|
||||||
|
* [Source IP address](#source-ip-address)
|
||||||
* [TCP Services](#exposing-tcp-services)
|
* [TCP Services](#exposing-tcp-services)
|
||||||
* [UDP Services](#exposing-udp-services)
|
* [UDP Services](#exposing-udp-services)
|
||||||
* [Proxy Protocol](#proxy-protocol)
|
* [Proxy Protocol](#proxy-protocol)
|
||||||
|
@ -333,6 +334,13 @@ version to fully support Kube-Lego is nginx Ingress controller 0.8.
|
||||||
[Kube-Lego]:https://github.com/jetstack/kube-lego
|
[Kube-Lego]:https://github.com/jetstack/kube-lego
|
||||||
[Let's Encrypt]:https://letsencrypt.org
|
[Let's Encrypt]:https://letsencrypt.org
|
||||||
|
|
||||||
|
## Source IP address
|
||||||
|
|
||||||
|
By default NGINX uses the content of the header `X-Forwarded-For` as the source of truth to get information about the client IP address. This works without issues in L7 **if we configure the setting `proxy-real-ip-cidr`** with the correct information of the IP/network address of the external load balancer.
|
||||||
|
If the ingress controller is running in AWS we need to use the VPC IPv4 CIDR. This allows NGINX to avoid the spoofing of the header.
|
||||||
|
Another option is to enable proxy protocol using `use-proxy-protocol: "true"`.
|
||||||
|
In this mode NGINX do not uses the content of the header to get the source IP address of the connection.
|
||||||
|
|
||||||
## Exposing TCP services
|
## Exposing TCP services
|
||||||
|
|
||||||
Ingress does not support TCP services (yet). For this reason this Ingress controller uses the flag `--tcp-services-configmap` to point to an existing config map where the key is the external port to use and the value is `<namespace/service name>:<service port>:[PROXY]:[PROXY]`
|
Ingress does not support TCP services (yet). For this reason this Ingress controller uses the flag `--tcp-services-configmap` to point to an existing config map where the key is the external port to use and the value is `<namespace/service name>:<service port>:[PROXY]:[PROXY]`
|
||||||
|
|
|
@ -261,11 +261,6 @@ type Configuration struct {
|
||||||
// https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_headers_hash_bucket_size
|
// https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_headers_hash_bucket_size
|
||||||
ProxyHeadersHashBucketSize int `json:"proxy-headers-hash-bucket-size,omitempty"`
|
ProxyHeadersHashBucketSize int `json:"proxy-headers-hash-bucket-size,omitempty"`
|
||||||
|
|
||||||
// RealClientFrom defines the trusted source of the client source IP address
|
|
||||||
// The valid values are "auto", "http-proxy" and "tcp-proxy"
|
|
||||||
// Default: auto
|
|
||||||
RealClientFrom string `json:"real-client-from,omitempty"`
|
|
||||||
|
|
||||||
// Enables or disables emitting nginx version in error messages and in the “Server” response header field.
|
// Enables or disables emitting nginx version in error messages and in the “Server” response header field.
|
||||||
// http://nginx.org/en/docs/http/ngx_http_core_module.html#server_tokens
|
// http://nginx.org/en/docs/http/ngx_http_core_module.html#server_tokens
|
||||||
// Default: true
|
// Default: true
|
||||||
|
@ -479,7 +474,6 @@ func NewDefault() Configuration {
|
||||||
LimitConnZoneVariable: defaultLimitConnZoneVariable,
|
LimitConnZoneVariable: defaultLimitConnZoneVariable,
|
||||||
BindAddressIpv4: defBindAddress,
|
BindAddressIpv4: defBindAddress,
|
||||||
BindAddressIpv6: defBindAddress,
|
BindAddressIpv6: defBindAddress,
|
||||||
RealClientFrom: "auto",
|
|
||||||
ZipkinCollectorPort: 9411,
|
ZipkinCollectorPort: 9411,
|
||||||
ZipkinServiceName: "nginx",
|
ZipkinServiceName: "nginx",
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ package template
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -39,10 +38,6 @@ const (
|
||||||
bindAddress = "bind-address"
|
bindAddress = "bind-address"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
realClientRegex = regexp.MustCompile(`auto|http-proxy|tcp-proxy`)
|
|
||||||
)
|
|
||||||
|
|
||||||
// ReadConfig obtains the configuration defined by the user merged with the defaults.
|
// ReadConfig obtains the configuration defined by the user merged with the defaults.
|
||||||
func ReadConfig(src map[string]string) config.Configuration {
|
func ReadConfig(src map[string]string) config.Configuration {
|
||||||
conf := map[string]string{}
|
conf := map[string]string{}
|
||||||
|
@ -125,11 +120,6 @@ func ReadConfig(src map[string]string) config.Configuration {
|
||||||
glog.Warningf("unexpected error merging defaults: %v", err)
|
glog.Warningf("unexpected error merging defaults: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !realClientRegex.MatchString(to.RealClientFrom) {
|
|
||||||
glog.Warningf("unexpected value for RealClientFromSetting (%v). Using default \"auto\"", to.RealClientFrom)
|
|
||||||
to.RealClientFrom = "auto"
|
|
||||||
}
|
|
||||||
|
|
||||||
return to
|
return to
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -152,8 +152,6 @@ var (
|
||||||
},
|
},
|
||||||
"isValidClientBodyBufferSize": isValidClientBodyBufferSize,
|
"isValidClientBodyBufferSize": isValidClientBodyBufferSize,
|
||||||
"buildForwardedFor": buildForwardedFor,
|
"buildForwardedFor": buildForwardedFor,
|
||||||
"trustHTTPHeaders": trustHTTPHeaders,
|
|
||||||
"trustProxyProtocol": trustProxyProtocol,
|
|
||||||
"buildAuthSignURL": buildAuthSignURL,
|
"buildAuthSignURL": buildAuthSignURL,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -671,28 +669,6 @@ func buildForwardedFor(input interface{}) string {
|
||||||
return fmt.Sprintf("$http_%v", ffh)
|
return fmt.Sprintf("$http_%v", ffh)
|
||||||
}
|
}
|
||||||
|
|
||||||
func trustHTTPHeaders(input interface{}) bool {
|
|
||||||
conf, ok := input.(config.TemplateConfig)
|
|
||||||
if !ok {
|
|
||||||
glog.Errorf("%v", input)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return conf.Cfg.RealClientFrom == "http-proxy" ||
|
|
||||||
(conf.Cfg.RealClientFrom == "auto" && !conf.Cfg.UseProxyProtocol)
|
|
||||||
}
|
|
||||||
|
|
||||||
func trustProxyProtocol(input interface{}) bool {
|
|
||||||
conf, ok := input.(config.TemplateConfig)
|
|
||||||
if !ok {
|
|
||||||
glog.Errorf("%v", input)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return conf.Cfg.RealClientFrom == "tcp-proxy" ||
|
|
||||||
(conf.Cfg.RealClientFrom == "auto" && conf.Cfg.UseProxyProtocol)
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildAuthSignURL(input interface{}) string {
|
func buildAuthSignURL(input interface{}) string {
|
||||||
s, ok := input.(string)
|
s, ok := input.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -703,12 +679,12 @@ func buildAuthSignURL(input interface{}) string {
|
||||||
u, _ := url.Parse(s)
|
u, _ := url.Parse(s)
|
||||||
q := u.Query()
|
q := u.Query()
|
||||||
if len(q) == 0 {
|
if len(q) == 0 {
|
||||||
return fmt.Sprintf("%v?rd=$request_uri", s)
|
return fmt.Sprintf("%v?rd=$scheme://$http_host$request_uri", s)
|
||||||
}
|
}
|
||||||
|
|
||||||
if q.Get("rd") != "" {
|
if q.Get("rd") != "" {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%v&rd=$request_uri", s)
|
return fmt.Sprintf("%v&rd=$scheme://$http_host$request_uri", s)
|
||||||
}
|
}
|
||||||
|
|
|
@ -359,8 +359,8 @@ func TestBuildAuthSignURL(t *testing.T) {
|
||||||
cases := map[string]struct {
|
cases := map[string]struct {
|
||||||
Input, Output string
|
Input, Output string
|
||||||
}{
|
}{
|
||||||
"default url": {"http://google.com", "http://google.com?rd=$request_uri"},
|
"default url": {"http://google.com", "http://google.com?rd=$scheme://$http_host$request_uri"},
|
||||||
"with random field": {"http://google.com?cat=0", "http://google.com?cat=0&rd=$request_uri"},
|
"with random field": {"http://google.com?cat=0", "http://google.com?cat=0&rd=$scheme://$http_host$request_uri"},
|
||||||
"with rd field": {"http://google.com?cat&rd=$request", "http://google.com?cat&rd=$request"},
|
"with rd field": {"http://google.com?cat&rd=$request", "http://google.com?cat&rd=$request"},
|
||||||
}
|
}
|
||||||
for k, tc := range cases {
|
for k, tc := range cases {
|
||||||
|
|
|
@ -151,12 +151,13 @@ http {
|
||||||
'' close;
|
'' close;
|
||||||
}
|
}
|
||||||
|
|
||||||
{{ if (trustHTTPHeaders $all) }}
|
|
||||||
# Trust HTTP X-Forwarded-* Headers, but use direct values if they're missing.
|
|
||||||
map {{ buildForwardedFor $cfg.ForwardedForHeader }} $the_real_ip {
|
map {{ buildForwardedFor $cfg.ForwardedForHeader }} $the_real_ip {
|
||||||
# Get IP address from X-Forwarded-For HTTP header
|
{{ if $cfg.UseProxyProtocol }}
|
||||||
default $realip_remote_addr;
|
# Get IP address from Proxy Protocol
|
||||||
'' $remote_addr;
|
default $proxy_protocol_addr;
|
||||||
|
{{ else }}
|
||||||
|
default $remote_addr;
|
||||||
|
{{ end }}
|
||||||
}
|
}
|
||||||
|
|
||||||
# trust http_x_forwarded_proto headers correctly indicate ssl offloading
|
# trust http_x_forwarded_proto headers correctly indicate ssl offloading
|
||||||
|
@ -175,30 +176,6 @@ http {
|
||||||
'' $this_host;
|
'' $this_host;
|
||||||
}
|
}
|
||||||
|
|
||||||
{{ else }}
|
|
||||||
# Do not trust HTTP X-Forwarded-* Headers
|
|
||||||
map {{ buildForwardedFor $cfg.ForwardedForHeader }} $the_real_ip {
|
|
||||||
{{ if (trustProxyProtocol $all) }}
|
|
||||||
# Get IP address from Proxy Protocol
|
|
||||||
default $proxy_protocol_addr;
|
|
||||||
{{ else }}
|
|
||||||
# Get IP from direct remote address
|
|
||||||
default $realip_remote_addr;
|
|
||||||
{{ end }}
|
|
||||||
}
|
|
||||||
|
|
||||||
map $http_x_forwarded_host $best_http_host {
|
|
||||||
default $this_host;
|
|
||||||
}
|
|
||||||
map $http_x_forwarded_proto $pass_access_scheme {
|
|
||||||
default $scheme;
|
|
||||||
}
|
|
||||||
map $http_x_forwarded_port $pass_server_port {
|
|
||||||
default $server_port;
|
|
||||||
}
|
|
||||||
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ if $all.IsSSLPassthroughEnabled }}
|
{{ if $all.IsSSLPassthroughEnabled }}
|
||||||
# map port {{ $all.ListenPorts.SSLProxy }} to 443 for header X-Forwarded-Port
|
# map port {{ $all.ListenPorts.SSLProxy }} to 443 for header X-Forwarded-Port
|
||||||
map $pass_server_port $pass_port {
|
map $pass_server_port $pass_port {
|
||||||
|
@ -212,21 +189,6 @@ http {
|
||||||
}
|
}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
# Map a response error watching the header Content-Type
|
|
||||||
map $http_accept $httpAccept {
|
|
||||||
default html;
|
|
||||||
application/json json;
|
|
||||||
application/xml xml;
|
|
||||||
text/plain text;
|
|
||||||
}
|
|
||||||
|
|
||||||
map $httpAccept $httpReturnType {
|
|
||||||
default text/html;
|
|
||||||
json application/json;
|
|
||||||
xml application/xml;
|
|
||||||
text text/plain;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Obtain best http host
|
# Obtain best http host
|
||||||
map $http_host $this_host {
|
map $http_host $this_host {
|
||||||
default $http_host;
|
default $http_host;
|
||||||
|
@ -688,8 +650,8 @@ stream {
|
||||||
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
location {{ $path }} {
|
|
||||||
|
|
||||||
|
location {{ $path }} {
|
||||||
{{ if $all.Cfg.EnableVtsStatus }}{{ if $location.VtsFilterKey }} vhost_traffic_status_filter_by_set_key {{ $location.VtsFilterKey }};{{ end }}{{ end }}
|
{{ if $all.Cfg.EnableVtsStatus }}{{ if $location.VtsFilterKey }} vhost_traffic_status_filter_by_set_key {{ $location.VtsFilterKey }};{{ end }}{{ end }}
|
||||||
|
|
||||||
set $proxy_upstream_name "{{ buildUpstreamName $server.Hostname $all.Backends $location }}";
|
set $proxy_upstream_name "{{ buildUpstreamName $server.Hostname $all.Backends $location }}";
|
||||||
|
@ -786,6 +748,9 @@ stream {
|
||||||
proxy_set_header X-Original-URI $request_uri;
|
proxy_set_header X-Original-URI $request_uri;
|
||||||
proxy_set_header X-Scheme $pass_access_scheme;
|
proxy_set_header X-Scheme $pass_access_scheme;
|
||||||
|
|
||||||
|
# Pass the original X-Forwarded-For
|
||||||
|
proxy_set_header X-Original-Forwarded-For {{ buildForwardedFor $all.Cfg.ForwardedForHeader }};
|
||||||
|
|
||||||
# mitigate HTTPoxy Vulnerability
|
# mitigate HTTPoxy Vulnerability
|
||||||
# https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/
|
# https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/
|
||||||
proxy_set_header Proxy "";
|
proxy_set_header Proxy "";
|
||||||
|
|
Loading…
Reference in a new issue