A production-grade Python library for parsing, validating, and exporting Manufacturer Usage Description (MUD) profiles as defined in RFC 8520 with full support for RFC 9761 TLS/DTLS profiles.
MUD profiles allow IoT device manufacturers to formally describe the network behavior their devices require, enabling network administrators to automatically configure access control policies and significantly reduce the attack surface of IoT deployments.
- What is MUD?
- Features
- Installation
- Quick Start
- Detailed Usage Guide
- Command Line Interface
- Interactive Web Demo
- RFC Compliance
- Sample MUD Profiles
- API Reference
- Architecture
- Development
- Troubleshooting
- Contributing
- License
- Acknowledgements
Manufacturer Usage Description (MUD) is an IETF standard (RFC 8520) that provides a formal way for IoT device manufacturers to describe the intended network behavior of their devices.
IoT devices often have well-defined, limited network communication patterns. However, without formal descriptions:
- Network administrators don't know what traffic is legitimate
- Devices may be over-permissioned on the network
- Compromised devices can be used to attack other systems
- Manual firewall configuration is error-prone and time-consuming
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ IoT Device │────>│ MUD Manager │────>│ Firewall │
│ (sends URL)│ │ (fetches) │ │ (enforces) │
└─────────────┘ └──────┬──────┘ └─────────────┘
│
v
┌─────────────┐
│ MUD File │
│ (on web) │
└─────────────┘
- Device Announcement: IoT device announces its MUD URL (via DHCP, LLDP, or 802.1X)
- Profile Fetch: MUD manager fetches the MUD file from the URL
- Policy Generation: MUD file is parsed and converted to firewall rules
- Enforcement: Rules are applied to restrict device communication
A MUD file is a JSON document containing:
- Metadata: Device info, update timestamps, support status
- Access Control Lists (ACLs): Network communication rules
- Policy References: Which ACLs apply to inbound/outbound traffic
| Feature | Description |
|---|---|
| RFC 8520 Compliant | Full implementation of the MUD specification |
| RFC 9761 Support | TLS/DTLS profile extensions for secure communications |
| Modern Python | Built for Python 3.10+ with type hints and Pydantic v2 |
| Multiple Input Sources | Parse from files, strings, URLs, or dictionaries |
| Async Support | Asynchronous URL fetching for high-performance applications |
| Feature | Description |
|---|---|
| Schema Validation | Validates against RFC 8520 JSON schema |
| Semantic Validation | Cross-reference checking, constraint validation |
| Severity Levels | Errors, warnings, and informational messages |
| Detailed Reporting | Path-specific error messages with context |
| Format | Platform | Use Case |
|---|---|---|
| iptables | Linux | Traditional Linux firewall |
| nftables | Linux | Modern Linux firewall (netfilter) |
| Cisco ACL | Cisco IOS | Enterprise routers and switches |
| pfSense | BSD/pfSense | BSD-based firewalls |
| JSON | Any | Re-serialization, APIs |
| YAML | Any | Human-readable configuration |
| Interface | Description |
|---|---|
| Python API | Full-featured library for programmatic access |
| CLI | Rich command-line interface with syntax highlighting |
| Web Demo | Interactive Streamlit application |
- Python 3.10 or higher (uses modern features like match/case, union types)
- pip package manager
pip install mudparser# Development tools (pytest, mypy, ruff)
pip install mudparser[dev]
# Documentation tools (mkdocs, mkdocstrings)
pip install mudparser[docs]
# Demo application (streamlit)
pip install mudparser[demo]
# All extras
pip install mudparser[all]# Clone the repository
git clone https://github.com/elmiomar/mudparser.git
cd mudparser
# Install in development mode
pip install -e .# Or with all extras
pip install -e ".[all]"# Check version
mudparser --version
# Run a quick test
mudparser --helpfrommudparserimportMUDParser# Parse a MUD fileparser=MUDParser.from_file("device.mud.json")
# Get device infoprint(f"Device: {parser.mud.systeminfo}")
print(f"Rules: {parser.get_summary()['total_rules']}")
# Generate firewall rulesrules=parser.export.to_iptables(device_ip="192.168.1.100")
print(rules)# View profile information
mudparser info device.mud.json
# Validate a profile
mudparser validate device.mud.json
# Export to iptables
mudparser export device.mud.json -f iptables -d 192.168.1.100frommudparserimportMUDParser# Parse from file path (string or Path object)parser=MUDParser.from_file("path/to/device.mud.json")
# With pathlibfrompathlibimportPathparser=MUDParser.from_file(Path("data") /"device.mud.json")frommudparserimportMUDParserjson_content='''{ "ietf-mud:mud": { "mud-version": 1, "mud-url": "https://example.com/device.mud.json", "last-update": "2024-01-15T00:00:00Z", "cache-validity": 48, "is-supported": true, "systeminfo": "My IoT Device", "from-device-policy": { ... }, "to-device-policy": { ... } }, "ietf-access-control-list:access-lists": { ... }}'''parser=MUDParser.from_string(json_content, source="inline")frommudparserimportMUDParser# Synchronous fetchparser=MUDParser.from_url("https://iotanalytics.unsw.edu.au/mud/amazonEchoMud.json")
# Asynchronous fetch (for high-performance applications)importasyncioasyncdeffetch_profile():
parser=awaitMUDParser.from_url_async(
"https://iotanalytics.unsw.edu.au/mud/amazonEchoMud.json"
)
returnparserparser=asyncio.run(fetch_profile())frommudparserimportMUDParserdata= {
"ietf-mud:mud": {
"mud-version": 1,
"mud-url": "https://example.com/device.mud.json",
"last-update": "2024-01-15T00:00:00Z",
"cache-validity": 48,
"is-supported": True,
"systeminfo": "My IoT Device",
"from-device-policy": {
"access-lists": {
"access-list": [{"name": "from-device-acl"}]
}
},
"to-device-policy": {
"access-lists": {
"access-list": [{"name": "to-device-acl"}]
}
}
},
"ietf-access-control-list:access-lists": {
"acl": [
# ... ACL definitions
]
}
}
parser=MUDParser.from_dict(data, source="programmatic")frommudparserimportMUDParserparser=MUDParser.from_file("device.mud.json")
# Core metadata (always present)print(f"MUD Version: {parser.mud.mud_version}") # 1print(f"MUD URL: {parser.mud.mud_url}") # https://...print(f"Last Update: {parser.mud.last_update}") # datetime objectprint(f"Cache Validity: {parser.mud.cache_validity}h") # Hours (1-168)print(f"Is Supported: {parser.mud.is_supported}") # True/Falseprint(f"System Info: {parser.mud.systeminfo}") # Device description# Optional metadataprint(f"Manufacturer: {parser.mud.mfg_name}") # May be Noneprint(f"Model: {parser.mud.model_name}") # May be Noneprint(f"Firmware: {parser.mud.firmware_rev}") # May be Noneprint(f"Software: {parser.mud.software_rev}") # May be Noneprint(f"Documentation: {parser.mud.documentation}") # URL or Noneprint(f"MUD Signature: {parser.mud.mud_signature}") # Signature URLprint(f"Extensions: {parser.mud.extensions}") # List of extensionssummary=parser.get_summary()
print(f"URL: {summary['url']}")
print(f"Version: {summary['version']}")
print(f"Device: {summary['systeminfo']}")
print(f"Last Update: {summary['last_update']}")
print(f"Cache Validity: {summary['cache_validity_hours']} hours")
print(f"Supported: {summary['is_supported']}")
print(f"Manufacturer: {summary['manufacturer']}")
print(f"Model: {summary['model']}")
# Rule countsprint(f"Total ACLs: {summary['total_acls']}")
print(f"From-Device ACLs: {summary['from_device_acls']}")
print(f"To-Device ACLs: {summary['to_device_acls']}")
print(f"Total Rules: {summary['total_rules']}")
print(f"From-Device Rules: {summary['from_device_rules']}")
print(f"To-Device Rules: {summary['to_device_rules']}")
# Network resourcesprint(f"DNS Names: {summary['dns_names']}")
print(f"TCP Ports: {summary['ports']['tcp']}")
print(f"UDP Ports: {summary['ports']['udp']}")# Get all DNS names referenced in the profiledns_names=parser.get_dns_names()
print("DNS Names:")
fornameinsorted(dns_names):
print(f" - {name}")
# Get all ports usedports=parser.get_ports()
print(f"TCP Ports: {sorted(ports['tcp'])}")
print(f"UDP Ports: {sorted(ports['udp'])}")
# Get all ACE entries with directionfordirection, entryinparser.get_all_entries():
print(f"[{direction}] {entry.name}: {entry.get_description(direction)}")# Get all ACLsforaclinparser.profile.acls.acl:
print(f"\nACL: {acl.name}")
print(f" Type: {acl.acl_type.value}") # ipv4-acl-type, ipv6-acl-type, etc.print(f" Rules: {len(acl)}")
# Get ACLs by directionprint("\n=== FROM-DEVICE (Outbound) ===")
foraclinparser.get_from_device_acls():
print(f" {acl.name}: {len(acl)} rules")
print("\n=== TO-DEVICE (Inbound) ===")
foraclinparser.get_to_device_acls():
print(f" {acl.name}: {len(acl)} rules")foraclinparser.profile.acls.acl:
print(f"\nACL: {acl.name}")
forentryinacl.entries:
# Basic infoprint(f" Rule: {entry.name}")
# Actionifentry.is_accept():
print(" Action: ALLOW")
else:
print(" Action: DENY")
# Get human-readable descriptiondesc=entry.get_description(direction="from")
print(f" Description: {desc}")
# Access match conditionsmatches=entry.matches# Protocol informationprotocol=matches.get_protocol()
print(f" Protocol: {protocol}")
# DNS names in this ruledns_names=matches.get_dns_names()
ifdns_names:
print(f" DNS Names: {dns_names}")foraclinparser.profile.acls.acl:
forentryinacl.entries:
matches=entry.matches# IPv4 matchesifmatches.ipv4:
print(f" IPv4 Protocol: {matches.ipv4.protocol}")
ifmatches.ipv4.src_network:
print(f" Source Network: {matches.ipv4.src_network}")
ifmatches.ipv4.dst_network:
print(f" Dest Network: {matches.ipv4.dst_network}")
ifmatches.ipv4.src_dnsname:
print(f" Source DNS: {matches.ipv4.src_dnsname}")
ifmatches.ipv4.dst_dnsname:
print(f" Dest DNS: {matches.ipv4.dst_dnsname}")
# IPv6 matchesifmatches.ipv6:
print(f" IPv6 Protocol: {matches.ipv6.protocol}")
ifmatches.ipv6.dst_network:
print(f" Dest Network: {matches.ipv6.dst_network}")
# TCP matchesifmatches.tcp:
ifmatches.tcp.src_port:
print(f" TCP Source Port: {matches.tcp.src_port}")
ifmatches.tcp.dst_port:
print(f" TCP Dest Port: {matches.tcp.dst_port}")
ifmatches.tcp.direction_initiated:
print(f" Direction: {matches.tcp.direction_initiated.value}")
# UDP matchesifmatches.udp:
ifmatches.udp.src_port:
print(f" UDP Source Port: {matches.udp.src_port}")
ifmatches.udp.dst_port:
print(f" UDP Dest Port: {matches.udp.dst_port}")
# ICMP matchesifmatches.icmp:
print(f" ICMP Type: {matches.icmp.type}, Code: {matches.icmp.code}")
# Ethernet matchesifmatches.eth:
ifmatches.eth.src_mac:
print(f" Source MAC: {matches.eth.src_mac}")
ifmatches.eth.dst_mac:
print(f" Dest MAC: {matches.eth.dst_mac}")
ifmatches.eth.ethertype:
print(f" Ethertype: {matches.eth.ethertype}")
# MUD-specific matchesifmatches.mud:
match_type=matches.mud.get_match_type()
print(f" MUD Match Type: {match_type}")
ifmatches.mud.manufacturer:
print(f" Manufacturer: {matches.mud.manufacturer}")
ifmatches.mud.controller:
print(f" Controller: {matches.mud.controller}")
ifmatches.mud.local_networks:
print(" Local Networks: Yes")
ifmatches.mud.same_manufacturer:
print(" Same Manufacturer: Yes")frommudparserimportMUDParserparser=MUDParser.from_file("device.mud.json")
# Simple validation (returns list of error strings)errors=parser.validate()
iferrors:
print("Validation FAILED:")
forerrorinerrors:
print(f" - {error}")
else:
print("Validation PASSED!")frommudparserimportMUDParserfrommudparser.validatorimportMUDValidator, ValidationSeverityparser=MUDParser.from_file("device.mud.json")
validator=MUDValidator()
# Full validation with detailed resultsresult=validator.validate(parser.profile)
# Summaryprint(f"Valid: {result.is_valid}")
print(f"Total Issues: {len(result.issues)}")
print(f"Errors: {result.error_count}")
print(f"Warnings: {result.warning_count}")
# Detailed issuesforissueinresult.issues:
severity_icon= {
ValidationSeverity.ERROR: "ERROR",
ValidationSeverity.WARNING: "WARNING",
ValidationSeverity.INFO: "INFO"
}[issue.severity]
print(f"[{severity_icon}] {issue.message}")
ifissue.path:
print(f" Path: {issue.path}")
# Get only errorsforerrorinresult.errors:
print(f"Error: {error.message}")
# Get only warningsforwarninginresult.warnings:
print(f"Warning: {warning.message}")# Strict mode treats warnings as errorsresult=validator.validate(parser.profile, strict=True)
ifnotresult.is_valid:
print("Profile has errors or warnings in strict mode")frommudparser.validatorimportvalidate_profile, validate_json# Validate a MUDProfile objectresult=validate_profile(parser.profile)
# Validate raw JSON dataresult=validate_json({
"ietf-mud:mud": { ... },
"ietf-access-control-list:access-lists": { ... }
})frommudparserimportMUDParserparser=MUDParser.from_file("device.mud.json")
# Basic exportrules=parser.export.to_iptables(device_ip="192.168.1.100")
# With optionsrules=parser.export.to_iptables(
device_ip="192.168.1.100",
chain_prefix="IOT", # Custom chain prefix (default: MUD)include_comments=True, # Include rule comments (default: True)
)
print(rules)
# Save to filewithopen("firewall_rules.sh", "w") asf:
f.write(rules)Example iptables output:
#!/bin/bash# IPTables rules generated from MUD profile# Device: Amazon Echo# MUD URL: https://example.com/echo.mud.json# Device IP: 192.168.1.100# Create custom chains
iptables -N IOT_FROM_ECHO 2>/dev/null || iptables -F IOT_FROM_ECHO
iptables -N IOT_TO_ECHO 2>/dev/null || iptables -F IOT_TO_ECHO
# Jump to custom chains
iptables -A FORWARD -s 192.168.1.100 -j IOT_FROM_ECHO
iptables -A FORWARD -d 192.168.1.100 -j IOT_TO_ECHO
# FROM-DEVICE rules (outbound traffic from IoT device)
iptables -A IOT_FROM_ECHO -s 192.168.1.100 -p tcp -d api.amazon.com --dport 443 \
-m state --state NEW,ESTABLISHED -m comment --comment "allow-https-api" -j ACCEPT
# Default deny
iptables -A IOT_FROM_ECHO -j DROP
iptables -A IOT_TO_ECHO -j DROPrules=parser.export.to_nftables(
device_ip="192.168.1.100",
table_name="iot_devices", # Custom table name (default: mud_filter)
)
# Save to filewithopen("rules.nft", "w") asf:
f.write(rules)
# Apply with: nft -f rules.nftExample nftables output:
#!/usr/sbin/nft -f
# nftables rules generated from MUD profile
# Device: Amazon Echo
table inet iot_devices {
chain from_echo {
type filter hook forward priority 0; policy drop;
ip saddr 192.168.1.100 tcp dport 443 ip daddr api.amazon.com accept
ip saddr 192.168.1.100 udp dport 53 accept
}
chain to_echo {
type filter hook forward priority 0; policy drop;
ip daddr 192.168.1.100 tcp sport 443 ip saddr api.amazon.com accept
}
}
rules=parser.export.to_cisco_acl(
acl_number_start=100, # Starting ACL number (default: 100)include_remarks=True, # Include remark statements (default: True)
)
print(rules)Example Cisco ACL output:
! Cisco ACL generated from MUD profile
! Device: Amazon Echo
! MUD URL: https://example.com/echo.mud.json
! FROM-DEVICE ACL (outbound from IoT device)
access-list 100 remark MUD profile: Amazon Echo - from-device
access-list 100 permit tcp any host api.amazon.com eq 443
access-list 100 permit udp any any eq 53
access-list 100 deny ip any any
! TO-DEVICE ACL (inbound to IoT device)
access-list 101 remark MUD profile: Amazon Echo - to-device
access-list 101 permit tcp host api.amazon.com eq 443 any
access-list 101 deny ip any any
rules=parser.export.to_pfsense(
device_ip="192.168.1.100",
interface="lan", # Interface name (default: lan)
)
# Save to file for importwithopen("pfsense_rules.xml", "w") asf:
f.write(rules)# JSON export (re-serialized MUD profile)json_output=parser.export.to_json(indent=2)
# YAML export (human-readable)yaml_output=parser.export.to_yaml()frommudparser.exportersimportExportFormat# Export using format enumoutput=parser.export.export(
format=ExportFormat.IPTABLES,
device_ip="192.168.1.100"
)
# Export using format stringoutput=parser.export.export(
format="nftables",
device_ip="192.168.1.100"
)
# Available formatsforfmtinExportFormat:
print(f" - {fmt.value}")# Get export summary before generating rulessummary=parser.export.get_summary()
print(f"Device: {summary['device_info']}")
print(f"From-Device Rules: {summary['from_device_rules']}")
print(f"To-Device Rules: {summary['to_device_rules']}")
print(f"Total Rules: {summary['total_rules']}")
print(f"DNS Names: {summary['dns_names']}")
print(f"Ports: {summary['ports']}")
print(f"Supported Formats: {summary['supported_formats']}")The mudparser CLI provides a rich, user-friendly interface for working with MUD profiles.
| Command | Description |
|---|---|
validate | Validate a MUD profile against RFC 8520 |
info | Display profile information and summary |
rules | Show access control rules in human-readable format |
export | Export to firewall rule format |
fetch | Fetch a MUD profile from URL |
diff | Compare two MUD profiles |
demo | Launch interactive Streamlit demo |
Validate a MUD profile for RFC compliance.
# Basic validation
mudparser validate device.mud.json
# Verbose output (show all issues including warnings)
mudparser validate device.mud.json --verbose
# Strict mode (treat warnings as errors)
mudparser validate device.mud.json --strict
# JSON output (for scripting)
mudparser validate device.mud.json --jsonDisplay profile information and summary.
# Show profile info
mudparser info device.mud.json
# JSON output
mudparser info device.mud.json --jsonExample output:
╭──────────────────────────────── MUD Profile ─────────────────────────────────╮
│ Amazon Echo │
│ URL: https://amazonecho.com/amazonecho │
╰──────────────────────────────────────────────────────────────────────────────╯
Profile Metadata
┏━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Property ┃ Value ┃
┡━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ MUD Version │ 1 │
│ Last Update │ 2024-01-15T00:00:00+00:00 │
│ Cache Validity │ 48 hours │
│ Supported │ Yes │
└────────────────┴──────────────────────────────────┘
Access Control Summary
┏━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━┳━━━━━━━┓
┃ Direction ┃ ACLs ┃ Rules ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━╇━━━━━━━┩
│ From Device (Outbound) │ 2 │ 15 │
│ To Device (Inbound) │ 1 │ 8 │
│ Total │ 3 │ 23 │
└────────────────────────┴──────┴───────┘
Referenced DNS Names:
- api.amazonalexa.com
- device-metrics-us.amazon.com
- softwareupdates.amazon.com
Referenced Ports:
TCP: 80, 443
UDP: 53, 123
Show access control rules in human-readable format.
# Show all rules
mudparser rules device.mud.json
# Show only from-device (outbound) rules
mudparser rules device.mud.json --direction from
# Show only to-device (inbound) rules
mudparser rules device.mud.json --direction to
# JSON output
mudparser rules device.mud.json --jsonExport to firewall rule format.
# Export to iptables
mudparser export device.mud.json -f iptables -d 192.168.1.100
# Export to nftables with custom table name
mudparser export device.mud.json -f nftables -d 192.168.1.100
# Export to Cisco ACL
mudparser export device.mud.json -f cisco
# Export to pfSense
mudparser export device.mud.json -f pfsense -d 192.168.1.100
# Save to file
mudparser export device.mud.json -f iptables -d 192.168.1.100 -o rules.sh
# Available formats: iptables, nftables, cisco, pfsense, json, yamlFetch a MUD profile from URL.
# Fetch and display info
mudparser fetch https://iotanalytics.unsw.edu.au/mud/amazonEchoMud.json
# Fetch and save to file
mudparser fetch https://example.com/device.mud.json -o device.mud.json
# Fetch and validate
mudparser fetch https://example.com/device.mud.json --validateCompare two MUD profiles.
# Compare two profiles
mudparser diff old.mud.json new.mud.json
# Show only changes in rules
mudparser diff old.mud.json new.mud.json --rules-onlyLaunch the interactive Streamlit web demo.
# Launch on default port (8501)
mudparser demo
# Launch on custom port
mudparser demo --port 8080# Show version
mudparser --version
# Show help
mudparser --help
# Show help for a specific command
mudparser validate --helpMudParser includes an interactive Streamlit web application for exploring MUD profiles.
# Via CLI
mudparser demo
# Directly with Streamlit
streamlit run demo/streamlit_app.py
# On a custom port
mudparser demo --port 8080- Load Profiles: Upload files, paste JSON, or use built-in samples
- Overview Tab: Device info, metadata, and rule summaries
- Rules Tab: Interactive ACL/ACE browser with filtering
- Validation Tab: Detailed validation results with severity levels
- Export Tab: Generate and download firewall rules
- Raw JSON Tab: View and download the original profile
The demo provides:
- Visual representation of ACLs and rules
- Color-coded action indicators (ALLOW/DENY)
- Interactive export with format selection
- Real-time validation feedback
MudParser fully implements RFC 8520, including:
| Field | Status | Description |
|---|---|---|
mud-version | ✅ | MUD specification version |
mud-url | ✅ | URL of this MUD file |
mud-signature | ✅ | URL of PKCS#7 signature |
last-update | ✅ | Last modification timestamp |
cache-validity | ✅ | Hours this file can be cached (1-168) |
is-supported | ✅ | Whether device is still supported |
systeminfo | ✅ | Device description (max 60 chars) |
mfg-name | ✅ | Manufacturer name |
model-name | ✅ | Model name |
firmware-rev | ✅ | Firmware revision |
software-rev | ✅ | Software revision |
documentation | ✅ | Documentation URL |
extensions | ✅ | List of MUD extensions |
from-device-policy | ✅ | Outbound traffic policy |
to-device-policy | ✅ | Inbound traffic policy |
| Feature | Status | Description |
|---|---|---|
| IPv4 ACL | ✅ | IPv4 access control list |
| IPv6 ACL | ✅ | IPv6 access control list |
| Ethernet ACL | ✅ | Layer 2 access control list |
| Match Type | Status | Fields |
|---|---|---|
| IPv4 | ✅ | protocol, src/dst network, src/dst DNS name, DSCP, ECN, length, TTL, flags |
| IPv6 | ✅ | protocol, src/dst network, src/dst DNS name, DSCP, ECN, flow-label, length, hop-limit |
| TCP | ✅ | src/dst port (with operators), flags, direction-initiated |
| UDP | ✅ | src/dst port (with operators) |
| ICMP | ✅ | type, code |
| ICMPv6 | ✅ | type, code |
| Ethernet | ✅ | src/dst MAC address, ethertype |
| Extension | Status | Description |
|---|---|---|
manufacturer | ✅ | Match by manufacturer domain |
same-manufacturer | ✅ | Match devices from same manufacturer |
model | ✅ | Match by model URI |
local-networks | ✅ | Match local network traffic |
controller | ✅ | Match MUD controller (dns, ntp, gateway) |
my-controller | ✅ | Match custom controller URIs |
| Operator | Status | Description |
|---|---|---|
eq | ✅ | Equals |
lt | ✅ | Less than |
gt | ✅ | Greater than |
neq | ✅ | Not equal |
range | ✅ | Port range |
MudParser supports RFC 9761 TLS profile extensions:
| Feature | Status | Description |
|---|---|---|
| TLS version constraints | ✅ | Minimum/maximum TLS versions |
| Cipher suite restrictions | ✅ | Allowed/forbidden cipher suites |
| SPKI pin sets | ✅ | Certificate pinning with SPKI hashes |
| Client authentication | ✅ | Client certificate requirements |
| Server authentication | ✅ | Server certificate requirements |
| DTLS profiles | ✅ | DTLS-specific settings |
MudParser includes several sample MUD profiles for testing:
| File | Device | Description |
|---|---|---|
data/amazon_echo.json | Amazon Echo | Full profile with many rules |
data/amazon_echo_short.json | Amazon Echo | Simplified version |
data/ring_doorbell.json | Ring Doorbell | Smart doorbell profile |
data/philips_hue_bulb.json | Philips Hue | Smart light bulb profile |
data/nest_smoke_sensor.json | Nest Protect | Smoke/CO detector profile |
# View Amazon Echo profile
mudparser info data/amazon_echo_short.json
# Validate Ring Doorbell
mudparser validate data/ring_doorbell.json
# Export Philips Hue to iptables
mudparser export data/philips_hue_bulb.json -f iptables -d 192.168.1.100
# Compare profiles
mudparser diff data/amazon_echo_short.json data/amazon_echo.json- UNSW IoT Analytics - 28 real device profiles
- NIST MUD-PD - MUD profile generator
- Community MUD Files - Crowd-sourced profiles
The main entry point for parsing MUD files.
classMUDParser:
# Class methods for parsing@classmethoddeffrom_file(cls, path: str|Path, source: str=None) ->MUDParser
@classmethoddeffrom_string(cls, content: str, source: str=None) ->MUDParser@classmethoddeffrom_url(cls, url: str, timeout: float=30.0) ->MUDParser
@classmethodasyncdeffrom_url_async(cls, url: str, timeout: float=30.0) ->MUDParser@classmethoddeffrom_dict(cls, data: dict, source: str=None) ->MUDParser# Properties
@propertydefmud(self) ->MUD# MUD container
@propertydefprofile(self) ->MUDProfile# Full profile
@propertydefexport(self) ->MUDExporter# Exporter interface# Methodsdefvalidate(self, strict: bool=False) ->list[str]
defget_summary(self) ->dict[str, Any]
defget_acl(self, name: str) ->AccessControlList|Nonedefget_from_device_acls(self) ->list[AccessControlList]
defget_to_device_acls(self) ->list[AccessControlList]
defget_dns_names(self) ->set[str]
defget_ports(self) ->dict[str, set[int]]
defget_all_entries(self) ->Iterator[tuple[str, AccessControlEntry]]
defto_dict(self) ->dictdefto_json(self, indent: int=None) ->strValidation engine with detailed results.
classMUDValidator:
defvalidate(
self,
profile: MUDProfile,
strict: bool=False
) ->ValidationResultclassValidationResult:
is_valid: boolissues: list[ValidationIssue]
error_count: intwarning_count: interrors: list[ValidationIssue] # Propertywarnings: list[ValidationIssue] # Propertydefto_dict(self) ->dictclassValidationIssue:
severity: ValidationSeveritymessage: strpath: str|NoneclassValidationSeverity(Enum):
ERROR="error"WARNING="warning"INFO="info"Export interface for generating firewall rules.
classMUDExporter:
defto_json(self, indent: int=None) ->strdefto_yaml(self) ->strdefto_iptables(
self,
device_ip: str,
chain_prefix: str="MUD",
include_comments: bool=True
) ->strdefto_nftables(
self,
device_ip: str,
table_name: str="mud_filter"
) ->strdefto_cisco_acl(
self,
acl_number_start: int=100,
include_remarks: bool=True
) ->strdefto_pfsense(
self,
device_ip: str,
interface: str="lan"
) ->strdefexport(
self,
format: str|ExportFormat,
**kwargs
) ->strdefget_summary(self) ->dict[str, Any]classMUD(BaseModel):
mud_version: intmud_url: HttpUrlmud_signature: HttpUrl|Nonelast_update: datetimecache_validity: int# 1-168 hoursis_supported: boolsysteminfo: str|Nonemfg_name: str|Nonemodel_name: str|Nonefirmware_rev: str|Nonesoftware_rev: str|Nonedocumentation: HttpUrl|Noneextensions: list[str]
from_device_policy: PolicyReferenceto_device_policy: PolicyReferenceclassAccessControlList(BaseModel):
name: stracl_type: ACLTypeaces: ACEs@propertydefentries(self) ->list[AccessControlEntry]
defis_ipv4(self) ->booldefis_ipv6(self) ->booldefis_ethernet(self) ->booldefget_accept_rules(self) ->list[AccessControlEntry]
defget_deny_rules(self) ->list[AccessControlEntry]classAccessControlEntry(BaseModel):
name: strmatches: ACEMatchesactions: ACEActionsdefis_accept(self) ->booldefis_deny(self) ->booldefget_description(self, direction: str="from") ->strclassMUDParserError(Exception):
"""Base exception for all MUD parser errors."""classMUDFileNotFoundError(MUDParserError):
"""Raised when a MUD file is not found."""file_path: strclassMUDSchemaError(MUDParserError):
"""Raised when JSON structure is invalid."""message: strclassMUDValidationError(MUDParserError):
"""Raised when validation fails."""message: strerrors: list[str]
classMUDNetworkError(MUDParserError):
"""Raised when network operations fail."""message: strurl: str|Nonestatus_code: int|Nonemudparser/
├── pyproject.toml # Modern Python packaging
├── README.md # This file
├── CHANGELOG.md # Version history
├── LICENSE # MIT License
│
├── src/mudparser/ # Main package (src layout)
│ ├── __init__.py # Package exports
│ ├── __main__.py # CLI entry point
│ ├── parser.py # MUDParser class
│ ├── validator.py # Validation engine
│ ├── cli.py # Typer CLI application
│ ├── exceptions.py # Custom exceptions
│ │
│ ├── models/ # Pydantic data models
│ │ ├── __init__.py
│ │ ├── mud.py # MUD container model
│ │ ├── acl.py # ACL models
│ │ ├── ace.py # ACE models
│ │ ├── matches.py # Match condition models
│ │ └── tls.py # RFC 9761 TLS models
│ │
│ └── exporters/ # Export implementations
│ ├── __init__.py
│ ├── base.py # MUDExporter class
│ ├── iptables.py # iptables generator
│ ├── nftables.py # nftables generator
│ ├── cisco.py # Cisco ACL generator
│ └── pfsense.py # pfSense XML generator
│
├── tests/ # Test suite
│ ├── conftest.py # Pytest fixtures
│ ├── test_parser.py
│ ├── test_models.py
│ ├── test_validator.py
│ └── test_exporter.py
│
├── docs/ # MkDocs documentation
│ ├── index.md
│ ├── installation.md
│ ├── quickstart.md
│ └── ...
│
├── examples/ # Example scripts
│ ├── basic_usage.py
│ └── export_to_firewall.py
│
├── demo/ # Streamlit demo app
│ └── streamlit_app.py
│
└── data/ # Sample MUD profiles
├── amazon_echo.json
├── ring_doorbell.json
└── ...
- Type Safety: Full type hints with Pydantic v2 for runtime validation
- RFC Compliance: Strict adherence to RFC 8520 and RFC 9761
- Extensibility: Easy to add new export formats
- Testability: High test coverage with comprehensive fixtures
- User Experience: Rich CLI with helpful error messages
| Package | Purpose |
|---|---|
pydantic>=2.0 | Data validation and models |
httpx>=0.25 | HTTP client for URL fetching |
pyyaml>=6.0 | YAML export |
typer>=0.9 | CLI framework |
rich>=13.0 | Terminal formatting |
| Package | Purpose |
|---|---|
pytest>=7.0 | Testing framework |
pytest-cov>=4.0 | Coverage reporting |
pytest-asyncio>=0.21 | Async test support |
mypy>=1.5 | Type checking |
ruff>=0.1 | Linting and formatting |
# Clone the repository
git clone https://github.com/elmiomar/mudparser.git
cd mudparser
# Create virtual environment
python -m venv venv
source venv/bin/activate # Linux/macOS# or
.\venv\Scripts\activate # Windows# Install in development mode with all extras
pip install -e ".[all]"# Run all tests
pytest
# Run with verbose output
pytest -v
# Run with coverage report
pytest --cov=mudparser --cov-report=html
# Run specific test file
pytest tests/test_parser.py
# Run specific test
pytest tests/test_parser.py::TestParserFromFile::test_parse_amazon_echo_short
# Run tests matching a pattern
pytest -k "validation"# Linting with ruff
ruff check src/
# Auto-fix linting issues
ruff check src/ --fix
# Format code
ruff format src/
# Type checking
mypy src/mudparser/# Install docs dependencies
pip install -e ".[docs]"# Serve documentation locally (with hot reload)
mkdocs serve
# Build static site
mkdocs build
# Deploy to GitHub Pages
mkdocs gh-deploy# Update version in pyproject.toml# Update CHANGELOG.md# Build package
python -m build
# Upload to PyPI
python -m twine upload dist/*Solution: Install the package:
pip install mudparser
# or for development
pip install -e .Solution: Check the file path:
frompathlibimportPath# Use absolute pathparser=MUDParser.from_file(Path("data/device.mud.json").absolute())
# Or verify file existspath=Path("device.mud.json")
ifnotpath.exists():
print(f"File not found: {path}")Solution: Ensure your MUD file has all required fields:
{
"ietf-mud:mud": {
"mud-version": 1,
"mud-url": "https://example.com/device.mud.json",
"last-update": "2024-01-15T00:00:00Z",
"cache-validity": 48,
"is-supported": true,
"from-device-policy": { ... },
"to-device-policy": { ... }
},
"ietf-access-control-list:access-lists": { ... }
}Solution: MUD URLs should use HTTPS:
{
"ietf-mud:mud": {
"mud-url": "https://example.com/device.mud.json"
}
}Note: MudParser exports DNS names as-is. For production use, resolve DNS names before applying rules:
importsocketdns_names=parser.get_dns_names()
fornameindns_names:
try:
ip=socket.gethostbyname(name)
print(f"{name} -> {ip}")
exceptsocket.gaierror:
print(f"Could not resolve: {name}")- Check the documentation
- Search existing issues
- Open a new issue
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Run tests (
pytest) - Run linting (
ruff check src/) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Follow the existing code style
- Add tests for new features
- Update documentation as needed
- Keep commits focused and atomic
- Write clear commit messages
This project is licensed under the MIT License - see the LICENSE file for details.
MIT License
Copyright (c) 2024 Omar Ilias EL MIMOUNI
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
- RFC 8520 - Manufacturer Usage Description Specification
- RFC 9761 - MUD for TLS and DTLS Profiles
- RFC 8519 - YANG Data Model for Network Access Control Lists
- IETF OPSAWG Working Group
- UNSW IoT Analytics - Sample MUD profiles
- NIST MUD-PD - MUD profile generator tool
- NIST MUD Implementation - Reference implementation
- Community MUD Files - Crowd-sourced profiles
- Omar Ilias EL MIMOUNI - Initial work - omarilias.elmimouni@nist.gov
- Documentation: https://elmimouni.net/mudparser
- GitHub Repository: https://github.com/elmiomar/mudparser
- Issue Tracker: https://github.com/elmiomar/mudparser/issues
- Changelog: CHANGELOG.md
- PyPI Package: https://pypi.org/project/mudparser/
Built with Python and Pydantic | RFC 8520 & RFC 9761 Compliant