> ## Documentation Index
> Fetch the complete documentation index at: https://docs.siderolabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# OIDC login with Tailscale

> Enable secure login to Omni over your tailnet with Tailscale's OIDC provider.

Tailscale provides seamless OIDC authentication through [tsidp](https://github.com/tailscale/tsidp). When accessing Omni through Tailscale, you can make use of this through the following steps.

## Prerequisites

You will need [a Tailscale account](https://login.tailscale.com/start) with the MagicDNS and HTTPS certificates features enabled.

## Tailscale setup

Browse to [https://login.tailscale.com/admin/acls/file](https://login.tailscale.com/admin/acls/file) to edit the access controls for your tailnet, and add the following JSON to the `grants` section:

```json tsidp-grant.json theme={null}
  "grants": [
    {
      "src": ["*"],
      "dst": ["*"],
      "app": {
        "tailscale.com/cap/tsidp": [
          {
            "users": ["*"],
            "resources": ["*"],
            "allow_admin_ui": true,
            "allow_dcr": true,
            "extraClaims": {
              "email_verified": true
            },
            "includeInUserInfo": true
          }
        ]
      }
    }
  ]
```

On [https://login.tailscale.com/admin/settings/keys](https://login.tailscale.com/admin/settings/keys), generate a new auth key. Make sure to select **Reusable** so it can be used for both tsidp and the Tailscale reverse proxy used for Omni.

Finally, go to [https://login.tailscale.com/admin/dns](https://login.tailscale.com/admin/dns) and note your Tailnet DNS name.

## Prepare deployment

Before proceeding, set your Tailnet DNS name as a variable, it is referenced throughout the steps below:

```bash theme={null}
export TAILNET_DNS=<your-tailnet.ts.net>
```

**Step 1: Set up your environment**

Create a `.env` file with your Tailscale auth key and the tsidp issuer URL:

```bash theme={null}
cat <<EOF > .env
TS_AUTHKEY=<your-generated-key>
OIDC_ISSUER_URL=https://tsidp.${TAILNET_DNS}
EOF
```

**Step 2: Generate the Omni encryption key**

Generate a GPG key used to encrypt data written to etcd at rest. Copy the fingerprint printed by `--list-secret-keys` and paste it into the `--quick-add-key` command:

```bash theme={null}
gpg --quick-generate-key "Omni (Used for etcd data encryption) how-to-guide@siderolabs.com" rsa4096 cert never
gpg --list-secret-keys
gpg --quick-add-key <fingerprint> rsa4096 encr never
gpg --export-secret-key --armor how-to-guide@siderolabs.com > omni.asc
```

**Step 3: Configure the Tailscale reverse proxy**

Create `serve-config.json`. This tells the Tailscale sidecar which ports to expose over HTTPS and where to forward incoming traffic:

```bash theme={null}
cat <<EOF > serve-config.json
{
  "TCP": {
    "443": {
      "HTTPS": true
    },
    "8090": {
      "HTTPS": true
    },
    "8100": {
      "HTTPS": true
    }
  },
  "Web": {
    "omni.${TAILNET_DNS}:443": {
      "Handlers": {
        "/": {
          "Proxy": "http://omni:8080"
        }
      }
    },
    "omni.${TAILNET_DNS}:8090": {
      "Handlers": {
        "/": {
          "Proxy": "http://omni:8090"
        }
      }
    },
    "omni.${TAILNET_DNS}:8100": {
      "Handlers": {
        "/": {
          "Proxy": "http://omni:8100"
        }
      }
    }
  }
}
EOF
```

**Step 4: Define the service stack**

Create `docker-compose.yml`. This defines three services: `tsidp` as the OIDC provider, `omni-tailscale` as the Tailscale reverse proxy, and `omni` itself. Replace `<your-admin-email>` with the email address of the first Omni admin.

```bash theme={null}
cat <<EOF > docker-compose.yml
services:
  tsidp:
    image: ghcr.io/tailscale/tsidp:latest
    environment:
      - TAILSCALE_USE_WIP_CODE=1
      - TS_HOSTNAME=tsidp
    volumes:
      - tsidp-data:/var/lib/tsidp
    env_file:
      - .env
    command:
      - "--dir=/var/lib/tsidp"

  omni-tailscale:
    image: tailscale/tailscale:latest
    environment:
      - TS_SERVE_CONFIG=/config/serve.json
      - TS_HOSTNAME=omni
      - TS_STATE_DIR=/var/lib/tailscale
    env_file:
      - .env
    volumes:
      - ./serve-config.json:/config/serve.json:ro
      - ts-state:/var/lib/tailscale

  omni:
    image: ghcr.io/siderolabs/omni:latest
    volumes:
      - omni-data:/_out/etcd
      - ./omni.asc:/omni.asc:ro
      - /dev/net/tun:/dev/net/tun
    cap_add:
      - NET_ADMIN
    command:
      - --private-key-source=file:///omni.asc
      - --advertised-api-url=https://omni.${TAILNET_DNS}/
      - --machine-api-advertised-url=https://omni.${TAILNET_DNS}:8090/
      - --advertised-kubernetes-proxy-url=https://omni.${TAILNET_DNS}:8100/
      - --siderolink-wireguard-advertised-addr=omni.${TAILNET_DNS}:50180
      - --auth-oidc-enabled
      - --auth-oidc-provider-url=\${OIDC_ISSUER_URL}
      - --auth-oidc-client-id=\${OIDC_CLIENT_ID}
      - --auth-oidc-client-secret=\${OIDC_CLIENT_SECRET}
      - --auth-oidc-scopes=openid
      - --auth-oidc-scopes=profile
      - --auth-oidc-scopes=email
      - --initial-users=<your-admin-email>

volumes:
  tsidp-data:
  ts-state:
  omni-data:
EOF
```

Alternatively, you can configure the OIDC authentication flags above using a configuration file instead. See [OIDC](../reference/omni-configuration#oidc) in the Omni Configuration Examples.

## OIDC client setup

At this point all that's left to do is register Omni as an OIDC client in tsidp. Start up only tsidp first:

```bash theme={null}
docker compose up tsidp
```

Browse to `https://tsidp.${TAILNET_DNS}` and create a new client. For the redirect URI, use `https://omni.${TAILNET_DNS}/oidc/consume`.

Copy the client ID and secret that tsidp generates, then append them to your `.env` file:

```bash theme={null}
cat <<EOF >> .env
OIDC_CLIENT_ID=<paste-client-id-here>
OIDC_CLIENT_SECRET=<paste-secret-here>
EOF
```

Now start up the complete stack:

```bash theme={null}
docker compose up
```

Browse to `https://omni.${TAILNET_DNS}/`. You should be prompted to log in with your Tailscale user and then taken to the Omni UI.

If login fails, you may need to update the `--initial-users` value in `docker-compose.yml` to match the email address displayed on the login screen.
