Prometheus node_exporter on Tailnet
When monitoring multiple servers with Prometheus, security should be a top priority. Node Exporter exposes detailed system metrics that you definitely don’t want accessible from the public internet. This guide shows how to set up Node Exporter to be accessible only through your Tailscale network (tailnet).
The Security Challenge
By default, Node Exporter listens on port 9100 and serves metrics to anyone who can reach that port. This creates a security risk:
- Information disclosure: System metrics reveal sensitive information about your infrastructure
- Attack surface: Additional open ports increase your attack surface
- Compliance concerns: Exposing internal metrics may violate security policies
Our Solution: Tailnet-Only Access
- All servers are on the same tailnet
- node_exporter should be accessible only via tailnet
- node_exporter should not be accessible from the public internet
This approach provides:
- Zero-trust networking: Only devices on your tailnet can access metrics
- Encrypted traffic: All communication goes through Tailscale’s encrypted tunnels
- Simplified firewall rules: Clear allow/deny rules based on Tailscale’s IP range
The Installation Script
Here’s our complete setup script that handles installation, service configuration, and firewall rules:
#!/bin/bash
set -e
# Define version
VERSION="1.9.1"
echo "[*] Installing node_exporter v$VERSION..."
# Download and install node_exporter
cd /tmp
wget -q https://github.com/prometheus/node_exporter/releases/download/v$VERSION/node_exporter-$VERSION.linux-amd64.tar.gz
tar xfz node_exporter-$VERSION.linux-amd64.tar.gz
cp node_exporter-$VERSION.linux-amd64/node_exporter /usr/local/bin/
chmod +x /usr/local/bin/node_exporter
echo "[*] Creating systemd service..."
cat <<EOF | tee /etc/systemd/system/node_exporter.service
[Unit]
Description=Node Exporter
Wants=network-online.target
After=network-online.target
[Service]
User=root
Group=root
Type=simple
ExecStart=/usr/local/bin/node_exporter
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reexec
systemctl daemon-reload
systemctl enable node_exporter
systemctl start node_exporter
echo "[*] Configuring UFW..."
# Allow SSH explicitly (precaution)
ufw allow OpenSSH comment "Allow SSH"
# Set default policies
ufw default allow incoming
ufw default allow outgoing
# Block node_exporter from everywhere EXCEPT Tailnet
ufw allow from 100.0.0.0/8 to any port 9100 comment "Allow node_exporter from Tailnet"
ufw deny 9100 comment "Block node_exporter from outside Tailnet"
# Enable UFW if inactive
if ufw status | grep -q "Status: inactive"; then
echo "[*] Enabling UFW firewall with default allow policy..."
echo "y" | ufw enable
else
echo "[*] UFW already active."
fi
ufw reload
echo "[✓] Final UFW status:"
ufw status verbose
echo "[✓] node_exporter service status:"
systemctl status node_exporter --no-pager
Key Security Features
1. Tailscale IP Range Restriction
The script uses 100.0.0.0/8
which is Tailscale’s reserved IP range. This ensures only devices authenticated to your tailnet can access the metrics.
2. Explicit Deny Rule
The ufw deny 9100
rule provides defense-in-depth by explicitly blocking external access to the Node Exporter port.
3. SSH Protection
The script preserves SSH access as a safety measure, preventing you from locking yourself out during firewall configuration.
Verification Steps
After running the script, verify your setup:
Check Node Exporter Status
systemctl status node_exporter
Verify Firewall Rules
ufw status verbose
You should see rules similar to:
9100 ALLOW 100.0.0.0/8 # Allow node_exporter from Tailnet
9100 DENY Anywhere # Block node_exporter from outside Tailnet
Test Access
From another machine on your tailnet:
curl http://[TAILSCALE_IP]:9100/metrics
From outside the tailnet, this should fail or timeout.
Prometheus Configuration
Configure your Prometheus server to scrape metrics using Tailscale IPs:
scrape_configs:
- job_name: 'node_exporter_tailnet'
file_sd_configs:
- files:
- '/etc/prometheus/targets/node_exporter.json'
refresh_interval: 30s
Create the /etc/prometheus/targets/node_exporter.json
file with your tailnet IPs:
[
{
"targets": ["worker1:9100"],
"labels": {
"instance": "worker1"
}
},
{
"targets": ["worker2:9100"],
"labels": {
"instance": "worker2"
}
},
{
"targets": ["worker3:9100"],
"labels": {
"instance": "worker3"
}
}
]
Troubleshooting
Can’t Access Metrics
- Verify Tailscale is running on both machines
- Check firewall rules with
ufw status
- Confirm Node Exporter is listening:
ss -tlnp | grep 9100
Firewall Issues
If you need to reset UFW rules:
ufw --force reset
Then re-run the configuration portion of the script.
Conclusion
This setup provides a secure, maintainable way to monitor your infrastructure with Prometheus while keeping sensitive metrics away from the public internet. The combination of Tailscale’s zero-trust networking and properly configured firewall rules ensures your monitoring data remains private and secure.
This post was enhanced with assistance from Claude.
Comments