diff options
author | Fred-Barclay <Fred-Barclay@users.noreply.github.com> | 2018-08-24 12:09:16 -0500 |
---|---|---|
committer | Fred-Barclay <Fred-Barclay@users.noreply.github.com> | 2018-08-24 12:09:16 -0500 |
commit | ce1fcbab3d20a2eb39ce2b6b70537b3f05de6415 (patch) | |
tree | 4cca5f201efccb97662e7c63edc9467b339801af /contrib | |
parent | Fixup obs.profile (no python) (diff) | |
download | firejail-ce1fcbab3d20a2eb39ce2b6b70537b3f05de6415.tar.gz firejail-ce1fcbab3d20a2eb39ce2b6b70537b3f05de6415.tar.zst firejail-ce1fcbab3d20a2eb39ce2b6b70537b3f05de6415.zip |
Add python program to more easily debug profiles
Should help with issues like #1946 where the user needs to comment out
all profile lines and then re-enable them individually to test
Diffstat (limited to 'contrib')
-rwxr-xr-x | contrib/jail_prober.py | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/contrib/jail_prober.py b/contrib/jail_prober.py new file mode 100755 index 000000000..a5f279cb4 --- /dev/null +++ b/contrib/jail_prober.py | |||
@@ -0,0 +1,163 @@ | |||
1 | #!/usr/bin/env python3 | ||
2 | """ | ||
3 | Figure out which profile options may be causing a particular program to break | ||
4 | when run in firejail. | ||
5 | |||
6 | Instead of having to comment out each line in a profile by hand, and then | ||
7 | enable each line individually until the bad line or lines are found, this | ||
8 | largely automates the process. Users only have to provide the path to the | ||
9 | profile, program name, and answer 'y' for yes or 'n' for no when prompted. | ||
10 | |||
11 | After completion, you'll be provided with some information to copy and then | ||
12 | paste into a GitHub issue in the Firejail project repository: | ||
13 | https://github.com/netblue30/firejail/issues | ||
14 | |||
15 | Paths to the profile should be absolute. If the program is in your path, then | ||
16 | you only have to type the profile name. Else, you'll need to provide the | ||
17 | absolute path to the profile. | ||
18 | |||
19 | Examples: | ||
20 | python jail_prober.py /etc/firejail/spotify.profile spotify | ||
21 | python jail_prober.py /usr/local/etc/firejail/firefox.profile /usr/bin/firefox | ||
22 | """ | ||
23 | |||
24 | import sys | ||
25 | import os | ||
26 | import subprocess | ||
27 | |||
28 | |||
29 | def check_params(profilePath): | ||
30 | """ | ||
31 | Ensure the path to the profile is valid and that an actual profile has been | ||
32 | passed (as opposed to a config or .local file). | ||
33 | |||
34 | :params profilePath: The absolute path to the problematic profile. | ||
35 | """ | ||
36 | if not os.path.isfile(profilePath): | ||
37 | raise FileNotFoundError( | ||
38 | 'The path %s is not a valid system path.' % profilePath) | ||
39 | if not profilePath.endswith('.profile'): | ||
40 | raise ValueError('%s is not a valid Firejail profile.' % profilePath) | ||
41 | |||
42 | |||
43 | def get_args(profilePath): | ||
44 | """ | ||
45 | Read the profile, stripping out comments and newlines | ||
46 | |||
47 | :params profilePath: The absolute path to the problematic profile. | ||
48 | |||
49 | :returns profile: A list containing all active profile arguments | ||
50 | """ | ||
51 | with open(profilePath, 'r') as f: | ||
52 | profile = f.readlines() | ||
53 | profile = [ | ||
54 | arg.strip() for arg in profile | ||
55 | if not arg.startswith('#') and arg.strip() != '' | ||
56 | ] | ||
57 | |||
58 | return profile | ||
59 | |||
60 | |||
61 | def arg_converter(argList, style): | ||
62 | """ | ||
63 | Convert between firejail command-line arguments (--example=something) and | ||
64 | profile arguments (example something) | ||
65 | |||
66 | :params argList: A list of firejail arguments | ||
67 | |||
68 | :params style: Whether to convert arguments to command-line form or profile | ||
69 | form | ||
70 | """ | ||
71 | if style == 'to_profile': | ||
72 | oldSep = '=' | ||
73 | newSep = ' ' | ||
74 | prefix = '' | ||
75 | elif style == 'to_commandline': | ||
76 | oldSep = ' ' | ||
77 | newSep = '=' | ||
78 | prefix = '--' | ||
79 | newArgs = [prefix + word.replace(oldSep, newSep) for word in argList] | ||
80 | # Additional strip of '--' if converting to profile form | ||
81 | if style == 'to_profile': | ||
82 | newArgs = [word[2:] for word in newArgs] | ||
83 | |||
84 | # Remove invalid '--include' args if converting to command-line form | ||
85 | elif style == 'to_commandline': | ||
86 | newArgs = [word for word in newArgs if 'include' not in word] | ||
87 | |||
88 | return newArgs | ||
89 | |||
90 | |||
91 | def run_firejail(program, allArgs): | ||
92 | """ | ||
93 | Attempt to run the program in firejail, incrementally adding to the number | ||
94 | of firejail arguments. Initial run has no additional params besides | ||
95 | noprofile. | ||
96 | |||
97 | :params program: The program name. If it doesn't exist in the user's path | ||
98 | then the full path should be provided. | ||
99 | |||
100 | :params allArgs: A list of all Firejail arguments to try, in command-line | ||
101 | format. | ||
102 | |||
103 | :returns goodArgs: A list of arguments that the user has reported to not | ||
104 | affect the program | ||
105 | |||
106 | :returns badArgs: A list of arguments that the user has reported to break | ||
107 | the program when sandboxing with Firejail | ||
108 | """ | ||
109 | goodArgs = ['firejail', '--noprofile', program] | ||
110 | badArgs = [] | ||
111 | print('Attempting to run %s in Firejail' % program) | ||
112 | for arg in allArgs: | ||
113 | print('Running with', arg) | ||
114 | subprocess.call(goodArgs) | ||
115 | ans = input('Did %s run correctly? [y]/n ' % program) | ||
116 | if ans == 'n' or ans == 'N': | ||
117 | badArgs.append(arg) | ||
118 | else: | ||
119 | goodArgs.insert(-1, arg) | ||
120 | print('\n') | ||
121 | # Don't include 'firejail', '--noprofile', or program name in arguments | ||
122 | goodArgs = goodArgs[2:-1] | ||
123 | |||
124 | return goodArgs, badArgs | ||
125 | |||
126 | |||
127 | def main(): | ||
128 | profilePath = sys.argv[1] | ||
129 | program = sys.argv[2] | ||
130 | # Quick error check and extract arguments | ||
131 | check_params(profilePath) | ||
132 | profile = get_args(profilePath) | ||
133 | allArgs = arg_converter(profile, 'to_commandline') | ||
134 | # Find out which profile options break the program when running in firejail | ||
135 | goodArgs, badArgs = run_firejail(program, allArgs) | ||
136 | |||
137 | goodArgs = arg_converter(goodArgs, 'to_profile') | ||
138 | badArgs = arg_converter(badArgs, 'to_profile') | ||
139 | |||
140 | print('\n###########################') | ||
141 | print('Debugging completed.') | ||
142 | print( | ||
143 | 'Please copy the following and report it to the Firejail development', | ||
144 | 'team on GitHub at %s \n\n' % | ||
145 | 'https://github.com/netblue30/firejail/issues') | ||
146 | |||
147 | subprocess.call(['firejail', '--version']) | ||
148 | |||
149 | print('These profile options break the program.') | ||
150 | print('```') | ||
151 | for item in badArgs: | ||
152 | print(item) | ||
153 | print('```\n\n\n') | ||
154 | |||
155 | print('This is a minimal working profile:') | ||
156 | print('```') | ||
157 | for item in goodArgs: | ||
158 | print(item) | ||
159 | print('```') | ||
160 | |||
161 | |||
162 | if __name__ == '__main__': | ||
163 | main() | ||