Skip to content

Instantly share code, notes, and snippets.

@cdxker
Last active September 30, 2024 18:41
Show Gist options
  • Save cdxker/160f5cd730fcd6818dbf32c954b5577c to your computer and use it in GitHub Desktop.
Save cdxker/160f5cd730fcd6818dbf32c954b5577c to your computer and use it in GitHub Desktop.
How to configure tailscale with keycloak OIDC auth

Tailscale is OIDC compliant now!!!

We self-hosted headscale, but wanted to move to their cloud product for higher reliablity.

The way that tailscale authenticates for an email like [email protected] is:

  1. Makes a call to https://trieve.ai/.well-known/webfinger?resource=acct:[email protected]. The response should look like this
{
  "links": [
    {
      "href": "https://auth.trieve.ai/realms/trieve-organization",
      "rel": "http://openid.net/specs/connect/1.0/issuer"
    }
  ],
  "subject": "acct:[email protected]"
}
  1. Makes calls to your oidc provider based on the href value.

this is gist a blog and Tailscale has pretty good documentation on everything after webfinger in their docs and in this blog.

Settting up webfinger is an excersize left to the reader.

The blog reccomends to have .well-known/web-finger just return a static response. image

tailscale/tailscale#379

This doesn't work for multiple users, this is how we setup a simple webfinger server to support multiple users.

Quickstart

git clone https://gist.github.com/cdxker/160f5cd730fcd6818dbf32c954b5577c web-finger
cd web-finger
echo "OAUTH_SERVER_URL=auth.trieve.ai" > .env # Replace this with your auth-server url
docker compose -f 4_docker-compose.yml up -d --build

We use Caddy to host our static files, so all we have to do is setup a rule to redirect traffic to trieve.ai/.well-known/webfinger

App Breakdown

2_App.py

I made a simple flask server to make this more dynamic.

3_Dockerfile

Made a basic Dockerfile, python is annoying on bare metal.

4_docker-compose.yml

Setup for docker-compose so you can easily deploy

5_Caddyfile

Caddyfile for directing traffic to different servers and assigning the SSL

from flask import Flask, request, jsonify
import os
app = Flask(__name__)
OAUTH_SERVER_URL = os.environ.get('OAUTH_SERVER_URL', 'https://auth.trieve.ai')
@app.route("/.well-known/webfinger")
def webfinger():
resource = request.args.get('resource')
if not resource:
return jsonify({"error": "Resource parameter is required"}), 400
# Here you would typically look up the user or resource
# and return the appropriate information
# This is a simplified example
response = {
"subject": resource,
"links": [
{
"rel": "http://openid.net/specs/connect/1.0/issuer",
"href": f"{OAUTH_SERVER_URL}"
}
]
}
return jsonify(response)
@app.route('/health', methods=['GET'])
def health():
return "i am here"
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
# Use an official Python runtime as the base image
FROM python:3.9-slim
# Set the working directory in the container
WORKDIR /app
# Install the required packages
RUN pip install --no-cache-dir flask
# Copy the current directory contents into the container at /app
COPY . /app
# Make port 5000 available to the world outside this container
EXPOSE 5000
# Run app.py when the container launches
CMD ["python", "app.py"]
version: '3.8'
services:
webfinger-server:
image: trieve/webfinger-oauth-accept
build: .
ports:
- "5000:5000"
environment:
- OAUTH_SERVER_URL=${OAUTH_SERVER_URL}
restart: unless-stopped
networks:
app-network:
driver: bridge
{
email [email protected]
}
trieve.ai {
reverse_proxy /.well-known/webfinger localhost:5000 # Routing this to webfinger
reverse_proxy localhost:6000 # Route this our website url or anything else you want
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment