# 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 **✅ RECOMMENDED APPROACH**: Use `.env` file for credential storage This method eliminates YAML/shell parsing issues with special characters in bcrypt hashes and represents Docker Compose best practice for credential management. **File**: `/home/jramos/homelab/services/tinyauth/.env` ```bash USERS=jramos:$$2y$$05$$CNW/Anbac0mD./ajAepRm.aUvpeAFtOWVrqSxge5wEKZK3yD1.tT. ``` **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=${USERS} # References .env file variable ``` **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` stored in `.env` file - **Bcrypt Hash**: Generate with `htpasswd -nbB username password`, then extract hash portion - **Double Dollar Signs**: In `.env` files, use `$$` to escape dollar signs in bcrypt hashes (e.g., `$$2y$$05$$...`) - **.env File Security**: Set permissions with `chmod 600 .env` to restrict access **Why .env File is Recommended**: - ✅ Prevents YAML/shell parsing issues with special characters in bcrypt hashes - ✅ Cleaner separation of secrets from configuration - ✅ Easier to manage multiple users (just edit one variable) - ✅ Avoids quoting complexity in docker-compose.yml - ✅ Standard practice for Docker Compose credential management ### 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 plaintext password storage - TinyAuth compares bcrypt hash of input against stored hash - plaintext storage fails **Solution**: 1. Generate bcrypt hash: ```bash htpasswd -nbB jramos YourPassword # Output: jramos:$2b$05$AbCdEfGhIjKlMnOpQrStUvWxYz0123456789... ``` 2. Store hash in `.env` file with `$$` escaping: ```bash USERS=jramos:$$2y$$05$$AbCdEfGhIjKlMnOpQrStUvWxYz0123456789... ``` 3. Restart container: ```bash cd /home/jramos/homelab/services/tinyauth docker-compose down docker-compose up -d ``` **Why Bcrypt Hash is Required**: - Security: Bcrypt is computationally expensive, resists brute force attacks - Industry Standard: Modern password storage best practice - One-way Hash: Even if .env is compromised, passwords cannot be reversed **Validation**: ```bash # Check environment variable is set correctly inside container docker exec tinyauth env | grep USERS # Should show: USERS=jramos:$2y$05$... (single $ inside container) # Test authentication curl -u jramos:YourPassword http://192.168.2.10:8000/api/auth/nginx # Should return HTTP 200 on success ``` ### Issue #5: "User not found" Error - Resolved with .env File Approach **Symptoms**: - Login page loaded correctly - Valid credentials entered but TinyAuth returned "User not found" - Logs showed: `WRN internal/service/auth_service.go:130 > Local user not found username=jramos` - docker-compose.yml had USERS environment variable configured with bcrypt hash **Root Cause**: - YAML/shell parsing of bcrypt hashes with special characters (`$`) was inconsistent - Even with single quotes in docker-compose.yml, the hash could be corrupted during environment variable expansion - Different YAML parsers handle quoted strings with `$` symbols differently - The quoted string approach created subtle parsing issues that prevented TinyAuth from recognizing the user **Solution**: 1. Create `.env` file in `/home/jramos/homelab/services/tinyauth/`: ```bash USERS=jramos:$$2y$$05$$CNW/Anbac0mD./ajAepRm.aUvpeAFtOWVrqSxge5wEKZK3yD1.tT. ``` 2. Update docker-compose.yml to reference the variable: ```yaml environment: - USERS=${USERS} ``` 3. Restart container: ```bash cd /home/jramos/homelab/services/tinyauth docker-compose down docker-compose up -d ``` **Why This Works**: - `.env` files use different escaping rules than YAML - `$$` in `.env` files escapes to a single `$` in the environment variable - Docker Compose reads .env files automatically and substitutes `${USERS}` with the file content - Eliminates YAML parser ambiguity with special characters - The `.env` approach is Docker Compose's intended method for managing credentials **Validation**: ```bash # Verify .env file exists and has correct format cat /home/jramos/homelab/services/tinyauth/.env # Should show: USERS=jramos:$$2y$$05$$... # Verify environment variable is correct inside container docker exec tinyauth env | grep USERS # Should show: USERS=jramos:$2y$05$... (single $ inside container) # Test authentication curl -u jramos:YourPassword http://192.168.2.10:8000/api/auth/nginx # Should return HTTP 200 ``` **✅ This is now the RECOMMENDED configuration method** - see Configuration section above. ## 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 `.env` file with USERS variable (comma-separated for multiple users): ```bash USERS=jramos:$$2y$$05$$...,alice:$$2y$$05$$...,bob:$$2y$$05$$... ``` **Remember**: Use `$$` (double dollar signs) to escape `$` in .env files 3. Restart container: ```bash cd /home/jramos/homelab/services/tinyauth docker-compose down && docker-compose up -d ``` **Changing Passwords**: 1. Generate new bcrypt hash with new password 2. Replace the hash in `.env` file (remember to use `$$` for escaping) 3. Restart container **Security Note**: Credentials are stored in `.env` file. For production use, consider: - Set file permissions: `chmod 600 .env` - Environment variable injection from secrets management (Docker Secrets, Vault) - 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 .env file (contains credentials) - CRITICAL cp .env .env.backup-$(date +%Y%m%d) # Backup docker-compose.yml 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 cd /home/jramos/homelab/services/tinyauth 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" or "User not found" error **Check**: 1. `.env` file exists in same directory as docker-compose.yml 2. USERS environment variable uses bcrypt hash with `$$` escaping in .env: `cat .env` 3. Hash is correctly loaded inside container: `docker exec tinyauth env | grep USERS` 4. Password hasn't changed since hash generation 5. Account isn't locked (wait 5 minutes after 5 failed attempts) **Commands**: ```bash # Verify .env file exists and has correct format cat /home/jramos/homelab/services/tinyauth/.env # Should show: USERS=jramos:$$2y$$05$$... # Verify hash format inside container (single $, not double) docker exec tinyauth env | grep USERS # Should show: USERS=jramos:$2y$05$... (single $ inside container) # Test authentication directly curl -u jramos:YourPassword http://192.168.2.10:8000/api/auth/nginx # Should return HTTP 200 on success ``` ### 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 .env file**: Ensure file permissions restrict read access - Mitigation: `chmod 600 .env` - 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/.env 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 (VM 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 .env to Proxmox Backup Server - [ ] 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 - User authentication working with .env configuration