mirror of
https://github.com/swissmakers/fail2ban-ui.git
synced 2026-04-11 13:47:05 +02:00
Adding development-containers that we used to develop fail2ban-ui, can also be used for easy and fast stack-testing
This commit is contained in:
50
development/README.md
Normal file
50
development/README.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Development Environment
|
||||
|
||||
This directory contains Docker Compose configurations for local development and testing of Fail2ban UI.
|
||||
|
||||
## Available Development Setups
|
||||
|
||||
### 1. OIDC Authentication Testing (`oidc/`)
|
||||
|
||||
Complete OIDC authentication setup with Keycloak and Pocket-ID for testing authentication flows.
|
||||
|
||||
**See:** [oidc/README.md](./oidc/README.md)
|
||||
|
||||
### 2. SSH and Local Fail2ban Testing (`ssh_and_local/`)
|
||||
|
||||
Setup for testing Fail2ban UI with:
|
||||
- Local Fail2ban instance (container)
|
||||
- Remote Fail2ban instance via SSH (container)
|
||||
|
||||
**See:** [ssh_and_local/README.md](./ssh_and_local/README.md)
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. **Build the fail2ban-ui development image:**
|
||||
```bash
|
||||
podman build -t localhost/fail2ban-ui:dev .
|
||||
# or
|
||||
docker build -t localhost/fail2ban-ui:dev .
|
||||
```
|
||||
|
||||
2. **Choose a development setup:**
|
||||
- For OIDC testing: `cd oidc/`
|
||||
- For SSH/Local testing: `cd ssh_and_local/`
|
||||
|
||||
3. **Start the services:**
|
||||
```bash
|
||||
podman compose up -d
|
||||
# or
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
4. **Access the services:**
|
||||
- Fail2ban UI: `http://localhost:3080` (or configured port)
|
||||
- OIDC Provider (Pocket-ID): `http://localhost:3000` (if using OIDC setup)
|
||||
|
||||
## Notes
|
||||
|
||||
- All development containers use the `DEV_` prefix for easy identification
|
||||
- Data volumes are stored in subdirectories (e.g., `./config`, `./pocket-id-data`)
|
||||
- These setups are for **development only** - not for production use
|
||||
- Some containers require `privileged: true` or specific capabilities for full functionality
|
||||
25
development/oidc/.env.example
Normal file
25
development/oidc/.env.example
Normal file
@@ -0,0 +1,25 @@
|
||||
# Environment variables for keycloak OIDC development setup
|
||||
# Copy this file to .env and update with your server's IP address or hostname
|
||||
#
|
||||
# Usage:
|
||||
# cp .env.example .env
|
||||
# # Edit .env with your server details
|
||||
# podman compose up -d
|
||||
|
||||
# Your server's public URL for fail2ban-ui (used for redirect URIs)
|
||||
# This is the URL that browsers will use to access fail2ban-ui
|
||||
# Example for remote server: http://172.16.10.18:3080
|
||||
# Example for localhost: http://localhost:3080
|
||||
PUBLIC_FRONTEND_URL=http://172.16.10.18:3080
|
||||
|
||||
# Your server's public URL for Keycloak (usually same host, different port)
|
||||
# This is the URL that browsers will use to access Keycloak
|
||||
# Example for remote server: http://172.16.10.18:3000
|
||||
# Example for localhost: http://localhost:3000
|
||||
KEYCLOAK_URL=http://172.16.10.18:3000
|
||||
|
||||
# Keycloak's public URL (used for issuer in discovery document)
|
||||
# Should match KEYCLOAK_URL
|
||||
# Example for remote server: http://172.16.10.18:3000
|
||||
# Example for localhost: http://localhost:3000
|
||||
KEYCLOAK_PUBLIC_URL=http://172.16.10.18:3000
|
||||
12
development/oidc/.gitignore
vendored
Normal file
12
development/oidc/.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
config/
|
||||
f2b-run-local/
|
||||
fail2ban-config-local/
|
||||
keycloak-data/
|
||||
keycloak-db/
|
||||
pocket-id-data/
|
||||
authentik-media/
|
||||
authentik-db/
|
||||
authelia-config/
|
||||
authelia-db/
|
||||
authelia-redis/
|
||||
.env
|
||||
483
development/oidc/README.md
Normal file
483
development/oidc/README.md
Normal file
@@ -0,0 +1,483 @@
|
||||
# OIDC Authentication Development Setup
|
||||
|
||||
This setup provides a complete OIDC authentication testing environment with Keycloak, Pocket-ID, and Authentik.
|
||||
|
||||
## Available OIDC Providers
|
||||
|
||||
### 1. Keycloak (Primary - Default)
|
||||
- **Container:** `DEV_keycloak`
|
||||
- **Port:** `3000` (mapped from internal `8080`)
|
||||
- **Management Port:** `9000` (for health checks)
|
||||
- **URL:** `http://localhost:3000`
|
||||
- **Admin Console:** `http://localhost:3000`
|
||||
- **Purpose:** Enterprise-grade OIDC provider (recommended)
|
||||
- **Data:** Stored in `./keycloak-data/` and `./keycloak-db/`
|
||||
- **Database:** PostgreSQL (container: `DEV_keycloak-db`)
|
||||
|
||||
### 2. Pocket-ID (Alternative 1)
|
||||
- **Container:** `DEV_pocket-id` (commented out by default)
|
||||
- **Port:** `3005` (when enabled)
|
||||
- **URL:** `http://localhost:3005`
|
||||
- **Purpose:** Lightweight OIDC provider with passkey support
|
||||
- **Data:** Stored in `./pocket-id-data/`
|
||||
- **Note:** Uncomment in `container-compose.yml` to use
|
||||
|
||||
### 3. Authentik (Alternative 2)
|
||||
- **Containers:** `DEV_authentik-server`, `DEV_authentik-worker` (commented out by default)
|
||||
- **Ports:** `3007` (HTTP), `3008` (HTTPS) (when enabled)
|
||||
- **URL:** `http://localhost:3007`
|
||||
- **Purpose:** Full-featured identity provider with OIDC support
|
||||
- **Data:** Stored in `./authentik-media/` and `./authentik-db/`
|
||||
- **Note:** Requires migrations and initial setup. Uncomment in `container-compose.yml` to use
|
||||
|
||||
### 4. Fail2ban-UI
|
||||
- **Container:** `DEV_fail2ban-ui-oidc`
|
||||
- **Port:** `3080`
|
||||
- **URL:** `http://localhost:3080`
|
||||
- **Purpose:** Main application with OIDC authentication enabled
|
||||
- **OIDC Provider:** Keycloak (default, configurable)
|
||||
- **Network:** Uses host network mode to access Keycloak on localhost
|
||||
|
||||
## Quick Start
|
||||
|
||||
**✅ Automatic Setup:** The OIDC client is automatically configured for Keycloak! Just start the containers and everything should work.
|
||||
|
||||
**Note:** All services bind to `0.0.0.0` for easy access from any network interface.
|
||||
|
||||
### For Remote Server Access
|
||||
|
||||
**Default Configuration:** The setup defaults to `localhost` for local development. For remote server access, you need to create a `.env` file with your server's IP address or hostname.
|
||||
|
||||
**Option 1: Using .env file (Recommended)**
|
||||
|
||||
1. Copy the example file:
|
||||
```bash
|
||||
cd /opt/fail2ban-ui/development/oidc
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
2. Edit `.env` and update with your server's IP address or hostname:
|
||||
```bash
|
||||
# Example: If your server IP is 172.16.10.18
|
||||
PUBLIC_FRONTEND_URL=http://172.16.10.18:3080
|
||||
KEYCLOAK_URL=http://172.16.10.18:3000
|
||||
KEYCLOAK_PUBLIC_URL=http://172.16.10.18:3000
|
||||
```
|
||||
|
||||
3. Start containers (docker-compose/podman-compose will automatically load .env):
|
||||
```bash
|
||||
podman compose up -d
|
||||
```
|
||||
|
||||
**Option 2: Using environment variables**
|
||||
|
||||
```bash
|
||||
# Set your server's IP address or hostname
|
||||
export PUBLIC_FRONTEND_URL=http://YOUR_SERVER_IP:3080
|
||||
export KEYCLOAK_URL=http://YOUR_SERVER_IP:3000
|
||||
export KEYCLOAK_PUBLIC_URL=http://YOUR_SERVER_IP:3000
|
||||
|
||||
# Then start containers
|
||||
cd /opt/fail2ban-ui/development/oidc
|
||||
podman compose up -d
|
||||
```
|
||||
|
||||
**Important:**
|
||||
- Without setting these, redirect URIs will use `localhost` which won't work from remote browsers
|
||||
- After changing these values, you may need to recreate the Keycloak client:
|
||||
```bash
|
||||
podman compose down
|
||||
rm -rf config/keycloak-client-secret
|
||||
podman compose up -d
|
||||
```
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
### 1. Build the Fail2ban-UI Image
|
||||
|
||||
```bash
|
||||
cd /opt/fail2ban-ui
|
||||
podman build -t localhost/fail2ban-ui:dev .
|
||||
# or
|
||||
docker build -t localhost/fail2ban-ui:dev .
|
||||
```
|
||||
|
||||
### 2. Start the Services
|
||||
|
||||
```bash
|
||||
cd /opt/fail2ban-ui/development/oidc
|
||||
podman compose up -d
|
||||
# or
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
**Important Notes:**
|
||||
- **Keycloak startup time:** Keycloak takes 30-60 seconds to fully start. Be patient!
|
||||
- **Container status:** The fail2ban-ui container will wait for Keycloak to start
|
||||
- **If fail2ban-ui is stuck in "Created" status:**
|
||||
- Wait for Keycloak to show "healthy" status: `podman compose ps`
|
||||
- Or manually start fail2ban-ui: `podman start DEV_fail2ban-ui-oidc` (it will retry connecting)
|
||||
|
||||
### 3. Automatic Keycloak Client Configuration
|
||||
|
||||
**✅ Automatic:** The OIDC client is automatically created by the `keycloak-init` container. No manual configuration needed!
|
||||
|
||||
The `keycloak-init` container will:
|
||||
- Wait for Keycloak to be ready
|
||||
- Automatically create the `fail2ban-ui` OIDC client
|
||||
- Configure redirect URIs and web origins
|
||||
- Save the client secret to `/config/keycloak-client-secret`
|
||||
- Fail2ban-ui will automatically read the secret from this file
|
||||
|
||||
**If you see "Client not found" error:**
|
||||
|
||||
This means the `keycloak-init` container hasn't run yet or failed. To fix:
|
||||
|
||||
1. **Check if Keycloak is running:**
|
||||
```bash
|
||||
podman compose ps keycloak
|
||||
# Should show "healthy" status
|
||||
```
|
||||
|
||||
2. **Run keycloak-init manually:**
|
||||
```bash
|
||||
cd /opt/fail2ban-ui/development/oidc
|
||||
podman compose run --rm keycloak-init
|
||||
```
|
||||
|
||||
3. **Verify the client secret was created:**
|
||||
```bash
|
||||
ls -la config/keycloak-client-secret
|
||||
cat config/keycloak-client-secret
|
||||
```
|
||||
|
||||
4. **Restart fail2ban-ui:**
|
||||
```bash
|
||||
podman compose restart fail2ban-ui
|
||||
```
|
||||
|
||||
**Manual Configuration (Alternative):**
|
||||
|
||||
If automatic configuration fails, you can manually create the client:
|
||||
|
||||
**Manual Setup Steps (if needed):**
|
||||
|
||||
1. **Wait for Keycloak to Start:**
|
||||
- Check logs: `podman logs DEV_keycloak`
|
||||
- Wait for "Keycloak started" message (may take 30-60 seconds)
|
||||
- Check container status: `podman compose ps` - Keycloak should show "healthy" status
|
||||
|
||||
2. **Access Keycloak Admin Console:**
|
||||
- Open `http://localhost:3000` in your browser (or use your host's IP address)
|
||||
- Login with:
|
||||
- **Username:** `admin`
|
||||
- **Password:** `admin`
|
||||
|
||||
3. **Select Realm:**
|
||||
- The default configuration uses the `master` realm (already selected)
|
||||
- **Optional:** Create a custom realm for production use (see below)
|
||||
|
||||
4. **Create OIDC Client (if auto-configuration failed):**
|
||||
- In the left sidebar, click **Clients**
|
||||
- Click **Create client** button (top right)
|
||||
- **Client ID:** Enter `fail2ban-ui` (must match `OIDC_CLIENT_ID` in container-compose.yml)
|
||||
- **Client protocol:** Select `openid-connect`
|
||||
- Click **Next**
|
||||
|
||||
- **Client authentication:** Toggle **ON** (this makes it a confidential client)
|
||||
- **Authorization:** Leave OFF (unless you need it)
|
||||
- **Authentication flow:** Leave default settings
|
||||
- Click **Next**
|
||||
|
||||
- **Login settings:**
|
||||
- **Root URL:** Leave empty
|
||||
- **Home URL:** Leave empty
|
||||
- **Valid redirect URIs:** Add `http://localhost:3080/auth/callback` (must match exactly)
|
||||
- **Valid post logout redirect URIs:** Leave empty
|
||||
- **Web origins:** Add `http://localhost:3080` (for CORS)
|
||||
- Click **Save**
|
||||
|
||||
5. **Get Client Secret (REQUIRED):**
|
||||
- After saving, you'll be on the client settings page
|
||||
- Click the **Credentials** tab
|
||||
- Copy the **Client secret** value (click "Copy" or manually copy)
|
||||
- **Update `container-compose.yml`:**
|
||||
```bash
|
||||
# Edit the file
|
||||
nano /opt/fail2ban-ui/development/oidc/container-compose.yml
|
||||
# or
|
||||
vi /opt/fail2ban-ui/development/oidc/container-compose.yml
|
||||
```
|
||||
- Find the line: `- OIDC_CLIENT_SECRET=change-me-secret`
|
||||
- Replace `change-me-secret` with the actual secret you copied
|
||||
- Save the file
|
||||
|
||||
6. **Restart fail2ban-ui:**
|
||||
```bash
|
||||
podman compose restart fail2ban-ui
|
||||
```
|
||||
|
||||
7. **Create a Test User (Optional but recommended):**
|
||||
- In Keycloak admin console, click **Users** in the left sidebar
|
||||
- Click **Create new user** button (top right)
|
||||
- **Username:** `testuser` (or any username)
|
||||
- **Email:** `test@example.com` (optional)
|
||||
- **Email verified:** Toggle ON (optional)
|
||||
- Click **Create**
|
||||
- Go to the **Credentials** tab
|
||||
- Click **Set password**
|
||||
- Enter a password
|
||||
- **Temporary:** Toggle OFF (so user doesn't need to reset password on first login)
|
||||
- Click **Save**
|
||||
|
||||
**Now you can access fail2ban-ui:**
|
||||
- Open `http://localhost:3080` in your browser
|
||||
- You should be redirected to Keycloak login
|
||||
- Login with your test user credentials
|
||||
- After successful authentication, you'll be redirected back to fail2ban-ui
|
||||
|
||||
**Optional: Create Custom Realm (for production):**
|
||||
|
||||
If you want to use a custom realm instead of `master`:
|
||||
|
||||
1. In Keycloak admin console, click the realm dropdown (top left, shows "master")
|
||||
2. Click **Create Realm**
|
||||
3. **Realm name:** Enter `myrealm` (or any name)
|
||||
4. **Enabled:** Toggle ON
|
||||
5. Click **Create**
|
||||
6. **Update `container-compose.yml`:**
|
||||
```yaml
|
||||
- OIDC_ISSUER_URL=http://localhost:3000/realms/myrealm
|
||||
```
|
||||
7. **Restart fail2ban-ui:**
|
||||
```bash
|
||||
podman compose restart fail2ban-ui
|
||||
```
|
||||
8. **Create the OIDC client in the new realm** (follow steps 4-6 above)
|
||||
|
||||
### 4. Test Authentication
|
||||
|
||||
1. **Verify Fail2ban-UI Started Successfully:**
|
||||
- Check logs: `podman logs DEV_fail2ban-ui-oidc`
|
||||
- Look for: "OIDC authentication enabled" (should appear after Keycloak is ready)
|
||||
- If you see retry messages, wait a bit longer for Keycloak to fully start
|
||||
|
||||
2. **Access Fail2ban UI:**
|
||||
- Open `http://localhost:3080`
|
||||
- You should be redirected to Keycloak login
|
||||
|
||||
3. **Login:**
|
||||
- Use your Keycloak test user credentials
|
||||
- After successful authentication, you'll be redirected back to Fail2ban UI
|
||||
|
||||
4. **Verify Session:**
|
||||
- Check the header for your user information
|
||||
- Verify logout functionality
|
||||
|
||||
## Switching Between Providers
|
||||
|
||||
### Using Pocket-ID Instead of Keycloak
|
||||
|
||||
1. **Stop current services:**
|
||||
```bash
|
||||
podman compose down
|
||||
```
|
||||
|
||||
2. **Edit `container-compose.yml`:**
|
||||
- Comment out the `keycloak` and `postgres` services
|
||||
- Uncomment the `pocket-id` service
|
||||
- Update fail2ban-ui environment variables:
|
||||
```yaml
|
||||
- OIDC_PROVIDER=pocketid
|
||||
- OIDC_ISSUER_URL=http://localhost:3005
|
||||
- OIDC_LOGOUT_URL=http://localhost:3005/logout
|
||||
```
|
||||
- Change fail2ban-ui back to bridge network (remove `network_mode: host`)
|
||||
|
||||
3. **Start services:**
|
||||
```bash
|
||||
podman compose up -d
|
||||
```
|
||||
|
||||
4. **Configure Pocket-ID:**
|
||||
- Access `http://localhost:3005`
|
||||
- Create admin account
|
||||
- Create OIDC client with redirect URI: `http://localhost:3080/auth/callback`
|
||||
|
||||
### Using Authentik Instead of Keycloak
|
||||
|
||||
1. **Stop current services:**
|
||||
```bash
|
||||
podman compose down
|
||||
```
|
||||
|
||||
2. **Edit `container-compose.yml`:**
|
||||
- Comment out the `keycloak` and `postgres` services
|
||||
- Uncomment all `authentik-*` services
|
||||
- Update fail2ban-ui environment variables:
|
||||
```yaml
|
||||
- OIDC_PROVIDER=authentik
|
||||
- OIDC_ISSUER_URL=http://localhost:3007/application/o/fail2ban-ui/
|
||||
```
|
||||
- Change fail2ban-ui back to bridge network (remove `network_mode: host`)
|
||||
|
||||
3. **Start services:**
|
||||
```bash
|
||||
podman compose up -d
|
||||
```
|
||||
|
||||
4. **Run migrations and setup:**
|
||||
```bash
|
||||
podman compose run --rm authentik-server migrate
|
||||
```
|
||||
|
||||
5. **Access initial setup:**
|
||||
- Open `http://localhost:3007/if/flow/initial-setup/`
|
||||
- Create initial admin user
|
||||
|
||||
6. **Configure Authentik:**
|
||||
- Create OIDC Provider
|
||||
- Create Provider Application
|
||||
- Configure client ID and secret
|
||||
|
||||
## Configuration Options
|
||||
|
||||
### OIDC Environment Variables
|
||||
|
||||
Edit `container-compose.yml` to customize OIDC settings:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
- OIDC_ENABLED=true # Enable/disable OIDC
|
||||
- OIDC_PROVIDER=keycloak # Provider: keycloak, authentik, pocketid
|
||||
- OIDC_ISSUER_URL=http://localhost:3000/realms/master # Must match provider's discovery document
|
||||
- OIDC_CLIENT_ID=fail2ban-ui
|
||||
- OIDC_CLIENT_SECRET=your-secret
|
||||
- OIDC_REDIRECT_URL=http://localhost:3080/auth/callback # External URL for browser redirects
|
||||
- OIDC_SCOPES=openid,profile,email # Comma-separated scopes
|
||||
- OIDC_SESSION_MAX_AGE=7200 # Session timeout (seconds)
|
||||
- OIDC_USERNAME_CLAIM=preferred_username
|
||||
- OIDC_SKIP_VERIFY=true # Skip TLS verification (dev only)
|
||||
```
|
||||
|
||||
**Note:** `OIDC_ISSUER_URL` must match the issuer returned by the provider's discovery document. For Keycloak with `KC_HOSTNAME=localhost`, use `http://localhost:3000/realms/master`.
|
||||
|
||||
### Provider-Specific Issuer URLs
|
||||
|
||||
- **Keycloak:** `http://localhost:3000/realms/master` (or your custom realm name)
|
||||
- **Pocket-ID:** `http://localhost:3005`
|
||||
- **Authentik:** `http://localhost:3007/application/o/<client-slug>/`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Keycloak Not Accessible / Healthcheck Failing
|
||||
|
||||
- Check if container is running: `podman ps | grep keycloak`
|
||||
- Check container health status: `podman inspect DEV_keycloak | grep -A 10 Health`
|
||||
- Check logs: `podman logs DEV_keycloak`
|
||||
- Verify port mapping: `netstat -tlnp | grep 3000`
|
||||
- Wait for database to be ready (healthcheck)
|
||||
- Keycloak takes 30-60 seconds to fully start - wait for "Keycloak started" in logs
|
||||
- **Healthcheck:** Keycloak v26+ uses port 9000 for health endpoints. Verify health endpoint:
|
||||
```bash
|
||||
# From host (if port 9000 is exposed):
|
||||
curl http://localhost:9000/health/ready
|
||||
|
||||
# Or test from inside container:
|
||||
podman exec DEV_keycloak bash -c 'exec 3<>/dev/tcp/localhost/9000 && echo -e "GET /health/ready HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n" >&3 && cat <&3'
|
||||
```
|
||||
- **If healthcheck keeps failing:** You can temporarily modify `container-compose.yml` to remove the `condition: service_healthy` from `depends_on` and let fail2ban-ui's retry logic handle the connection:
|
||||
```yaml
|
||||
depends_on:
|
||||
- keycloak # Remove condition to start immediately
|
||||
```
|
||||
|
||||
### Fail2ban-UI Fails to Start / OIDC Initialization Errors
|
||||
|
||||
- **Container stuck in "Created" status:**
|
||||
- This means it's waiting for Keycloak to start (via `depends_on`)
|
||||
- Check Keycloak status: `podman compose ps` - should show "healthy" when ready
|
||||
- Check Keycloak logs: `podman logs DEV_keycloak`
|
||||
- If Keycloak healthcheck is failing but Keycloak is running, you can:
|
||||
1. Wait longer (Keycloak takes 30-60 seconds to start)
|
||||
2. Manually start fail2ban-ui: `podman start DEV_fail2ban-ui-oidc` (it will retry connecting)
|
||||
3. Temporarily remove `condition: service_healthy` from `depends_on` in `container-compose.yml`
|
||||
|
||||
- **"Connection refused" errors:** Keycloak isn't ready yet. The application will retry automatically (up to 10 times with exponential backoff). Wait for Keycloak to fully start (30-60 seconds).
|
||||
|
||||
- **"Issuer did not match" errors:**
|
||||
- This happens when `OIDC_ISSUER_URL` doesn't match the issuer in Keycloak's discovery document
|
||||
- Keycloak is configured with `KC_HOSTNAME=localhost`, so it returns `http://localhost:3000/realms/master` as issuer
|
||||
- Ensure `OIDC_ISSUER_URL` is set to: `http://localhost:3000/realms/master` (or your custom realm)
|
||||
- Verify the issuer: `curl http://localhost:3000/realms/master/.well-known/openid-configuration | grep issuer`
|
||||
|
||||
- **"Realm does not exist" errors:**
|
||||
- The default configuration uses the `master` realm which always exists
|
||||
- If you see this error, check that `OIDC_ISSUER_URL` in `container-compose.yml` matches an existing realm
|
||||
- Verify the realm exists: `curl http://localhost:3000/realms/master/.well-known/openid-configuration`
|
||||
- Or access Keycloak admin console and check available realms
|
||||
|
||||
- **Check fail2ban-ui logs:** `podman logs DEV_fail2ban-ui-oidc` for detailed error messages
|
||||
- **Check Keycloak is ready:** Wait for log message "Keycloak ... started" and verify health endpoint responds
|
||||
|
||||
### Authentication Fails
|
||||
|
||||
1. **Check OIDC Configuration:**
|
||||
- Verify `OIDC_ISSUER_URL` matches provider URL exactly
|
||||
- Ensure client ID and secret match provider configuration
|
||||
- Check redirect URI matches exactly
|
||||
|
||||
2. **Check Fail2ban-UI Logs:**
|
||||
```bash
|
||||
podman logs DEV_fail2ban-ui-oidc
|
||||
```
|
||||
|
||||
3. **Verify Provider Client:**
|
||||
- Ensure client is active in provider admin (accessible at `http://localhost:3000` for Keycloak)
|
||||
- Check redirect URI is exactly: `http://localhost:3080/auth/callback`
|
||||
|
||||
### Session Issues
|
||||
|
||||
- Check `OIDC_SESSION_SECRET` is set (or auto-generated)
|
||||
- Verify session cookie settings (should work with `OIDC_SKIP_VERIFY=true` in dev)
|
||||
- Clear browser cookies and try again
|
||||
|
||||
### Keycloak Realm Not Found
|
||||
|
||||
- **Default Configuration:** The setup uses the `master` realm by default. This realm always exists in Keycloak.
|
||||
- **Custom Realm:** If you created a custom realm (e.g., `myrealm`), ensure:
|
||||
- The realm name in `OIDC_ISSUER_URL` matches exactly (case-sensitive)
|
||||
- The realm is enabled in Keycloak admin console
|
||||
- Update `OIDC_ISSUER_URL` in `container-compose.yml` to: `http://localhost:3000/realms/myrealm`
|
||||
- **Check Realm:** Access `http://localhost:3000/realms/<realm-name>/.well-known/openid-configuration` to verify the realm exists
|
||||
|
||||
## Cleanup
|
||||
|
||||
To remove all containers and volumes:
|
||||
|
||||
```bash
|
||||
podman compose down -v
|
||||
# or
|
||||
docker-compose down -v
|
||||
```
|
||||
|
||||
This will remove:
|
||||
- All containers
|
||||
- Volume data (Keycloak database, Fail2ban-UI config, etc.)
|
||||
|
||||
**Note:** This deletes all development data. Make sure to backup anything important.
|
||||
|
||||
## Production Considerations
|
||||
|
||||
⚠️ **This setup is for development only!**
|
||||
|
||||
For production:
|
||||
- Use HTTPS/TLS (not HTTP)
|
||||
- Set `OIDC_SKIP_VERIFY=false`
|
||||
- Use strong, randomly generated secrets
|
||||
- Configure proper reverse proxy
|
||||
- Use secure session secrets
|
||||
- Change default admin passwords
|
||||
- Enable proper logging and monitoring
|
||||
- Use production-ready database configurations
|
||||
- Configure proper backup strategies
|
||||
293
development/oidc/container-compose.yml
Normal file
293
development/oidc/container-compose.yml
Normal file
@@ -0,0 +1,293 @@
|
||||
services:
|
||||
# Keycloak: OIDC Provider (Recommended)
|
||||
keycloak:
|
||||
image: quay.io/keycloak/keycloak:latest
|
||||
container_name: DEV_keycloak
|
||||
environment:
|
||||
# Admin credentials (change for production!)
|
||||
KC_BOOTSTRAP_ADMIN_USERNAME: admin
|
||||
KC_BOOTSTRAP_ADMIN_PASSWORD: admin
|
||||
# Database settings
|
||||
KC_DB: postgres
|
||||
KC_DB_URL_HOST: postgres
|
||||
KC_DB_USERNAME: keycloak
|
||||
KC_DB_PASSWORD: keycloak
|
||||
# Hostname configuration for development
|
||||
# Using full URL so issuer matches what fail2ban-ui expects
|
||||
# When using KC_HOSTNAME_BACKCHANNEL_DYNAMIC=true, hostname must be a full URL
|
||||
# Set KEYCLOAK_PUBLIC_URL environment variable to your server's public URL
|
||||
# If not set, defaults to http://localhost:3000
|
||||
KC_HOSTNAME: ${KEYCLOAK_PUBLIC_URL:-http://localhost:3000}
|
||||
KC_HOSTNAME_STRICT: "false"
|
||||
KC_HOSTNAME_BACKCHANNEL_DYNAMIC: "true"
|
||||
# Development mode (use "start" for production)
|
||||
KC_HEALTH_ENABLED: "true"
|
||||
KC_METRICS_ENABLED: "true"
|
||||
command:
|
||||
- start-dev
|
||||
- --http-host=0.0.0.0
|
||||
- --http-port=8080
|
||||
ports:
|
||||
- "0.0.0.0:3000:8080"
|
||||
- "0.0.0.0:9000:9000" # Management port for health checks
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./keycloak-data:/opt/keycloak/data:z
|
||||
healthcheck:
|
||||
# Keycloak v26+ uses port 9000 for health endpoints (management port)
|
||||
test: ["CMD-SHELL", "exec 3<>/dev/tcp/localhost/9000 && echo -e 'GET /health/ready HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n' >&3 && cat <&3 | grep -q '200 OK' || exit 1"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 30
|
||||
start_period: 90s
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- oidc-network
|
||||
|
||||
# PostgreSQL: Database for Keycloak
|
||||
postgres:
|
||||
image: postgres:15-alpine
|
||||
container_name: DEV_keycloak-db
|
||||
environment:
|
||||
POSTGRES_DB: keycloak
|
||||
POSTGRES_USER: keycloak
|
||||
POSTGRES_PASSWORD: keycloak
|
||||
volumes:
|
||||
- ./keycloak-db:/var/lib/postgresql/data:z
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U keycloak"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- oidc-network
|
||||
|
||||
# Pocket-ID: OIDC Provider (Alternative 1) - Not fully tested
|
||||
# Uncomment to use Pocket-ID instead of Keycloak
|
||||
# pocket-id:
|
||||
# image: ghcr.io/pocket-id/pocket-id:latest
|
||||
# container_name: DEV_pocket-id
|
||||
# environment:
|
||||
# - APP_URL=http://localhost:3005
|
||||
# # Encryption key (choose one method):
|
||||
# # Method 1: Direct key (simple but less secure)
|
||||
# # Generate with: openssl rand -base64 32
|
||||
# - ENCRYPTION_KEY=aF98qjUZuCJ_j_9Bh8hXx5U832ZpYQW
|
||||
# # Method 2: File-based key (recommended)
|
||||
# # Put the base64 key in a file and point to it here.
|
||||
# # ENCRYPTION_KEY_FILE=/path/to/encryption_key
|
||||
# - TRUST_PROXY=false
|
||||
# - MAXMIND_LICENSE_KEY=
|
||||
# - PUID=1000
|
||||
# - PGID=1000
|
||||
# - DB_PROVIDER=sqlite
|
||||
# - DB_CONNECTION_STRING=sqlite:///pocket-id/var/db.sqlite
|
||||
# - PORT=1411
|
||||
# volumes:
|
||||
# - ./pocket-id-data:/pocket-id/var:z
|
||||
# ports:
|
||||
# - "3005:1411"
|
||||
# restart: unless-stopped
|
||||
# networks:
|
||||
# - oidc-network
|
||||
|
||||
# Authentik: OIDC Provider (Alternative 2) - Not fully tested
|
||||
# Uncomment to use Authentik instead of Keycloak
|
||||
# Note: Authentik requires additional setup (migrations, initial admin user)
|
||||
# authentik-server:
|
||||
# image: ghcr.io/goauthentik/server:latest
|
||||
# container_name: DEV_authentik-server
|
||||
# environment:
|
||||
# - AUTHENTIK_SECRET_KEY=dev-secret-key-change-in-production-generate-with-openssl-rand-base64-60
|
||||
# - AUTHENTIK_ERROR_REPORTING__ENABLED=false
|
||||
# - AUTHENTIK_DISABLE_UPDATE_CHECK=true
|
||||
# - AUTHENTIK_DISABLE_STARTUP_ANALYTICS=true
|
||||
# - PG_PASS=authentik
|
||||
# - AUTHENTIK_REDIS__HOST=redis
|
||||
# - AUTHENTIK_POSTGRESQL__HOST=postgresql
|
||||
# - AUTHENTIK_POSTGRESQL__USER=authentik
|
||||
# - AUTHENTIK_POSTGRESQL__PASSWORD=authentik
|
||||
# - AUTHENTIK_POSTGRESQL__NAME=authentik
|
||||
# ports:
|
||||
# - "3007:9000"
|
||||
# - "3008:9443"
|
||||
# depends_on:
|
||||
# - authentik-postgresql
|
||||
# - authentik-redis
|
||||
# volumes:
|
||||
# - ./authentik-media:/media:z
|
||||
# restart: unless-stopped
|
||||
# networks:
|
||||
# - oidc-network
|
||||
#
|
||||
# authentik-worker:
|
||||
# image: ghcr.io/goauthentik/server:latest
|
||||
# container_name: DEV_authentik-worker
|
||||
# environment:
|
||||
# - AUTHENTIK_SECRET_KEY=dev-secret-key-change-in-production-generate-with-openssl-rand-base64-60
|
||||
# - AUTHENTIK_ERROR_REPORTING__ENABLED=false
|
||||
# - AUTHENTIK_DISABLE_UPDATE_CHECK=true
|
||||
# - AUTHENTIK_DISABLE_STARTUP_ANALYTICS=true
|
||||
# - PG_PASS=authentik
|
||||
# - AUTHENTIK_REDIS__HOST=redis
|
||||
# - AUTHENTIK_POSTGRESQL__HOST=postgresql
|
||||
# - AUTHENTIK_POSTGRESQL__USER=authentik
|
||||
# - AUTHENTIK_POSTGRESQL__PASSWORD=authentik
|
||||
# - AUTHENTIK_POSTGRESQL__NAME=authentik
|
||||
# command: ["authentik", "worker"]
|
||||
# depends_on:
|
||||
# - authentik-postgresql
|
||||
# - authentik-redis
|
||||
# volumes:
|
||||
# - ./authentik-media:/media:z
|
||||
# restart: unless-stopped
|
||||
# networks:
|
||||
# - oidc-network
|
||||
#
|
||||
# authentik-postgresql:
|
||||
# image: postgres:15-alpine
|
||||
# container_name: DEV_authentik-db
|
||||
# environment:
|
||||
# POSTGRES_PASSWORD: authentik
|
||||
# POSTGRES_USER: authentik
|
||||
# POSTGRES_DB: authentik
|
||||
# volumes:
|
||||
# - ./authentik-db:/var/lib/postgresql/data:z
|
||||
# restart: unless-stopped
|
||||
# networks:
|
||||
# - oidc-network
|
||||
#
|
||||
# authentik-redis:
|
||||
# image: redis:7-alpine
|
||||
# container_name: DEV_authentik-redis
|
||||
# restart: unless-stopped
|
||||
# networks:
|
||||
# - oidc-network
|
||||
|
||||
# Keycloak Client Auto-Configuration
|
||||
# This container automatically creates the OIDC client in Keycloak
|
||||
# Set PUBLIC_FRONTEND_URL environment variable to your server's public URL (e.g., http://192.168.1.100:3080)
|
||||
# If not set, defaults to http://localhost:3080
|
||||
keycloak-init:
|
||||
image: curlimages/curl:latest
|
||||
container_name: DEV_keycloak-init
|
||||
depends_on:
|
||||
keycloak:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
- KEYCLOAK_URL=http://keycloak:8080
|
||||
- KEYCLOAK_ADMIN=admin
|
||||
- KEYCLOAK_PASSWORD=admin
|
||||
- REALM=master
|
||||
- CLIENT_ID=fail2ban-ui
|
||||
# PUBLIC_FRONTEND_URL: Set this to your server's public URL (IP or hostname)
|
||||
# If not set, defaults to http://localhost:3080
|
||||
- PUBLIC_FRONTEND_URL=${PUBLIC_FRONTEND_URL:-http://localhost:3080}
|
||||
- SECRET_FILE=/config/keycloak-client-secret
|
||||
volumes:
|
||||
- ./init-keycloak.sh:/init-keycloak.sh:ro
|
||||
- ./config:/config:z
|
||||
# Run as root to avoid permission issues
|
||||
user: "0:0"
|
||||
command: ["/bin/sh", "-c", "apk add --no-cache jq 2>/dev/null && /bin/sh /init-keycloak.sh 2>&1"]
|
||||
networks:
|
||||
- oidc-network
|
||||
restart: "no" # Only run once
|
||||
|
||||
# Fail2ban-UI: Main application with OIDC authentication
|
||||
fail2ban-ui:
|
||||
image: localhost/fail2ban-ui:dev
|
||||
container_name: DEV_fail2ban-ui-oidc
|
||||
privileged: true
|
||||
# Use host network to access Keycloak on localhost:3000
|
||||
# This allows issuer URL to match Keycloak's discovery document
|
||||
network_mode: host
|
||||
# ports:
|
||||
# - "3080:8080" # Not needed with host network
|
||||
environment:
|
||||
- PORT=3080
|
||||
- BIND_ADDRESS=0.0.0.0
|
||||
|
||||
# OIDC Configuration for Keycloak (default)
|
||||
# PUBLIC_FRONTEND_URL: Server's public URL for redirects (if not set, defaults to http://localhost:3080)
|
||||
# KEYCLOAK_URL: Server's public URL for Keycloak (if not set, defaults to http://localhost:3000)
|
||||
# Create a .env file with these variables for remote server access
|
||||
- PUBLIC_FRONTEND_URL=${PUBLIC_FRONTEND_URL:-http://localhost:3080}
|
||||
- KEYCLOAK_URL=${KEYCLOAK_URL:-http://localhost:3000}
|
||||
# OIDC settings
|
||||
- OIDC_ENABLED=true
|
||||
- OIDC_PROVIDER=keycloak
|
||||
- OIDC_ISSUER_URL=${KEYCLOAK_URL}/realms/master
|
||||
- OIDC_CLIENT_ID=fail2ban-ui
|
||||
- OIDC_CLIENT_SECRET=auto-configured
|
||||
- OIDC_CLIENT_SECRET_FILE=/config/keycloak-client-secret
|
||||
- OIDC_REDIRECT_URL=${PUBLIC_FRONTEND_URL}/auth/callback
|
||||
- OIDC_SCOPES=openid,profile,email
|
||||
- OIDC_SESSION_MAX_AGE=7200
|
||||
- OIDC_USERNAME_CLAIM=preferred_username
|
||||
- OIDC_SKIP_VERIFY=true
|
||||
# Optional: Logout URL
|
||||
#- OIDC_LOGOUT_URL=${KEYCLOAK_URL}/realms/master/protocol/openid-connect/logout
|
||||
|
||||
# Alternative OIDC Configuration Examples (uncomment and adjust as needed):
|
||||
# For Pocket-ID:
|
||||
# - OIDC_PROVIDER=pocketid
|
||||
# - OIDC_ISSUER_URL=http://localhost:3005
|
||||
# - OIDC_LOGOUT_URL=http://localhost:3005/logout
|
||||
#
|
||||
# For Authentik:
|
||||
# - OIDC_PROVIDER=authentik
|
||||
# - OIDC_ISSUER_URL=http://localhost:3007/application/o/fail2ban-ui/
|
||||
|
||||
volumes:
|
||||
# Required for fail2ban-ui: Stores SQLite database, application settings, and SSH keys
|
||||
- ./config:/config:z
|
||||
# Required for fail2ban-ui: Used for testing logpath before enabling jails
|
||||
- /var/log:/var/log:ro
|
||||
|
||||
# Required for compose-local fail2ban instance: We mount the same Fail2Ban config as the linuxserver-fail2ban container (under /config/fail2ban to fail2ban-ui can modify configs)
|
||||
- ./fail2ban-config-local/fail2ban:/etc/fail2ban:z
|
||||
# Required for compose-local fail2ban instance: Mount the same run directory that contains fail2ban.sock for communication between fail2ban-ui and the linuxserver-fail2ban container
|
||||
- ./f2b-run-local:/var/run/fail2ban:z
|
||||
|
||||
restart: unless-stopped
|
||||
# Wait for Keycloak to be healthy before starting
|
||||
# Note: keycloak-init runs as a one-time job, so we only wait for Keycloak
|
||||
# The application will retry connecting to Keycloak if needed
|
||||
# Note: With host network mode, depends_on still works but network isolation is bypassed
|
||||
depends_on:
|
||||
- keycloak
|
||||
|
||||
# Fail2ban-Local: Local Fail2ban instance for testing local connector
|
||||
fail2ban-local:
|
||||
image: lscr.io/linuxserver/fail2ban:latest
|
||||
container_name: DEV_fail2ban-local
|
||||
cap_add:
|
||||
# Required for fail2ban container: Allows to manage network interfaces and iptables from the container
|
||||
- NET_ADMIN
|
||||
# Required for fail2ban container: Allows to create raw sockets (needed for fail2ban.sock)
|
||||
- NET_RAW
|
||||
# Required for fail2ban container: Allows to run as root (needed to manage network interfaces and raw sockets)
|
||||
- SYS_ADMIN
|
||||
#privileged: true
|
||||
network_mode: host # needed to add iptables rules to the host network
|
||||
environment:
|
||||
- TZ=Europe/Zurich
|
||||
- VERBOSITY=-vv
|
||||
volumes:
|
||||
# To make sure linuxserver-fail2ban configs are persistent across container restarts (also needed by fail2ban-ui to modify configs)
|
||||
- ./fail2ban-config-local:/config:z
|
||||
# Directory that contains fail2ban.sock for communication between fail2ban-ui and fail2ban container
|
||||
- ./f2b-run-local:/var/run/fail2ban:z
|
||||
|
||||
# Log sources for fail2ban container
|
||||
- /var/log:/var/log:ro
|
||||
- /var/log/httpd:/remotelogs/apache2:ro
|
||||
restart: unless-stopped
|
||||
|
||||
networks:
|
||||
oidc-network:
|
||||
driver: bridge
|
||||
166
development/oidc/init-keycloak.sh
Executable file
166
development/oidc/init-keycloak.sh
Executable file
@@ -0,0 +1,166 @@
|
||||
#!/bin/bash
|
||||
# Automatic Keycloak OIDC client configuration script
|
||||
# This script creates the fail2ban-ui OIDC client in Keycloak automatically
|
||||
|
||||
set -e
|
||||
|
||||
KEYCLOAK_URL="${KEYCLOAK_URL:-http://localhost:3000}"
|
||||
KEYCLOAK_ADMIN="${KEYCLOAK_ADMIN:-admin}"
|
||||
KEYCLOAK_PASSWORD="${KEYCLOAK_PASSWORD:-admin}"
|
||||
REALM="${REALM:-master}"
|
||||
CLIENT_ID="${CLIENT_ID:-fail2ban-ui}"
|
||||
CLIENT_SECRET="${CLIENT_SECRET:-}"
|
||||
# Use PUBLIC_FRONTEND_URL if provided, otherwise default to localhost
|
||||
PUBLIC_FRONTEND_URL="${PUBLIC_FRONTEND_URL:-http://localhost:3080}"
|
||||
REDIRECT_URI="${REDIRECT_URI:-${PUBLIC_FRONTEND_URL}/auth/callback}"
|
||||
WEB_ORIGIN="${WEB_ORIGIN:-${PUBLIC_FRONTEND_URL}}"
|
||||
|
||||
# Extract host and port from KEYCLOAK_URL for health check
|
||||
# KEYCLOAK_URL is the internal URL (e.g., http://keycloak:8080)
|
||||
# Health endpoint is on management port 9000
|
||||
KEYCLOAK_HOST=$(echo "${KEYCLOAK_URL}" | sed -E 's|https?://([^:/]+).*|\1|')
|
||||
KEYCLOAK_HEALTH_URL="http://${KEYCLOAK_HOST}:9000/health/ready"
|
||||
|
||||
echo "Waiting for Keycloak to be ready..."
|
||||
echo "Checking health endpoint: ${KEYCLOAK_HEALTH_URL}"
|
||||
max_attempts=120 # Increased timeout since Keycloak can take a while
|
||||
attempt=0
|
||||
while [ $attempt -lt $max_attempts ]; do
|
||||
# Check health endpoint on management port 9000
|
||||
if curl -s -f "${KEYCLOAK_HEALTH_URL}" > /dev/null 2>&1; then
|
||||
echo "Keycloak is ready!"
|
||||
break
|
||||
fi
|
||||
# Also try the main port as fallback
|
||||
if curl -s -f "${KEYCLOAK_URL}/health/ready" > /dev/null 2>&1; then
|
||||
echo "Keycloak is ready (via main port)!"
|
||||
break
|
||||
fi
|
||||
attempt=$((attempt + 1))
|
||||
if [ $((attempt % 10)) -eq 0 ]; then
|
||||
echo "Attempt $attempt/$max_attempts: Keycloak not ready yet, waiting..."
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
|
||||
if [ $attempt -eq $max_attempts ]; then
|
||||
echo "ERROR: Keycloak did not become ready in time"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Waiting for Keycloak admin API to be available..."
|
||||
sleep 5
|
||||
|
||||
echo "Getting admin access token..."
|
||||
ADMIN_TOKEN=$(curl -s -X POST "${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token" \
|
||||
-H "Content-Type: application/x-www-form-urlencoded" \
|
||||
-d "username=${KEYCLOAK_ADMIN}" \
|
||||
-d "password=${KEYCLOAK_PASSWORD}" \
|
||||
-d "grant_type=password" \
|
||||
-d "client_id=admin-cli" | jq -r '.access_token')
|
||||
|
||||
if [ -z "$ADMIN_TOKEN" ] || [ "$ADMIN_TOKEN" = "null" ]; then
|
||||
echo "ERROR: Failed to get admin token"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Checking if client already exists..."
|
||||
EXISTING_CLIENT=$(curl -s -X GET "${KEYCLOAK_URL}/admin/realms/${REALM}/clients?clientId=${CLIENT_ID}" \
|
||||
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
|
||||
-H "Content-Type: application/json" | jq -r '.[0].id // empty')
|
||||
|
||||
if [ -n "$EXISTING_CLIENT" ]; then
|
||||
echo "Client '${CLIENT_ID}' already exists, updating..."
|
||||
CLIENT_UUID="$EXISTING_CLIENT"
|
||||
|
||||
# Update client configuration
|
||||
curl -s -X PUT "${KEYCLOAK_URL}/admin/realms/${REALM}/clients/${CLIENT_UUID}" \
|
||||
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"clientId\": \"${CLIENT_ID}\",
|
||||
\"enabled\": true,
|
||||
\"clientAuthenticatorType\": \"client-secret\",
|
||||
\"redirectUris\": [\"${REDIRECT_URI}\"],
|
||||
\"webOrigins\": [\"${WEB_ORIGIN}\"],
|
||||
\"protocol\": \"openid-connect\",
|
||||
\"publicClient\": false,
|
||||
\"standardFlowEnabled\": true,
|
||||
\"directAccessGrantsEnabled\": true
|
||||
}" > /dev/null
|
||||
|
||||
echo "Client updated successfully"
|
||||
else
|
||||
echo "Creating new client '${CLIENT_ID}'..."
|
||||
|
||||
# Create client
|
||||
CLIENT_RESPONSE=$(curl -s -X POST "${KEYCLOAK_URL}/admin/realms/${REALM}/clients" \
|
||||
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{
|
||||
\"clientId\": \"${CLIENT_ID}\",
|
||||
\"enabled\": true,
|
||||
\"clientAuthenticatorType\": \"client-secret\",
|
||||
\"redirectUris\": [\"${REDIRECT_URI}\"],
|
||||
\"webOrigins\": [\"${WEB_ORIGIN}\"],
|
||||
\"protocol\": \"openid-connect\",
|
||||
\"publicClient\": false,
|
||||
\"standardFlowEnabled\": true,
|
||||
\"directAccessGrantsEnabled\": true
|
||||
}")
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "ERROR: Failed to create client"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get the client UUID
|
||||
CLIENT_UUID=$(curl -s -X GET "${KEYCLOAK_URL}/admin/realms/${REALM}/clients?clientId=${CLIENT_ID}" \
|
||||
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
|
||||
-H "Content-Type: application/json" | jq -r '.[0].id')
|
||||
|
||||
echo "Client created successfully with UUID: ${CLIENT_UUID}"
|
||||
fi
|
||||
|
||||
# Get or regenerate client secret
|
||||
echo "Getting client secret..."
|
||||
CLIENT_SECRET=$(curl -s -X GET "${KEYCLOAK_URL}/admin/realms/${REALM}/clients/${CLIENT_UUID}/client-secret" \
|
||||
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
|
||||
-H "Content-Type: application/json" | jq -r '.value')
|
||||
|
||||
if [ -z "$CLIENT_SECRET" ] || [ "$CLIENT_SECRET" = "null" ]; then
|
||||
echo "Regenerating client secret..."
|
||||
CLIENT_SECRET=$(curl -s -X POST "${KEYCLOAK_URL}/admin/realms/${REALM}/clients/${CLIENT_UUID}/client-secret" \
|
||||
-H "Authorization: Bearer ${ADMIN_TOKEN}" \
|
||||
-H "Content-Type: application/json" | jq -r '.value')
|
||||
fi
|
||||
|
||||
if [ -z "$CLIENT_SECRET" ] || [ "$CLIENT_SECRET" = "null" ]; then
|
||||
echo "ERROR: Failed to get client secret"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "OIDC Client Configuration Complete!"
|
||||
echo "=========================================="
|
||||
echo "Client ID: ${CLIENT_ID}"
|
||||
echo "Client Secret: ${CLIENT_SECRET}"
|
||||
echo "Realm: ${REALM}"
|
||||
echo "Redirect URI: ${REDIRECT_URI}"
|
||||
echo "=========================================="
|
||||
|
||||
# Save secret to shared volume for fail2ban-ui to read
|
||||
SECRET_FILE="${SECRET_FILE:-/config/keycloak-client-secret}"
|
||||
# Create directory if it doesn't exist
|
||||
mkdir -p "$(dirname "${SECRET_FILE}")" 2>/dev/null || true
|
||||
# Write secret file (running as root, so should have permissions)
|
||||
if echo "${CLIENT_SECRET}" > "${SECRET_FILE}" 2>/dev/null; then
|
||||
chmod 644 "${SECRET_FILE}" 2>/dev/null || true
|
||||
echo "Client secret saved to ${SECRET_FILE} for fail2ban-ui"
|
||||
else
|
||||
echo "ERROR: Failed to write client secret to ${SECRET_FILE}"
|
||||
echo "Client secret: ${CLIENT_SECRET}"
|
||||
echo "Please save this secret manually to ${SECRET_FILE}"
|
||||
exit 1
|
||||
fi
|
||||
5
development/ssh_and_local/.gitignore
vendored
Normal file
5
development/ssh_and_local/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
fail2ban-config-local
|
||||
fail2ban-config-ssh
|
||||
f2b-run-local
|
||||
f2b-run-ssh
|
||||
ssh-keys
|
||||
250
development/ssh_and_local/README.md
Normal file
250
development/ssh_and_local/README.md
Normal file
@@ -0,0 +1,250 @@
|
||||
# SSH and Local Fail2ban Development Setup
|
||||
|
||||
This setup provides a complete testing environment for Fail2ban UI with:
|
||||
- **Local Fail2ban instance** (container) - for testing local connector
|
||||
- **Remote Fail2ban instance via SSH** (container) - for testing SSH connector
|
||||
|
||||
## Services
|
||||
|
||||
### 1. Fail2ban-Local
|
||||
- **Container:** `DEV_fail2ban-local`
|
||||
- **Purpose:** Local Fail2ban instance for testing local connector
|
||||
- **Network:** `host` mode (for iptables access)
|
||||
- **Config:** `./fail2ban-config-local/`
|
||||
- **Socket:** `./f2b-run-local/`
|
||||
|
||||
### 2. Fail2ban-SSH
|
||||
- **Container:** `DEV_fail2ban-ssh`
|
||||
- **Purpose:** Remote Fail2ban instance accessible via SSH
|
||||
- **Network:** Bridge mode
|
||||
- **SSH Port:** `2222` (mapped from container port 22)
|
||||
- **SSH User:** `testuser`
|
||||
- **SSH Key:** Auto-generated in `./ssh-keys/`
|
||||
- **Config:** `./fail2ban-config-ssh/`
|
||||
|
||||
### 3. Fail2ban-UI
|
||||
- **Container:** `DEV_fail2ban-ui`
|
||||
- **Port:** `3080`
|
||||
- **URL:** `http://172.16.10.18:3080` (or configured BIND_ADDRESS)
|
||||
- **Purpose:** Main application for managing both Fail2ban instances
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
### 1. Build the Fail2ban-UI Image
|
||||
|
||||
```bash
|
||||
cd /opt/fail2ban-ui
|
||||
podman build -t localhost/fail2ban-ui:dev .
|
||||
# or
|
||||
docker build -t localhost/fail2ban-ui:dev .
|
||||
```
|
||||
|
||||
### 2. Start the Services
|
||||
|
||||
```bash
|
||||
cd /opt/fail2ban-ui/development/ssh_and_local
|
||||
podman compose up -d
|
||||
# or
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### 3. Wait for SSH Container Setup
|
||||
|
||||
The SSH container takes a moment to:
|
||||
- Generate SSH keys (if not present)
|
||||
- Configure SSH server
|
||||
- Set up user permissions
|
||||
- Configure sudoers
|
||||
|
||||
Check logs to verify:
|
||||
```bash
|
||||
podman logs DEV_fail2ban-ssh
|
||||
```
|
||||
|
||||
Look for:
|
||||
```
|
||||
========================================
|
||||
SSH Test Container Ready
|
||||
========================================
|
||||
```
|
||||
|
||||
### 4. Configure Fail2ban-UI
|
||||
|
||||
1. **Access Fail2ban UI:**
|
||||
- Open `http://172.16.10.18:3080` (or your configured BIND_ADDRESS:PORT)
|
||||
- Or if using host network: `http://localhost:3080`
|
||||
|
||||
2. **Add Local Server:**
|
||||
- Go to "Manage Servers"
|
||||
- The local Fail2ban instance should be auto-detected
|
||||
- Enable the local connector
|
||||
|
||||
3. **Add SSH Server:**
|
||||
- Go to "Manage Servers"
|
||||
- Click "Add Server"
|
||||
- Configure:
|
||||
- **Name:** `SSH Test Server`
|
||||
- **Type:** `SSH`
|
||||
- **Host:** `127.0.0.1`
|
||||
- **Port:** `2222`
|
||||
- **SSH User:** `testuser`
|
||||
- **SSH Key:** Select `/config/.ssh/id_rsa` (auto-mounted)
|
||||
- Enable the connector
|
||||
- Click "Test Connection" to verify
|
||||
|
||||
## SSH Connection Details
|
||||
|
||||
- **Host:** `127.0.0.1`
|
||||
- **Port:** `2222`
|
||||
- **User:** `testuser`
|
||||
- **Key Path (in container):** `/config/.ssh/id_rsa`
|
||||
- **Key Path (host):** `./ssh-keys/id_rsa`
|
||||
|
||||
### Test SSH Connection Manually
|
||||
|
||||
```bash
|
||||
# From host
|
||||
podman exec -it DEV_fail2ban-ui ssh \
|
||||
-o StrictHostKeyChecking=no \
|
||||
-o UserKnownHostsFile=/dev/null \
|
||||
-o BatchMode=yes \
|
||||
-i /config/.ssh/id_rsa \
|
||||
-p 2222 \
|
||||
testuser@127.0.0.1
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Fail2ban-UI Environment Variables
|
||||
|
||||
Edit `container-compose.yml` to customize:
|
||||
|
||||
```yaml
|
||||
environment:
|
||||
- PORT=3080
|
||||
- BIND_ADDRESS=172.16.10.18 # Change to your IP or 0.0.0.0
|
||||
# OIDC settings (if testing OIDC)
|
||||
- OIDC_ENABLED=false # Set to true to enable OIDC
|
||||
```
|
||||
|
||||
### SSH Container Customization
|
||||
|
||||
The SSH container is pre-configured with:
|
||||
- Passwordless SSH key authentication
|
||||
- Sudo permissions for fail2ban-client commands
|
||||
- Proper file permissions (FACLs) for Fail2ban config directories
|
||||
- Root access for network management
|
||||
|
||||
To modify SSH configuration, edit the `command` section in `container-compose.yml`.
|
||||
|
||||
## Volume Structure
|
||||
|
||||
```
|
||||
./config/ # Fail2ban-UI configuration and database
|
||||
./ssh-keys/ # SSH key pair (shared between containers)
|
||||
./fail2ban-config-local/ # Local Fail2ban configuration
|
||||
./f2b-run-local/ # Local Fail2ban socket directory
|
||||
./fail2ban-config-ssh/ # SSH Fail2ban configuration
|
||||
```
|
||||
|
||||
## Testing Scenarios
|
||||
|
||||
### 1. Local Connector Test
|
||||
|
||||
1. Enable local connector in Fail2ban-UI
|
||||
2. Create a test jail
|
||||
3. Verify jail appears in dashboard
|
||||
4. Test ban/unban operations
|
||||
5. Verify configuration changes persist
|
||||
|
||||
### 2. SSH Connector Test
|
||||
|
||||
1. Add SSH server in Fail2ban-UI
|
||||
2. Test connection (should succeed)
|
||||
3. Create a test jail on remote server
|
||||
4. Verify jail appears in dashboard
|
||||
5. Test ban/unban operations
|
||||
6. Verify configuration changes sync to remote
|
||||
|
||||
### 3. Multi-Server Management
|
||||
|
||||
1. Enable both local and SSH connectors
|
||||
2. Verify both servers appear in server selector
|
||||
3. Switch between servers
|
||||
4. Verify each server's jails are isolated
|
||||
5. Test operations on each server independently
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### SSH Connection Fails
|
||||
|
||||
1. **Check SSH container is ready:**
|
||||
```bash
|
||||
podman logs DEV_fail2ban-ssh | tail -20
|
||||
```
|
||||
|
||||
2. **Verify SSH keys exist:**
|
||||
```bash
|
||||
ls -la ./ssh-keys/
|
||||
```
|
||||
|
||||
3. **Test SSH manually:**
|
||||
```bash
|
||||
podman exec -it DEV_fail2ban-ui ssh -v -i /config/.ssh/id_rsa -p 2222 testuser@127.0.0.1
|
||||
```
|
||||
|
||||
4. **Check SSH container port:**
|
||||
```bash
|
||||
netstat -tlnp | grep 2222
|
||||
```
|
||||
|
||||
### Local Connector Issues
|
||||
|
||||
1. **Check socket exists:**
|
||||
```bash
|
||||
ls -la ./f2b-run-local/
|
||||
```
|
||||
|
||||
2. **Verify permissions:**
|
||||
```bash
|
||||
podman exec -it DEV_fail2ban-local ls -la /var/run/fail2ban/
|
||||
```
|
||||
|
||||
3. **Check Fail2ban status:**
|
||||
```bash
|
||||
podman exec -it DEV_fail2ban-local fail2ban-client status
|
||||
```
|
||||
|
||||
### Permission Errors
|
||||
|
||||
- Ensure volumes have correct SELinux labels (`:z` or `:Z`)
|
||||
- Check container is running with required capabilities
|
||||
- Verify file permissions in mounted directories
|
||||
|
||||
## Cleanup
|
||||
|
||||
To remove all containers and volumes:
|
||||
|
||||
```bash
|
||||
podman compose down -v
|
||||
# or
|
||||
docker-compose down -v
|
||||
```
|
||||
|
||||
This will remove:
|
||||
- All containers
|
||||
- Volume data (configs, SSH keys, databases)
|
||||
|
||||
**Note:** This deletes all development data. SSH keys will be regenerated on next start.
|
||||
|
||||
## Production Considerations
|
||||
|
||||
⚠️ **This setup is for development only!**
|
||||
|
||||
For production:
|
||||
- Use proper SSH key management (not this auto-generated key)
|
||||
- Use dedicated service accounts (not testuser)
|
||||
- Use HTTPS/TLS (not HTTP) / Configure proper reverse proxy
|
||||
- Use strong, randomly generated secrets
|
||||
- Use secure session secrets
|
||||
- Enable proper logging and monitoring
|
||||
137
development/ssh_and_local/container-compose.yml
Normal file
137
development/ssh_and_local/container-compose.yml
Normal file
@@ -0,0 +1,137 @@
|
||||
services:
|
||||
fail2ban-local:
|
||||
image: lscr.io/linuxserver/fail2ban:latest
|
||||
container_name: DEV_fail2ban-local
|
||||
cap_add:
|
||||
# Required for fail2ban container: Allows to manage network interfaces and iptables from the container
|
||||
- NET_ADMIN
|
||||
# Required for fail2ban container: Allows to create raw sockets (needed for fail2ban.sock)
|
||||
- NET_RAW
|
||||
# Required for fail2ban container: Allows to run as root (needed to manage network interfaces and raw sockets)
|
||||
- SYS_ADMIN
|
||||
#privileged: true
|
||||
network_mode: host # needed to add iptables rules to the host network
|
||||
environment:
|
||||
- TZ=Europe/Zurich
|
||||
- VERBOSITY=-vv
|
||||
volumes:
|
||||
# To make sure linuxserver-fail2ban configs are persistent across container restarts (also needed by fail2ban-ui to modify configs)
|
||||
- ./fail2ban-config-local:/config:z
|
||||
# Directory that contains fail2ban.sock for communication between fail2ban-ui and fail2ban container
|
||||
- ./f2b-run-local:/var/run/fail2ban:z
|
||||
|
||||
# Log sources for fail2ban container
|
||||
- /var/log:/var/log:ro
|
||||
- /var/log/httpd:/remotelogs/apache2:ro
|
||||
restart: unless-stopped
|
||||
|
||||
|
||||
fail2ban-ui:
|
||||
#image: registry.swissmakers.ch/infra/fail2ban-ui:latest
|
||||
image: localhost/fail2ban-ui:dev
|
||||
container_name: DEV_fail2ban-ui
|
||||
privileged: true
|
||||
network_mode: host
|
||||
environment:
|
||||
- PORT=3080
|
||||
- BIND_ADDRESS=172.16.10.18
|
||||
|
||||
volumes:
|
||||
# Required for fail2ban-ui: Stores SQLite database, application settings, and SSH keys of the fail2ban-ui container
|
||||
- ./config:/config:Z
|
||||
# Mount persistent SSH keys directory
|
||||
- ./ssh-keys:/config/.ssh:z
|
||||
# Required for fail2ban-ui: Used for testing, that logpath is working, before enabeling a jail. Without this read only access the fail2ban-ui will not be able to enable jails (logpath-test would fail)
|
||||
- /var/log:/var/log:ro
|
||||
- /var/log/httpd:/remotelogs/apache2:ro # this mounts the apache2 logs of a RPM based system (e.g. Rocky Linux) to the default location set by linuxserver-fail2ban. (on debian based systems this is /var/log/apache2 and currently hardcoded in the linuxserver-fail2ban container)
|
||||
|
||||
# Required for compose-local fail2ban instance: We mount the same Fail2Ban config as the linuxserver-fail2ban container (under /config/fail2ban to fail2ban-ui can modify configs)
|
||||
- ./fail2ban-config-local/fail2ban:/etc/fail2ban:z
|
||||
# Required for compose-local fail2ban instance: Mount the same run directory that contains fail2ban.sock for communication between fail2ban-ui and the linuxserver-fail2ban container
|
||||
- ./f2b-run-local:/var/run/fail2ban:z
|
||||
|
||||
restart: unless-stopped
|
||||
|
||||
|
||||
fail2ban-ssh:
|
||||
image: lscr.io/linuxserver/fail2ban:latest
|
||||
container_name: DEV_fail2ban-ssh
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
- NET_RAW
|
||||
- SYS_ADMIN
|
||||
network_mode: bridge
|
||||
ports:
|
||||
- "2222:22" # SSH port mapping
|
||||
environment:
|
||||
- TZ=Europe/Zurich
|
||||
- VERBOSITY=-vv
|
||||
- PUID=0 # Run as root for SSH setup
|
||||
- PGID=0
|
||||
volumes:
|
||||
- ./fail2ban-config-ssh:/config:z
|
||||
- /var/log:/var/log:ro
|
||||
- /var/log/httpd:/remotelogs/apache2:ro
|
||||
# Mount persistent SSH keys - shared between containers
|
||||
- ./ssh-keys:/mnt/ssh-keys:z
|
||||
# We use entrypoint override to run SSH setup before the container's init
|
||||
entrypoint: /bin/sh
|
||||
command: >
|
||||
-c "
|
||||
apk update && apk add --no-cache openssh-server openssh-keygen sudo acl;
|
||||
ssh-keygen -A;
|
||||
useradd -m -s /bin/bash testuser 2>/dev/null || true;
|
||||
passwd -d testuser 2>/dev/null || usermod -U testuser 2>/dev/null || true;
|
||||
chage -E -1 testuser 2>/dev/null || true;
|
||||
mkdir -p /home/testuser/.ssh;
|
||||
if [ ! -f /mnt/ssh-keys/id_rsa ]; then
|
||||
echo 'Generating new SSH key pair...';
|
||||
ssh-keygen -t rsa -b 4096 -m PEM -f /mnt/ssh-keys/id_rsa -N '';
|
||||
chmod 600 /mnt/ssh-keys/id_rsa;
|
||||
chmod 644 /mnt/ssh-keys/id_rsa.pub;
|
||||
echo 'SSH key pair generated in persistent volume';
|
||||
else
|
||||
echo 'Using existing SSH key pair from persistent volume';
|
||||
fi;
|
||||
cp /mnt/ssh-keys/id_rsa /home/testuser/.ssh/id_rsa;
|
||||
cp /mnt/ssh-keys/id_rsa.pub /home/testuser/.ssh/id_rsa.pub;
|
||||
cat /mnt/ssh-keys/id_rsa.pub > /home/testuser/.ssh/authorized_keys;
|
||||
chmod 700 /home/testuser/.ssh;
|
||||
chmod 600 /home/testuser/.ssh/id_rsa;
|
||||
chmod 644 /home/testuser/.ssh/id_rsa.pub;
|
||||
chmod 600 /home/testuser/.ssh/authorized_keys;
|
||||
chown -R testuser:testuser /home/testuser/.ssh;
|
||||
echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config;
|
||||
echo 'PasswordAuthentication no' >> /etc/ssh/sshd_config;
|
||||
echo 'PubkeyAuthentication yes' >> /etc/ssh/sshd_config;
|
||||
echo 'LogLevel VERBOSE' >> /etc/ssh/sshd_config;
|
||||
echo 'AuthorizedKeysFile .ssh/authorized_keys' >> /etc/ssh/sshd_config;
|
||||
echo 'SyslogFacility AUTH' >> /etc/ssh/sshd_config;
|
||||
mkdir -p /etc/sudoers.d;
|
||||
echo 'testuser ALL=(ALL) NOPASSWD: /usr/bin/fail2ban-client *' > /etc/sudoers.d/fail2ban-ui;
|
||||
echo 'testuser ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart fail2ban' >> /etc/sudoers.d/fail2ban-ui;
|
||||
echo 'testuser ALL=(ALL) NOPASSWD: /usr/bin/systemctl reload fail2ban' >> /etc/sudoers.d/fail2ban-ui;
|
||||
chmod 440 /etc/sudoers.d/fail2ban-ui;
|
||||
mkdir -p /etc/fail2ban/jail.d /etc/fail2ban/filter.d /etc/fail2ban/action.d;
|
||||
setfacl -Rm u:testuser:rwX /etc/fail2ban 2>/dev/null || true;
|
||||
setfacl -dRm u:testuser:rwX /etc/fail2ban 2>/dev/null || true;
|
||||
[ -d /etc/fail2ban/action.d ] && setfacl -m u:testuser:rwX /etc/fail2ban/action.d 2>/dev/null || true;
|
||||
[ -d /etc/fail2ban/filter.d ] && setfacl -m u:testuser:rwX /etc/fail2ban/filter.d 2>/dev/null || true;
|
||||
[ -d /etc/fail2ban/jail.d ] && setfacl -m u:testuser:rwX /etc/fail2ban/jail.d 2>/dev/null || true;
|
||||
[ -d /config/fail2ban/action.d ] && setfacl -m u:testuser:rwX /config/fail2ban/action.d 2>/dev/null || true;
|
||||
[ -d /config/fail2ban/filter.d ] && setfacl -m u:testuser:rwX /config/fail2ban/filter.d 2>/dev/null || true;
|
||||
[ -d /config/fail2ban/jail.d ] && setfacl -m u:testuser:rwX /config/fail2ban/jail.d 2>/dev/null || true;
|
||||
echo '========================================';
|
||||
echo 'SSH Test Container Ready';
|
||||
echo '========================================';
|
||||
echo 'Host: 127.0.0.1';
|
||||
echo 'Port: 2222';
|
||||
echo 'User: testuser';
|
||||
echo 'Key: /config/.ssh/id_rsa (in fail2ban-ui container)';
|
||||
echo 'Test command: podman exec -it DEV_fail2ban-ui ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o BatchMode=yes -i /config/.ssh/id_rsa -p 2222 testuser@127.0.0.1';
|
||||
echo '========================================';
|
||||
/usr/sbin/sshd -D -e &
|
||||
exec /init
|
||||
"
|
||||
|
||||
restart: unless-stopped
|
||||
Reference in New Issue
Block a user