Access Control Lists (ACLs)
ACLs are the most critical security component of your MESH deployment. They control which nodes can communicate with each other and on which ports.
Critical security warning
Never use permissive ACLs ("*" rules) in production. This allows any compromised endpoint to access all other devices in your mesh, including other forensic targets and analyst workstations. Always implement the principle of least privilege.
Why ACLs are critical for MESH
In a forensic mesh network, security isolation is paramount:
Threat model:
- Compromised endpoints: Target devices may be infected with malware
- Lateral movement: Malware could attempt to spread to other devices
- Data exfiltration: Compromised devices could try to access other forensic targets
- Analyst protection: Analyst workstations must be protected from compromised endpoints
Security goals:
- Endpoints should only be accessible by authorised analysts using a secure collection node
- Endpoints should never be able to access each other
- Endpoints should never be able to access analyst workstations
- Analysts should have controlled access to specific endpoints
- All access should be logged and auditable
ACL syntax and structure
ACLs are defined in YAML format with the following structure:
# Define groups of users/nodes
groups:
group:name:
- user1
- user2
# Define access control rules
acls:
- action: accept # or deny
src:
- source_group_or_user
dst:
- destination_group_or_user:port
Components:
groups: Logical groupings of users or nodes
- Format:
group:name - Contains list of usernames or node names
- Makes ACL rules more maintainable
- Can be nested or referenced in rules
acls: List of access control rules
- Evaluated in order (first match wins)
- Each rule has
action,src, anddst
action: What to do with matching traffic
accept: Allow the trafficdeny: Block the traffic
src: Source of the traffic
- Can be: username, node name, group, or
"*"(any) - Multiple sources can be listed
dst: Destination of the traffic
- Format:
target:portortarget:*(all ports) - Can be: username, node name, group, or
"*"(any) - Port can be specific (e.g.,
5555for ADB) or*(all ports)
Testing vs. production ACLs
Testing ACL (permissive - DO NOT USE IN PRODUCTION)
# WARNING: This is for testing only!
# Allows all traffic between all nodes
acls:
- action: accept
src:
- "*"
dst:
- "*:*"
Testing only
This permissive ACL is only for initial testing and development. It provides no security and should never be used in production or with real forensic targets.
When to use:
- Initial setup and testing
- Verifying connectivity between nodes
- Troubleshooting connection issues
When to remove:
- Before connecting any real forensic targets
- Before deploying in production
- Before handling sensitive data
Production ACL (restrictive - RECOMMENDED)
# Production ACL - Principle of least privilege
groups:
group:analysts:
- analyst1
- analyst2
group:endpoints:
- android-device-1
- android-device-2
acls:
# Analysts can access all endpoints on ADB port
- action: accept
src:
- group:analysts
dst:
- group:endpoints:5555 # ADB port only
# Analysts can access all endpoints on all ports (for full forensics)
- action: accept
src:
- group:analysts
dst:
- group:endpoints:*
# Endpoints CANNOT access each other (critical for isolation)
- action: deny
src:
- group:endpoints
dst:
- group:endpoints:*
# Endpoints CANNOT access analysts (prevent reverse connections)
- action: deny
src:
- group:endpoints
dst:
- group:analysts:*
# Analysts can access each other (for collaboration)
- action: accept
src:
- group:analysts
dst:
- group:analysts:*
# Deny all other traffic (default deny)
- action: deny
src:
- "*"
dst:
- "*:*"
Real-world ACL examples
Example 1: Single analyst, multiple endpoints
# Simple forensic setup
groups:
group:analysts:
- forensic-workstation
group:endpoints:
- suspect-phone-1
- suspect-phone-2
- suspect-phone-3
acls:
# Analyst can access all endpoints
- action: accept
src:
- group:analysts
dst:
- group:endpoints:*
# Endpoints are completely isolated
- action: deny
src:
- group:endpoints
dst:
- "*:*"
Example 2: Multi-team deployment
# Multiple analyst teams with separate endpoints
groups:
group:team-alpha-analysts:
- analyst-alice
- analyst-bob
group:team-alpha-endpoints:
- case-123-phone-1
- case-123-phone-2
group:team-bravo-analysts:
- analyst-charlie
- analyst-diana
group:team-bravo-endpoints:
- case-456-phone-1
- case-456-phone-2
acls:
# Team Alpha: analysts can access their endpoints
- action: accept
src:
- group:team-alpha-analysts
dst:
- group:team-alpha-endpoints:*
# Team Bravo: analysts can access their endpoints
- action: accept
src:
- group:team-bravo-analysts
dst:
- group:team-bravo-endpoints:*
# Team Alpha: analysts can collaborate
- action: accept
src:
- group:team-alpha-analysts
dst:
- group:team-alpha-analysts:*
# Team Bravo: analysts can collaborate
- action: accept
src:
- group:team-bravo-analysts
dst:
- group:team-bravo-analysts:*
# Teams CANNOT access each other's endpoints
- action: deny
src:
- group:team-alpha-analysts
dst:
- group:team-bravo-endpoints:*
- action: deny
src:
- group:team-bravo-analysts
dst:
- group:team-alpha-endpoints:*
# Endpoints are completely isolated
- action: deny
src:
- group:team-alpha-endpoints
dst:
- "*:*"
- action: deny
src:
- group:team-bravo-endpoints
dst:
- "*:*"
# Default deny
- action: deny
src:
- "*"
dst:
- "*:*"
Example 3: Port-specific access (ADB only)
# Restrict analysts to only ADB access
groups:
group:analysts:
- forensic-workstation
group:endpoints:
- android-device-1
- android-device-2
acls:
# Analysts can ONLY access ADB port (5555)
- action: accept
src:
- group:analysts
dst:
- group:endpoints:5555
# Deny all other ports
- action: deny
src:
- group:analysts
dst:
- group:endpoints:*
# Endpoints completely isolated
- action: deny
src:
- group:endpoints
dst:
- "*:*"
ACL best practices for forensic deployments
1. Principle of least privilege
Always grant the minimum access required:
- Analysts should only access endpoints they're investigating
- Limit port access to only what's needed (e.g., ADB port 5555)
- Use time-limited access when possible (rotate keys regularly)
2. Endpoint isolation
Endpoints must never access each other:
Why this matters:
- Prevents malware from spreading between forensic targets
- Stops compromised devices from accessing other evidence
- Maintains chain of custody integrity
3. Prevent reverse connections
Endpoints should not initiate connections to analysts:
# Prevent endpoints from connecting to analysts
- action: deny
src:
- group:endpoints
dst:
- group:analysts:*
Why this matters:
- Prevents malware from attacking analyst workstations
- Stops data exfiltration from endpoints
- Maintains unidirectional trust model
4. Use groups for maintainability
Bad (hard to maintain):
Good (easy to maintain):
groups:
group:analysts:
- analyst1
- analyst2
- analyst3
group:endpoints:
- phone1
- phone2
- phone3
acls:
- action: accept
src:
- group:analysts
dst:
- group:endpoints:*
5. Default deny at the end
Always end with a default deny rule:
acls:
# ... your specific rules ...
# Default deny (catches anything not explicitly allowed)
- action: deny
src:
- "*"
dst:
- "*:*"
Testing and validating ACLs
After creating or modifying ACLs in the MESH control plane web UI, always test them:
1. Test from analyst workstation
# Should succeed: Analyst accessing endpoint
ping 100.64.2.1 # Endpoint mesh IP
# Should succeed: ADB connection
meshcli adbpair 100.64.2.1:5555
2. Test endpoint isolation
# On endpoint device (via ADB shell)
meshcli adbshell
# Should FAIL: Endpoint trying to access another endpoint
ping 100.64.2.2 # Another endpoint's mesh IP
# Should FAIL: Endpoint trying to access analyst
ping 100.64.1.1 # Analyst mesh IP
3. Check logs on control plane
# View ACL enforcement logs
docker compose logs headscale | grep -i acl
# Look for denied connections
docker compose logs headscale | grep -i denied
Common ACL mistakes and how to avoid them
Mistake 1: Using "*" in production
Wrong:
Right:
Mistake 2: Forgetting endpoint isolation
Wrong (endpoints can access each other):
Right (endpoints explicitly denied):
acls:
- action: accept
src:
- group:analysts
dst:
- group:endpoints:*
- action: deny
src:
- group:endpoints
dst:
- group:endpoints:*
Mistake 3: Allowing reverse connections
Wrong (endpoints can connect to analysts):
Right (only analysts initiate connections):
acls:
- action: accept
src:
- group:analysts
dst:
- group:endpoints:*
- action: deny
src:
- group:endpoints
dst:
- group:analysts:*
Mistake 4: Overly broad port access
Wrong (all ports open):
Better (specific ports only):