Saltar al contenido principal

Private Networking with Tailscale for Secure Database Access

Exposing your database to the public internet is a security risk. Even with strong passwords and firewall rules, public endpoints are attack targets. Private networking provides a secure way to access your database without exposing it publicly.

🎯 Why Private Networking?

Security Benefits

  • No public exposure: Database not accessible from the internet
  • Encrypted traffic: All traffic encrypted end-to-end
  • Zero-trust model: Only authorized devices can connect
  • Audit trail: Track all access attempts

Use Cases

  • Development teams: Secure access for remote developers
  • CI/CD pipelines: Automated deployments without public endpoints
  • Monitoring tools: Secure access for observability systems
  • Backup systems: Secure backup connections

🔐 What is Tailscale?

Tailscale creates a secure mesh VPN using WireGuard, allowing devices to connect as if they're on the same local network, regardless of their physical location.

Key Features

  • Zero-config VPN: Automatic setup and key management
  • Cross-platform: Works on Linux, macOS, Windows, iOS, Android
  • Mesh networking: Direct peer-to-peer connections
  • Access control: Fine-grained permissions
  • Audit logs: Track all connections

🚀 Setting Up Tailscale

Step 1: Install Tailscale

On Linux (Database Server):

# Add Tailscale repository
curl -fsSL https://tailscale.com/install.sh | sh

# Start Tailscale
sudo tailscale up

On macOS:

brew install tailscale
sudo tailscale up

On Windows:

Download from tailscale.com/download

Step 2: Authenticate

When you run tailscale up, you'll get a URL to authenticate:

To authenticate, visit:
https://login.tailscale.com/admin/a/...

Open the URL in your browser and sign in with your Google, Microsoft, or GitHub account.

Step 3: Verify Connection

# Check Tailscale status
tailscale status

# Test connectivity
ping <database-server-tailscale-ip>

🗄️ Configuring MariaDB for Tailscale

Step 1: Find Tailscale IP

On your database server:

tailscale ip -4
# Output: 100.x.x.x

Step 2: Configure MariaDB Binding

Edit MariaDB configuration:

sudo nano /etc/mysql/mariadb.conf.d/50-server.cnf

Update bind address:

[mysqld]
bind-address = 100.x.x.x # Tailscale IP
# Or bind to all interfaces (less secure)
# bind-address = 0.0.0.0

Restart MariaDB:

sudo systemctl restart mariadb

Step 3: Verify MariaDB is Listening

# Check if MariaDB is listening on Tailscale IP
sudo netstat -tlnp | grep 3306

# Should show:
# tcp 0 0 100.x.x.x:3306 0.0.0.0:* LISTEN 1234/mysqld

🔧 Configuring Filess.io with Tailscale

On Filess.io Dedicated Runtime, you can enable Tailscale integration:

Via Console

  1. Navigate to your database instance
  2. Go to "Networking" section
  3. Enable "Tailscale Integration"
  4. Authorize Tailscale connection
  5. Note the Tailscale IP address

Via API

# Visual example of Tailscale configuration
# (Imagine a beautiful UI here)

Get Tailscale Connection Details

# Placeholder for API example

Response:

{
"enabled": true,
"tailscale_ip": "100.x.x.x",
"connection_string": "mariadb://user:[email protected]:3306/dbname"
}

💻 Connecting from Application

Update Connection String

Update your .env file:

# Before (public endpoint)
DATABASE_URL="mysql://user:[email protected]:3306/filess_example"

# After (Tailscale private network)
DATABASE_URL="mysql://user:[email protected]:3306/filess_example"

Test Connection

# From your development machine (with Tailscale installed)
mysql -h 100.x.x.x -u user -p filess_example

Application Code (No Changes Needed)

Your application code doesn't need changes - just update the connection string:

// src/index.ts
// Connection string from .env automatically used by Prisma
export const prisma = new PrismaClient();

🏢 Team Access Management

Adding Team Members

  1. Go to Tailscale Admin Console
  2. Navigate to "Machines" or "Users"
  3. Invite team members
  4. They install Tailscale and authenticate
  5. They can now access the database via Tailscale IP

Access Control Lists (ACLs)

Create ACL rules in Tailscale:

{
"groups": {
"group:developers": ["[email protected]", "[email protected]"],
"group:ops": ["[email protected]"]
},
"hosts": {
"database": "100.x.x.x"
},
"acls": [
{
"action": "accept",
"src": ["group:developers"],
"dst": ["database:3306"]
},
{
"action": "accept",
"src": ["group:ops"],
"dst": ["database:*"]
}
]
}

Device Tags

Tag devices for easier management:

# Tag a device
tailscale set --advertise-tags=tag:database-server

# In ACL, reference by tag
{
"acls": [
{
"action": "accept",
"src": ["tag:developer-laptops"],
"dst": ["tag:database-server:3306"]
}
]
}

🔄 CI/CD Integration

GitHub Actions

# .github/workflows/test.yml
name: Run Tests

on: [push]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Install Tailscale
run: |
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --authkey=${{ secrets.TAILSCALE_AUTH_KEY }}

- name: Wait for Tailscale
run: sleep 10

- name: Run tests
env:
DATABASE_URL: mariadb://user:[email protected]:3306/filess_example
run: npm test

GitLab CI

# .gitlab-ci.yml
test:
image: node:20
before_script:
- curl -fsSL https://tailscale.com/install.sh | sh
- sudo tailscale up --authkey=$TAILSCALE_AUTH_KEY
- sleep 10
script:
- npm test
variables:
DATABASE_URL: "mariadb://user:[email protected]:3306/filess_example"

🔍 Monitoring and Debugging

Check Tailscale Status

# On database server
tailscale status

# Output:
# 100.x.x.x database-server [email protected] linux -
# 100.y.y.y dev-laptop [email protected] macOS -

Test Connectivity

# Ping test
ping 100.x.x.x

# Port test
nc -zv 100.x.x.x 3306

# MySQL connection test
mysql -h 100.x.x.x -u user -p -e "SELECT 1"

View Tailscale Logs

# System logs
sudo journalctl -u tailscaled -f

# Tailscale status with details
tailscale status --json | jq

🛡️ Security Best Practices

1. Use Tailscale ACLs

Restrict access to only necessary devices:

{
"acls": [
{
"action": "accept",
"src": ["tag:authorized-devices"],
"dst": ["tag:database-server:3306"]
}
]
}

2. Rotate Keys Regularly

# Generate new auth key
# In Tailscale admin console: Settings > Keys

# Use new key
tailscale up --authkey=<new-key>

3. Monitor Access

Enable audit logs in Tailscale admin console to track all connections.

4. Use Database Firewall

Even on private network, use MariaDB's built-in firewall:

-- Allow only specific Tailscale IPs
CREATE TABLE mysql.firewall_users (
USERHOST VARCHAR(80) PRIMARY KEY,
RULE TEXT
);

INSERT INTO mysql.firewall_users VALUES
('[email protected]', 'ALLOW'),
('[email protected]', 'ALLOW');

🔄 Failover and High Availability

Multiple Database Servers

With Tailscale, you can easily connect to multiple database servers:

// src/config/database.ts
const databaseConfig = {
primary: {
host: process.env.DB_PRIMARY_TAILSCALE_IP,
port: 3306
},
replica: {
host: process.env.DB_REPLICA_TAILSCALE_IP,
port: 3306
}
};

Load Balancing

Use Tailscale's built-in load balancing or configure at application level:

// Round-robin connection selection
const getDatabaseConnection = () => {
const servers = [config.primary, config.replica];
const index = Math.floor(Math.random() * servers.length);
return servers[index];
};

📊 Performance Considerations

Latency

Tailscale uses direct peer-to-peer connections when possible, minimizing latency:

# Test latency
ping 100.x.x.x

# Typical results:
# - Same region: <10ms
# - Different regions: 20-50ms
# - Different continents: 50-150ms

Bandwidth

Tailscale traffic is encrypted but efficient:

  • Overhead: ~4 bytes per packet
  • Encryption: WireGuard (fast, modern encryption)
  • Compression: Not applied (database traffic is already compressed)

🧪 Testing Private Network Setup

Test Script

#!/bin/bash
# test-tailscale-connection.sh

DB_HOST="100.x.x.x"
DB_USER="test_user"
DB_PASS="test_password"
DB_NAME="filess_example"

echo "Testing Tailscale connection to database..."

# Test 1: Ping
if ping -c 1 $DB_HOST > /dev/null 2>&1; then
echo "✓ Ping successful"
else
echo "✗ Ping failed"
exit 1
fi

# Test 2: Port connectivity
if nc -zv $DB_HOST 3306 > /dev/null 2>&1; then
echo "✓ Port 3306 accessible"
else
echo "✗ Port 3306 not accessible"
exit 1
fi

# Test 3: MySQL connection
if mysql -h $DB_HOST -u $DB_USER -p$DB_PASS -e "SELECT 1" $DB_NAME > /dev/null 2>&1; then
echo "✓ MySQL connection successful"
else
echo "✗ MySQL connection failed"
exit 1
fi

echo "All tests passed!"

🎯 Integration with Example App

Update your application to use Tailscale:

// src/utils/database-health.ts
export async function checkDatabaseHealth() {
try {
await prisma.$queryRaw`SELECT 1`;
return {
status: 'healthy',
network: 'tailscale',
latency: await measureLatency()
};
} catch (error) {
return {
status: 'unhealthy',
error: error.message
};
}
}

✅ Checklist

  • Tailscale installed on database server
  • Tailscale installed on development machines
  • MariaDB configured to listen on Tailscale IP
  • Connection strings updated
  • ACLs configured for access control
  • Team members added to Tailscale
  • CI/CD pipelines updated
  • Monitoring and logging enabled
  • Backup connections tested
  • Documentation updated

🔮 Next Steps

  • Subnet routing: Route entire subnets through Tailscale
  • Exit nodes: Use Tailscale as VPN for internet traffic
  • MagicDNS: Use friendly hostnames instead of IPs
  • SSH access: Combine with SSH for additional security

With Tailscale configured, your database is accessible only to authorized devices on your private network, significantly improving security without sacrificing convenience.