- Add Issue #5: "User not found" error resolved with .env file approach - Update Configuration section to show .env as RECOMMENDED method - Document $$ escaping requirement for bcrypt hashes in .env files - Update credential management, troubleshooting, and backup sections - Update status to "User authentication working with .env configuration" The .env file approach eliminates YAML/shell parsing issues with special characters in bcrypt hashes and represents Docker Compose best practice. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
21 KiB
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_requestintegration - 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
- User accesses protected service: Browser requests
https://netbox.apophisnetworking.net - Nginx intercepts: NPM receives request, triggers
auth_request /tinyauth - TinyAuth validation: NPM forwards credentials to TinyAuth's
/api/auth/nginxendpoint - Authentication decision:
- ✅ Valid credentials: TinyAuth returns HTTP 200 → NPM proxies to NetBox
- ❌ Invalid credentials: TinyAuth returns HTTP 401 → NPM redirects to login page
- Login redirect: User sent to
https://tinyauth.apophisnetworking.net/login?redirect_uri=... - 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
USERS=jramos:$$2y$$05$$CNW/Anbac0mD./ajAepRm.aUvpeAFtOWVrqSxge5wEKZK3yD1.tT.
File: /home/jramos/homelab/services/tinyauth/docker-compose.yml
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_hashstored in.envfile - Bcrypt Hash: Generate with
htpasswd -nbB username password, then extract hash portion - Double Dollar Signs: In
.envfiles, use$$to escape dollar signs in bcrypt hashes (e.g.,$$2y$$05$$...) - .env File Security: Set permissions with
chmod 600 .envto 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:
# 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.netreturned HTTP 500 - NPM logs showed Nginx configuration errors
Root Causes:
- Syntax errors in NPM advanced configuration
- Incorrect
proxy_passformat for auth_request subrequest - Missing
internal;directive for/tinyauthlocation
Solution:
- Corrected Nginx syntax in NPM advanced config
- Added
internal;directive to prevent external access to auth endpoint - Verified
proxy_passURL format matches TinyAuth API expectations
Validation:
# 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_URLwas set tohttp://192.168.2.10:8000(IP address)- TinyAuth v4 validates that APP_URL uses a domain name for security
Solution: Changed docker-compose.yml:
- - 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:8000failed
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:
- - "8000:8000"
+ - "8000:3000"
Validation:
# 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:
- Generate bcrypt hash:
htpasswd -nbB jramos YourPassword
# Output: jramos:$2b$05$AbCdEfGhIjKlMnOpQrStUvWxYz0123456789...
- Store hash in
.envfile with$$escaping:
USERS=jramos:$$2y$$05$$AbCdEfGhIjKlMnOpQrStUvWxYz0123456789...
- Restart container:
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:
# 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:
-
Create
.envfile in/home/jramos/homelab/services/tinyauth/:USERS=jramos:$$2y$$05$$CNW/Anbac0mD./ajAepRm.aUvpeAFtOWVrqSxge5wEKZK3yD1.tT. -
Update docker-compose.yml to reference the variable:
environment: - USERS=${USERS} -
Restart container:
cd /home/jramos/homelab/services/tinyauth docker-compose down docker-compose up -d
Why This Works:
.envfiles use different escaping rules than YAML$$in.envfiles 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
.envapproach is Docker Compose's intended method for managing credentials
Validation:
# 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:
-
Generate bcrypt hash:
htpasswd -nbB newuser password123 -
Update
.envfile with USERS variable (comma-separated for multiple users):USERS=jramos:$$2y$$05$$...,alice:$$2y$$05$$...,bob:$$2y$$05$$...Remember: Use
$$(double dollar signs) to escape$in .env files -
Restart container:
cd /home/jramos/homelab/services/tinyauth docker-compose down && docker-compose up -d
Changing Passwords:
- Generate new bcrypt hash with new password
- Replace the hash in
.envfile (remember to use$$for escaping) - 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
# Container logs
docker logs -f tinyauth
# Last 100 lines
docker logs --tail 100 tinyauth
# Authentication attempts
docker logs tinyauth | grep "authentication"
Health Check
# 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
cd /home/jramos/homelab/services/tinyauth
docker-compose restart
Backup
# 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
# 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:
- NPM proxy host for tinyauth.apophisnetworking.net exists and is enabled
- SSL certificate is valid
- TinyAuth container is running:
docker ps | grep tinyauth
Commands:
docker logs nginx-proxy-manager | grep tinyauth
curl -I https://tinyauth.apophisnetworking.net
Symptoms: "Invalid password" or "User not found" error
Check:
.envfile exists in same directory as docker-compose.yml- USERS environment variable uses bcrypt hash with
$$escaping in .env:cat .env - Hash is correctly loaded inside container:
docker exec tinyauth env | grep USERS - Password hasn't changed since hash generation
- Account isn't locked (wait 5 minutes after 5 failed attempts)
Commands:
# 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:
- APP_URL=https://tinyauth.apophisnetworking.net # NOT http://192.168.2.10:8000
Symptoms: Connection timeout to TinyAuth
Check:
- Port mapping is correct (8000:3000):
docker ps | grep tinyauth - Container is listening:
docker exec tinyauth netstat -tlnp - Firewall rules allow port 8000
Symptoms: Authentication works but redirect fails
Check:
redirect_uriparameter in login URL matches original request- NPM advanced config includes
X-Original-URIheader - 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
-
File Permissions:
chmod 600 /home/jramos/homelab/services/tinyauth/.env chmod 600 /home/jramos/homelab/services/tinyauth/docker-compose.yml -
Network Isolation:
- TinyAuth should only be accessible via NPM, not directly exposed
- Consider firewall rules restricting port 8000 to NPM's IP
-
Regular Updates:
- Monitor TinyAuth releases: https://github.com/steveiliop56/tinyauth/releases
- Update Docker image monthly or when security patches released
-
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