diff --git a/README.md b/README.md index 3db110b..e8dafbe 100644 --- a/README.md +++ b/README.md @@ -1,96 +1,53 @@ # πŸ” Passkey Auth for Kubernetes Nginx Ingress -A WebAuthn-based passkey authentication provider that integrates seamlessly with Kubernetes nginx ingress controllers. This service provides secure, passwordless authentication using passkeys (FIDO2/WebAuthn) and acts as an auth backend for nginx ingress. +A WebAuthn-based passkey authentication provider that integrates ingress controllers, currently support Kubernetes Nginx Ingress controller. Provides secure, passwordless authentication using passkeys (FIDO2/WebAuthn) as an auth backend for nginx ingress. ## ✨ Features -- **Pa2. **"User not found" during login**: - - Ensure user is registered and approved (if required) - - Check that email address matches exactly +- **Passwordless Authentication**: Uses WebAuthn/FIDO2 passkeys for secure authentication +- **Email-Based Access Control**: Users identified by email with configurable allowlists +- **Nginx Ingress Integration**: Works as auth backend using nginx `auth_request` directive +- **User Management**: An simple Admin interface for managing users and approval status +- **Kubernetes Native**: Designed for Kubernetes deployment with persistent storage -3. **WebAuthn errors**:dless Authentication**: Uses WebAuthn/FIDO2 passkeys for secure authentication -- **Email-Based Access Control**: Users are identified by email addresses with configurable allowlists -- **Nginx Ingress Integration**: Works as an auth backend using nginx `auth_request` directive -- **User Management**: Admin interface for managing users and their approval status -- **Access Control**: Configure allowed email addresses and approval requirements -- **Kubernetes Native**: Designed specifically for Kubernetes deployment -- **Persistent Storage**: Uses SQLite with persistent volumes for data storage -- **Modern UI**: Clean, responsive web interface for user registration and management +## Security Benefits -## πŸ—οΈ Architecture - -``` -β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” -β”‚ User Browser β”‚ β”‚ Nginx Ingress β”‚ β”‚ Your App β”‚ -β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ -β”‚ 1. Request │───▢│ 2. Auth Check │───▢│ 4. Serve App β”‚ -β”‚ 4. Redirect │◀───│ 3. 401/302 β”‚ β”‚ β”‚ -β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ - β”‚ - β”‚ auth_request - β–Ό - β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” - β”‚ Passkey Auth β”‚ - β”‚ β”‚ - β”‚ - WebAuthn β”‚ - β”‚ - User Mgmt β”‚ - β”‚ - Session Mgmt β”‚ - β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ -``` +- **No passwords stored** - Only WebAuthn public keys +- **Email-based access control** - Restrict registration to specific domains/emails +- **Phishing resistant** - WebAuthn is tied to the domain +- **MFA built-in** - Passkeys require user presence and verification +- **Session security** - Secure cookie-based sessions ## πŸš€ Quick Start -### Prerequisites - -- Kubernetes cluster with nginx ingress controller -- Docker -- kubectl configured to access your cluster - -### 1. Clone and Build +### 1. Build and Test Locally ```bash git clone cd passkey-auth -# Build the Docker image -./scripts/build.sh +docker-compose up ``` ### 2. Configure -Edit `k8s/deployment.yaml` to update: +Edit `k8s/deployment.yaml` to update your domain: ```yaml -# Update these values in the ConfigMap webauthn: - rp_id: "your-domain.com" # Your domain + rp_id: "your-domain.com" rp_origins: - - "https://your-domain.com" # Your domain with protocol + - "https://your-domain.com" cors: allowed_origins: - - "https://your-domain.com" # Your domain with protocol - # Wildcard domains are supported for subdomains: - - "*.your-domain.com" # Matches api.your-domain.com, app.your-domain.com, etc. + - "https://your-domain.com" auth: - session_secret: "your-secure-secret-key" # Generate a secure random string + session_secret: "your-secure-secret-key" ``` -### 3. Deploy to Kubernetes - -```bash -# Deploy the passkey auth service -./scripts/deploy.sh - -# Verify deployment -kubectl get pods -n passkey-auth -kubectl logs -f deployment/passkey-auth -n passkey-auth -``` - -### 4. Configure Your App's Ingress - -Update your application's ingress to use passkey auth: +### 3. Setup Your App's Ingress ```yaml apiVersion: networking.k8s.io/v1 @@ -98,14 +55,9 @@ kind: Ingress metadata: name: your-app-ingress annotations: - # Auth backend - points to passkey auth service - nginx.ingress.kubernetes.io/auth-url: "http://passkey-auth-service.passkey-auth.svc.cluster.local/auth" - # Redirect unauthorized users to login page - nginx.ingress.kubernetes.io/auth-signin: "https://your-domain.com/auth" - - # Pass user info to your app - nginx.ingress.kubernetes.io/auth-response-headers: "X-Auth-User,X-Auth-User-ID" + nginx.ingress.kubernetes.io/auth-url: "https://your-passkey-auth.com/auth" + nginx.ingress.kubernetes.io/auth-signin: "https://your-passkey-auth.com/?redirect=https%3A%2F%2F$host$request_uri" spec: rules: - host: your-app.com @@ -120,129 +72,39 @@ spec: number: 80 ``` -### 5. Create Ingress for Passkey Auth - -```yaml -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: passkey-auth-ingress - namespace: passkey-auth -spec: - rules: - - host: your-domain.com - http: - paths: - - path: /auth - pathType: Prefix - backend: - service: - name: passkey-auth-service - port: - number: 80 -``` - -Apply the ingress: - -```bash -kubectl apply -f k8s/ingress-example.yaml -``` ## πŸ‘₯ User Management -### Access the Admin Interface +Navigate to `https:///your-passkey-auth.com` to access the admin interface with three tabs: +- **Register User**: Register new users with passkeys +- **Test Login**: Test authentication +- **Manage Users**: View and manage all users with `ADMIN_USER` email address -1. Navigate to `https://your-domain.com/auth` in your browser -2. You'll see the admin dashboard with three tabs: - - **Register User**: Register new users with passkeys - - **Test Login**: Test authentication - - **Manage Users**: View and manage all users - -### User Registration Flow - -1. **Register**: Admin enters email address and display name -2. **Email Validation**: System checks if email is in the allowlist (if configured) -3. **Passkey Creation**: Browser prompts for passkey creation (TouchID, Windows Hello, etc.) -4. **Approval**: If `require_approval` is enabled, admin must approve users manually -5. **Authentication**: Users can now authenticate with their passkeys - -### User Approval Process - -When `require_approval` is enabled in the configuration: - -1. **New users register** but cannot authenticate until approved -2. **Admin reviews pending users** in the "Manage Users" tab -3. **Admin clicks "Approve"** next to pending users -4. **Users can now authenticate** with their passkeys - -**To approve a user:** -1. Navigate to the admin interface at `https://your-domain.com/auth` -2. Click the "Manage Users" tab -3. Find users with "Pending" status -4. Click the "Approve" button next to the user -5. Confirm the approval in the dialog - -**User Status Indicators:** -- 🟒 **Approved**: User can authenticate -- 🟑 **Pending**: User registered but needs approval - -### Configuration Options - -In `config.yaml` or environment variables: +### Configuration ```yaml +# config.yaml auth: require_approval: true # Require admin approval for new users - session_secret: "secret" # Session encryption key allowed_emails: # Email allowlist (empty = allow all) - "admin@company.com" - "user@company.com" ``` - -Environment variable overrides: -- `WEBAUTHN_RP_ID`: WebAuthn Relying Party ID (your domain) -- `SESSION_SECRET`: Session encryption secret -- `DATABASE_PATH`: SQLite database file path -- `PORT`: Server port (default: 8080) -- `ALLOWED_EMAILS`: Comma-separated list of allowed emails +Check [config.example.yaml](./config.example.yaml) for more details ## πŸ”§ Development ### Local Development -1. **Install Go dependencies**: ```bash +# Install dependencies and run locally go mod download -``` - -2. **Run locally**: -```bash -# Update config.yaml for local development go run main.go + +# Access at http://localhost:8080 ``` -3. **Access the interface**: -``` -http://localhost:8080 -``` - -### Project Structure - -``` -passkey-auth/ -β”œβ”€β”€ main.go # Application entry point -β”œβ”€β”€ internal/ -β”‚ β”œβ”€β”€ auth/ # WebAuthn implementation -β”‚ β”œβ”€β”€ config/ # Configuration management -β”‚ β”œβ”€β”€ database/ # SQLite database layer -β”‚ └── handlers/ # HTTP handlers -β”œβ”€β”€ web/ # Static web files -β”œβ”€β”€ k8s/ # Kubernetes manifests -β”œβ”€β”€ scripts/ # Deployment scripts -└── config.yaml # Configuration file -``` - -### API Endpoints +### Key API Endpoints | Endpoint | Method | Description | |----------|--------|-------------| @@ -250,169 +112,11 @@ passkey-auth/ | `/api/register/finish` | POST | Complete passkey registration | | `/api/login/begin` | POST | Start passkey authentication | | `/api/login/finish` | POST | Complete passkey authentication | -| `/api/logout` | POST | Logout user | | `/auth` | GET | Nginx auth check endpoint | -| `/api/users` | GET | List all users (admin) | -| `/api/users` | POST | Create new user (admin) | -| `/api/users/{id}` | PUT | Update user (approve/admin) | -| `/api/users/{id}` | DELETE | Delete user (admin) | +| `/api/users` | GET/POST | List/create users | | `/health` | GET | Health check | -## πŸ”’ Security Considerations - -### Production Deployment - -1. **HTTPS Only**: Always use HTTPS in production -2. **Secure Session Secret**: Use a strong, random session secret -3. **Domain Configuration**: Ensure `rp_id` matches your domain exactly -4. **Network Security**: Use Kubernetes network policies to restrict access -5. **Regular Backups**: Backup the SQLite database regularly - -### Session Configuration - -```yaml -# Secure session configuration for production -auth: - session_secret: "your-256-bit-random-key" # Use a proper secret manager -``` - -### Database Security - -The SQLite database contains: -- User information (email addresses, display names) -- WebAuthn credentials (public keys, metadata) -- No passwords or private keys are stored - -## πŸ” Email Access Control - -### Allowlist Configuration - -You can control who can register by configuring an email allowlist: - -```yaml -# config.yaml -auth: - allowed_emails: - - "admin@yourcompany.com" - - "engineering@yourcompany.com" - - "support@yourcompany.com" -``` - -Or via environment variable: -```bash -export ALLOWED_EMAILS="admin@company.com,user1@company.com,user2@company.com" -``` - -### Access Control Options - -1. **Open Registration** (allowed_emails is empty or not set): - - Any email address can register - - Suitable for internal/trusted environments - -2. **Allowlist Mode** (allowed_emails configured): - - Only specified email addresses can register - - Recommended for production environments - -3. **Combined with Approval**: - - Users must be in allowlist AND get admin approval - - Maximum security for sensitive applications - -## πŸ“Š Monitoring - -### Health Checks - -The service exposes a health endpoint at `/health`: - -```bash -curl http://passkey-auth-service.passkey-auth.svc.cluster.local/health -``` - -### Logs - -View application logs: - -```bash -kubectl logs -f deployment/passkey-auth -n passkey-auth -``` - -### Metrics - -For production, consider adding metrics collection: -- Authentication success/failure rates -- User registration rates -- Session duration statistics - -## πŸ› Troubleshooting - -### Common Issues - -1. **Docker build failures with SQLite CGO errors**: - - The Dockerfile uses Debian-based images (golang:1.21-bullseye) instead of Alpine - - This resolves musl vs glibc compatibility issues with go-sqlite3 - - If you encounter `pread64` or `pwrite64` errors, ensure you're using a glibc-based image - -2. **WebAuthn encoding errors** (challenge not ArrayBuffer): - - The web interface includes base64url conversion functions - - Ensure you're using the included HTML file, not a custom frontend - - Binary WebAuthn data must be converted between base64url and ArrayBuffer - -3. **"User not found" during login**: - - Ensure user is registered and approved (if required) - - Check that username matches exactly - -4. **WebAuthn errors**: - - Verify `rp_id` matches your domain - - Ensure HTTPS is used (required for WebAuthn) - - Check browser support for WebAuthn - -5. **Auth backend not working**: - - Verify ingress annotations are correct - - Check that the auth service is accessible from nginx - - Review nginx ingress controller logs - -6. **Session issues**: - - Ensure session secret is consistent - - Check cookie settings (secure flag for HTTPS) - - Verify session storage is persistent - -### Debug Commands - -```bash -# Check pod status -kubectl get pods -n passkey-auth - -# View logs -kubectl logs deployment/passkey-auth -n passkey-auth - -# Check service -kubectl get svc -n passkey-auth - -# Test auth endpoint -kubectl exec -it deployment/passkey-auth -n passkey-auth -- wget -O- http://localhost:8080/health - -# Port forward for local testing -kubectl port-forward svc/passkey-auth-service 8080:80 -n passkey-auth -``` - -## 🀝 Contributing - -1. Fork the repository -2. Create a feature branch -3. Make your changes -4. Add tests if applicable -5. Submit a pull request ## πŸ“„ License Apache License 2.0 - -## πŸ™ Acknowledgments - -- [go-webauthn](https://github.com/go-webauthn/webauthn) - WebAuthn library for Go -- [Gorilla](https://github.com/gorilla) - HTTP utilities for Go -- WebAuthn/FIDO2 specifications -- Kubernetes and nginx ingress controller teams - ---- - -For support or questions, please create an issue in the repository. diff --git a/scripts/build.sh b/scripts/build.sh deleted file mode 100755 index 696c961..0000000 --- a/scripts/build.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash - -set -e - -echo "πŸš€ Building Passkey Auth..." - -# Build Docker image -echo "Building Docker image..." -docker build -t passkey-auth:latest . - -echo "βœ… Build complete!" -echo "" -echo "Next steps:" -echo "1. Update k8s/deployment.yaml with your domain and session secret" -echo "2. Run ./scripts/deploy.sh to deploy to Kubernetes" -echo "3. Configure your nginx ingress to use passkey auth" diff --git a/scripts/deploy.sh b/scripts/deploy.sh deleted file mode 100644 index 415b5bb..0000000 --- a/scripts/deploy.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash - -set -e - -echo "πŸš€ Deploying Passkey Auth to Kubernetes..." - -# Check if kubectl is available -if ! command -v kubectl &> /dev/null; then - echo "❌ kubectl is not installed or not in PATH" - exit 1 -fi - -# Create namespace -echo "Creating namespace..." -kubectl apply -f k8s/namespace.yaml - -# Deploy the application -echo "Deploying application..." -kubectl apply -f k8s/deployment.yaml - -# Wait for deployment to be ready -echo "Waiting for deployment to be ready..." -kubectl wait --for=condition=available --timeout=300s deployment/passkey-auth -n passkey-auth - -echo "βœ… Deployment complete!" -echo "" -echo "To check the status:" -echo " kubectl get pods -n passkey-auth" -echo "" -echo "To view logs:" -echo " kubectl logs -f deployment/passkey-auth -n passkey-auth" -echo "" -echo "To configure nginx ingress, see k8s/ingress-example.yaml" diff --git a/scripts/dev.sh b/scripts/dev.sh deleted file mode 100755 index 7d5f1cc..0000000 --- a/scripts/dev.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash - -set -e - -echo "πŸ”§ Starting Passkey Auth in development mode..." - -# Check if Go is installed -if ! command -v go &> /dev/null; then - echo "❌ Go is not installed" - exit 1 -fi - -# Install dependencies -echo "Installing dependencies..." -go mod download - -# Set development environment variables -export PORT=8080 -export WEBAUTHN_RP_ID=localhost -export DATABASE_PATH=./dev-passkey-auth.db -export SESSION_SECRET=dev-secret-not-for-production - -echo "πŸš€ Starting server..." -echo "Access the admin interface at: http://localhost:8080" -echo "Press Ctrl+C to stop" - -# Run the application -go run main.go diff --git a/scripts/github-setup.sh b/scripts/github-setup.sh deleted file mode 100755 index 401dd7b..0000000 --- a/scripts/github-setup.sh +++ /dev/null @@ -1,77 +0,0 @@ -#!/bin/bash - -# GitHub Repository Setup Script -# Run this script after creating the repository on GitHub - -set -e - -echo "πŸš€ Passkey Auth - GitHub Repository Setup" -echo "==========================================" - -# Check if we're in a git repository -if [ ! -d ".git" ]; then - echo "❌ Error: Not in a git repository. Run this from the project root." - exit 1 -fi - -# Check for uncommitted changes -if ! git diff-index --quiet HEAD --; then - echo "⚠️ Warning: You have uncommitted changes." - echo "Please commit or stash them before proceeding." - exit 1 -fi - -# Prompt for GitHub username and repository name -read -p "Enter your GitHub username: " GITHUB_USERNAME -read -p "Enter the repository name (default: passkey-auth): " REPO_NAME -REPO_NAME=${REPO_NAME:-passkey-auth} - -# Set the repository URL -REPO_URL="https://github.com/${GITHUB_USERNAME}/${REPO_NAME}.git" - -echo "" -echo "πŸ“‹ Repository Details:" -echo " Username: ${GITHUB_USERNAME}" -echo " Repository: ${REPO_NAME}" -echo " URL: ${REPO_URL}" -echo "" - -# Confirm before proceeding -read -p "Do you want to proceed? (y/N): " CONFIRM -if [[ ! $CONFIRM =~ ^[Yy]$ ]]; then - echo "Aborted." - exit 1 -fi - -echo "" -echo "βš™οΈ Setting up remote repository..." - -# Add remote origin -if git remote get-url origin >/dev/null 2>&1; then - echo "πŸ“ Updating existing origin remote..." - git remote set-url origin "${REPO_URL}" -else - echo "πŸ“ Adding origin remote..." - git remote add origin "${REPO_URL}" -fi - -# Set upstream branch and push -echo "πŸ“€ Pushing to GitHub..." -git branch -M main -git push -u origin main - -echo "" -echo "βœ… Success! Your repository has been pushed to GitHub." -echo "" -echo "πŸ”— Repository URL: https://github.com/${GITHUB_USERNAME}/${REPO_NAME}" -echo "" -echo "πŸ“‹ Next Steps:" -echo " 1. Visit your repository on GitHub" -echo " 2. Add repository description and topics" -echo " 3. Configure branch protection rules (optional)" -echo " 4. Set up GitHub Pages for documentation (optional)" -echo " 5. Configure secrets for GitHub Actions:" -echo " - DOCKER_USERNAME (for Docker Hub publishing)" -echo " - DOCKER_PASSWORD (for Docker Hub publishing)" -echo "" -echo "πŸŽ‰ Your open source project is now live!" diff --git a/scripts/test.sh b/scripts/test.sh deleted file mode 100755 index 4243159..0000000 --- a/scripts/test.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/bash - -set -e - -echo "πŸ§ͺ Testing Passkey Auth Build..." - -# Clean previous builds -rm -f bin/passkey-auth - -# Test build -echo "Building application..." -go build -o bin/passkey-auth . - -if [ -f "bin/passkey-auth" ]; then - echo "βœ… Build successful!" - echo "πŸ“¦ Binary size: $(du -h bin/passkey-auth | cut -f1)" -else - echo "❌ Build failed!" - exit 1 -fi - -# Test basic functionality -echo "" -echo "πŸ” Testing basic configuration..." - -# Create test config -cat > test-config.yaml << EOF -server: - port: "8080" - host: "localhost" - -webauthn: - rp_display_name: "Test Auth" - rp_id: "localhost" - rp_origins: - - "http://localhost:8080" - -database: - path: "test.db" - -cors: - allowed_origins: - - "*" - -auth: - session_secret: "test-secret" - require_approval: false - allowed_emails: - - "test@example.com" - - "admin@example.com" -EOF - -echo "βœ… Test configuration created" - -# Clean up -rm -f test-config.yaml test.db - -echo "" -echo "πŸŽ‰ All tests passed!" -echo "" -echo "To run the application:" -echo " ./bin/passkey-auth" -echo "" -echo "To run in development mode:" -echo " ./scripts/dev.sh" diff --git a/scripts/undeploy.sh b/scripts/undeploy.sh deleted file mode 100644 index d93ee7c..0000000 --- a/scripts/undeploy.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -set -e - -echo "🧹 Undeploying Passkey Auth from Kubernetes..." - -# Delete the application -echo "Deleting application..." -kubectl delete -f k8s/deployment.yaml --ignore-not-found=true - -# Delete namespace (this will also delete PVC - data will be lost!) -read -p "⚠️ This will delete all data. Are you sure? (y/N): " -n 1 -r -echo -if [[ $REPLY =~ ^[Yy]$ ]]; then - kubectl delete -f k8s/namespace.yaml --ignore-not-found=true - echo "βœ… Undeployment complete!" -else - echo "❌ Cancelled" -fi