# TinyAuth - SSO Authentication Layer ## Overview TinyAuth is a lightweight, self-hosted authentication service providing Single Sign-On (SSO) capabilities for homelab services. Deployed as a Docker container within LXC CT 115, it acts as a centralized authentication gateway that integrates with Nginx Proxy Manager to protect services like NetBox. **Key Benefits**: - Centralized credential management - Nginx `auth_request` integration - Bcrypt-hashed password storage - Simple, dependency-free deployment - Foundation for extending SSO to multiple services ## Infrastructure Details | Property | Value | |----------|-------| | **Container** | CT 115 (LXC with Docker support) | | **IP Address** | 192.168.2.10 | | **Port** | 8000 (internal), 443 (via NPM) | | **Domain** | tinyauth.apophisnetworking.net | | **Docker Image** | ghcr.io/steveiliop56/tinyauth:v4 | | **Technology** | Go-based authentication service | | **Configuration** | Environment variable-based | | **Deployment Date** | 2025-12-18 | ## Integration Architecture ``` ┌─────────────────────────────────────┐ │ INTERNET │ └──────────────────┬──────────────────┘ │ │ HTTPS ▼ ┌─────────────────────────────────────────────────────────────────────────────┐ │ CT 102 - Nginx Proxy Manager (192.168.2.101) │ │ ┌───────────────────────────────────────────────────────────────────────┐ │ │ │ SSL Termination, Reverse Proxy, auth_request Handler │ │ │ └───────────────────────────────┬───────────────────────────────────────┘ │ └──────────────────────────────────┼──────────────────────────────────────────┘ │ ┌──────────────┴───────────────┐ │ │ ▼ ▼ ┌───────────────────────────┐ ┌───────────────────────────────┐ │ CT 115 - TinyAuth │ │ CT 103 - NetBox │ │ (192.168.2.10:8000) │ │ (192.168.2.104:8000) │ │ │ │ │ │ ┌─────────────────────┐ │ │ ┌─────────────────────────┐ │ │ │ /api/auth/nginx │ │ │ │ NetBox Application │ │ │ │ Authentication │◄─┼──┼──│ (Protected Resource) │ │ │ │ Endpoint │ │ │ │ │ │ │ └─────────────────────┘ │ │ └─────────────────────────┘ │ └───────────────────────────┘ └───────────────────────────────┘ ``` ### Authentication Flow 1. **User accesses protected service**: Browser requests `https://netbox.apophisnetworking.net` 2. **Nginx intercepts**: NPM receives request, triggers `auth_request /tinyauth` 3. **TinyAuth validation**: NPM forwards credentials to TinyAuth's `/api/auth/nginx` endpoint 4. **Authentication decision**: - ✅ **Valid credentials**: TinyAuth returns HTTP 200 → NPM proxies to NetBox - ❌ **Invalid credentials**: TinyAuth returns HTTP 401 → NPM redirects to login page 5. **Login redirect**: User sent to `https://tinyauth.apophisnetworking.net/login?redirect_uri=...` 6. **Post-login**: After successful authentication, user redirected back to original URL ## Configuration ### Docker Compose **File**: `/home/jramos/homelab/services/tinyauth/docker-compose.yml` ```yaml services: tinyauth: container_name: tinyauth image: ghcr.io/steveiliop56/tinyauth:v4 restart: unless-stopped ports: - "8000:3000" # External:Internal (TinyAuth runs on port 3000 internally) environment: - APP_URL=https://tinyauth.apophisnetworking.net - USERS='jramos:$2b$05$...' # Bcrypt-hashed password (MUST be single-quoted) ``` **Critical Configuration Notes**: - **APP_URL**: MUST use the domain name, not an IP address (IP addresses trigger validation errors) - **Port Mapping**: TinyAuth listens on port 3000 internally, exposed as 8000 externally - **USERS Format**: `'username:bcrypt_hash'` - Single quotes prevent shell interpretation of special characters - **Bcrypt Hash**: Generate with `htpasswd -nbB username password`, then extract hash portion ### Nginx Proxy Manager Configuration **Proxy Host**: `netbox.apophisnetworking.net` - **Scheme**: http - **Forward Hostname/IP**: 192.168.2.104 - **Forward Port**: 8000 - **Force SSL**: Enabled - **HTTP/2 Support**: Enabled **Advanced Configuration**: ```nginx # Main location block - protect the entire service location / { proxy_pass $forward_scheme://$server:$port; # Trigger authentication subrequest auth_request /tinyauth; # On authentication failure, redirect to login error_page 401 = @tinyauth_login; } # Internal authentication endpoint location /tinyauth { internal; # Only accessible to nginx (not external requests) proxy_pass http://192.168.2.10:8000/api/auth/nginx; proxy_pass_request_body off; # Don't forward request body to auth endpoint proxy_set_header Content-Length ""; # Forward original request context to TinyAuth proxy_set_header X-Original-URI $request_uri; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Uri $request_uri; } # Login redirect handler location @tinyauth_login { return 302 https://tinyauth.apophisnetworking.net/login?redirect_uri=$scheme://$http_host$request_uri; } ``` **NPM Proxy Host for TinyAuth Itself**: - **Domain**: tinyauth.apophisnetworking.net - **Forward**: http://192.168.2.10:8000 - **Force SSL**: Enabled ## Issues Encountered & Solutions ### Issue #1: 500 Internal Server Error (Initial Deployment) **Symptoms**: - Accessing `netbox.apophisnetworking.net` returned HTTP 500 - NPM logs showed Nginx configuration errors **Root Causes**: 1. Syntax errors in NPM advanced configuration 2. Incorrect `proxy_pass` format for auth_request subrequest 3. Missing `internal;` directive for `/tinyauth` location **Solution**: - Corrected Nginx syntax in NPM advanced config - Added `internal;` directive to prevent external access to auth endpoint - Verified `proxy_pass` URL format matches TinyAuth API expectations **Validation**: ```bash # Check Nginx config syntax docker exec -it nginx-proxy-manager nginx -t # Monitor NPM logs during request docker logs -f nginx-proxy-manager ``` ### Issue #2: "IP addresses not allowed" Error **Symptoms**: - TinyAuth returned: `{"error": "IP addresses not allowed"}` - Login page appeared but validation failed immediately **Root Cause**: - `APP_URL` was set to `http://192.168.2.10:8000` (IP address) - TinyAuth v4 validates that APP_URL uses a domain name for security **Solution**: Changed docker-compose.yml: ```diff - - APP_URL=http://192.168.2.10:8000 + - APP_URL=https://tinyauth.apophisnetworking.net ``` **Why This Matters**: - Security: Prevents session fixation and CSRF attacks - SSL: Ensures proper cookie domain scoping - Production Practice: Domain-based deployments are standard in production ### Issue #3: Port Mapping Confusion **Symptoms**: - Container started successfully but authentication requests timed out - Direct connection to `http://192.168.2.10:8000` failed **Root Cause**: - TinyAuth runs on port 3000 **internally** - Initial port mapping was `8000:8000`, but container wasn't listening on 8000 - Docker port mapping syntax: `host_port:container_port` **Solution**: ```diff - - "8000:8000" + - "8000:3000" ``` **Validation**: ```bash # Verify TinyAuth is accessible curl http://192.168.2.10:8000/api/auth/nginx # Check container port binding docker ps | grep tinyauth # Should show: 0.0.0.0:8000->3000/tcp ``` ### Issue #4: Invalid Password / Authentication Failure **Symptoms**: - Login page loaded correctly - Entering correct credentials returned "Invalid password" - After 5 failed attempts, account locked for 5 minutes **Root Cause**: - TinyAuth v4 requires **bcrypt-hashed passwords**, not plaintext - Initial configuration used `USERS=jramos:Nbkx4md007` (plaintext) - TinyAuth compares bcrypt hash of input against stored hash - plaintext storage fails **Solution**: 1. Generate bcrypt hash: ```bash htpasswd -nbB jramos Nbkx4md007 # Output: jramos:$2b$05$AbCdEfGhIjKlMnOpQrStUvWxYz0123456789... ``` 2. Update docker-compose.yml with **single-quoted hash**: ```yaml - USERS='jramos:$2b$05$AbCdEfGhIjKlMnOpQrStUvWxYz0123456789...' ``` 3. Restart container: ```bash cd /home/jramos/homelab/services/tinyauth docker-compose down docker-compose up -d ``` **Why Single Quotes Matter**: - `$` has special meaning in shell/YAML double-quoted strings - Single quotes prevent variable expansion and preserve the literal bcrypt hash - Without quotes, the hash will be corrupted during environment variable parsing **Validation**: ```bash # Check environment variable is set correctly docker exec tinyauth env | grep USERS # Should show: USERS='jramos:$2b$05$...' # Test authentication curl -u jramos:Nbkx4md007 http://192.168.2.10:8000/api/auth/nginx # Should return HTTP 200 on success ``` ## Access & Credentials ### Login URL - **Primary**: https://tinyauth.apophisnetworking.net/login - **Direct (internal)**: http://192.168.2.10:8000 (not recommended - use NPM-proxied domain) ### Credential Management **Adding New Users**: 1. Generate bcrypt hash: ```bash htpasswd -nbB newuser password123 ``` 2. Update docker-compose.yml USERS variable (comma-separated for multiple users): ```yaml - USERS='jramos:$2b$05$...,alice:$2b$05$...,bob:$2b$05$...' ``` 3. Restart container: ```bash docker-compose down && docker-compose up -d ``` **Changing Passwords**: 1. Generate new bcrypt hash with new password 2. Replace the hash in docker-compose.yml 3. Restart container **Security Note**: Credentials are stored in docker-compose.yml. For production use, consider: - Environment variable injection from secrets management - Integration with LDAP/Active Directory - Migration to more robust SSO (Authelia, Keycloak) ## Maintenance ### Logs ```bash # Container logs docker logs -f tinyauth # Last 100 lines docker logs --tail 100 tinyauth # Authentication attempts docker logs tinyauth | grep "authentication" ``` ### Health Check ```bash # Container status docker ps | grep tinyauth # Authentication endpoint test curl -I http://192.168.2.10:8000/api/auth/nginx # Expected: HTTP 401 (not authenticated) or HTTP 200 (if providing valid creds) ``` ### Restart ```bash cd /home/jramos/homelab/services/tinyauth docker-compose restart ``` ### Backup ```bash # Backup docker-compose.yml (contains credentials) cp docker-compose.yml docker-compose.yml.backup-$(date +%Y%m%d) ``` ### Updates ```bash # Pull latest TinyAuth image docker pull ghcr.io/steveiliop56/tinyauth:v4 # Recreate container with new image docker-compose down docker-compose pull docker-compose up -d ``` ## Troubleshooting ### Symptoms: Login page doesn't load **Check**: 1. NPM proxy host for tinyauth.apophisnetworking.net exists and is enabled 2. SSL certificate is valid 3. TinyAuth container is running: `docker ps | grep tinyauth` **Commands**: ```bash docker logs nginx-proxy-manager | grep tinyauth curl -I https://tinyauth.apophisnetworking.net ``` ### Symptoms: "Invalid password" error **Check**: 1. USERS environment variable uses bcrypt hash: `docker exec tinyauth env | grep USERS` 2. Hash is single-quoted in docker-compose.yml 3. Password hasn't changed since hash generation 4. Account isn't locked (wait 5 minutes after 5 failed attempts) **Commands**: ```bash # Verify hash format docker exec tinyauth env | grep USERS # Test authentication directly curl -u jramos:Nbkx4md007 http://192.168.2.10:8000/api/auth/nginx ``` ### Symptoms: "IP addresses not allowed" **Fix**: Update APP_URL to use domain instead of IP: ```yaml - APP_URL=https://tinyauth.apophisnetworking.net # NOT http://192.168.2.10:8000 ``` ### Symptoms: Connection timeout to TinyAuth **Check**: 1. Port mapping is correct (8000:3000): `docker ps | grep tinyauth` 2. Container is listening: `docker exec tinyauth netstat -tlnp` 3. Firewall rules allow port 8000 ### Symptoms: Authentication works but redirect fails **Check**: 1. `redirect_uri` parameter in login URL matches original request 2. NPM advanced config includes `X-Original-URI` header 3. No extra path manipulation in NPM config ## Performance & Scaling ### Resource Usage - **Memory**: ~50-100 MB - **CPU**: <1% idle, ~2-5% during authentication bursts - **Disk**: ~20 MB (Docker image) - **Network**: Minimal (authentication requests are small) ### Capacity - **Concurrent Users**: Designed for small-scale homelab use (~10-50 users) - **Authentication Latency**: <50ms for local network requests - **Session Management**: Cookie-based, no server-side session storage ### Limitations - **No Multi-Factor Authentication (MFA)**: Consider Authelia for MFA support - **No LDAP/OAuth Integration**: Users managed in environment variables only - **No Audit Logging**: Authentication events logged to container stdout only - **No Rate Limiting**: Beyond the 5-attempt lockout (5 minutes) ## Security Considerations ### Strengths ✅ Bcrypt password hashing (computationally expensive, resists brute force) ✅ HTTPS enforcement via NPM ✅ Account lockout after 5 failed attempts ✅ Minimal attack surface (single authentication endpoint) ✅ No database dependencies (reduces vulnerability vectors) ### Weaknesses & Mitigations ⚠️ **Credentials in docker-compose.yml**: Ensure file permissions restrict read access - Mitigation: `chmod 600 docker-compose.yml` - Future: Migrate to secrets management (Docker Secrets, Vault) ⚠️ **No MFA**: Single-factor authentication only - Mitigation: Use strong, unique passwords - Future: Consider Authelia or Keycloak for MFA ⚠️ **Session fixation risk**: Sessions not explicitly invalidated - Mitigation: Use short session timeouts - Future: Investigate TinyAuth session configuration options ⚠️ **Limited audit logging**: Authentication events not persisted - Mitigation: Forward logs to centralized logging (Loki, via rsyslog) - Future: Integrate with SIEM for security monitoring ### Recommended Hardening 1. **File Permissions**: ```bash chmod 600 /home/jramos/homelab/services/tinyauth/docker-compose.yml ``` 2. **Network Isolation**: - TinyAuth should only be accessible via NPM, not directly exposed - Consider firewall rules restricting port 8000 to NPM's IP 3. **Regular Updates**: - Monitor TinyAuth releases: https://github.com/steveiliop56/tinyauth/releases - Update Docker image monthly or when security patches released 4. **Log Monitoring**: - Configure alerts for repeated authentication failures - Forward logs to Loki (CT 101 - monitoring stack) ## Future Enhancements ### Short-Term - [ ] Add additional users for team access - [ ] Integrate TinyAuth with Grafana for monitoring dashboard authentication - [ ] Configure log forwarding to Loki for centralized authentication auditing - [ ] Document session timeout configuration ### Medium-Term - [ ] Extend authentication to Proxmox web UI (if supported by TinyAuth) - [ ] Implement automated backup of docker-compose.yml to PBS - [ ] Explore TinyAuth API for programmatic user management ### Long-Term - [ ] Evaluate migration to Authelia for MFA support and LDAP integration - [ ] Implement SSO across all homelab services (Gitea, n8n, Proxmox, Grafana) - [ ] Integrate with external identity provider (Google, GitHub OAuth) ## References - **TinyAuth Official Documentation**: https://tinyauth.app/docs/getting-started/ - **TinyAuth GitHub Repository**: https://github.com/steveiliop56/tinyauth - **Nginx auth_request Module**: http://nginx.org/en/docs/http/ngx_http_auth_request_module.html - **Nginx Proxy Manager**: https://nginxproxymanager.com/ - **Bcrypt Algorithm**: https://en.wikipedia.org/wiki/Bcrypt - **NetBox Integration**: `/home/jramos/homelab/services/netbox/README.md` (if exists) --- **Maintained by**: Homelab Infrastructure Team **Last Updated**: 2025-12-18 **Status**: ✅ Operational - Protecting NetBox with SSO authentication