Add comprehensive Keycloak setup guide and external service support
KEYCLOAK-SETUP-GUIDE.md: Complete manual explaining Keycloak concepts, manual setup, and external services WHAT'S A REALM: - Isolated container for users/clients/config - Like a "company" or "organization" - master realm = admin only - homelab realm = your actual users - Fully isolated from each other WHAT'S AN OAUTH2 CLIENT: - Each service (ActualBudget, etc.) is a "client" - Needs Client ID, Secret, and Redirect URIs - Redirect URIs must match EXACTLY - Guide explains the authentication flow MANUAL SETUP INSTRUCTIONS: - Step-by-step via web UI - Create realm manually - Create OAuth clients manually - Configure redirect URIs - Create users and set passwords - Test the setup EXTERNAL SERVICE SUPPORT (Pikapod, etc.): Script now asks about setup type: 1. Local only (http://localhost) 2. Public domain (https://yourdomain.com) 3. Both local and public For external services: - Prompts for your public domain - Warns that Keycloak MUST be accessible at https://auth.yourdomain.com - Checks if Caddy/DNS are configured - Asks for external service URL (e.g., Pikapod) - Configures redirect URIs for all scenarios REDIRECT URIS NOW INCLUDE: - http://localhost:5006/* (local dev) - https://budget.yourdomain.com/* (self-hosted) - https://actualbudget-abc.pikapod.net/* (external) - Multiple patterns for flexibility SAVED CONFIG FILES UPDATED: - Shows LOCAL DEVELOPMENT URLs - Shows PRODUCTION URLs (if public domain set) - Shows EXTERNAL SERVICE URLs (if external service set) - Lists all configured redirect URIs - Clear instructions for each scenario CADDY CONFIGURATION GUIDE: - How to configure DNS A/CNAME records - Caddyfile example for Keycloak - Security headers included - Step-by-step setup for external access RECONFIGURATION SUPPORT: - Guide explains how to add realms manually - Guide explains how to add clients manually - CLI examples for adding realms/users/clients - Can re-run script to configure additional realms COMMON USE CASES: 1. All local services 2. Self-hosted with domain 3. Mixed (local + external like Pikapod) Each use case explained with complete examples TROUBLESHOOTING: - Invalid redirect URI - Client not found - Invalid client secret - External service can't reach Keycloak - CORS/redirect failures - Admin console login issues With this update, users can: ✅ Understand what Keycloak is and how it works ✅ Configure it manually if they prefer ✅ Use it with external services like Pikapod ✅ Set up proper DNS/Caddy for production ✅ Troubleshoot common issues ✅ Add realms and clients later
This commit is contained in:
@@ -0,0 +1,678 @@
|
||||
# Keycloak Setup Guide
|
||||
## Complete Manual and Automated Configuration Guide
|
||||
|
||||
This guide explains Keycloak concepts and how to configure it both automatically (via the script) and manually (via the web UI).
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
1. [What is Keycloak?](#what-is-keycloak)
|
||||
2. [Key Concepts](#key-concepts)
|
||||
3. [Automated Setup (via Script)](#automated-setup)
|
||||
4. [Manual Setup (via Web UI)](#manual-setup)
|
||||
5. [Configuring External Services](#configuring-external-services)
|
||||
6. [Reconfiguration & Adding Realms](#reconfiguration)
|
||||
7. [Common Use Cases](#common-use-cases)
|
||||
8. [Troubleshooting](#troubleshooting)
|
||||
|
||||
---
|
||||
|
||||
## What is Keycloak?
|
||||
|
||||
Keycloak is an **Identity and Access Management (IAM)** system that provides:
|
||||
- **Single Sign-On (SSO)**: Log in once, access all your services
|
||||
- **User Management**: Create, manage, and authenticate users in one place
|
||||
- **OAuth2/OIDC**: Industry-standard authentication for web apps
|
||||
- **Social Login**: Allow login via Google, GitHub, etc.
|
||||
- **Multi-Factor Authentication (MFA)**: Add extra security with 2FA/TOTP
|
||||
- **LDAP/Active Directory Integration**: Connect to existing user directories
|
||||
|
||||
**Think of Keycloak as:** A centralized login system for all your self-hosted services.
|
||||
|
||||
---
|
||||
|
||||
## Key Concepts
|
||||
|
||||
### 1. **Realm**
|
||||
A **realm** is an isolated container for users, clients, and configuration.
|
||||
|
||||
**Analogy:** Think of a realm like a "company" or "organization" in Keycloak.
|
||||
|
||||
**Why you need it:**
|
||||
- The default `master` realm is for Keycloak admin only
|
||||
- You create a separate realm (e.g., `homelab`) for your actual users and applications
|
||||
- Realms are completely isolated - users in one realm can't access another
|
||||
|
||||
**Example:**
|
||||
- `master` realm: Only for Keycloak administrators
|
||||
- `homelab` realm: For your personal services (ActualBudget, Jellyfin, etc.)
|
||||
- `family` realm: Separate realm for family members (optional)
|
||||
|
||||
### 2. **OAuth2/OpenID Connect (OIDC) Client**
|
||||
A **client** is an application that uses Keycloak for authentication.
|
||||
|
||||
**Analogy:** Each service (ActualBudget, Jellyfin, etc.) is a "client" that asks Keycloak "Is this user allowed to log in?"
|
||||
|
||||
**Required information:**
|
||||
- **Client ID**: Name of the application (e.g., `actualbudget`)
|
||||
- **Client Secret**: Password for the application (auto-generated, 64-char hex)
|
||||
- **Redirect URIs**: Where Keycloak sends users after login
|
||||
- Example: `https://budget.yourdomain.com/*`
|
||||
- Must match EXACTLY or login will fail
|
||||
|
||||
**Flow:**
|
||||
1. User clicks "Login" in ActualBudget
|
||||
2. ActualBudget redirects to Keycloak: `https://auth.yourdomain.com/login`
|
||||
3. User logs in with username/password
|
||||
4. Keycloak redirects back to ActualBudget: `https://budget.yourdomain.com/callback`
|
||||
5. ActualBudget gets user info and logs them in
|
||||
|
||||
### 3. **Users**
|
||||
A **user** is a person who can log in to your services.
|
||||
|
||||
**User attributes:**
|
||||
- Username (required, unique)
|
||||
- Email (optional but recommended)
|
||||
- First name / Last name (optional)
|
||||
- Password (set via Credentials tab)
|
||||
- Email verified (set to true to skip verification)
|
||||
- Enabled (must be true for user to log in)
|
||||
|
||||
### 4. **Redirect URIs**
|
||||
**Critical concept:** The redirect URI is where Keycloak sends the user after successful login.
|
||||
|
||||
**Common mistakes:**
|
||||
- ❌ `http://localhost:5006` (won't work for external services)
|
||||
- ❌ `https://budget.example.com` (missing wildcard or path)
|
||||
- ✅ `https://budget.example.com/*` (correct - allows all paths)
|
||||
|
||||
**For external services (like Pikapod):**
|
||||
- Pikapod gives you a URL like: `https://actualbudget-abc123.pikapod.net`
|
||||
- Your redirect URI: `https://actualbudget-abc123.pikapod.net/*`
|
||||
- Your Keycloak URL: `https://auth.yourdomain.com` (must be publicly accessible)
|
||||
|
||||
---
|
||||
|
||||
## Automated Setup (via Script)
|
||||
|
||||
The script automates everything for you. Here's what it does:
|
||||
|
||||
### Step 1: Install Keycloak
|
||||
```bash
|
||||
./ubuntu-post-install.sh
|
||||
# Select KEYCLOAK in whiptail menu
|
||||
```
|
||||
|
||||
Prompts:
|
||||
- Admin password (for Keycloak admin console)
|
||||
- Database password (for PostgreSQL)
|
||||
|
||||
### Step 2: Automated Configuration
|
||||
```
|
||||
Configure Keycloak with initial realm and clients? (y/n): y
|
||||
```
|
||||
|
||||
This automatically:
|
||||
1. ✅ Waits for Keycloak to start (health check)
|
||||
2. ✅ Logs in using admin CLI (`kcadm.sh`)
|
||||
3. ✅ Creates a realm (e.g., `homelab`)
|
||||
4. ✅ Creates OAuth client for ActualBudget (if selected)
|
||||
5. ✅ Creates generic OAuth client template
|
||||
6. ✅ Saves all credentials to `~/docker/keycloak/*.txt`
|
||||
7. ✅ Optionally creates initial user
|
||||
|
||||
### Step 3: What Gets Created
|
||||
|
||||
**Realm:** `homelab` (or your custom name)
|
||||
|
||||
**ActualBudget OAuth Client:**
|
||||
- Client ID: `actualbudget`
|
||||
- Client Secret: (saved to `actualbudget-oauth.txt`)
|
||||
- Redirect URIs:
|
||||
- `http://localhost:5006/*` (local development)
|
||||
- `http://yourdomain.com:5006/*` (local with domain)
|
||||
- `https://yourdomain.com/*` (production - any subdomain)
|
||||
- `https://budget.yourdomain.com/*` (specific subdomain)
|
||||
|
||||
**Generic OAuth Client:**
|
||||
- Client ID: `generic-app`
|
||||
- Client Secret: (saved to `generic-oauth.txt`)
|
||||
- Can be cloned for other services
|
||||
|
||||
**Initial User:**
|
||||
- Username, email, password you provide
|
||||
- Immediately active
|
||||
- Can log in to all services
|
||||
|
||||
### Step 4: Configuration Files
|
||||
|
||||
All credentials saved to:
|
||||
```
|
||||
~/docker/keycloak/actualbudget-oauth.txt
|
||||
~/docker/keycloak/generic-oauth.txt
|
||||
```
|
||||
|
||||
These files contain:
|
||||
- Client ID
|
||||
- Client Secret
|
||||
- Authorization URL
|
||||
- Token URL
|
||||
- User Info URL
|
||||
- Instructions for configuring each service
|
||||
|
||||
---
|
||||
|
||||
## Manual Setup (via Web UI)
|
||||
|
||||
If you prefer to configure Keycloak manually, or want to add services later:
|
||||
|
||||
### Access Admin Console
|
||||
```
|
||||
URL: http://localhost:8180/admin
|
||||
Username: admin
|
||||
Password: [your admin password]
|
||||
```
|
||||
|
||||
### Step 1: Create a Realm
|
||||
|
||||
1. **Click dropdown** in top-left corner (shows "Master")
|
||||
2. **Click "Create Realm"**
|
||||
3. **Realm name:** `homelab` (or your choice)
|
||||
4. **Click "Create"**
|
||||
|
||||
**Settings to configure:**
|
||||
- **Login tab:**
|
||||
- ✅ User registration: OFF (you create users manually)
|
||||
- ✅ Forgot password: ON (allows password resets)
|
||||
- ✅ Remember me: ON (convenience)
|
||||
- ✅ Login with email: ON (users can use email instead of username)
|
||||
|
||||
- **Email tab:** (optional, for password resets)
|
||||
- Configure SMTP settings if you want email features
|
||||
|
||||
### Step 2: Create an OAuth2 Client (for ActualBudget)
|
||||
|
||||
1. **Switch to your realm** (`homelab`) via dropdown
|
||||
2. **Go to Clients** (left menu)
|
||||
3. **Click "Create client"**
|
||||
|
||||
**General Settings:**
|
||||
- **Client type:** OpenID Connect
|
||||
- **Client ID:** `actualbudget`
|
||||
- **Name:** `ActualBudget`
|
||||
- **Description:** `Personal Finance Management`
|
||||
- **Click "Next"**
|
||||
|
||||
**Capability config:**
|
||||
- ✅ Client authentication: ON (creates a secret)
|
||||
- ✅ Authorization: OFF (not needed)
|
||||
- ✅ Standard flow: ON (authorization code flow)
|
||||
- ✅ Direct access grants: ON (allows username/password)
|
||||
- ❌ Implicit flow: OFF (deprecated)
|
||||
- ❌ Service accounts: OFF (not needed for web apps)
|
||||
- **Click "Next"**
|
||||
|
||||
**Login settings:**
|
||||
|
||||
**Important: Adjust these for your setup!**
|
||||
|
||||
**For local ActualBudget:**
|
||||
```
|
||||
Root URL: http://localhost:5006
|
||||
Home URL: http://localhost:5006
|
||||
Valid redirect URIs:
|
||||
http://localhost:5006/*
|
||||
http://localhost:5006/callback
|
||||
|
||||
Valid post logout redirect URIs: +
|
||||
|
||||
Web origins:
|
||||
http://localhost:5006
|
||||
```
|
||||
|
||||
**For external ActualBudget (Pikapod, etc.):**
|
||||
```
|
||||
Root URL: https://actualbudget-abc123.pikapod.net
|
||||
Home URL: https://actualbudget-abc123.pikapod.net
|
||||
Valid redirect URIs:
|
||||
https://actualbudget-abc123.pikapod.net/*
|
||||
https://actualbudget-abc123.pikapod.net/callback
|
||||
|
||||
Valid post logout redirect URIs: +
|
||||
|
||||
Web origins:
|
||||
https://actualbudget-abc123.pikapod.net
|
||||
```
|
||||
|
||||
**For self-hosted with domain:**
|
||||
```
|
||||
Root URL: https://budget.yourdomain.com
|
||||
Home URL: https://budget.yourdomain.com
|
||||
Valid redirect URIs:
|
||||
https://budget.yourdomain.com/*
|
||||
https://budget.yourdomain.com/callback
|
||||
|
||||
Valid post logout redirect URIs: +
|
||||
|
||||
Web origins:
|
||||
https://budget.yourdomain.com
|
||||
```
|
||||
|
||||
4. **Click "Save"**
|
||||
|
||||
### Step 3: Get Client Secret
|
||||
|
||||
1. **Go to "Credentials" tab**
|
||||
2. **Copy "Client secret"** (you'll need this for ActualBudget)
|
||||
3. **Save it somewhere safe!**
|
||||
|
||||
### Step 4: Create a User
|
||||
|
||||
1. **Go to Users** (left menu)
|
||||
2. **Click "Create user"**
|
||||
|
||||
**User details:**
|
||||
- **Username:** `john` (required)
|
||||
- **Email:** `john@example.com` (optional but recommended)
|
||||
- **Email verified:** ✅ ON (skip email verification)
|
||||
- **First name:** `John`
|
||||
- **Last name:** `Doe`
|
||||
- **Enabled:** ✅ ON (user can log in)
|
||||
- **Click "Create"**
|
||||
|
||||
**Set password:**
|
||||
1. **Go to "Credentials" tab**
|
||||
2. **Click "Set password"**
|
||||
3. **Enter password** (twice)
|
||||
4. **Temporary:** ❌ OFF (user won't be forced to change it)
|
||||
5. **Click "Save"**
|
||||
6. **Confirm** in popup
|
||||
|
||||
### Step 5: Test Login
|
||||
|
||||
1. **Go to Realm Settings** → **Endpoints**
|
||||
2. **Click "OpenID Endpoint Configuration"** (opens JSON)
|
||||
3. **Find:** `authorization_endpoint`
|
||||
4. **Copy URL** and open in browser
|
||||
5. **Add:** `?client_id=actualbudget&response_type=code&redirect_uri=http://localhost:5006/callback`
|
||||
6. **Log in** with your user
|
||||
7. **You should see:** Redirect to callback URL (may error if ActualBudget not configured, but login works)
|
||||
|
||||
---
|
||||
|
||||
## Configuring External Services
|
||||
|
||||
### Keycloak MUST be Publicly Accessible
|
||||
|
||||
**Critical:** For external services like Pikapod, your Keycloak must be accessible from the internet.
|
||||
|
||||
### Requirements:
|
||||
1. ✅ **Domain name** (e.g., `yourdomain.com`)
|
||||
2. ✅ **DNS A record** pointing to your server
|
||||
3. ✅ **Caddy reverse proxy** with HTTPS
|
||||
4. ✅ **Port 80/443 open** in firewall
|
||||
5. ✅ **Keycloak accessible** at `https://auth.yourdomain.com`
|
||||
|
||||
### Setup Caddy for Keycloak
|
||||
|
||||
**Add to Caddyfile:**
|
||||
```caddy
|
||||
auth.yourdomain.com {
|
||||
log {
|
||||
output file /var/log/caddy/keycloak-access.log
|
||||
format json
|
||||
level INFO
|
||||
}
|
||||
|
||||
reverse_proxy localhost:8180
|
||||
|
||||
# Security headers
|
||||
header {
|
||||
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
||||
X-Frame-Options "SAMEORIGIN"
|
||||
X-Content-Type-Options "nosniff"
|
||||
X-XSS-Protection "1; mode=block"
|
||||
Referrer-Policy "strict-origin-when-cross-origin"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Reload Caddy:**
|
||||
```bash
|
||||
cd ~/docker/caddy
|
||||
docker exec -w /etc/caddy caddy caddy reload
|
||||
```
|
||||
|
||||
**Test:**
|
||||
```
|
||||
https://auth.yourdomain.com/admin
|
||||
```
|
||||
|
||||
### Configure DNS
|
||||
|
||||
**Add A record:**
|
||||
```
|
||||
auth.yourdomain.com → [Your Server IP]
|
||||
```
|
||||
|
||||
**Or use CNAME:**
|
||||
```
|
||||
auth → yourdomain.com
|
||||
```
|
||||
|
||||
### Example: ActualBudget on Pikapod
|
||||
|
||||
**Scenario:**
|
||||
- Keycloak: `https://auth.yourdomain.com` (your server)
|
||||
- ActualBudget: `https://actualbudget-abc123.pikapod.net` (Pikapod)
|
||||
|
||||
**In Keycloak:**
|
||||
|
||||
1. **Create client:** `actualbudget-pikapod`
|
||||
2. **Redirect URIs:**
|
||||
```
|
||||
https://actualbudget-abc123.pikapod.net/*
|
||||
https://actualbudget-abc123.pikapod.net/callback
|
||||
```
|
||||
3. **Web origins:**
|
||||
```
|
||||
https://actualbudget-abc123.pikapod.net
|
||||
```
|
||||
|
||||
**In ActualBudget (Pikapod):**
|
||||
|
||||
Settings → Authentication:
|
||||
```
|
||||
Client ID: actualbudget-pikapod
|
||||
Client Secret: [from Keycloak credentials tab]
|
||||
|
||||
Authorization URL: https://auth.yourdomain.com/realms/homelab/protocol/openid-connect/auth
|
||||
Token URL: https://auth.yourdomain.com/realms/homelab/protocol/openid-connect/token
|
||||
User Info URL: https://auth.yourdomain.com/realms/homelab/protocol/openid-connect/userinfo
|
||||
```
|
||||
|
||||
**Flow:**
|
||||
1. User visits `https://actualbudget-abc123.pikapod.net`
|
||||
2. Clicks "Login"
|
||||
3. Redirects to `https://auth.yourdomain.com/realms/homelab/...`
|
||||
4. User logs in
|
||||
5. Redirects back to `https://actualbudget-abc123.pikapod.net/callback`
|
||||
6. User is logged in!
|
||||
|
||||
---
|
||||
|
||||
## Reconfiguration & Adding Realms
|
||||
|
||||
You can re-run the script to add more realms or clients!
|
||||
|
||||
### Option 1: Re-run the Script
|
||||
|
||||
```bash
|
||||
cd ~/docker/keycloak
|
||||
docker compose down
|
||||
cd ~
|
||||
./ubuntu-post-install.sh
|
||||
# Select KEYCLOAK again
|
||||
# Choose "Configure Keycloak..." → Yes
|
||||
# Enter new realm name: "family"
|
||||
# Create new users
|
||||
```
|
||||
|
||||
**This creates:**
|
||||
- New realm with new users
|
||||
- New OAuth clients for that realm
|
||||
- Separate from your existing realm
|
||||
|
||||
### Option 2: Add Realm Manually
|
||||
|
||||
**Via Web UI:**
|
||||
1. Go to admin console
|
||||
2. Click realm dropdown
|
||||
3. "Create Realm"
|
||||
4. Name: `family`
|
||||
5. Repeat client/user creation steps
|
||||
|
||||
### Option 3: Use Script Helper
|
||||
|
||||
The script can be extended to add a helper:
|
||||
|
||||
```bash
|
||||
cd ~/docker/keycloak
|
||||
|
||||
# Login to admin CLI
|
||||
docker exec keycloak /opt/keycloak/bin/kcadm.sh config credentials \
|
||||
--server http://localhost:8080 \
|
||||
--realm master \
|
||||
--user admin \
|
||||
--password [YOUR_ADMIN_PASSWORD]
|
||||
|
||||
# Create new realm
|
||||
docker exec keycloak /opt/keycloak/bin/kcadm.sh create realms \
|
||||
-s realm=family \
|
||||
-s enabled=true
|
||||
|
||||
# Create new client
|
||||
docker exec keycloak /opt/keycloak/bin/kcadm.sh create clients -r family \
|
||||
-s clientId=my-new-service \
|
||||
-s enabled=true \
|
||||
-s clientAuthenticatorType=client-secret \
|
||||
-s secret=$(openssl rand -hex 32) \
|
||||
-s 'redirectUris=["https://service.yourdomain.com/*"]'
|
||||
|
||||
# Create new user
|
||||
docker exec keycloak /opt/keycloak/bin/kcadm.sh create users -r family \
|
||||
-s username=alice \
|
||||
-s email=alice@example.com \
|
||||
-s enabled=true
|
||||
|
||||
# Set password
|
||||
docker exec keycloak /opt/keycloak/bin/kcadm.sh set-password -r family \
|
||||
--username alice \
|
||||
--new-password 'AlicePassword123!'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Use Cases
|
||||
|
||||
### Use Case 1: All Local Services
|
||||
**Setup:**
|
||||
- Keycloak: `http://localhost:8180`
|
||||
- ActualBudget: `http://localhost:5006`
|
||||
- Jellyfin: `http://localhost:8096`
|
||||
|
||||
**Configuration:**
|
||||
- No domain needed
|
||||
- Use `localhost` URLs everywhere
|
||||
- Redirect URIs: `http://localhost:PORT/*`
|
||||
|
||||
### Use Case 2: Self-Hosted with Domain
|
||||
**Setup:**
|
||||
- Keycloak: `https://auth.yourdomain.com`
|
||||
- ActualBudget: `https://budget.yourdomain.com`
|
||||
- Jellyfin: `https://jellyfin.yourdomain.com`
|
||||
|
||||
**Configuration:**
|
||||
- Requires domain + Caddy
|
||||
- Use HTTPS URLs
|
||||
- Redirect URIs: `https://service.yourdomain.com/*`
|
||||
|
||||
### Use Case 3: Mixed (Local + External)
|
||||
**Setup:**
|
||||
- Keycloak: `https://auth.yourdomain.com` (self-hosted)
|
||||
- ActualBudget: `https://actualbudget-abc.pikapod.net` (Pikapod)
|
||||
- Jellyfin: `https://jellyfin.yourdomain.com` (self-hosted)
|
||||
|
||||
**Configuration:**
|
||||
- Keycloak MUST be publicly accessible
|
||||
- Each service gets its own client
|
||||
- ActualBudget redirect: `https://actualbudget-abc.pikapod.net/*`
|
||||
- Jellyfin redirect: `https://jellyfin.yourdomain.com/*`
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue: "Invalid redirect URI"
|
||||
**Cause:** Redirect URI in Keycloak doesn't match what the app is using.
|
||||
|
||||
**Fix:**
|
||||
1. Check error message for actual redirect URI
|
||||
2. Add EXACT URI to Keycloak client settings
|
||||
3. Include wildcard: `https://domain.com/*`
|
||||
|
||||
### Issue: "Client not found"
|
||||
**Cause:** Client ID doesn't match.
|
||||
|
||||
**Fix:**
|
||||
1. Check client ID in Keycloak
|
||||
2. Ensure it matches exactly in application
|
||||
3. Case-sensitive!
|
||||
|
||||
### Issue: "Invalid client secret"
|
||||
**Cause:** Wrong secret or expired.
|
||||
|
||||
**Fix:**
|
||||
1. Go to Keycloak → Clients → Credentials
|
||||
2. Copy secret again (or regenerate)
|
||||
3. Update in application
|
||||
|
||||
### Issue: External service can't reach Keycloak
|
||||
**Cause:** Keycloak not publicly accessible.
|
||||
|
||||
**Fix:**
|
||||
1. Ensure Caddy is running: `docker ps | grep caddy`
|
||||
2. Check DNS: `dig auth.yourdomain.com`
|
||||
3. Test URL: `curl https://auth.yourdomain.com`
|
||||
4. Check firewall: `sudo ufw status` (80/443 open?)
|
||||
|
||||
### Issue: Login succeeds but redirect fails
|
||||
**Cause:** CORS or redirect URI mismatch.
|
||||
|
||||
**Fix:**
|
||||
1. Add domain to "Web Origins" in client settings
|
||||
2. Check redirect URI includes protocol (https://)
|
||||
3. Check for typos in domain name
|
||||
|
||||
### Issue: Can't login to Keycloak admin console
|
||||
**Cause:** Container not started or wrong password.
|
||||
|
||||
**Fix:**
|
||||
```bash
|
||||
# Check if running
|
||||
docker ps | grep keycloak
|
||||
|
||||
# Check logs
|
||||
docker logs keycloak --tail 50
|
||||
|
||||
# Restart
|
||||
cd ~/docker/keycloak
|
||||
docker compose restart
|
||||
|
||||
# Reset admin password (if needed)
|
||||
docker exec keycloak /opt/keycloak/bin/kcadm.sh config credentials \
|
||||
--server http://localhost:8080 \
|
||||
--realm master \
|
||||
--user admin \
|
||||
--password NEW_PASSWORD_HERE
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Important URLs
|
||||
|
||||
**Local:**
|
||||
```
|
||||
Admin Console: http://localhost:8180/admin
|
||||
Realm Endpoints: http://localhost:8180/realms/{realm-name}/.well-known/openid-configuration
|
||||
```
|
||||
|
||||
**Production:**
|
||||
```
|
||||
Admin Console: https://auth.yourdomain.com/admin
|
||||
Realm Endpoints: https://auth.yourdomain.com/realms/{realm-name}/.well-known/openid-configuration
|
||||
```
|
||||
|
||||
### OAuth URLs (for realm "homelab")
|
||||
|
||||
**Local:**
|
||||
```
|
||||
Authorization: http://localhost:8180/realms/homelab/protocol/openid-connect/auth
|
||||
Token: http://localhost:8180/realms/homelab/protocol/openid-connect/token
|
||||
User Info: http://localhost:8180/realms/homelab/protocol/openid-connect/userinfo
|
||||
Logout: http://localhost:8180/realms/homelab/protocol/openid-connect/logout
|
||||
```
|
||||
|
||||
**Production:**
|
||||
```
|
||||
Authorization: https://auth.yourdomain.com/realms/homelab/protocol/openid-connect/auth
|
||||
Token: https://auth.yourdomain.com/realms/homelab/protocol/openid-connect/token
|
||||
User Info: https://auth.yourdomain.com/realms/homelab/protocol/openid-connect/userinfo
|
||||
Logout: https://auth.yourdomain.com/realms/homelab/protocol/openid-connect/logout
|
||||
```
|
||||
|
||||
### Common Commands
|
||||
|
||||
```bash
|
||||
# Start Keycloak
|
||||
cd ~/docker/keycloak
|
||||
docker compose up -d
|
||||
|
||||
# Stop Keycloak
|
||||
docker compose down
|
||||
|
||||
# View logs
|
||||
docker logs keycloak -f
|
||||
|
||||
# Access shell
|
||||
docker exec -it keycloak bash
|
||||
|
||||
# Login to admin CLI
|
||||
docker exec keycloak /opt/keycloak/bin/kcadm.sh config credentials \
|
||||
--server http://localhost:8080 \
|
||||
--realm master \
|
||||
--user admin \
|
||||
--password YOUR_PASSWORD
|
||||
|
||||
# Export realm configuration (backup)
|
||||
docker exec keycloak /opt/keycloak/bin/kc.sh export \
|
||||
--dir /opt/keycloak/data/export \
|
||||
--realm homelab
|
||||
|
||||
# Copy export to host
|
||||
docker cp keycloak:/opt/keycloak/data/export ./backup/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**Keycloak provides:**
|
||||
- ✅ Single Sign-On for all your services
|
||||
- ✅ Centralized user management
|
||||
- ✅ OAuth2/OIDC authentication
|
||||
- ✅ Works with local and external services
|
||||
- ✅ Professional-grade security
|
||||
|
||||
**Automated setup does:**
|
||||
- ✅ Creates realm
|
||||
- ✅ Creates OAuth clients
|
||||
- ✅ Creates initial user
|
||||
- ✅ Saves all credentials
|
||||
- ✅ Ready to use immediately
|
||||
|
||||
**Manual setup allows:**
|
||||
- ✅ Full control over configuration
|
||||
- ✅ Multiple realms (family, work, etc.)
|
||||
- ✅ Custom client settings
|
||||
- ✅ Advanced features (LDAP, MFA, etc.)
|
||||
|
||||
**For external services:**
|
||||
- ✅ Keycloak must be publicly accessible
|
||||
- ✅ Use Caddy with HTTPS
|
||||
- ✅ Configure proper redirect URIs
|
||||
- ✅ Test OAuth flow before production
|
||||
|
||||
For questions or issues, check the Keycloak documentation: https://www.keycloak.org/documentation
|
||||
+131
-11
@@ -3162,8 +3162,63 @@ KC_COMPOSE
|
||||
# Get realm name
|
||||
prompt_text " Realm name (e.g., homelab, services):" "homelab" KC_REALM
|
||||
|
||||
# Get domain for redirect URIs
|
||||
prompt_text " Your domain (for OAuth callbacks, e.g., example.com):" "localhost" KC_DOMAIN
|
||||
# Get domain configuration for redirect URIs
|
||||
echo ""
|
||||
echo " ──────────────────────────────────────────────────────────────"
|
||||
echo " DOMAIN CONFIGURATION"
|
||||
echo " ──────────────────────────────────────────────────────────────"
|
||||
echo ""
|
||||
echo " Keycloak needs to know where your services are hosted."
|
||||
echo ""
|
||||
echo " Options:"
|
||||
echo " 1. Local only (http://localhost:PORT)"
|
||||
echo " 2. Public domain (https://yourdomain.com)"
|
||||
echo " 3. Both local and public"
|
||||
echo ""
|
||||
prompt_text " Enter your setup (1/2/3):" "1" KC_SETUP_TYPE
|
||||
|
||||
KC_DOMAIN="localhost"
|
||||
KC_PUBLIC_DOMAIN=""
|
||||
KC_EXTERNAL_SERVICE=""
|
||||
|
||||
if [ "$KC_SETUP_TYPE" = "2" ] || [ "$KC_SETUP_TYPE" = "3" ]; then
|
||||
echo ""
|
||||
prompt_text " Your public domain (e.g., example.com):" "" KC_PUBLIC_DOMAIN
|
||||
|
||||
echo ""
|
||||
echo " ⚠ IMPORTANT: For Keycloak to work with external services,"
|
||||
echo " it MUST be accessible at https://auth.$KC_PUBLIC_DOMAIN"
|
||||
echo ""
|
||||
echo " This requires:"
|
||||
echo " ✓ DNS A record: auth.$KC_PUBLIC_DOMAIN → Your Server IP"
|
||||
echo " ✓ Caddy reverse proxy configured"
|
||||
echo " ✓ Ports 80/443 open in firewall"
|
||||
echo ""
|
||||
prompt_yn " Is Keycloak accessible at https://auth.$KC_PUBLIC_DOMAIN? (y/n):" "n" KC_DOMAIN_READY
|
||||
|
||||
if [ "$KC_DOMAIN_READY" != "y" ] && [ "$KC_DOMAIN_READY" != "Y" ]; then
|
||||
echo ""
|
||||
echo " ⚠ WARNING: Keycloak won't work with external services until"
|
||||
echo " you configure Caddy and DNS. See KEYCLOAK-SETUP-GUIDE.md"
|
||||
echo ""
|
||||
echo " You can still proceed and configure Caddy later."
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Ask about external services (like Pikapod)
|
||||
echo ""
|
||||
prompt_yn " Are you using external hosted services (e.g., Pikapod)? (y/n):" "n" KC_HAS_EXTERNAL
|
||||
|
||||
if [ "$KC_HAS_EXTERNAL" = "y" ] || [ "$KC_HAS_EXTERNAL" = "Y" ]; then
|
||||
echo ""
|
||||
echo " Enter the URL of your external service (e.g., https://actualbudget-abc.pikapod.net)"
|
||||
prompt_text " External service URL:" "" KC_EXTERNAL_SERVICE
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$KC_SETUP_TYPE" = "1" ] || [ "$KC_SETUP_TYPE" = "3" ]; then
|
||||
KC_DOMAIN="localhost"
|
||||
fi
|
||||
|
||||
# Wait for Keycloak to be fully ready (can take 30-60 seconds)
|
||||
echo ""
|
||||
@@ -3214,6 +3269,33 @@ KC_COMPOSE
|
||||
echo " Creating OAuth2 client for ActualBudget..."
|
||||
AB_CLIENT_SECRET=$(openssl rand -hex 32)
|
||||
|
||||
# Build redirect URIs based on configuration
|
||||
AB_REDIRECT_URIS='["http://localhost:5006/*","http://localhost:5006/callback"'
|
||||
|
||||
if [ -n "$KC_PUBLIC_DOMAIN" ]; then
|
||||
AB_REDIRECT_URIS="$AB_REDIRECT_URIS"',"https://budget.'$KC_PUBLIC_DOMAIN'/*","https://budget.'$KC_PUBLIC_DOMAIN'/callback"'
|
||||
AB_REDIRECT_URIS="$AB_REDIRECT_URIS"',"https://'$KC_PUBLIC_DOMAIN':5006/*","https://'$KC_PUBLIC_DOMAIN':5006/callback"'
|
||||
fi
|
||||
|
||||
if [ -n "$KC_EXTERNAL_SERVICE" ]; then
|
||||
AB_REDIRECT_URIS="$AB_REDIRECT_URIS"',"'$KC_EXTERNAL_SERVICE'/*","'$KC_EXTERNAL_SERVICE'/callback"'
|
||||
fi
|
||||
|
||||
AB_REDIRECT_URIS="$AB_REDIRECT_URIS"']'
|
||||
|
||||
# Build web origins
|
||||
AB_WEB_ORIGINS='["http://localhost:5006"'
|
||||
|
||||
if [ -n "$KC_PUBLIC_DOMAIN" ]; then
|
||||
AB_WEB_ORIGINS="$AB_WEB_ORIGINS"',"https://budget.'$KC_PUBLIC_DOMAIN'","https://'$KC_PUBLIC_DOMAIN':5006"'
|
||||
fi
|
||||
|
||||
if [ -n "$KC_EXTERNAL_SERVICE" ]; then
|
||||
AB_WEB_ORIGINS="$AB_WEB_ORIGINS"',"'$KC_EXTERNAL_SERVICE'"'
|
||||
fi
|
||||
|
||||
AB_WEB_ORIGINS="$AB_WEB_ORIGINS"']'
|
||||
|
||||
docker exec keycloak /opt/keycloak/bin/kcadm.sh create clients -r "$KC_REALM" \
|
||||
-s clientId=actualbudget \
|
||||
-s name="ActualBudget" \
|
||||
@@ -3225,8 +3307,8 @@ KC_COMPOSE
|
||||
-s standardFlowEnabled=true \
|
||||
-s directAccessGrantsEnabled=true \
|
||||
-s serviceAccountsEnabled=false \
|
||||
-s 'redirectUris=["http://localhost:5006/*","http://'$KC_DOMAIN':5006/*","https://'$KC_DOMAIN'/*","https://budget.'$KC_DOMAIN'/*"]' \
|
||||
-s 'webOrigins=["http://localhost:5006","http://'$KC_DOMAIN':5006","https://'$KC_DOMAIN'","https://budget.'$KC_DOMAIN'"]' \
|
||||
-s "redirectUris=$AB_REDIRECT_URIS" \
|
||||
-s "webOrigins=$AB_WEB_ORIGINS" \
|
||||
-s protocol=openid-connect > /dev/null 2>&1
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
@@ -3235,7 +3317,12 @@ KC_COMPOSE
|
||||
echo " Client Secret: $AB_CLIENT_SECRET"
|
||||
echo ""
|
||||
|
||||
# Save to file
|
||||
# Save to file with appropriate URLs
|
||||
KC_AUTH_URL="http://localhost:8180"
|
||||
if [ -n "$KC_PUBLIC_DOMAIN" ]; then
|
||||
KC_AUTH_URL="https://auth.$KC_PUBLIC_DOMAIN"
|
||||
fi
|
||||
|
||||
cat > "$KC_DIR/actualbudget-oauth.txt" << EOF
|
||||
ActualBudget OAuth2 Configuration
|
||||
==================================
|
||||
@@ -3243,18 +3330,51 @@ ActualBudget OAuth2 Configuration
|
||||
Client ID: actualbudget
|
||||
Client Secret: $AB_CLIENT_SECRET
|
||||
|
||||
LOCAL DEVELOPMENT:
|
||||
Authorization URL: http://localhost:8180/realms/$KC_REALM/protocol/openid-connect/auth
|
||||
Token URL: http://localhost:8180/realms/$KC_REALM/protocol/openid-connect/token
|
||||
User Info URL: http://localhost:8180/realms/$KC_REALM/protocol/openid-connect/userinfo
|
||||
EOF
|
||||
|
||||
For production (with Caddy):
|
||||
Authorization URL: https://auth.$KC_DOMAIN/realms/$KC_REALM/protocol/openid-connect/auth
|
||||
Token URL: https://auth.$KC_DOMAIN/realms/$KC_REALM/protocol/openid-connect/token
|
||||
User Info URL: https://auth.$KC_DOMAIN/realms/$KC_REALM/protocol/openid-connect/userinfo
|
||||
if [ -n "$KC_PUBLIC_DOMAIN" ]; then
|
||||
cat >> "$KC_DIR/actualbudget-oauth.txt" << EOF
|
||||
|
||||
PRODUCTION (with Caddy at https://auth.$KC_PUBLIC_DOMAIN):
|
||||
Authorization URL: https://auth.$KC_PUBLIC_DOMAIN/realms/$KC_REALM/protocol/openid-connect/auth
|
||||
Token URL: https://auth.$KC_PUBLIC_DOMAIN/realms/$KC_REALM/protocol/openid-connect/token
|
||||
User Info URL: https://auth.$KC_PUBLIC_DOMAIN/realms/$KC_REALM/protocol/openid-connect/userinfo
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [ -n "$KC_EXTERNAL_SERVICE" ]; then
|
||||
cat >> "$KC_DIR/actualbudget-oauth.txt" << EOF
|
||||
|
||||
EXTERNAL SERVICE ($KC_EXTERNAL_SERVICE):
|
||||
- Use PRODUCTION URLs above
|
||||
- Keycloak MUST be accessible at: https://auth.$KC_PUBLIC_DOMAIN
|
||||
- Redirect URI configured: $KC_EXTERNAL_SERVICE/*
|
||||
EOF
|
||||
fi
|
||||
|
||||
cat >> "$KC_DIR/actualbudget-oauth.txt" << EOF
|
||||
|
||||
Redirect URIs configured:
|
||||
- http://localhost:5006/*
|
||||
- https://budget.$KC_DOMAIN/*
|
||||
- http://localhost:5006/* (local)
|
||||
EOF
|
||||
|
||||
if [ -n "$KC_PUBLIC_DOMAIN" ]; then
|
||||
cat >> "$KC_DIR/actualbudget-oauth.txt" << EOF
|
||||
- https://budget.$KC_PUBLIC_DOMAIN/* (self-hosted)
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [ -n "$KC_EXTERNAL_SERVICE" ]; then
|
||||
cat >> "$KC_DIR/actualbudget-oauth.txt" << EOF
|
||||
- $KC_EXTERNAL_SERVICE/* (external)
|
||||
EOF
|
||||
fi
|
||||
|
||||
cat >> "$KC_DIR/actualbudget-oauth.txt" << EOF
|
||||
|
||||
To configure ActualBudget:
|
||||
1. Go to ActualBudget settings
|
||||
|
||||
Reference in New Issue
Block a user