diff --git a/examples/customization/external-auth-headers/nginx/Makefile b/examples/customization/external-auth-headers/nginx/Makefile new file mode 100644 index 000000000..65875f426 --- /dev/null +++ b/examples/customization/external-auth-headers/nginx/Makefile @@ -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 + diff --git a/examples/customization/external-auth-headers/nginx/README.md b/examples/customization/external-auth-headers/nginx/README.md new file mode 100644 index 000000000..a705dd220 --- /dev/null +++ b/examples/customization/external-auth-headers/nginx/README.md @@ -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 +< + +403 Forbidden + +

403 Forbidden

+
nginx/1.11.10
+ + +* 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 +``` diff --git a/examples/customization/external-auth-headers/nginx/authsvc/Dockerfile b/examples/customization/external-auth-headers/nginx/authsvc/Dockerfile new file mode 100644 index 000000000..318eab4e8 --- /dev/null +++ b/examples/customization/external-auth-headers/nginx/authsvc/Dockerfile @@ -0,0 +1,5 @@ +FROM alpine:3.5 +MAINTAINER Roman Safronov +COPY authsvc / +EXPOSE 8080 +ENTRYPOINT ["/authsvc"] diff --git a/examples/customization/external-auth-headers/nginx/authsvc/authsvc.go b/examples/customization/external-auth-headers/nginx/authsvc/authsvc.go new file mode 100644 index 000000000..5ca9ffbb8 --- /dev/null +++ b/examples/customization/external-auth-headers/nginx/authsvc/authsvc.go @@ -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) +} diff --git a/examples/customization/external-auth-headers/nginx/deploy/auth-service.yaml b/examples/customization/external-auth-headers/nginx/deploy/auth-service.yaml new file mode 100644 index 000000000..87a58730b --- /dev/null +++ b/examples/customization/external-auth-headers/nginx/deploy/auth-service.yaml @@ -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 diff --git a/examples/customization/external-auth-headers/nginx/deploy/default-backend.yaml b/examples/customization/external-auth-headers/nginx/deploy/default-backend.yaml new file mode 100644 index 000000000..ae6227507 --- /dev/null +++ b/examples/customization/external-auth-headers/nginx/deploy/default-backend.yaml @@ -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 diff --git a/examples/customization/external-auth-headers/nginx/deploy/echo-service.yaml b/examples/customization/external-auth-headers/nginx/deploy/echo-service.yaml new file mode 100644 index 000000000..d4bbe29cc --- /dev/null +++ b/examples/customization/external-auth-headers/nginx/deploy/echo-service.yaml @@ -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: / diff --git a/examples/customization/external-auth-headers/nginx/deploy/nginx-ingress-controller.yaml b/examples/customization/external-auth-headers/nginx/deploy/nginx-ingress-controller.yaml new file mode 100644 index 000000000..28eb85074 --- /dev/null +++ b/examples/customization/external-auth-headers/nginx/deploy/nginx-ingress-controller.yaml @@ -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 diff --git a/examples/customization/external-auth-headers/nginx/echosvc/Dockerfile b/examples/customization/external-auth-headers/nginx/echosvc/Dockerfile new file mode 100644 index 000000000..a8fac219d --- /dev/null +++ b/examples/customization/external-auth-headers/nginx/echosvc/Dockerfile @@ -0,0 +1,5 @@ +FROM alpine:3.5 +MAINTAINER Roman Safronov +COPY echosvc / +EXPOSE 8080 +ENTRYPOINT ["/echosvc"] diff --git a/examples/customization/external-auth-headers/nginx/echosvc/echosvc.go b/examples/customization/external-auth-headers/nginx/echosvc/echosvc.go new file mode 100644 index 000000000..d8d5dce83 --- /dev/null +++ b/examples/customization/external-auth-headers/nginx/echosvc/echosvc.go @@ -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) +}