OPA/WASM Rules
OPA (Open Policy Agent) rules use the Rego language compiled to WebAssembly for complex policy logic that goes beyond simple pattern matching.
Structure
OPA rules require two files:
- Rego policy (
.rego
) - Rule logic - Metadata (
.wasm.json
) - Rule information
Real example
Rego policy (docker-security.rego
)
rego
package audit.docker
# Detect containers running as root
deny[msg] if {
input.file_type == "dockerfile"
node := input.nodes[_]
node.path == "USER"
contains(node.value, "root")
msg := "Container runs as root user - use a non-root user for security"
}
# Detect use of 'latest' tag in FROM
deny[msg] if {
input.file_type == "dockerfile"
node := input.nodes[_]
node.path == "FROM"
contains(node.value, ":latest")
msg := "Using 'latest' tag is not recommended - specify exact version"
}
# Detect use of ADD instead of COPY
deny[msg] if {
input.file_type == "dockerfile"
node := input.nodes[_]
node.path == "ADD"
msg := "Use COPY instead of ADD for local files"
}
# Detect privileged containers
deny[msg] if {
input.file_type == "dockerfile"
node := input.nodes[_]
node.path == "RUN"
contains(node.value, "sudo")
msg := "Avoid using sudo in containers - not needed and security risk"
}
Metadata (docker-security.wasm.json
)
json
{
"id": "opa.docker.security",
"severity": "HIGH",
"category": "docker",
"message": "Docker security violations detected",
"entrypoint": "data.audit.docker.deny",
"remediation": "Review Docker security best practices",
"description": "Detects common Docker security issues: root user, latest tags, ADD usage, and sudo in containers"
}
Creation process
1. Write the Rego policy
rego
package audit.security
deny[msg] if {
input.file_type == "dockerfile"
node := input.nodes[_]
node.path == "USER"
contains(node.value, "root")
msg := "Container runs as root user"
}
2. Compile to WebAssembly
bash
opa build -t wasm -e data.audit.security.deny policy.rego
3. Create metadata
json
{
"id": "opa.docker.no-root",
"severity": "HIGH",
"category": "docker",
"message": "Container runs as root user",
"entrypoint": "data.audit.security.deny"
}
Rego syntax
Basic structure
rego
package audit.category
deny[msg] if {
# Conditions
condition1
condition2
msg := "Error message"
}
Data access
rego
# Access input properties
input.file_type == "dockerfile"
input.nodes[_].path == "USER"
input.nodes[_].value
# Iterate arrays
node := input.nodes[_]
Useful functions
rego
# Contains text
contains(node.value, "root")
# Starts with
startswith(node.value, "FROM")
# Ends with
endswith(node.value, ":latest")
Metadata fields
Field | Description |
---|---|
id | Unique identifier of the rule |
severity | Level: CRITICAL, HIGH, MEDIUM, LOW |
category | Language/technology category |
message | Main rule message |
entrypoint | Policy entry point (data.package.rule) |
remediation | Suggested remediation |
description | Detailed description |
Use cases
Docker validation
rego
package audit.docker
deny[msg] if {
input.file_type == "dockerfile"
node := input.nodes[_]
node.path == "FROM"
contains(node.value, ":latest")
msg := "Using 'latest' tag is not recommended"
}
Configuration validation
rego
package audit.config
deny[msg] if {
input.file_type == "yaml"
node := input.nodes[_]
node.path == "database.password"
node.value == "admin"
msg := "Default password detected"
}
Complex validation
rego
package audit.complex
deny[msg] if {
input.file_type == "dockerfile"
# Ensure it does not use root
user_node := input.nodes[_]
user_node.path == "USER"
contains(user_node.value, "root")
# And that it does not use latest
from_node := input.nodes[_]
from_node.path == "FROM"
contains(from_node.value, ":latest")
msg := "Container has multiple security issues"
}
Advantages
✅ Perfect for:
- Complex policy logic
- Validation of multiple conditions
- Relationship analysis
- Reusable policies
- Advanced conditional logic
❌ Limitations:
- Steep learning curve
- Slower performance
- Requires compilation
- More complex to maintain
Differences from other types
Aspect | YAML | Semgrep | OPA |
---|---|---|---|
Complexity | Low | Medium | High |
Logic | No | Limited | Full |
Performance | High | Medium | Low |
Flexibility | Low | High | Very High |
Maintenance | Easy | Medium | Hard |
When to use OPA
✅ Use OPA when:
- You need complex logic
- There are multiple conditions
- You want reusable policies
- You need relationship analysis
- You have specific business rules
❌ Do not use OPA when:
- You only need simple matching
- You want maximum performance
- You have basic patterns
- You are not familiar with Rego
Next steps
- YAML Rules - For simple patterns
- JSON Rules - For configurations
- Semgrep Rules - For complex patterns