Files
Jordan Ramos 7df2b1075e docs(tinyauth): document .env file solution for "User not found" error
- 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>
2025-12-18 18:12:35 -07:00
..

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

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_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:

# 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:

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:

# 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:

-  - 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:

-    - "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:

  1. Generate bcrypt hash:
htpasswd -nbB jramos YourPassword
# Output: jramos:$2b$05$AbCdEfGhIjKlMnOpQrStUvWxYz0123456789...
  1. Store hash in .env file with $$ escaping:
USERS=jramos:$$2y$$05$$AbCdEfGhIjKlMnOpQrStUvWxYz0123456789...
  1. 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:

  1. Create .env file in /home/jramos/homelab/services/tinyauth/:

    USERS=jramos:$$2y$$05$$CNW/Anbac0mD./ajAepRm.aUvpeAFtOWVrqSxge5wEKZK3yD1.tT.
    
  2. Update docker-compose.yml to reference the variable:

    environment:
      - USERS=${USERS}
    
  3. Restart container:

    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:

# 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

Credential Management

Adding New Users:

  1. Generate bcrypt hash:

    htpasswd -nbB newuser password123
    
  2. Update .env file 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

  3. Restart container:

    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

# 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:

  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:

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:

# 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:

  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
  1. File Permissions:

    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:

  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


Maintained by: Homelab Infrastructure Team Last Updated: 2025-12-18 Status: Operational - User authentication working with .env configuration