Added: example of external service response headers propagation (requested by @aledbf)

This commit is contained in:
rsafronov 2017-03-13 16:45:27 -04:00
parent 7034e1de69
commit 5ee1eed434
10 changed files with 418 additions and 0 deletions

View file

@ -0,0 +1,23 @@
all: push
TAG=0.1
PREFIX?=electroma/ingress-demo-
ARCH?=amd64
GOLANG_VERSION=1.8
TEMP_DIR:=$(shell mktemp -d)
build: clean
CGO_ENABLED=0 GOOS=linux GOARCH=$(ARCH) go build -o authsvc/authsvc authsvc/authsvc.go
CGO_ENABLED=0 GOOS=linux GOARCH=$(ARCH) go build -o echosvc/echosvc echosvc/echosvc.go
container: build
docker build --pull -t $(PREFIX)authsvc-$(ARCH):$(TAG) authsvc
docker build --pull -t $(PREFIX)echosvc-$(ARCH):$(TAG) echosvc
push: container
docker push $(PREFIX)authsvc-$(ARCH):$(TAG)
docker push $(PREFIX)echosvc-$(ARCH):$(TAG)
clean:
rm -f authsvc/authsvc echosvc/echosvc

View file

@ -0,0 +1,138 @@
# External authentication, authentication service response headers propagation
This example demonstrates propagation of selected authentication service response headers
to backend service.
Sample configuration includes:
* Sample authentication service producing several response headers
* Authentication logic is based on HTTP header: requests with header `User` containing string `internal` are considered authenticated
* After successful authentication service generates response headers `UserID` and `UserRole`
* Sample echo service displaying header information
* Two ingress objects pointing to echo service
* Public, which allows access from unauthenticated users
* Private, which allows access from authenticated users only
You can deploy the controller as
follows:
```console
$ kubectl create -f deploy/
deployment "demo-auth-service" created
service "demo-auth-service" created
ingress "demo-auth-service" created
deployment "default-http-backend" created
service "default-http-backend" created
deployment "demo-echo-service" created
service "demo-echo-service" created
ingress "public-demo-echo-service" created
ingress "secure-demo-echo-service" created
deployment "nginx-ingress-controller" created
$ kubectl get po
NAME READY STATUS RESTARTS AGE
NAME READY STATUS RESTARTS AGE
default-http-backend-2657704409-vv0hm 1/1 Running 0 29s
demo-auth-service-2769076528-7g9mh 1/1 Running 0 30s
demo-echo-service-3636052215-3vw8c 1/1 Running 0 29s
kubectl get ing
NAME HOSTS ADDRESS PORTS AGE
public-demo-echo-service public-demo-echo-service.kube.local 80 1m
secure-demo-echo-service secure-demo-echo-service.kube.local 80 1m
```
Test 1: public service with no auth header
```
$ curl -H 'Host: public-demo-echo-service.kube.local' -v 192.168.99.100
* Rebuilt URL to: 192.168.99.100/
* Trying 192.168.99.100...
* Connected to 192.168.99.100 (192.168.99.100) port 80 (#0)
> GET / HTTP/1.1
> Host: public-demo-echo-service.kube.local
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.11.10
< Date: Mon, 13 Mar 2017 20:19:21 GMT
< Content-Type: text/plain; charset=utf-8
< Content-Length: 20
< Connection: keep-alive
<
* Connection #0 to host 192.168.99.100 left intact
UserID: , UserRole:
```
Test 2: secure service with no auth header
```
$ curl -H 'Host: secure-demo-echo-service.kube.local' -v 192.168.99.100
* Rebuilt URL to: 192.168.99.100/
* Trying 192.168.99.100...
* Connected to 192.168.99.100 (192.168.99.100) port 80 (#0)
> GET / HTTP/1.1
> Host: secure-demo-echo-service.kube.local
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 403 Forbidden
< Server: nginx/1.11.10
< Date: Mon, 13 Mar 2017 20:18:48 GMT
< Content-Type: text/html
< Content-Length: 170
< Connection: keep-alive
<
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.11.10</center>
</body>
</html>
* Connection #0 to host 192.168.99.100 left intact
```
Test 3: public service with valid auth header
```
$ curl -H 'Host: public-demo-echo-service.kube.local' -H 'User:internal' -v 192.168.99.100
* Rebuilt URL to: 192.168.99.100/
* Trying 192.168.99.100...
* Connected to 192.168.99.100 (192.168.99.100) port 80 (#0)
> GET / HTTP/1.1
> Host: public-demo-echo-service.kube.local
> User-Agent: curl/7.43.0
> Accept: */*
> User:internal
>
< HTTP/1.1 200 OK
< Server: nginx/1.11.10
< Date: Mon, 13 Mar 2017 20:19:59 GMT
< Content-Type: text/plain; charset=utf-8
< Content-Length: 44
< Connection: keep-alive
<
* Connection #0 to host 192.168.99.100 left intact
UserID: 1443635317331776148, UserRole: admin
```
Test 4: public service with valid auth header
```
$ curl -H 'Host: secure-demo-echo-service.kube.local' -H 'User:internal' -v 192.168.99.100
* Rebuilt URL to: 192.168.99.100/
* Trying 192.168.99.100...
* Connected to 192.168.99.100 (192.168.99.100) port 80 (#0)
> GET / HTTP/1.1
> Host: secure-demo-echo-service.kube.local
> User-Agent: curl/7.43.0
> Accept: */*
> User:internal
>
< HTTP/1.1 200 OK
< Server: nginx/1.11.10
< Date: Mon, 13 Mar 2017 20:17:23 GMT
< Content-Type: text/plain; charset=utf-8
< Content-Length: 43
< Connection: keep-alive
<
* Connection #0 to host 192.168.99.100 left intact
UserID: 605394647632969758, UserRole: admin
```

View file

@ -0,0 +1,5 @@
FROM alpine:3.5
MAINTAINER Roman Safronov <electroma@gmail.com>
COPY authsvc /
EXPOSE 8080
ENTRYPOINT ["/authsvc"]

View file

@ -0,0 +1,33 @@
package main
import (
"fmt"
"net/http"
"strings"
"math/rand"
"strconv"
)
// Sample authentication service returning several HTTP headers in response
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if strings.ContainsAny(r.Header.Get("User"), "internal") {
w.Header().Add("UserID", strconv.Itoa(rand.Int()))
w.Header().Add("UserRole", "admin")
w.Header().Add("Other", "not used")
fmt.Fprint(w, "ok")
} else {
rc := http.StatusForbidden
if c := r.URL.Query().Get("code"); len(c) > 0 {
c, _ := strconv.Atoi(c)
if c > 0 && c < 600 {
rc = c
}
}
w.WriteHeader(rc)
fmt.Fprint(w, "unauthorized")
}
})
http.ListenAndServe(":8080", nil)
}

View file

@ -0,0 +1,41 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: demo-auth-service
labels:
k8s-app: demo-auth-service
namespace: default
spec:
replicas: 1
template:
metadata:
labels:
k8s-app: demo-auth-service
spec:
terminationGracePeriodSeconds: 60
containers:
- name: auth-service
image: electroma/ingress-demo-authsvc-amd64:0.1
ports:
- containerPort: 8080
resources:
limits:
cpu: 10m
memory: 20Mi
requests:
cpu: 10m
memory: 20Mi
---
apiVersion: v1
kind: Service
metadata:
name: demo-auth-service
labels:
k8s-app: demo-auth-service
namespace: default
spec:
ports:
- port: 80
targetPort: 8080
selector:
k8s-app: demo-auth-service

View file

@ -0,0 +1,48 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: default-http-backend
labels:
k8s-app: default-http-backend
namespace: default
spec:
replicas: 1
template:
metadata:
labels:
k8s-app: default-http-backend
spec:
terminationGracePeriodSeconds: 60
containers:
- name: default-http-backend
image: gcr.io/google_containers/defaultbackend:1.0
livenessProbe:
httpGet:
path: /healthz
port: 8080
scheme: HTTP
initialDelaySeconds: 30
timeoutSeconds: 5
ports:
- containerPort: 8080
resources:
limits:
cpu: 10m
memory: 20Mi
requests:
cpu: 10m
memory: 20Mi
---
apiVersion: v1
kind: Service
metadata:
name: default-http-backend
namespace: default
labels:
k8s-app: default-http-backend
spec:
ports:
- port: 80
targetPort: 8080
selector:
k8s-app: default-http-backend

View file

@ -0,0 +1,77 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: demo-echo-service
labels:
k8s-app: demo-echo-service
namespace: default
spec:
replicas: 1
template:
metadata:
labels:
k8s-app: demo-echo-service
spec:
terminationGracePeriodSeconds: 60
containers:
- name: echo-service
image: electroma/ingress-demo-echosvc-amd64:0.1
ports:
- containerPort: 8080
resources:
limits:
cpu: 10m
memory: 20Mi
requests:
cpu: 10m
memory: 20Mi
---
apiVersion: v1
kind: Service
metadata:
name: demo-echo-service
labels:
k8s-app: demo-echo-service
namespace: default
spec:
ports:
- port: 80
targetPort: 8080
selector:
k8s-app: demo-echo-service
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: public-demo-echo-service
annotations:
ingress.kubernetes.io/auth-url: http://demo-auth-service.default.svc.cluster.local?code=200
ingress.kubernetes.io/auth-response-headers: UserID, UserRole
namespace: default
spec:
rules:
- host: public-demo-echo-service.kube.local
http:
paths:
- backend:
serviceName: demo-echo-service
servicePort: 80
path: /
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: secure-demo-echo-service
annotations:
ingress.kubernetes.io/auth-url: http://demo-auth-service.default.svc.cluster.local
ingress.kubernetes.io/auth-response-headers: UserID, UserRole
namespace: default
spec:
rules:
- host: secure-demo-echo-service.kube.local
http:
paths:
- backend:
serviceName: demo-echo-service
servicePort: 80
path: /

View file

@ -0,0 +1,33 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-ingress-controller
labels:
k8s-app: nginx-ingress-controller
namespace: kube-system
spec:
replicas: 1
template:
metadata:
labels:
k8s-app: nginx-ingress-controller
spec:
terminationGracePeriodSeconds: 60
containers:
- image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.2
name: nginx-ingress-controller
ports:
- containerPort: 80
hostPort: 80
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
args:
- /nginx-ingress-controller
- --default-backend-service=$(POD_NAMESPACE)/default-http-backend

View file

@ -0,0 +1,5 @@
FROM alpine:3.5
MAINTAINER Roman Safronov <electroma@gmail.com>
COPY echosvc /
EXPOSE 8080
ENTRYPOINT ["/echosvc"]

View file

@ -0,0 +1,15 @@
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "UserID: %s, UserRole: %s", r.Header.Get("UserID"), r.Header.Get("UserRole"))}
// Sample "echo" service displaying UserID and UserRole HTTP request headers
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}