Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions tools/policy-advisor/README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
# Policy Advisor
This program can help you generate a [`.spec.policy`](../../docs/getting_started/interface_specification.md#spec) template in the EnhanceProtect mode with built-in rules. The template can be a good start to craft the final policy.

You can provide the context information and the behavior model data of the target application to make the template more precise.
Please use the `-f FEATURES` and `-c CAPABILITIES` arguments to specify the context information. The `-f FEATURES` argument used to describe the application features. The `-c CAPABILITIES` argument used to describe the capabilities required by application explicitly. The behavior model data file is passed by the `-m BEHAVIOR_MODEL` argument. It's an ArmorProfileModel object that is generated with [the BehaviorModeling Mode](../../docs/guides/policies_and_rules/policy_modes/behavior_modeling.md).
You can provide the context information and the behavior data of the target application to make the template more precise.
Please use the `-f FEATURES` and `-c CAPABILITIES` arguments to specify the context information. The `-f FEATURES` argument used to describe the application features. The `-c CAPABILITIES` argument used to describe the capabilities required by application explicitly. The behavior data file is passed by the `-m BEHAVIOR_DATA` argument. It's an ArmorProfileModel object that is generated with [the BehaviorModeling Mode](../../docs/guides/policies_and_rules/policy_modes/behavior_modeling.md).


## Use cases
Generate a policy template that runs in EnhanceProtect mode with built-in rules supported by AppArmor and BPF enforcers.

`policy-advisor.py AppArmor,BPF -f share-containers-pid-ns -c sys_admin,net_admin,kill`

Filter out the conflicted built-in rules with behavior model data to make the policy template more precise.
Filter out the conflicted built-in rules with behavior data to make the policy template more precise.

`policy-advisor.py AppArmor,BPF -f share-containers-pid-ns -c sys_admin,net_admin,kill -m model_data.json`
`policy-advisor.py AppArmor,BPF -f share-containers-pid-ns -c sys_admin,net_admin,kill -m data.json`


## Usage
```
policy-advisor.py [-h] [-f FEATURES] [-c CAPABILITIES]
[-m BEHAVIOR_MODEL] [-d]
[-m BEHAVIOR_DATA] [-d]
enforcers

positional arguments:
Expand Down Expand Up @@ -52,9 +52,9 @@ optional arguments:
Available Values: CAPABILITIES(7) without 'CAP_' prefix (they should be combined with commas).
For Example: "sys_admin,net_admin,sys_module"

-m BEHAVIOR_MODEL The behavior model data is an ArmorProfileModel object that is generated by vArmor.
The input file must be in JSON format. You can export the data with kubectl command, such as:
kubectl get ArmorProfileModel -n {NAMESPACE} {NAME} -o json > model.json
-m BEHAVIOR_DATA The behavior data is a JSON file that includes an ArmorProfileModel object.
You can export the behavior data with kubectl command, such as:
`kubectl get ArmorProfileModel -n {NAMESPACE} {NAME} -o json > data.json`

-d Print debug information.
```
78 changes: 40 additions & 38 deletions tools/policy-advisor/policy-advisor.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,25 @@ def skip_the_rule_with_context(rule, enforcers, app_features, app_capabilities):
return False


def skip_the_rule_with_model_data(rule, enforcers, armor_profile_model):
def skip_the_rule_with_behavior_data(rule, enforcers, behavior_data):
if not has_common_item(enforcers, rule["enforcers"]):
return True

if "conflicts" in rule:
if "capabilities" in rule["conflicts"]:
model_caps = retrieve_capabilities_from_model(armor_profile_model)
return has_common_item(rule["conflicts"]["capabilities"], model_caps)
caps = retrieve_capabilities_from_behavior_data(behavior_data)
return has_common_item(rule["conflicts"]["capabilities"], caps)

if "syscalls" in rule["conflicts"]:
syscalls = retrieve_syscalls_from_model(armor_profile_model)
syscalls = retrieve_syscalls_from_behavior_data(behavior_data)
return has_common_item(rule["conflicts"]["syscalls"], syscalls)

if "executions" in rule["conflicts"]:
executions = retrieve_executions_from_model(armor_profile_model)
executions = retrieve_executions_from_behavior_data(behavior_data)
return has_common_item(rule["conflicts"]["executions"], executions)

if "files" in rule["conflicts"]:
files = retrieve_files_from_model(armor_profile_model)
files = retrieve_files_from_behavior_data(behavior_data)
return files_conflict_with_rule(rule["conflicts"]["files"], files)

return False
Expand All @@ -59,7 +59,7 @@ def set_enforcer(policy, enforcers):
policy["enforcer"] += "Seccomp"


def generate_policy_template(policy, built_in_rules, enforcers, app_features, app_capabilities, armor_profile_model, debug):
def generate_policy_template(policy, built_in_rules, enforcers, app_features, app_capabilities, behavior_data, debug):
if "privileged-container" in app_features:
policy["enhanceProtect"]["privileged"] = True

Expand All @@ -69,8 +69,8 @@ def generate_policy_template(policy, built_in_rules, enforcers, app_features, ap
if skip_the_rule_with_context(rule, enforcers, app_features, app_capabilities):
continue

# Filter out the rule with behavior model data
if skip_the_rule_with_model_data(rule, enforcers, armor_profile_model):
# Filter out the rule with behavior data
if skip_the_rule_with_behavior_data(rule, enforcers, behavior_data):
continue

set_enforcer(policy, enforcers)
Expand All @@ -85,8 +85,8 @@ def generate_policy_template(policy, built_in_rules, enforcers, app_features, ap
if skip_the_rule_with_context(rule, enforcers, app_features, app_capabilities):
continue

# Filter out the rule with behavior model data
if skip_the_rule_with_model_data(rule, enforcers, armor_profile_model):
# Filter out the rule with behavior data
if skip_the_rule_with_behavior_data(rule, enforcers, behavior_data):
continue

set_enforcer(policy, enforcers)
Expand All @@ -101,8 +101,8 @@ def generate_policy_template(policy, built_in_rules, enforcers, app_features, ap
if skip_the_rule_with_context(rule, enforcers, app_features, app_capabilities):
continue

# Filter out the rule with behavior model data
if skip_the_rule_with_model_data(rule, enforcers, armor_profile_model):
# Filter out the rule with behavior data
if skip_the_rule_with_behavior_data(rule, enforcers, behavior_data):
continue

set_enforcer(policy, enforcers)
Expand All @@ -115,8 +115,8 @@ def generate_policy_template(policy, built_in_rules, enforcers, app_features, ap
if skip_the_rule_with_context(rule, enforcers, app_features, app_capabilities):
continue

# Filter out the rule with behavior model data
if skip_the_rule_with_model_data(rule, enforcers, armor_profile_model):
# Filter out the rule with behavior data
if skip_the_rule_with_behavior_data(rule, enforcers, behavior_data):
continue

set_enforcer(policy, enforcers)
Expand All @@ -129,8 +129,8 @@ def generate_policy_template(policy, built_in_rules, enforcers, app_features, ap
if skip_the_rule_with_context(rule, enforcers, app_features, app_capabilities):
continue

# Filter out the rule with behavior model data
if skip_the_rule_with_model_data(rule, enforcers, armor_profile_model):
# Filter out the rule with behavior data
if skip_the_rule_with_behavior_data(rule, enforcers, behavior_data):
continue

set_enforcer(policy, enforcers)
Expand All @@ -140,15 +140,15 @@ def generate_policy_template(policy, built_in_rules, enforcers, app_features, ap
# ========= Attack Protection - Disable Sensitive Operations =========
# Note:
# We use the built-in rules of the sensitive operation category
# only if the behavior model is provided.
if armor_profile_model:
# only if the behavior data is provided.
if behavior_data:
for rule in built_in_rules["sensitive_operations"]:
# Filter out the rule with context
if skip_the_rule_with_context(rule, enforcers, app_features, app_capabilities):
continue

# Filter out the rule with behavior model data
if skip_the_rule_with_model_data(rule, enforcers, armor_profile_model):
# Filter out the rule with behavior data
if skip_the_rule_with_behavior_data(rule, enforcers, behavior_data):
continue

set_enforcer(policy, enforcers)
Expand All @@ -161,21 +161,23 @@ def generate_policy_template(policy, built_in_rules, enforcers, app_features, ap
if skip_the_rule_with_context(rule, enforcers, app_features, app_capabilities):
continue

# Filter out the rule with behavior model data
if skip_the_rule_with_model_data(rule, enforcers, armor_profile_model):
# Filter out the rule with behavior data
if skip_the_rule_with_behavior_data(rule, enforcers, behavior_data):
continue

set_enforcer(policy, enforcers)
policy["enhanceProtect"]["vulMitigationRules"].append(rule["id"])
debug_print(rule, debug)


def built_in_rules_advisor(built_in_rules, enforcers, app_features=[], app_capabilities=[], armor_profile_model={}, debug=False):
def built_in_rules_advisor(built_in_rules, enforcers, app_features=[], app_capabilities=[], behavior_data={}, debug=False):
policy = {
"enforcer": "",
"mode": "EnhanceProtect",
"enhanceProtect": {
"privileged": False,
"auditViolations": True,
"allowViolations": False,
"hardeningRules": [],
"attackProtectionRules": [
{
Expand All @@ -187,7 +189,7 @@ def built_in_rules_advisor(built_in_rules, enforcers, app_features=[], app_capab
}
}

generate_policy_template(policy, built_in_rules, enforcers, app_features, app_capabilities, armor_profile_model, debug)
generate_policy_template(policy, built_in_rules, enforcers, app_features, app_capabilities, behavior_data, debug)

print('''
Please take note of the following tips about the template:
Expand All @@ -208,15 +210,15 @@ def built_in_rules_advisor(built_in_rules, enforcers, app_features=[], app_capab

if __name__ == "__main__":
parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter,
description='''This program can help users generate a `.spec.policy` template with built-in rules or the behavior model data.
description='''This program can help users generate a `.spec.policy` template with built-in rules or the behavior data.
The template can be a good start to craft the final policy. Please use the -f and -c command-line arguments to specify the context.

use cases:
1). Generate a policy template that runs in EnhanceProtect mode with built-in rules supported by AppArmor and BPF enforcers.
policy-advisor.py AppArmor,BPF -f share-containers-pid-ns -c sys_admin,net_admin,kill

2). Filter out the conflicted built-in rules with behavior model data to make the policy template more precise.
policy-advisor.py AppArmor,BPF -f share-containers-pid-ns -c sys_admin,net_admin,kill -m model_data.json
2). Filter out the conflicted built-in rules with behavior data to make the policy template more precise.
policy-advisor.py AppArmor,BPF -f share-containers-pid-ns -c sys_admin,net_admin,kill -m data.json
''')

parser.add_argument("enforcers", type=str,
Expand Down Expand Up @@ -250,10 +252,10 @@ def built_in_rules_advisor(built_in_rules, enforcers, app_features=[], app_capab
Available Values: CAPABILITIES(7) without 'CAP_' prefix (they should be combined with commas).
For Example: "sys_admin,net_admin,sys_module"\n\n''')

parser.add_argument("-m", dest="behavior_model", type=str, default="",
help='''The behavior model data is an ArmorProfileModel object that is generated by vArmor.
The input file must be in JSON format. You can export the data with kubectl command, such as:
kubectl get ArmorProfileModel -n {NAMESPACE} {NAME} -o json > model.json\n\n''')
parser.add_argument("-m", dest="behavior_data", type=str, default="",
help='''The behavior data is a JSON file that includes an ArmorProfileModel object.
You can export the behavior data with kubectl command, such as:
kubectl get ArmorProfileModel -n {NAMESPACE} {NAME} -o json > data.json\n\n''')

parser.add_argument("-d", dest="debug", action="store_true", default=False, help="Print debug information.")

Expand All @@ -266,16 +268,16 @@ def built_in_rules_advisor(built_in_rules, enforcers, app_features=[], app_capab
if len(capabilities) == 1 and '' in capabilities:
capabilities = []

if args.behavior_model and not os.path.exists(args.behavior_model):
print("[!] The model file isn't exist.")
if args.behavior_data and not os.path.exists(args.behavior_data):
print("[!] The behavior data file isn't exist.")
sys.exit(1)

with open(os.path.join(current_dir, "./built-in-rules.json"), "r") as f:
built_in_rules = json.load(f)

if args.behavior_model:
with open(args.behavior_model, "r") as model_f:
armor_profile_model = json.load(model_f)
built_in_rules_advisor(built_in_rules, enforcers, features, capabilities, armor_profile_model, args.debug)
if args.behavior_data:
with open(args.behavior_data, "r") as model_f:
behavior_data = json.load(model_f)
built_in_rules_advisor(built_in_rules, enforcers, features, capabilities, behavior_data, args.debug)
else:
built_in_rules_advisor(built_in_rules, enforcers, features, capabilities, {}, args.debug)
12 changes: 6 additions & 6 deletions tools/policy-advisor/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ def test_files_conflict_with_rule(self):
"disable-write-etc": {
"rules": [
{
"path_regex": "^\/etc$|^\/etc\/|\/containerd\/.*\/fs\/root\/etc$|\/containerd\/.*\/fs\/root\/etc\/",
"path_regex": r"^\/etc$|^\/etc\/|\/containerd\/.*\/fs\/root\/etc$|\/containerd\/.*\/fs\/root\/etc\/",
"permissions": [
"w",
"a"
Expand Down Expand Up @@ -89,7 +89,7 @@ def test_files_conflict_with_rule(self):
"mitigate-sa-leak": {
"rules": [
{
"path_regex": "\/run\/secrets\/kubernetes.io\/serviceaccount\/",
"path_regex": r"\/run\/secrets\/kubernetes.io\/serviceaccount\/",
"permissions": [
"r"
]
Expand Down Expand Up @@ -140,7 +140,7 @@ def test_files_conflict_with_rule(self):
"mitigate-disk-device-number-leak": {
"rules": [
{
"path_regex": "\/proc\/partitions|\/proc\/.*\/mountinfo",
"path_regex": r"\/proc\/partitions|\/proc\/.*\/mountinfo",
"permissions": [
"r"
]
Expand Down Expand Up @@ -238,7 +238,7 @@ def test_files_conflict_with_rule(self):
"mitigate-overlayfs-leak": {
"rules": [
{
"path_regex": "\/proc\/mounts|\/proc\/.*\/mounts|\/proc\/.*\/mountinfo",
"path_regex": r"\/proc\/mounts|\/proc\/.*\/mounts|\/proc\/.*\/mountinfo",
"permissions": [
"r"
]
Expand Down Expand Up @@ -289,7 +289,7 @@ def test_files_conflict_with_rule(self):
"mitigate-host-ip-leak": {
"rules": [
{
"path_regex": "\/proc\/net\/arp|\/proc\/.*\/net\/arp",
"path_regex": r"\/proc\/net\/arp|\/proc\/.*\/net\/arp",
"permissions": [
"r"
]
Expand Down Expand Up @@ -379,7 +379,7 @@ def test_files_conflict_with_rule(self):
"cgroups-lxcfs-escape-mitigation": {
"rules": [
{
"path_regex": "\/release_agent$|\/devices\/devices.allow$|\/devices\/.*\/devices.allow$|\/devices\/cgroup.procs$|\/devices\/.*\/cgroup.procs$|\/devices\/tasks$|\/devices\/.*\/tasks$",
"path_regex": r"\/release_agent$|\/devices\/devices.allow$|\/devices\/.*\/devices.allow$|\/devices\/cgroup.procs$|\/devices\/.*\/cgroup.procs$|\/devices\/tasks$|\/devices\/.*\/tasks$",
"permissions": [
"w",
"a"
Expand Down
43 changes: 13 additions & 30 deletions tools/policy-advisor/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,45 +28,28 @@ def files_conflict_with_rule(file_rules, files):
return False


def retrieve_capabilities_from_model(armor_profile_model):
def retrieve_capabilities_from_behavior_data(behavior_data):
caps = []
if "data" in armor_profile_model and \
"dynamicResult" in armor_profile_model["data"] and \
"appArmor" in armor_profile_model["data"]["dynamicResult"] and \
"capabilities" in armor_profile_model["data"]["dynamicResult"]["appArmor"]:
caps.extend(armor_profile_model["data"]["dynamicResult"]["appArmor"]["capabilities"])
caps.extend(behavior_data.get("data", {}).get("dynamicResult", {}).get("appArmor", {}).get("capabilities", []))
caps.extend(behavior_data.get("data", {}).get("dynamicResult", {}).get("bpf", {}).get("syscalls", []))
return caps


def retrieve_syscalls_from_model(armor_profile_model):
if "data" in armor_profile_model and \
"dynamicResult" in armor_profile_model["data"] and \
"seccomp" in armor_profile_model["data"]["dynamicResult"] and \
"syscalls" in armor_profile_model["data"]["dynamicResult"]["seccomp"]:
return armor_profile_model["data"]["dynamicResult"]["seccomp"]["syscalls"]
return []
def retrieve_syscalls_from_behavior_data(behavior_data):
return behavior_data.get("data", {}).get("dynamicResult", {}).get("seccomp", {}).get("syscalls", [])


def retrieve_executions_from_model(armor_profile_model):
def retrieve_executions_from_behavior_data(behavior_data):
executions = []
if "data" in armor_profile_model and \
"dynamicResult" in armor_profile_model["data"] and \
"appArmor" in armor_profile_model["data"]["dynamicResult"] and \
"executions" in armor_profile_model["data"]["dynamicResult"]["appArmor"]:

for execution in armor_profile_model["data"]["dynamicResult"]["appArmor"]["executions"]:
executions.append(os.path.basename(execution))

for execution in behavior_data.get("data", {}).get("dynamicResult", {}).get("appArmor", {}).get("executions", []):
executions.append(os.path.basename(execution))
for execution in behavior_data.get("data", {}).get("dynamicResult", {}).get("bpf", {}).get("executions", []):
executions.append(os.path.basename(execution))
return executions


def retrieve_files_from_model(armor_profile_model):
def retrieve_files_from_behavior_data(behavior_data):
files = []
if "data" in armor_profile_model and \
"dynamicResult" in armor_profile_model["data"] and \
"appArmor" in armor_profile_model["data"]["dynamicResult"] and \
"files" in armor_profile_model["data"]["dynamicResult"]["appArmor"]:

files.extend(armor_profile_model["data"]["dynamicResult"]["appArmor"]["files"])

files.extend(behavior_data.get("data", {}).get("dynamicResult", {}).get("appArmor", {}).get("files", []))
files.extend(behavior_data.get("data", {}).get("dynamicResult", {}).get("bpf", {}).get("files", []))
return files
Loading
Loading