Skip to content

Commit 43d6957

Browse files
committed
Feature: Add enhanced support for Tailscale, including priv/role auto assignment, and a full tailscale setup guide.
1 parent 4e11477 commit 43d6957

8 files changed

Lines changed: 417 additions & 39 deletions

File tree

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ Welcome to the xyOps documentation. xyOps is a job scheduler, workflow engine, a
5555
- **[Users and Roles](users.md)**: Account model, roles, resource restrictions, preferences, and avatars.
5656
- **[Privileges](privileges.md)**: Full list of privileges and what each grants across the application.
5757
- **[SSO Setup](sso.md)**: Single Sign-On integration and external identity provider setup.
58+
- **[Tailscale](tailscale.md)**: Guide specifically for using Tailscale as an SSO provider.
5859

5960
## File Formats and Protocols
6061

docs/sso.md

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Single Sign-On (SSO) is a mechanism for outsourcing the xyOps user authenticatio
66

77
Configuring SSO is a complex, highly technical process that requires careful coordination between identity providers, certificates, middleware, and application settings. It is easy to get things wrong and expose security holes in your system. While we provide all necessary documentation here, we strongly recommend our [Enterprise Plan](https://xyops.io/pricing). This gives you access to our white-glove onboarding service, where our team will guide you through every step, validate your configuration, and ensure your integration is both secure and reliable. This also gets you priority ticket support, and live chat support from a xyOps engineer.
88

9-
xyOps uses the "trusted headers" implementation for SSO, allowing for easy integration with several authentication tools and middlewares. These include [OAuth2-Proxy](https://github.com/oauth2-proxy/oauth2-proxy), [Vouch](https://github.com/vouch/vouch-proxy), [Authelia](https://github.com/authelia/authelia), and [Authentik](https://github.com/goauthentik/authentik), among others. It also supports [Tailscale](https://tailscale.com/) (i.e. [Tailscale Serve](https://tailscale.com/kb/1312/serve)) which forwards headers in the same way.
9+
xyOps uses the "trusted headers" implementation for SSO, allowing for easy integration with several authentication tools and middlewares. These include [OAuth2-Proxy](https://github.com/oauth2-proxy/oauth2-proxy), [Vouch](https://github.com/vouch/vouch-proxy), [Authelia](https://github.com/authelia/authelia), and [Authentik](https://github.com/goauthentik/authentik), among others. It also supports [Tailscale](tailscale.md) (i.e. [Tailscale Serve](https://tailscale.com/kb/1312/serve)) which forwards headers in the same way.
1010

1111
The trusted header flow works as follows:
1212

@@ -131,7 +131,9 @@ All the SSO settings for xyOps are contained in the `/opt/xyops/conf/sso.json` f
131131
"replace_roles": false,
132132
"replace_privileges": false,
133133
"admin_bootstrap": "",
134-
"logout_url": ""
134+
"logout_url": "",
135+
"command": "",
136+
"preset": ""
135137
}
136138
```
137139

@@ -152,6 +154,7 @@ Here are descriptions of all the SSO properties:
152154
| `admin_bootstrap` | String | Temporarily assign full administrator privileges to a given user. This is used for bootstrapping the system on initial setup. See [Admin Bootstrap](#admin-bootstrap) for more. |
153155
| `logout_url` | String | Set this to the URL to redirect the user to after xyOps performs its own logout. See [Logging Out](#logging-out) below for details. |
154156
| `command` | String | Optional custom shell command to filter all incoming SSO requests and inject headers. See [Custom Command](#custom-command) below for details. |
157+
| `preset` | String | Optional preset ID which pre-configures SSO for specific providers. Currently used for [Tailscale](tailscale.md). |
155158

156159
### Header Map
157160

@@ -583,25 +586,7 @@ Authelia can also be [integrated with Nginx](https://www.authelia.com/integratio
583586

584587
## Tailscale
585588

586-
xyOps works with [Tailscale](https://tailscale.com/) (specifically [Tailscale Serve](https://tailscale.com/kb/1312/serve)), which acts as an SSO auth system by forwarding trusted headers. Here is how you should configure your [Header Map](#header-map) for Tailscale Serve use:
587-
588-
```json
589-
"header_map": {
590-
"username": "Tailscale-User-Login",
591-
"full_name": "Tailscale-User-Name",
592-
"email": "Tailscale-User-Login",
593-
"avatar": "Tailscale-User-Profile-Pic",
594-
"groups": ""
595-
}
596-
```
597-
598-
Tailscale doesn't forward along user groups, so we cannot auto-assign user roles or privileges. However, you can use the [Admin Bootstrap](#admin-bootstrap) feature to make yourself a full administrator, and then manually promote your other users once they login and have local xyOps user accounts. Also, please read the [Header Cleanup](#header-cleanup) section regarding username cleanup, so you can be sure how your Tailscale username (which comes in an an email address) will be translated on the xyOps side.
599-
600-
Finally, make sure you set your [IP Whitelist](#ip-whitelist) to only accept headers from localhost, as that is how Tailscale Serve routes traffic:
601-
602-
```json
603-
"whitelist": ["127.0.0.1", "::1/128"]
604-
```
589+
See our dedicated [Tailscale Guide](tailscale.md) to set up xyOps with Tailscale.
605590

606591
## Custom Command
607592

docs/tailscale.md

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
# Tailscale
2+
3+
## Overview
4+
5+
xyOps pairs well with [Tailscale](https://tailscale.com/) (specifically [Tailscale Serve](https://tailscale.com/kb/1312/serve)), which acts as an SSO auth system by forwarding trusted headers. This document describes how to set up xyOps on your Tailnet using the Tailscale Serve CLI, and also as a "sidecar" with Docker Compose.
6+
7+
First, please read the [xyOps SSO Guide](sso.md), as it introduces several concepts we will be referencing here.
8+
9+
## Header Map
10+
11+
Here is how you should configure your [SSO Header Map](sso.md#header-map) for Tailscale Serve use:
12+
13+
```json
14+
"header_map": {
15+
"username": "Tailscale-User-Login",
16+
"full_name": "Tailscale-User-Name",
17+
"email": "Tailscale-User-Login",
18+
"avatar": "Tailscale-User-Profile-Pic",
19+
"groups": "Tailscale-App-Capabilities"
20+
}
21+
```
22+
23+
Alternatively, you can simply enable SSO and set the `preset` property to `tailscale`, which configures the header map (among other things) for you. Example with environment variables:
24+
25+
```sh
26+
XYOPS_SSO__enabled="true"
27+
XYOPS_SSO__preset="tailscale"
28+
```
29+
30+
## Usernames
31+
32+
Please read the [SSO Header Cleanup](sso.md#header-cleanup) section regarding username cleanup, so you understand how your Tailscale username (which comes in as an email address) will be translated on the xyOps side. By default, the email domain is stripped, and your username becomes the first part of your email address.
33+
34+
## Admin Bootstrap
35+
36+
If you want to just get up and running quickly, you can use the [SSO Admin Bootstrap](sso.md#admin-bootstrap) feature to automatically promote yourself to a full administrator. Just note that this is designed as a one-time setup shortcut, and will log a loud warning in the xyOps activity log on every login.
37+
38+
## Capabilities
39+
40+
Tailscale can forward along what they call "capabilities", which can translate to roles and privileges on the xyOps side. In short, you can use Tailscale's capability grants to automatically assign your xyOps users appropriate privileges and/or roles. Here is how to set it up:
41+
42+
1. Login to your [Tailscale Admin Console](https://login.tailscale.com/welcome).
43+
2. Click on the "Access Controls" tab.
44+
3. Click on "JSON Editor".
45+
46+
Locate the `grants` array in the JSON editor, or create it if needed. Here is an example:
47+
48+
```json
49+
"grants": [
50+
{
51+
"src": ["autogroup:admin"],
52+
"dst": ["autogroup:admin"],
53+
"app": {
54+
"xyops.io/cap/ts": [ {"privileges": ["admin"], "roles": []} ],
55+
}
56+
}
57+
]
58+
```
59+
60+
This will automatically promote anyone in your `autogroup:admin` Tailscale group to a full xyOps administrator. The `autogroup:admin` group should already exist in your account, as it is automatically created for you by Tailscale. You can see all your groups, and also also add new groups, by clicking on the "Visual Editor" tab, and then the "Groups" sub-tab.
61+
62+
The `app` section of the grant JSON is very important -- the syntax must be exact:
63+
64+
```json
65+
"app": {
66+
"xyops.io/cap/ts": [ {"privileges": ["admin"], "roles": []} ],
67+
}
68+
```
69+
70+
The `xyops.io/cap/ts` is a unique key that xyOps looks for when it parses the incoming headers. It must be set exactly to `xyops.io/cap/ts`, and the value must be an array of objects, as shown above. The properties inside the object are passed directly to xyOps:
71+
72+
- The `privileges` sub-array can contain any valid [xyOps Privilege IDs](privileges.md), e.g. `admin`.
73+
- The `roles` sub-array can contain any valid [xyOps Role IDs](users.md#roles), which are user-created.
74+
75+
Note that by default, roles and privileges are applied "additively" to user records. Meaning, the SSO sync will never *remove* a role or privilege. This is so you can manually apply your own user roles and permissions using the xyOps Admin UI, and everything plays nice. However, if you do not want this behavior, and instead want Tailscale to be the single source of truth for all user roles and privileges, set the SSO [replace_roles](sso.md#configuration) and/or [replace_privileges](sso.md#configuration) properties to `true`. Those will replace **all** the user roles and/or privileges with whatever we get from the Tailscale capabilities trusted header. This sync happens on every user login and session refresh, wiping out any local changes made in xyOps.
76+
77+
Make sure you click the "Save" button when you are finished editing the JSON in your Tailscale Admin Console.
78+
79+
## Base URL
80+
81+
xyOps needs to know the base URL of the hosted application so it can generate self-referencing URLs in things like outbound emails and web hooks. To do this, you'll need to set the [base_app_url](config.md#base_app_url) configuration property to your Tailscale-provided app URL. The hostname will be the current machine name, followed by your custom Tailnet domain. Example:
82+
83+
```json
84+
"base_app_url": "https://joemax.taild89302.ts.net"
85+
```
86+
87+
Or via environment variable:
88+
89+
```sh
90+
XYOPS_base_app_url="https://joemax.taild89302.ts.net"
91+
```
92+
93+
Additionally, if you plan on adding remote satellite servers, xyOps needs to know what hostname to use for advertising itself to its cluster (i.e. how satellite servers will connect back to the primary conductor server). To do this, add a `hostname` top-level configuration property set to your machine's Tailnet hostname:
94+
95+
```json
96+
"hostname": "joemax.taild89302.ts.net"
97+
```
98+
99+
Or via environment variable:
100+
101+
```sh
102+
XYOPS_hostname="joemax.taild89302.ts.net"
103+
```
104+
105+
By default xyOps uses the current machine's local hostname for this, so we want to override it to use our special Tailnet hostname.
106+
107+
## HTTPS
108+
109+
Assuming you have [Tailscale HTTPS](https://login.tailscale.com/admin/dns) enabled on your Tailnet (highly recommended), this means that Tailscale Serve will **only** support HTTPS URLs to your app. So, we need to set a couple of extra configuration properties so you can add satellite servers and have them communicate over HTTPS as well:
110+
111+
- `satellite.config.secure`: `true`
112+
- `satellite.config.port`: `443`
113+
114+
Or via environment variables:
115+
116+
```sh
117+
XYOPS_satellite__config__secure="true"
118+
XYOPS_satellite__config__port="443"
119+
```
120+
121+
## Serve
122+
123+
Once you have xyOps configured and running with SSO enabled, start [Tailscale Serve](https://tailscale.com/kb/1312/serve) using these command-line arguments:
124+
125+
```sh
126+
tailscale serve --accept-app-caps=xyops.io/cap/ts 5522
127+
```
128+
129+
The special `--accept-app-caps=xyops.io/cap/ts` argument tells Tailscale to forward the special `Tailscale-App-Capabilities` HTTP header with all incoming requests, which xyOps uses to apply user privileges and roles (see [Capabilities](#capabilities) above).
130+
131+
Note that the first time you visit your Tailscale-provided secure HTTPS URL for your served app, there may be a short delay as Tailscale provisions the TLS certificate. If you receive a timeout error, please wait a few seconds and refresh. This is normal.
132+
133+
## Logout
134+
135+
When a user clicks the "Logout" button in the xyOps UI, the user's session data and cookie are deleted. However, with an SSO provider like Tailscale we cannot "fully" log the user out, because they will still be connected and authenticated with their Tailnet, and thus if they just navigate their browser back to the app, they'll instantly log back in (this is by design).
136+
137+
So, for Tailscale SSO the best way to handle this is to set the [logout_url](sso.md#logging-out) to the following value, which displays a message to the user describing the "partial logout" situation:
138+
139+
```sh
140+
XYOPS_SSO__logout_url="/api/app/sso_logout"
141+
```
142+
143+
Note that if you use the `preset` feature to enable Tailscale (see above), the `logout_url` is set automatically for you.
144+
145+
## Security
146+
147+
For security hardening, set your [SSO IP Whitelist](sso.md#ip-whitelist) to only accept trusted headers from localhost, as that is how Tailscale Serve routes traffic:
148+
149+
```json
150+
"whitelist": ["127.0.0.1", "::1/128"]
151+
```
152+
153+
The whitelist can also be specified via environment variable. Just use a CSV list of IPs and/or CIDRs in that case:
154+
155+
```sh
156+
XYOPS_SSO__whitelist="127.0.0.1,::1/128"
157+
```
158+
159+
Finally, remember to **delete the stock admin user account** that xyOps automatically creates on first install. Since you are using SSO this account is technically "unreachable", but deleting it is safest as it has an insecure password by default.
160+
161+
## Sidecar
162+
163+
Tailscale has a really cool feature which allows xyOps to run in a Docker container as its own dedicated "node" on your Tailnet. This is done by running Tailscale as a [sidecar container](https://tailscale.com/blog/docker-tailscale-guide) alongside a xyOps container. Tailscale then handles everything including DNS, TLS, and proxying requests to xyOps, including forwarding trusted headers for automatic login and privilege / role assignments. This guide contains everything you need to get up and running.
164+
165+
### Tailscale Admin Setup
166+
167+
The first thing you'll need to do is login to your [Tailscale Admin Console](https://login.tailscale.com/welcome), and then:
168+
169+
1. Read the [Capabilities](#capabilities) section above, to add a grant into your Tailnet policy file, so xyOps can automatically assign privileges and roles to your users.
170+
2. Create an Auth Key. See [Generate An Auth Key]( https://tailscale.com/kb/1085/auth-keys#generate-an-auth-key) for instructions.
171+
172+
Remember, auth keys expire by default. See [Tailscale Key Expiry](https://tailscale.com/docs/features/access-control/auth-keys#key-expiry) to learn more. For permanent access, you can disable key expiry, and add a tag to the machine.
173+
174+
### Host Setup
175+
176+
Make sure you have [Docker](https://www.docker.com/) and the Docker CLI with Docker Compose all installed and running.
177+
178+
Create a directory on your host machine for xyOps to use, and `cd` into it. It just needs to store a couple of configuration files and some volume-mapped directories for the containers.
179+
180+
### Env File
181+
182+
Create an `.env` file in our host directory with the following contents:
183+
184+
```sh
185+
# Tailscale Configuration
186+
TS_AUTHKEY="YOUR_TAILSCALE_AUTHKEY" # Get auth key from https://tailscale.com/admin/authkeys. See: https://tailscale.com/kb/1085/auth-keys#generate-an-auth-key for instructions.
187+
TS_HOST="xyops.taild89302.ts.net" # Change to your own tailnet domain, keeping `xyops.` at the front.
188+
TZ="America/Los_Angeles" # Change to your own local timezone, or comment out if this is set in your shell already.
189+
```
190+
191+
- Change the `TS_AUTHKEY` value to your own auth key you just created on your Tailscale console.
192+
- Change the `TS_HOST` value to your own Tailnet domain name, leaving in the `xyops.` prefix. See the [DNS](https://login.tailscale.com/admin/dns) tab in your Tailscale Admin Console.
193+
- Change the `TZ` to your own local timezone, so xyOps can rotate logs and reset daily stats at "your" midnight.
194+
195+
### Docker Compose
196+
197+
Create a `compose.yaml` file in our host directory with the following contents:
198+
199+
```yaml
200+
configs:
201+
ts-serve:
202+
content: |
203+
{"TCP":{"443":{"HTTPS":true}},
204+
"Web":{"$${TS_CERT_DOMAIN}:443":
205+
{"Handlers":{"/":
206+
{"Proxy":"http://127.0.0.1:5522","AcceptAppCaps":["xyops.io/cap/ts"]}}}},
207+
"AllowFunnel":{"$${TS_CERT_DOMAIN}:443":false}}
208+
209+
services:
210+
# Tailscale Sidecar Configuration
211+
tailscale:
212+
image: tailscale/tailscale:latest
213+
container_name: tailscale-xyops # Name for local container management
214+
hostname: xyops # Name used within your Tailscale environment
215+
environment:
216+
TS_AUTHKEY: "${TS_AUTHKEY}"
217+
TS_STATE_DIR: "/var/lib/tailscale"
218+
TS_SERVE_CONFIG: "/config/serve.json"
219+
TS_USERSPACE: "false"
220+
TS_ENABLE_HEALTH_CHECK: "true" # Enable healthcheck endpoint: "/healthz"
221+
TS_LOCAL_ADDR_PORT: "127.0.0.1:41234" # The <addr>:<port> for the healthz endpoint
222+
TS_ACCEPT_DNS: "true" # Use Tailscale MagicDNS
223+
TS_AUTH_ONCE: "true"
224+
configs:
225+
- source: ts-serve
226+
target: /config/serve.json
227+
volumes:
228+
- ./ts-config:/config # Config folder used to store Tailscale files
229+
- ./ts-state:/var/lib/tailscale # Tailscale requirement
230+
devices:
231+
- /dev/net/tun:/dev/net/tun
232+
cap_add:
233+
- net_admin
234+
healthcheck:
235+
test: ["CMD", "wget", "--spider", "-q", "http://127.0.0.1:41234/healthz"]
236+
interval: 1m
237+
timeout: 10s
238+
retries: 3
239+
start_period: 10s
240+
restart: always
241+
242+
xyops01:
243+
image: ghcr.io/pixlcore/xyops:latest
244+
container_name: xyops01
245+
network_mode: service:tailscale # Use Sidecar
246+
init: true
247+
restart: unless-stopped
248+
environment:
249+
XYOPS_xysat_local: "true"
250+
XYOPS_hostname: "${TS_HOST}"
251+
XYOPS_masters: "${TS_HOST}"
252+
XYOPS_base_app_url: "https://${TS_HOST}"
253+
XYOPS_satellite__config__secure: "true"
254+
XYOPS_satellite__config__port: "443"
255+
XYOPS_SSO__enabled: "true"
256+
XYOPS_SSO__preset: "tailscale"
257+
XYOPS_SSO__whitelist: "127.0.0.1,::1/128"
258+
TZ: "${TZ}"
259+
volumes:
260+
- xy-data:/opt/xyops/data
261+
- ./xyops01-conf:/opt/xyops/conf
262+
- /var/run/docker.sock:/var/run/docker.sock
263+
depends_on:
264+
tailscale:
265+
condition: service_healthy
266+
267+
volumes:
268+
xy-data:
269+
```
270+
271+
You should not need to edit anything in the `compose.yaml` file, but here are a couple of notes:
272+
273+
- The `XYOPS_xysat_local` environment variable causes xyOps to launch [xySat](hosting.md#satellite) in the background, in the same container. This is so you can start running jobs right away -- it is great for testing and home labs, but *not recommended for production*.
274+
- The `/var/run/docker.sock` bind is optional, and allows xyOps to launch its own containers (i.e. for the [Docker Plugin](plugins.md#docker-plugin), and the [Plugin Marketplace](marketplace.md)).
275+
276+
### Start Up
277+
278+
Type this command to start everything up (the `-d` switch runs it in the background):
279+
280+
```sh
281+
docker compose -p xyops-tailscale up -d
282+
```
283+
284+
And then visit your `TS_HOST` URL in your favorite browser. Example:
285+
286+
https://xyops.taild89302.ts.net/
287+
288+
Note that the first time you visit the URL, there may be a short delay as Tailscale provisions the TLS certificate.

0 commit comments

Comments
 (0)