Skip to content

Commit de6ee53

Browse files
authored
Merge pull request #261 from bytedance/policy-advisor-use-bpf-data
Refactor Policy Advisor to Use BPF Behavioral Data
2 parents 5fefbd7 + a43f9d1 commit de6ee53

File tree

5 files changed

+79
-82
lines changed

5 files changed

+79
-82
lines changed

tools/policy-advisor/README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
# Policy Advisor
22
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.
33

4-
You can provide the context information and the behavior model data of the target application to make the template more precise.
5-
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).
4+
You can provide the context information and the behavior data of the target application to make the template more precise.
5+
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).
66

77

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

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

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

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

1717

1818
## Usage
1919
```
2020
policy-advisor.py [-h] [-f FEATURES] [-c CAPABILITIES]
21-
[-m BEHAVIOR_MODEL] [-d]
21+
[-m BEHAVIOR_DATA] [-d]
2222
enforcers
2323
2424
positional arguments:
@@ -52,9 +52,9 @@ optional arguments:
5252
Available Values: CAPABILITIES(7) without 'CAP_' prefix (they should be combined with commas).
5353
For Example: "sys_admin,net_admin,sys_module"
5454
55-
-m BEHAVIOR_MODEL The behavior model data is an ArmorProfileModel object that is generated by vArmor.
56-
The input file must be in JSON format. You can export the data with kubectl command, such as:
57-
kubectl get ArmorProfileModel -n {NAMESPACE} {NAME} -o json > model.json
55+
-m BEHAVIOR_DATA The behavior data is a JSON file that includes an ArmorProfileModel object.
56+
You can export the behavior data with kubectl command, such as:
57+
`kubectl get ArmorProfileModel -n {NAMESPACE} {NAME} -o json > data.json`
5858
5959
-d Print debug information.
6060
```

tools/policy-advisor/policy-advisor.py

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,25 @@ def skip_the_rule_with_context(rule, enforcers, app_features, app_capabilities):
2626
return False
2727

2828

29-
def skip_the_rule_with_model_data(rule, enforcers, armor_profile_model):
29+
def skip_the_rule_with_behavior_data(rule, enforcers, behavior_data):
3030
if not has_common_item(enforcers, rule["enforcers"]):
3131
return True
3232

3333
if "conflicts" in rule:
3434
if "capabilities" in rule["conflicts"]:
35-
model_caps = retrieve_capabilities_from_model(armor_profile_model)
36-
return has_common_item(rule["conflicts"]["capabilities"], model_caps)
35+
caps = retrieve_capabilities_from_behavior_data(behavior_data)
36+
return has_common_item(rule["conflicts"]["capabilities"], caps)
3737

3838
if "syscalls" in rule["conflicts"]:
39-
syscalls = retrieve_syscalls_from_model(armor_profile_model)
39+
syscalls = retrieve_syscalls_from_behavior_data(behavior_data)
4040
return has_common_item(rule["conflicts"]["syscalls"], syscalls)
4141

4242
if "executions" in rule["conflicts"]:
43-
executions = retrieve_executions_from_model(armor_profile_model)
43+
executions = retrieve_executions_from_behavior_data(behavior_data)
4444
return has_common_item(rule["conflicts"]["executions"], executions)
4545

4646
if "files" in rule["conflicts"]:
47-
files = retrieve_files_from_model(armor_profile_model)
47+
files = retrieve_files_from_behavior_data(behavior_data)
4848
return files_conflict_with_rule(rule["conflicts"]["files"], files)
4949

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

6161

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

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

72-
# Filter out the rule with behavior model data
73-
if skip_the_rule_with_model_data(rule, enforcers, armor_profile_model):
72+
# Filter out the rule with behavior data
73+
if skip_the_rule_with_behavior_data(rule, enforcers, behavior_data):
7474
continue
7575

7676
set_enforcer(policy, enforcers)
@@ -85,8 +85,8 @@ def generate_policy_template(policy, built_in_rules, enforcers, app_features, ap
8585
if skip_the_rule_with_context(rule, enforcers, app_features, app_capabilities):
8686
continue
8787

88-
# Filter out the rule with behavior model data
89-
if skip_the_rule_with_model_data(rule, enforcers, armor_profile_model):
88+
# Filter out the rule with behavior data
89+
if skip_the_rule_with_behavior_data(rule, enforcers, behavior_data):
9090
continue
9191

9292
set_enforcer(policy, enforcers)
@@ -101,8 +101,8 @@ def generate_policy_template(policy, built_in_rules, enforcers, app_features, ap
101101
if skip_the_rule_with_context(rule, enforcers, app_features, app_capabilities):
102102
continue
103103

104-
# Filter out the rule with behavior model data
105-
if skip_the_rule_with_model_data(rule, enforcers, armor_profile_model):
104+
# Filter out the rule with behavior data
105+
if skip_the_rule_with_behavior_data(rule, enforcers, behavior_data):
106106
continue
107107

108108
set_enforcer(policy, enforcers)
@@ -115,8 +115,8 @@ def generate_policy_template(policy, built_in_rules, enforcers, app_features, ap
115115
if skip_the_rule_with_context(rule, enforcers, app_features, app_capabilities):
116116
continue
117117

118-
# Filter out the rule with behavior model data
119-
if skip_the_rule_with_model_data(rule, enforcers, armor_profile_model):
118+
# Filter out the rule with behavior data
119+
if skip_the_rule_with_behavior_data(rule, enforcers, behavior_data):
120120
continue
121121

122122
set_enforcer(policy, enforcers)
@@ -129,8 +129,8 @@ def generate_policy_template(policy, built_in_rules, enforcers, app_features, ap
129129
if skip_the_rule_with_context(rule, enforcers, app_features, app_capabilities):
130130
continue
131131

132-
# Filter out the rule with behavior model data
133-
if skip_the_rule_with_model_data(rule, enforcers, armor_profile_model):
132+
# Filter out the rule with behavior data
133+
if skip_the_rule_with_behavior_data(rule, enforcers, behavior_data):
134134
continue
135135

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

150-
# Filter out the rule with behavior model data
151-
if skip_the_rule_with_model_data(rule, enforcers, armor_profile_model):
150+
# Filter out the rule with behavior data
151+
if skip_the_rule_with_behavior_data(rule, enforcers, behavior_data):
152152
continue
153153

154154
set_enforcer(policy, enforcers)
@@ -161,21 +161,23 @@ def generate_policy_template(policy, built_in_rules, enforcers, app_features, ap
161161
if skip_the_rule_with_context(rule, enforcers, app_features, app_capabilities):
162162
continue
163163

164-
# Filter out the rule with behavior model data
165-
if skip_the_rule_with_model_data(rule, enforcers, armor_profile_model):
164+
# Filter out the rule with behavior data
165+
if skip_the_rule_with_behavior_data(rule, enforcers, behavior_data):
166166
continue
167167

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

172172

173-
def built_in_rules_advisor(built_in_rules, enforcers, app_features=[], app_capabilities=[], armor_profile_model={}, debug=False):
173+
def built_in_rules_advisor(built_in_rules, enforcers, app_features=[], app_capabilities=[], behavior_data={}, debug=False):
174174
policy = {
175175
"enforcer": "",
176176
"mode": "EnhanceProtect",
177177
"enhanceProtect": {
178178
"privileged": False,
179+
"auditViolations": True,
180+
"allowViolations": False,
179181
"hardeningRules": [],
180182
"attackProtectionRules": [
181183
{
@@ -187,7 +189,7 @@ def built_in_rules_advisor(built_in_rules, enforcers, app_features=[], app_capab
187189
}
188190
}
189191

190-
generate_policy_template(policy, built_in_rules, enforcers, app_features, app_capabilities, armor_profile_model, debug)
192+
generate_policy_template(policy, built_in_rules, enforcers, app_features, app_capabilities, behavior_data, debug)
191193

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

209211
if __name__ == "__main__":
210212
parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter,
211-
description='''This program can help users generate a `.spec.policy` template with built-in rules or the behavior model data.
213+
description='''This program can help users generate a `.spec.policy` template with built-in rules or the behavior data.
212214
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.
213215
214216
use cases:
215217
1). Generate a policy template that runs in EnhanceProtect mode with built-in rules supported by AppArmor and BPF enforcers.
216218
policy-advisor.py AppArmor,BPF -f share-containers-pid-ns -c sys_admin,net_admin,kill
217219
218-
2). Filter out the conflicted built-in rules with behavior model data to make the policy template more precise.
219-
policy-advisor.py AppArmor,BPF -f share-containers-pid-ns -c sys_admin,net_admin,kill -m model_data.json
220+
2). Filter out the conflicted built-in rules with behavior data to make the policy template more precise.
221+
policy-advisor.py AppArmor,BPF -f share-containers-pid-ns -c sys_admin,net_admin,kill -m data.json
220222
''')
221223

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

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

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

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

269-
if args.behavior_model and not os.path.exists(args.behavior_model):
270-
print("[!] The model file isn't exist.")
271+
if args.behavior_data and not os.path.exists(args.behavior_data):
272+
print("[!] The behavior data file isn't exist.")
271273
sys.exit(1)
272274

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

276-
if args.behavior_model:
277-
with open(args.behavior_model, "r") as model_f:
278-
armor_profile_model = json.load(model_f)
279-
built_in_rules_advisor(built_in_rules, enforcers, features, capabilities, armor_profile_model, args.debug)
278+
if args.behavior_data:
279+
with open(args.behavior_data, "r") as model_f:
280+
behavior_data = json.load(model_f)
281+
built_in_rules_advisor(built_in_rules, enforcers, features, capabilities, behavior_data, args.debug)
280282
else:
281283
built_in_rules_advisor(built_in_rules, enforcers, features, capabilities, {}, args.debug)

tools/policy-advisor/test_utils.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ def test_files_conflict_with_rule(self):
77
"disable-write-etc": {
88
"rules": [
99
{
10-
"path_regex": "^\/etc$|^\/etc\/|\/containerd\/.*\/fs\/root\/etc$|\/containerd\/.*\/fs\/root\/etc\/",
10+
"path_regex": r"^\/etc$|^\/etc\/|\/containerd\/.*\/fs\/root\/etc$|\/containerd\/.*\/fs\/root\/etc\/",
1111
"permissions": [
1212
"w",
1313
"a"
@@ -89,7 +89,7 @@ def test_files_conflict_with_rule(self):
8989
"mitigate-sa-leak": {
9090
"rules": [
9191
{
92-
"path_regex": "\/run\/secrets\/kubernetes.io\/serviceaccount\/",
92+
"path_regex": r"\/run\/secrets\/kubernetes.io\/serviceaccount\/",
9393
"permissions": [
9494
"r"
9595
]
@@ -140,7 +140,7 @@ def test_files_conflict_with_rule(self):
140140
"mitigate-disk-device-number-leak": {
141141
"rules": [
142142
{
143-
"path_regex": "\/proc\/partitions|\/proc\/.*\/mountinfo",
143+
"path_regex": r"\/proc\/partitions|\/proc\/.*\/mountinfo",
144144
"permissions": [
145145
"r"
146146
]
@@ -238,7 +238,7 @@ def test_files_conflict_with_rule(self):
238238
"mitigate-overlayfs-leak": {
239239
"rules": [
240240
{
241-
"path_regex": "\/proc\/mounts|\/proc\/.*\/mounts|\/proc\/.*\/mountinfo",
241+
"path_regex": r"\/proc\/mounts|\/proc\/.*\/mounts|\/proc\/.*\/mountinfo",
242242
"permissions": [
243243
"r"
244244
]
@@ -289,7 +289,7 @@ def test_files_conflict_with_rule(self):
289289
"mitigate-host-ip-leak": {
290290
"rules": [
291291
{
292-
"path_regex": "\/proc\/net\/arp|\/proc\/.*\/net\/arp",
292+
"path_regex": r"\/proc\/net\/arp|\/proc\/.*\/net\/arp",
293293
"permissions": [
294294
"r"
295295
]
@@ -379,7 +379,7 @@ def test_files_conflict_with_rule(self):
379379
"cgroups-lxcfs-escape-mitigation": {
380380
"rules": [
381381
{
382-
"path_regex": "\/release_agent$|\/devices\/devices.allow$|\/devices\/.*\/devices.allow$|\/devices\/cgroup.procs$|\/devices\/.*\/cgroup.procs$|\/devices\/tasks$|\/devices\/.*\/tasks$",
382+
"path_regex": r"\/release_agent$|\/devices\/devices.allow$|\/devices\/.*\/devices.allow$|\/devices\/cgroup.procs$|\/devices\/.*\/cgroup.procs$|\/devices\/tasks$|\/devices\/.*\/tasks$",
383383
"permissions": [
384384
"w",
385385
"a"

tools/policy-advisor/utils.py

Lines changed: 13 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -28,45 +28,28 @@ def files_conflict_with_rule(file_rules, files):
2828
return False
2929

3030

31-
def retrieve_capabilities_from_model(armor_profile_model):
31+
def retrieve_capabilities_from_behavior_data(behavior_data):
3232
caps = []
33-
if "data" in armor_profile_model and \
34-
"dynamicResult" in armor_profile_model["data"] and \
35-
"appArmor" in armor_profile_model["data"]["dynamicResult"] and \
36-
"capabilities" in armor_profile_model["data"]["dynamicResult"]["appArmor"]:
37-
caps.extend(armor_profile_model["data"]["dynamicResult"]["appArmor"]["capabilities"])
33+
caps.extend(behavior_data.get("data", {}).get("dynamicResult", {}).get("appArmor", {}).get("capabilities", []))
34+
caps.extend(behavior_data.get("data", {}).get("dynamicResult", {}).get("bpf", {}).get("syscalls", []))
3835
return caps
3936

4037

41-
def retrieve_syscalls_from_model(armor_profile_model):
42-
if "data" in armor_profile_model and \
43-
"dynamicResult" in armor_profile_model["data"] and \
44-
"seccomp" in armor_profile_model["data"]["dynamicResult"] and \
45-
"syscalls" in armor_profile_model["data"]["dynamicResult"]["seccomp"]:
46-
return armor_profile_model["data"]["dynamicResult"]["seccomp"]["syscalls"]
47-
return []
38+
def retrieve_syscalls_from_behavior_data(behavior_data):
39+
return behavior_data.get("data", {}).get("dynamicResult", {}).get("seccomp", {}).get("syscalls", [])
4840

4941

50-
def retrieve_executions_from_model(armor_profile_model):
42+
def retrieve_executions_from_behavior_data(behavior_data):
5143
executions = []
52-
if "data" in armor_profile_model and \
53-
"dynamicResult" in armor_profile_model["data"] and \
54-
"appArmor" in armor_profile_model["data"]["dynamicResult"] and \
55-
"executions" in armor_profile_model["data"]["dynamicResult"]["appArmor"]:
56-
57-
for execution in armor_profile_model["data"]["dynamicResult"]["appArmor"]["executions"]:
58-
executions.append(os.path.basename(execution))
59-
44+
for execution in behavior_data.get("data", {}).get("dynamicResult", {}).get("appArmor", {}).get("executions", []):
45+
executions.append(os.path.basename(execution))
46+
for execution in behavior_data.get("data", {}).get("dynamicResult", {}).get("bpf", {}).get("executions", []):
47+
executions.append(os.path.basename(execution))
6048
return executions
6149

6250

63-
def retrieve_files_from_model(armor_profile_model):
51+
def retrieve_files_from_behavior_data(behavior_data):
6452
files = []
65-
if "data" in armor_profile_model and \
66-
"dynamicResult" in armor_profile_model["data"] and \
67-
"appArmor" in armor_profile_model["data"]["dynamicResult"] and \
68-
"files" in armor_profile_model["data"]["dynamicResult"]["appArmor"]:
69-
70-
files.extend(armor_profile_model["data"]["dynamicResult"]["appArmor"]["files"])
71-
53+
files.extend(behavior_data.get("data", {}).get("dynamicResult", {}).get("appArmor", {}).get("files", []))
54+
files.extend(behavior_data.get("data", {}).get("dynamicResult", {}).get("bpf", {}).get("files", []))
7255
return files

0 commit comments

Comments
 (0)