├── .gitignore
├── README.md
├── gke_k8s
├── Dockerfile
├── README.md
├── k8s
│ ├── deployment_1.yml
│ ├── deployment_2.yml
│ └── whitelist.yml
└── setup_k8s.sh
├── local
├── .gitignore
├── README.md
├── _quarto.yml
├── about.qmd
├── emails
│ └── email_list.txt
├── index.qmd
├── local_app.png
└── styles.css
└── simple
├── README.md
├── app_setup.png
└── render_blueprint.png
/.gitignore:
--------------------------------------------------------------------------------
1 | oauth.env
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Make Static Sites Private With OAuth For Free
2 |
3 | Like GitHub Pages, but you choose who can see it, without usernames & passwords :magic_wand: :tophat: :rabbit:
4 |
5 | ## Background
6 |
7 | Do you want to serve a static site semi-privately so only specific users can see it? For example, you may want to host private docs or offer a paid course. There are many complicated solutions that involve building a login flow and maintaining a database of usernames/passwords. Thankfully, there is a much easier way with [Oauth2 Proxy](https://oauth2-proxy.github.io/oauth2-proxy/docs/).
8 |
9 | Concretely, this tutorial shows how to use the Oauth2 Proxy to make a static site private with minimal dependencies and secure it with an email whitelist (a [text file with emails](./local/emails/email_list.txt)). There are many other authorization schemes in addition to an email whitelist, [which you can read about here](https://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/overview).
10 |
11 | [This section](./local/README.md#how-does-this-work) describes how OAuth works in the context of this tutorial.
12 |
13 | # Tutorial
14 |
15 | This tutorial has three parts that become progressively complex depending on your goals. However, you can stop at any lesson once you are satisfied.
16 |
17 | Prerequisites: knowledge of Docker and familiarity with hosting static sites (like on GitHub Pages).
18 |
19 | 1. **[Running OAuth Locally](./local/README.md):** this runs a minimal static site locally, secured with the OAuth2 Proxy. This allows you to gain an intuition of how things work before proceeding to the next step.
20 |
21 | 2. **[Serve The Private Site (For Free!)](./simple/README.md):** You will host the same site you created locally **for free!** You will also learn how to set up SSL for `https` with a custom domain.
22 |
23 | 3. **(Optional) [Hosting on Kubernetes](./gke_k8s/README.md)**: Finally, you will deploy a website secured by OAuth on Kubernetes. This assumes some experience with Kubernetes.
24 |
25 | # FAQ
26 |
27 | 1. **Does the proxy only work for static sites?** No! You can put applications behind the proxy too. See [this explanation](./local/README.md#is-this-only-for-static-sites).
28 |
29 | 2. **Does GitHub Pages have something like this?**: Only if you [purchase GitHub Enterprise Cloud](https://docs.github.com/en/enterprise-cloud@latest/pages/getting-started-with-github-pages/changing-the-visibility-of-your-github-pages-site) which is absurdly expensive if you want it solely for the purposes of securing a static site (> $100/month for just 5 users!).
30 |
31 | 3. **Can't you do this with [Netlify](https://www.netlify.com/)?**: To do something similar on Netlify, you have to use [invite-only private sites](https://docs.netlify.com/visitor-access/identity/registration-login/#set-registration-preferences), which triggers [identity pricing](https://www.netlify.com/pricing/#add-ons-identity), which means that **you need to pay over $99 per month if you have over 5 Active users!** That is ridiculous.
32 |
33 |
34 |
--------------------------------------------------------------------------------
/gke_k8s/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python
2 | RUN echo "
Hello Folks!
" > index.html
3 | CMD ["python", "-m", "http.server", "8083"]
4 |
--------------------------------------------------------------------------------
/gke_k8s/README.md:
--------------------------------------------------------------------------------
1 | # OAuth In Kubernetes
2 |
3 | This section shows how to set up the [OAuth2 Proxy](https://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/overview) on [GKE](https://cloud.google.com/kubernetes-engine) to secure a website. The motivation for this is that in many companies and enterprises, serving a site on Kubernetes may be the only option.
4 |
5 | Unlike previous examples, the static site is served via its own web server rather than directly through the OAuth2 Proxy. This web server's networking restricts access to be visible only internally within the Kubernetes cluster. The proxy forwards traffic to this web server. This is a more general pattern in case you want to host other web applications that are not static sites (like dashboards).
6 |
7 | :point_right: If you are unfamiliar with Kubernetes or OAuth, start with [Minimal Oauth](../local/README.md). :point_left:
8 |
9 | ## Pre-requisites
10 |
11 | - A domain name. If you don't have one, you [can buy one here](https://domains.google.com/)
12 | - A google cloud account
13 | - Deploy a hello-world Kubernetes cluster with [these instructions](https://cloud.google.com/kubernetes-engine/docs/deploy-app-cluster)
14 |
15 | ## Steps
16 |
17 | You can see the commands I used in [setup_k8s.sh](./setup_k8s.sh)
18 |
19 | ### 1. Build Docker Container
20 |
21 | ```bash
22 | DOCKER_NM= # ex: hamelsmu/hello-web
23 | docker build -t $DOCKER_NM --platform=linux/amd64 . && docker push $DOCKER_NM
24 | ```
25 |
26 | ### 2. Create Google Kubernetes Cluster & Static IP
27 |
28 | ```bash
29 | gcloud container clusters create-auto oauth-demo \
30 | --region us-west1 \
31 | --project=
32 |
33 | gcloud compute addresses create oauth-ip-address --global
34 | ```
35 |
36 | ### 3. Setup GitHub OAuth and configuration variables
37 |
38 | Create an [OAuth App](https://github.com/settings/applications/new), and fill out the following fields:
39 |
40 | - **Application Name:** Any name you want, I called it `k8s-oauth`
41 | - **Homepage URL:** this is the url of the website `https://hamel.page`
42 | - **Application Description:** you can skip this
43 | - **Authorization callback URL:** your url with the path `/oauth2/callback` for example, I put `https://hamel.page/oauth2/callback`.
44 |
45 | Take note of the `ClientID` and `Client secret`, which you will use below.
46 |
47 | Create a file named `oauth.env` with the following contents:
48 |
49 | ```text
50 | OAUTH_CLIENT_ID=your-client-id # from GitHub
51 | OAUTH_CLIENT_SECRET=your-client-secret # from GitHub
52 | OAUTH2_PROXY_COOKIE_SECRET=your-cookie-secret # you generate this locally, see below.
53 | OAUTH2_PROXY_SKIP_PROVIDER_BUTTON=true
54 | ```
55 |
56 | You can generate the `OAUTH2_PROXY_COOKIE_SECRET` by running this:
57 |
58 | ```bash
59 | python -c 'import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())'
60 | ```
61 |
62 | Finally, you can store these in your Kubernetes cluster:
63 |
64 | ```bash
65 | kl create configmap oauth-env-file --from-env-file oauth.env
66 | ```
67 |
68 | Note: you should use [secrets](https://kubernetes.io/docs/concepts/configuration/secret/) instead, but the goal is to keep things as minimal as possible.
69 |
70 | ### 4. Deploy your application
71 |
72 | We can first deploy our web app with [k8s/deployment_1.yml](./k8s/deployment_1.yml) without any OAuth security. You will need to edit the image name in the `Deployment` as well as the domains in `ManagedCertificate`. After some time, you will be able to see your web page at your domain name with `https` properly working.
73 |
74 | ```
75 | kubectl apply -f k8s/deployment_1.yml
76 | ```
77 |
78 | Next, we can deploy the web app with an OAuth reverse proxy in front of it with [k8s/deployment_2.yml](./k8s/deployment_2.yml):
79 |
80 | ```
81 | kubectl apply -f k8s/whitelist.yml # this is a whitelist of all the email addresses you want to allow to access your app.
82 | kubectl apply -f k8s/deployment_2.yml
83 | ```
84 |
85 | ## Demo
86 |
87 | See [https://hamel.page](https://hamel.page)
88 |
89 | It will ask you to authenticate via GitHub, but will give you a 403 if your email is not in [k8s/whitelist.yml](./k8s/whitelist.yml).
90 |
91 |
92 |
--------------------------------------------------------------------------------
/gke_k8s/k8s/deployment_1.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: hello
5 | labels:
6 | app: hello
7 | spec:
8 | replicas: 2
9 | selector:
10 | matchLabels:
11 | app: hello
12 | template:
13 | metadata:
14 | labels:
15 | app: hello
16 | spec:
17 | containers:
18 | - name: website
19 | image: docker.io/hamelsmu/hello-web
20 | ports:
21 | - containerPort: 8083
22 | ---
23 | ## Sets up networking for the app
24 | apiVersion: v1
25 | kind: Service
26 | metadata:
27 | name: hamelapp
28 | spec:
29 | selector:
30 | app: hello
31 | ports:
32 | - port: 8083
33 | targetPort: 8083
34 | ---
35 | # This is the managed certificate
36 | apiVersion: networking.gke.io/v1
37 | kind: ManagedCertificate
38 | metadata:
39 | name: managed-cert
40 | spec:
41 | domains:
42 | - hamel.page
43 | ---
44 | # boilerplate from https://cloud.google.com/kubernetes-engine/docs/how-to/managed-certs
45 | apiVersion: networking.k8s.io/v1
46 | kind: Ingress
47 | metadata:
48 | name: managed-cert-ingress
49 | annotations:
50 | kubernetes.io/ingress.global-static-ip-name: oauth-ip-address
51 | networking.gke.io/managed-certificates: managed-cert
52 | kubernetes.io/ingress.class: "gce"
53 | spec:
54 | defaultBackend:
55 | service:
56 | name: hamelapp
57 | port:
58 | number: 8083
59 |
--------------------------------------------------------------------------------
/gke_k8s/k8s/deployment_2.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: hello
5 | labels:
6 | app: hello
7 | spec:
8 | replicas: 1
9 | selector:
10 | matchLabels:
11 | app: hello
12 | template:
13 | metadata:
14 | labels:
15 | app: hello
16 | spec:
17 | containers:
18 | - name: website
19 | image: docker.io/hamelsmu/hello-web
20 | ports:
21 | - containerPort: 8083
22 | ---
23 | ## Sets up networking for the app
24 | apiVersion: v1
25 | kind: Service
26 | metadata:
27 | name: hamelapp
28 | spec:
29 | selector:
30 | app: hello
31 | ports:
32 | - port: 8083
33 | targetPort: 8083
34 | ---
35 | # This is the managed certificate for https
36 | apiVersion: networking.gke.io/v1
37 | kind: ManagedCertificate
38 | metadata:
39 | name: managed-cert
40 | spec:
41 | domains:
42 | - hamel.page
43 | ---
44 | # boilerplate from https://cloud.google.com/kubernetes-engine/docs/how-to/managed-certs
45 | apiVersion: networking.k8s.io/v1
46 | kind: Ingress
47 | metadata:
48 | name: managed-cert-ingress
49 | annotations:
50 | kubernetes.io/ingress.global-static-ip-name: oauth-ip-address
51 | networking.gke.io/managed-certificates: managed-cert
52 | kubernetes.io/ingress.class: "gce"
53 | spec:
54 | defaultBackend:
55 | service:
56 | name: oauth2-proxy
57 | port:
58 | name: oauth2-proxy
59 | ---
60 | apiVersion: apps/v1
61 | kind: Deployment
62 | metadata:
63 | name: oauth2-proxy
64 | labels:
65 | app: oauth2-proxy
66 | spec:
67 | replicas: 1
68 | selector:
69 | matchLabels:
70 | app: oauth2-proxy
71 | template:
72 | metadata:
73 | labels:
74 | app: oauth2-proxy
75 | spec:
76 | containers:
77 | - name: oauth2-proxy
78 | image: quay.io/oauth2-proxy/oauth2-proxy #:v7.2.0
79 | readinessProbe: # Probes are set at the container level.
80 | httpGet:
81 | path: /ping # This is an HTTP GET, using the health URL.
82 | port: 4180
83 | periodSeconds: 5 # The probe fires every five seconds.
84 | envFrom:
85 | - configMapRef:
86 | name: oauth-env-file
87 | args: ["--provider", "github",
88 | "--reverse-proxy", "true",
89 | "--upstream", "http://hamelapp:8083",
90 | "--http-address=0.0.0.0:4180",
91 | "--https-address=0.0.0.0:4443",
92 | "--scope", "user:email",
93 | "--cookie-expire", "0h0m30s",
94 | "--session-cookie-minimal", "true",
95 | "--skip-provider-button", "true",
96 | "--authenticated-emails-file", "/app/emails/email_list.txt"]
97 | ports:
98 | - containerPort: 4180 # this is port this container serves on by default
99 | volumeMounts:
100 | - name: emails
101 | mountPath: "/app/emails"
102 | readOnly: true
103 | volumes:
104 | - name: emails
105 | configMap:
106 | name: email-whitelist
107 | ---
108 | ## Sets up networking for the proxy
109 | apiVersion: v1
110 | kind: Service
111 | metadata:
112 | name: oauth2-proxy
113 | spec:
114 | selector:
115 | app: oauth2-proxy
116 | ports:
117 | - port: 4180
118 | name: oauth2-proxy
119 | targetPort: 4180
120 |
--------------------------------------------------------------------------------
/gke_k8s/k8s/whitelist.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | metadata:
3 | name: email-whitelist
4 | data:
5 | email_list.txt: |
6 | hamel.husain@gmail.com
7 | inc007@gmail.com
8 | mwlorgat@gmail.com
9 | kind: ConfigMap
10 |
--------------------------------------------------------------------------------
/gke_k8s/setup_k8s.sh:
--------------------------------------------------------------------------------
1 | #setup container
2 | docker build -t hamelsmu/hello-web --platform=linux/amd64 . && docker push hamelsmu/hello-web
3 |
4 | gcloud container clusters create-auto oauth-demo \
5 | --region us-west1 \
6 | --project=kubeflow-dev
7 |
8 | # Create a static IP address
9 | gcloud compute addresses create oauth-ip-address --global
10 |
11 | # Get the IP address
12 |
13 | gcloud compute addresses describe oauth-ip-address --global
14 | # 34.110.194.239
15 | # Setup your DNS with an A record for your domain pointing to this IP address
16 | # This IP will be referenced by the ingress resource by name "oauth-ip-address"
17 |
18 | ### Create a file named oauth.env with the following contents
19 | # generate the cookie secret like this:
20 | # python -c 'import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())'
21 |
22 | # OAUTH_CLIENT_ID=your-client-id
23 | # OAUTH_CLIENT_SECRET=your-client-secret
24 | # OAUTH2_PROXY_COOKIE_SECRET=your-cookie-secret
25 |
26 | # Create a configmap from the oauth.env file
27 | kl create configmap oauth-env-file --from-env-file oauth.env
28 |
--------------------------------------------------------------------------------
/local/.gitignore:
--------------------------------------------------------------------------------
1 | /.quarto/
2 | .env
3 | _site/
--------------------------------------------------------------------------------
/local/README.md:
--------------------------------------------------------------------------------
1 | # Minimal OAuth Locally
2 |
3 | We will use the Oauth2 Proxy to make a static site private with minimal dependencies. In this case, I'm going to serve a [Quarto](https://quarto.org/) site (my favorite static site generator), and secure it with an email whitelist (a [text file with emails](./local/emails/email_list.txt)). There are many other authorization schemes in addition to an email whitelist, [which you can read about here](https://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/overview).
4 |
5 | [This section](../README.md#how-does-this-work) describes how OAuth works in the context of this example.
6 |
7 | ## Steps
8 |
9 | ### 1. Generate the static site
10 |
11 | First, install [Quarto](https://quarto.org/), and run the following command:
12 |
13 | ```bash
14 | quarto render
15 | ```
16 |
17 | This will create the directory `_site/` with a static site in it (HTML, CSS, etc).
18 |
19 | ### 2. Create an OAuth App
20 |
21 | Create an [OAuth App](https://github.com/settings/applications/new), but fill out the fields like this for local testing:
22 |
23 | > 
24 |
25 | _The `oauth2/callback` path on the Callback URL is something specific to the OAuth2 proxy. It is the [endpoint](https://oauth2-proxy.github.io/oauth2-proxy/docs/features/endpoints) this proxy uses to handle the callbacks from the OAuth provider._
26 |
27 | Make sure you store the `Client ID` and `Client Secret` into the enviornment variables `OAUTH2_PROXY_CLIENT_ID` and `OAUTH2_PROXY_CLIENT_SECRET`, respectively.
28 |
29 | ### 3. Start The Proxy + WebServer Locally
30 |
31 | First, add your email address to [emails/email_list.txt](./emails/email_list.txt) to whitelist yourself. The OAuth2 proxy uses this list to determine who is authorized to see your site. There are many other authorization schemes in addition to an email whitelist, [which you can read about here](https://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/overview).
32 |
33 | Next, generate a cookie secret by running `python -c 'import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())'` and store that value in the `OAUTH2_PROXY_COOKIE_SECRET` environment variable.
34 |
35 | Next, run the following command from this directory:
36 |
37 | ```bash
38 | docker run -v $(pwd)/_site:/app \
39 | -v $(pwd)/emails:/site_config \
40 | -p 4180:4180 \
41 | quay.io/oauth2-proxy/oauth2-proxy \
42 | --provider github \
43 | --upstream "file:///app/#/" \
44 | --http-address=":4180" \
45 | --authenticated-emails-file "/site_config/email_list.txt" \
46 | --scope user:email \
47 | --cookie-expire 0h0m30s \
48 | --session-cookie-minimal true \
49 | --skip-provider-button true \
50 | --cookie-secret $OAUTH2_PROXY_COOKIE_SECRET \
51 | --client-id $OAUTH2_PROXY_CLIENT_ID \
52 | --client-secret $OAUTH2_PROXY_CLIENT_SECRET \
53 | --cookie-csrf-per-request=true \
54 | --redirect-url="http://localhost:4180/oauth2/callback" \
55 | --cookie-secure=false \
56 | --cookie-csrf-expire=5m
57 | ```
58 |
59 | These flags are annotated below (but you have to copy and paste the above version for it to work.)
60 |
61 | ```bash
62 | # COPY & PASTE THE ABOVE VERSION, THIS IS JUST AN EXPLANATION
63 |
64 | docker run -v $(pwd)/_site:/app \ # the directory with the static site
65 | -v $(pwd)/emails:/site_config \ # the dirctory with the email list
66 | -p 4180:4180 # bind the ports
67 | quay.io/oauth2-proxy/oauth2-proxy \ # the official docker image for Oauth2 proxy
68 | --provider github \ # use GitHub as the Oauth provider
69 | --upstream "file:///app/#/" \ # The location of the static site files
70 | --http-address=":4180" \ # Bind the 4180 port on all interfaces which is necessary for Docker (we aren't using https for local testing).
71 | --authenticated-emails-file "/site_config/email_list.txt" \ # This is the email whitelist
72 | --scope user:email \ # This tells the Oauth provider, GitHub, to share your email with the Oauth proxy
73 | --cookie-expire 0h0m30s \ # Optional: This helps the cookie expire more quickly which could be helpful for security
74 | --session-cookie-minimal true \ # Optional: don't store uncessary info in cookie since we aren't using features that require it
75 | --skip-provider-button true \ # Don't need a seperate "login with GitHub" screen
76 | --cookie-secret $OAUTH2_PROXY_COOKIE_SECRET \ # This is the secret you generate, see https://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/overview
77 | --client-id $OAUTH2_PROXY_CLIENT_ID \ # This is the ID of your Oauth App from GitHub
78 | --client-secret $OAUTH2_PROXY_CLIENT_SECRET \ # This is the secret of your Oauth App from GitHub
79 | --cookie-csrf-per-request=true \ # allows for parallel requests
80 | # THE BELOW FLAGS ARE ONLY FOR LOCAL TESTING \
81 | --redirect-url="http://localhost:4180/oauth2/callback" \ # this is necessary for local testing only
82 | --cookie-secure=false \ # this is necessary for local testing only
83 | --cookie-csrf-expire=5m # this is necessary for local testing only
84 | ```
85 |
86 | Note how the OAuth2 Proxy doubles as a web server also! That is what `--upstream` flag enables.[^1]
87 |
88 | ### 4. Test Security / Access
89 |
90 | Preview your site at [http://localhost:4180](http://localhost:4180).
91 |
92 | There is a file named [emails/email_list.txt](./emails/email_list.txt) that contains a list of the email identities that are allowed to view your site. Try misspelling your email on purpose and see what happens when you do a hard refresh after a few seconds. Try changing it back.
93 |
94 | If you are curious how all this works, [read this](#how-does-this-work).
95 |
96 | # Next Lesson: Deploy It!
97 |
98 | Now that you have a minimal idea of how this works locally, you can proceed to host your static site on a VM or other hosted solution.
99 |
100 | **:point_right: [See Lesson 2: Serving Your Site](../simple/README.md). :point_left:**
101 |
102 | In that lesson, we will show you how to:
103 |
104 | - Use a hosting provider to deploy your secure static site, **for free**.
105 | - Enable `https` and set up a custom domain.
106 |
107 | # Appendix
108 |
109 | ## Is this only for static sites?
110 |
111 | No! You can put applications behind the proxy, like a dashboard. Instead of passing `file://...` to the `--upstream` flag, you pass a URL, like `http://your.internal.app`. You can see an [example of this on Kuberenetes here](https://github.com/hamelsmu/k8s-oauth/blob/main/gke_k8s/k8s/deployment_2.yml#L89) and you can read more about this in [the docs on configuring the upstream](https://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/overview#upstreams-configuration).
112 |
113 | ## How does this work?
114 |
115 | With regards to security, [OAuth](https://oauth.net/) is often used for **[authentication](https://www.okta.com/identity-101/authentication-vs-authorization/)**[^2], or identifying who you are. You may have seen sites that have you sign in with GitHub, Google, etc. These [OAuth providers](https://en.wikipedia.org/wiki/List_of_OAuth_providers) pass your identity (and other information if you consent) to the site you are trying to access. In our case, we just want to verify a user's email address so we can determine if they are allowed to see our site.
116 |
117 | There is another step in the security flow referred to as **[authorization](https://www.okta.com/identity-101/authentication-vs-authorization/#:~:text=Authorization%20in%20system%20security%20is,access%20control%20or%20client%20privilege.)** which only shows content the verified user is allowed to see (which is typically implemented in your website's code). Thankfully, [Oauth2 Proxy](https://oauth2-proxy.github.io/oauth2-proxy/docs/) offers many ways of **authorization** that's built-in. This is nice as it doesn't force you to change your website's code (like adding login forms, conditional logic, etc).
118 |
119 | A common reason for using OAuth for authentication is to save users from creating an additional username/password for a site.
120 |
121 |
122 | [^1]: For performance purposes, you may want to put the proxy behind another webserver like [Caddy](https://caddyserver.com/) or [Nginx](https://www.nginx.com/), but it's not required in this example.
123 |
124 | [^2]: OAuth may be used for more than authentication. OAuth allows a user to grant third parties access to query data or interact with APIs relevant to the OAuth provider. For example, when a user signs into your website with GitHub, OAuth gives your app (which is the OAuth2-proxy) a token with a scope (permission level) that the user consents to. In this example, this is what the consent screen looks like, which indicates that the app wishes to get your email: 
You can set [other scopes](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/scopes-for-oauth-apps) for the OAuth application. In our case, we only want to verify the user's email address so we set the scope to `user:email` with the argument `--scope user:email`.
125 |
--------------------------------------------------------------------------------
/local/_quarto.yml:
--------------------------------------------------------------------------------
1 | project:
2 | type: website
3 |
4 | website:
5 | title: "local"
6 | navbar:
7 | left:
8 | - href: index.qmd
9 | text: Home
10 | - about.qmd
11 |
12 | format:
13 | html:
14 | theme: cosmo
15 | css: styles.css
16 | toc: true
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/local/about.qmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "About"
3 | ---
4 |
5 | About this site
6 |
--------------------------------------------------------------------------------
/local/emails/email_list.txt:
--------------------------------------------------------------------------------
1 | hamel.husain@gmail.com
2 | inc007@gmail.com
3 | mwlorgat@gmail.com
4 |
--------------------------------------------------------------------------------
/local/index.qmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "local"
3 | ---
4 |
5 | This is a Quarto website.
6 |
7 | To learn more about Quarto websites visit .
8 |
--------------------------------------------------------------------------------
/local/local_app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hamelsmu/oauth-tutorial/c1fa4ef4d5d2ff3b141b0ad7ed20294bafafe612/local/local_app.png
--------------------------------------------------------------------------------
/local/styles.css:
--------------------------------------------------------------------------------
1 | /* css styles */
2 |
--------------------------------------------------------------------------------
/simple/README.md:
--------------------------------------------------------------------------------
1 | # Serving Your Site
2 |
3 | We are going to serve a [Quarto](https://quarto.org/) site behind an OAuth proxy to make it private, as [described here](../README.md).
4 |
5 | ## Prerequisites
6 |
7 | 1. You have taken the [first tutorial](../local/README.md).
8 |
9 | 2. A custom domain that you don't mind experimenting with. If you don't have one, you can [buy one here](https://domains.google.com). In this example, I'm using the custom domain `hamel.rsvp`.
10 |
11 | ## Render (Free)
12 |
13 | [Render](https://render.com/) is a hosting service that provides a generous free tier. We just have to learn a little bit about their YAML, but if you did the [first tutorial](../local/README.md), it will be approachable.
14 |
15 | Render also has [Oauth2 Proxy tutorial](https://render.com/blog/password-protect-with-oauth2-proxy) which is pretty close to what we want. We are going to simplify that example considerably, while also getting rid of things that aren't free.
16 |
17 | ### Setup
18 |
19 | 1. Create another OAuth application and save the `Client ID` and `Client Secret` as you did in the [minimal example](../local/README.md). You can fill it out like this:
20 |
21 | 
22 |
23 | 2. [fork this repo](https://github.com/hamelsmu/oauth-render-quarto/tree/main).
24 |
25 | 3. **Optional:** Change the content of your site by editing one of the `.qmd` files, then run `quarto render` to re-generate the content into the `_site/` folder. **Make sure you check-in any changes, including the `_site` folder into your repo.**
26 |
27 | 4. [Click this link to deploy the app](https://dashboard.render.com/blueprints), and grant Render access to the repo you just forked. Next, fill in values for the `OAUTH2_PROXY_CLIENT_ID` and `OAUTH2_PROXY_CLIENT_SECRET`:
28 |
29 | 
30 |
31 | 5. Set up your custom domain by navigating to [your dashboard](https://dashboard.render.com/) and clicking on this project, which is named `oauth2-proxy-render` (unless you changed it). On the left-hand side click `Settings`. Under `Settings`, scroll down to the section named `Custom Domains`. Add your domain there and follow the instructions. Render will take care of provisioning an SSL certificate to enable `https` on your domain for you.
32 |
33 | ### Testing Your Site
34 |
35 | Anytime your push a change to your repo, your site will rebuild. Try misspelling your email in the `email_list.txt` file and see what happens, then try changing it back. **Warning: if you revoke/grant access, it takes 2-3 minutes for it to take effect, and you may have to clear your cache - be patient!**
36 |
37 | ### How does it work?
38 |
39 | See the [repo's README](https://github.com/hamelsmu/oauth-render-quarto).
40 |
41 |
42 | ### Alternatives to Render
43 |
44 | These are some alternatives to Render that work similarly.
45 |
46 | - [Fly.io](https://fly.io/). Doesn't have a free tier, but is anything _really_ free?
47 | - [Railway](https://railway.app/)
48 |
49 | ## Hosting on A VM
50 |
51 | You might not like serverless solutions. They can often be harder to debug. However, you can host a blog on a VM as well. Here are some tips if you are hosting things on a VM.
52 |
53 | 1. You might want to use [Caddy](https://caddyserver.com/) or [Nginx](https://www.nginx.com/) as your web server. Caddy is easier to use and just as powerful. Both of these servers facilitate [automatically provisioning SSL certificates](https://caddyserver.com/docs/automatic-https#issuer-fallback) for `https` (but it requires some setup). Even though OAuth2 Proxy can be a web server itself, it is convenient to put one of these in front of the proxy for the automatic SSL functionality. Furthermore, this setup gives you the flexibility to host a mix of private and public sites from the same VM.
54 |
55 | 2. If you have a web server that handles SSL for you that forwards traffic to the OAuth2 Proxy, your OAuth2 proxy will likely receive `http` traffic from the web server. Therefore, you might want to configure your `docker run` command accordingly:
56 |
57 | The below command assumes:
58 | - There is an `app/emails` and `app/site` directory in the current directory.
59 | - You have set the `OAUTH2_PROXY_COOKIE_SECRET`, `OAUTH2_PROXY_CLIENT_ID`, and `OAUTH2_PROXY_CLIENT_SECRET` environment variables.
60 |
61 | ```bash
62 | docker run -v $(pwd)/app/site:/app \
63 | -v $(pwd)/app/emails:/site_config \
64 | -p 4180:4180 \
65 | quay.io/oauth2-proxy/oauth2-proxy \
66 | --provider github \
67 | --upstream "file:///app/#/" \
68 | --http-address=":4180" \
69 | --authenticated-emails-file "/site_config/email_list.txt" \
70 | --scope user:email \
71 | --cookie-expire 0h0m30s \
72 | --session-cookie-minimal true \
73 | --skip-provider-button true \
74 | --cookie-secret $OAUTH2_PROXY_COOKIE_SECRET \
75 | --client-id $OAUTH2_PROXY_CLIENT_ID \
76 | --client-secret $OAUTH2_PROXY_CLIENT_SECRET \
77 | --cookie-csrf-per-request=true
78 | ```
79 |
80 | In the above example, you would have your webserver forward traffic to `localhost:4180`.
81 |
82 | 3. Use [rsync](https://linuxize.com/post/how-to-use-rsync-for-local-and-remote-data-transfer-and-synchronization/) to update your static site or email white list when necessary. For example, this is how I would sync my local files with my VM in this case:
83 |
84 | ```bash
85 | rsync -a email_list.txt myvm:/home/ubuntu/app/emails/
86 | rsync -a _site/* myvm:/home/ubuntu/app/site
87 | ```
88 |
89 | I recommend updating your `~/.ssh/config` file so that you can reference your VM with a name like `myvm`, as shown above. For example, this is the relevant part of my `~/.ssh/config`, which has the IP, username, and location of the private key I need to access my VM.
90 |
91 | ```
92 | Host myvm
93 | HostName 111.22.333.44 # your VM's Public IP Address
94 | User ubuntu # The username you need to login
95 | IdentityFile /Users/hamelsmu/.ssh/vm_private_key.rsa
96 | ```
97 |
98 |
99 | # Next Steps
100 |
101 | As an advanced exercise, I show how to do this [same thing on Kubernetes](../gke_k8s/README.md). That example:
102 |
103 | - Deploys a website on Kubernetes behind a load balancer with the OAuth proxy.
104 | - Sets up automated SSL for `https` with Google Managed Certificates
105 | - Deploys a separate webserver for the website to make the pattern more generalizable.
106 |
107 | Deploying this on Kubernetes is much more complicated, but is something I wanted to play with.
108 |
109 | **:point_right: [See Lesson 3: Deploying The OAuth2 Proxy On Kubernetes](../gke_k8s/README.md). :point_left:**
110 |
--------------------------------------------------------------------------------
/simple/app_setup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hamelsmu/oauth-tutorial/c1fa4ef4d5d2ff3b141b0ad7ed20294bafafe612/simple/app_setup.png
--------------------------------------------------------------------------------
/simple/render_blueprint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hamelsmu/oauth-tutorial/c1fa4ef4d5d2ff3b141b0ad7ed20294bafafe612/simple/render_blueprint.png
--------------------------------------------------------------------------------