Skip to content
This repository has been archived by the owner on Apr 17, 2019. It is now read-only.

[nginx-ingress-controller] Allow authentication in Ingress rules #946

Merged
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions ingress/Godeps/Godeps.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ingress/controllers/nginx/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nginx-ingress-controller
23 changes: 23 additions & 0 deletions ingress/controllers/nginx/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,29 @@ This means only one of the rules should define annotations to configure the upst

Please check the [auth](examples/custom-upstream-check/README.md) example

### Authentication

Is possible to add authentication adding additional annotations in the Ingress rule. The source of the authentication is a secret that contains usernames and passwords inside the the key `auth`

The annotations are:

```
ingress-nginx.kubernetes.io/auth-type:[basic|digest]
```

Indicates the [HTTP Authentication Type: Basic or Digest Access Authentication](https://tools.ietf.org/html/rfc2617).

```
ingress-nginx.kubernetes.io/auth-secret:secretName
```

Name of the secret that contains the usernames and passwords with access to the `path/s` defined in the Ingress Rule.
The secret must be created in the same namespace than the Ingress rule

```
ingress-nginx.kubernetes.io/auth-realm:"realm string"
```


### NGINX status page

Expand Down
16 changes: 13 additions & 3 deletions ingress/controllers/nginx/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ import (
"k8s.io/kubernetes/pkg/util/intstr"
"k8s.io/kubernetes/pkg/watch"

"k8s.io/contrib/ingress/controllers/nginx/healthcheck"
"k8s.io/contrib/ingress/controllers/nginx/nginx"
"k8s.io/contrib/ingress/controllers/nginx/nginx/auth"
"k8s.io/contrib/ingress/controllers/nginx/nginx/healthcheck"
"k8s.io/contrib/ingress/controllers/nginx/nginx/ratelimit"
"k8s.io/contrib/ingress/controllers/nginx/nginx/rewrite"
)
Expand Down Expand Up @@ -584,6 +585,12 @@ func (lbc *loadBalancerController) getUpstreamServers(ngxCfg nginx.NginxConfigur
continue
}

nginxAuth, err := auth.ParseAnnotations(lbc.client, ing, auth.DefAuthDirectory)
glog.V(3).Infof("nginx auth %v", nginxAuth)
if err != nil {
glog.V(3).Infof("error reading authentication in Ingress %v/%v: %v", ing.GetNamespace(), ing.GetName(), err)
}

rl, err := ratelimit.ParseAnnotations(ing)
glog.V(3).Infof("nginx rate limit %v", rl)
if err != nil {
Expand Down Expand Up @@ -617,12 +624,14 @@ func (lbc *loadBalancerController) getUpstreamServers(ngxCfg nginx.NginxConfigur
for _, loc := range server.Locations {
if loc.Path == rootLocation && nginxPath == rootLocation && loc.IsDefBackend {
loc.Upstream = *ups
loc.Auth = *nginxAuth
loc.RateLimit = *rl

locRew, err := rewrite.ParseAnnotations(ing)
if err != nil {
glog.V(3).Infof("error parsing rewrite annotations for Ingress rule %v/%v: %v", ing.GetNamespace(), ing.GetName(), err)
}
loc.Redirect = *locRew
loc.RateLimit = *rl

addLoc = false
continue
Expand All @@ -645,8 +654,9 @@ func (lbc *loadBalancerController) getUpstreamServers(ngxCfg nginx.NginxConfigur
server.Locations = append(server.Locations, &nginx.Location{
Path: nginxPath,
Upstream: *ups,
Redirect: *locRew,
Auth: *nginxAuth,
RateLimit: *rl,
Redirect: *locRew,
})
}
}
Expand Down
126 changes: 126 additions & 0 deletions ingress/controllers/nginx/examples/auth/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@

This example shows how to add authentication in a Ingress rule using a secret that contains a file generated with `htpasswd`.


```
$ htpasswd -c auth foo
New password: <bar>
New password:
Re-type new password:
Adding password for user foo
```

```
$ kubectl create secret generic basic-auth --from-file=auth
secret "basic-auth" created
```

```
$ kubectl get secret basic-auth -o yaml
apiVersion: v1
data:
auth: Zm9vOiRhcHIxJE9GRzNYeWJwJGNrTDBGSERBa29YWUlsSDkuY3lzVDAK
kind: Secret
metadata:
name: basic-auth
namespace: default
type: Opaque
```

```
echo "
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-with-auth
annotations:
# type of authentication
ingress-nginx.kubernetes.io/auth-type: basic
# name of the secret that contains the user/password definitions
ingress-nginx.kubernetes.io/auth-secret: basic-auth
# message to display with an appropiate context why the authentication is required
ingress-nginx.kubernetes.io/auth-realm: "Authentication Required - foo"
spec:
rules:
- host: foo.bar.com
http:
paths:
- path: /
backend:
serviceName: echoheaders
servicePort: 80
" | kubectl create -f -

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add text inbetween the quoted blocks explaining what each block is doing

```

```
$ curl -v http://10.2.29.4/ -H 'Host: foo.bar.com'
* Trying 10.2.29.4...
* Connected to 10.2.29.4 (10.2.29.4) port 80 (#0)
> GET / HTTP/1.1
> Host: foo.bar.com
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< Server: nginx/1.10.0
< Date: Wed, 11 May 2016 05:27:23 GMT
< Content-Type: text/html
< Content-Length: 195
< Connection: keep-alive
< WWW-Authenticate: Basic realm="Authentication Required - foo"
<
<html>
<head><title>401 Authorization Required</title></head>
<body bgcolor="white">
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx/1.10.0</center>
</body>
</html>
* Connection #0 to host 10.2.29.4 left intact
```

```
$ curl -v http://10.2.29.4/ -H 'Host: foo.bar.com' -u 'foo:bar'
* Trying 10.2.29.4...
* Connected to 10.2.29.4 (10.2.29.4) port 80 (#0)
* Server auth using Basic with user 'foo'
> GET / HTTP/1.1
> Host: foo.bar.com
> Authorization: Basic Zm9vOmJhcg==
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.10.0
< Date: Wed, 11 May 2016 06:05:26 GMT
< Content-Type: text/plain
< Transfer-Encoding: chunked
< Connection: keep-alive
< Vary: Accept-Encoding
<
CLIENT VALUES:
client_address=10.2.29.4
command=GET
real path=/
query=nil
request_version=1.1
request_uri=http://foo.bar.com:8080/

SERVER VALUES:
server_version=nginx: 1.9.11 - lua: 10001

HEADERS RECEIVED:
accept=*/*
authorization=Basic Zm9vOmJhcg==
connection=close
host=foo.bar.com
user-agent=curl/7.43.0
x-forwarded-for=10.2.29.1
x-forwarded-host=foo.bar.com
x-forwarded-port=80
x-forwarded-proto=http
x-real-ip=10.2.29.1
BODY:
* Connection #0 to host 10.2.29.4 left intact
-no body in request-
```
12 changes: 12 additions & 0 deletions ingress/controllers/nginx/nginx.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,18 @@ http {
{{ $limits := buildRateLimit $location }}
{{- range $limit := $limits }}
{{ $limit }}{{ end }}

{{ if $location.Auth.Secured -}}
{{ if eq $location.Auth.Type "basic" }}
auth_basic "{{ $location.Auth.Realm }}";
auth_basic_user_file {{ $location.Auth.File }};
{{ else }}
#TODO: add nginx-http-auth-digest module
auth_digest "{{ $location.Auth.Realm }}";
auth_digest_user_file {{ $location.Auth.File }};
{{ end }}
{{- end }}

proxy_set_header Host $host;

# Pass Real IP
Expand Down
Loading