Add pfSense backup utility and documentation
- Add backup-pfsense-config.sh script for automated config backups via SSH - Auto-commits backups to git with timestamped filenames - Includes validation, error handling, and troubleshooting guides - Add scripts/README.md with detailed usage and crontab examples - Add BACKUP-QUICKSTART.md for quick reference commands - Update README.md to reference automated backup workflow - Create backups/ directory structure The script tests SSH connectivity successfully to pfSense.
This commit is contained in:
68
pfsense.home.arpa/BACKUP-QUICKSTART.md
Normal file
68
pfsense.home.arpa/BACKUP-QUICKSTART.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Quick Backup Reference
|
||||
|
||||
One-liner commands to backup your pfSense configuration.
|
||||
|
||||
## Basic Commands
|
||||
|
||||
```bash
|
||||
# Navigate to the pfsense folder
|
||||
cd /Users/kenjim/workspace/src/personal/appa-net/pfsense.home.arpa
|
||||
|
||||
# Test SSH connection (no download)
|
||||
./scripts/backup-pfsense-config.sh --dry-run
|
||||
|
||||
# Backup with auto-commit to git (default)
|
||||
./scripts/backup-pfsense-config.sh
|
||||
|
||||
# Backup without auto-commit
|
||||
./scripts/backup-pfsense-config.sh -n
|
||||
|
||||
# Backup from different host
|
||||
./scripts/backup-pfsense-config.sh 192.168.1.1
|
||||
```
|
||||
|
||||
## Schedule Daily Backups
|
||||
|
||||
Add to your crontab to run at 2 AM every day:
|
||||
|
||||
```bash
|
||||
crontab -e
|
||||
```
|
||||
|
||||
Then add this line:
|
||||
```cron
|
||||
0 2 * * * cd /Users/kenjim/workspace/src/personal/appa-net/pfsense.home.arpa && ./scripts/backup-pfsense-config.sh > /tmp/pfsense-backup.log 2>&1
|
||||
```
|
||||
|
||||
Verify it was added:
|
||||
```bash
|
||||
crontab -l | grep backup-pfsense
|
||||
```
|
||||
|
||||
## View Backups
|
||||
|
||||
```bash
|
||||
# List all backups
|
||||
ls -lh backups/
|
||||
|
||||
# Show latest backup
|
||||
ls -lh backups/ | tail -1
|
||||
|
||||
# Check git history
|
||||
git log --oneline backups/
|
||||
|
||||
# See what changed in latest backup
|
||||
git show HEAD:backups/
|
||||
```
|
||||
|
||||
## Restore from Backup
|
||||
|
||||
From pfSense WebUI:
|
||||
1. **Diagnostics → Backup & Restore**
|
||||
2. **Choose File** → select backup from `backups/` folder
|
||||
3. **Restore Configuration**
|
||||
4. Reboot when prompted
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2026-04-22
|
||||
150
pfsense.home.arpa/INDEX.md
Normal file
150
pfsense.home.arpa/INDEX.md
Normal file
@@ -0,0 +1,150 @@
|
||||
# pfsense.home.arpa Documentation Index
|
||||
|
||||
Quick reference to all configuration files and guides in this folder.
|
||||
|
||||
## Files in This Directory
|
||||
|
||||
### 📘 **README.md** (Main Configuration Guide)
|
||||
Comprehensive reference for managing your pfSense router. Covers:
|
||||
- VLAN structure and definitions
|
||||
- Step-by-step configuration instructions
|
||||
- DHCP setup
|
||||
- Firewall rules and access control
|
||||
- Troubleshooting guide
|
||||
|
||||
**When to read**: When you want detailed explanations or reference material
|
||||
**Length**: ~400 lines (detailed)
|
||||
|
||||
### ⚡ **VLAN-QUICKSTART.md** (Implementation Checklist)
|
||||
Step-by-step checklist to implement VLANs. Use this for hands-on setup.
|
||||
- Pre-implementation checklist
|
||||
- 6 phases with checkbox items
|
||||
- Testing procedures
|
||||
- Backup & documentation steps
|
||||
|
||||
**When to read**: When actively configuring your pfSense
|
||||
**Length**: ~300 lines (action-oriented)
|
||||
|
||||
### 📊 **VLAN-CONFIG.md** (Configuration Reference)
|
||||
YAML-formatted configuration definitions and quick lookup.
|
||||
- VLAN definitions (IDs, subnets, purposes)
|
||||
- Firewall rule summary (matrix view)
|
||||
- DHCP configuration
|
||||
- Device assignments
|
||||
- Implementation checklist
|
||||
- Design rationale
|
||||
|
||||
**When to read**: For quick lookup of VLAN IDs, subnet ranges, firewall rules
|
||||
**Length**: ~150 lines (reference)
|
||||
|
||||
### 🗺️ **VLAN-TOPOLOGY.md** (Visual Architecture)
|
||||
Network diagrams and traffic flow visualization.
|
||||
- High-level topology ASCII diagrams
|
||||
- Detailed dataflow examples
|
||||
- Firewall rule chain visualization
|
||||
- Port connectivity diagrams
|
||||
- Traffic examples (allowed and blocked)
|
||||
- Isolation guarantees
|
||||
|
||||
**When to read**: To understand network architecture or debug traffic issues
|
||||
**Length**: ~250 lines (visual/conceptual)
|
||||
|
||||
## Quick Navigation
|
||||
|
||||
**I want to...**
|
||||
|
||||
| Task | File | Section |
|
||||
|------|------|---------|
|
||||
| Set up VLANs for the first time | VLAN-QUICKSTART.md | Phase 1-6 |
|
||||
| Understand VLAN architecture | VLAN-TOPOLOGY.md | High-Level Topology |
|
||||
| Look up a VLAN subnet | VLAN-CONFIG.md | VLAN Definitions |
|
||||
| Configure firewall rules | README.md | Step 4: Configure Firewall Rules |
|
||||
| Set up DHCP | README.md | Step 3: Configure DHCP |
|
||||
| Troubleshoot a problem | README.md | Troubleshooting section |
|
||||
| Debug traffic | VLAN-TOPOLOGY.md | Traffic Examples |
|
||||
| Backup my config | README.md | Backup & Recovery |
|
||||
| Add a new device to a VLAN | VLAN-CONFIG.md | Device Assignments |
|
||||
|
||||
## File Relationships
|
||||
|
||||
```
|
||||
README.md
|
||||
├─ Detailed explanation of all features
|
||||
├─ References: VLAN-CONFIG.md, VLAN-TOPOLOGY.md
|
||||
└─ Use with: VLAN-QUICKSTART.md for hands-on setup
|
||||
|
||||
VLAN-QUICKSTART.md
|
||||
├─ Step-by-step checklist
|
||||
├─ References: VLAN-CONFIG.md
|
||||
└─ Use with: README.md for details
|
||||
|
||||
VLAN-CONFIG.md
|
||||
├─ Quick reference data
|
||||
├─ Subnet/VLAN ID lookup
|
||||
└─ Use with: README.md, VLAN-TOPOLOGY.md for context
|
||||
|
||||
VLAN-TOPOLOGY.md
|
||||
├─ Visual architecture
|
||||
├─ Traffic flow examples
|
||||
└─ Use with: README.md for firewall rule explanations
|
||||
```
|
||||
|
||||
## Setup Workflow Recommended
|
||||
|
||||
1. **Read** VLAN-TOPOLOGY.md (understand the architecture)
|
||||
2. **Review** VLAN-CONFIG.md (familiarize yourself with IDs and subnets)
|
||||
3. **Follow** VLAN-QUICKSTART.md (step-by-step implementation)
|
||||
4. **Reference** README.md (for detailed explanations during setup)
|
||||
5. **Store** backups/ folder (save pfSense XML configs here)
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
pfsense.home.arpa/
|
||||
├── README.md # Main configuration guide
|
||||
├── VLAN-QUICKSTART.md # Hands-on setup checklist
|
||||
├── VLAN-CONFIG.md # VLAN reference data
|
||||
├── VLAN-TOPOLOGY.md # Network diagrams
|
||||
├── INDEX.md # This file
|
||||
├── backups/ # Store pfSense backups here
|
||||
│ └── pfsense-config-YYYY-MM-DD.xml
|
||||
└── scripts/ # Optional: Automation scripts
|
||||
└── (future: Ansible, Terraform, etc.)
|
||||
```
|
||||
|
||||
## Backup Location
|
||||
|
||||
All pfSense configuration exports should be saved to `backups/` folder:
|
||||
|
||||
```bash
|
||||
# After exporting from pfSense WebUI:
|
||||
mv ~/Downloads/config.xml backups/pfsense-config-2026-04-22.xml
|
||||
git add backups/
|
||||
git commit -m "pfSense: Backup after VLAN configuration"
|
||||
```
|
||||
|
||||
## Future Additions
|
||||
|
||||
As your network grows, consider adding:
|
||||
|
||||
- `FIREWALL-RULES.md` — Detailed firewall rule documentation
|
||||
- `DHCP-RESERVATIONS.md` — Static IP assignments for devices
|
||||
- `DNS-CONFIG.md` — DNS resolver and record configuration
|
||||
- `SCRIPTS/` — Ansible playbooks, Terraform configs, or backup scripts
|
||||
- `MIGRATION-GUIDE.md` — How to restore from backup or migrate to new pfSense instance
|
||||
|
||||
## Version History
|
||||
|
||||
| Date | Version | Changes |
|
||||
|------|---------|---------|
|
||||
| 2026-04-22 | 1.0 | Initial VLAN configuration (3 VLANs: Secure, AIWorkload, IoT) |
|
||||
|
||||
## Related Documents
|
||||
|
||||
- [Root README.md](../README.md) — Project overview
|
||||
- [zet.home.arpa/PROXY-SETUP.md](../zet.home.arpa/PROXY-SETUP.md) — Squid proxy on VLAN (future: will reference this VLAN config)
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2026-04-22
|
||||
**Current Configuration Version**: 1.0
|
||||
296
pfsense.home.arpa/README.md
Normal file
296
pfsense.home.arpa/README.md
Normal file
@@ -0,0 +1,296 @@
|
||||
# pfSense Router Configuration
|
||||
|
||||
Central hub for your home network. Manages DHCP, DNS, routing, firewalling, and network segmentation via VLANs.
|
||||
|
||||
## Overview
|
||||
|
||||
**Device**: pfSense Router
|
||||
**Primary IP**: 172.27.0.1 (LAN default gateway)
|
||||
**Role**: Router, Firewall, DHCP server, DNS resolver, VLAN orchestration
|
||||
|
||||
## Network Architecture
|
||||
|
||||
Your home network is segmented into security zones using VLANs. Each VLAN has:
|
||||
- Isolated broadcast domain
|
||||
- Separate IP subnet
|
||||
- Controlled routing and firewall rules between VLANs
|
||||
- Dedicated DHCP scope (if needed)
|
||||
|
||||
### VLAN Structure
|
||||
|
||||
| VLAN ID | Name | Purpose | Subnet | Gateway | Notes |
|
||||
|---------|------|---------|--------|---------|-------|
|
||||
| 1 | `LAN_SECURE` | Trusted personal devices | 172.27.0.0/24 | 172.27.0.1 | Primary network (default) |
|
||||
| 2 | `VLAN_AIWORKLOAD` | AI/ML dangerous workloads (openclaw) | 172.27.2.0/24 | 172.27.2.1 | Isolated, minimal internet access |
|
||||
| 3 | `VLAN_IOT` | IoT devices (cameras, smart home) | 172.27.3.0/24 | 172.27.3.1 | Limited trust, blocked from LAN_SECURE |
|
||||
|
||||
## Configuration Steps
|
||||
|
||||
### Prerequisites
|
||||
- Access to pfSense WebUI or SSH
|
||||
- Understanding of your network hardware (which ports support VLANs)
|
||||
|
||||
### Step 1: Create VLANs on Physical Interface
|
||||
|
||||
1. Navigate: **Interfaces → VLANs**
|
||||
2. Click **+ Add**
|
||||
3. Create `VLAN_AIWORKLOAD`:
|
||||
- **Parent Interface**: `em0` (or your LAN NIC)
|
||||
- **VLAN Tag**: `2`
|
||||
- **VLAN Priority**: `0` (default)
|
||||
- **Description**: `VLAN_AIWORKLOAD`
|
||||
- Click **Save**
|
||||
|
||||
4. Create `VLAN_IOT`:
|
||||
- **Parent Interface**: `em0`
|
||||
- **VLAN Tag**: `3`
|
||||
- **VLAN Priority**: `0`
|
||||
- **Description**: `VLAN_IOT`
|
||||
- Click **Save**
|
||||
|
||||
5. Click **Apply Changes**
|
||||
|
||||
### Step 2: Create Virtual Interfaces
|
||||
|
||||
1. Navigate: **Interfaces → Assignments**
|
||||
2. Click **+ Add** next to `VLAN_AIWORKLOAD_2`:
|
||||
- A new interface (e.g., `OPT1`) is created automatically
|
||||
- Click the pencil icon to configure it
|
||||
|
||||
3. Configure `OPT1` (VLAN_AIWORKLOAD):
|
||||
- **Enable Interface**: ✓ Checked
|
||||
- **Description**: `VLAN_AIWORKLOAD`
|
||||
- **IPv4 Configuration Type**: `Static IPv4`
|
||||
- **IPv4 Address**: `172.27.2.1`
|
||||
- **IPv4 Subnet Mask**: `255.255.255.0` (/24)
|
||||
- Click **Save**
|
||||
|
||||
4. Repeat for `VLAN_IOT_3`:
|
||||
- Configure as `OPT2`
|
||||
- **Description**: `VLAN_IOT`
|
||||
- **IPv4 Address**: `172.27.3.1`
|
||||
- **IPv4 Subnet Mask**: `255.255.255.0` (/24)
|
||||
- Click **Save**
|
||||
|
||||
5. Click **Apply Changes**
|
||||
|
||||
### Step 3: Configure DHCP for Each VLAN
|
||||
|
||||
#### VLAN_AIWORKLOAD DHCP
|
||||
1. Navigate: **Services → DHCP Server**
|
||||
2. Click the **VLAN_AIWORKLOAD** tab
|
||||
3. **Enable DHCP server on VLAN_AIWORKLOAD interface**: ✓ Checked
|
||||
4. **Range**: `172.27.2.100` to `172.27.2.200`
|
||||
5. **Gateway**: `172.27.2.1`
|
||||
6. **Servers**:
|
||||
- DNS 1: `172.27.0.1` (pfSense resolver)
|
||||
- DNS 2: `8.8.8.8` (optional fallback)
|
||||
7. Click **Save**
|
||||
|
||||
#### VLAN_IOT DHCP
|
||||
1. Click the **VLAN_IOT** tab
|
||||
2. **Enable DHCP server on VLAN_IOT interface**: ✓ Checked
|
||||
3. **Range**: `172.27.3.100` to `172.27.3.200`
|
||||
4. **Gateway**: `172.27.3.1`
|
||||
5. **Servers**:
|
||||
- DNS 1: `172.27.0.1`
|
||||
- DNS 2: `8.8.8.8`
|
||||
6. Click **Save**
|
||||
|
||||
### Step 4: Configure Firewall Rules
|
||||
|
||||
#### Allow WAN → All VLANs
|
||||
Navigate: **Firewall → Rules → WAN**
|
||||
- Default rule should allow established connections
|
||||
|
||||
#### Default LAN → VLAN Rules
|
||||
Navigate: **Firewall → Rules → LAN_SECURE**
|
||||
|
||||
1. **Allow LAN_SECURE → VLAN_AIWORKLOAD** (if needed):
|
||||
- Action: `Pass`
|
||||
- Interface: `LAN`
|
||||
- Direction: `in`
|
||||
- Source: `LAN_SECURE subnet`
|
||||
- Destination: `VLAN_AIWORKLOAD subnet`
|
||||
- Click **Save**
|
||||
|
||||
2. **Block LAN_SECURE ↔ VLAN_IOT** (by default, implicit deny):
|
||||
- *No rule needed* — VLANs are isolated by default
|
||||
- Optionally add explicit block rule for security
|
||||
|
||||
#### VLAN_AIWORKLOAD Rules
|
||||
Navigate: **Firewall → Rules → VLAN_AIWORKLOAD**
|
||||
|
||||
1. **Allow VLAN_AIWORKLOAD → WAN** (for outbound internet):
|
||||
- Action: `Pass`
|
||||
- Source: `VLAN_AIWORKLOAD subnet`
|
||||
- Destination: `any`
|
||||
- Protocol: `TCP/UDP`
|
||||
- Click **Save**
|
||||
|
||||
2. **Block VLAN_AIWORKLOAD → LAN_SECURE**:
|
||||
- Action: `Block`
|
||||
- Source: `VLAN_AIWORKLOAD subnet`
|
||||
- Destination: `LAN_SECURE subnet`
|
||||
- Click **Save**
|
||||
|
||||
#### VLAN_IOT Rules
|
||||
Navigate: **Firewall → Rules → VLAN_IOT**
|
||||
|
||||
1. **Allow VLAN_IOT → WAN** (for NTP, updates, cloud APIs):
|
||||
- Action: `Pass`
|
||||
- Source: `VLAN_IOT subnet`
|
||||
- Destination: `any`
|
||||
- Protocol: `TCP/UDP`
|
||||
- Click **Save**
|
||||
|
||||
2. **Block VLAN_IOT → LAN_SECURE**:
|
||||
- Action: `Block`
|
||||
- Source: `VLAN_IOT subnet`
|
||||
- Destination: `LAN_SECURE subnet`
|
||||
- Click **Save**
|
||||
|
||||
3. **Block VLAN_IOT → VLAN_AIWORKLOAD** (optional):
|
||||
- Action: `Block`
|
||||
- Source: `VLAN_IOT subnet`
|
||||
- Destination: `VLAN_AIWORKLOAD subnet`
|
||||
- Click **Save**
|
||||
|
||||
### Step 5: Configure Port Assignments (If Hardware Supports)
|
||||
|
||||
If your switch/NIC supports physical VLAN tagging:
|
||||
|
||||
Navigate: **Interfaces → Physical Ports** (varies by pfSense version)
|
||||
|
||||
Example configuration:
|
||||
- **Port 1**: LAN_SECURE (VLAN 1, untagged)
|
||||
- **Port 2**: VLAN_AIWORKLOAD (VLAN 2, tagged)
|
||||
- **Port 3**: VLAN_IOT (VLAN 3, tagged)
|
||||
- **Port 4**: WAN
|
||||
|
||||
*This step depends on your hardware. If using a managed switch, configure VLAN tagging there instead.*
|
||||
|
||||
## Network Access Matrix
|
||||
|
||||
Shows which VLANs can reach which destinations:
|
||||
|
||||
```
|
||||
FROM TO_LAN_SECURE TO_AIWORKLOAD TO_IOT TO_WAN
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
LAN_SECURE ✓ (same) ✗ BLOCK ✗ BLOCK ✓
|
||||
VLAN_AIWORKLOAD ✗ BLOCK ✓ (same) ✗ BLOCK ✓
|
||||
VLAN_IOT ✗ BLOCK ✗ BLOCK ✓ (same) ✓
|
||||
```
|
||||
|
||||
## Device Assignment
|
||||
|
||||
Assign devices to VLANs via:
|
||||
|
||||
1. **DHCP reservations** (Recommended):
|
||||
- Navigate: **Services → DHCP Server**
|
||||
- Tab: Desired VLAN
|
||||
- Scroll to **DHCP Static Mappings**
|
||||
- Add device MAC → static IP in that VLAN
|
||||
- Example: Openclaw server → `172.27.2.50` (VLAN_AIWORKLOAD)
|
||||
|
||||
2. **Manual static configuration**:
|
||||
- Configure device IP in the target VLAN subnet
|
||||
- Set gateway to VLAN gateway (172.27.2.1, 172.27.3.1, etc.)
|
||||
|
||||
3. **Switch port assignment** (if hardware supports):
|
||||
- Assign physical switch ports to VLANs
|
||||
- Devices connected to those ports get VLAN membership
|
||||
|
||||
## DNS Configuration
|
||||
|
||||
Navigate: **Services → DNS Resolver**
|
||||
|
||||
1. **Enable DNS Resolver**: ✓ Checked
|
||||
2. **Network Interfaces**: Select all interfaces (LAN, VLAN_AIWORKLOAD, VLAN_IOT)
|
||||
3. **Forward Mode**: Check if needed for external DNS
|
||||
4. **Access Lists**:
|
||||
- Ensure all VLANs can query DNS on their gateways
|
||||
|
||||
This allows devices in each VLAN to resolve hostnames locally.
|
||||
|
||||
## Backup & Recovery
|
||||
|
||||
### Automated Backup (Recommended)
|
||||
|
||||
Use the included backup utility script for automated, versioned backups:
|
||||
|
||||
```bash
|
||||
cd pfsense.home.arpa
|
||||
./scripts/backup-pfsense-config.sh
|
||||
```
|
||||
|
||||
This script:
|
||||
- Connects to pfSense via SSH (using your public key)
|
||||
- Downloads the current configuration XML
|
||||
- Validates it's a valid pfSense config
|
||||
- Stores it in `backups/` with a timestamped filename
|
||||
- Automatically commits to git with configuration details
|
||||
|
||||
**Schedule automated daily backups:**
|
||||
```bash
|
||||
# Add to crontab (backups every day at 2 AM)
|
||||
0 2 * * * cd /path/to/appa-net/pfsense.home.arpa && ./scripts/backup-pfsense-config.sh
|
||||
```
|
||||
|
||||
For more details, see: [scripts/README.md](scripts/README.md)
|
||||
|
||||
### Manual Backup
|
||||
|
||||
If you prefer manual backups or SSH isn't available:
|
||||
|
||||
1. Navigate: **Diagnostics → Backup & Restore**
|
||||
2. Click **Download configuration as XML**
|
||||
3. Save to: `pfsense.home.arpa/backups/pfsense-config-YYYY-MM-DD.xml`
|
||||
4. Commit manually:
|
||||
```bash
|
||||
git add backups/pfsense-config-2026-04-22.xml
|
||||
git commit -m "pfSense: Backup configuration (manual)"
|
||||
git push
|
||||
```
|
||||
|
||||
### Restoring Configuration
|
||||
|
||||
1. Navigate: **Diagnostics → Backup & Restore**
|
||||
2. Click **Choose File** and select XML backup
|
||||
3. Click **Restore Configuration**
|
||||
4. Reboot when prompted
|
||||
|
||||
## Maintenance Tasks
|
||||
|
||||
- **Monthly**: Export and backup pfSense configuration
|
||||
- **Quarterly**: Review firewall rules and DHCP assignments
|
||||
- **As needed**: Adjust rules based on new devices or requirements
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### VLANs not working:
|
||||
- Verify VLAN tags are correct in Interfaces → VLANs
|
||||
- Ensure virtual interfaces are enabled (Interfaces → Assignments)
|
||||
- Check physical switch VLAN configuration if using managed switch
|
||||
|
||||
### Devices can't get DHCP:
|
||||
- Verify DHCP is enabled for the VLAN
|
||||
- Check DHCP range is correct
|
||||
- Inspect DHCP leases: **Status → DHCP Leases**
|
||||
|
||||
### Can't ping between VLANs (expected):
|
||||
- Verify firewall rules are allowing or blocking as desired
|
||||
- Check rule order (first match wins)
|
||||
- Use **Diagnostics → Packet Capture** to debug
|
||||
|
||||
## References
|
||||
|
||||
- [pfSense VLAN Documentation](https://docs.netgate.com/pfsense/en/latest/vlan/index.html)
|
||||
- [pfSense Firewall Rules](https://docs.netgate.com/pfsense/en/latest/firewall/index.html)
|
||||
- RFC 2644 — VLAN tagging
|
||||
- RFC 5735 — Special Use IPv4 Addresses
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2026-04-22
|
||||
**Configuration Version:** 1.0
|
||||
173
pfsense.home.arpa/VLAN-CONFIG.md
Normal file
173
pfsense.home.arpa/VLAN-CONFIG.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# VLAN Configuration Reference
|
||||
|
||||
Network segmentation configuration for pfsense.home.arpa router.
|
||||
|
||||
## VLAN Definitions
|
||||
|
||||
```yaml
|
||||
vlans:
|
||||
lan_secure:
|
||||
vlan_id: 1
|
||||
description: "Main trusted network"
|
||||
subnet: "172.27.0.0/24"
|
||||
gateway: "172.27.0.1"
|
||||
dhcp_start: "172.27.0.100"
|
||||
dhcp_end: "172.27.0.200"
|
||||
purpose: "Primary network for personal/trusted devices"
|
||||
isolation: "Gateway to WAN, can access VLANs as configured"
|
||||
firewall_default: "allow_outbound"
|
||||
|
||||
vlan_aiworkload:
|
||||
vlan_id: 2
|
||||
description: "AI/ML Workload (Dangerous/OpenClaw)"
|
||||
subnet: "172.27.2.0/24"
|
||||
gateway: "172.27.2.1"
|
||||
dhcp_start: "172.27.2.100"
|
||||
dhcp_end: "172.27.2.200"
|
||||
purpose: "Isolated workload for AI/ML experiments, sandbox for untrusted code"
|
||||
isolation: "Blocked from LAN_SECURE, can access WAN"
|
||||
firewall_default: "deny_incoming, allow_outbound_to_wan"
|
||||
access_from_secure: "none" # LAN_SECURE cannot reach this VLAN
|
||||
|
||||
vlan_iot:
|
||||
vlan_id: 3
|
||||
description: "IoT Devices"
|
||||
subnet: "172.27.3.0/24"
|
||||
gateway: "172.27.3.1"
|
||||
dhcp_start: "172.27.3.100"
|
||||
dhcp_end: "172.27.3.200"
|
||||
purpose: "Smart home devices (cameras, sensors, thermostats, etc.)"
|
||||
isolation: "Blocked from LAN_SECURE, can access WAN for updates/APIs"
|
||||
firewall_default: "deny_incoming, allow_outbound_to_wan"
|
||||
access_from_secure: "none" # LAN_SECURE cannot reach this VLAN
|
||||
```
|
||||
|
||||
## Firewall Rule Summary
|
||||
|
||||
### From LAN_SECURE (172.27.0.0/24)
|
||||
- ✓ To Internet (WAN)
|
||||
- ✗ To VLAN_AIWORKLOAD (blocked)
|
||||
- ✗ To VLAN_IOT (blocked)
|
||||
- ✓ Internal (same subnet)
|
||||
|
||||
### From VLAN_AIWORKLOAD (172.27.2.0/24)
|
||||
- ✓ To Internet (WAN)
|
||||
- ✗ To LAN_SECURE (blocked)
|
||||
- ✗ To VLAN_IOT (blocked)
|
||||
- ✓ Internal (same subnet)
|
||||
|
||||
### From VLAN_IOT (172.27.3.0/24)
|
||||
- ✓ To Internet (WAN)
|
||||
- ✗ To LAN_SECURE (blocked)
|
||||
- ✗ To VLAN_AIWORKLOAD (blocked)
|
||||
- ✓ Internal (same subnet)
|
||||
|
||||
## DHCP Configuration
|
||||
|
||||
Each VLAN has its own DHCP server:
|
||||
|
||||
```
|
||||
VLAN_SECURE: 172.27.0.100 - 172.27.0.200 (Gateway: 172.27.0.1)
|
||||
VLAN_AIWORKLOAD: 172.27.2.100 - 172.27.2.200 (Gateway: 172.27.2.1)
|
||||
VLAN_IOT: 172.27.3.100 - 172.27.3.200 (Gateway: 172.27.3.1)
|
||||
```
|
||||
|
||||
**DNS Server** (for all VLANs): 172.27.0.1 (pfSense resolver)
|
||||
|
||||
## Physical Switch Configuration (If Applicable)
|
||||
|
||||
If using a managed switch, configure VLAN tagging:
|
||||
|
||||
```
|
||||
Port 1 (LAN_SECURE):
|
||||
- Mode: Access
|
||||
- VLAN: 1 (untagged, native)
|
||||
- Devices: Personal computers, laptops
|
||||
|
||||
Port 2 (VLAN_AIWORKLOAD):
|
||||
- Mode: Access
|
||||
- VLAN: 2 (untagged)
|
||||
- Devices: Openclaw server, GPU workstations
|
||||
- OR: Trunk (if pfSense applies tags)
|
||||
|
||||
Port 3 (VLAN_IOT):
|
||||
- Mode: Access
|
||||
- VLAN: 3 (untagged)
|
||||
- Devices: Smart home devices, cameras, sensors
|
||||
- OR: Trunk (if pfSense applies tags)
|
||||
|
||||
Port 4 (Uplink to pfSense):
|
||||
- Mode: Trunk
|
||||
- VLANs: 1, 2, 3
|
||||
- Tagged: 2, 3 (VLAN 1 typically untagged on trunk)
|
||||
```
|
||||
|
||||
## Device Assignments
|
||||
|
||||
Assign devices to VLANs using DHCP static mappings or by setting up switch port VLANs.
|
||||
|
||||
### Planned Devices
|
||||
|
||||
**VLAN_SECURE (LAN_SECURE):**
|
||||
- [ ] Your personal laptop/desktop
|
||||
- [ ] Network printer (if any)
|
||||
- [ ] Home automation controller (if trusted)
|
||||
|
||||
**VLAN_AIWORKLOAD (VLAN_AIWORKLOAD):**
|
||||
- [ ] Openclaw server / AI workstation
|
||||
- [ ] GPU compute server
|
||||
- [ ] Experimental machine learning environment
|
||||
|
||||
**VLAN_IOT (VLAN_IOT):**
|
||||
- [ ] Smart home cameras
|
||||
- [ ] Temperature/humidity sensors
|
||||
- [ ] Smart thermostat
|
||||
- [ ] IoT gateway (if not trusted)
|
||||
- [ ] Smart switches/outlets
|
||||
|
||||
## Implementation Checklist
|
||||
|
||||
- [ ] Create VLAN 2 (VLAN_AIWORKLOAD) on parent interface
|
||||
- [ ] Create VLAN 3 (VLAN_IOT) on parent interface
|
||||
- [ ] Apply VLAN changes
|
||||
- [ ] Create virtual interface for VLAN_AIWORKLOAD (OPT1)
|
||||
- [ ] Set IP: 172.27.2.1/24
|
||||
- [ ] Enable interface
|
||||
- [ ] Apply changes
|
||||
- [ ] Create virtual interface for VLAN_IOT (OPT2)
|
||||
- [ ] Set IP: 172.27.3.1/24
|
||||
- [ ] Enable interface
|
||||
- [ ] Apply changes
|
||||
- [ ] Configure DHCP for VLAN_AIWORKLOAD
|
||||
- [ ] Configure DHCP for VLAN_IOT
|
||||
- [ ] Configure firewall rules for LAN_SECURE
|
||||
- [ ] Configure firewall rules for VLAN_AIWORKLOAD
|
||||
- [ ] Configure firewall rules for VLAN_IOT
|
||||
- [ ] Test DHCP on each VLAN
|
||||
- [ ] Test inter-VLAN isolation
|
||||
- [ ] Backup pfSense configuration
|
||||
- [ ] Commit configuration to git
|
||||
|
||||
## Notes & Decisions
|
||||
|
||||
### Why These Subnets?
|
||||
- **172.27.x.x/16**: Private RFC 1918 range (172.16.0.0 - 172.31.255.255)
|
||||
- Each VLAN gets a /24 subnet (254 usable IPs per VLAN)
|
||||
- Easy to route and remember (VLAN ID = third octet)
|
||||
|
||||
### Why This Isolation?
|
||||
- **LAN_SECURE** ↔ **VLAN_AIWORKLOAD**: Complete isolation prevents compromised AI workload from reaching trusted devices
|
||||
- **LAN_SECURE** ↔ **VLAN_IOT**: IoT devices have broader vulnerabilities; isolation prevents lateral movement
|
||||
- **VLAN_AIWORKLOAD** ↔ **VLAN_IOT**: Reduces attack surface between untrusted zones
|
||||
- All VLANs → WAN: Allows devices to update, phone home, or reach cloud services
|
||||
|
||||
### Future Enhancements
|
||||
- Add guest VLAN for visitors
|
||||
- Configure VPN access to VLAN_SECURE only
|
||||
- Implement QoS rules per VLAN
|
||||
- Add Intrusion Detection (Suricata) on VLAN boundaries
|
||||
- Monitor inter-VLAN traffic in firewall logs
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2026-04-22
|
||||
291
pfsense.home.arpa/VLAN-QUICKSTART.md
Normal file
291
pfsense.home.arpa/VLAN-QUICKSTART.md
Normal file
@@ -0,0 +1,291 @@
|
||||
# VLAN Implementation Quickstart
|
||||
|
||||
Step-by-step checklist for configuring VLANs on pfSense. Use this guide to implement your 3-VLAN network.
|
||||
|
||||
## Pre-Implementation
|
||||
|
||||
Before you start, gather this information:
|
||||
|
||||
- [ ] pfSense WebUI URL (usually `https://192.168.1.1` or similar)
|
||||
- [ ] Admin credentials for pfSense
|
||||
- [ ] Your WAN/LAN interface names (check: **Interfaces → Assignments**)
|
||||
- [ ] Backup your current pfSense config (download before making changes)
|
||||
- [ ] Physical switch info (if you have one) — check if it supports VLAN tagging
|
||||
|
||||
## Phase 1: Create VLANs (5 minutes)
|
||||
|
||||
1. **Log into pfSense WebUI**
|
||||
- [ ] Open `https://[pfSense-IP]`
|
||||
- [ ] Enter admin credentials
|
||||
|
||||
2. **Navigate to VLAN Creation**
|
||||
- [ ] Go: **Interfaces → VLANs**
|
||||
- [ ] Click **Display Advanced**
|
||||
|
||||
3. **Create VLAN_AIWORKLOAD**
|
||||
- [ ] Click **+ Add**
|
||||
- [ ] Parent Interface: `em0` (or your LAN NIC name)
|
||||
- [ ] VLAN Tag: `2`
|
||||
- [ ] VLAN Priority: `0`
|
||||
- [ ] Description: `VLAN_AIWORKLOAD`
|
||||
- [ ] Click **Save**
|
||||
|
||||
4. **Create VLAN_IOT**
|
||||
- [ ] Click **+ Add**
|
||||
- [ ] Parent Interface: `em0`
|
||||
- [ ] VLAN Tag: `3`
|
||||
- [ ] VLAN Priority: `0`
|
||||
- [ ] Description: `VLAN_IOT`
|
||||
- [ ] Click **Save**
|
||||
|
||||
5. **Apply Changes**
|
||||
- [ ] Click **Apply Changes** button
|
||||
- [ ] Wait for reboot/apply to complete
|
||||
|
||||
## Phase 2: Assign Virtual Interfaces (5 minutes)
|
||||
|
||||
1. **Navigate to Assignments**
|
||||
- [ ] Go: **Interfaces → Assignments**
|
||||
|
||||
2. **Note down the OPT interfaces created**
|
||||
- [ ] You should see two new entries: e.g., `em0.2` and `em0.3`
|
||||
- [ ] These will be assigned as `OPT1` and `OPT2` (or similar)
|
||||
|
||||
3. **Click the OPT1 link** (VLAN_AIWORKLOAD)
|
||||
- [ ] Description: `VLAN_AIWORKLOAD`
|
||||
- [ ] IPv4 Configuration Type: `Static IPv4`
|
||||
- [ ] IPv4 Address: `172.27.2.1`
|
||||
- [ ] IPv4 Subnet Mask: `255.255.255.0`
|
||||
- [ ] IPv6 Configuration Type: `None`
|
||||
- [ ] **Enable Interface**: ✓ Check this box
|
||||
- [ ] Scroll down and click **Save**
|
||||
|
||||
4. **Repeat for OPT2** (VLAN_IOT)
|
||||
- [ ] Description: `VLAN_IOT`
|
||||
- [ ] IPv4 Configuration Type: `Static IPv4`
|
||||
- [ ] IPv4 Address: `172.27.3.1`
|
||||
- [ ] IPv4 Subnet Mask: `255.255.255.0`
|
||||
- [ ] **Enable Interface**: ✓ Check this box
|
||||
- [ ] Scroll down and click **Save**
|
||||
|
||||
5. **Apply Changes**
|
||||
- [ ] Click **Apply Changes** button
|
||||
|
||||
## Phase 3: Configure DHCP (10 minutes)
|
||||
|
||||
1. **Navigate to DHCP Server**
|
||||
- [ ] Go: **Services → DHCP Server**
|
||||
|
||||
2. **Configure VLAN_AIWORKLOAD DHCP**
|
||||
- [ ] Click **VLAN_AIWORKLOAD** tab
|
||||
- [ ] **Enable DHCP server on VLAN_AIWORKLOAD interface**: ✓ Check
|
||||
- [ ] **Range Start**: `172.27.2.100`
|
||||
- [ ] **Range End**: `172.27.2.200`
|
||||
- [ ] Scroll down to **Servers** section
|
||||
- [ ] **DNS 1**: `172.27.0.1` (pfSense)
|
||||
- [ ] **DNS 2**: `8.8.8.8` (optional backup)
|
||||
- [ ] **Gateway**: Should auto-populate as `172.27.2.1`
|
||||
- [ ] Scroll down and click **Save**
|
||||
|
||||
3. **Configure VLAN_IOT DHCP**
|
||||
- [ ] Click **VLAN_IOT** tab
|
||||
- [ ] **Enable DHCP server on VLAN_IOT interface**: ✓ Check
|
||||
- [ ] **Range Start**: `172.27.3.100`
|
||||
- [ ] **Range End**: `172.27.3.200`
|
||||
- [ ] Scroll down to **Servers** section
|
||||
- [ ] **DNS 1**: `172.27.0.1`
|
||||
- [ ] **DNS 2**: `8.8.8.8`
|
||||
- [ ] **Gateway**: Should auto-populate as `172.27.3.1`
|
||||
- [ ] Scroll down and click **Save**
|
||||
|
||||
4. **Verify LAN DHCP**
|
||||
- [ ] Click **LAN** tab
|
||||
- [ ] Confirm **Enable DHCP server on LAN interface** is ✓ checked
|
||||
- [ ] Verify gateway is `172.27.0.1`
|
||||
- [ ] Click **Save**
|
||||
|
||||
5. **Apply Changes**
|
||||
- [ ] Click **Apply Changes** button
|
||||
|
||||
## Phase 4: Configure Firewall Rules (15 minutes)
|
||||
|
||||
### LAN → VLAN Rules
|
||||
|
||||
1. **Go to LAN rules**
|
||||
- [ ] **Firewall → Rules → LAN**
|
||||
|
||||
2. **Add rule: Block LAN → VLAN_AIWORKLOAD**
|
||||
- [ ] Click **+ Add** (at bottom)
|
||||
- [ ] Action: `Block`
|
||||
- [ ] Interface: `LAN`
|
||||
- [ ] Direction: `in`
|
||||
- [ ] Address Family: `IPv4`
|
||||
- [ ] Protocol: `any`
|
||||
- [ ] Source: `LAN subnet` (or specify `172.27.0.0/24`)
|
||||
- [ ] Destination: `VLAN_AIWORKLOAD subnet` (specify `172.27.2.0/24`)
|
||||
- [ ] Description: `Block LAN → VLAN_AIWORKLOAD`
|
||||
- [ ] Click **Save**
|
||||
|
||||
3. **Add rule: Block LAN → VLAN_IOT**
|
||||
- [ ] Click **+ Add**
|
||||
- [ ] Action: `Block`
|
||||
- [ ] Interface: `LAN`
|
||||
- [ ] Source: `172.27.0.0/24`
|
||||
- [ ] Destination: `172.27.3.0/24`
|
||||
- [ ] Description: `Block LAN → VLAN_IOT`
|
||||
- [ ] Click **Save**
|
||||
|
||||
### VLAN_AIWORKLOAD Rules
|
||||
|
||||
1. **Go to VLAN_AIWORKLOAD rules**
|
||||
- [ ] **Firewall → Rules → VLAN_AIWORKLOAD** (or OPT1)
|
||||
|
||||
2. **Add rule: Block VLAN_AIWORKLOAD → LAN**
|
||||
- [ ] Click **+ Add**
|
||||
- [ ] Action: `Block`
|
||||
- [ ] Interface: `VLAN_AIWORKLOAD`
|
||||
- [ ] Source: `VLAN_AIWORKLOAD subnet` (specify `172.27.2.0/24`)
|
||||
- [ ] Destination: `LAN subnet` (specify `172.27.0.0/24`)
|
||||
- [ ] Description: `Block VLAN_AIWORKLOAD → LAN`
|
||||
- [ ] Click **Save**
|
||||
|
||||
3. **Add rule: Allow VLAN_AIWORKLOAD → WAN**
|
||||
- [ ] Click **+ Add**
|
||||
- [ ] Action: `Pass`
|
||||
- [ ] Interface: `VLAN_AIWORKLOAD`
|
||||
- [ ] Source: `VLAN_AIWORKLOAD subnet` (specify `172.27.2.0/24`)
|
||||
- [ ] Destination: `any`
|
||||
- [ ] Protocol: `any`
|
||||
- [ ] Description: `Allow VLAN_AIWORKLOAD → Internet`
|
||||
- [ ] Click **Save**
|
||||
|
||||
### VLAN_IOT Rules
|
||||
|
||||
1. **Go to VLAN_IOT rules**
|
||||
- [ ] **Firewall → Rules → VLAN_IOT** (or OPT2)
|
||||
|
||||
2. **Add rule: Block VLAN_IOT → LAN**
|
||||
- [ ] Click **+ Add**
|
||||
- [ ] Action: `Block`
|
||||
- [ ] Interface: `VLAN_IOT`
|
||||
- [ ] Source: `VLAN_IOT subnet` (specify `172.27.3.0/24`)
|
||||
- [ ] Destination: `LAN subnet` (specify `172.27.0.0/24`)
|
||||
- [ ] Description: `Block VLAN_IOT → LAN`
|
||||
- [ ] Click **Save**
|
||||
|
||||
3. **Add rule: Block VLAN_IOT → VLAN_AIWORKLOAD**
|
||||
- [ ] Click **+ Add**
|
||||
- [ ] Action: `Block`
|
||||
- [ ] Source: `172.27.3.0/24`
|
||||
- [ ] Destination: `172.27.2.0/24`
|
||||
- [ ] Description: `Block VLAN_IOT → VLAN_AIWORKLOAD`
|
||||
- [ ] Click **Save**
|
||||
|
||||
4. **Add rule: Allow VLAN_IOT → WAN**
|
||||
- [ ] Click **+ Add**
|
||||
- [ ] Action: `Pass`
|
||||
- [ ] Source: `VLAN_IOT subnet` (specify `172.27.3.0/24`)
|
||||
- [ ] Destination: `any`
|
||||
- [ ] Protocol: `any`
|
||||
- [ ] Description: `Allow VLAN_IOT → Internet`
|
||||
- [ ] Click **Save**
|
||||
|
||||
### Apply Firewall Changes
|
||||
|
||||
- [ ] Click **Apply Changes** button (usually at top of rules)
|
||||
|
||||
## Phase 5: Testing (10 minutes)
|
||||
|
||||
### Test DHCP
|
||||
|
||||
1. **Connect a test device to VLAN_AIWORKLOAD**
|
||||
- [ ] Assign a device to this VLAN (via switch port or manually)
|
||||
- [ ] Check if device gets IP in range 172.27.2.100-200
|
||||
- [ ] Verify gateway shows 172.27.2.1
|
||||
- [ ] Test ping to gateway: `ping 172.27.2.1` ✓
|
||||
|
||||
2. **Connect a test device to VLAN_IOT**
|
||||
- [ ] Assign a device to this VLAN
|
||||
- [ ] Check if device gets IP in range 172.27.3.100-200
|
||||
- [ ] Verify gateway shows 172.27.3.1
|
||||
- [ ] Test ping to gateway: `ping 172.27.3.1` ✓
|
||||
|
||||
### Test Inter-VLAN Isolation
|
||||
|
||||
1. **Test VLAN_AIWORKLOAD cannot reach LAN**
|
||||
- [ ] From device on VLAN_AIWORKLOAD (172.27.2.x)
|
||||
- [ ] Try ping to LAN device (172.27.0.x)
|
||||
- [ ] Should timeout/fail ✗ (expected)
|
||||
|
||||
2. **Test VLAN_IOT cannot reach LAN**
|
||||
- [ ] From device on VLAN_IOT (172.27.3.x)
|
||||
- [ ] Try ping to LAN device (172.27.0.x)
|
||||
- [ ] Should timeout/fail ✗ (expected)
|
||||
|
||||
3. **Test LAN cannot reach VLANs**
|
||||
- [ ] From LAN device (172.27.0.x)
|
||||
- [ ] Try ping to VLAN_AIWORKLOAD device (172.27.2.x)
|
||||
- [ ] Should timeout/fail ✗ (expected)
|
||||
|
||||
### Test Internet Access
|
||||
|
||||
1. **Test VLAN_AIWORKLOAD → Internet**
|
||||
- [ ] From device on VLAN_AIWORKLOAD
|
||||
- [ ] Test DNS: `nslookup google.com` ✓
|
||||
- [ ] Test internet: `ping 8.8.8.8` ✓
|
||||
|
||||
2. **Test VLAN_IOT → Internet**
|
||||
- [ ] From device on VLAN_IOT
|
||||
- [ ] Test DNS: `nslookup google.com` ✓
|
||||
- [ ] Test internet: `ping 8.8.8.8` ✓
|
||||
|
||||
## Phase 6: Backup & Documentation (5 minutes)
|
||||
|
||||
1. **Backup pfSense Configuration**
|
||||
- [ ] Go: **Diagnostics → Backup & Restore**
|
||||
- [ ] Click **Download configuration as XML**
|
||||
- [ ] Save as: `pfsense-config-vlan-setup-2026-04-22.xml`
|
||||
|
||||
2. **Commit to Git**
|
||||
```bash
|
||||
cd /Users/kenjim/workspace/src/personal/appa-net
|
||||
git add pfsense.home.arpa/
|
||||
git commit -m "pfSense: Initial VLAN configuration (VLAN_AIWORKLOAD, VLAN_IOT)"
|
||||
git push
|
||||
```
|
||||
- [ ] Commit completed
|
||||
|
||||
3. **Document Completion**
|
||||
- [ ] Update this file with completion date
|
||||
- [ ] Note any deviations from plan
|
||||
- [ ] Record interface names if different from expected
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| Device not getting DHCP | Check DHCP is enabled for that VLAN in **Services → DHCP Server** |
|
||||
| Can't ping gateway | Verify virtual interface is enabled (**Interfaces → Assignments**) |
|
||||
| Can't reach internet | Check WAN allow rules in firewall |
|
||||
| Still can reach between VLANs | Check firewall rules order (first match wins); rules may be in wrong order |
|
||||
| Switch not forwarding VLAN traffic | Verify trunk port on switch is tagged for all VLANs |
|
||||
|
||||
## Post-Implementation
|
||||
|
||||
Once everything is working:
|
||||
|
||||
1. **Assign your devices** to VLANs via DHCP static mappings
|
||||
- See [VLAN-CONFIG.md](VLAN-CONFIG.md) for device list
|
||||
|
||||
2. **Monitor firewall logs** for unexpected traffic
|
||||
- Go: **Status → System Logs → Firewall**
|
||||
|
||||
3. **Update your documentation** as you add more devices
|
||||
|
||||
4. **Schedule regular backups**
|
||||
- Monthly: Export pfSense config to `backups/pfsense-config-YYYY-MM-DD.xml`
|
||||
|
||||
---
|
||||
|
||||
**Estimated Total Time**: 45 minutes
|
||||
**Last Updated**: 2026-04-22
|
||||
262
pfsense.home.arpa/VLAN-TOPOLOGY.md
Normal file
262
pfsense.home.arpa/VLAN-TOPOLOGY.md
Normal file
@@ -0,0 +1,262 @@
|
||||
# VLAN Network Topology
|
||||
|
||||
Visual representation of your segmented home network architecture.
|
||||
|
||||
## High-Level Topology
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ Internet │
|
||||
│ (WAN) │
|
||||
└────────┬────────┘
|
||||
│
|
||||
┌──────────┴──────────┐
|
||||
│ │
|
||||
┌────▼─────────────────────▼────┐
|
||||
│ pfSense Router │
|
||||
│ (172.27.0.1) │
|
||||
│ │
|
||||
│ • DHCP Server │
|
||||
│ • DNS Resolver │
|
||||
│ • Firewall │
|
||||
│ • VLAN Gateway │
|
||||
└────┬────────┬────────┬────────┘
|
||||
│ │ │
|
||||
┌───────────┘ │ └──────────┐
|
||||
│ │ │
|
||||
┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐
|
||||
│ VLAN 1 │ │ VLAN 2 │ │ VLAN 3 │
|
||||
│ LAN_SECURE │ │ AIWORKLOAD │ │ IOT │
|
||||
│ 172.27.0.0 │ │ 172.27.2.0 │ │ 172.27.3.0 │
|
||||
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
|
||||
│ │ │
|
||||
│ │ │
|
||||
┌──────▼────────┐ ┌──────▼────────┐ ┌──────▼────────┐
|
||||
│ │ │ │ │ │
|
||||
│ Trusted Devices│ │ Openclaw │ │ IoT Devices │
|
||||
│ │ │ GPU Workload │ │ │
|
||||
│ • Laptop │ │ │ │ • Cameras │
|
||||
│ • Desktop │ │ (Sandbox/ │ │ • Sensors │
|
||||
│ • Phone │ │ Experiment) │ │ • Thermostat │
|
||||
│ │ │ │ │ • Smart Outlets│
|
||||
└────────────────┘ └────────────────┘ └────────────────┘
|
||||
```
|
||||
|
||||
## Detailed Dataflow
|
||||
|
||||
### Device to Internet (All VLANs)
|
||||
```
|
||||
Device (VLAN X)
|
||||
↓
|
||||
Gateway (172.27.X.1)
|
||||
↓
|
||||
pfSense Firewall
|
||||
↓
|
||||
WAN Interface
|
||||
↓
|
||||
Internet Router/Modem
|
||||
↓
|
||||
Internet ✓
|
||||
```
|
||||
|
||||
### Trusted to Untrusted (Blocked)
|
||||
```
|
||||
LAN_SECURE Device (172.27.0.100)
|
||||
↓
|
||||
Request to VLAN_AIWORKLOAD (172.27.2.X)
|
||||
↓
|
||||
pfSense Firewall Rule: BLOCK
|
||||
↓
|
||||
X Connection Refused
|
||||
```
|
||||
|
||||
## Firewall Rule Chain
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Inbound Packet on Interface (e.g., LAN_SECURE) │
|
||||
└────────────────────┬────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌────────────────────────┐
|
||||
│ Source IP in subnet? │
|
||||
│ (172.27.0.0/24) │
|
||||
└────────┬───────┬────────┘
|
||||
│ │
|
||||
YES│ │NO → Block (rule 1)
|
||||
│ │
|
||||
▼ │
|
||||
┌────────────────────────┐
|
||||
│ Destination Subnet? │
|
||||
└────────┬───────┬────────┘
|
||||
│ │
|
||||
172.27.0.0/24 172.27.2.0/24 172.27.3.0/24
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
Same VLAN ALLOW (rule 2) BLOCK (rule 3)
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
✓ PASS ✓ PASS ✗ BLOCK
|
||||
```
|
||||
|
||||
## Port Connectivity (Example with 4-Port Switch)
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────┐
|
||||
│ Managed Network Switch │
|
||||
│ (or pfSense internal if no switch) │
|
||||
├──────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Port 1 (Access, VLAN 1) │
|
||||
│ ├─ Trusted Device 1 │
|
||||
│ └─ Trusted Device 2 │
|
||||
│ │
|
||||
│ Port 2 (Access, VLAN 2) │
|
||||
│ ├─ Openclaw Server │
|
||||
│ └─ GPU Workstation │
|
||||
│ │
|
||||
│ Port 3 (Access, VLAN 3) │
|
||||
│ ├─ Smart Camera 1 │
|
||||
│ ├─ Smart Camera 2 │
|
||||
│ ├─ IoT Sensor │
|
||||
│ └─ Smart Thermostat │
|
||||
│ │
|
||||
│ Port 4 (Trunk - All VLANs Tagged) │
|
||||
│ └─ pfSense Router │
|
||||
│ (Receives VLAN-tagged frames) │
|
||||
│ │
|
||||
└──────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Traffic Examples
|
||||
|
||||
### ✓ Allowed Traffic Paths
|
||||
|
||||
```
|
||||
1. Trusted Device → Internet
|
||||
172.27.0.100 → 8.8.8.8:53
|
||||
Gateway: 172.27.0.1 → pfSense → WAN → Internet ✓
|
||||
|
||||
2. AI Workload → Internet
|
||||
172.27.2.50 → updates.example.com:443
|
||||
Gateway: 172.27.2.1 → pfSense → WAN → Internet ✓
|
||||
|
||||
3. IoT Device → NTP Server
|
||||
172.27.3.102 → pool.ntp.org:123
|
||||
Gateway: 172.27.3.1 → pfSense → WAN → Internet ✓
|
||||
|
||||
4. Trusted Device → Trusted Device (same VLAN)
|
||||
172.27.0.100 → 172.27.0.150 (same broadcast domain) ✓
|
||||
```
|
||||
|
||||
### ✗ Blocked Traffic Paths
|
||||
|
||||
```
|
||||
1. Trusted → AI Workload
|
||||
172.27.0.100 → 172.27.2.50
|
||||
Firewall Rule: BLOCK ✗
|
||||
(Prevents lateral movement if AI workload is compromised)
|
||||
|
||||
2. Trusted → IoT Device
|
||||
172.27.0.100 → 172.27.3.100
|
||||
Firewall Rule: BLOCK ✗
|
||||
(Prevents IoT compromise affecting trusted devices)
|
||||
|
||||
3. AI Workload → Trusted Device (reverse)
|
||||
172.27.2.50 → 172.27.0.100
|
||||
Firewall Rule: BLOCK ✗
|
||||
(Prevents compromised workload from scanning trusted network)
|
||||
|
||||
4. IoT → AI Workload
|
||||
172.27.3.102 → 172.27.2.50
|
||||
Firewall Rule: BLOCK ✗
|
||||
(Reduces attack surface between untrusted zones)
|
||||
```
|
||||
|
||||
## VLAN 802.1Q Tagging (Switch-Level)
|
||||
|
||||
If using a managed switch with VLAN support:
|
||||
|
||||
```
|
||||
Frame from pfSense Port 4 (Trunk):
|
||||
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Ethernet Header │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ Destination MAC | Source MAC | 802.1Q Tag | Type │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ AA:BB:CC:DD:EE | 11:22:33:44:55:66 | VLAN: 2 | IPv4│
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ IPv4 Payload (IP Header + Data) │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
|
||||
When frame arrives at Port 2 (Access, VLAN 2):
|
||||
├─ Switch removes 802.1Q tag
|
||||
├─ Delivers untagged frame to device
|
||||
└─ Device sees: AA:BB:CC:DD:EE → 11:22:33:44:55:66 [IPv4 Data]
|
||||
```
|
||||
|
||||
## Isolation Guarantees
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ VLAN Isolation Mechanisms │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 1. Layer 2 (Link Layer) │
|
||||
│ └─ VLANs have separate broadcast domains │
|
||||
│ └─ ARP packets don't cross VLAN boundaries │
|
||||
│ └─ Broadcast storms are contained │
|
||||
│ │
|
||||
│ 2. Layer 3 (Network Layer) │
|
||||
│ └─ Different subnets per VLAN │
|
||||
│ └─ Devices can't directly route between VLANs│
|
||||
│ └─ Must go through Layer 3 gateway (pfSense) │
|
||||
│ │
|
||||
│ 3. Firewall Rules (pfSense) │
|
||||
│ └─ Explicit deny between VLANs (unless allowed) │
|
||||
│ └─ Stateful inspection prevents spoofing │
|
||||
│ └─ Rate limiting and IDS possible │
|
||||
│ │
|
||||
│ 4. Switch-Level Isolation (if applicable) │
|
||||
│ └─ 802.1Q VLAN tags ensure switch-level routing │
|
||||
│ └─ Malformed frames or tag injection blocked │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Routing Summary
|
||||
|
||||
```
|
||||
Routing Table on pfSense:
|
||||
|
||||
Destination Next Hop Interface Metric
|
||||
────────────────────────────────────────────────────────────
|
||||
0.0.0.0/0 192.168.1.1 WAN 1
|
||||
172.27.0.0/24 direct LAN (VLAN 1) 0
|
||||
172.27.2.0/24 direct OPT1 (VLAN 2) 0
|
||||
172.27.3.0/24 direct OPT2 (VLAN 3) 0
|
||||
```
|
||||
|
||||
### How Routing Works
|
||||
|
||||
1. **Packet from LAN_SECURE to VLAN_AIWORKLOAD:**
|
||||
- Source: 172.27.0.100, Dest: 172.27.2.50
|
||||
- pfSense checks routing table
|
||||
- Destination 172.27.2.0/24 → exists on OPT1
|
||||
- pfSense checks firewall rule for LAN → OPT1
|
||||
- Rule says: BLOCK
|
||||
- Packet is dropped ✗
|
||||
|
||||
2. **Packet from VLAN_AIWORKLOAD to Internet:**
|
||||
- Source: 172.27.2.50, Dest: 8.8.8.8
|
||||
- pfSense checks routing table
|
||||
- Destination 8.8.8.8 → matches 0.0.0.0/0 (default route)
|
||||
- Next hop: WAN gateway (192.168.1.1)
|
||||
- pfSense checks firewall rule for OPT1 → WAN
|
||||
- Rule says: ALLOW
|
||||
- Packet forwarded to WAN ✓
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2026-04-22
|
||||
11
pfsense.home.arpa/backups/.gitkeep
Normal file
11
pfsense.home.arpa/backups/.gitkeep
Normal file
@@ -0,0 +1,11 @@
|
||||
# Backups Directory
|
||||
|
||||
pfSense configuration backups are stored here with timestamped filenames.
|
||||
|
||||
**DO NOT delete backup files without understanding the implications.**
|
||||
|
||||
Format: `pfsense-config-YYYY-MM-DD_HHMMSS.xml`
|
||||
|
||||
All backup files are committed to git, so previous versions can be restored from git history if needed.
|
||||
|
||||
For backup automation, see: [scripts/README.md](../scripts/README.md)
|
||||
226
pfsense.home.arpa/scripts/README.md
Normal file
226
pfsense.home.arpa/scripts/README.md
Normal file
@@ -0,0 +1,226 @@
|
||||
# pfSense Backup Scripts
|
||||
|
||||
Automation utilities for backing up and managing pfSense configuration.
|
||||
|
||||
## Scripts
|
||||
|
||||
### `backup-pfsense-config.sh`
|
||||
|
||||
Automated backup utility that:
|
||||
- Connects to your pfSense router via SSH (using public key authentication)
|
||||
- Downloads the current configuration XML
|
||||
- Validates the backup is a valid pfSense config
|
||||
- Stores it in `backups/` folder with a timestamped filename
|
||||
- Automatically commits to git with a detailed message
|
||||
|
||||
#### Usage
|
||||
|
||||
**Basic usage (default host):**
|
||||
```bash
|
||||
cd pfsense.home.arpa
|
||||
./scripts/backup-pfsense-config.sh
|
||||
```
|
||||
|
||||
**Specify custom host:**
|
||||
```bash
|
||||
./scripts/backup-pfsense-config.sh 192.168.1.1
|
||||
./scripts/backup-pfsense-config.sh pfsense.home.arpa
|
||||
```
|
||||
|
||||
**Download without auto-committing to git:**
|
||||
```bash
|
||||
./scripts/backup-pfsense-config.sh --no-commit
|
||||
# or
|
||||
./scripts/backup-pfsense-config.sh -n
|
||||
```
|
||||
|
||||
**Test SSH connectivity (dry-run):**
|
||||
```bash
|
||||
./scripts/backup-pfsense-config.sh --dry-run
|
||||
```
|
||||
|
||||
**Show help:**
|
||||
```bash
|
||||
./scripts/backup-pfsense-config.sh --help
|
||||
# or
|
||||
./scripts/backup-pfsense-config.sh -h
|
||||
```
|
||||
|
||||
#### Prerequisites
|
||||
|
||||
1. **SSH Public Key Authentication**
|
||||
- Your public key must be installed on pfSense
|
||||
- Run on pfSense: `cat ~/.ssh/authorized_keys` to verify
|
||||
- If not installed, manually copy your key: `ssh-copy-id root@pfsense`
|
||||
|
||||
2. **SSH Host Configuration** (recommended)
|
||||
- Add to your `~/.ssh/config`:
|
||||
```
|
||||
Host pfsense
|
||||
HostName 172.27.0.1 # or your pfSense IP
|
||||
User root
|
||||
IdentityFile ~/.ssh/id_rsa # or your key path
|
||||
```
|
||||
- This allows `ssh pfsense` to work directly
|
||||
|
||||
3. **Git Repository**
|
||||
- Must be run from within a git repository
|
||||
- The script auto-commits backups to git
|
||||
|
||||
4. **Dependencies**
|
||||
- `bash` (4.0+)
|
||||
- `ssh` and `scp`
|
||||
- `git`
|
||||
- `du`, `shasum`, `grep` (standard Unix utilities)
|
||||
|
||||
#### Features
|
||||
|
||||
- ✅ **Automatic validation** — Confirms backup is valid XML and contains pfSense markers
|
||||
- ✅ **Timestamped files** — Format: `pfsense-config-YYYY-MM-DD_HHMMSS.xml`
|
||||
- ✅ **Rich git commits** — Includes VLAN count, firewall rule count, SHA256 hash
|
||||
- ✅ **Error handling** — Clear error messages and troubleshooting tips
|
||||
- ✅ **Dry-run mode** — Test SSH connectivity without downloading
|
||||
- ✅ **Human-readable output** — Color-coded info/warning/error messages
|
||||
- ✅ **Configurable** — Via environment variables or command-line arguments
|
||||
|
||||
#### Example Output
|
||||
|
||||
```
|
||||
╔════════════════════════════════════════════════════════════════╗
|
||||
║ pfSense Configuration Backup Utility ║
|
||||
╚════════════════════════════════════════════════════════════════╝
|
||||
|
||||
[INFO] Configuration:
|
||||
Host: pfsense
|
||||
User: root
|
||||
Remote path: /conf/config.xml
|
||||
Backup dir: ./backups
|
||||
Auto-commit: true
|
||||
|
||||
[INFO] Checking prerequisites...
|
||||
[✓] SSH is available
|
||||
[✓] Git is available
|
||||
[✓] Backup directory exists
|
||||
[✓] Git repository found
|
||||
|
||||
[INFO] Testing SSH connection to pfsense...
|
||||
[✓] SSH connection to pfsense successful
|
||||
|
||||
[INFO] Fetching pfSense configuration from pfsense...
|
||||
[✓] Configuration downloaded to ./backups/pfsense-config-2026-04-22_143022.xml
|
||||
[INFO] Backup size: 68K
|
||||
|
||||
[INFO] Validating backup file...
|
||||
[✓] Backup file is valid XML with pfSense markers
|
||||
|
||||
[INFO] Committing backup to git repository...
|
||||
[✓] Added backup to git staging area
|
||||
[✓] Configuration committed to git
|
||||
|
||||
════════════════════════════════════════════════════════════════
|
||||
[✓] pfSense configuration backup completed successfully!
|
||||
════════════════════════════════════════════════════════════════
|
||||
|
||||
Backup Details:
|
||||
Location: ./backups/pfsense-config-2026-04-22_143022.xml
|
||||
Size: 68K
|
||||
Timestamp: 2026-04-22_143022
|
||||
From: root@pfsense:/conf/config.xml
|
||||
|
||||
Next Steps:
|
||||
1. Review the changes: git show HEAD
|
||||
2. Push to remote: git push origin main
|
||||
3. Schedule automated backups in crontab
|
||||
```
|
||||
|
||||
#### Scheduling Automated Backups
|
||||
|
||||
To backup your pfSense config automatically every day at 2 AM:
|
||||
|
||||
1. **Edit your crontab:**
|
||||
```bash
|
||||
crontab -e
|
||||
```
|
||||
|
||||
2. **Add this line:**
|
||||
```cron
|
||||
0 2 * * * cd /Users/kenjim/workspace/src/personal/appa-net/pfsense.home.arpa && ./scripts/backup-pfsense-config.sh
|
||||
```
|
||||
|
||||
3. **Verify the entry:**
|
||||
```bash
|
||||
crontab -l | grep backup-pfsense
|
||||
```
|
||||
|
||||
Now your config will be backed up automatically every day!
|
||||
|
||||
#### Troubleshooting
|
||||
|
||||
| Problem | Solution |
|
||||
|---------|----------|
|
||||
| `Permission denied (publickey)` | Install public key: `ssh-copy-id root@pfsense` |
|
||||
| `Connection refused` | Check pfSense IP/hostname is correct |
|
||||
| `No such file or directory: /conf/config.xml` | Not running on pfSense; check SSH host |
|
||||
| `XML validation failed` | Config may be corrupted; try manual backup via WebUI |
|
||||
| Git commit fails | Ensure you're in the git repository root |
|
||||
|
||||
#### Environment Variables
|
||||
|
||||
Control script behavior with environment variables:
|
||||
|
||||
```bash
|
||||
# Use different SSH user
|
||||
PFSENSE_USER=admin ./scripts/backup-pfsense-config.sh
|
||||
|
||||
# Disable auto-commit
|
||||
AUTO_COMMIT=false ./scripts/backup-pfsense-config.sh
|
||||
|
||||
# Combine both
|
||||
PFSENSE_USER=admin AUTO_COMMIT=false ./scripts/backup-pfsense-config.sh pfsense.home.arpa
|
||||
```
|
||||
|
||||
## Backup Files Location
|
||||
|
||||
All backups are stored in the `backups/` directory:
|
||||
|
||||
```
|
||||
backups/
|
||||
├── pfsense-config-2026-04-22_143022.xml
|
||||
├── pfsense-config-2026-04-21_023001.xml
|
||||
└── pfsense-config-2026-04-20_023000.xml
|
||||
```
|
||||
|
||||
### Viewing Differences Between Backups
|
||||
|
||||
Compare two configurations:
|
||||
|
||||
```bash
|
||||
# Show what changed between two backups
|
||||
diff backups/pfsense-config-2026-04-22_143022.xml backups/pfsense-config-2026-04-21_023001.xml
|
||||
|
||||
# Or use git to see changes across commits
|
||||
git log --oneline backups/
|
||||
git diff HEAD~1..HEAD -- backups/pfsense-config-*.xml
|
||||
```
|
||||
|
||||
### Restoring from a Backup
|
||||
|
||||
To restore a previous configuration on pfSense:
|
||||
|
||||
1. Download the backup file from this repository
|
||||
2. Log into pfSense WebUI
|
||||
3. Go: **Diagnostics → Backup & Restore**
|
||||
4. Choose the backup file and click **Restore Configuration**
|
||||
5. Reboot when prompted
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- [ ] Compress old backups to save space
|
||||
- [ ] Upload to cloud storage (S3, etc.)
|
||||
- [ ] Encrypt sensitive configs before storing
|
||||
- [ ] Notify on significant changes (email alert)
|
||||
- [ ] Generate change reports showing diffs
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2026-04-22
|
||||
388
pfsense.home.arpa/scripts/backup-pfsense-config.sh
Executable file
388
pfsense.home.arpa/scripts/backup-pfsense-config.sh
Executable file
@@ -0,0 +1,388 @@
|
||||
#!/bin/bash
|
||||
|
||||
################################################################################
|
||||
# pfSense Configuration Backup Utility
|
||||
#
|
||||
# This script automatically backs up your pfSense router configuration and
|
||||
# stores it in the repository with a timestamped filename.
|
||||
#
|
||||
# Prerequisites:
|
||||
# - SSH access to pfSense router (with public key authentication)
|
||||
# - SSH host entry configured (default: 'pfsense' in ~/.ssh/config)
|
||||
# - Git repository initialized in the current working directory
|
||||
# - Sufficient disk space for configuration backup (typically < 1MB)
|
||||
#
|
||||
# Usage:
|
||||
# ./backup-pfsense-config.sh # Uses default host 'pfsense'
|
||||
# ./backup-pfsense-config.sh pfsense.home.arpa # Specify custom host
|
||||
# ./backup-pfsense-config.sh -h # Show help
|
||||
#
|
||||
################################################################################
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Configuration (set defaults)
|
||||
PFSENSE_HOST="pfsense" # SSH host (from ~/.ssh/config or IP)
|
||||
PFSENSE_USER="${PFSENSE_USER:-root}" # SSH user
|
||||
PFSENSE_CONFIG_PATH="/conf/config.xml"
|
||||
BACKUP_DIR="$(dirname "$0")/../backups"
|
||||
TIMESTAMP="$(date +%Y-%m-%d_%H%M%S)"
|
||||
BACKUP_FILE="${BACKUP_DIR}/pfsense-config-${TIMESTAMP}.xml"
|
||||
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo ".")"
|
||||
AUTO_COMMIT="${AUTO_COMMIT:-true}"
|
||||
|
||||
# Color codes for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
################################################################################
|
||||
# Helper Functions
|
||||
################################################################################
|
||||
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[✓]${NC} $1"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1" >&2
|
||||
}
|
||||
|
||||
show_help() {
|
||||
cat << EOF
|
||||
Usage: $(basename "$0") [OPTIONS] [HOST]
|
||||
|
||||
Backup pfSense router configuration to repository.
|
||||
|
||||
ARGUMENTS:
|
||||
HOST SSH host or IP address (default: 'pfsense')
|
||||
Should be an entry in ~/.ssh/config or IP address
|
||||
Example: pfsense, 192.168.1.1, pfsense.home.arpa
|
||||
|
||||
OPTIONS:
|
||||
-h, --help Show this help message
|
||||
-n, --no-commit Download config without auto-committing to git
|
||||
--dry-run Show what would be done without making changes
|
||||
|
||||
ENVIRONMENT VARIABLES:
|
||||
PFSENSE_USER SSH user (default: 'root')
|
||||
AUTO_COMMIT Whether to auto-commit (default: 'true')
|
||||
|
||||
EXAMPLES:
|
||||
# Backup using default host and auto-commit
|
||||
$ ./backup-pfsense-config.sh
|
||||
|
||||
# Backup specific host
|
||||
$ ./backup-pfsense-config.sh 172.27.0.1
|
||||
|
||||
# Backup without auto-commit
|
||||
$ ./backup-pfsense-config.sh -n
|
||||
|
||||
# Dry run (test connectivity)
|
||||
$ ./backup-pfsense-config.sh --dry-run
|
||||
|
||||
CONFIGURATION:
|
||||
SSH host entry: ${PFSENSE_HOST}
|
||||
SSH user: ${PFSENSE_USER}
|
||||
Backup directory: ${BACKUP_DIR}
|
||||
Config file: ${PFSENSE_CONFIG_PATH}
|
||||
|
||||
NOTES:
|
||||
- Requires SSH public key authentication to pfSense
|
||||
- First run may prompt to accept SSH key fingerprint
|
||||
- Backup files are stored with timestamps (YYYY-MM-DD_HHMMSS)
|
||||
- Backups are committed to git with descriptive messages
|
||||
- Each backup is human-readable XML for easy diffs
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
check_prerequisites() {
|
||||
log_info "Checking prerequisites..."
|
||||
|
||||
# Check SSH connectivity
|
||||
if ! command -v ssh &> /dev/null; then
|
||||
log_error "SSH is not installed or not in PATH"
|
||||
return 1
|
||||
fi
|
||||
log_success "SSH is available"
|
||||
|
||||
# Check git
|
||||
if ! command -v git &> /dev/null; then
|
||||
log_error "Git is not installed or not in PATH"
|
||||
return 1
|
||||
fi
|
||||
log_success "Git is available"
|
||||
|
||||
# Check backup directory exists
|
||||
if [[ ! -d "${BACKUP_DIR}" ]]; then
|
||||
log_warning "Backup directory does not exist: ${BACKUP_DIR}"
|
||||
log_info "Creating backup directory..."
|
||||
mkdir -p "${BACKUP_DIR}" || {
|
||||
log_error "Failed to create backup directory"
|
||||
return 1
|
||||
}
|
||||
log_success "Backup directory created"
|
||||
fi
|
||||
|
||||
# Verify we're in a git repository
|
||||
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
||||
log_error "Not in a git repository (checked from $(pwd))"
|
||||
return 1
|
||||
fi
|
||||
log_success "Git repository found"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
test_ssh_connection() {
|
||||
log_info "Testing SSH connection to ${PFSENSE_HOST}..."
|
||||
|
||||
if ssh -o ConnectTimeout=10 "${PFSENSE_USER}@${PFSENSE_HOST}" "echo 'SSH connection successful'" > /dev/null 2>&1; then
|
||||
log_success "SSH connection to ${PFSENSE_HOST} successful"
|
||||
return 0
|
||||
else
|
||||
log_error "Failed to connect to ${PFSENSE_HOST} via SSH"
|
||||
log_info "Troubleshooting tips:"
|
||||
log_info " 1. Verify SSH host is configured: ssh-keygen -F ${PFSENSE_HOST}"
|
||||
log_info " 2. Test SSH manually: ssh ${PFSENSE_USER}@${PFSENSE_HOST}"
|
||||
log_info " 3. Check firewall allows SSH (port 22) from your machine"
|
||||
log_info " 4. Verify public key is installed on pfSense: /root/.ssh/authorized_keys"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
fetch_config() {
|
||||
log_info "Fetching pfSense configuration from ${PFSENSE_HOST}..."
|
||||
|
||||
# Use scp to copy the remote file
|
||||
if ! scp "${PFSENSE_USER}@${PFSENSE_HOST}:${PFSENSE_CONFIG_PATH}" "${BACKUP_FILE}" 2>/dev/null; then
|
||||
log_error "Failed to download configuration from ${PFSENSE_HOST}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "${BACKUP_FILE}" ]]; then
|
||||
log_error "Backup file was not created: ${BACKUP_FILE}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "Configuration downloaded to ${BACKUP_FILE}"
|
||||
|
||||
# Show file size
|
||||
local file_size
|
||||
file_size=$(du -h "${BACKUP_FILE}" | cut -f1)
|
||||
log_info "Backup size: ${file_size}"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
validate_config() {
|
||||
log_info "Validating backup file..."
|
||||
|
||||
# Check if file is valid XML
|
||||
if ! grep -q '<?xml' "${BACKUP_FILE}"; then
|
||||
log_error "Backup file does not appear to be valid XML"
|
||||
log_warning "Removing invalid backup: ${BACKUP_FILE}"
|
||||
rm -f "${BACKUP_FILE}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check for pfSense-specific markers
|
||||
if ! grep -q '<pfsense' "${BACKUP_FILE}"; then
|
||||
log_warning "Backup file may not be a valid pfSense configuration"
|
||||
log_info "File contents (first 20 lines):"
|
||||
head -20 "${BACKUP_FILE}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_success "Backup file is valid XML with pfSense markers"
|
||||
return 0
|
||||
}
|
||||
|
||||
commit_to_git() {
|
||||
log_info "Committing backup to git repository..."
|
||||
|
||||
local relative_path
|
||||
relative_path=$(git -C "${REPO_ROOT}" ls-files --full-name "${BACKUP_FILE}" 2>/dev/null || echo "${BACKUP_FILE}")
|
||||
|
||||
if [[ -z "${relative_path}" ]]; then
|
||||
relative_path="${BACKUP_FILE}"
|
||||
fi
|
||||
|
||||
# Add the backup file to git
|
||||
if git -C "${REPO_ROOT}" add "${BACKUP_FILE}"; then
|
||||
log_success "Added backup to git staging area"
|
||||
else
|
||||
log_error "Failed to add backup to git"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Create a meaningful commit message
|
||||
local short_date
|
||||
short_date=$(date +%Y-%m-%d\ %H:%M:%S)
|
||||
local commit_msg="pfSense: Backup configuration (${short_date})"
|
||||
|
||||
# Get configuration summary from XML
|
||||
local vlan_count
|
||||
vlan_count=$(grep -c '<vlan>' "${BACKUP_FILE}" 2>/dev/null || echo "unknown")
|
||||
local rule_count
|
||||
rule_count=$(grep -c '<rule>' "${BACKUP_FILE}" 2>/dev/null || echo "unknown")
|
||||
|
||||
# Enhanced commit message with details
|
||||
local detailed_msg="${commit_msg}
|
||||
|
||||
Contains:
|
||||
- VLANs: ${vlan_count}
|
||||
- Firewall rules: ${rule_count}
|
||||
- Backup timestamp: ${TIMESTAMP}
|
||||
|
||||
File: $(basename "${BACKUP_FILE}")
|
||||
SHA256: $(shasum -a 256 "${BACKUP_FILE}" | awk '{print $1}')
|
||||
"
|
||||
|
||||
# Commit the backup
|
||||
if git -C "${REPO_ROOT}" commit -m "${detailed_msg}" > /dev/null 2>&1; then
|
||||
log_success "Configuration committed to git"
|
||||
log_info "Commit message:"
|
||||
echo "${detailed_msg}" | sed 's/^/ /'
|
||||
return 0
|
||||
else
|
||||
log_warning "No changes to commit (file may be identical to previous backup)"
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
show_summary() {
|
||||
echo ""
|
||||
echo "════════════════════════════════════════════════════════════════"
|
||||
log_success "pfSense configuration backup completed successfully!"
|
||||
echo "════════════════════════════════════════════════════════════════"
|
||||
echo ""
|
||||
echo "Backup Details:"
|
||||
echo " Location: ${BACKUP_FILE}"
|
||||
echo " Size: $(du -h "${BACKUP_FILE}" | cut -f1)"
|
||||
echo " Timestamp: ${TIMESTAMP}"
|
||||
echo " From: ${PFSENSE_USER}@${PFSENSE_HOST}:${PFSENSE_CONFIG_PATH}"
|
||||
echo ""
|
||||
echo "Next Steps:"
|
||||
echo " 1. Review the changes: git show HEAD"
|
||||
echo " 2. Push to remote: git push origin main"
|
||||
echo " 3. Schedule automated backups in crontab"
|
||||
echo ""
|
||||
echo "To schedule daily backups, add to crontab:"
|
||||
echo " 0 2 * * * cd $(pwd) && ./scripts/backup-pfsense-config.sh"
|
||||
echo ""
|
||||
}
|
||||
|
||||
################################################################################
|
||||
# Main Execution
|
||||
################################################################################
|
||||
|
||||
main() {
|
||||
# Parse arguments properly (flags first, then positional)
|
||||
local dry_run=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
-n|--no-commit)
|
||||
AUTO_COMMIT="false"
|
||||
shift
|
||||
;;
|
||||
--dry-run)
|
||||
dry_run=true
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
log_error "Unknown option: $1"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
# This is a positional argument (hostname)
|
||||
PFSENSE_HOST="$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "╔════════════════════════════════════════════════════════════════╗"
|
||||
echo "║ pfSense Configuration Backup Utility ║"
|
||||
echo "╚════════════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
# Show configuration
|
||||
log_info "Configuration:"
|
||||
echo " Host: ${PFSENSE_HOST}"
|
||||
echo " User: ${PFSENSE_USER}"
|
||||
echo " Remote path: ${PFSENSE_CONFIG_PATH}"
|
||||
echo " Backup dir: ${BACKUP_DIR}"
|
||||
echo " Auto-commit: ${AUTO_COMMIT}"
|
||||
echo ""
|
||||
|
||||
# Check prerequisites
|
||||
if ! check_prerequisites; then
|
||||
log_error "Prerequisites check failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Test SSH connection
|
||||
if ! test_ssh_connection; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
if [[ "${dry_run}" == "true" ]]; then
|
||||
log_success "Dry-run completed successfully. SSH connection is working."
|
||||
echo "To perform actual backup, run: $(basename "$0") ${PFSENSE_HOST}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Fetch configuration
|
||||
if ! fetch_config; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Validate the backup
|
||||
if ! validate_config; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Commit to git (if enabled)
|
||||
if [[ "${AUTO_COMMIT}" == "true" ]]; then
|
||||
if ! commit_to_git; then
|
||||
log_warning "Backup was downloaded but git commit may have failed"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
log_info "Skipping git commit (AUTO_COMMIT=false)"
|
||||
fi
|
||||
|
||||
# Show summary
|
||||
echo ""
|
||||
show_summary
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user