- Add Groups field to User struct with JSON storage - Include GetGroups() and SetGroups() helper methods - Extract groups from OIDC claims in FromClaim() - Add database migration 202509161200 for groups column - Update config-example.yaml with groups scope - Add comprehensive documentation and testing
204 lines
8.1 KiB
Markdown
204 lines
8.1 KiB
Markdown
# OIDC Role Mapping Implementation
|
|
|
|
This document describes the complete OIDC role mapping implementation for Headscale and Headplane, enabling enterprise-grade role-based access control through OIDC group claims.
|
|
|
|
## Overview
|
|
|
|
The implementation bridges OIDC group membership with Headplane role assignments, automatically mapping users to appropriate roles based on their group membership in the identity provider (e.g., Keycloak, Azure AD, Okta).
|
|
|
|
## Architecture
|
|
|
|
### 1. Headscale Changes
|
|
|
|
#### Database Schema Updates
|
|
- **New field**: `groups` column added to `users` table
|
|
- **Type**: `TEXT` field storing JSON array of group names
|
|
- **Migration**: `202509161200` adds the column with proper rollback support
|
|
|
|
#### OIDC Integration Enhancement
|
|
- **Groups extraction**: Enhanced `FromClaim()` method to extract and store OIDC groups
|
|
- **Claims processing**: Groups are extracted from both ID token and UserInfo endpoint
|
|
- **Storage**: Groups are persisted as JSON in the database on every login
|
|
|
|
#### Key Files Modified
|
|
- `hscontrol/types/users.go`: Added Groups field and helper methods
|
|
- `hscontrol/oidc.go`: Enhanced claims processing (groups already extracted)
|
|
- `hscontrol/db/db.go`: Added database migration
|
|
|
|
#### Helper Methods Added
|
|
```go
|
|
// GetGroups returns user's groups as a slice of strings
|
|
func (u *User) GetGroups() []string
|
|
|
|
// SetGroups stores user's groups as JSON in database
|
|
func (u *User) SetGroups(groups []string)
|
|
```
|
|
|
|
### 2. Headplane Changes
|
|
|
|
#### Role Mapping System
|
|
- **Function**: `mapOidcGroupsToRole()` maps OIDC groups to Headplane roles
|
|
- **Hierarchy**: Roles are assigned based on highest privilege group membership
|
|
- **Configurable**: Support for custom group-to-role mappings
|
|
|
|
#### Database Schema Updates
|
|
- **New field**: `groups` column added to `users` table (JSON array)
|
|
- **Migration**: `0003_add_groups_column.sql`
|
|
|
|
#### Authentication Flow Enhancement
|
|
- **Groups extraction**: Enhanced `FlowUser` interface to include groups
|
|
- **Role assignment**: Automatic role assignment based on group mapping during login
|
|
- **Persistence**: Groups and capabilities are updated on every login
|
|
|
|
#### Key Files Modified
|
|
- `app/utils/oidc.ts`: Enhanced FlowUser interface and group extraction
|
|
- `app/server/web/roles.ts`: Added group-to-role mapping function
|
|
- `app/routes/auth/oidc-callback.ts`: Updated authentication flow
|
|
- `app/server/db/schema.ts`: Added groups field to schema
|
|
|
|
#### Default Group Mappings
|
|
| Group Pattern | Headplane Role | Capabilities |
|
|
|---------------|----------------|--------------|
|
|
| `owner`, `headplane-owner` | `owner` | Full system access |
|
|
| `admin*`, `headplane-admin` | `admin` | Administrative access |
|
|
| `network*`, `headplane-network` | `network_admin` | Network configuration |
|
|
| `it*`, `headplane-it` | `it_admin` | IT operations |
|
|
| `audit*`, `headplane-audit` | `auditor` | Read-only access |
|
|
| Other groups | `member` | No UI access (zero capabilities) |
|
|
|
|
## Configuration
|
|
|
|
### Headscale OIDC Configuration
|
|
```yaml
|
|
oidc:
|
|
issuer: "https://your-provider.com/realm"
|
|
client_id: "headscale-client"
|
|
client_secret: "your-secret"
|
|
scope: ["openid", "profile", "email", "groups"]
|
|
# Groups will be automatically extracted and stored
|
|
```
|
|
|
|
### Headplane OIDC Configuration
|
|
```yaml
|
|
oidc:
|
|
enabled: true
|
|
issuer_url: "https://your-provider.com/realm"
|
|
client_id: "headplane-client"
|
|
client_secret: "headplane-secret"
|
|
scope: "openid profile email groups"
|
|
|
|
# Optional: Custom group-to-role mappings
|
|
role_mapping:
|
|
owner: ["company-owners", "headplane-owners"]
|
|
admin: ["company-admins", "it-admins"]
|
|
network_admin: ["network-team"]
|
|
auditor: ["audit-team", "compliance"]
|
|
```
|
|
|
|
## Testing Setup
|
|
|
|
### Docker Compose Environment
|
|
The implementation includes a complete testing environment with:
|
|
|
|
- **Keycloak**: OIDC provider with preconfigured realm and test users
|
|
- **PostgreSQL**: Database for Keycloak
|
|
- **Headscale**: Built with OIDC groups support
|
|
- **Headplane**: Configured for role mapping
|
|
|
|
### Test Users
|
|
| Email | Password | Groups | Expected Role |
|
|
|-------|----------|--------|---------------|
|
|
| `owner@example.com` | `password123` | `headscale-owner` | `owner` |
|
|
| `admin@example.com` | `password123` | `headscale-admin` | `admin` |
|
|
| `network@example.com` | `password123` | `headscale-network` | `network_admin` |
|
|
| `auditor@example.com` | `password123` | `headscale-audit` | `auditor` |
|
|
| `member@example.com` | `password123` | `headscale-member` | `member` |
|
|
|
|
### Running Tests
|
|
```bash
|
|
cd docker-dev
|
|
docker compose -f docker-compose-oidc-test.yml up -d
|
|
./test-oidc-roles.sh
|
|
```
|
|
|
|
### Manual Testing
|
|
1. **Keycloak Admin**: http://localhost:8280 (admin/admin)
|
|
2. **Headplane UI**: http://localhost:3000/admin
|
|
3. Test login with different users to verify role assignments
|
|
|
|
## Security Considerations
|
|
|
|
### Capabilities Model
|
|
- **Zero-trust**: New users get `member` role with zero capabilities by default
|
|
- **Explicit mapping**: Only users with recognized groups get elevated privileges
|
|
- **Dynamic updates**: User roles are updated on every login based on current group membership
|
|
|
|
### Group Validation
|
|
- **Sanitization**: Groups are filtered to ensure they are strings
|
|
- **Multiple sources**: Groups extracted from both ID token and UserInfo endpoint
|
|
- **Fallback handling**: Graceful handling when no groups are provided
|
|
|
|
### API Key Management
|
|
- **Per-user keys**: Each OIDC user should have individual API keys (future enhancement)
|
|
- **Shared key limitation**: Current implementation uses shared API key for simplicity
|
|
|
|
## Migration Path
|
|
|
|
### Existing Deployments
|
|
1. **Backup databases**: Both Headscale and Headplane databases
|
|
2. **Deploy Headscale changes**: Run migration `202509161200`
|
|
3. **Deploy Headplane changes**: Run migration `0003_add_groups_column.sql`
|
|
4. **Update configurations**: Add OIDC group scope and role mappings
|
|
5. **Test with non-privileged user**: Verify role mapping works correctly
|
|
|
|
### Rollback Procedure
|
|
1. **Headscale**: Rollback migration removes groups column
|
|
2. **Headplane**: Remove groups column and revert OIDC callback logic
|
|
3. **Configuration**: Remove groups from OIDC scope
|
|
|
|
## Benefits
|
|
|
|
### Enterprise Integration
|
|
- **SSO Compatibility**: Works with any OIDC-compliant provider
|
|
- **Group Synchronization**: Automatic role updates based on identity provider changes
|
|
- **Centralized Management**: User access controlled through existing identity systems
|
|
|
|
### Operational Advantages
|
|
- **Reduced Manual Work**: No manual role assignment required
|
|
- **Dynamic Access**: User permissions update automatically on login
|
|
- **Audit Trail**: Clear mapping between identity provider groups and system roles
|
|
|
|
### Security Improvements
|
|
- **Principle of Least Privilege**: Users get minimum required access
|
|
- **Consistent Enforcement**: Role-based access control across all features
|
|
- **Identity Provider Integration**: Leverage existing security policies
|
|
|
|
## Future Enhancements
|
|
|
|
### Planned Improvements
|
|
1. **Individual API Keys**: Per-user API key generation and management
|
|
2. **Group Hierarchies**: Support for nested group inheritance
|
|
3. **Custom Capabilities**: Fine-grained permission customization per group
|
|
4. **Audit Logging**: Enhanced logging of role assignments and changes
|
|
5. **UI Role Management**: Administrative interface for role mapping configuration
|
|
|
|
### Integration Opportunities
|
|
1. **SCIM Support**: Automatic user provisioning and deprovisioning
|
|
2. **Just-in-Time Access**: Temporary role elevation based on approval workflows
|
|
3. **External Authorization**: Integration with external policy engines (OPA, etc.)
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Issues
|
|
1. **Missing Groups**: Ensure OIDC provider includes groups in claims
|
|
2. **Wrong Roles**: Check group name matching and mapping configuration
|
|
3. **No Access**: Verify user has at least one recognized group
|
|
4. **Token Issues**: Check OIDC scope includes "groups"
|
|
|
|
### Debug Steps
|
|
1. **Check Headscale logs**: Verify groups are being extracted from OIDC claims
|
|
2. **Inspect database**: Verify groups are stored in users table
|
|
3. **Review Headplane logs**: Check role mapping function execution
|
|
4. **Test OIDC flow**: Use OIDC debugging tools to inspect token claims
|
|
|
|
This implementation provides a robust foundation for enterprise OIDC integration while maintaining security and operational efficiency. |