Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NGINX Identity Aware Proxy #202

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d89265b
fork chart/oauth2-proxy
tamsky Apr 26, 2019
01c5305
add app config option: insecureSSL, which doesn't do what we need
tamsky Apr 26, 2019
e312b8a
add nginx configmap and deployment from ../openvpn/templates/
tamsky Apr 26, 2019
ac3939d
update Chart name, version
tamsky Apr 26, 2019
bde8e3e
move oauth{image,service,resource} settings in values.yaml and update…
tamsky Apr 26, 2019
09ace7a
rename template files to reflect contents, add nginx to values.yaml
tamsky Apr 26, 2019
edcba8a
update template value names
tamsky Apr 27, 2019
add0236
add fullname_ingress helper
tamsky Apr 27, 2019
8fe117b
update oauth.configmap metadata
tamsky Apr 27, 2019
a1028b4
update oauth.secret name
tamsky Apr 27, 2019
c0e8e44
move oauth container into nginx.deployment
tamsky Apr 27, 2019
3c5945e
Values: add top-level replicaCount, ingress settings, nginx service s…
tamsky Apr 27, 2019
c0138bc
hardcode oauth on port tcp/81, update deployment, nginx configs to fo…
tamsky Apr 27, 2019
fa859d3
add service for nginx proxy
tamsky Apr 27, 2019
027d049
add in auth_request rules, DNS_ZONE and iap_hostname
tamsky Apr 27, 2019
a38f424
valid requests now proxy to internal services
tamsky Apr 28, 2019
86b9622
fix final redirect after oauth2 final callback
tamsky Apr 28, 2019
18f53e5
add pr123 namespace and test services for testing
tamsky Apr 28, 2019
12dcf8d
cleanup
tamsky Apr 29, 2019
d7c8453
fix comments
tamsky Apr 29, 2019
cc48145
rename Chart to nginx-iap
tamsky Apr 29, 2019
4d33eb5
Merge branch 'master' into tamsky/oauth2-unlimited-staging-proxy
aknysh May 1, 2019
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
21 changes: 21 additions & 0 deletions incubator/nginx-iap/.helmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*~
# Various IDEs
.project
.idea/
*.tmproj
4 changes: 4 additions & 0 deletions incubator/nginx-iap/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
apiVersion: v1
description: A Helm chart for Kubernetes
Copy link
Member

Choose a reason for hiding this comment

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

update description

name: nginx-iap
version: 0.0.1
26 changes: 26 additions & 0 deletions incubator/nginx-iap/pr123/apple.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
kind: Pod
Copy link
Member

Choose a reason for hiding this comment

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

are the files in pr123 folder just examples?
how they are going to be deployed?
maybe just move to examples in README?

apiVersion: v1
metadata:
namespace: pr123
name: apple-app
labels:
app: apple
spec:
containers:
- name: apple-app
image: hashicorp/http-echo
args:
- "-text=apple"
---

kind: Service
apiVersion: v1
metadata:
namespace: pr123
name: apple
spec:
selector:
app: apple
ports:
- port: 80
targetPort: 5678 # Default port for image
27 changes: 27 additions & 0 deletions incubator/nginx-iap/pr123/banana.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
kind: Pod
apiVersion: v1
metadata:
namespace: pr123
name: banana-app
labels:
app: banana
spec:
containers:
- name: banana-app
image: hashicorp/http-echo
args:
- "-text=banana"

---

kind: Service
apiVersion: v1
metadata:
namespace: pr123
name: banana
spec:
selector:
app: banana
ports:
- port: 80
targetPort: 5678 # Default port for image
18 changes: 18 additions & 0 deletions incubator/nginx-iap/pr123/echoserver-deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: echoserver
namespace: pr123
spec:
replicas: 1
template:
metadata:
labels:
app: echoserver
spec:
containers:
- image: gcr.io/google_containers/echoserver:1.4
imagePullPolicy: Always
name: echoserver
ports:
- containerPort: 8080
4 changes: 4 additions & 0 deletions incubator/nginx-iap/pr123/echoserver-namespace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: pr123
13 changes: 13 additions & 0 deletions incubator/nginx-iap/pr123/echoserver-service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: v1
kind: Service
metadata:
name: echoserver
namespace: pr123
spec:
ports:
- port: 80
targetPort: 8080
protocol: TCP
type: NodePort
selector:
app: echoserver
15 changes: 15 additions & 0 deletions incubator/nginx-iap/templates/NOTES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
1. Get the application URL by running these commands:
{{- if contains "NodePort" .Values.oauth.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT/login
{{- else if contains "LoadBalancer" .Values.oauth.service.type }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get svc -w {{ template "fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo http://$SERVICE_IP:{{ .Values.oauth.service.externalPort }}
{{- else if contains "ClusterIP" .Values.oauth.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "fullname" . }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use your application"
kubectl port-forward $POD_NAME 8080:{{ .Values.oauth.service.externalPort }}
{{- end }}
40 changes: 40 additions & 0 deletions incubator/nginx-iap/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 24 | trimSuffix "-" -}}
{{- end -}}

{{/*
Create a default fully qualified app name.
We truncate at 24 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "fullname" -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- printf "%s-%s" .Release.Name $name | trunc 24 | trimSuffix "-" -}}
{{- end -}}

{{/*
Create a default fully qualified oauth name.
We limit this value to 24 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
Copy link
Member

Choose a reason for hiding this comment

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

no trunc 24 here, should add it or update the description

*/}}
{{- define "fullname_oauth" -}}
{{- printf "%.17s-oauth" .Release.Name | trimSuffix "-" -}}
Copy link
Member

Choose a reason for hiding this comment

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

what is 17?

{{- end -}}

{{/*
Create a default fully qualified proxy name.
We truncate at 24 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
Copy link
Member

Choose a reason for hiding this comment

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

no trunc 24 here, should add it or update the description

*/}}
{{- define "fullname_proxy" -}}
{{- printf "%.17s-proxy" .Release.Name | trimSuffix "-" -}}
{{- end -}}

{{/*
Create a default fully qualified proxy name.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
Create a default fully qualified proxy name.
Create a default fully qualified ingress name.

We truncate at 24 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
Copy link
Member

Choose a reason for hiding this comment

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

no trunc 24 here, should add it or update the description

*/}}
{{- define "fullname_ingress" -}}
{{- printf "%.16s-ingress" .Release.Name | trimSuffix "-" -}}
Copy link
Member

Choose a reason for hiding this comment

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

what is 16s?

{{- end -}}
111 changes: 111 additions & 0 deletions incubator/nginx-iap/templates/nginx.configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "fullname_proxy" . }}
labels:
app: {{ template "fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
data:
default.conf: |-
# nginx requires a 'resolver' directive when variables appear in proxy_pass arguments
# (eg: proxy_pass "http://$service.$pr_number..." )
resolver {{ .Values.kube_dns_hostname }} ipv6=off;

upstream localhost_oauth2_proxy {
least_conn;
server 127.0.0.1:81;
}

server {
listen 80;

# TODO(mtamsky): It might be clearer if we had two server{}
# blocks, each with unique server_name patterns. This would
# create a clear separation of the iap from internal services.

# Currently, this server{} config answers for both:
# iap.DOMAIN
server_name {{ .Values.iap_hostname }}.{{ .Values.DNS_ZONE }};
# pr123-service.DOMAIN
server_name ~^(?<pr_number>[Pp][Rr][0-9]+)-(?<service>[^.]+)\.{{ .Values.iap_hostname }}.{{ .Values.DNS_ZONE }}$;

# Assemble the two captured vars
set $pr_number_service "${pr_number}-${service}";

access_log /dev/stdout;
error_log /dev/stderr;

# 'internal' directive designates this location is nginx-internal-only
# cf. http://nginx.org/en/docs/http/ngx_http_core_module.html#internal
location /oauth2/auth {
internal;
proxy_pass http://localhost_oauth2_proxy;
}

# initiate the oauth flow
location /oauth2/start {
auth_request off;
proxy_pass http://localhost_oauth2_proxy;
}

# Presents the initial login page with "Sign in with a XYZ account" button.
# Also logs the user out by clearing their _oauth_cookie.
location /oauth2/sign_in {
auth_request off;
rewrite_log on;
if ($arg_initial = "true") {
proxy_pass http://localhost_oauth2_proxy/oauth2/sign_in?iap_redirect=${arg_iap_redirect};
break;
# we continue processing this request
# http://nginx.org/en/docs/http/ngx_http_rewrite_module.html#break
}

# This is currently where the callback? oauth flow winds up after valid
# authentiation with a valid _oauth_cookie.

# TODO(tamsky): determine if we can move iap_redirect to a unique path
# instead of piggybacking on the /oauth2/sign_in location
if ($arg_iap_redirect) {
# redirect to final desination, trailing-'?' tells nginx to drop current $query_string
rewrite ^.*$ ${arg_iap_redirect}? redirect;
}

# This 'proxy_pass' URL is what generates the start-of-oauth-flow:
proxy_pass http://localhost_oauth2_proxy/oauth2/sign_in?iap_redirect=${pr_number_service}.{{ .Values.iap_hostname }}.{{ .Values.DNS_ZONE }}${request_uri};
}

location /oauth2/callback {
auth_request off;
proxy_pass http://localhost_oauth2_proxy;
}

location / {
# All paths (not matched above) are protected by the 'auth_request' directive.
auth_request /oauth2/auth;

# Headers we include in the auth_request:
auth_request_set $auth_user $upstream_http_x_authenticated_email;

# TODO(tamsky): verify we need 'initial=true' pieces.
error_page 401 =307 $scheme://{{.Values.iap_hostname}}.{{ .Values.DNS_ZONE }}/oauth2/sign_in?initial=true&iap_redirect=$scheme://${pr_number_service}.{{ .Values.iap_hostname }}.{{ .Values.DNS_ZONE }}$request_uri;

# Headers that are set in the request sent to the oauth2_proxy:
proxy_set_header x-forwarded-user $auth_user;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;

# Lookup hostname using DNS and proxy the current request to service_ip_address:80
proxy_pass http://${service}.${pr_number}.svc.cluster.local.;
# trailing dot is correct
}

# TODO(tamsky): add 'location /logout {}'

proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Upstream-Response-Time $upstream_response_time;
proxy_next_upstream error http_500;
proxy_set_header Host $host;
}
Loading