Securing Internal Services: Using HAProxy ACLs and AdGuard Home DNS Rewrites

Learn how to restrict sensitive services like monitoring dashboards and admin panels to your internal network using HAProxy access control lists combined with AdGuard Home DNS rewrites giving your users friendly domain names without exposing services to the internet.

4 min read
Securing Internal Services: Using HAProxy ACLs and AdGuard Home DNS Rewrites

Not Everything Needs to Be Public

When running a homelab or small business infrastructure, you often host multiple services behind a reverse proxy. While some services like your blog or public-facing applications need to be accessible from anywhere, others like monitoring dashboards, internal tools, or administrative interfaces should only be available to your internal network.

The challenge? You still want internal users to access these services using friendly domain names (like grafana.example.com or admin.example.com) without exposing them to the entire internet.

A Two-Layer Security Approach

The solution combines two powerful tools that many homelabbers already use:

  1. HAProxy ACLs - Block external access at the reverse proxy level
  2. AdGuard Home DNS Rewrites - Route internal requests directly to your load balancer

This creates a defense-in-depth strategy where even if someone tried to access your internal services from outside, they'd be denied at the proxy level.

Architecture Overview

Here's how the traffic flows:

External Request:

Internet → Public DNS → HAProxy (403 Forbidden) ✗

Internal Request:

Internal User → AdGuard Home → HAProxy (Internal IP) → Service ✓

Implementation: Step-by-Step

Step 1: Configure HAProxy Access Control Lists

In your HAProxy configuration, add network-based restrictions to your HTTPS frontend. Here's what you need to add:

frontend https_frontend
    bind *:443 ssl crt /etc/haproxy/certs/ alpn h2,http/1.1
    
    # ... existing configuration ...
    
    # Define your internal network ACL
    acl internal_network src 192.168.0.0/24
    
    # Define which hosts should be internal-only
    acl host_grafana hdr(host) -i grafana.example.com
    acl host_tesla hdr(host) -i admin.example.com
    
    # Block external access (must come before routing rules)
    http-request deny deny_status 403 if host_grafana !internal_network
    http-request deny deny_status 403 if host_tesla !internal_network
    
    # ... routing rules ...
    use_backend grafana_backend if host_grafana
    use_backend tesla_backend if host_tesla

What this does:

  • Creates an ACL matching your internal subnet (adjust to your network)
  • Denies any request to specified hosts that doesn't come from the internal network
  • Returns a clean 403 Forbidden to external requests

Step 2: Configure AdGuard Home DNS Rewrites

In AdGuard Home, add DNS rewrites for your internal-only services:

  1. Navigate to Filters → DNS rewrites
  2. Add entries for each internal service:
grafana.example.com → 192.168.0.X (your HAProxy IP)
admin.example.com → 192.168.0.X (your HAProxy IP)

What this does:

  • Internal devices using AdGuard Home as their DNS server will resolve these FQDNs to your internal HAProxy IP
  • External DNS remains unchanged (still points to your public IP)
  • Requests never leave your network, improving both security and performance

Step 3: Test and Verify

After configuration, test both scenarios:

Test 1: Internal Access

# From internal network
curl -I https://grafana.example.com
# Should return: HTTP/2 200 OK

Test 2: External Access

# From external network (or using mobile data)
curl -I https://grafana.example.com
# Should return: HTTP/2 403 Forbidden

Test 3: DNS Resolution

# From internal network
nslookup grafana.example.com
# Should return: 192.168.0.X (internal IP)

Step 4: Apply Changes

Don't forget to reload HAProxy to apply the configuration:

# Test configuration first
haproxy -c -f /etc/haproxy/haproxy.cfg

# Reload if test passes
systemctl reload haproxy

Security Benefits

This approach provides multiple layers of security:

  1. Network Segmentation: Services are only accessible from trusted networks
  2. Defense in Depth: Even if DNS is compromised, HAProxy blocks unauthorized access
  3. Performance: Internal traffic stays internal, reducing latency
  4. Audit Trail: HAProxy logs all denied external attempts
  5. Flexibility: Easy to adjust which services are internal vs. public

Common Use Cases

This technique is perfect for:

  • Monitoring Dashboards (Grafana, Prometheus, Uptime Kuma)
  • Administrative Interfaces (Portainer, Traefik dashboards)
  • Development Tools (GitLab, CI/CD pipelines)
  • Personal Services (Password managers, note-taking apps)
  • IoT Control Panels (Home Assistant, Node-RED)
  • Internal APIs (Automation webhooks, integration services)

Advanced Configurations

Multiple Network Ranges

If you have multiple trusted networks (home, VPN, office):

acl internal_network src 192.168.0.0/24
acl vpn_network src 10.8.0.0/24
acl office_network src 172.16.0.0/24

acl trusted_networks src 192.168.0.0/24 10.8.0.0/24 172.16.0.0/24

http-request deny deny_status 403 if host_grafana !trusted_networks

Custom Deny Page

Instead of a simple 403, serve a custom error page:

http-request deny deny_status 403 if host_grafana !internal_network
http-response set-header Content-Type text/html if { status 403 }
errorfile 403 /etc/haproxy/errors/403-custom.http

IP Whitelist for Specific Services

For services that need selective external access:

acl admin_ips src 203.0.113.10 203.0.113.20
acl internal_network src 192.168.0.0/24

http-request deny deny_status 403 if host_special_admin !internal_network !admin_ips

Best Practices

  1. Document Your Internal Services: Keep a list of which services are internal-only
  2. Regular Audits: Review your ACLs periodically to ensure they're still appropriate
  3. Monitor Denied Requests: Set up alerts for repeated 403s (potential attack indicators)
  4. Use VPN for Remote Access: For remote access to internal services, use WireGuard or similar
  5. Test Both Paths: Always verify both internal and external access after changes
  6. Backup Configurations: Keep versioned backups of both HAProxy and AdGuard configs

Troubleshooting

Problem: Internal users getting 403 errors

Solutions:

  • Verify client IP is in the allowed subnet
  • Check HAProxy logs: tail -f /var/log/haproxy.log
  • Confirm DNS rewrite is working: nslookup service.domain.com

Problem: External access still works

Solutions:

  • Verify ACL is placed BEFORE routing rules
  • Check if requests are coming through a CDN/proxy
  • Reload HAProxy configuration

Problem: DNS not resolving internally

Solutions:

  • Verify AdGuard Home is set as primary DNS
  • Check DNS rewrite rules are enabled
  • Flush DNS cache on client: ipconfig /flushdns (Windows) or sudo systemd-resolve --flush-caches (Linux)

Combining HAProxy ACLs with AdGuard Home DNS rewrites creates a robust, user-friendly security layer for your internal services. Your users get the convenience of friendly domain names while you maintain strict control over who can access what.

This approach scales easily adding a new internal-only service is as simple as adding two lines to your configuration files. It's a perfect example of security that doesn't compromise usability and it's built on tools you're probably already using.