Skip to content

Commit

Permalink
Initial implementation of internal-rp
Browse files Browse the repository at this point in the history
  • Loading branch information
trotterdylan committed Jan 25, 2022
1 parent 4a71d2d commit 6f444c5
Show file tree
Hide file tree
Showing 8 changed files with 279 additions and 0 deletions.
25 changes: 25 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Required environment variables:
#
# PORT
# ROOT_DOMAIN
# SERVICE_i
# BACKEND_i
# BUCKET_PREFIX_i
# OAUTH2_PROXY_CLIENT_ID
# OAUTH2_PROXY_CLIENT_SECRET
# OAUTH2_PROXY_COOKIE_SECRET
# OAUTH2_PROXY_EMAIL_DOMAINS

FROM nginx:1.21-alpine

ENV OAUTH2_PROXY_VERSION=7.2.1
ENV GCSPROXY_VERSION=0.3.0

RUN curl -sSL https://github.com/oauth2-proxy/oauth2-proxy/releases/download/v${OAUTH2_PROXY_VERSION}/oauth2-proxy-v${OAUTH2_PROXY_VERSION}.linux-amd64.tar.gz | tar -xz -C /tmp \
&& cp /tmp/oauth2-proxy-v${OAUTH2_PROXY_VERSION}.linux-amd64/oauth2-proxy /usr/local/bin

RUN wget https://github.com/daichirata/gcsproxy/releases/download/v${GCSPROXY_VERSION}/gcsproxy_${GCSPROXY_VERSION}_amd64_linux -O /usr/local/bin/gcsproxy \
&& chmod +x /usr/local/bin/gcsproxy

COPY templates /templates
COPY docker-entrypoint.d/ /docker-entrypoint.d/
94 changes: 94 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# internal-rp

## Overview

internal-rp is a configurable reverse proxy for managing access to internal
services such as dashboards. It consists of an nginx-based Docker image that can
be configured solely by environment variables. Access to services is allowed
only for users authenticated via [OAuth2
Proxy](https://oauth2-proxy.github.io/oauth2-proxy/).

Services can consist of **backend services** which are HTTP servers reachable by
internal-rp, and **static services** which are static files living in GCS. An
example with four services, two backend and two static, is shown below:

```
USER ┌───────────┐
authenticated │ │
│ routes │ │
│ ┌─────►│ static_1/ │ static_1.internal.xyz.com
│ │ │ │
internal.xyz.com │ │ │
│ ├─────►│ static_2/ │ static_2.internal.xyz.com
│ │ │ │
▼ │ │ │
┌─────────────┐ │ └───────────┘
│ │ │ bucket
│ internal-rp ├─────┤
│ │ │ ┌───────────┐
└───┬─────────┘ │ │ │
│ ▲ ├─────►│ backend_1 │ backend_1.internal.xyz.com
│ │ │ │ │
│ │ │ └───────────┘
▼ │ │
┌────────┴────┐ │ ┌───────────┐
│ oauth │ │ │ │
│ provider │ └─────►│ backend_2 │ backend_2.internal.xyz.com
│ │ │ │
└─────────────┘ └───────────┘
```

In this example, the proxy is directly accessible at internal.xyz.com and it
will produce a simple web page with links to each of the services. The services
are accessible at subdomains of internal.xyz.com as shown. Users will not be
able to access the proxy web page or any of the services until they have
authenticated with the OAuth provider, and once authenticated they will be able
to freely access all of these services.

## Configuration

The Docker image is configured solely by environment variables. These are
outlined below.

**Required environment variables:**

* `PORT` - Ingress port for the proxy.
* `ROOT_DOMAIN` - Root domain under which services will be exposed as
subdomains.

**Per-service environment variables:**

Each of these variables can be repeated with indexes starting at 1.

* `SERVICE_i` - The i'th service name. This service will be accessible at this
subdomain of root.
* `BACKEND_i` - The base URL of a backend service to proxy for the
i'th service.
* `BUCKET_PREFIX_i` - The GCS bucket prefix for the i'th service.

The proxy illustrated above would be configured with these env vars:

```
SERVICE_1=static_1
BUCKET_PREFIX_1=bucket/static_1
SERVICE_2=static_2
BUCKET_PREFIX_2=bucket/static_2
SERVICE_3=backend_1
BACKEND_3=http://backend_1
SERVICE_4=backend_2
BACKEND_4=http://backend_2
```

Note that only one of BUCKET_PREFIX_i or BACKEND_i for a given i should be
specified.

**Required OAuth environment variables:**

* `OAUTH2_PROXY_CLIENT_ID` - OAuth client ID.
* `OAUTH2_PROXY_CLIENT_SECRET` - OAuth client secret.
* `OAUTH2_PROXY_COOKIE_SECRET` - 32 character signing secret.
* `OAUTH2_PROXY_EMAIL_DOMAINS` - Email domain for allowed users.

By default OAuth2 Proxy assumes Google is the auth provider. For more details
About OAuth2 Proxy see their [provider configuration
docs](https://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/oauth_provider).
24 changes: 24 additions & 0 deletions docker-entrypoint.d/19-generate-templates.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/sh

set -e

i=1
mkdir -p /etc/nginx/templates

cp templates/root.conf.template /etc/nginx/templates/

while true; do
service=`eval 'echo $SERVICE_'$i`
[ -z "$service" ] && break
backend=`eval 'echo $BACKEND_'$i`
if [ -z "$backend" ]; then
sed -e "s/SERVICE/SERVICE_$i/" \
-e "s/BUCKET_PREFIX/BUCKET_PREFIX_$i/" \
templates/static.conf.template > /etc/nginx/templates/${service}.conf.template
else
sed -e "s/SERVICE/SERVICE_$i/" \
-e "s/BACKEND/BACKEND_$i/" \
templates/proxy.conf.template > /etc/nginx/templates/${service}.conf.template
fi
i=$((i+1))
done
19 changes: 19 additions & 0 deletions docker-entrypoint.d/40-start-oauth2-proxy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/sh

set -e

export OAUTH2_PROXY_COOKIE_DOMAINS=".${ROOT_DOMAIN}"
export OAUTH2_PROXY_WHITELIST_DOMAINS=".${ROOT_DOMAIN}"

nohup oauth2-proxy &
pid=$!

while [ `curl -s -o /dev/null -w %{http_code} http://127.0.0.1:4180/ping` -eq '000' ]; do
if [ ! -e /proc/$pid ]; then
echo "oauth2-proxy exited"
exit 1
fi
sleep 0.1
done

nohup gcsproxy -b 127.0.0.1:4181 -v &
34 changes: 34 additions & 0 deletions docker-entrypoint.d/41-generate-index.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/bin/sh

set -e

cat << EOF > /usr/share/nginx/html/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>$ROOT_DOMAIN</title>
</head>
<body>
<ul>
EOF

i=1

while true; do
service=`eval 'echo $SERVICE_'$i`
[ -z "$service" ] && break
cat << EOF >> /usr/share/nginx/html/index.html
<li>
<a href="https://$service.$ROOT_DOMAIN/">
$service
</a>
</li>
EOF
i=$((i+1))
done

cat << EOF >> /usr/share/nginx/html/index.html
</ul>
</body>
</html>
EOF
20 changes: 20 additions & 0 deletions templates/proxy.conf.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
server {
listen ${PORT};
server_name ${SERVICE}.${ROOT_DOMAIN};

location = /oauth2/auth {
proxy_pass http://127.0.0.1:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
# nginx auth_request includes headers but not body
proxy_set_header Content-Length "";
proxy_pass_request_body off;
}

location / {
auth_request /oauth2/auth;
error_page 401 = https://${ROOT_DOMAIN}/oauth2/sign_in?rd=$scheme://$host$request_uri;
proxy_pass ${BACKEND};
}
}
37 changes: 37 additions & 0 deletions templates/root.conf.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
server {
listen ${PORT} default_server deferred;
server_name _;
return 444;
}

server {
listen ${PORT};
server_name ${ROOT_DOMAIN};

location /oauth2/ {
proxy_pass http://127.0.0.1:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Auth-Request-Redirect $request_uri;
# or, if you are handling multiple domains:
# proxy_set_header X-Auth-Request-Redirect $scheme://$host$request_uri;
}

location = /oauth2/auth {
proxy_pass http://127.0.0.1:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
# nginx auth_request includes headers but not body
proxy_set_header Content-Length "";
proxy_pass_request_body off;
}

location / {
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;
root /usr/share/nginx/html;
index index.html;
}
}
26 changes: 26 additions & 0 deletions templates/static.conf.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
server {
listen ${PORT};
server_name ${SERVICE}.${ROOT_DOMAIN};

location = /oauth2/auth {
proxy_pass http://127.0.0.1:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
# nginx auth_request includes headers but not body
proxy_set_header Content-Length "";
proxy_pass_request_body off;
}

location = / {
auth_request /oauth2/auth;
error_page 401 = https://${ROOT_DOMAIN}/oauth2/sign_in?rd=$scheme://$host$request_uri;
proxy_pass http://127.0.0.1:4181/${BUCKET_PREFIX}/index.html;
}

location / {
auth_request /oauth2/auth;
error_page 401 = https://${ROOT_DOMAIN}/oauth2/sign_in?rd=$scheme://$host$request_uri;
proxy_pass http://127.0.0.1:4181/${BUCKET_PREFIX}/;
}
}

0 comments on commit 6f444c5

Please sign in to comment.