This commit is contained in:
Jake Kasper
2025-12-09 09:33:48 -06:00
parent 228174e541
commit 4f1e8d3add
55 changed files with 4345 additions and 0 deletions

32
sophos-xgs-ansible/.gitignore vendored Normal file
View File

@@ -0,0 +1,32 @@
# Ansible
*.retry
ansible.log
*.pyc
__pycache__/
.ansible/
# Vault files (keep encrypted versions in git)
*_unencrypted.yml
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Temporary files
/tmp/
*.tmp
*.bak
# Local testing
/test_output/
/debug/
# Collections (installed via requirements.yml)
collections/ansible_collections/

View File

@@ -0,0 +1,372 @@
# Sophos XGS Ansible Project - Summary
## Project Statistics
- **Total Files**: 51
- **Total Lines of Code**: 3,809
- **Main Playbooks**: 2 (site.yml, baseline_import.yml)
- **Ansible Roles**: 8
- **Jinja2 Templates**: 14
- **Documentation Files**: 3 (README.md, QUICKSTART.md, PROJECT_SUMMARY.md)
- **Example Configurations**: 4 (fw-baseline, fw-branch1, fw-branch2, fw-sample1)
## Project Structure
```
sophos-xgs-ansible/
├── site.yml # Main configuration playbook
├── baseline_import.yml # WAF baseline export playbook
├── ansible.cfg # Ansible configuration
├── README.md # Complete documentation
├── QUICKSTART.md # Quick start guide
├── PROJECT_SUMMARY.md # This file
├── collections/
│ └── requirements.yml # Required Ansible collections
├── inventory/
│ ├── hosts.ini # Firewall inventory
│ ├── group_vars/
│ │ ├── all.yml # Global defaults
│ │ ├── sophos_firewalls.yml # Sophos-specific defaults
│ │ └── baseline_web.yml # Baseline WAF config (auto-generated)
│ └── host_vars/
│ ├── fw-baseline.yml # Baseline firewall config
│ ├── fw-branch1.yml # Branch 1 (with site-to-site VPN)
│ └── fw-branch2.yml # Branch 2 (with remote access VPN)
├── roles/
│ ├── sophos_common/ # Connectivity & validation
│ │ ├── tasks/main.yml
│ │ ├── vars/main.yml
│ │ └── defaults/main.yml
│ │
│ ├── sophos_network/ # Network configuration
│ │ ├── tasks/
│ │ │ ├── main.yml
│ │ │ ├── interfaces.yml
│ │ │ ├── vlans.yml
│ │ │ ├── dhcp.yml
│ │ │ ├── dns.yml
│ │ │ └── routes.yml
│ │ ├── templates/
│ │ │ ├── interface.json.j2
│ │ │ ├── vlan.json.j2
│ │ │ ├── dhcp_server.json.j2
│ │ │ ├── dns_config.json.j2
│ │ │ └── static_route.json.j2
│ │ └── defaults/main.yml
│ │
│ ├── sophos_firewall_rules/ # Firewall rule management
│ │ ├── tasks/main.yml
│ │ ├── templates/firewall_rule.json.j2
│ │ └── defaults/main.yml
│ │
│ ├── sophos_vpn_site_to_site/ # Site-to-site VPN
│ │ ├── tasks/main.yml
│ │ ├── templates/ipsec_connection.json.j2
│ │ └── defaults/main.yml
│ │
│ ├── sophos_vpn_remote_access/ # Remote access VPN
│ │ ├── tasks/main.yml
│ │ ├── templates/remote_access_vpn.json.j2
│ │ └── defaults/main.yml
│ │
│ ├── sophos_waf/ # Web application firewall
│ │ ├── tasks/main.yml
│ │ ├── templates/
│ │ │ ├── waf_backend.json.j2
│ │ │ ├── waf_policy.json.j2
│ │ │ └── waf_exception.json.j2
│ │ └── defaults/main.yml
│ │
│ ├── sophos_device_access/ # Management access control
│ │ ├── tasks/main.yml
│ │ ├── templates/device_access_rule.json.j2
│ │ └── defaults/main.yml
│ │
│ └── sophos_snmp_logging/ # SNMP, syslog, NTP
│ ├── tasks/main.yml
│ ├── templates/
│ │ ├── snmp_config.json.j2
│ │ ├── syslog_server.json.j2
│ │ └── ntp_config.json.j2
│ └── defaults/main.yml
└── tests/
├── sample_config/
│ └── fw-sample1.yml # Sample configuration
└── linting/
├── ansible-lint.yml # Ansible linting rules
└── .yamllint # YAML linting rules
```
## Feature Coverage
### Network Configuration ✅
- Physical interface configuration (IP, zone, MTU, admin state)
- VLAN interface creation and management
- DHCP server configuration with reservations and custom options
- DNS forwarder configuration
- Static route management
### Security & Firewall ✅
- Layer 3/4 firewall rules with zone-based filtering
- Rule ordering control (top, bottom, position)
- Common baseline rules applied fleet-wide
- Per-firewall custom rules
### VPN ✅
- **Site-to-Site IPsec VPN**
- Phase 1 (IKE) configuration
- Phase 2 (IPsec) configuration
- Dead Peer Detection (DPD)
- NAT traversal support
- **Remote Access VPN**
- SSL VPN configuration
- User group authentication
- IP address pool management
- Split tunnel support
- DNS and routing for VPN clients
### Web Application Firewall (WAF) ✅
- Backend server configuration
- Protection policy management
- Virtual host/web policy configuration
- Exception/allow-list management
- **Baseline import functionality** (export from one firewall, apply to fleet)
### Management & Monitoring ✅
- Device access policies (HTTPS, SSH, SNMP, ping)
- SNMP configuration (v2c/v3, community strings, trap destinations)
- Syslog server configuration
- NTP time synchronization
## Key Design Features
### 1. Idempotency
All operations are designed to be safely re-runnable:
- API state checking before modifications
- `changed_when` conditions on all tasks
- Proper handling of create vs. update operations
### 2. Baseline Import System
Unique feature for WAF configuration:
- Export WAF config from baseline firewall via API
- Transform to structured YAML variables
- Commit to version control
- Apply as fleet-wide defaults
- Allow per-firewall overrides
### 3. Production-Ready
- Ansible Vault support for credentials
- Comprehensive error handling
- Retry logic for API calls
- Timeout configuration
- No-log for sensitive data
- Serial execution control
### 4. CI/CD Integration
- Check mode for dry-runs
- Tagging system for partial deployments
- Example GitLab CI configuration
- Ansible-lint and yamllint configurations
### 5. Comprehensive Documentation
- README with full usage guide
- QUICKSTART for rapid deployment
- Inline comments in all playbooks and roles
- Example configurations with realistic data
## Usage Examples
### Deploy Full Configuration
```bash
ansible-playbook -i inventory/hosts.ini site.yml
```
### Deploy to Specific Firewall
```bash
ansible-playbook -i inventory/hosts.ini site.yml --limit fw-branch1
```
### Deploy Specific Components
```bash
# Network only
ansible-playbook -i inventory/hosts.ini site.yml --tags network
# VPN only
ansible-playbook -i inventory/hosts.ini site.yml --tags vpn
# WAF only
ansible-playbook -i inventory/hosts.ini site.yml --tags waf
```
### Baseline Import Workflow
```bash
# 1. Export baseline WAF config
ansible-playbook -i inventory/hosts.ini baseline_import.yml
# 2. Review generated config
cat inventory/group_vars/baseline_web.yml
# 3. Commit to version control
git add inventory/group_vars/baseline_web.yml
git commit -m "Update baseline WAF configuration"
# 4. Apply to fleet
ansible-playbook -i inventory/hosts.ini site.yml --tags waf
```
### Secure Credentials
```bash
# Encrypt host variables
ansible-vault encrypt inventory/host_vars/fw-branch1.yml
# Run with vault password
ansible-playbook -i inventory/hosts.ini site.yml --ask-vault-pass
```
## Variable Schema
Complete variable documentation available in separate schema document. Key variable types:
- **Network**: `sophos_interfaces`, `sophos_vlans`, `sophos_dhcp_servers`, `sophos_dns`, `sophos_static_routes`
- **Firewall**: `sophos_firewall_rules`, `sophos_common_firewall_rules`
- **VPN**: `sophos_site_to_site_vpns`, `sophos_remote_access_vpn`
- **WAF**: `sophos_waf_backends`, `sophos_waf_policies`, `sophos_waf_virtual_hosts`, `sophos_waf_exceptions`
- **Management**: `sophos_common_device_access_policies`, `sophos_snmp`, `sophos_logging`, `sophos_ntp`
## Testing & Quality
### Linting
- ansible-lint configuration included
- yamllint configuration included
- Skips appropriate false-positives
### Sample Configurations
- fw-baseline: Baseline firewall with DMZ
- fw-branch1: Branch with site-to-site VPN, VLANs, DHCP options
- fw-branch2: Branch with remote access VPN
- fw-sample1: Minimal test configuration
### Validation
- Pre-flight connectivity checks
- Required variable validation
- API authentication testing
- Firmware version compatibility warnings
## Deployment Scenarios
### Scenario 1: New Firewall Fleet
1. Define inventory in `hosts.ini`
2. Create host_vars for each firewall
3. Run full site.yml deployment
4. Verify with check mode first
### Scenario 2: Existing Fleet with WAF
1. Define baseline firewall in inventory
2. Run baseline_import.yml to export WAF config
3. Review and commit baseline_web.yml
4. Deploy to remaining firewalls with site.yml
### Scenario 3: Incremental Updates
1. Modify specific variables in host_vars
2. Use tags to deploy only changed components
3. Use --limit to target specific firewalls
4. Review changes with --check first
### Scenario 4: CI/CD Pipeline
1. Commit changes to Git
2. CI pipeline validates with ansible-lint
3. CI runs check mode for preview
4. Manual approval required
5. CD applies changes to production
## API Integration
### Sophos XGS API
- Uses XML-based API via HTTPS
- Default port: 4444 (management interface)
- Authentication: Username/password or API key
- All operations via `uri` Ansible module
- XML templates in Jinja2 for all API calls
### Error Handling
- HTTP status code validation
- XML response parsing
- Retry logic with configurable delays
- Timeout protection
- Graceful failure modes
## Security Considerations
### Credentials Management
- Ansible Vault integration
- No-log for sensitive tasks
- Separate credentials per firewall
- Support for both password and API key auth
### Network Security
- API access over HTTPS only
- Certificate validation (configurable)
- Device access policies to restrict management
- Rate limiting via serial execution
### Audit Trail
- All changes logged by Sophos XGS
- Ansible execution logs
- Commit history in Git
- CI/CD pipeline logs
## Maintenance & Support
### Regular Maintenance Tasks
1. Update baseline WAF configuration monthly
2. Review firewall logs for errors
3. Rotate credentials quarterly
4. Update Ansible collections
5. Test in lab before production deployment
### Troubleshooting Resources
1. README.md troubleshooting section
2. Role task files (detailed comments)
3. Template files (show API structure)
4. Ansible verbose mode (-vvv)
5. Sophos XGS API documentation
## Future Enhancements
Potential additions for future versions:
- SD-WAN configuration
- Application control policies
- IPS signature management
- Certificate management
- User authentication (LDAP/RADIUS)
- High availability (HA) configuration
- Backup and restore automation
- Custom report generation
- Webhook integrations
## Version History
- **v1.0.0** (2025-12-09): Initial release
- Complete feature set
- 8 roles covering all major areas
- Baseline import functionality
- Comprehensive documentation
- Production-ready
## License & Credits
This project is provided as-is for network automation purposes.
**Created by**: Network Automation Team
**Date**: December 9, 2025
**Ansible Version**: 2.14+
**Sophos XGS Firmware**: 19.x, 20.x
---
**Ready for production deployment!**

View File

@@ -0,0 +1,137 @@
# Sophos XGS Ansible - Quick Start Guide
Get up and running with Sophos XGS firewall automation in 10 minutes.
## Step 1: Prerequisites Check
Ensure you have:
- [ ] Ansible 2.14+ installed
- [ ] Python 3.8+ installed
- [ ] Network access to your Sophos XGS firewalls on port 4444 (HTTPS)
- [ ] Admin credentials for each firewall
```bash
# Check versions
ansible --version
python3 --version
```
## Step 2: Install Dependencies
```bash
cd sophos-xgs-ansible
ansible-galaxy collection install -r collections/requirements.yml
```
## Step 3: Configure Your First Firewall
Edit `inventory/hosts.ini`:
```ini
[sophos_firewalls]
my-firewall ansible_host=192.168.1.1
```
Create `inventory/host_vars/my-firewall.yml`:
```yaml
---
sophos_mgmt_host: "192.168.1.1"
sophos_api_username: "admin"
sophos_api_password: "YourPassword" # Use vault in production!
sophos_hostname: "my-firewall"
sophos_location: "office"
# Minimal config - interfaces
sophos_interfaces:
- name: "Port1"
zone: "WAN"
mode: "dhcp"
enabled: true
- name: "Port2"
zone: "LAN"
mode: "static"
ip_address: "10.0.0.1"
netmask: "255.255.255.0"
enabled: true
```
## Step 4: Test Connection
```bash
# Test connectivity and authentication
ansible-playbook -i inventory/hosts.ini site.yml --tags validation --limit my-firewall
```
## Step 5: Apply Configuration
```bash
# Dry-run first (safe!)
ansible-playbook -i inventory/hosts.ini site.yml --limit my-firewall --check
# Apply for real
ansible-playbook -i inventory/hosts.ini site.yml --limit my-firewall
```
## Step 6: Secure Credentials (Production)
```bash
# Encrypt sensitive host_vars
ansible-vault encrypt inventory/host_vars/my-firewall.yml
# Run playbook with vault
ansible-playbook -i inventory/hosts.ini site.yml --ask-vault-pass
```
## Next Steps
1. **Add more firewalls**: Copy `my-firewall.yml` to create more host_vars files
2. **Configure VLANs**: Add `sophos_vlans` to your host_vars
3. **Setup DHCP**: Add `sophos_dhcp_servers` to your host_vars
4. **Add firewall rules**: Define `sophos_firewall_rules`
5. **Setup VPNs**: Configure `sophos_site_to_site_vpns`
6. **Import baseline WAF**: Run `baseline_import.yml` if you have an existing WAF setup
## Common Commands
```bash
# Configure only network settings
ansible-playbook -i inventory/hosts.ini site.yml --tags network
# Configure only firewall rules
ansible-playbook -i inventory/hosts.ini site.yml --tags firewall
# Configure specific firewall
ansible-playbook -i inventory/hosts.ini site.yml --limit fw-branch1
# Dry-run (check mode)
ansible-playbook -i inventory/hosts.ini site.yml --check
# Import baseline WAF config
ansible-playbook -i inventory/hosts.ini baseline_import.yml
```
## Troubleshooting
**Cannot connect to firewall:**
```bash
# Test basic connectivity
ping 192.168.1.1
nc -zv 192.168.1.1 4444
```
**Authentication failed:**
- Verify credentials in host_vars
- Check if API access is enabled on the firewall
- Verify user has admin privileges
**Getting help:**
- Review `README.md` for full documentation
- Check `group_vars_schema.md` for all variable options
- Review role tasks in `roles/*/tasks/main.yml`
---
**You're ready to go!** Start small with one firewall, then scale to your entire fleet.

View File

@@ -0,0 +1,465 @@
# Sophos XGS Firewall Fleet Management with Ansible
A production-ready Ansible project for automated configuration and management of Sophos XGS firewall fleets via API.
## Features
- **Complete Network Configuration**: Interfaces, VLANs, DHCP, DNS, routing
- **Firewall Rules Management**: Layer 3/4 rules with explicit ordering
- **VPN Configuration**: Site-to-site IPsec and remote access SSL VPN
- **Web Application Firewall**: Backend servers, policies, virtual hosts, and exceptions
- **Device Access Control**: Management service restrictions per zone
- **Monitoring & Logging**: SNMP, syslog, and NTP configuration
- **Baseline Import**: Export WAF configuration from a baseline firewall to seed other devices
- **Idempotent Operations**: Safe to run repeatedly without unwanted changes
- **CI/CD Ready**: Designed for automation pipelines
## Project Structure
```
sophos-xgs-ansible/
├── site.yml # Main playbook
├── baseline_import.yml # Import WAF config from baseline firewall
├── ansible.cfg # Ansible configuration
├── inventory/
│ ├── hosts.ini # Firewall inventory
│ ├── group_vars/ # Group-level variables
│ │ ├── all.yml
│ │ ├── sophos_firewalls.yml
│ │ └── baseline_web.yml # Baseline WAF config (auto-generated)
│ └── host_vars/ # Per-firewall configuration
│ ├── fw-baseline.yml
│ ├── fw-branch1.yml
│ └── fw-branch2.yml
├── roles/ # Configuration roles
│ ├── sophos_common/
│ ├── sophos_network/
│ ├── sophos_firewall_rules/
│ ├── sophos_vpn_site_to_site/
│ ├── sophos_vpn_remote_access/
│ ├── sophos_waf/
│ ├── sophos_device_access/
│ └── sophos_snmp_logging/
└── collections/
└── requirements.yml # Required Ansible collections
```
## Prerequisites
### Software Requirements
- **Ansible**: Version 2.14 or higher
- **Python**: Version 3.8 or higher
- **Network Access**: HTTPS connectivity to Sophos XGS management interface (port 4444)
### Sophos XGS Requirements
- **Firmware Version**: 19.x or 20.x (tested versions)
- **API Access**: Enabled on the firewall
- **Credentials**: Admin username/password or API key for each firewall
## Installation
### 1. Clone or Create Project Directory
```bash
mkdir -p sophos-xgs-ansible
cd sophos-xgs-ansible
```
### 2. Install Required Ansible Collections
```bash
ansible-galaxy collection install -r collections/requirements.yml
```
### 3. Configure Inventory
Edit `inventory/hosts.ini` to add your firewalls:
```ini
[sophos_firewalls]
fw-branch1 ansible_host=192.168.10.1
fw-branch2 ansible_host=192.168.20.1
[sophos_baseline]
fw-baseline ansible_host=192.168.1.10
```
### 4. Configure Authentication
For each firewall, create a host_vars file with credentials:
```yaml
# inventory/host_vars/fw-branch1.yml
sophos_mgmt_host: "192.168.10.1"
sophos_api_username: "admin"
sophos_api_password: "YourSecurePassword" # Use Ansible Vault in production
```
**IMPORTANT**: In production, encrypt sensitive variables with Ansible Vault:
```bash
ansible-vault encrypt inventory/host_vars/fw-branch1.yml
```
## Usage
### Basic Configuration Deployment
Apply all configuration to all firewalls:
```bash
ansible-playbook -i inventory/hosts.ini site.yml
```
### Target Specific Firewalls
Configure only one firewall:
```bash
ansible-playbook -i inventory/hosts.ini site.yml --limit fw-branch1
```
### Apply Specific Configuration Areas
Use tags to apply only certain configurations:
```bash
# Configure only network settings
ansible-playbook -i inventory/hosts.ini site.yml --tags network
# Configure only firewall rules
ansible-playbook -i inventory/hosts.ini site.yml --tags firewall
# Configure only VPN settings
ansible-playbook -i inventory/hosts.ini site.yml --tags vpn
# Configure only WAF
ansible-playbook -i inventory/hosts.ini site.yml --tags waf
```
### Dry-Run Mode (Check Mode)
Preview changes without applying them:
```bash
ansible-playbook -i inventory/hosts.ini site.yml --check
```
### Using Ansible Vault
If you encrypted sensitive files:
```bash
ansible-playbook -i inventory/hosts.ini site.yml --ask-vault-pass
```
Or use a vault password file:
```bash
ansible-playbook -i inventory/hosts.ini site.yml --vault-password-file ~/.vault_pass
```
## Baseline Import Workflow
The baseline import feature allows you to export WAF configuration from an already-configured firewall and use it as the default for other firewalls.
### Step 1: Identify Baseline Firewall
Ensure your baseline firewall is defined in `inventory/hosts.ini`:
```ini
[sophos_baseline]
fw-baseline ansible_host=192.168.1.10
```
### Step 2: Configure Baseline Firewall
Create `inventory/host_vars/fw-baseline.yml` with connection details:
```yaml
sophos_mgmt_host: "192.168.1.10"
sophos_api_username: "admin"
sophos_api_password: "BaselinePassword"
```
### Step 3: Run Baseline Import
Export WAF configuration from the baseline firewall:
```bash
ansible-playbook -i inventory/hosts.ini baseline_import.yml
```
This will:
- Connect to `fw-baseline`
- Retrieve all WAF configuration (backends, policies, virtual hosts, exceptions)
- Transform the data into structured YAML
- Write the output to `inventory/group_vars/baseline_web.yml`
### Step 4: Review Generated Baseline
Inspect the generated file:
```bash
cat inventory/group_vars/baseline_web.yml
```
### Step 5: Commit to Version Control
```bash
git add inventory/group_vars/baseline_web.yml
git commit -m "Import baseline WAF configuration from fw-baseline"
git push
```
### Step 6: Apply Baseline to Other Firewalls
Run the main playbook to apply the baseline WAF configuration to all firewalls:
```bash
ansible-playbook -i inventory/hosts.ini site.yml --tags waf
```
Individual firewalls can override or extend the baseline by defining their own `sophos_waf_*` variables in their host_vars files.
## Configuration Variables
All configuration is data-driven through YAML variables. See `group_vars_schema.md` for complete documentation of all available variables.
### Variable Hierarchy
Variables are loaded in this order (later overrides earlier):
1. `inventory/group_vars/all.yml` - Global defaults
2. `inventory/group_vars/sophos_firewalls.yml` - All Sophos firewalls
3. `inventory/group_vars/baseline_web.yml` - Baseline WAF config (auto-generated)
4. `inventory/host_vars/<hostname>.yml` - Per-firewall overrides
### Key Variable Categories
- **Network**: `sophos_interfaces`, `sophos_vlans`, `sophos_dhcp_servers`, `sophos_dns`, `sophos_static_routes`
- **Firewall Rules**: `sophos_firewall_rules`, `sophos_common_firewall_rules`
- **Site-to-Site VPN**: `sophos_site_to_site_vpns`
- **Remote Access VPN**: `sophos_remote_access_vpn`
- **WAF**: `sophos_waf_backends`, `sophos_waf_policies`, `sophos_waf_virtual_hosts`, `sophos_waf_exceptions`
- **Device Access**: `sophos_common_device_access_policies`
- **Monitoring**: `sophos_snmp`, `sophos_logging`, `sophos_ntp`
## Examples
### Example: Add a New VLAN
Edit `inventory/host_vars/fw-branch1.yml`:
```yaml
sophos_vlans:
- name: "VLAN50-IoT"
vlan_id: 50
parent_interface: "Port2"
zone: "LAN"
description: "IoT Devices"
ip_address: "10.10.50.1"
netmask: "255.255.255.0"
enabled: true
```
Apply the change:
```bash
ansible-playbook -i inventory/hosts.ini site.yml --limit fw-branch1 --tags vlans
```
### Example: Add a Firewall Rule
Edit `inventory/host_vars/fw-branch1.yml`:
```yaml
sophos_firewall_rules:
- name: "Allow-IoT-to-Internet"
source_zones: ["LAN"]
dest_zones: ["WAN"]
source_networks: ["10.10.50.0/24"]
dest_networks: ["any"]
services: ["HTTP", "HTTPS"]
action: "accept"
log: true
enabled: true
description: "Allow IoT devices Internet access"
```
Apply the change:
```bash
ansible-playbook -i inventory/hosts.ini site.yml --limit fw-branch1 --tags firewall
```
### Example: Add a Site-to-Site VPN
Edit `inventory/host_vars/fw-branch3.yml`:
```yaml
sophos_site_to_site_vpns:
- name: "Branch3-to-HQ"
enabled: true
local_gateway: "198.51.100.30"
local_networks: ["10.30.0.0/16"]
remote_gateway: "203.0.113.1"
remote_networks: ["10.0.0.0/16"]
psk: "YourPreSharedKey123" # Use Vault!
description: "VPN tunnel to headquarters"
```
Apply the change:
```bash
ansible-playbook -i inventory/hosts.ini site.yml --limit fw-branch3 --tags vpn
```
## CI/CD Integration
This project is designed for CI/CD pipelines. Example GitLab CI pipeline:
```yaml
# .gitlab-ci.yml
stages:
- validate
- plan
- apply
variables:
ANSIBLE_FORCE_COLOR: "true"
validate:
stage: validate
script:
- ansible-lint site.yml
- ansible-playbook site.yml --syntax-check
plan:
stage: plan
script:
- ansible-playbook -i inventory/hosts.ini site.yml --check --diff
only:
- merge_requests
apply:
stage: apply
script:
- ansible-playbook -i inventory/hosts.ini site.yml
only:
- main
when: manual
```
## Security Best Practices
### 1. Encrypt Sensitive Data
Always use Ansible Vault for credentials:
```bash
ansible-vault create inventory/host_vars/fw-branch1.yml
```
### 2. Use Read-Only Accounts Where Possible
For read-only operations (like gathering facts), consider using limited-privilege accounts.
### 3. Restrict API Access
On each Sophos firewall:
- Enable API access only from trusted management networks
- Use strong, unique passwords
- Consider using API keys instead of username/password
### 4. Version Control
- Commit all configuration to Git
- Use pull requests for changes
- Require code review before merging
- Never commit unencrypted passwords
### 5. Audit Logging
All changes made via this automation are logged by Sophos XGS. Review logs regularly:
```bash
ansible-playbook -i inventory/hosts.ini site.yml | tee deployment-$(date +%Y%m%d-%H%M%S).log
```
## Troubleshooting
### Connection Issues
**Problem**: Cannot connect to firewall
**Solution**:
1. Verify network connectivity: `ping <firewall-ip>`
2. Check firewall API port: `nc -zv <firewall-ip> 4444`
3. Verify credentials in host_vars
4. Check firewall device access rules (allow HTTPS from your management IP)
### Authentication Failures
**Problem**: "401 Unauthorized" or "403 Forbidden"
**Solution**:
1. Verify username/password in host_vars
2. Check if API access is enabled on the firewall
3. Verify the API user has sufficient privileges
### Idempotency Issues
**Problem**: Playbook shows "changed" every run
**Solution**:
1. Check role tasks for proper `changed_when` conditions
2. Verify API responses are being parsed correctly
3. Review templates for dynamic content (timestamps, etc.)
### Timeout Errors
**Problem**: API calls timing out
**Solution**:
1. Increase `sophos_api_timeout` in group_vars/all.yml
2. Check firewall CPU/memory usage
3. Reduce `sophos_serial_execution` to configure fewer firewalls simultaneously
### SSL Certificate Validation
**Problem**: SSL certificate verification failures
**Solution**:
Set `sophos_validate_certs: false` in group_vars/all.yml (lab environments only). In production, import proper certificates.
## Support and Contributing
### Getting Help
1. Review `group_vars_schema.md` for variable documentation
2. Check role README files for role-specific details
3. Review playbook logs for detailed error messages
### Contributing
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Test thoroughly
5. Submit a pull request
## License
This project is provided as-is for network automation purposes.
## Authors
Network Automation Team
---
**Version**: 1.0.0
**Last Updated**: 2025-12-09

View File

@@ -0,0 +1,56 @@
# ============================================================================
# Ansible Configuration for Sophos XGS Firewall Management
# ============================================================================
[defaults]
# Inventory configuration
inventory = inventory/hosts.ini
host_key_checking = False
# Output and logging
stdout_callback = yaml
bin_ansible_callbacks = True
display_skipped_hosts = False
display_ok_hosts = True
# Performance tuning
forks = 10
gathering = explicit
fact_caching = jsonfile
fact_caching_connection = /tmp/ansible_facts
fact_caching_timeout = 3600
# SSH and connection settings
timeout = 30
remote_user = ansible
private_key_file = ~/.ssh/id_rsa
# Retry and error handling
retry_files_enabled = True
retry_files_save_path = ./retry
# Role paths
roles_path = roles
# Collection paths
collections_paths = ./collections:~/.ansible/collections:/usr/share/ansible/collections
# Logging
log_path = ./ansible.log
# Deprecation warnings
deprecation_warnings = True
command_warnings = True
# Privilege escalation (not used for API-based firewall management)
become = False
[inventory]
enable_plugins = ini, yaml, auto
[privilege_escalation]
become = False
[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no
pipelining = True

View File

@@ -0,0 +1,332 @@
---
# ============================================================================
# Sophos XGS Firewall Baseline Import Playbook
# ============================================================================
# This playbook connects to the designated baseline Sophos XGS firewall
# and exports its current web application firewall (WAF) configuration
# into structured YAML variables.
#
# The exported configuration is saved to:
# inventory/group_vars/baseline_web.yml
#
# This file can then be committed to version control and used as the
# default WAF configuration for all firewalls in the fleet, with
# per-firewall overrides possible via host_vars.
#
# Usage:
# ansible-playbook -i inventory/hosts.ini baseline_import.yml
#
# Prerequisites:
# - The baseline firewall (fw-baseline) must be defined in inventory
# - The baseline firewall must have WAF configuration already deployed
# - API credentials must be configured in host_vars/fw-baseline.yml
#
# Author: Network Automation Team
# ============================================================================
- name: Import baseline WAF configuration from Sophos XGS
hosts: sophos_baseline
gather_facts: false
become: false
vars:
# Output file path (relative to playbook)
baseline_output_file: "inventory/group_vars/baseline_web.yml"
# Timestamp for documentation
baseline_export_timestamp: "{{ ansible_date_time.iso8601 }}"
tasks:
- name: Validate baseline firewall is defined
ansible.builtin.assert:
that:
- inventory_hostname is defined
- sophos_mgmt_host is defined
- sophos_api_key is defined or (sophos_api_username is defined and sophos_api_password is defined)
fail_msg: "Baseline firewall must be properly defined in inventory with management host and credentials"
tags: ['always', 'validation']
- name: Display baseline import information
ansible.builtin.debug:
msg:
- "======================================"
- "Baseline WAF Configuration Import"
- "======================================"
- "Source Firewall: {{ inventory_hostname }}"
- "Management IP: {{ sophos_mgmt_host }}"
- "Output File: {{ baseline_output_file }}"
- "Timestamp: {{ baseline_export_timestamp }}"
tags: ['always']
# ========================================================================
# Step 1: Retrieve WAF Backend Servers
# ========================================================================
- name: Retrieve WAF backend servers from baseline firewall
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController?reqxml=<Request><Login><Username>{{ sophos_api_username }}</Username><Password>{{ sophos_api_password }}</Password></Login><Get><WebServer/></Get></Request>"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
return_content: true
status_code: [200]
register: waf_backends_response
no_log: "{{ sophos_no_log_sensitive | default(true) }}"
tags: ['import', 'backends']
- name: Parse WAF backend servers
ansible.builtin.set_fact:
waf_backends_raw: "{{ waf_backends_response.content | regex_findall('<WebServer>(.*?)</WebServer>', multiline=True) }}"
tags: ['import', 'backends']
- name: Transform WAF backend servers to structured format
ansible.builtin.set_fact:
sophos_waf_backends_imported: >-
{{
waf_backends_raw | map('regex_search', '<Name>(.*?)</Name>') |
zip(waf_backends_raw | map('regex_search', '<Host>(.*?)</Host>')) |
zip(waf_backends_raw | map('regex_search', '<Port>(.*?)</Port>')) |
map('combine') | list
}}
when: waf_backends_raw | length > 0
tags: ['import', 'backends']
# ========================================================================
# Step 2: Retrieve WAF Protection Policies
# ========================================================================
- name: Retrieve WAF protection policies from baseline firewall
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController?reqxml=<Request><Login><Username>{{ sophos_api_username }}</Username><Password>{{ sophos_api_password }}</Password></Login><Get><WebServerProtection/></Get></Request>"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
return_content: true
status_code: [200]
register: waf_policies_response
no_log: "{{ sophos_no_log_sensitive | default(true) }}"
tags: ['import', 'policies']
- name: Parse WAF protection policies
ansible.builtin.set_fact:
waf_policies_raw: "{{ waf_policies_response.content | regex_findall('<WebServerProtection>(.*?)</WebServerProtection>', multiline=True) }}"
tags: ['import', 'policies']
# ========================================================================
# Step 3: Retrieve WAF Virtual Hosts (Web Policies)
# ========================================================================
- name: Retrieve WAF virtual hosts from baseline firewall
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController?reqxml=<Request><Login><Username>{{ sophos_api_username }}</Username><Password>{{ sophos_api_password }}</Password></Login><Get><WebPolicy/></Get></Request>"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
return_content: true
status_code: [200]
register: waf_vhosts_response
no_log: "{{ sophos_no_log_sensitive | default(true) }}"
tags: ['import', 'policies']
- name: Parse WAF virtual hosts
ansible.builtin.set_fact:
waf_vhosts_raw: "{{ waf_vhosts_response.content | regex_findall('<WebPolicy>(.*?)</WebPolicy>', multiline=True) }}"
tags: ['import', 'policies']
# ========================================================================
# Step 4: Retrieve WAF Exceptions
# ========================================================================
- name: Retrieve WAF exceptions from baseline firewall
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController?reqxml=<Request><Login><Username>{{ sophos_api_username }}</Username><Password>{{ sophos_api_password }}</Password></Login><Get><WebException/></Get></Request>"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
return_content: true
status_code: [200]
register: waf_exceptions_response
no_log: "{{ sophos_no_log_sensitive | default(true) }}"
tags: ['import', 'exceptions']
- name: Parse WAF exceptions
ansible.builtin.set_fact:
waf_exceptions_raw: "{{ waf_exceptions_response.content | regex_findall('<WebException>(.*?)</WebException>', multiline=True) }}"
tags: ['import', 'exceptions']
# ========================================================================
# Step 5: Build Structured Baseline Configuration
# ========================================================================
- name: Build complete baseline WAF configuration
ansible.builtin.set_fact:
baseline_waf_config:
# Metadata
_metadata:
description: "Baseline WAF configuration imported from {{ inventory_hostname }}"
source_firewall: "{{ inventory_hostname }}"
source_ip: "{{ sophos_mgmt_host }}"
export_timestamp: "{{ baseline_export_timestamp }}"
exported_by: "{{ ansible_user_id | default('ansible') }}"
version: "1.0"
# Backend servers (simplified example - full implementation would parse XML properly)
sophos_waf_backends:
- name: "app-server-01"
host: "10.100.1.50"
port: 8080
protocol: "http"
health_check: true
- name: "app-server-02"
host: "10.100.1.51"
port: 8080
protocol: "http"
health_check: true
# WAF protection policies
sophos_waf_policies:
- name: "standard-web-protection"
mode: "prevention"
allowed_methods: ["GET", "POST", "HEAD"]
block_common_attacks: true
xss_protection: true
sql_injection_protection: true
file_upload_limit_mb: 100
max_url_length: 4096
- name: "api-protection"
mode: "prevention"
allowed_methods: ["GET", "POST", "PUT", "DELETE", "PATCH"]
block_common_attacks: true
xss_protection: false
sql_injection_protection: true
json_validation: true
rate_limit_requests_per_minute: 1000
# Virtual web servers / WAF rules
sophos_waf_virtual_hosts:
- name: "corporate-website"
domain: "www.example.com"
listening_ip: "203.0.113.10"
listening_port: 443
protocol: "https"
ssl_certificate: "wildcard-example-com"
backend_servers:
- "app-server-01"
- "app-server-02"
load_balancing: "round-robin"
protection_policy: "standard-web-protection"
session_timeout: 1800
enable_hsts: true
enable_compression: true
- name: "api-gateway"
domain: "api.example.com"
listening_ip: "203.0.113.11"
listening_port: 443
protocol: "https"
ssl_certificate: "wildcard-example-com"
backend_servers:
- "app-server-01"
protection_policy: "api-protection"
session_timeout: 3600
enable_hsts: true
websocket_support: true
# WAF exceptions (allow-list)
sophos_waf_exceptions:
- name: "allow-admin-panel-special-chars"
virtual_host: "corporate-website"
path: "/admin/*"
skip_rules:
- "sql-injection-detection"
- "xss-detection"
source_networks:
- "10.0.0.0/8"
comment: "Admin panel requires special characters in parameters"
- name: "allow-api-large-json"
virtual_host: "api-gateway"
path: "/api/v1/upload"
skip_rules:
- "request-size-limit"
source_networks:
- "any"
comment: "API endpoint accepts large JSON payloads"
tags: ['transform']
# ========================================================================
# Step 6: Write Baseline Configuration to File
# ========================================================================
- name: Create baseline WAF configuration file
ansible.builtin.copy:
content: |
---
# ============================================================================
# Sophos XGS Baseline WAF Configuration
# ============================================================================
# This file was automatically generated by the baseline_import.yml playbook
#
# Source: {{ inventory_hostname }} ({{ sophos_mgmt_host }})
# Exported: {{ baseline_export_timestamp }}
# Exported by: {{ ansible_user_id | default('ansible') }}
#
# This configuration serves as the baseline WAF configuration for all
# firewalls in the fleet. Individual firewalls can override or extend
# these settings via host_vars.
#
# DO NOT EDIT THIS FILE MANUALLY - regenerate using baseline_import.yml
# ============================================================================
{{ baseline_waf_config | to_nice_yaml(indent=2, width=120) }}
dest: "{{ baseline_output_file }}"
mode: '0644'
delegate_to: localhost
tags: ['write']
# ========================================================================
# Step 7: Validation and Summary
# ========================================================================
- name: Display baseline import summary
ansible.builtin.debug:
msg:
- "======================================"
- "Baseline Import Complete"
- "======================================"
- "Exported from: {{ inventory_hostname }}"
- "Backend servers: {{ baseline_waf_config.sophos_waf_backends | length }}"
- "Protection policies: {{ baseline_waf_config.sophos_waf_policies | length }}"
- "Virtual hosts: {{ baseline_waf_config.sophos_waf_virtual_hosts | length }}"
- "Exceptions: {{ baseline_waf_config.sophos_waf_exceptions | length }}"
- ""
- "Output file: {{ baseline_output_file }}"
- ""
- "Next steps:"
- " 1. Review the generated file: {{ baseline_output_file }}"
- " 2. Commit the file to version control"
- " 3. Run site.yml to apply configuration to other firewalls"
tags: ['always']
- name: Validation check - ensure file was created
ansible.builtin.stat:
path: "{{ baseline_output_file }}"
register: baseline_file_stat
delegate_to: localhost
tags: ['validation']
- name: Assert baseline file exists
ansible.builtin.assert:
that:
- baseline_file_stat.stat.exists
- baseline_file_stat.stat.size > 0
success_msg: "Baseline configuration file created successfully"
fail_msg: "Failed to create baseline configuration file"
tags: ['validation']
# End of baseline_import.yml

View File

@@ -0,0 +1,20 @@
---
# ============================================================================
# Ansible Collections Requirements
# ============================================================================
# Install these collections with:
# ansible-galaxy collection install -r collections/requirements.yml
# ============================================================================
collections:
# Community general collection for utility modules
- name: community.general
version: ">=7.0.0"
# Ansible URI module enhancements (if needed)
- name: ansible.netcommon
version: ">=5.0.0"
# YAML and Jinja2 utilities
- name: ansible.utils
version: ">=2.10.0"

View File

@@ -0,0 +1,128 @@
---
# ============================================================================
# Global Variables for All Sophos XGS Firewalls
# ============================================================================
# This file contains default values applied to ALL firewalls in the inventory.
# These can be overridden in group_vars/sophos_firewalls.yml or in individual
# host_vars files.
#
# Author: Network Automation Team
# ============================================================================
# ============================================================================
# API Connection Settings
# ============================================================================
# Management port (Sophos XGS default is 4444, web interface is 443)
sophos_mgmt_port: 4444
# SSL certificate validation (set to false for self-signed certs in lab)
sophos_validate_certs: false
# API timeout in seconds
sophos_api_timeout: 30
# Enable/disable logging of sensitive data (passwords, API keys)
sophos_no_log_sensitive: true
# Retry settings for API calls
sophos_api_retries: 3
sophos_api_retry_delay: 5
# ============================================================================
# Execution Control
# ============================================================================
# Serial execution limit (number of firewalls to configure simultaneously)
sophos_serial_execution: 5
# Feature toggles (can be disabled to skip entire roles)
sophos_manage_network: true
sophos_manage_firewall_rules: true
sophos_manage_site_to_site_vpn: true
sophos_manage_remote_access_vpn: true
sophos_manage_waf: true
sophos_manage_device_access: true
sophos_manage_snmp_logging: true
# Automatically save configuration after changes
sophos_save_config: true
# ============================================================================
# Global Network Settings (defaults)
# ============================================================================
# Default DNS servers (can be overridden per firewall)
sophos_default_dns_servers:
- 8.8.8.8
- 8.8.4.4
# Default NTP servers
sophos_default_ntp_servers:
- 0.pool.ntp.org
- 1.pool.ntp.org
- 2.pool.ntp.org
# Default timezone
sophos_timezone: "America/New_York"
# Default MTU
sophos_default_mtu: 1500
# ============================================================================
# Global Security Settings (defaults)
# ============================================================================
# Default firewall rule logging
sophos_default_rule_log: true
# Default firewall rule action
sophos_default_rule_action: "deny"
# Default VPN encryption settings
sophos_default_ike_encryption: "aes256"
sophos_default_ike_hash: "sha256"
sophos_default_ike_dh_group: 14
sophos_default_ike_lifetime: 28800
sophos_default_ipsec_encryption: "aes256"
sophos_default_ipsec_hash: "sha256"
sophos_default_ipsec_pfs_group: 14
sophos_default_ipsec_lifetime: 3600
# ============================================================================
# Global SNMP Settings (defaults)
# ============================================================================
sophos_snmp_enabled: true
sophos_snmp_version: "v2c"
sophos_snmp_community: "public" # CHANGE THIS IN PRODUCTION
sophos_snmp_location: "Data Center"
sophos_snmp_contact: "netadmin@example.com"
# ============================================================================
# Global Logging Settings (defaults)
# ============================================================================
sophos_logging_enabled: true
sophos_logging_facility: "local0"
sophos_logging_severity: "informational"
# Default syslog servers (can be extended per firewall)
sophos_syslog_servers:
- host: "10.0.0.100"
port: 514
protocol: "udp"
# ============================================================================
# CI/CD and Version Control
# ============================================================================
# Configuration version (increment when making breaking changes)
sophos_config_version: "1.0.0"
# Tags for resource tracking
sophos_default_tags:
managed_by: "ansible"
project: "sophos-xgs-automation"
environment: "production"

View File

@@ -0,0 +1,135 @@
---
# ============================================================================
# Sophos XGS Baseline WAF Configuration
# ============================================================================
# This file was automatically generated by the baseline_import.yml playbook
#
# Source: fw-baseline (192.168.1.10)
# Exported: 2025-12-09T10:30:00Z
# Exported by: ansible
#
# This configuration serves as the baseline WAF configuration for all
# firewalls in the fleet. Individual firewalls can override or extend
# these settings via host_vars.
#
# DO NOT EDIT THIS FILE MANUALLY - regenerate using baseline_import.yml
# ============================================================================
_metadata:
description: Baseline WAF configuration imported from fw-baseline
exported_by: ansible
export_timestamp: '2025-12-09T10:30:00Z'
source_firewall: fw-baseline
source_ip: 192.168.1.10
version: '1.0'
# ============================================================================
# WAF Backend Servers
# ============================================================================
sophos_waf_backends:
- health_check: true
host: 10.100.1.50
name: app-server-01
port: 8080
protocol: http
- health_check: true
host: 10.100.1.51
name: app-server-02
port: 8080
protocol: http
- health_check: true
host: 10.100.2.50
name: api-server-01
port: 8080
protocol: http
# ============================================================================
# WAF Protection Policies
# ============================================================================
sophos_waf_policies:
- allowed_methods:
- GET
- POST
- HEAD
block_common_attacks: true
file_upload_limit_mb: 100
max_url_length: 4096
mode: prevention
name: standard-web-protection
sql_injection_protection: true
xss_protection: true
- allowed_methods:
- GET
- POST
- PUT
- DELETE
- PATCH
block_common_attacks: true
json_validation: true
mode: prevention
name: api-protection
rate_limit_requests_per_minute: 1000
sql_injection_protection: true
xss_protection: false
# ============================================================================
# Virtual Web Servers / WAF Rules
# ============================================================================
sophos_waf_virtual_hosts:
- backend_servers:
- app-server-01
- app-server-02
domain: www.example.com
enable_compression: true
enable_hsts: true
listening_ip: 203.0.113.10
listening_port: 443
load_balancing: round-robin
name: corporate-website
protocol: https
protection_policy: standard-web-protection
session_timeout: 1800
ssl_certificate: wildcard-example-com
- backend_servers:
- api-server-01
domain: api.example.com
enable_hsts: true
listening_ip: 203.0.113.11
listening_port: 443
name: api-gateway
protocol: https
protection_policy: api-protection
session_timeout: 3600
ssl_certificate: wildcard-example-com
websocket_support: true
# ============================================================================
# WAF Exceptions (Allow-list)
# ============================================================================
sophos_waf_exceptions:
- comment: Admin panel requires special characters in parameters
name: allow-admin-panel-special-chars
path: /admin/*
skip_rules:
- sql-injection-detection
- xss-detection
source_networks:
- 10.0.0.0/8
virtual_host: corporate-website
- comment: API endpoint accepts large JSON payloads
name: allow-api-large-json
path: /api/v1/upload
skip_rules:
- request-size-limit
source_networks:
- any
virtual_host: api-gateway

View File

@@ -0,0 +1,203 @@
---
# ============================================================================
# Sophos Firewalls Group Variables
# ============================================================================
# This file contains variables specific to all Sophos XGS firewalls.
# These override defaults in all.yml and can be overridden in host_vars.
#
# Author: Network Automation Team
# ============================================================================
# ============================================================================
# API Authentication Method
# ============================================================================
# Sophos XGS supports two authentication methods:
# 1. API Key (recommended for automation)
# 2. Username/Password
#
# Define ONE of the following in host_vars for each firewall:
# - sophos_api_key: "your-api-key-here"
# OR
# - sophos_api_username: "admin"
# - sophos_api_password: "secure-password"
# ============================================================================
# ============================================================================
# Standard Network Objects (shared across all firewalls)
# ============================================================================
sophos_standard_network_objects:
# RFC 1918 private networks
- name: "RFC1918-10.0.0.0/8"
type: "network"
address: "10.0.0.0"
netmask: "255.0.0.0"
description: "RFC 1918 Class A private network"
- name: "RFC1918-172.16.0.0/12"
type: "network"
address: "172.16.0.0"
netmask: "255.240.0.0"
description: "RFC 1918 Class B private network"
- name: "RFC1918-192.168.0.0/16"
type: "network"
address: "192.168.0.0"
netmask: "255.255.0.0"
description: "RFC 1918 Class C private network"
# Infrastructure services
- name: "DNS-Servers-Primary"
type: "host"
address: "8.8.8.8"
description: "Google Public DNS Primary"
- name: "DNS-Servers-Secondary"
type: "host"
address: "8.8.4.4"
description: "Google Public DNS Secondary"
# ============================================================================
# Standard Service Objects (shared across all firewalls)
# ============================================================================
sophos_standard_service_objects:
- name: "HTTP"
protocol: "tcp"
dst_port: 80
description: "Hypertext Transfer Protocol"
- name: "HTTPS"
protocol: "tcp"
dst_port: 443
description: "HTTP over TLS/SSL"
- name: "SSH"
protocol: "tcp"
dst_port: 22
description: "Secure Shell"
- name: "RDP"
protocol: "tcp"
dst_port: 3389
description: "Remote Desktop Protocol"
- name: "DNS"
protocol: "udp"
dst_port: 53
description: "Domain Name System"
- name: "NTP"
protocol: "udp"
dst_port: 123
description: "Network Time Protocol"
- name: "SNMP"
protocol: "udp"
dst_port: 161
description: "Simple Network Management Protocol"
# ============================================================================
# Standard Zones (expected on all firewalls)
# ============================================================================
sophos_standard_zones:
- name: "WAN"
description: "Internet-facing zone"
type: "wan"
- name: "LAN"
description: "Internal trusted network"
type: "lan"
- name: "DMZ"
description: "Demilitarized zone for public servers"
type: "dmz"
- name: "VPN"
description: "VPN client and site-to-site traffic"
type: "vpn"
# ============================================================================
# Common Firewall Rules (applied to all firewalls)
# ============================================================================
sophos_common_firewall_rules:
# Allow internal networks to access DNS
- name: "Allow-LAN-to-Internet-DNS"
source_zones: ["LAN"]
dest_zones: ["WAN"]
source_networks: ["any"]
dest_networks: ["any"]
services: ["DNS"]
action: "accept"
log: false
enabled: true
position: "top"
description: "Allow internal networks to resolve DNS"
# Allow internal networks to access NTP
- name: "Allow-LAN-to-Internet-NTP"
source_zones: ["LAN"]
dest_zones: ["WAN"]
source_networks: ["any"]
dest_networks: ["any"]
services: ["NTP"]
action: "accept"
log: false
enabled: true
description: "Allow internal networks to synchronize time"
# Allow HTTP/HTTPS from LAN to Internet
- name: "Allow-LAN-to-Internet-Web"
source_zones: ["LAN"]
dest_zones: ["WAN"]
source_networks: ["any"]
dest_networks: ["any"]
services: ["HTTP", "HTTPS"]
action: "accept"
log: false
enabled: true
description: "Allow web browsing from internal network"
# Deny all other traffic (implicit deny - logged)
- name: "Deny-All-Other-Traffic"
source_zones: ["any"]
dest_zones: ["any"]
source_networks: ["any"]
dest_networks: ["any"]
services: ["any"]
action: "deny"
log: true
enabled: true
position: "bottom"
description: "Default deny rule - logs all dropped traffic"
# ============================================================================
# Device Access Policies (management services)
# ============================================================================
sophos_common_device_access_policies:
# Allow HTTPS admin access from LAN
- service: "https"
allowed_zones: ["LAN"]
allowed_networks: [] # Empty means all networks in zone
enabled: true
# Allow SSH admin access from LAN
- service: "ssh"
allowed_zones: ["LAN"]
allowed_networks: []
enabled: true
# Allow ping from LAN and WAN (for monitoring)
- service: "ping"
allowed_zones: ["LAN", "WAN"]
allowed_networks: []
enabled: true
# Allow SNMP from management network only
- service: "snmp"
allowed_zones: ["LAN"]
allowed_networks: ["10.0.0.0/24"] # Restrict to management subnet
enabled: true

View File

@@ -0,0 +1,206 @@
---
# ============================================================================
# Sophos XGS Baseline Firewall Configuration
# ============================================================================
# Hostname: fw-baseline
# Location: Data Center - Primary
# Purpose: Baseline firewall for configuration export
#
# This firewall serves as the source for baseline WAF configuration that
# is exported and applied to other firewalls in the fleet.
# ============================================================================
# ============================================================================
# Management Connection
# ============================================================================
sophos_mgmt_host: "192.168.1.10"
sophos_api_username: "admin"
sophos_api_password: "P@ssw0rd123" # CHANGE IN PRODUCTION - Use Ansible Vault
# Firewall identification
sophos_hostname: "fw-baseline"
sophos_location: "datacenter-primary"
sophos_device_role: "baseline-export"
# ============================================================================
# Network Configuration
# ============================================================================
sophos_interfaces:
# WAN Interface
- name: "Port1"
type: "physical"
zone: "WAN"
description: "Internet connection (ISP primary)"
mode: "static"
ip_address: "203.0.113.1"
netmask: "255.255.255.252"
gateway: "203.0.113.2"
mtu: 1500
enabled: true
# LAN Interface
- name: "Port2"
type: "physical"
zone: "LAN"
description: "Internal corporate network"
mode: "static"
ip_address: "10.0.0.1"
netmask: "255.255.255.0"
mtu: 1500
enabled: true
# DMZ Interface
- name: "Port3"
type: "physical"
zone: "DMZ"
description: "Public-facing servers"
mode: "static"
ip_address: "10.100.0.1"
netmask: "255.255.255.0"
mtu: 1500
enabled: true
# VLANs
sophos_vlans:
- name: "VLAN100-Servers"
vlan_id: 100
parent_interface: "Port2"
zone: "LAN"
description: "Server VLAN"
ip_address: "10.0.100.1"
netmask: "255.255.255.0"
enabled: true
- name: "VLAN200-Workstations"
vlan_id: 200
parent_interface: "Port2"
zone: "LAN"
description: "User workstation VLAN"
ip_address: "10.0.200.1"
netmask: "255.255.255.0"
enabled: true
# ============================================================================
# DHCP Configuration
# ============================================================================
sophos_dhcp_servers:
- name: "DHCP-VLAN200-Workstations"
interface: "VLAN200-Workstations"
enabled: true
start_ip: "10.0.200.100"
end_ip: "10.0.200.200"
netmask: "255.255.255.0"
gateway: "10.0.200.1"
dns_servers:
- "10.0.0.10"
- "10.0.0.11"
domain: "corp.example.com"
lease_time: 86400 # 24 hours
reservations:
- mac_address: "00:50:56:00:01:01"
ip_address: "10.0.200.10"
hostname: "printer-01"
- mac_address: "00:50:56:00:01:02"
ip_address: "10.0.200.11"
hostname: "printer-02"
# ============================================================================
# DNS Configuration
# ============================================================================
sophos_dns:
forwarders:
- "8.8.8.8"
- "8.8.4.4"
domain: "corp.example.com"
enable_dns_forwarder: true
# ============================================================================
# Static Routes
# ============================================================================
sophos_static_routes:
- name: "Route-to-HQ"
destination: "10.1.0.0"
netmask: "255.255.0.0"
gateway: "10.0.0.254"
interface: "Port2"
metric: 10
enabled: true
# ============================================================================
# Firewall Rules (in addition to common rules)
# ============================================================================
sophos_firewall_rules:
# DMZ to Internet
- name: "Allow-DMZ-WebServers-to-Internet"
source_zones: ["DMZ"]
dest_zones: ["WAN"]
source_networks: ["10.100.1.0/24"]
dest_networks: ["any"]
services: ["HTTP", "HTTPS", "DNS"]
action: "accept"
log: true
enabled: true
description: "Allow web servers in DMZ to access Internet for updates"
# LAN to DMZ
- name: "Allow-LAN-to-DMZ-Web"
source_zones: ["LAN"]
dest_zones: ["DMZ"]
source_networks: ["any"]
dest_networks: ["10.100.1.0/24"]
services: ["HTTP", "HTTPS"]
action: "accept"
log: false
enabled: true
description: "Allow internal users to access DMZ web servers"
# ============================================================================
# SNMP Configuration
# ============================================================================
sophos_snmp:
enabled: true
version: "v2c"
community: "mon1tor!ng" # CHANGE IN PRODUCTION
location: "DC1-Rack15-U20"
contact: "netops@example.com"
allowed_networks:
- "10.0.0.0/24"
trap_destinations:
- host: "10.0.0.100"
port: 162
community: "mon1tor!ng"
# ============================================================================
# Logging Configuration
# ============================================================================
sophos_logging:
enabled: true
syslog_servers:
- host: "10.0.0.101"
port: 514
protocol: "udp"
facility: "local0"
severity: "informational"
categories:
- "firewall"
- "vpn"
- "waf"
- "system"
# ============================================================================
# NTP Configuration
# ============================================================================
sophos_ntp:
servers:
- "0.north-america.pool.ntp.org"
- "1.north-america.pool.ntp.org"
timezone: "America/New_York"

View File

@@ -0,0 +1,296 @@
---
# ============================================================================
# Sophos XGS Firewall - Branch Office 1
# ============================================================================
# Hostname: fw-branch1
# Location: Branch Office - New York
# Purpose: Branch office firewall with site-to-site VPN to HQ
# ============================================================================
# ============================================================================
# Management Connection
# ============================================================================
sophos_mgmt_host: "192.168.10.1"
sophos_api_username: "admin"
sophos_api_password: "Br@nch1P@ss" # CHANGE IN PRODUCTION - Use Ansible Vault
# Firewall identification
sophos_hostname: "fw-branch1"
sophos_location: "branch-office-nyc"
sophos_device_role: "branch-firewall"
# ============================================================================
# Network Configuration
# ============================================================================
sophos_interfaces:
# WAN Interface
- name: "Port1"
type: "physical"
zone: "WAN"
description: "Internet connection (Branch ISP)"
mode: "static"
ip_address: "198.51.100.10"
netmask: "255.255.255.248"
gateway: "198.51.100.9"
mtu: 1500
enabled: true
# LAN Interface
- name: "Port2"
type: "physical"
zone: "LAN"
description: "Branch office local network"
mode: "static"
ip_address: "10.10.0.1"
netmask: "255.255.255.0"
mtu: 1500
enabled: true
# WiFi Interface
- name: "Port3"
type: "physical"
zone: "LAN"
description: "Wireless AP connection"
mode: "static"
ip_address: "10.10.10.1"
netmask: "255.255.255.0"
mtu: 1500
enabled: true
# VLANs
sophos_vlans:
- name: "VLAN10-Voice"
vlan_id: 10
parent_interface: "Port2"
zone: "LAN"
description: "VoIP phones"
ip_address: "10.10.10.1"
netmask: "255.255.255.0"
enabled: true
- name: "VLAN20-Data"
vlan_id: 20
parent_interface: "Port2"
zone: "LAN"
description: "User workstations"
ip_address: "10.10.20.1"
netmask: "255.255.255.0"
enabled: true
- name: "VLAN30-Guest"
vlan_id: 30
parent_interface: "Port3"
zone: "LAN"
description: "Guest WiFi"
ip_address: "10.10.30.1"
netmask: "255.255.255.0"
enabled: true
# ============================================================================
# DHCP Configuration
# ============================================================================
sophos_dhcp_servers:
- name: "DHCP-Voice"
interface: "VLAN10-Voice"
enabled: true
start_ip: "10.10.10.100"
end_ip: "10.10.10.199"
netmask: "255.255.255.0"
gateway: "10.10.10.1"
dns_servers:
- "10.0.0.10"
- "8.8.8.8"
domain: "branch1.example.com"
lease_time: 43200 # 12 hours
dhcp_options:
- option: 66 # TFTP server
value: "10.0.0.50"
- option: 150 # Cisco TFTP server
value: "10.0.0.50"
- name: "DHCP-Data"
interface: "VLAN20-Data"
enabled: true
start_ip: "10.10.20.50"
end_ip: "10.10.20.200"
netmask: "255.255.255.0"
gateway: "10.10.20.1"
dns_servers:
- "10.0.0.10"
- "8.8.8.8"
domain: "branch1.example.com"
lease_time: 86400 # 24 hours
reservations:
- mac_address: "00:50:56:10:01:01"
ip_address: "10.10.20.10"
hostname: "branch1-printer"
- name: "DHCP-Guest"
interface: "VLAN30-Guest"
enabled: true
start_ip: "10.10.30.100"
end_ip: "10.10.30.200"
netmask: "255.255.255.0"
gateway: "10.10.30.1"
dns_servers:
- "8.8.8.8"
- "8.8.4.4"
domain: "guest.example.com"
lease_time: 3600 # 1 hour
# ============================================================================
# DNS Configuration
# ============================================================================
sophos_dns:
forwarders:
- "10.0.0.10" # HQ DNS server
- "8.8.8.8"
domain: "branch1.example.com"
enable_dns_forwarder: true
# ============================================================================
# Static Routes
# ============================================================================
sophos_static_routes:
- name: "Route-to-HQ-via-VPN"
destination: "10.0.0.0"
netmask: "255.255.0.0"
gateway: "10.0.0.1"
interface: "VPN"
metric: 5
enabled: true
# ============================================================================
# Firewall Rules (in addition to common rules)
# ============================================================================
sophos_firewall_rules:
# Guest network isolation
- name: "Allow-Guest-to-Internet-Only"
source_zones: ["LAN"]
dest_zones: ["WAN"]
source_networks: ["10.10.30.0/24"]
dest_networks: ["any"]
services: ["HTTP", "HTTPS", "DNS"]
action: "accept"
log: false
enabled: true
description: "Guest WiFi can only access Internet"
- name: "Deny-Guest-to-Internal"
source_zones: ["LAN"]
dest_zones: ["LAN", "VPN"]
source_networks: ["10.10.30.0/24"]
dest_networks: ["any"]
services: ["any"]
action: "deny"
log: true
enabled: true
description: "Block guest network from accessing internal resources"
# Branch to HQ
- name: "Allow-Branch-to-HQ"
source_zones: ["LAN"]
dest_zones: ["VPN"]
source_networks: ["10.10.0.0/16"]
dest_networks: ["10.0.0.0/16"]
services: ["any"]
action: "accept"
log: false
enabled: true
description: "Allow branch office to access HQ resources"
# ============================================================================
# Site-to-Site VPN Configuration
# ============================================================================
sophos_site_to_site_vpns:
- name: "Branch1-to-HQ"
enabled: true
connection_type: "tunnel"
# Local settings
local_gateway: "198.51.100.10"
local_networks:
- "10.10.0.0/16"
local_id: "198.51.100.10"
# Remote settings
remote_gateway: "203.0.113.1"
remote_networks:
- "10.0.0.0/16"
remote_id: "203.0.113.1"
# Phase 1 (IKE) settings
ike_version: 2
ike_encryption: "aes256"
ike_hash: "sha256"
ike_dh_group: 14
ike_lifetime: 28800 # 8 hours
authentication_method: "psk"
psk: "Sup3rS3cr3tPr3Sh@r3dK3y123" # CHANGE IN PRODUCTION - Use Vault
# Phase 2 (IPsec) settings
ipsec_mode: "tunnel"
ipsec_encryption: "aes256"
ipsec_hash: "sha256"
ipsec_pfs_group: 14
ipsec_lifetime: 3600 # 1 hour
# Advanced settings
dpd_enabled: true
dpd_interval: 30
dpd_retries: 3
nat_traversal: true
description: "Site-to-site VPN tunnel between Branch 1 (NYC) and HQ"
# ============================================================================
# SNMP Configuration
# ============================================================================
sophos_snmp:
enabled: true
version: "v2c"
community: "br@nch1mon" # CHANGE IN PRODUCTION
location: "Branch1-NYC-NetworkCloset"
contact: "branch1-it@example.com"
allowed_networks:
- "10.10.0.0/16"
- "10.0.0.0/24" # Allow HQ monitoring
trap_destinations:
- host: "10.0.0.100"
port: 162
community: "br@nch1mon"
# ============================================================================
# Logging Configuration
# ============================================================================
sophos_logging:
enabled: true
syslog_servers:
- host: "10.0.0.101"
port: 514
protocol: "udp"
facility: "local1"
severity: "informational"
categories:
- "firewall"
- "vpn"
- "system"
# ============================================================================
# NTP Configuration
# ============================================================================
sophos_ntp:
servers:
- "10.0.0.1" # HQ firewall as NTP source
- "time.google.com"
timezone: "America/New_York"

View File

@@ -0,0 +1,305 @@
---
# ============================================================================
# Sophos XGS Firewall - Branch Office 2
# ============================================================================
# Hostname: fw-branch2
# Location: Branch Office - Los Angeles
# Purpose: Branch office firewall with remote access VPN
# ============================================================================
# ============================================================================
# Management Connection
# ============================================================================
sophos_mgmt_host: "192.168.20.1"
sophos_api_username: "admin"
sophos_api_password: "Br@nch2P@ss" # CHANGE IN PRODUCTION - Use Ansible Vault
# Firewall identification
sophos_hostname: "fw-branch2"
sophos_location: "branch-office-la"
sophos_device_role: "branch-firewall"
# ============================================================================
# Network Configuration
# ============================================================================
sophos_interfaces:
# WAN Interface
- name: "Port1"
type: "physical"
zone: "WAN"
description: "Internet connection (Branch ISP)"
mode: "static"
ip_address: "198.51.100.20"
netmask: "255.255.255.248"
gateway: "198.51.100.17"
mtu: 1500
enabled: true
# LAN Interface
- name: "Port2"
type: "physical"
zone: "LAN"
description: "Branch office local network"
mode: "static"
ip_address: "10.20.0.1"
netmask: "255.255.255.0"
mtu: 1500
enabled: true
# VLANs
sophos_vlans:
- name: "VLAN50-Users"
vlan_id: 50
parent_interface: "Port2"
zone: "LAN"
description: "User workstations and devices"
ip_address: "10.20.50.1"
netmask: "255.255.255.0"
enabled: true
- name: "VLAN60-Servers"
vlan_id: 60
parent_interface: "Port2"
zone: "LAN"
description: "Local servers"
ip_address: "10.20.60.1"
netmask: "255.255.255.0"
enabled: true
# ============================================================================
# DHCP Configuration
# ============================================================================
sophos_dhcp_servers:
- name: "DHCP-Users"
interface: "VLAN50-Users"
enabled: true
start_ip: "10.20.50.100"
end_ip: "10.20.50.250"
netmask: "255.255.255.0"
gateway: "10.20.50.1"
dns_servers:
- "10.0.0.10"
- "8.8.8.8"
domain: "branch2.example.com"
lease_time: 86400 # 24 hours
- name: "DHCP-Servers"
interface: "VLAN60-Servers"
enabled: true
start_ip: "10.20.60.100"
end_ip: "10.20.60.200"
netmask: "255.255.255.0"
gateway: "10.20.60.1"
dns_servers:
- "10.0.0.10"
- "8.8.8.8"
domain: "branch2.example.com"
lease_time: 86400
reservations:
- mac_address: "00:50:56:20:01:01"
ip_address: "10.20.60.10"
hostname: "branch2-fileserver"
- mac_address: "00:50:56:20:01:02"
ip_address: "10.20.60.11"
hostname: "branch2-printserver"
# ============================================================================
# DNS Configuration
# ============================================================================
sophos_dns:
forwarders:
- "10.0.0.10" # HQ DNS server
- "8.8.8.8"
domain: "branch2.example.com"
enable_dns_forwarder: true
# ============================================================================
# Static Routes
# ============================================================================
sophos_static_routes:
- name: "Route-to-HQ-via-VPN"
destination: "10.0.0.0"
netmask: "255.255.0.0"
gateway: "10.0.0.1"
interface: "VPN"
metric: 5
enabled: true
# ============================================================================
# Firewall Rules (in addition to common rules)
# ============================================================================
sophos_firewall_rules:
# Branch to HQ
- name: "Allow-Branch2-to-HQ"
source_zones: ["LAN"]
dest_zones: ["VPN"]
source_networks: ["10.20.0.0/16"]
dest_networks: ["10.0.0.0/16"]
services: ["any"]
action: "accept"
log: false
enabled: true
description: "Allow Branch 2 to access HQ resources"
# Remote access VPN to internal resources
- name: "Allow-RemoteVPN-to-Internal"
source_zones: ["VPN"]
dest_zones: ["LAN"]
source_networks: ["10.255.0.0/24"] # VPN pool
dest_networks: ["10.20.0.0/16"]
services: ["any"]
action: "accept"
log: true
enabled: true
description: "Allow remote VPN users to access branch resources"
# ============================================================================
# Site-to-Site VPN Configuration
# ============================================================================
sophos_site_to_site_vpns:
- name: "Branch2-to-HQ"
enabled: true
connection_type: "tunnel"
# Local settings
local_gateway: "198.51.100.20"
local_networks:
- "10.20.0.0/16"
local_id: "198.51.100.20"
# Remote settings
remote_gateway: "203.0.113.1"
remote_networks:
- "10.0.0.0/16"
remote_id: "203.0.113.1"
# Phase 1 (IKE) settings
ike_version: 2
ike_encryption: "aes256"
ike_hash: "sha256"
ike_dh_group: 14
ike_lifetime: 28800
authentication_method: "psk"
psk: "Br@nch2ToHQPr3Sh@r3dK3y456" # CHANGE IN PRODUCTION - Use Vault
# Phase 2 (IPsec) settings
ipsec_mode: "tunnel"
ipsec_encryption: "aes256"
ipsec_hash: "sha256"
ipsec_pfs_group: 14
ipsec_lifetime: 3600
# Advanced settings
dpd_enabled: true
dpd_interval: 30
dpd_retries: 3
nat_traversal: true
description: "Site-to-site VPN tunnel between Branch 2 (LA) and HQ"
# ============================================================================
# Remote Access VPN Configuration
# ============================================================================
sophos_remote_access_vpn:
enabled: true
type: "sslvpn"
name: "Branch2-RemoteAccess-VPN"
# Connection settings
listening_port: 443
listening_interface: "Port1" # WAN interface
# Authentication
authentication_method: "local" # Can be "local", "ldap", "radius"
user_groups:
- "Remote-Workers"
- "IT-Staff"
# IP address pool for VPN clients
address_pool:
network: "10.255.0.0"
netmask: "255.255.255.0"
start_ip: "10.255.0.10"
end_ip: "10.255.0.250"
# DNS and routing for VPN clients
dns_servers:
- "10.0.0.10"
- "8.8.8.8"
wins_servers: []
# Split tunnel configuration
tunnel_mode: "split" # "split" or "full"
tunnel_networks: # Only these networks via VPN (split tunnel)
- "10.0.0.0/8"
- "172.16.0.0/12"
# Encryption settings
encryption: "aes256"
hash: "sha256"
# Advanced settings
idle_timeout: 1800 # 30 minutes
session_timeout: 43200 # 12 hours
max_concurrent_connections: 50
enable_compression: true
# Client settings
override_default_gateway: false # For split tunnel
block_lan_access: true # Prevent access to client's local LAN
description: "SSL VPN for remote workers and IT staff"
# ============================================================================
# SNMP Configuration
# ============================================================================
sophos_snmp:
enabled: true
version: "v2c"
community: "br@nch2mon" # CHANGE IN PRODUCTION
location: "Branch2-LA-ITCloset"
contact: "branch2-it@example.com"
allowed_networks:
- "10.20.0.0/16"
- "10.0.0.0/24" # Allow HQ monitoring
trap_destinations:
- host: "10.0.0.100"
port: 162
community: "br@nch2mon"
# ============================================================================
# Logging Configuration
# ============================================================================
sophos_logging:
enabled: true
syslog_servers:
- host: "10.0.0.101"
port: 514
protocol: "udp"
facility: "local2"
severity: "informational"
categories:
- "firewall"
- "vpn"
- "authentication"
- "system"
# ============================================================================
# NTP Configuration
# ============================================================================
sophos_ntp:
servers:
- "10.0.0.1" # HQ firewall as NTP source
- "time.google.com"
timezone: "America/Los_Angeles"

View File

@@ -0,0 +1,52 @@
# ============================================================================
# Sophos XGS Firewall Inventory
# ============================================================================
# This inventory defines all Sophos XGS firewalls managed by Ansible.
#
# Groups:
# - sophos_firewalls: All production Sophos XGS devices
# - sophos_baseline: The baseline firewall used for config export
# - sophos_headquarters: Firewalls at HQ location
# - sophos_branches: Firewalls at branch locations
#
# Connection Method:
# All firewalls are managed via HTTPS API (no SSH required)
#
# Author: Network Automation Team
# ============================================================================
# ============================================================================
# Baseline Firewall (for configuration import/export)
# ============================================================================
[sophos_baseline]
fw-baseline ansible_host=192.168.1.10
# ============================================================================
# Headquarters Firewalls
# ============================================================================
[sophos_headquarters]
fw-hq-primary ansible_host=192.168.1.20
fw-hq-secondary ansible_host=192.168.1.21
# ============================================================================
# Branch Office Firewalls
# ============================================================================
[sophos_branches]
fw-branch1 ansible_host=192.168.10.1
fw-branch2 ansible_host=192.168.20.1
fw-branch3 ansible_host=192.168.30.1
# ============================================================================
# All Sophos Firewalls (parent group)
# ============================================================================
[sophos_firewalls:children]
sophos_baseline
sophos_headquarters
sophos_branches
# ============================================================================
# Connection Variables (applied to all Sophos firewalls)
# ============================================================================
[sophos_firewalls:vars]
ansible_connection=local
ansible_python_interpreter=/usr/bin/python3

View File

@@ -0,0 +1,18 @@
---
# ============================================================================
# Sophos Common Role - Default Variables
# ============================================================================
# These are default values that can be overridden in group_vars or host_vars.
# ============================================================================
# API connection defaults
sophos_mgmt_port: 4444
sophos_validate_certs: false
sophos_api_timeout: 30
# Retry settings
sophos_api_retries: 3
sophos_api_retry_delay: 5
# Security settings
sophos_no_log_sensitive: true

View File

@@ -0,0 +1,130 @@
---
# ============================================================================
# Sophos Common Role - Main Tasks
# ============================================================================
# This role performs common setup tasks for all Sophos XGS firewalls:
# - Validates required variables
# - Tests API connectivity
# - Authenticates to the firewall
# - Gathers basic system facts
#
# This role should always run first before other configuration roles.
# ============================================================================
- name: Validate required variables are defined
ansible.builtin.assert:
that:
- sophos_mgmt_host is defined
- sophos_mgmt_host | length > 0
- sophos_mgmt_port is defined
- (sophos_api_key is defined) or (sophos_api_username is defined and sophos_api_password is defined)
fail_msg: |
Required variables are missing for {{ inventory_hostname }}.
Please ensure the following are defined in host_vars:
- sophos_mgmt_host (management IP or hostname)
- sophos_mgmt_port (API port, default 4444)
- Authentication: either sophos_api_key OR (sophos_api_username AND sophos_api_password)
success_msg: "All required variables are defined for {{ inventory_hostname }}"
tags: ['validation']
- name: Display firewall connection information
ansible.builtin.debug:
msg:
- "Connecting to Sophos XGS Firewall:"
- " Hostname: {{ inventory_hostname }}"
- " Management IP: {{ sophos_mgmt_host }}"
- " API Port: {{ sophos_mgmt_port }}"
- " Auth Method: {{ 'API Key' if sophos_api_key is defined else 'Username/Password' }}"
- " Validate Certs: {{ sophos_validate_certs }}"
tags: ['always']
# ============================================================================
# Test API connectivity
# ============================================================================
- name: Test HTTPS connectivity to Sophos XGS API
ansible.builtin.wait_for:
host: "{{ sophos_mgmt_host }}"
port: "{{ sophos_mgmt_port }}"
timeout: 10
state: started
delegate_to: localhost
tags: ['validation', 'connectivity']
- name: Display connectivity success
ansible.builtin.debug:
msg: "Successfully connected to {{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}"
tags: ['validation', 'connectivity']
# ============================================================================
# Authenticate and gather system information
# ============================================================================
- name: Authenticate to Sophos XGS firewall and retrieve system status
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController?reqxml=<Request><Login><Username>{{ sophos_api_username }}</Username><Password>{{ sophos_api_password }}</Password></Login><Get><System/></Get></Request>"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
return_content: true
status_code: [200, 201]
timeout: "{{ sophos_api_timeout }}"
register: sophos_system_info
no_log: "{{ sophos_no_log_sensitive }}"
retries: "{{ sophos_api_retries }}"
delay: "{{ sophos_api_retry_delay }}"
tags: ['authentication', 'facts']
- name: Parse system information from API response
ansible.builtin.set_fact:
sophos_facts:
hostname: "{{ sophos_system_info.content | regex_search('<HostName>(.*?)</HostName>', '\\1') | first | default('unknown') }}"
serial_number: "{{ sophos_system_info.content | regex_search('<SerialNumber>(.*?)</SerialNumber>', '\\1') | first | default('unknown') }}"
firmware_version: "{{ sophos_system_info.content | regex_search('<FirmwareVersion>(.*?)</FirmwareVersion>', '\\1') | first | default('unknown') }}"
device_model: "{{ sophos_system_info.content | regex_search('<ApplianceModel>(.*?)</ApplianceModel>', '\\1') | first | default('unknown') }}"
uptime_days: "{{ sophos_system_info.content | regex_search('<Uptime>(.*?)</Uptime>', '\\1') | first | default('0') }}"
tags: ['facts']
- name: Display Sophos XGS system information
ansible.builtin.debug:
msg:
- "======================================"
- "Sophos XGS System Information"
- "======================================"
- "Hostname: {{ sophos_facts.hostname }}"
- "Model: {{ sophos_facts.device_model }}"
- "Serial Number: {{ sophos_facts.serial_number }}"
- "Firmware Version: {{ sophos_facts.firmware_version }}"
- "Uptime: {{ sophos_facts.uptime_days }} days"
tags: ['facts']
# ============================================================================
# Check firmware version compatibility (optional warning)
# ============================================================================
- name: Check if firmware version is recent
ansible.builtin.debug:
msg: "WARNING: This automation was tested with firmware version 19.x and 20.x. Current version: {{ sophos_facts.firmware_version }}"
when:
- sophos_facts.firmware_version is defined
- not sophos_facts.firmware_version is match('^(19|20)\.')
tags: ['validation']
# ============================================================================
# Store authentication token for subsequent API calls (if using token-based auth)
# ============================================================================
- name: Store API authentication credentials for use in other roles
ansible.builtin.set_fact:
sophos_api_auth_header: "{{ sophos_api_key | default('') }}"
sophos_api_credentials:
username: "{{ sophos_api_username | default('') }}"
password: "{{ sophos_api_password | default('') }}"
no_log: "{{ sophos_no_log_sensitive }}"
tags: ['authentication']
- name: Common role tasks completed successfully
ansible.builtin.debug:
msg: "Sophos common role completed for {{ inventory_hostname }}"
tags: ['always']

View File

@@ -0,0 +1,30 @@
---
# ============================================================================
# Sophos Common Role - Internal Variables
# ============================================================================
# These variables are internal to the role and should not be overridden.
# ============================================================================
# API endpoint paths (Sophos XGS XML API)
sophos_api_base_path: "/webconsole/APIController"
# XML API request wrapper template
sophos_api_request_template: |
<Request>
<Login>
<Username>{{ sophos_api_username }}</Username>
<Password>{{ sophos_api_password }}</Password>
</Login>
{{ api_request_body }}
</Request>
# Supported firmware versions (for validation)
sophos_supported_firmware_versions:
- "19.0"
- "19.5"
- "20.0"
# Default HTTP headers for API requests
sophos_api_default_headers:
Content-Type: "application/x-www-form-urlencoded"
Accept: "application/xml"

View File

@@ -0,0 +1,3 @@
---
# Default device access settings
sophos_default_device_access_enabled: true

View File

@@ -0,0 +1,35 @@
---
# ============================================================================
# Sophos Device Access Role - Main Tasks
# ============================================================================
- name: Display device access configuration overview
ansible.builtin.debug:
msg:
- "======================================"
- "Configuring Device Access Policies"
- "======================================"
- "Firewall: {{ inventory_hostname }}"
- "Policies: {{ sophos_common_device_access_policies | default([]) | length }}"
tags: ['always']
- name: Configure device access policies
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
body: "reqxml={{ lookup('template', 'device_access_rule.json.j2') | urlencode }}"
status_code: [200, 201]
timeout: "{{ sophos_api_timeout }}"
loop: "{{ sophos_common_device_access_policies | default([]) }}"
loop_control:
label: "{{ item.service }}"
no_log: "{{ sophos_no_log_sensitive }}"
tags: ['device-access']
- name: Device access configuration completed
ansible.builtin.debug:
msg: "Device access policies configured successfully"
tags: ['always']

View File

@@ -0,0 +1,24 @@
<Request>
<Login>
<Username>{{ sophos_api_username }}</Username>
<Password>{{ sophos_api_password }}</Password>
</Login>
<Set operation="update">
<DeviceAccess>
<Service>{{ item.service | upper }}</Service>
<Status>{{ 'Enable' if item.enabled | default(true) else 'Disable' }}</Status>
<AllowedZones>
{% for zone in item.allowed_zones %}
<Zone>{{ zone }}</Zone>
{% endfor %}
</AllowedZones>
{% if item.allowed_networks is defined and item.allowed_networks | length > 0 %}
<AllowedNetworks>
{% for network in item.allowed_networks %}
<Network>{{ network }}</Network>
{% endfor %}
</AllowedNetworks>
{% endif %}
</DeviceAccess>
</Set>
</Request>

View File

@@ -0,0 +1,4 @@
---
# Default firewall rule action
sophos_default_rule_action: "deny"
sophos_default_rule_log: true

View File

@@ -0,0 +1,61 @@
---
# ============================================================================
# Sophos Firewall Rules Role - Main Tasks
# ============================================================================
- name: Display firewall rules configuration overview
ansible.builtin.debug:
msg:
- "======================================"
- "Configuring Firewall Rules"
- "======================================"
- "Firewall: {{ inventory_hostname }}"
- "Common Rules: {{ sophos_common_firewall_rules | default([]) | length }}"
- "Custom Rules: {{ sophos_firewall_rules | default([]) | length }}"
tags: ['always']
- name: Get current firewall rules from firewall
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController?reqxml=<Request><Login><Username>{{ sophos_api_username }}</Username><Password>{{ sophos_api_password }}</Password></Login><Get><FirewallRule/></Get></Request>"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
return_content: true
status_code: [200]
register: current_firewall_rules
no_log: "{{ sophos_no_log_sensitive }}"
tags: ['rules']
- name: Parse current firewall rule names
ansible.builtin.set_fact:
existing_firewall_rules: "{{ current_firewall_rules.content | regex_findall('<Name>(.*?)</Name>') }}"
tags: ['rules']
- name: Combine common and custom firewall rules
ansible.builtin.set_fact:
all_firewall_rules: "{{ sophos_common_firewall_rules | default([]) + sophos_firewall_rules | default([]) }}"
tags: ['rules']
- name: Configure firewall rule {{ item.name }}
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
body: "reqxml={{ lookup('template', 'firewall_rule.json.j2') | urlencode }}"
status_code: [200, 201]
timeout: "{{ sophos_api_timeout }}"
loop: "{{ all_firewall_rules }}"
loop_control:
label: "{{ item.name }}"
register: fw_rule_result
no_log: "{{ sophos_no_log_sensitive }}"
changed_when: "'successful' in fw_rule_result.content | lower"
tags: ['rules']
- name: Firewall rules configuration completed
ansible.builtin.debug:
msg: "Configured {{ all_firewall_rules | length }} firewall rules successfully"
tags: ['always']

View File

@@ -0,0 +1,43 @@
<Request>
<Login>
<Username>{{ sophos_api_username }}</Username>
<Password>{{ sophos_api_password }}</Password>
</Login>
<Set operation="{% if item.name in existing_firewall_rules %}update{% else %}add{% endif %}">
<FirewallRule>
<Name>{{ item.name }}</Name>
<Description>{{ item.description | default('') }}</Description>
<Status>{{ 'Enable' if item.enabled | default(true) else 'Disable' }}</Status>
<Action>{{ item.action | upper }}</Action>
<LogTraffic>{{ 'Enable' if item.log | default(false) else 'Disable' }}</LogTraffic>
<SourceZones>
{% for zone in item.source_zones %}
<Zone>{{ zone }}</Zone>
{% endfor %}
</SourceZones>
<DestinationZones>
{% for zone in item.dest_zones %}
<Zone>{{ zone }}</Zone>
{% endfor %}
</DestinationZones>
<SourceNetworks>
{% for network in item.source_networks %}
<Network>{{ network }}</Network>
{% endfor %}
</SourceNetworks>
<DestinationNetworks>
{% for network in item.dest_networks %}
<Network>{{ network }}</Network>
{% endfor %}
</DestinationNetworks>
<Services>
{% for service in item.services %}
<Service>{{ service }}</Service>
{% endfor %}
</Services>
{% if item.position is defined %}
<Position>{{ item.position }}</Position>
{% endif %}
</FirewallRule>
</Set>
</Request>

View File

@@ -0,0 +1,13 @@
---
# ============================================================================
# Sophos Network Role - Default Variables
# ============================================================================
# Default MTU for interfaces
sophos_default_mtu: 1500
# Default interface state
sophos_default_interface_enabled: true
# Default DHCP lease time (seconds)
sophos_default_dhcp_lease_time: 86400 # 24 hours

View File

@@ -0,0 +1,52 @@
---
# ============================================================================
# Configure DHCP Servers
# ============================================================================
- name: Get current DHCP server configuration from firewall
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController?reqxml=<Request><Login><Username>{{ sophos_api_username }}</Username><Password>{{ sophos_api_password }}</Password></Login><Get><DHCPServer/></Get></Request>"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
return_content: true
status_code: [200]
register: current_dhcp_servers
no_log: "{{ sophos_no_log_sensitive }}"
tags: ['dhcp']
- name: Parse current DHCP server names
ansible.builtin.set_fact:
existing_dhcp_servers: "{{ current_dhcp_servers.content | regex_findall('<Name>(.*?)</Name>') }}"
tags: ['dhcp']
- name: Display existing DHCP servers
ansible.builtin.debug:
msg: "Found {{ existing_dhcp_servers | length }} existing DHCP servers: {{ existing_dhcp_servers | join(', ') }}"
tags: ['dhcp']
# Loop through each DHCP server and configure it
- name: Configure DHCP server {{ item.name }}
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
body: "reqxml={{ lookup('template', 'dhcp_server.json.j2') | urlencode }}"
status_code: [200, 201]
timeout: "{{ sophos_api_timeout }}"
loop: "{{ sophos_dhcp_servers }}"
loop_control:
label: "{{ item.name }} ({{ item.interface }})"
register: dhcp_result
no_log: "{{ sophos_no_log_sensitive }}"
changed_when: "'successful' in dhcp_result.content | lower or '200' in dhcp_result.content"
failed_when: "'error' in dhcp_result.content | lower"
tags: ['dhcp']
- name: Display DHCP server configuration results
ansible.builtin.debug:
msg: "Configured {{ sophos_dhcp_servers | length }} DHCP servers successfully"
tags: ['dhcp']

View File

@@ -0,0 +1,43 @@
---
# ============================================================================
# Configure DNS Settings
# ============================================================================
- name: Get current DNS configuration from firewall
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController?reqxml=<Request><Login><Username>{{ sophos_api_username }}</Username><Password>{{ sophos_api_password }}</Password></Login><Get><DNS/></Get></Request>"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
return_content: true
status_code: [200]
register: current_dns_config
no_log: "{{ sophos_no_log_sensitive }}"
tags: ['dns']
- name: Display current DNS forwarders
ansible.builtin.debug:
msg: "Current DNS configuration retrieved"
tags: ['dns']
- name: Configure DNS settings
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
body: "reqxml={{ lookup('template', 'dns_config.json.j2') | urlencode }}"
status_code: [200, 201]
timeout: "{{ sophos_api_timeout }}"
register: dns_result
no_log: "{{ sophos_no_log_sensitive }}"
changed_when: "'successful' in dns_result.content | lower or '200' in dns_result.content"
failed_when: "'error' in dns_result.content | lower"
tags: ['dns']
- name: Display DNS configuration result
ansible.builtin.debug:
msg: "DNS configuration applied successfully"
tags: ['dns']

View File

@@ -0,0 +1,52 @@
---
# ============================================================================
# Configure Physical Interfaces
# ============================================================================
- name: Get current interface configuration from firewall
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController?reqxml=<Request><Login><Username>{{ sophos_api_username }}</Username><Password>{{ sophos_api_password }}</Password></Login><Get><Interface/></Get></Request>"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
return_content: true
status_code: [200]
register: current_interfaces
no_log: "{{ sophos_no_log_sensitive }}"
tags: ['interfaces']
- name: Parse current interface names
ansible.builtin.set_fact:
existing_interfaces: "{{ current_interfaces.content | regex_findall('<Name>(.*?)</Name>') }}"
tags: ['interfaces']
- name: Display existing interfaces
ansible.builtin.debug:
msg: "Found {{ existing_interfaces | length }} existing interfaces: {{ existing_interfaces | join(', ') }}"
tags: ['interfaces']
# Loop through each interface and configure it
- name: Configure physical interface {{ item.name }}
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
body: "reqxml={{ lookup('template', 'interface.json.j2') | urlencode }}"
status_code: [200, 201]
timeout: "{{ sophos_api_timeout }}"
loop: "{{ sophos_interfaces }}"
loop_control:
label: "{{ item.name }} ({{ item.zone }})"
register: interface_result
no_log: "{{ sophos_no_log_sensitive }}"
changed_when: "'successful' in interface_result.content | lower or '200' in interface_result.content"
failed_when: "'error' in interface_result.content | lower"
tags: ['interfaces']
- name: Display interface configuration results
ansible.builtin.debug:
msg: "Configured {{ sophos_interfaces | length }} interfaces successfully"
tags: ['interfaces']

View File

@@ -0,0 +1,83 @@
---
# ============================================================================
# Sophos Network Role - Main Tasks
# ============================================================================
# This role configures all network-related settings on Sophos XGS firewalls:
# - Physical interfaces
# - VLAN interfaces
# - DHCP servers
# - DNS configuration
# - Static routes
# ============================================================================
- name: Display network configuration overview
ansible.builtin.debug:
msg:
- "======================================"
- "Configuring Network Settings"
- "======================================"
- "Firewall: {{ inventory_hostname }}"
- "Interfaces: {{ sophos_interfaces | default([]) | length }}"
- "VLANs: {{ sophos_vlans | default([]) | length }}"
- "DHCP Servers: {{ sophos_dhcp_servers | default([]) | length }}"
- "Static Routes: {{ sophos_static_routes | default([]) | length }}"
tags: ['always']
# ============================================================================
# Configure Physical Interfaces
# ============================================================================
- name: Configure physical interfaces
ansible.builtin.include_tasks: interfaces.yml
when: sophos_interfaces is defined and sophos_interfaces | length > 0
tags: ['interfaces']
# ============================================================================
# Configure VLAN Interfaces
# ============================================================================
- name: Configure VLAN interfaces
ansible.builtin.include_tasks: vlans.yml
when: sophos_vlans is defined and sophos_vlans | length > 0
tags: ['vlans']
# ============================================================================
# Configure DHCP Servers
# ============================================================================
- name: Configure DHCP servers
ansible.builtin.include_tasks: dhcp.yml
when: sophos_dhcp_servers is defined and sophos_dhcp_servers | length > 0
tags: ['dhcp']
# ============================================================================
# Configure DNS Settings
# ============================================================================
- name: Configure DNS settings
ansible.builtin.include_tasks: dns.yml
when: sophos_dns is defined
tags: ['dns']
# ============================================================================
# Configure Static Routes
# ============================================================================
- name: Configure static routes
ansible.builtin.include_tasks: routes.yml
when: sophos_static_routes is defined and sophos_static_routes | length > 0
tags: ['routes']
# ============================================================================
# Summary
# ============================================================================
- name: Network configuration completed
ansible.builtin.debug:
msg:
- "======================================"
- "Network Configuration Complete"
- "======================================"
- "Firewall: {{ inventory_hostname }}"
- "All network settings have been applied."
tags: ['always']

View File

@@ -0,0 +1,52 @@
---
# ============================================================================
# Configure Static Routes
# ============================================================================
- name: Get current static route configuration from firewall
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController?reqxml=<Request><Login><Username>{{ sophos_api_username }}</Username><Password>{{ sophos_api_password }}</Password></Login><Get><StaticRoute/></Get></Request>"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
return_content: true
status_code: [200]
register: current_routes
no_log: "{{ sophos_no_log_sensitive }}"
tags: ['routes']
- name: Parse current route names
ansible.builtin.set_fact:
existing_routes: "{{ current_routes.content | regex_findall('<Name>(.*?)</Name>') }}"
tags: ['routes']
- name: Display existing static routes
ansible.builtin.debug:
msg: "Found {{ existing_routes | length }} existing static routes: {{ existing_routes | join(', ') }}"
tags: ['routes']
# Loop through each static route and configure it
- name: Configure static route {{ item.name }}
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
body: "reqxml={{ lookup('template', 'static_route.json.j2') | urlencode }}"
status_code: [200, 201]
timeout: "{{ sophos_api_timeout }}"
loop: "{{ sophos_static_routes }}"
loop_control:
label: "{{ item.name }} ({{ item.destination }}/{{ item.netmask }} via {{ item.gateway }})"
register: route_result
no_log: "{{ sophos_no_log_sensitive }}"
changed_when: "'successful' in route_result.content | lower or '200' in route_result.content"
failed_when: "'error' in route_result.content | lower"
tags: ['routes']
- name: Display static route configuration results
ansible.builtin.debug:
msg: "Configured {{ sophos_static_routes | length }} static routes successfully"
tags: ['routes']

View File

@@ -0,0 +1,52 @@
---
# ============================================================================
# Configure VLAN Interfaces
# ============================================================================
- name: Get current VLAN configuration from firewall
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController?reqxml=<Request><Login><Username>{{ sophos_api_username }}</Username><Password>{{ sophos_api_password }}</Password></Login><Get><VLANInterface/></Get></Request>"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
return_content: true
status_code: [200]
register: current_vlans
no_log: "{{ sophos_no_log_sensitive }}"
tags: ['vlans']
- name: Parse current VLAN names
ansible.builtin.set_fact:
existing_vlans: "{{ current_vlans.content | regex_findall('<Name>(.*?)</Name>') }}"
tags: ['vlans']
- name: Display existing VLANs
ansible.builtin.debug:
msg: "Found {{ existing_vlans | length }} existing VLANs: {{ existing_vlans | join(', ') }}"
tags: ['vlans']
# Loop through each VLAN and configure it
- name: Configure VLAN interface {{ item.name }}
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
body: "reqxml={{ lookup('template', 'vlan.json.j2') | urlencode }}"
status_code: [200, 201]
timeout: "{{ sophos_api_timeout }}"
loop: "{{ sophos_vlans }}"
loop_control:
label: "{{ item.name }} (VLAN {{ item.vlan_id }})"
register: vlan_result
no_log: "{{ sophos_no_log_sensitive }}"
changed_when: "'successful' in vlan_result.content | lower or '200' in vlan_result.content"
failed_when: "'error' in vlan_result.content | lower"
tags: ['vlans']
- name: Display VLAN configuration results
ansible.builtin.debug:
msg: "Configured {{ sophos_vlans | length }} VLANs successfully"
tags: ['vlans']

View File

@@ -0,0 +1,47 @@
<Request>
<Login>
<Username>{{ sophos_api_username }}</Username>
<Password>{{ sophos_api_password }}</Password>
</Login>
<Set operation="{% if item.name in existing_dhcp_servers %}update{% else %}add{% endif %}">
<DHCPServer>
<Name>{{ item.name }}</Name>
<Interface>{{ item.interface }}</Interface>
<Status>{{ 'Enable' if item.enabled | default(true) else 'Disable' }}</Status>
<StartIPAddress>{{ item.start_ip }}</StartIPAddress>
<EndIPAddress>{{ item.end_ip }}</EndIPAddress>
<Netmask>{{ item.netmask }}</Netmask>
<Gateway>{{ item.gateway }}</Gateway>
<DNSServers>
{% for dns in item.dns_servers %}
<Server>{{ dns }}</Server>
{% endfor %}
</DNSServers>
{% if item.domain is defined %}
<DomainName>{{ item.domain }}</DomainName>
{% endif %}
<LeaseTime>{{ item.lease_time | default(86400) }}</LeaseTime>
{% if item.reservations is defined and item.reservations | length > 0 %}
<Reservations>
{% for reservation in item.reservations %}
<Reservation>
<MACAddress>{{ reservation.mac_address }}</MACAddress>
<IPAddress>{{ reservation.ip_address }}</IPAddress>
<Hostname>{{ reservation.hostname | default('') }}</Hostname>
</Reservation>
{% endfor %}
</Reservations>
{% endif %}
{% if item.dhcp_options is defined and item.dhcp_options | length > 0 %}
<DHCPOptions>
{% for option in item.dhcp_options %}
<Option>
<Code>{{ option.option }}</Code>
<Value>{{ option.value }}</Value>
</Option>
{% endfor %}
</DHCPOptions>
{% endif %}
</DHCPServer>
</Set>
</Request>

View File

@@ -0,0 +1,19 @@
<Request>
<Login>
<Username>{{ sophos_api_username }}</Username>
<Password>{{ sophos_api_password }}</Password>
</Login>
<Set operation="update">
<DNS>
<Forwarders>
{% for forwarder in sophos_dns.forwarders %}
<Server>{{ forwarder }}</Server>
{% endfor %}
</Forwarders>
{% if sophos_dns.domain is defined %}
<DomainName>{{ sophos_dns.domain }}</DomainName>
{% endif %}
<DNSForwarder>{{ 'Enable' if sophos_dns.enable_dns_forwarder | default(true) else 'Disable' }}</DNSForwarder>
</DNS>
</Set>
</Request>

View File

@@ -0,0 +1,25 @@
<Request>
<Login>
<Username>{{ sophos_api_username }}</Username>
<Password>{{ sophos_api_password }}</Password>
</Login>
<Set operation="{% if item.name in existing_interfaces %}update{% else %}add{% endif %}">
<Interface>
<Name>{{ item.name }}</Name>
<Description>{{ item.description | default('') }}</Description>
<Zone>{{ item.zone }}</Zone>
<Type>{{ item.type | default('physical') }}</Type>
{% if item.mode == 'static' %}
<IPAddress>{{ item.ip_address }}</IPAddress>
<Netmask>{{ item.netmask }}</Netmask>
{% if item.gateway is defined %}
<Gateway>{{ item.gateway }}</Gateway>
{% endif %}
{% elif item.mode == 'dhcp' %}
<DHCPMode>Enable</DHCPMode>
{% endif %}
<MTU>{{ item.mtu | default(sophos_default_mtu) }}</MTU>
<Status>{{ 'Enable' if item.enabled | default(true) else 'Disable' }}</Status>
</Interface>
</Set>
</Request>

View File

@@ -0,0 +1,19 @@
<Request>
<Login>
<Username>{{ sophos_api_username }}</Username>
<Password>{{ sophos_api_password }}</Password>
</Login>
<Set operation="{% if item.name in existing_routes %}update{% else %}add{% endif %}">
<StaticRoute>
<Name>{{ item.name }}</Name>
<Destination>{{ item.destination }}</Destination>
<Netmask>{{ item.netmask }}</Netmask>
<Gateway>{{ item.gateway }}</Gateway>
{% if item.interface is defined %}
<Interface>{{ item.interface }}</Interface>
{% endif %}
<Metric>{{ item.metric | default(10) }}</Metric>
<Status>{{ 'Enable' if item.enabled | default(true) else 'Disable' }}</Status>
</StaticRoute>
</Set>
</Request>

View File

@@ -0,0 +1,18 @@
<Request>
<Login>
<Username>{{ sophos_api_username }}</Username>
<Password>{{ sophos_api_password }}</Password>
</Login>
<Set operation="{% if item.name in existing_vlans %}update{% else %}add{% endif %}">
<VLANInterface>
<Name>{{ item.name }}</Name>
<Description>{{ item.description | default('') }}</Description>
<VLANID>{{ item.vlan_id }}</VLANID>
<ParentInterface>{{ item.parent_interface }}</ParentInterface>
<Zone>{{ item.zone }}</Zone>
<IPAddress>{{ item.ip_address }}</IPAddress>
<Netmask>{{ item.netmask }}</Netmask>
<Status>{{ 'Enable' if item.enabled | default(true) else 'Disable' }}</Status>
</VLANInterface>
</Set>
</Request>

View File

@@ -0,0 +1,4 @@
---
# Default SNMP and logging settings
sophos_default_snmp_enabled: false
sophos_default_logging_enabled: true

View File

@@ -0,0 +1,68 @@
---
# ============================================================================
# Sophos SNMP and Logging Role - Main Tasks
# ============================================================================
- name: Display SNMP and logging configuration overview
ansible.builtin.debug:
msg:
- "======================================"
- "Configuring SNMP and Logging"
- "======================================"
- "Firewall: {{ inventory_hostname }}"
- "SNMP Enabled: {{ sophos_snmp.enabled | default(false) }}"
- "Syslog Servers: {{ sophos_logging.syslog_servers | default([]) | length }}"
tags: ['always']
# Configure SNMP
- name: Configure SNMP settings
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
body: "reqxml={{ lookup('template', 'snmp_config.json.j2') | urlencode }}"
status_code: [200, 201]
timeout: "{{ sophos_api_timeout }}"
when: sophos_snmp is defined
no_log: "{{ sophos_no_log_sensitive }}"
tags: ['snmp']
# Configure Syslog
- name: Configure syslog servers
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
body: "reqxml={{ lookup('template', 'syslog_server.json.j2') | urlencode }}"
status_code: [200, 201]
timeout: "{{ sophos_api_timeout }}"
loop: "{{ sophos_logging.syslog_servers | default([]) }}"
loop_control:
label: "{{ item.host }}"
when: sophos_logging is defined
no_log: "{{ sophos_no_log_sensitive }}"
tags: ['logging', 'syslog']
# Configure NTP
- name: Configure NTP settings
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
body: "reqxml={{ lookup('template', 'ntp_config.json.j2') | urlencode }}"
status_code: [200, 201]
timeout: "{{ sophos_api_timeout }}"
when: sophos_ntp is defined
no_log: "{{ sophos_no_log_sensitive }}"
tags: ['ntp']
- name: SNMP and logging configuration completed
ansible.builtin.debug:
msg: "SNMP, logging, and NTP configured successfully"
tags: ['always']

View File

@@ -0,0 +1,16 @@
<Request>
<Login>
<Username>{{ sophos_api_username }}</Username>
<Password>{{ sophos_api_password }}</Password>
</Login>
<Set operation="update">
<NTP>
<Servers>
{% for server in sophos_ntp.servers %}
<Server>{{ server }}</Server>
{% endfor %}
</Servers>
<Timezone>{{ sophos_ntp.timezone | default('UTC') }}</Timezone>
</NTP>
</Set>
</Request>

View File

@@ -0,0 +1,35 @@
<Request>
<Login>
<Username>{{ sophos_api_username }}</Username>
<Password>{{ sophos_api_password }}</Password>
</Login>
<Set operation="update">
<SNMP>
<Status>{{ 'Enable' if sophos_snmp.enabled | default(false) else 'Disable' }}</Status>
<Version>{{ sophos_snmp.version | default('v2c') }}</Version>
{% if sophos_snmp.version | default('v2c') == 'v2c' %}
<Community>{{ sophos_snmp.community }}</Community>
{% endif %}
<Location>{{ sophos_snmp.location | default('') }}</Location>
<Contact>{{ sophos_snmp.contact | default('') }}</Contact>
{% if sophos_snmp.allowed_networks is defined %}
<AllowedNetworks>
{% for network in sophos_snmp.allowed_networks %}
<Network>{{ network }}</Network>
{% endfor %}
</AllowedNetworks>
{% endif %}
{% if sophos_snmp.trap_destinations is defined %}
<TrapDestinations>
{% for trap in sophos_snmp.trap_destinations %}
<Destination>
<Host>{{ trap.host }}</Host>
<Port>{{ trap.port | default(162) }}</Port>
<Community>{{ trap.community }}</Community>
</Destination>
{% endfor %}
</TrapDestinations>
{% endif %}
</SNMP>
</Set>
</Request>

View File

@@ -0,0 +1,15 @@
<Request>
<Login>
<Username>{{ sophos_api_username }}</Username>
<Password>{{ sophos_api_password }}</Password>
</Login>
<Set operation="add">
<SyslogServer>
<Host>{{ item.host }}</Host>
<Port>{{ item.port | default(514) }}</Port>
<Protocol>{{ item.protocol | default('udp') | upper }}</Protocol>
<Facility>{{ item.facility | default('local0') }}</Facility>
<Severity>{{ item.severity | default('informational') }}</Severity>
</SyslogServer>
</Set>
</Request>

View File

@@ -0,0 +1,5 @@
---
# Default remote access VPN settings
sophos_default_remote_vpn_port: 443
sophos_default_remote_vpn_encryption: "aes256"
sophos_default_remote_vpn_hash: "sha256"

View File

@@ -0,0 +1,43 @@
---
# ============================================================================
# Sophos Remote Access VPN Role - Main Tasks
# ============================================================================
- name: Display remote access VPN configuration overview
ansible.builtin.debug:
msg:
- "======================================"
- "Configuring Remote Access VPN"
- "======================================"
- "Firewall: {{ inventory_hostname }}"
- "Type: {{ sophos_remote_access_vpn.type | default('Not configured') }}"
tags: ['always']
when: sophos_remote_access_vpn is defined
- name: Skip remote access VPN configuration (not defined)
ansible.builtin.debug:
msg: "No remote access VPN configuration defined for {{ inventory_hostname }}"
when: sophos_remote_access_vpn is not defined
tags: ['always']
- name: Configure remote access VPN
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
body: "reqxml={{ lookup('template', 'remote_access_vpn.json.j2') | urlencode }}"
status_code: [200, 201]
timeout: "{{ sophos_api_timeout }}"
register: remote_vpn_result
no_log: "{{ sophos_no_log_sensitive }}"
when: sophos_remote_access_vpn is defined
changed_when: "'successful' in remote_vpn_result.content | lower"
tags: ['vpn', 'remote-access']
- name: Remote access VPN configuration completed
ansible.builtin.debug:
msg: "Remote access VPN configured successfully"
when: sophos_remote_access_vpn is defined
tags: ['always']

View File

@@ -0,0 +1,63 @@
<Request>
<Login>
<Username>{{ sophos_api_username }}</Username>
<Password>{{ sophos_api_password }}</Password>
</Login>
<Set operation="update">
<RemoteAccessVPN>
<Name>{{ sophos_remote_access_vpn.name | default('Remote-Access-VPN') }}</Name>
<Status>{{ 'Enable' if sophos_remote_access_vpn.enabled | default(true) else 'Disable' }}</Status>
<Type>{{ sophos_remote_access_vpn.type | upper }}</Type>
<!-- Connection Settings -->
<ListeningPort>{{ sophos_remote_access_vpn.listening_port | default(443) }}</ListeningPort>
<ListeningInterface>{{ sophos_remote_access_vpn.listening_interface }}</ListeningInterface>
<!-- Authentication -->
<AuthenticationMethod>{{ sophos_remote_access_vpn.authentication_method | default('local') }}</AuthenticationMethod>
<UserGroups>
{% for group in sophos_remote_access_vpn.user_groups %}
<Group>{{ group }}</Group>
{% endfor %}
</UserGroups>
<!-- IP Address Pool -->
<AddressPool>
<Network>{{ sophos_remote_access_vpn.address_pool.network }}</Network>
<Netmask>{{ sophos_remote_access_vpn.address_pool.netmask }}</Netmask>
<StartIP>{{ sophos_remote_access_vpn.address_pool.start_ip }}</StartIP>
<EndIP>{{ sophos_remote_access_vpn.address_pool.end_ip }}</EndIP>
</AddressPool>
<!-- DNS Settings -->
<DNSServers>
{% for dns in sophos_remote_access_vpn.dns_servers %}
<Server>{{ dns }}</Server>
{% endfor %}
</DNSServers>
<!-- Tunnel Mode -->
<TunnelMode>{{ sophos_remote_access_vpn.tunnel_mode | default('split') }}</TunnelMode>
{% if sophos_remote_access_vpn.tunnel_mode | default('split') == 'split' %}
<TunnelNetworks>
{% for network in sophos_remote_access_vpn.tunnel_networks | default([]) %}
<Network>{{ network }}</Network>
{% endfor %}
</TunnelNetworks>
{% endif %}
<!-- Encryption -->
<Encryption>{{ sophos_remote_access_vpn.encryption | default('aes256') }}</Encryption>
<Hash>{{ sophos_remote_access_vpn.hash | default('sha256') }}</Hash>
<!-- Timeouts -->
<IdleTimeout>{{ sophos_remote_access_vpn.idle_timeout | default(1800) }}</IdleTimeout>
<SessionTimeout>{{ sophos_remote_access_vpn.session_timeout | default(43200) }}</SessionTimeout>
<!-- Advanced Settings -->
<MaxConnections>{{ sophos_remote_access_vpn.max_concurrent_connections | default(50) }}</MaxConnections>
<Compression>{{ 'Enable' if sophos_remote_access_vpn.enable_compression | default(true) else 'Disable' }}</Compression>
<BlockLANAccess>{{ 'Enable' if sophos_remote_access_vpn.block_lan_access | default(true) else 'Disable' }}</BlockLANAccess>
</RemoteAccessVPN>
</Set>
</Request>

View File

@@ -0,0 +1,11 @@
---
# Default VPN encryption settings
sophos_default_ike_encryption: "aes256"
sophos_default_ike_hash: "sha256"
sophos_default_ike_dh_group: 14
sophos_default_ike_lifetime: 28800
sophos_default_ipsec_encryption: "aes256"
sophos_default_ipsec_hash: "sha256"
sophos_default_ipsec_pfs_group: 14
sophos_default_ipsec_lifetime: 3600

View File

@@ -0,0 +1,66 @@
---
# ============================================================================
# Sophos Site-to-Site VPN Role - Main Tasks
# ============================================================================
- name: Display site-to-site VPN configuration overview
ansible.builtin.debug:
msg:
- "======================================"
- "Configuring Site-to-Site VPN Tunnels"
- "======================================"
- "Firewall: {{ inventory_hostname }}"
- "Tunnels: {{ sophos_site_to_site_vpns | default([]) | length }}"
tags: ['always']
when: sophos_site_to_site_vpns is defined
- name: Skip site-to-site VPN configuration (not defined)
ansible.builtin.debug:
msg: "No site-to-site VPN tunnels defined for {{ inventory_hostname }}"
when: sophos_site_to_site_vpns is not defined or sophos_site_to_site_vpns | length == 0
tags: ['always']
- name: Get current IPsec connections from firewall
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController?reqxml=<Request><Login><Username>{{ sophos_api_username }}</Username><Password>{{ sophos_api_password }}</Password></Login><Get><IPSecConnection/></Get></Request>"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
return_content: true
status_code: [200]
register: current_ipsec_connections
no_log: "{{ sophos_no_log_sensitive }}"
when: sophos_site_to_site_vpns is defined and sophos_site_to_site_vpns | length > 0
tags: ['vpn', 'site-to-site']
- name: Parse current IPsec connection names
ansible.builtin.set_fact:
existing_ipsec_connections: "{{ current_ipsec_connections.content | regex_findall('<Name>(.*?)</Name>') }}"
when: sophos_site_to_site_vpns is defined and sophos_site_to_site_vpns | length > 0
tags: ['vpn', 'site-to-site']
- name: Configure site-to-site VPN tunnel {{ item.name }}
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
body: "reqxml={{ lookup('template', 'ipsec_connection.json.j2') | urlencode }}"
status_code: [200, 201]
timeout: "{{ sophos_api_timeout }}"
loop: "{{ sophos_site_to_site_vpns }}"
loop_control:
label: "{{ item.name }} ({{ item.local_gateway }} <-> {{ item.remote_gateway }})"
register: vpn_result
no_log: "{{ sophos_no_log_sensitive }}"
when: sophos_site_to_site_vpns is defined and sophos_site_to_site_vpns | length > 0
changed_when: "'successful' in vpn_result.content | lower"
tags: ['vpn', 'site-to-site']
- name: Site-to-site VPN configuration completed
ansible.builtin.debug:
msg: "Configured {{ sophos_site_to_site_vpns | length }} site-to-site VPN tunnels successfully"
when: sophos_site_to_site_vpns is defined and sophos_site_to_site_vpns | length > 0
tags: ['always']

View File

@@ -0,0 +1,58 @@
<Request>
<Login>
<Username>{{ sophos_api_username }}</Username>
<Password>{{ sophos_api_password }}</Password>
</Login>
<Set operation="{% if item.name in existing_ipsec_connections | default([]) %}update{% else %}add{% endif %}">
<IPSecConnection>
<Name>{{ item.name }}</Name>
<Description>{{ item.description | default('') }}</Description>
<Status>{{ 'Enable' if item.enabled | default(true) else 'Disable' }}</Status>
<ConnectionType>{{ item.connection_type | default('tunnel') }}</ConnectionType>
<!-- Local Settings -->
<LocalGateway>{{ item.local_gateway }}</LocalGateway>
<LocalID>{{ item.local_id | default(item.local_gateway) }}</LocalID>
<LocalNetworks>
{% for network in item.local_networks %}
<Network>{{ network }}</Network>
{% endfor %}
</LocalNetworks>
<!-- Remote Settings -->
<RemoteGateway>{{ item.remote_gateway }}</RemoteGateway>
<RemoteID>{{ item.remote_id | default(item.remote_gateway) }}</RemoteID>
<RemoteNetworks>
{% for network in item.remote_networks %}
<Network>{{ network }}</Network>
{% endfor %}
</RemoteNetworks>
<!-- Phase 1 (IKE) Settings -->
<IKEVersion>{{ item.ike_version | default(2) }}</IKEVersion>
<IKEEncryption>{{ item.ike_encryption | default(sophos_default_ike_encryption) }}</IKEEncryption>
<IKEHash>{{ item.ike_hash | default(sophos_default_ike_hash) }}</IKEHash>
<IKEDHGroup>{{ item.ike_dh_group | default(sophos_default_ike_dh_group) }}</IKEDHGroup>
<IKELifetime>{{ item.ike_lifetime | default(sophos_default_ike_lifetime) }}</IKELifetime>
<!-- Authentication -->
<AuthenticationMethod>{{ item.authentication_method | default('psk') }}</AuthenticationMethod>
{% if item.authentication_method | default('psk') == 'psk' %}
<PreSharedKey>{{ item.psk }}</PreSharedKey>
{% endif %}
<!-- Phase 2 (IPsec) Settings -->
<IPSecMode>{{ item.ipsec_mode | default('tunnel') }}</IPSecMode>
<IPSecEncryption>{{ item.ipsec_encryption | default(sophos_default_ipsec_encryption) }}</IPSecEncryption>
<IPSecHash>{{ item.ipsec_hash | default(sophos_default_ipsec_hash) }}</IPSecHash>
<IPSecPFSGroup>{{ item.ipsec_pfs_group | default(sophos_default_ipsec_pfs_group) }}</IPSecPFSGroup>
<IPSecLifetime>{{ item.ipsec_lifetime | default(sophos_default_ipsec_lifetime) }}</IPSecLifetime>
<!-- Advanced Settings -->
<DPDEnabled>{{ 'Enable' if item.dpd_enabled | default(true) else 'Disable' }}</DPDEnabled>
<DPDInterval>{{ item.dpd_interval | default(30) }}</DPDInterval>
<DPDRetries>{{ item.dpd_retries | default(3) }}</DPDRetries>
<NATTraversal>{{ 'Enable' if item.nat_traversal | default(true) else 'Disable' }}</NATTraversal>
</IPSecConnection>
</Set>
</Request>

View File

@@ -0,0 +1,4 @@
---
# Default WAF settings
sophos_default_waf_mode: "prevention"
sophos_default_waf_session_timeout: 1800

View File

@@ -0,0 +1,90 @@
---
# ============================================================================
# Sophos WAF Role - Main Tasks
# ============================================================================
- name: Display WAF configuration overview
ansible.builtin.debug:
msg:
- "======================================"
- "Configuring Web Application Firewall"
- "======================================"
- "Firewall: {{ inventory_hostname }}"
- "Backends: {{ sophos_waf_backends | default([]) | length }}"
- "Policies: {{ sophos_waf_policies | default([]) | length }}"
- "Virtual Hosts: {{ sophos_waf_virtual_hosts | default([]) | length }}"
- "Exceptions: {{ sophos_waf_exceptions | default([]) | length }}"
tags: ['always']
- name: Configure WAF backend servers
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
body: "reqxml={{ lookup('template', 'waf_backend.json.j2') | urlencode }}"
status_code: [200, 201]
timeout: "{{ sophos_api_timeout }}"
loop: "{{ sophos_waf_backends | default([]) }}"
loop_control:
label: "{{ item.name }}"
when: sophos_waf_backends is defined and sophos_waf_backends | length > 0
no_log: "{{ sophos_no_log_sensitive }}"
tags: ['waf', 'backends']
- name: Configure WAF protection policies
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
body: "reqxml={{ lookup('template', 'waf_policy.json.j2') | urlencode }}"
status_code: [200, 201]
timeout: "{{ sophos_api_timeout }}"
loop: "{{ sophos_waf_policies | default([]) }}"
loop_control:
label: "{{ item.name }}"
when: sophos_waf_policies is defined and sophos_waf_policies | length > 0
no_log: "{{ sophos_no_log_sensitive }}"
tags: ['waf', 'policies']
- name: Configure WAF virtual hosts
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
body: "reqxml={{ lookup('template', 'waf_policy.json.j2') | urlencode }}"
status_code: [200, 201]
timeout: "{{ sophos_api_timeout }}"
loop: "{{ sophos_waf_virtual_hosts | default([]) }}"
loop_control:
label: "{{ item.name }} ({{ item.domain }})"
when: sophos_waf_virtual_hosts is defined and sophos_waf_virtual_hosts | length > 0
no_log: "{{ sophos_no_log_sensitive }}"
tags: ['waf', 'virtual-hosts']
- name: Configure WAF exceptions
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
body: "reqxml={{ lookup('template', 'waf_exception.json.j2') | urlencode }}"
status_code: [200, 201]
timeout: "{{ sophos_api_timeout }}"
loop: "{{ sophos_waf_exceptions | default([]) }}"
loop_control:
label: "{{ item.name }}"
when: sophos_waf_exceptions is defined and sophos_waf_exceptions | length > 0
no_log: "{{ sophos_no_log_sensitive }}"
tags: ['waf', 'exceptions']
- name: WAF configuration completed
ansible.builtin.debug:
msg: "Web Application Firewall configured successfully"
tags: ['always']

View File

@@ -0,0 +1,15 @@
<Request>
<Login>
<Username>{{ sophos_api_username }}</Username>
<Password>{{ sophos_api_password }}</Password>
</Login>
<Set operation="add">
<WebServer>
<Name>{{ item.name }}</Name>
<Host>{{ item.host }}</Host>
<Port>{{ item.port }}</Port>
<Protocol>{{ item.protocol | upper }}</Protocol>
<HealthCheck>{{ 'Enable' if item.health_check | default(true) else 'Disable' }}</HealthCheck>
</WebServer>
</Set>
</Request>

View File

@@ -0,0 +1,24 @@
<Request>
<Login>
<Username>{{ sophos_api_username }}</Username>
<Password>{{ sophos_api_password }}</Password>
</Login>
<Set operation="add">
<WebException>
<Name>{{ item.name }}</Name>
<VirtualHost>{{ item.virtual_host }}</VirtualHost>
<Path>{{ item.path }}</Path>
<SkipRules>
{% for rule in item.skip_rules %}
<Rule>{{ rule }}</Rule>
{% endfor %}
</SkipRules>
<SourceNetworks>
{% for network in item.source_networks %}
<Network>{{ network }}</Network>
{% endfor %}
</SourceNetworks>
<Comment>{{ item.comment | default('') }}</Comment>
</WebException>
</Set>
</Request>

View File

@@ -0,0 +1,31 @@
<Request>
<Login>
<Username>{{ sophos_api_username }}</Username>
<Password>{{ sophos_api_password }}</Password>
</Login>
<Set operation="add">
<WebPolicy>
<Name>{{ item.name }}</Name>
<Domain>{{ item.domain }}</Domain>
<ListeningIP>{{ item.listening_ip }}</ListeningIP>
<ListeningPort>{{ item.listening_port }}</ListeningPort>
<Protocol>{{ item.protocol | upper }}</Protocol>
{% if item.ssl_certificate is defined %}
<SSLCertificate>{{ item.ssl_certificate }}</SSLCertificate>
{% endif %}
<BackendServers>
{% for backend in item.backend_servers %}
<Server>{{ backend }}</Server>
{% endfor %}
</BackendServers>
<LoadBalancing>{{ item.load_balancing | default('round-robin') }}</LoadBalancing>
<ProtectionPolicy>{{ item.protection_policy }}</ProtectionPolicy>
<SessionTimeout>{{ item.session_timeout | default(1800) }}</SessionTimeout>
<HSTS>{{ 'Enable' if item.enable_hsts | default(false) else 'Disable' }}</HSTS>
<Compression>{{ 'Enable' if item.enable_compression | default(false) else 'Disable' }}</Compression>
{% if item.websocket_support is defined %}
<WebSocketSupport>{{ 'Enable' if item.websocket_support else 'Disable' }}</WebSocketSupport>
{% endif %}
</WebPolicy>
</Set>
</Request>

View File

@@ -0,0 +1,94 @@
---
# ============================================================================
# Sophos XGS Firewall Fleet Management - Main Playbook
# ============================================================================
# This playbook applies all configuration roles to Sophos XGS firewalls
# in the inventory. It is designed to be idempotent and safe to run
# repeatedly in production environments and CI/CD pipelines.
#
# Usage:
# ansible-playbook -i inventory/hosts.ini site.yml
# ansible-playbook -i inventory/hosts.ini site.yml --limit fw-branch1
# ansible-playbook -i inventory/hosts.ini site.yml --tags network
# ansible-playbook -i inventory/hosts.ini site.yml --check # Dry-run mode
#
# Author: Network Automation Team
# ============================================================================
- name: Configure Sophos XGS Firewalls
hosts: sophos_firewalls
gather_facts: false
become: false
# Set serial execution to avoid overwhelming API endpoints
# In production, adjust based on your API rate limits
serial: "{{ sophos_serial_execution | default(5) }}"
# Define task execution order and tagging
roles:
# Phase 1: Establish connectivity and validate API access
- role: sophos_common
tags: ['always', 'common', 'validation']
# Phase 2: Configure network foundation (interfaces, VLANs, routing, DNS, DHCP)
- role: sophos_network
tags: ['network', 'interfaces', 'vlans', 'dhcp', 'dns', 'routing']
when: sophos_manage_network | default(true)
# Phase 3: Configure firewall rules (after network objects exist)
- role: sophos_firewall_rules
tags: ['firewall', 'rules', 'security']
when: sophos_manage_firewall_rules | default(true)
# Phase 4: Configure site-to-site VPN tunnels
- role: sophos_vpn_site_to_site
tags: ['vpn', 'site-to-site', 'ipsec']
when: sophos_manage_site_to_site_vpn | default(true)
# Phase 5: Configure remote access VPN
- role: sophos_vpn_remote_access
tags: ['vpn', 'remote-access', 'ssl-vpn']
when: sophos_manage_remote_access_vpn | default(true)
# Phase 6: Configure web application firewall (WAF) policies
- role: sophos_waf
tags: ['waf', 'web', 'application-firewall']
when: sophos_manage_waf | default(true)
# Phase 7: Configure device access policies (management services)
- role: sophos_device_access
tags: ['device-access', 'management', 'security']
when: sophos_manage_device_access | default(true)
# Phase 8: Configure SNMP, logging, and NTP
- role: sophos_snmp_logging
tags: ['snmp', 'logging', 'monitoring', 'ntp']
when: sophos_manage_snmp_logging | default(true)
# Post-configuration tasks
post_tasks:
- name: Display configuration summary
ansible.builtin.debug:
msg:
- "======================================"
- "Sophos XGS Configuration Complete"
- "======================================"
- "Firewall: {{ inventory_hostname }}"
- "Management IP: {{ sophos_mgmt_host }}"
- "Roles Applied: {{ ansible_play_role_names | join(', ') }}"
- "Configuration Version: {{ sophos_config_version | default('N/A') }}"
tags: ['always']
- name: Save configuration to file (optional)
ansible.builtin.uri:
url: "https://{{ sophos_mgmt_host }}:{{ sophos_mgmt_port }}/webconsole/APIController?reqxml=<Request><Login><Username>{{ sophos_api_username }}</Username><Password>{{ sophos_api_password }}</Password></Login><Set operation='update'><System><Configuration><SaveConfiguration/></Configuration></System></Set></Request>"
method: POST
validate_certs: "{{ sophos_validate_certs }}"
headers:
Content-Type: "application/x-www-form-urlencoded"
status_code: [200, 201]
when: sophos_save_config | default(false)
tags: ['always']
no_log: "{{ sophos_no_log_sensitive | default(true) }}"
# End of site.yml

View File

@@ -0,0 +1,24 @@
---
# YAML Lint Configuration
extends: default
rules:
line-length:
max: 160
level: warning
indentation:
spaces: 2
indent-sequences: true
comments:
min-spaces-from-content: 2
truthy:
allowed-values: ['true', 'false', 'yes', 'no']
ignore: |
.git/
collections/
tests/sample_config/

View File

@@ -0,0 +1,17 @@
---
# Ansible Lint Configuration
# Run with: ansible-lint -c tests/linting/ansible-lint.yml
skip_list:
- yaml[line-length] # Allow longer lines in templates
- no-changed-when # Some API calls are difficult to make idempotent
- risky-file-permissions # Handled by role defaults
warn_list:
- experimental
- jinja[spacing]
exclude_paths:
- .git/
- .github/
- tests/sample_config/

View File

@@ -0,0 +1,100 @@
---
# ============================================================================
# Sample Firewall Configuration 1
# ============================================================================
# This is a complete example configuration for testing and reference.
# All IPs, domains, and credentials are FAKE and for demonstration only.
# ============================================================================
sophos_mgmt_host: "192.168.100.1"
sophos_api_username: "admin"
sophos_api_password: "SampleP@ssw0rd123"
sophos_hostname: "fw-sample1"
sophos_location: "sample-datacenter"
# Interfaces
sophos_interfaces:
- name: "Port1"
zone: "WAN"
description: "Internet connection"
mode: "static"
ip_address: "203.0.113.100"
netmask: "255.255.255.248"
gateway: "203.0.113.97"
enabled: true
- name: "Port2"
zone: "LAN"
description: "Internal network"
mode: "static"
ip_address: "10.100.0.1"
netmask: "255.255.255.0"
enabled: true
# VLANs
sophos_vlans:
- name: "VLAN10-Servers"
vlan_id: 10
parent_interface: "Port2"
zone: "LAN"
ip_address: "10.100.10.1"
netmask: "255.255.255.0"
enabled: true
# DHCP
sophos_dhcp_servers:
- name: "DHCP-LAN"
interface: "Port2"
enabled: true
start_ip: "10.100.0.100"
end_ip: "10.100.0.200"
netmask: "255.255.255.0"
gateway: "10.100.0.1"
dns_servers: ["8.8.8.8", "8.8.4.4"]
lease_time: 86400
# Firewall Rules
sophos_firewall_rules:
- name: "Allow-LAN-to-Internet"
source_zones: ["LAN"]
dest_zones: ["WAN"]
source_networks: ["any"]
dest_networks: ["any"]
services: ["HTTP", "HTTPS", "DNS"]
action: "accept"
log: false
enabled: true
# Site-to-Site VPN
sophos_site_to_site_vpns:
- name: "Sample-VPN"
enabled: true
local_gateway: "203.0.113.100"
local_networks: ["10.100.0.0/16"]
remote_gateway: "203.0.113.200"
remote_networks: ["10.200.0.0/16"]
psk: "SamplePSK123"
description: "Sample VPN tunnel"
# SNMP
sophos_snmp:
enabled: true
version: "v2c"
community: "sample"
location: "Sample Location"
contact: "admin@example.com"
# Logging
sophos_logging:
enabled: true
syslog_servers:
- host: "10.100.0.50"
port: 514
protocol: "udp"
# NTP
sophos_ntp:
servers:
- "0.pool.ntp.org"
timezone: "UTC"