ProbableOdyssey

TIL How to secure a web server with SSO

I was setting up an instance with the clearml-server for my team to start tracking their data science experiments and versioning their datasets and tracing artifacts. They were impressed with the application, however they raised some valid concerns:

Is it secure? Can we add SSO to this server?

I scoured the docs, apparently it’s available in the enterprise edition, but I was more keen to self-host and learn more about the process.

The details don’t really matter that much, but in a nutshell:

Turns out nginx might provide a solution! I came across a container for oauth2-proxy, which can be placed in front of my web app by nginx. In short, nginx directs traffic to oauth2-proxy, which then directs traffic to an underlying web server after a successful authentication.

On my server, I created an nginx directory:

/opt/nginx/
├── certs
│   ├── tailscaled.crt
│   └── tailscaled.key
└── nginx.conf

I’m using tailscale as my VPN solution for this project, which gives me a domain name that can only be accessed on my network, and also can issue a signed SSL certificate when you enable HTTPS and Magic DNS on your network. The certificate can be generated with tailscale cert <domain-name>.

The content of nginx.conf is:

 1worker_processes 1;
 2
 3events {}
 4
 5http {
 6    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
 7                      '$status $body_bytes_sent "$http_referer" '
 8                      '"$http_user_agent" "$http_x_forwarded_for"';
 9
10    access_log /var/log/nginx/access.log main;
11
12    sendfile on;
13    tcp_nopush on;
14    tcp_nodelay on;
15    keepalive_timeout 65;
16    types_hash_max_size 2048;
17
18    include /etc/nginx/mime.types;
19    default_type application/octet-stream;
20
21    # HTTPS server block
22    server {
23        listen 443 ssl;
24
25        # Use Tailscale-provided SSL certificates
26        ssl_certificate /etc/nginx/certs/tailscaled.crt;
27        ssl_certificate_key /etc/nginx/certs/tailscaled.key;
28
29        # Proxy traffic to oauth2-proxy
30        location / {
31            proxy_pass http://oauth2-proxy:4180;  # Ensure this matches the oauth2-proxy service
32            proxy_set_header Host $host;
33            proxy_set_header X-Real-IP $remote_addr;
34            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
35            proxy_set_header X-Forwarded-Proto $scheme;
36        }
37    }
38
39    # Optional HTTP-to-HTTPS redirect block
40    server {
41        listen 80;
42
43        return 301 https://$host$request_uri;
44    }
45}

If HTTPS isn’t required, the content of the server block after listen 443 ssl; can be moved into the server { listen 80; ... } block and the return 301 can be removed as well. Also ignore the ssl_certificate and ssl_certificate_key.

I then used this docker-compose.yml to spin up nginx and oauth2-proxy:

version: "3.6"
services:
  nginx:
    image: nginx:latest
    container_name: nginx
    volumes:
      - /opt/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - /opt/nginx/certs:/etc/nginx/certs:ro
    ports:
      - "80:80"
      - "443:443"
    depends_on:
      - oauth2-proxy
    networks:
      - frontend

  oauth2-proxy:
    image: quay.io/oauth2-proxy/oauth2-proxy:latest
    container_name: oauth2-proxy
    environment:
      - OAUTH2_PROXY_CLIENT_ID=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      - OAUTH2_PROXY_CLIENT_SECRET=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      - OAUTH2_PROXY_REDIRECT_URL=https://my-webserver-name.tailnet1234.ts.net/oauth2/callback
      - OAUTH2_PROXY_COOKIE_SECRET=XXXXXXXXXXXXXXXX
      - OAUTH2_PROXY_PROVIDER=google  # Or another OAuth provider
      - OAUTH2_PROXY_UPSTREAMS=http://my-webserver-container:5050
      - OAUTH2_PROXY_EMAIL_DOMAINS=company.com
      - OAUTH2_PROXY_HTTP_ADDRESS=0.0.0.0:4180  # Default OAuth2 proxy port
      - OAUTH2_PROXY_SKIP_PROVIDER_BUTTON=true  # Disable the OAuth2 provider button if needed
      - OAUTH2_PROXY_COOKIE_SECURE=true  # Enable if serving through HTTPS
      - OAUTH2_PROXY_COOKIE_HTTPONLY=true  # Use HTTP-only cookies for added security
    ports:
      - "4180:4180"
    networks:
      - frontend
    restart: always

Now with all the containers running, I’m able to navigate to https://my-webserver-name.tailnet1234.ts.net/oauth2/callback in my browser when connected to my VPN. I’m immediately redirected to an SSO page (from Google in this particular case). Once I sign in and confirm my identity. I get redirected back to the domain and I’m able to access the server web interface!

Also make sure to close the port for the underlying web app ;)

Reply to this post by email ↪