diff --git a/docs/Contributing/guides/README.md b/docs/Contributing/guides/README.md index a1e481de5a..53cf6cc607 100644 --- a/docs/Contributing/guides/README.md +++ b/docs/Contributing/guides/README.md @@ -11,6 +11,7 @@ This directory contains guides for common development tasks in Fleet. - [Troubleshooting Live Queries](troubleshooting-live-queries.md) - Guide for troubleshooting live queries - [Enroll hosts with plain osquery](enroll-hosts-with-plain-osquery.md) - Guide for enrolling hosts with plain osquery - [Upcoming activities](upcoming-activities.md) - Guide for managing upcoming host activities +- [mTLS reverse proxy setup](mtls-reverse-proxy-setup.md) - Guide for setting up an mTLS reverse proxy for testing ## UI development diff --git a/docs/Contributing/guides/mtls-reverse-proxy-setup.md b/docs/Contributing/guides/mtls-reverse-proxy-setup.md new file mode 100644 index 0000000000..78538fc74c --- /dev/null +++ b/docs/Contributing/guides/mtls-reverse-proxy-setup.md @@ -0,0 +1,209 @@ +# Create a public mTLS reverse proxy for testing mTLS + +```mermaid +flowchart LR + subgraph Internet + Client["Client (with client cert)"] + end + + subgraph Cloud_proxy + Proxy["Caddy / mTLS Proxy on VM"] + end + + subgraph Tunnel_path + ngrok["ngrok public URL"] + LocalApp["Your Fleet server"] + end + + Client -->|TLS + Client Cert| Proxy + Proxy -->|HTTPS + forwarded headers| ngrok + ngrok -->|tunnel traffic| LocalApp +``` + +## Assumptions & prerequisites + +* You own a domain, e.g. `example.site`, or some domain. +* You will use a subdomain, e.g. `mtls.example.site` or whatever you choose. +* You have a **client CA certificate** (root or intermediate) that signs all valid client certificates (e.g. `client-ca.crt`). +* You have a backend (ngrok URL) that you want to forward to (i.e. `https://my-fleet-server.ngrok.io`). +* You have access to the DigitalOcean or another cloud provider. +* Replace `example.site` and `my-fleet-server.ngrok.io` in this guide with your own values. + +--- + +## Step 1: Create a Droplet (VM) on DigitalOcean (or other cloud provider) + +1. Log in to your DigitalOcean account. +2. In the dashboard, click **Create → Droplet**. +3. Choose an OS image (e.g. Ubuntu 24.04 LTS is a good default). +4. Choose a plan (for testing, the cheapest is fine, e.g. “Basic” with 1 vCPU / 1 GB RAM). +5. Choose a datacenter region (ideally near your users or your dev location). +6. Add SSH keys (recommended) so you can SSH in securely. If you don’t have an SSH key, you can generate one (`ssh-keygen`) and paste the public key. +7. Finalize settings (hostname, backups, etc.), then click “Create Droplet”. + +After some moments, you’ll have a Droplet with a public IP address (say `203.0.113.45` as an example). + +--- + +## Step 2: Set up DNS so `mtls.yourdomain` points to the Droplet + +You need to make your subdomain resolve to that Droplet’s IP. + +1. In DigitalOcean dashboard → **Networking** → **Domains** (or **DNS**) + +2. In your DNS records page, add a record: + + * Type: **A** + * Hostname: `mtls` (so the full hostname is `mtls.example.site`) (or whatever you choose) + * Value / Points to: your Droplet’s IP (e.g. `203.0.113.45`) + * TTL: the default (or lower if you like) + +3. Wait for DNS propagation (may take some minutes). You can test with `dig mtls.example.site` or `ping mtls.example.site` to see it resolve to your Droplet IP. + +Once that is active, when clients connect to `mtls.example.site`, they reach your Droplet. + +--- + +## Step 3: SSH into the droplet, install Caddy + +1. SSH in: + +```bash +ssh root@203.0.113.45 +``` + +2. Update packages: + +```bash +apt update +apt upgrade -y +``` + +3. Install Caddy (the recommended way is via their official install script or package). On Ubuntu, a reliable method: + +```bash +# Install required packages +apt install -y debian-keyring debian-archive-keyring apt-transport-https + +# Add Caddy repo +curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | apt-key add - +curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list + +apt update +apt install -y caddy +``` + +This gives you a system service `caddy` which can manage and auto-reload configuration. + +--- + +## Step 4: Place your mTLS CA cert on the server + +You need to put the CA certificate (the public certificate of the CA that signs client certs) where Caddy can read it. + +1. On your local machine, copy your CA cert file (say `client-ca.crt`) over to the Droplet: + +```bash +scp client-ca.crt root@203.0.113.45:/etc/caddy/client-ca.crt +``` + +2. On the Droplet, ensure it's readable by Caddy (e.g. permissions): + +```bash +chmod 644 /etc/caddy/client-ca.crt +chown root:root /etc/caddy/client-ca.crt +``` + +--- + +## Step 5: Configure Caddy for TLS + mTLS + reverse proxy + header forwarding + +You need a `Caddyfile` (or JSON config) that: + +* Listens for `mtls.example.site` +* Uses TLS (Caddy will obtain a certificate automatically via Let’s Encrypt) +* Requires client certificate authentication, trusting your CA +* Proxies incoming requests to your backend URL (ngrok) +* Injects headers to your backend with the client’s leaf cert and serial + +Here’s an example `Caddyfile` you can use (put at `/etc/caddy/Caddyfile`): + +```caddyfile +mtls.example.site { + # Enable TLS (Caddy does automatic HTTPS) + tls { + # mTLS (client auth) config + client_auth { + mode require_and_verify + trusted_ca_cert_file /etc/caddy/client-ca.crt + } + } + + # Reverse proxy to your ngrok backend + reverse_proxy https://my-fleet-server.ngrok.io { + # Make ngrok happy: + header_up Host my-fleet-server.ngrok.io + + # Forward headers for client certificate information + # Use DER base64 encoding (PEM has newlines which break HTTP headers). Note: AWS ALB sends URL-encoded PEM format in X-Amzn-Mtls-Clientcert-Leaf. + header_up X-Client-Cert {http.request.tls.client.certificate_der_base64} + header_up X-Client-Cert-Serial {http.request.tls.client.serial} + header_up X-Client-Cert-Subject {http.request.tls.client.subject} + header_up X-Client-Cert-Issuer {http.request.tls.client.issuer} + } +} +``` + +Save that `Caddyfile`. + +--- + +## Step 6: Reload / restart Caddy + +After updating `/etc/caddy/Caddyfile`, reload Caddy to pick up the new config: + +```bash +systemctl reload caddy +``` + +Watch logs to check for TLS / client auth errors: + +```bash +journalctl -u caddy -f +``` + +If things go well, Caddy should: + +* Obtain a TLS certificate for `mtls.example.site` from Let’s Encrypt automatically +* Accept HTTPS connections at that hostname +* Require that connecting clients present a valid certificate signed by your CA +* If the client’s certificate is valid, proxy requests to `https://my-fleet-server.ngrok.io` +* Forward `X-Client-Cert` header with the PEM of the client’s certificate and `X-Client-Cert-Serial` header with the serial number to the backend + +You can test with `curl` from a client that has a valid certificate: + +```bash +curl --cert client-cert.pem --key client-key.pem https://mtls.example.site/healthz +``` + +You should see a response (the backend's response via ngrok), and your backend should receive headers: + +``` +X-Client-Cert: MIIDXTCCAkWgAwIBAgIJAK... (base64-encoded DER certificate) +X-Client-Cert-Serial: 542242443644849078027064623851697342324729218861 +X-Client-Cert-Subject: CN=victor@dev +X-Client-Cert-Issuer: CN=My CA +``` + +If you use a certificate not signed by your CA (or no certificate), the handshake should fail (client is rejected). + +--- + +## Step 7: Verify & troubleshoot + +* Test DNS resolves +* Use `curl` with correct / incorrect cert +* Look at Caddy logs for TLS handshake errors +* If the backend (ngrok) rejects certain headers or sees unexpected hostnames, you may need to adjust `header_up Host` or other headers + +--- diff --git a/docs/Contributing/research/orchestration/okta-conditional-access.md b/docs/Contributing/research/orchestration/okta-conditional-access.md index aa4262c9c0..d65736935d 100644 --- a/docs/Contributing/research/orchestration/okta-conditional-access.md +++ b/docs/Contributing/research/orchestration/okta-conditional-access.md @@ -110,6 +110,46 @@ sequenceDiagram * On factor evaluation, query required device policies and return pass/fail. * Provide a user-facing remediation URL when the factor denies. +**SAML flow with Okta using Fleet as IdP** +```mermaid +sequenceDiagram + autonumber + participant U as User / Browser + participant O as Okta + participant I as Fleet (SAML IdP) + participant A as Downstream App + + U->>O: Navigate to app / Okta sign-in + O->>O: Evaluate sign-on policy (requires external SAML IdP factor) + + Note over O,I: SP-initiated SAML
HTTP-redirect binding to IdP SSO + + O-->>U: 302 Redirect to IdP SSO with AuthnRequest (+RelayState) + U->>I: GET /sso?SAMLRequest=...&RelayState=... + I->>I: Validate AuthnRequest (Issuer=Okta, ACS, Conditions) + + rect rgb(245,245,245) + Note over U,I: Fleet performs device check + I->>I: Check device policies and build SAML Assertion + I->>I: Sign response/assertion with IdP private key + end + + Note over I,O: HTTP-POST binding to Okta ACS + + I-->>U: HTML form (SAMLResponse, RelayState) + U->>O: POST SAMLResponse to Okta ACS + + O->>O: Validation + O->>O: Mark "external SAML factor" = SUCCESS in policy chain + O->>O: Establish Okta session for user + + Note over O,A: Okta now acts as IdP to the downstream app + + O->>A: Send SAML response/assertion to app's ACS (via browser POST) + A->>A: Validate Okta assertion, create app session + A-->>U: Grant access +``` + **Links.** * [1Password Extended Access Management's Okta integration](https://blog.1password.com/extended-access-management-okta-guide) diff --git a/tools/test-orbit-mtls/README.md b/tools/test-orbit-mtls/README.md index bee8fc87a0..973ad52224 100644 --- a/tools/test-orbit-mtls/README.md +++ b/tools/test-orbit-mtls/README.md @@ -66,6 +66,8 @@ docker compose -f ./tools/test-orbit-mtls/docker-compose.yml up The proxy will listen to port 8888 and use TLS client certificates for authentication. +> Note: If you would like to use a public URL, see [public mTLS reverse proxy instructions](../../docs/Contributing/guides/mtls-reverse-proxy-setup.md) + ## 3. Generate Orbit installers with custom flags ```sh