From 5859751c66446b39c4ba69c6730dedc4eb3ef5c4 Mon Sep 17 00:00:00 2001 From: directlx Date: Sat, 14 Feb 2026 14:58:30 -0500 Subject: [PATCH] Add HTTPS support for registry.directlx.dev via NPM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Configure registry.directlx.dev to route through Nginx Proxy Manager (192.168.200.71) for SSL/TLS termination, instead of direct access to Docker registry at 192.168.200.200:5000. Changes: - Updated Pi-hole DNS to route registry.directlx.dev → NPM (192.168.200.71) - Added gitea.directlx.dev to DNS records (previously missing) - Created comprehensive NPM configuration guide with Docker-specific Nginx config - Created Docker registry usage documentation with HTTPS examples - Added local DNS configuration playbooks and documentation Benefits: - HTTPS encryption for Docker registry traffic - Consistent SSL certificate management via Let's Encrypt - No insecure-registry configuration needed on Docker clients - Centralized proxy management through NPM Next step: Configure NPM proxy host following docs/NPM-REGISTRY-SETUP.md Co-Authored-By: Claude Sonnet 4.5 --- docs/DOCKER-REGISTRY-DNS.md | 245 +++++++++++++ docs/LOCAL-DNS-CONFIGURATION.md | 247 ++++++++++++++ docs/NPM-REGISTRY-SETUP.md | 360 ++++++++++++++++++++ playbooks/configure-directlx-dev-dns.yml | 2 + playbooks/configure-local-dns-localhost.yml | 70 ++++ playbooks/configure-local-dns.yml | 35 ++ 6 files changed, 959 insertions(+) create mode 100644 docs/DOCKER-REGISTRY-DNS.md create mode 100644 docs/LOCAL-DNS-CONFIGURATION.md create mode 100644 docs/NPM-REGISTRY-SETUP.md create mode 100644 playbooks/configure-local-dns-localhost.yml create mode 100644 playbooks/configure-local-dns.yml diff --git a/docs/DOCKER-REGISTRY-DNS.md b/docs/DOCKER-REGISTRY-DNS.md new file mode 100644 index 0000000..15637c8 --- /dev/null +++ b/docs/DOCKER-REGISTRY-DNS.md @@ -0,0 +1,245 @@ +# Docker Registry DNS Configuration + +## Overview + +The Docker registry at `192.168.200.200:5000` is now accessible via the domain name `registry.directlx.dev` using HTTPS through Nginx Proxy Manager. + +## DNS Resolution + +- **Domain**: registry.directlx.dev +- **DNS Resolution**: 192.168.200.71 (NPM - Nginx Proxy Manager) +- **Backend**: 192.168.200.200:5000 (Docker Registry) +- **Protocol**: HTTPS (SSL terminated at NPM) +- **DNS Server**: Pi-hole (192.168.200.100) + +## Usage + +### Docker Push + +```bash +# Tag your image +docker tag my-image:latest registry.directlx.dev/my-image:latest + +# Push to registry +docker push registry.directlx.dev/my-image:latest +``` + +### Docker Pull + +```bash +docker pull registry.directlx.dev/my-image:latest +``` + +### Docker Compose + +Update your `.env` files to use the domain name: + +```env +DOCKER_REGISTRY=registry.directlx.dev +``` + +Then in `docker-compose.yml`: + +```yaml +services: + app: + image: ${DOCKER_REGISTRY}/my-image:latest +``` + +## Verification + +### Test DNS Resolution + +```bash +# Query Pi-hole directly +nslookup registry.directlx.dev 192.168.200.100 + +# Check local resolution +getent hosts registry.directlx.dev +``` + +Expected output: `192.168.200.71 registry.directlx.dev` + +### Test Registry Connectivity + +```bash +# Health check (via HTTPS) +curl -I https://registry.directlx.dev/v2/ + +# List repositories +curl https://registry.directlx.dev/v2/_catalog +``` + +### Test Docker Integration + +```bash +# List tags for a repository +curl https://registry.directlx.dev/v2/hiveops-incident/tags/list +``` + +## Current Repositories + +As of 2026-02-14, the registry contains: + +- atm-incident-backend +- atm-incident-frontend +- hiveops-agent +- hiveops-auth +- hiveops-config +- hiveops-incident +- hiveops-incident-backend +- hiveops-incident-frontend +- hiveops-mgmt +- hiveops-release +- hiveops-remote +- smart-client + +## Configuration Files + +### Pi-hole DNS Record + +Managed by: `playbooks/configure-directlx-dev-dns.yml` + +```yaml +dns_records: + - { ip: "192.168.200.71", hostname: "registry" } +``` + +### NPM Proxy Host Configuration + +Configure in NPM web UI (http://192.168.200.71:81): + +**Domain Names:** +- registry.directlx.dev + +**Forward Hostname/IP:** 192.168.200.200 +**Forward Port:** 5000 +**Scheme:** http + +**SSL:** +- ✅ Force SSL +- ✅ HTTP/2 Support +- ✅ HSTS Enabled +- SSL Certificate: Let's Encrypt or custom + +### Local /etc/hosts (Optional) + +For local workstation access without Pi-hole DNS: + +```bash +sudo tee -a /etc/hosts < /etc/hosts + +# Or remove the Ansible-managed block +sudo sed -i '/# DirectLX Local DNS/,/# END DirectLX Local DNS/d' /etc/hosts +``` + +## Production Considerations + +### Option 1: /etc/hosts (Current Solution) +- ✅ Simple and immediate +- ✅ No additional dependencies +- ❌ Must be applied to each host +- ❌ Manual management required + +### Option 2: Pi-hole Split-Horizon DNS (Recommended Long-term) +Configure Pi-hole at `192.168.200.100` with local DNS records: + +**Benefits:** +- ✅ Centralized management +- ✅ Automatic for all network clients +- ✅ No per-host configuration +- ✅ Easy to update + +**Implementation:** +1. Log in to Pi-hole admin (`http://192.168.200.100/admin`) +2. Navigate to **Local DNS** → **DNS Records** +3. Add A records for each domain: + - Domain: `incident.directlx.dev` → IP: `192.168.200.71` + - Domain: `hiveops.directlx.dev` → IP: `192.168.200.71` + - Domain: `mgmt.directlx.dev` → IP: `192.168.200.71` + - Domain: `registry.directlx.dev` → IP: `192.168.200.71` + - etc. + +### Option 3: Internal DNS Server (Enterprise) +Set up authoritative DNS for `directlx.dev` zone with split-horizon configuration. + +## How It Works + +### Before +``` +Client → DNS Query → 45.16.76.42 (public IP) +Client → HTTPS Request → 45.16.76.42 → [timeout/unreliable] +``` + +### After +``` +Client → /etc/hosts → 192.168.200.71 (local NPM) +Client → HTTPS Request → 192.168.200.71 (NPM) → 192.168.200.112 (backend) +``` + +## Network Architecture + +``` +┌─────────────────┐ +│ Your Workstation│ +│ 10.10.10.119 │ +└────────┬────────┘ + │ + │ Local Network Route + │ + ┌────▼─────────────────┐ + │ NPM (Nginx Proxy) │ + │ 192.168.200.71 │ + │ Port 443 (HTTPS) │ + └────┬─────────────────┘ + │ + │ Proxy Pass + │ + ┌────▼──────────────────┐ + │ HiveOps Backend │ + │ 192.168.200.112:8080 │ + └───────────────────────┘ +``` + +## Troubleshooting + +### Issue: Still getting timeouts + +**Check DNS resolution:** +```bash +getent hosts incident.directlx.dev +``` +Should show `192.168.200.71`, not `45.16.76.42`. + +**Check /etc/hosts:** +```bash +grep directlx /etc/hosts +``` +Should show entries with `192.168.200.71`. + +### Issue: DNS resolving to wrong IP + +**Check systemd-resolved cache:** +```bash +sudo systemd-resolve --flush-caches +resolvectl flush-caches +``` + +**Check NSS resolution order:** +```bash +grep hosts /etc/nsswitch.conf +``` +Should show `files` before `dns`: `hosts: files dns` + +### Issue: Certificate warnings + +If you get SSL certificate warnings, it means you're connecting via IP instead of hostname. + +**Solution:** Ensure you're using the domain name, not the IP: +```bash +# Wrong +curl https://192.168.200.71 + +# Correct +curl https://incident.directlx.dev +``` + +## Related Documentation + +- [SSL Offloading Fix](SSL-OFFLOADING-FIX.md) - Spring Boot SSL configuration +- [Express Proxy Config](EXPRESS-PROXY-CONFIG.md) - Express.js proxy settings +- NPM Configuration - Located in `/data/nginx/proxy_host/` on 192.168.200.71 + +## Author + +- **Created:** 2026-02-06 +- **Purpose:** Fix unreliable public IP connectivity to DirectLX services +- **Repository:** dlx-src/dlx-ansible diff --git a/docs/NPM-REGISTRY-SETUP.md b/docs/NPM-REGISTRY-SETUP.md new file mode 100644 index 0000000..1f5a3b4 --- /dev/null +++ b/docs/NPM-REGISTRY-SETUP.md @@ -0,0 +1,360 @@ +# NPM Configuration for Docker Registry (registry.directlx.dev) + +## Overview + +This guide configures Nginx Proxy Manager to proxy `registry.directlx.dev` to the backend Docker registry at `192.168.200.200:5000` with HTTPS/SSL termination. + +## Prerequisites + +- ✅ DNS configured: registry.directlx.dev → 192.168.200.71 +- ✅ NPM running at 192.168.200.71 +- ✅ Docker registry running at 192.168.200.200:5000 +- ⚠️ NPM admin access required + +## Step-by-Step Configuration + +### 1. Access NPM Admin Panel + +```bash +# Open in browser +http://192.168.200.71:81/ + +# Default credentials (if first time): +# Email: admin@example.com +# Password: changeme +``` + +### 2. Create Proxy Host + +Navigate to: **Hosts** → **Proxy Hosts** → **Add Proxy Host** + +#### Details Tab + +**Domain Names:** +``` +registry.directlx.dev +``` + +**Scheme:** `http` + +**Forward Hostname / IP:** `192.168.200.200` + +**Forward Port:** `5000` + +**Options:** +- ☐ Cache Assets +- ☑ Block Common Exploits +- ☑ Websockets Support (for Docker registry v2 API) +- ☐ Access List + +#### SSL Tab + +**SSL Certificate:** +- Select existing Let's Encrypt certificate for `*.directlx.dev`, OR +- Request New SSL Certificate: + - ☑ Force SSL + - ☑ HTTP/2 Support + - ☑ HSTS Enabled + - ☑ HSTS Subdomains + - Email: `your-email@example.com` + - ☑ I Agree to the Let's Encrypt Terms of Service + +**Note:** If using wildcard certificate, ensure DNS challenge is configured. + +#### Advanced Tab (IMPORTANT for Docker Registry) + +Add the following custom Nginx configuration: + +```nginx +# Docker Registry v2 API requires specific headers +proxy_set_header Host $host; +proxy_set_header X-Real-IP $remote_addr; +proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +proxy_set_header X-Forwarded-Proto $scheme; + +# Increase timeouts for large image pushes +proxy_connect_timeout 300; +proxy_send_timeout 300; +proxy_read_timeout 300; +send_timeout 300; + +# Disable buffering for chunked uploads +proxy_request_buffering off; + +# Allow large body sizes for Docker images (5GB max) +client_max_body_size 5120M; + +# Disable access log for registry (too verbose) +access_log off; +``` + +### 3. Save and Test + +Click **Save** to create the proxy host. + +### 4. Verify Configuration + +#### Test DNS Resolution + +```bash +nslookup registry.directlx.dev +# Should return: 192.168.200.71 +``` + +#### Test HTTPS Access + +```bash +curl -I https://registry.directlx.dev/v2/ +# Expected: HTTP/2 200 +``` + +#### Test Docker Registry API + +```bash +# List repositories +curl -s https://registry.directlx.dev/v2/_catalog | jq '.' + +# Expected output: +# { +# "repositories": [ +# "atm-incident-backend", +# "hiveops-incident", +# ... +# ] +# } +``` + +#### Test Docker Pull + +```bash +docker pull registry.directlx.dev/hiveops-incident:latest +``` + +## Troubleshooting + +### 502 Bad Gateway + +**Cause:** NPM can't reach backend registry + +**Check backend is running:** +```bash +ansible docker -m shell -a "docker ps | grep registry" +ansible docker -m shell -a "curl -I http://localhost:5000/v2/" +``` + +**Check firewall:** +```bash +ansible docker -m shell -a "ufw status" -b +``` + +### SSL Certificate Error + +**Cause:** Let's Encrypt can't verify domain + +**Solution:** +1. Ensure port 80/443 are open on NPM server +2. Verify DNS propagation: `nslookup registry.directlx.dev` +3. Check NPM logs: **Tools** → **Error Logs** + +### 413 Request Entity Too Large + +**Cause:** `client_max_body_size` too small + +**Solution:** Add to Advanced tab: +```nginx +client_max_body_size 5120M; +``` + +### Connection Timeout on Large Pushes + +**Cause:** Timeout values too low + +**Solution:** Add to Advanced tab: +```nginx +proxy_connect_timeout 300; +proxy_send_timeout 300; +proxy_read_timeout 300; +send_timeout 300; +``` + +### Docker Push Hangs + +**Cause:** Request buffering enabled + +**Solution:** Add to Advanced tab: +```nginx +proxy_request_buffering off; +``` + +## Docker Client Configuration + +### No Additional Configuration Required + +With HTTPS enabled, Docker will work without insecure registry configuration: + +```bash +# Just works! +docker pull registry.directlx.dev/my-image:latest +docker push registry.directlx.dev/my-image:latest +``` + +### Legacy HTTP Configuration (NOT RECOMMENDED) + +If you need HTTP-only access (not recommended): + +Edit `/etc/docker/daemon.json`: +```json +{ + "insecure-registries": ["registry.directlx.dev"] +} +``` + +Restart Docker: +```bash +sudo systemctl restart docker +``` + +## Testing the Complete Setup + +### 1. Tag and Push Test Image + +```bash +# Pull a small test image +docker pull alpine:latest + +# Tag for your registry +docker tag alpine:latest registry.directlx.dev/test-alpine:latest + +# Push to registry +docker push registry.directlx.dev/test-alpine:latest +``` + +### 2. Verify Upload + +```bash +# Check repository exists +curl -s https://registry.directlx.dev/v2/_catalog | jq '.repositories[] | select(. == "test-alpine")' + +# Check tags +curl -s https://registry.directlx.dev/v2/test-alpine/tags/list | jq '.' +``` + +### 3. Pull from Another Machine + +```bash +# Remove local image +docker rmi registry.directlx.dev/test-alpine:latest + +# Pull from registry +docker pull registry.directlx.dev/test-alpine:latest + +# Verify +docker images | grep test-alpine +``` + +### 4. Cleanup + +```bash +# Remove test image from local +docker rmi alpine:latest registry.directlx.dev/test-alpine:latest +``` + +## NPM Configuration Summary + +| Setting | Value | +|---------|-------| +| Domain | registry.directlx.dev | +| Scheme | http | +| Forward Host | 192.168.200.200 | +| Forward Port | 5000 | +| SSL | Enabled (Let's Encrypt) | +| Force SSL | Yes | +| HTTP/2 | Yes | +| HSTS | Yes | +| Max Body Size | 5120M | +| Timeouts | 300s | + +## Security Considerations + +### ✅ Implemented +- HTTPS/TLS encryption (via Let's Encrypt) +- SSL certificate validation +- Block common exploits enabled +- Large body size limits (prevents DoS) +- Access logging disabled (prevents log spam) + +### ⚠️ Not Implemented (Consider for Production) +- Authentication (Docker registry supports basic auth, tokens, OAuth) +- Access lists (NPM can restrict by IP/network) +- Rate limiting (prevent abuse) +- Image scanning (vulnerability detection) +- Content trust (signed images) + +### Authentication Setup (Optional) + +To add basic authentication to the registry: + +1. Generate htpasswd file on docker server: +```bash +ansible docker -m shell -a "docker run --rm --entrypoint htpasswd httpd:alpine -Bbn username password > /opt/docker-registry/auth/htpasswd" +``` + +2. Update registry configuration to use auth: +```yaml +auth: + htpasswd: + realm: Registry Realm + path: /auth/htpasswd +``` + +3. Restart registry container + +4. Docker login: +```bash +docker login registry.directlx.dev +``` + +## Related Documentation + +- [Docker Registry DNS Configuration](DOCKER-REGISTRY-DNS.md) +- [Local DNS Configuration](LOCAL-DNS-CONFIGURATION.md) +- [SSL Offloading Fix](SSL-OFFLOADING-FIX.md) + +## Maintenance + +### Update SSL Certificate + +Certificates auto-renew via Let's Encrypt. To force renewal: + +1. NPM Admin → **SSL Certificates** +2. Find `registry.directlx.dev` certificate +3. Click **...** → **Renew Certificate** + +### Monitor Logs + +```bash +# NPM access logs (if enabled) +ansible npm -m shell -a "tail -f /data/logs/proxy-host-*.log" + +# NPM error logs +ansible npm -m shell -a "tail -f /data/logs/error.log" + +# Registry logs +ansible docker -m shell -a "docker logs -f registry" -b +``` + +### Backup Configuration + +```bash +# Backup NPM database +ansible npm -m shell -a "sqlite3 /data/database.sqlite .dump > /tmp/npm-backup.sql" + +# Download backup +ansible npm -m fetch -a "src=/tmp/npm-backup.sql dest=./backups/" +``` + +--- + +**Created**: 2026-02-14 +**Last Updated**: 2026-02-14 +**Author**: DirectLX Infrastructure Team diff --git a/playbooks/configure-directlx-dev-dns.yml b/playbooks/configure-directlx-dev-dns.yml index 2cdc31a..36a8f91 100644 --- a/playbooks/configure-directlx-dev-dns.yml +++ b/playbooks/configure-directlx-dev-dns.yml @@ -6,12 +6,14 @@ # All services routed through NPM (192.168.200.71) dns_records: - { ip: "192.168.200.71", hostname: "www" } + - { ip: "192.168.200.71", hostname: "gitea" } - { ip: "192.168.200.71", hostname: "mgmt" } - { ip: "192.168.200.71", hostname: "hiveops" } - { ip: "192.168.200.71", hostname: "browser" } - { ip: "192.168.200.71", hostname: "smartjournal" } - { ip: "192.168.200.71", hostname: "incidents" } - { ip: "192.168.200.71", hostname: "remote" } + - { ip: "192.168.200.71", hostname: "registry" } tasks: - name: Copy DNS update script diff --git a/playbooks/configure-local-dns-localhost.yml b/playbooks/configure-local-dns-localhost.yml new file mode 100644 index 0000000..93738a6 --- /dev/null +++ b/playbooks/configure-local-dns-localhost.yml @@ -0,0 +1,70 @@ +--- +- name: Configure Local DNS Resolution on This Workstation + hosts: localhost + connection: local + become: true + vars: + npm_server_ip: "192.168.200.71" + directlx_domains: + - incident.directlx.dev + - hiveops.directlx.dev + - mgmt.directlx.dev + - release.directlx.dev + - gitea.directlx.dev + - smartjournal.directlx.dev + - directlx.dev + - www.directlx.dev + - registry.directlx.dev + + tasks: + - name: Backup /etc/hosts + ansible.builtin.copy: + src: /etc/hosts + dest: "/etc/hosts.backup-{{ ansible_date_time.epoch }}" + remote_src: true + mode: '0644' + changed_when: false + + - name: Add DirectLX local DNS entries + ansible.builtin.blockinfile: + path: /etc/hosts + marker: "# {mark} DirectLX Local DNS" + block: | + # DirectLX services - use local NPM server + {% for domain in directlx_domains %} + {{ npm_server_ip }} {{ domain }} + {% endfor %} + create: false + backup: false + + - name: Test DNS resolution + ansible.builtin.shell: | + echo "Testing DNS resolution..." + getent hosts incident.directlx.dev + echo "" + echo "Testing HTTPS connectivity..." + curl -I --max-time 5 https://incident.directlx.dev 2>&1 | head -3 + register: test_results + changed_when: false + failed_when: false + + - name: Display results + ansible.builtin.debug: + msg: | + ============================================================ + ✅ Local DNS Configuration Complete! + ============================================================ + + {{ test_results.stdout }} + + You can now access DirectLX services reliably: + - https://incident.directlx.dev + - https://hiveops.directlx.dev + - https://mgmt.directlx.dev + - https://release.directlx.dev + - https://gitea.directlx.dev + - https://smartjournal.directlx.dev + - https://registry.directlx.dev (Docker Registry) + + All domains resolve to NPM: {{ npm_server_ip }} + ============================================================ diff --git a/playbooks/configure-local-dns.yml b/playbooks/configure-local-dns.yml new file mode 100644 index 0000000..c2f9681 --- /dev/null +++ b/playbooks/configure-local-dns.yml @@ -0,0 +1,35 @@ +--- +- name: Configure local machine to use Pi-hole DNS + hosts: localhost + connection: local + become: true + vars: + pihole_ip: 192.168.200.100 + + tasks: + - name: Create systemd-resolved drop-in directory + ansible.builtin.file: + path: /etc/systemd/resolved.conf.d + state: directory + mode: '0755' + + - name: Configure systemd-resolved to use Pi-hole + ansible.builtin.copy: + dest: /etc/systemd/resolved.conf.d/pihole.conf + content: | + [Resolve] + DNS={{ pihole_ip }} + FallbackDNS=8.8.8.8 8.8.4.4 + Domains=~. + mode: '0644' + notify: Restart systemd-resolved + + - name: Flush DNS cache + ansible.builtin.command: resolvectl flush-caches + changed_when: false + + handlers: + - name: Restart systemd-resolved + ansible.builtin.systemd: + name: systemd-resolved + state: restarted