Replaces the minimal project description with a comprehensive README including a component overview table, quick start instructions, common Ansible operations, and links to detailed documentation. Aligns with Red Panda Approval™ standards.
542 lines
16 KiB
Markdown
542 lines
16 KiB
Markdown
# Casdoor SSO Identity Provider
|
|
|
|
Casdoor provides Single Sign-On (SSO) authentication for Agathos services. This document covers the design decisions, architecture, and deployment procedures.
|
|
|
|
## Design Philosophy
|
|
|
|
### Security Isolation
|
|
|
|
Casdoor handles identity and authentication - the most security-sensitive data in any system. For this reason, Casdoor uses a **dedicated PostgreSQL instance** on Titania rather than sharing the PostgreSQL server on Portia with other applications.
|
|
|
|
This isolation provides:
|
|
- **Data separation**: Authentication data is physically separated from application data
|
|
- **Access control**: The `casdoor` database user only has access to the `casdoor` database
|
|
- **Blast radius reduction**: A compromise of the shared database on Portia doesn't expose identity data
|
|
- **Production alignment**: Dev/UAT/Prod environments use the same architecture
|
|
|
|
### Native PostgreSQL with Docker Casdoor
|
|
|
|
The architecture splits cleanly:
|
|
|
|
```
|
|
┌──────────────────────────────────────────────────────────────┐
|
|
│ titania.incus │
|
|
│ │
|
|
│ ┌────────────────────────────────────────────────────────┐ │
|
|
│ │ Native PostgreSQL 17 (systemd) │ │
|
|
│ │ - SSL enabled for external connections │ │
|
|
│ │ - Local connections without SSL │ │
|
|
│ │ - Managed like any standard PostgreSQL install │ │
|
|
│ │ - Port 5432 │ │
|
|
│ └────────────────────────────────────────────────────────┘ │
|
|
│ ▲ │
|
|
│ │ localhost:5432 │
|
|
│ │ sslmode=disable │
|
|
│ │ │
|
|
│ ┌────────┴───────────────────────────────────────────────┐ │
|
|
│ │ Casdoor Docker Container (network_mode: host) │ │
|
|
│ │ - Runs as casdoor:casdoor user │ │
|
|
│ │ - Only has access to its database │ │
|
|
│ │ - Cannot touch PostgreSQL server config │ │
|
|
│ │ - Port 22081 (via HAProxy) │ │
|
|
│ └────────────────────────────────────────────────────────┘ │
|
|
│ │
|
|
└──────────────────────────────────────────────────────────────┘
|
|
│
|
|
│ External: SSL required
|
|
│ sslmode=verify-ca
|
|
▼
|
|
┌─────────────┐
|
|
│ PGadmin │
|
|
│ on Portia │
|
|
└─────────────┘
|
|
```
|
|
|
|
### Why Not Docker for PostgreSQL?
|
|
|
|
Docker makes PostgreSQL permission management unnecessarily complex:
|
|
- UID/GID mapping between host and container
|
|
- Volume permission issues
|
|
- SSL certificate ownership problems
|
|
- More difficult backups and maintenance
|
|
|
|
Native PostgreSQL is:
|
|
- Easier to manage (standard Linux administration)
|
|
- Better integrated with systemd
|
|
- Simpler backup procedures
|
|
- Well-documented and understood
|
|
|
|
### SSL Strategy
|
|
|
|
PostgreSQL connections follow a **split SSL policy**:
|
|
|
|
| Connection Source | SSL Requirement | Rationale |
|
|
|-------------------|-----------------|-----------|
|
|
| Casdoor (localhost) | `sslmode=disable` | Same host, trusted |
|
|
| PGadmin (Portia) | `sslmode=verify-ca` | External network, requires encryption |
|
|
| Other external | `hostssl` required | Enforced by pg_hba.conf |
|
|
|
|
This is controlled by `pg_hba.conf`:
|
|
```
|
|
# Local connections (Unix socket)
|
|
local all all peer
|
|
|
|
# Localhost connections (no SSL required)
|
|
host all all 127.0.0.1/32 md5
|
|
|
|
# External connections (SSL required)
|
|
hostssl all all 0.0.0.0/0 md5
|
|
```
|
|
|
|
### System User Pattern
|
|
|
|
The Casdoor service user is created without hardcoded UID/GID:
|
|
|
|
```yaml
|
|
- name: Create casdoor user
|
|
ansible.builtin.user:
|
|
name: "{{ casdoor_user }}"
|
|
system: true # System account, UID assigned by OS
|
|
```
|
|
|
|
The playbook queries the assigned UID/GID at runtime for Docker container user mapping.
|
|
|
|
## Architecture
|
|
|
|
### Components
|
|
|
|
| Component | Location | Purpose |
|
|
|-----------|----------|---------|
|
|
| PostgreSQL 17 | Native on Titania | Dedicated identity database |
|
|
| Casdoor | Docker on Titania | SSO identity provider |
|
|
| HAProxy | Titania | TLS termination, routing |
|
|
| Alloy | Titania | Syslog collection |
|
|
|
|
### Deployment Order
|
|
|
|
```
|
|
1. postgresql_ssl/deploy.yml → Install PostgreSQL, SSL, create casdoor DB
|
|
2. casdoor/deploy.yml → Deploy Casdoor container
|
|
3. pgadmin/deploy.yml → Distribute SSL cert to PGadmin (optional)
|
|
```
|
|
|
|
### Network Ports
|
|
|
|
| Port | Service | Access |
|
|
|------|---------|--------|
|
|
| 22081 | Casdoor HTTP | Via HAProxy (network_mode: host) |
|
|
| 5432 | PostgreSQL | SSL for external, plain for localhost |
|
|
| 51401 | Syslog | Local only (Alloy) |
|
|
|
|
### Data Persistence
|
|
|
|
PostgreSQL data (native install):
|
|
```
|
|
/var/lib/postgresql/17/main/ # Database files
|
|
/etc/postgresql/17/main/ # Configuration
|
|
/etc/postgresql/17/main/ssl/ # SSL certificates
|
|
```
|
|
|
|
Casdoor configuration:
|
|
```
|
|
/srv/casdoor/
|
|
├── conf/
|
|
│ └── app.conf # Casdoor configuration
|
|
└── docker-compose.yml # Service definition
|
|
```
|
|
|
|
## Prerequisites
|
|
|
|
### 1. Terraform (S3 Buckets)
|
|
|
|
Casdoor can use S3-compatible storage for avatars and attachments:
|
|
|
|
```bash
|
|
cd terraform
|
|
terraform apply
|
|
```
|
|
|
|
### 2. Ansible Vault Secrets
|
|
|
|
Add to `ansible/inventory/group_vars/all/vault.yml`:
|
|
|
|
```yaml
|
|
# PostgreSQL SSL postgres user password (for Titania's dedicated PostgreSQL)
|
|
vault_postgresql_ssl_postgres_password: "secure-postgres-password"
|
|
|
|
# Casdoor database password
|
|
vault_casdoor_db_password: "secure-db-password"
|
|
|
|
# Casdoor application secrets
|
|
vault_casdoor_auth_state: "random-32-char-string"
|
|
vault_casdoor_app_client_secret: "generated-client-secret"
|
|
|
|
# Casdoor initial user passwords (changed after first login)
|
|
vault_casdoor_admin_password: "initial-admin-password"
|
|
vault_casdoor_hostmaster_password: "initial-hostmaster-password"
|
|
|
|
# Optional (for RADIUS protocol)
|
|
vault_casdoor_radius_secret: "radius-secret"
|
|
```
|
|
|
|
Generate secrets:
|
|
```bash
|
|
# Database password
|
|
openssl rand -base64 24
|
|
|
|
# Auth state
|
|
openssl rand -hex 16
|
|
```
|
|
|
|
### 3. Alloy Log Collection
|
|
|
|
Ensure Alloy is deployed to receive syslog:
|
|
|
|
```bash
|
|
ansible-playbook alloy/deploy.yml --limit titania.incus
|
|
```
|
|
|
|
## Deployment
|
|
|
|
### Fresh Installation
|
|
|
|
```bash
|
|
cd ansible
|
|
|
|
# 1. Deploy PostgreSQL with SSL
|
|
ansible-playbook postgresql_ssl/deploy.yml
|
|
|
|
# 2. Deploy Casdoor
|
|
ansible-playbook casdoor/deploy.yml
|
|
|
|
# 3. Update PGadmin with SSL certificate (optional)
|
|
ansible-playbook pgadmin/deploy.yml
|
|
```
|
|
|
|
### Verify Deployment
|
|
|
|
```bash
|
|
# Check PostgreSQL status
|
|
ssh titania.incus "sudo systemctl status postgresql"
|
|
|
|
# Check Casdoor container
|
|
ssh titania.incus "cd /srv/casdoor && docker compose ps"
|
|
|
|
# Check logs
|
|
ssh titania.incus "cd /srv/casdoor && docker compose logs --tail=50"
|
|
|
|
# Test health endpoint
|
|
curl -s http://titania.incus:22081/api/health
|
|
```
|
|
|
|
### Redeployment
|
|
|
|
To redeploy Casdoor only (database preserved):
|
|
|
|
```bash
|
|
ansible-playbook casdoor/remove.yml
|
|
ansible-playbook casdoor/deploy.yml
|
|
```
|
|
|
|
To completely reset (including database):
|
|
```bash
|
|
ansible-playbook casdoor/remove.yml
|
|
ssh titania.incus "sudo -u postgres dropdb casdoor"
|
|
ssh titania.incus "sudo -u postgres dropuser casdoor"
|
|
ansible-playbook postgresql_ssl/deploy.yml
|
|
ansible-playbook casdoor/deploy.yml
|
|
```
|
|
|
|
## Configuration Reference
|
|
|
|
### Host Variables
|
|
|
|
Located in `ansible/inventory/host_vars/titania.incus.yml`:
|
|
|
|
```yaml
|
|
# PostgreSQL SSL (dedicated identity database)
|
|
postgresql_ssl_postgres_password: "{{ vault_postgresql_ssl_postgres_password }}"
|
|
postgresql_ssl_port: 5432
|
|
postgresql_ssl_cert_path: /etc/postgresql/17/main/ssl/server.crt
|
|
|
|
# Casdoor service account (system-assigned UID/GID)
|
|
casdoor_user: casdoor
|
|
casdoor_group: casdoor
|
|
casdoor_directory: /srv/casdoor
|
|
|
|
# Web
|
|
casdoor_port: 22081
|
|
casdoor_runmode: dev # or 'prod'
|
|
|
|
# Database (connects to localhost PostgreSQL)
|
|
casdoor_db_port: 5432
|
|
casdoor_db_name: casdoor
|
|
casdoor_db_user: casdoor
|
|
casdoor_db_password: "{{ vault_casdoor_db_password }}"
|
|
casdoor_db_sslmode: disable # Localhost, no SSL needed
|
|
|
|
# Logging
|
|
casdoor_syslog_port: 51401
|
|
```
|
|
|
|
### SSL Certificate
|
|
|
|
The self-signed certificate is generated automatically with:
|
|
- **Common Name**: `titania.incus`
|
|
- **Subject Alt Names**: `titania.incus`, `localhost`, `127.0.0.1`
|
|
- **Validity**: 10 years (`+3650d`)
|
|
- **Key Size**: 4096 bits
|
|
- **Location**: `/etc/postgresql/17/main/ssl/`
|
|
|
|
To regenerate certificates:
|
|
```bash
|
|
ssh titania.incus "sudo rm -rf /etc/postgresql/17/main/ssl/*"
|
|
ansible-playbook postgresql_ssl/deploy.yml
|
|
ansible-playbook pgadmin/deploy.yml # Update cert on Portia
|
|
```
|
|
|
|
## PGadmin Connection
|
|
|
|
To connect from PGadmin on Portia:
|
|
|
|
1. Navigate to https://pgadmin.ouranos.helu.ca
|
|
2. Add Server:
|
|
- **General tab**
|
|
- Name: `Titania PostgreSQL (Casdoor)`
|
|
- **Connection tab**
|
|
- Host: `titania.incus`
|
|
- Port: `5432`
|
|
- Database: `casdoor`
|
|
- Username: `casdoor`
|
|
- Password: *(from vault)*
|
|
- **SSL tab**
|
|
- SSL Mode: `Verify-CA`
|
|
- Root certificate: `/var/lib/pgadmin/certs/titania-postgres-ca.crt`
|
|
|
|
The certificate is automatically distributed by `ansible-playbook pgadmin/deploy.yml`.
|
|
|
|
## Application Branding & CSS Customization
|
|
|
|
Casdoor allows extensive customization of login/signup pages through CSS and HTML fields in the **Application** settings.
|
|
|
|
### Available CSS/HTML Fields
|
|
|
|
| Field | Purpose | Where Applied |
|
|
|-------|---------|---------------|
|
|
| `formCss` | Custom CSS for desktop login forms | Login, signup, consent pages |
|
|
| `formCssMobile` | Mobile-specific CSS overrides | Mobile views |
|
|
| `headerHtml` | Custom HTML in page header | All auth pages (can inject `<style>` tags) |
|
|
| `footerHtml` | Custom footer HTML | Replaces "Powered by Casdoor" |
|
|
| `formSideHtml` | HTML beside the form | Side panel content |
|
|
| `formBackgroundUrl` | Background image URL | Full-page background |
|
|
| `formBackgroundUrlMobile` | Mobile background image | Mobile background |
|
|
| `signupHtml` | Custom HTML for signup page | Signup page only |
|
|
| `signinHtml` | Custom HTML for signin page | Signin page only |
|
|
|
|
### Configuration via init_data.json
|
|
|
|
Application branding is configured in `ansible/casdoor/init_data.json.j2`:
|
|
|
|
```json
|
|
{
|
|
"applications": [
|
|
{
|
|
"name": "app-heluca",
|
|
"formCss": "<style>/* Your CSS here */</style>",
|
|
"footerHtml": "<div style=\"text-align:center;\">Powered by Helu.ca</div>",
|
|
"headerHtml": "<style>/* Additional CSS via style tag */</style>",
|
|
"formBackgroundUrl": "https://example.com/bg.jpg"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Example: Custom Theme CSS
|
|
|
|
The `formCss` field contains CSS to customize the Ant Design components:
|
|
|
|
```css
|
|
<style>
|
|
/* Login panel styling */
|
|
.login-panel {
|
|
background-color: #ffffff;
|
|
border-radius: 10px;
|
|
box-shadow: 0 0 30px 20px rgba(255,164,21,0.12);
|
|
}
|
|
|
|
/* Primary button colors */
|
|
.ant-btn-primary {
|
|
background-color: #4b96ff !important;
|
|
border-color: #4b96ff !important;
|
|
}
|
|
.ant-btn-primary:hover {
|
|
background-color: #58c0ff !important;
|
|
border-color: #58c0ff !important;
|
|
}
|
|
|
|
/* Link colors */
|
|
a { color: #ffa415; }
|
|
a:hover { color: #ffc219; }
|
|
|
|
/* Input focus states */
|
|
.ant-input:focus, .ant-input-focused {
|
|
border-color: #4b96ff !important;
|
|
box-shadow: 0 0 0 2px rgba(75,150,255,0.2) !important;
|
|
}
|
|
|
|
/* Checkbox styling */
|
|
.ant-checkbox-checked .ant-checkbox-inner {
|
|
background-color: #4b96ff !important;
|
|
border-color: #4b96ff !important;
|
|
}
|
|
</style>
|
|
```
|
|
|
|
### Example: Custom Footer
|
|
|
|
Replace the default "Powered by Casdoor" footer:
|
|
|
|
```html
|
|
<div style="text-align:center;padding:10px;color:#666;">
|
|
<a href="https://helu.ca" style="color:#4b96ff;text-decoration:none;">
|
|
Powered by Helu.ca
|
|
</a>
|
|
</div>
|
|
```
|
|
|
|
### Organization-Level Theme
|
|
|
|
Organization settings also affect theming. Configure in the **Organization** settings:
|
|
|
|
| Setting | Purpose |
|
|
|---------|---------|
|
|
| `themeData.colorPrimary` | Primary color (Ant Design) |
|
|
| `themeData.borderRadius` | Border radius for components |
|
|
| `themeData.isCompact` | Compact mode toggle |
|
|
| `logo` | Organization logo |
|
|
| `favicon` | Browser favicon |
|
|
| `websiteUrl` | Organization website |
|
|
|
|
### Updating Existing Applications
|
|
|
|
Changes to `init_data.json` only apply during **initial Casdoor setup**. For existing deployments:
|
|
|
|
1. **Via Admin UI**: Applications → Edit → Update CSS/HTML fields
|
|
2. **Via API**: Use Casdoor's REST API to update application settings
|
|
3. **Database reset**: Redeploy with `initDataNewOnly = false` (overwrites existing data)
|
|
|
|
### CSS Class Reference
|
|
|
|
Common CSS classes for targeting Casdoor UI elements:
|
|
|
|
| Class | Element |
|
|
|-------|---------|
|
|
| `.login-panel` | Main login form container |
|
|
| `.login-logo-box` | Logo container |
|
|
| `.login-username` | Username input wrapper |
|
|
| `.login-password` | Password input wrapper |
|
|
| `.login-button-box` | Submit button container |
|
|
| `.login-forget-password` | Forgot password link |
|
|
| `.login-signup-link` | Signup link |
|
|
| `.login-languages` | Language selector |
|
|
| `.back-button` | Back button |
|
|
| `.provider-img` | OAuth provider icons |
|
|
| `.signin-methods` | Sign-in method tabs |
|
|
| `.verification-code` | Verification code input |
|
|
| `.login-agreement` | Terms agreement checkbox |
|
|
|
|
## Initial Setup
|
|
|
|
After deployment, access Casdoor at https://id.ouranos.helu.ca:
|
|
|
|
1. **Login** with default credentials: `admin` / `123`
|
|
2. **Change admin password immediately**
|
|
3. **Create organization** for your domain
|
|
4. **Create applications** for services that need SSO:
|
|
- SearXNG (via OAuth2-Proxy)
|
|
- Grafana
|
|
- Other internal services
|
|
|
|
### OAuth2 Application Setup
|
|
|
|
For each service:
|
|
1. Applications → Add
|
|
2. Configure OAuth2 settings:
|
|
- Redirect URI: `https://service.ouranos.helu.ca/oauth2/callback`
|
|
- Grant types: Authorization Code
|
|
3. Note the Client ID and Client Secret for service configuration
|
|
|
|
## Troubleshooting
|
|
|
|
### PostgreSQL Issues
|
|
|
|
```bash
|
|
# Check PostgreSQL status
|
|
ssh titania.incus "sudo systemctl status postgresql"
|
|
|
|
# View PostgreSQL logs
|
|
ssh titania.incus "sudo journalctl -u postgresql -f"
|
|
|
|
# Check SSL configuration
|
|
ssh titania.incus "sudo -u postgres psql -c 'SHOW ssl;'"
|
|
ssh titania.incus "sudo -u postgres psql -c 'SHOW ssl_cert_file;'"
|
|
|
|
# Test SSL connection externally
|
|
openssl s_client -connect titania.incus:5432 -starttls postgres
|
|
```
|
|
|
|
### Casdoor Container Issues
|
|
|
|
```bash
|
|
# View container status
|
|
ssh titania.incus "cd /srv/casdoor && docker compose ps"
|
|
|
|
# View logs
|
|
ssh titania.incus "cd /srv/casdoor && docker compose logs casdoor"
|
|
|
|
# Restart
|
|
ssh titania.incus "cd /srv/casdoor && docker compose restart"
|
|
```
|
|
|
|
### Database Connection
|
|
|
|
```bash
|
|
# Connect as postgres admin
|
|
ssh titania.incus "sudo -u postgres psql"
|
|
|
|
# Connect as casdoor user
|
|
ssh titania.incus "sudo -u postgres psql -U casdoor -d casdoor -h localhost"
|
|
|
|
# List databases
|
|
ssh titania.incus "sudo -u postgres psql -c '\l'"
|
|
|
|
# List users
|
|
ssh titania.incus "sudo -u postgres psql -c '\du'"
|
|
```
|
|
|
|
### Health Check
|
|
|
|
```bash
|
|
# Casdoor health
|
|
curl -s http://titania.incus:22081/api/health | jq
|
|
|
|
# PostgreSQL accepting connections
|
|
ssh titania.incus "pg_isready -h localhost"
|
|
```
|
|
|
|
## Security Considerations
|
|
|
|
1. **Change default admin password** immediately after deployment
|
|
2. **Rotate database passwords** periodically (update vault, redeploy)
|
|
3. **Monitor authentication logs** in Grafana (via Alloy/Loki)
|
|
4. **SSL certificates** have 10-year validity, regenerate if compromised
|
|
5. **Backup PostgreSQL data** regularly - contains all identity data:
|
|
```bash
|
|
ssh titania.incus "sudo -u postgres pg_dump casdoor > casdoor_backup.sql"
|
|
```
|
|
|
|
## Related Documentation
|
|
|
|
- [Ansible Practices](ansible.md) - Playbook and variable patterns
|
|
- [Terraform Practices](terraform.md) - S3 bucket provisioning
|
|
- [OAuth2-Proxy](services/oauth2_proxy.md) - Protecting services with Casdoor SSO |