Add files via upload

This commit is contained in:
DominikWoh 2024-09-18 14:59:09 +02:00 committed by GitHub
parent 55911f4ed4
commit 7e99a0b840
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 275 additions and 0 deletions

View File

@ -0,0 +1,187 @@
import xml.etree.ElementTree as ET
import openpyxl
from openpyxl.worksheet.table import Table, TableStyleInfo
from openpyxl.comments import Comment
# Function to extract aliases from the XML
def extract_aliases(root):
aliases = {}
for alias in root.findall('.//alias'):
name = alias.find('name').text
content = alias.find('content').text if alias.find('content') is not None else ''
aliases[name] = content # Store only the name and content
return aliases
# Function to extract interface descriptions from the XML
def get_interface_mapping(root):
interface_mapping = {}
# Loop through each interface in the XML to get its name and description
for interface in root.findall('.//interfaces/*'):
interface_name = interface.tag
interface_descr = interface.find('descr').text if interface.find('descr') is not None else interface_name
interface_mapping[interface_name] = interface_descr
return interface_mapping
# Function to parse the XML and extract firewall rules
def parse_firewall_rules(xml_file):
tree = ET.parse(xml_file)
root = tree.getroot()
# Get interface name to description mapping
interface_mapping = get_interface_mapping(root)
# Extract aliases
aliases = extract_aliases(root)
rules_by_interface = {}
# Loop through each rule in the firewall section of the XML
for rule in root.findall('.//rule'):
# Determine if the rule is activated based on the <disabled> tag
if rule.find('disabled') is not None and rule.find('disabled').text == '1':
activated = 'No'
else:
activated = 'Yes'
protocol = rule.find('protocol').text if rule.find('protocol') is not None else 'any'
# Extract the action (block or pass) from the <type> tag
action = rule.find('type').text if rule.find('type') is not None else 'pass'
# Extract source details
source = rule.find('source')
source_ip = source.find('network').text if source is not None and source.find('network') is not None else 'any'
source_port = source.find('port').text if source is not None and source.find('port') is not None else 'any'
# Map the source IP to the interface description before applying the "!" logic
source_ip = interface_mapping.get(source_ip, source_ip)
# Check if <not>1</not> exists for source
if source.find('not') is not None and source.find('not').text == '1':
source_ip = '!' + source_ip
# Extract destination details
destination = rule.find('destination')
if destination is not None:
destination_ip = destination.find('network').text if destination.find('network') is not None else 'any'
destination_port = destination.find('port').text if destination.find('port') is not None else 'any'
else:
destination_ip = 'any'
destination_port = 'any'
# Map the destination IP to the interface description before applying the "!" logic
destination_ip = interface_mapping.get(destination_ip, destination_ip)
# Check if <not>1</not> exists for destination
if destination is not None and destination.find('not') is not None and destination.find('not').text == '1':
destination_ip = '!' + destination_ip
# Extract gateway and schedule, and replace "none" with "*"
gateway = rule.find('gateway').text if rule.find('gateway') is not None else '*'
schedule = rule.find('sched').text if rule.find('sched') is not None else '*'
# Replace "none" values with "*"
gateway = gateway if gateway != 'none' else '*'
schedule = schedule if schedule != 'none' else '*'
# Replace "none" or "unknown" in rule_number and rules with "*"
rule_number = rule.find('tracker').text if rule.find('tracker') is not None else '*'
rule_number = rule_number if rule_number != 'unknown' else '*'
rules = rule.find('rule').text if rule.find('rule') is not None else '*'
rules = rules if rules != 'none' else '*'
description = rule.find('descr').text if rule.find('descr') is not None else ''
# Get the interface for the rule and map it to its description
interface = rule.find('interface').text if rule.find('interface') is not None else 'unknown'
interface_description = interface_mapping.get(interface, interface)
# Ensure interface_description is not None and rename "unknown" to "WAN"
if interface_description is None or interface_description == 'unknown':
interface_description = 'WAN'
# Handle Floating rules that involve multiple interfaces
if ',' in interface_description:
interface_description = 'Floating'
# Append the rule data to the appropriate interface in the dictionary
if interface_description not in rules_by_interface:
rules_by_interface[interface_description] = []
rules_by_interface[interface_description].append([activated, action, protocol, source_ip, source_port, destination_ip, destination_port, gateway, schedule, rule_number, description, rules])
return rules_by_interface, aliases
# Function to write data to Excel with separate sheets for each interface and apply table formatting
def write_to_excel(data_by_interface, aliases, output_file):
# Create a workbook
wb = openpyxl.Workbook()
# Loop over each interface and its associated rules
for interface_description, rules in data_by_interface.items():
# Create a new sheet for each interface description
if interface_description in wb.sheetnames:
ws = wb[interface_description]
else:
ws = wb.create_sheet(title=interface_description)
# Write headers, including the new "Action" column after "Aktiviert?"
headers = ['Aktiviert?', 'Action', 'Protocol', 'Source', 'Port', 'Destination', 'Port', 'Gateway', 'Schedule', 'Number of Rule', 'Description', 'Rules']
ws.append(headers)
# Write each rule as a row and add alias comments
for row_idx, row in enumerate(rules, start=2): # Start from row 2 because row 1 is for headers
ws.append(row)
# Check and add comments for aliases in the relevant columns
if row[3] in aliases: # Source IP alias
comment_text = f"Alias: {aliases[row[3]]}"
ws.cell(row=row_idx, column=4).comment = Comment(comment_text, "Alias")
if row[4] in aliases: # Source Port alias
comment_text = f"Alias: {aliases[row[4]]}"
ws.cell(row=row_idx, column=5).comment = Comment(comment_text, "Alias")
if row[5] in aliases: # Destination IP alias
comment_text = f"Alias: {aliases[row[5]]}"
ws.cell(row=row_idx, column=6).comment = Comment(comment_text, "Alias")
if row[6] in aliases: # Destination Port alias
comment_text = f"Alias: {aliases[row[6]]}"
ws.cell(row=row_idx, column=7).comment = Comment(comment_text, "Alias")
# Define the range for the table (including headers)
table_range = f'A1:{chr(64+len(headers))}{len(rules)+1}' # A1 to last column and last row
# Create the table
table = Table(displayName=f"Table_{interface_description}", ref=table_range)
# Apply the "Blue, Table Medium 2" style
style = TableStyleInfo(name="TableStyleMedium2", showFirstColumn=False,
showLastColumn=False, showRowStripes=True, showColumnStripes=True)
table.tableStyleInfo = style
# Add the table to the sheet
ws.add_table(table)
# Remove the default sheet if no data was written to it
if 'Sheet' in wb.sheetnames and not wb['Sheet'].max_row > 1:
del wb['Sheet']
# Save the workbook
wb.save(output_file)
print(f"Data written to {output_file}")
# Main function to execute the process
def main():
xml_file = r'C:\GitHub\opnsense\config-opnsense.xml' # Path to your XML file
output_file = 'firewall_rules_by_interface.xlsx' # Name of the output Excel file
# Parse the XML and get the firewall rules by interface
rules_by_interface, aliases = parse_firewall_rules(xml_file)
# Write the data to Excel with separate sheets for each interface
write_to_excel(rules_by_interface, aliases, output_file)
if __name__ == "__main__":
main()

88
README.md Normal file
View File

@ -0,0 +1,88 @@
# OPNsense Firewall Rule Exporter
This repository contains a Python script that extracts OPNsense firewall rules from an XML configuration file and writes the rules into an Excel file. The Excel file organizes the firewall rules into separate sheets based on the interface, with additional functionality to handle aliases and comments.
## Features
- **Interface-based Sheet Organization**: The script creates a separate sheet in the Excel file for each network interface found in the OPNsense firewall configuration.
- **Alias Detection and Comments**: If aliases are used in source, destination, or port fields, the script automatically adds these aliases as comments in the Excel file.
- **Rule Fields**: For each rule, the following fields are extracted and displayed:
- `Aktiviert?`: Whether the rule is enabled or disabled.
- `Action`: Whether the rule allows (`pass`) or blocks (`block`) traffic.
- `Protocol`: The protocol used in the rule (e.g., TCP, UDP, any).
- `Source` and `Port`: The source network or alias and the port.
- `Destination` and `Port`: The destination network or alias and the port.
- `Gateway`: The gateway associated with the rule, if any.
- `Schedule`: The schedule associated with the rule, if any.
- `Number of Rule`: The rule number or tracker.
- `Description`: The description of the rule.
- `Rules`: Any additional rules or custom rules for each interface.
## Prerequisites
- **Python 3.7+**: Ensure you have Python installed on your machine.
- **Libraries**:
- `openpyxl`: Used to generate and manipulate Excel files.
- `xml.etree.ElementTree`: Used for parsing the XML file.
You can install the required libraries using pip:
```bash
pip install openpyxl
## Usage
1. **Clone the repository**:
```bash
git clone https://github.com/DominikWoh/opnsense-firewall-exporter.git
cd opnsense-firewall-exporter
Place the OPNsense XML Configuration: Copy the OPNsense configuration XML file into the repository folder.
### Configuration
Update the following in the script:
1. **XML File Path**:
```python
xml_file = r'C:\GitHub\opnsense\config-opnsense.xml'
Replace `'C:\GitHub\opnsense\config-opnsense.xml'` with the actual path to your OPNsense XML file.
2. **Excel Output Path**:
```python
output_file = 'firewall_rules_by_interface.xlsx'
Replace 'firewall_rules_by_interface.xlsx' with your desired output file name and location.
### Run the script:
python opnsense_exporter.py
### Generated Excel File
After running the script, an Excel file named `firewall_rules_by_interface.xlsx` will be generated in the current directory. This file will contain firewall rules for each interface in a separate sheet, with aliases annotated in the source, destination, and port fields as comments.
### Example
The script will take an OPNsense XML configuration file, extract rules for each network interface, and write them to an Excel file with the following format:
| Aktiviert? | Action | Protocol | Source | Port | Destination | Port | Gateway | Schedule | Number of Rule | Description | Rules |
|------------|--------|----------|----------------|------|----------------|------|---------|----------|----------------|-------------|-------|
| Yes | pass | TCP | 192.168.1.0/24 | 80 | 10.0.0.0/24 | 443 | * | * | 12345 | Allow Web | * |
- If `Source`, `Destination`, or `Port` fields contain an alias, you can hover over the cell to see the details of the alias.
### How It Works
- **Parse the XML**: The script uses Pythons `xml.etree.ElementTree` to parse the OPNsense firewall configuration file.
- **Extract Aliases**: The script identifies and extracts all aliases from the configuration and stores them for later use.
- **Add Comments**: When an alias is used in a source, destination, or port field, a comment with the alias details (such as IP ranges or ports) is added to the corresponding cell in the Excel sheet.
### Future Improvements
- Add support for custom Excel formatting or styles.
- Enhance error handling and support for more firewall rule fields.
- Support for exporting additional types of firewall configurations.
### Contributing
Contributions are welcome! If you find any issues or have ideas for improvements, feel free to open an issue or submit a pull request.