1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
|
#compdef firejail
# Documentation: man 1 zshcompsys
# HowTo: https://github.com/zsh-users/zsh-completions/blob/master/zsh-completions-howto.org
_all_firejails() {
local -a _all_firejails_list
for jail in ${(f)"$(_call_program modules_tag "firejail --list 2> /dev/null | cut -d: -f1")"}; do
_all_firejails_list+=${jail%% *}
done
_describe 'firejails list' _all_firejails_list
}
_all_cpus() {
_cpu_count=$(getconf _NPROCESSORS_ONLN)
for i in {0..$((_cpu_count-1))} ; do
print $i
done
}
_profiles() {
print $1/*.profile | sed -E "s;$1/;;g;s;\.profile;;g;"
}
_profiles_with_ext() {
print $1/*.profile
}
_all_profiles() {
_values 'profiles' $(_profiles _SYSCONFDIR_/firejail) $(_profiles $HOME/.config/firejail) $(_profiles_with_ext .)
}
_session_bus_names() {
_values names $(busctl --user list --no-legend --activatable | cut -d" " -f1)
# Alternatives to hack on for non-systemd systems:
# dbus-send --session --dest=org.freedesktop.DBus --type=method_call --print-reply=literal /org/freedesktop/DBus org.freedesktop.DBus.ListNames
# ls /usr/share/dbus-1/services | xargs -I FILENAME basename FILENAME .service
}
_system_bus_names() {
_values names $(busctl --system list --no-legend --activatable | cut -d" " -f1)
}
_caps() {
_values -s "," caps $(firejail --debug-caps | awk '/[0-9]+\s*- /{print $3}')
}
_firejail_args=(
'*::arguments:_normal'
'--appimage[sandbox an AppImage application]'
'--build[build a whitelisted profile for the application and print it on stdout]'
'--build=-[build a whitelisted profile for the application and save it]: :_files'
# Ignore that you can do -? too as it's the only short option
'--help[this help screen]'
'--join=-[join the sandbox name|pid]: :_all_firejails'
'--join-filesystem=-[join the mount namespace name|pid]: :_all_firejails'
'--list[list all sandboxes]'
'(--profile)--noprofile[do not use a security profile]'
'(--noprofile)--profile=-[use a custom profile]: :_all_profiles'
'--shutdown=-[shutdown the sandbox identified by name|pid]: :_all_firejails'
'--top[monitor the most CPU-intensive sandboxes]'
'--tree[print a tree of all sandboxed processes]'
'--version[print program version and exit]'
'--ids-check[verify file system]'
'--ids-init[initialize IDS database]'
'--debug[print sandbox debug messages]'
'--debug-blacklists[debug blacklisting]'
'--debug-caps[print all recognized capabilities]'
'--debug-errnos[print all recognized error numbers]'
'--debug-private-lib[debug for --private-lib option]'
'--debug-protocols[print all recognized protocols]'
'--debug-syscalls[print all recognized system calls]'
'--debug-syscalls32[print all recognized 32 bit system calls]'
'--debug-whitelists[debug whitelisting]'
'--caps.print=-[print the caps filter name|pid]:firejail:_all_firejails'
'--cpu.print=-[print the cpus in use name|pid]: :_all_firejails'
'--fs.print=-[print the filesystem log name|pid]: :_all_firejails'
'--profile.print=-[print the name of profile file name|pid]: :_all_firejails'
'--protocol.print=-[print the protocol filter name|pid]: :_all_firejails'
'--seccomp.print=-[print the seccomp filter for the sandbox identified by name|pid]: :_all_firejails'
'--allow-debuggers[allow tools such as strace and gdb inside the sandbox]'
'--allusers[all user home directories are visible inside the sandbox]'
# Should be _files, a comma and files or files -/
'*--bind=-[mount-bind dirname1/filename1 on top of dirname2/filename2]: :(file1,file2 dir1,dir2)'
'*--blacklist=-[blacklist directory or file]: :_files'
'--caps[enable default Linux capabilities filter]'
'--caps.drop=all[drop all capabilities]'
'*--caps.drop=-[drop capabilities: all|cap1,cap2,...]: :_caps'
'*--caps.keep=-[keep capabilities: cap1,cap2,...]: :_caps'
'--cpu=-[set cpu affinity]: :->cpus'
"--deterministic-exit-code[always exit with first child's status code]"
'--deterministic-shutdown[terminate orphan processes]'
'*--dns=-[set DNS server]: :'
'*--env=-[set environment variable]: :'
'--hostname=-[set sandbox hostname]: :'
'--hosts-file=-[use file as /etc/hosts]: :_files'
'*--ignore=-[ignore command in profile files]: :'
'--ipc-namespace[enable a new IPC namespace]'
'--join-or-start=-[join the sandbox or start a new one name|pid]: :_all_firejails'
'--keep-config-pulse[disable automatic ~/.config/pulse init]'
'--keep-dev-shm[/dev/shm directory is untouched (even with --private-dev)]'
'--keep-fd[inherit open file descriptors to sandbox]: :'
'--keep-shell-rc[do not copy shell rc files from /etc/skel]'
'--keep-var-tmp[/var/tmp directory is untouched]'
'--landlock.proc=-[add an access rule for /proc to the Landlock ruleset]: :(no ro rw)'
'--landlock.read=-[add a read access rule for the path to the Landlock ruleset]: :_files'
'--landlock.write=-[add a write access rule for the path to the Landlock ruleset]: :_files'
'--landlock.special=-[add an access rule for the path to the Landlock ruleset for creating block/char devices, named pipes and sockets]: :_files'
'--landlock.execute=-[add an execute access rule for the path to the Landlock ruleset]: :_files'
'--machine-id[spoof /etc/machine-id with a random id]'
'--memory-deny-write-execute[seccomp filter to block attempts to create memory mappings that are both writable and executable]'
'*--mkdir=-[create a directory]:'
'*--mkfile=-[create a file]:'
'--name=-[set sandbox name]: :'
'--net=none[enable a new, unconnected network namespace]'
# Sample values as I don't think
# many would enjoy getting a list from -20..20
'--nice=-[set nice value]: :(1 10 15 20)'
'--no3d[disable 3D hardware acceleration]'
'--noautopulse[disable automatic ~/.config/pulse init]'
'--noblacklist=-[disable blacklist for file or directory]: :_files'
'--nodbus[disable D-Bus access]'
'--nodvd[disable DVD and audio CD devices]'
'*--noexec=-[remount the file or directory noexec nosuid and nodev]: :_files'
'--nogroups[disable supplementary groups]'
'--noinput[disable input devices]'
'--nonewprivs[sets the NO_NEW_PRIVS prctl]'
'--noprinters[disable printers]'
'--nosound[disable sound system]'
'--nou2f[disable U2F devices]'
'--novideo[disable video devices]'
'--private[temporary home directory]'
'--private=-[use directory as user home]: :_files -/'
'--private-bin=-[build a new /bin in a temporary filesystem, and copy the programs in the list]: :_files -W /usr/bin'
'--private-cwd[do not inherit working directory inside jail]'
'--private-cwd=-[set working directory inside jail]: :_files -/'
'--private-dev[create a new /dev directory with a small number of common device files]'
'(--writable-etc)--private-etc=-[build a new /etc in a temporary filesystem, and copy the files and directories in the list]: :_files -W /etc'
'--private-opt=-[build a new /opt in a temporary filesystem]: :_files -W /opt'
'--private-srv=-[build a new /srv in a temporary filesystem]: :_files -W /srv'
'--private-tmp[mount a tmpfs on top of /tmp directory]'
'*--protocol=-[enable protocol filter]: :_values -s , protocols unix inet inet6 netlink packet bluetooth'
"--quiet[turn off Firejail's output.]"
'*--read-only=-[set directory or file read-only]: :_files'
'*--read-write=-[set directory or file read-write]: :_files'
'--restrict-namespaces[seccomp filter that blocks attempts to create new namespaces]'
'--restrict-namespaces=-[seccomp filter that blocks attempts to create specified namespaces]: :'
"--rlimit-as=-[set the maximum size of the process's virtual memory (address space) in bytes]: :"
'--rlimit-cpu=-[set the maximum CPU time in seconds]: :'
'--rlimit-fsize=-[set the maximum file size that can be created by a process]: :'
'--rlimit-nofile=-[set the maximum number of files that can be opened by a process]: :'
'--rlimit-nproc=-[set the maximum number of processes that can be created for the real user ID of the calling process]: :'
'--rlimit-sigpending=-[set the maximum number of pending signals for a process]: :'
'*--rmenv=-[remove environment variable in the new sandbox]: :_values environment-variables $(env | cut -d= -f1)'
'--seccomp[enable seccomp filter and apply the default blacklist]: :'
'--seccomp=-[enable seccomp filter, blacklist the default syscall list and the syscalls specified by the command]: :->seccomp'
'--seccomp.block-secondary[build only the native architecture filters]'
'*--seccomp.drop=-[enable seccomp filter, and blacklist the syscalls specified by the command]: :->seccomp'
'*--seccomp.keep=-[enable seccomp filter, and whitelist the syscalls specified by the command]: :->seccomp'
'*--seccomp.32.drop=-[enable seccomp filter, and blacklist the 32 bit syscalls specified by the command]: :'
'*--seccomp.32.keep=-[enable seccomp filter, and whitelist the 32 bit syscalls specified by the command]: :'
# FIXME: Add errnos
'--seccomp-error-action=-[change error code, kill process or log the attempt]: :(kill log)'
'--timeout=-[kill the sandbox automatically after the time has elapsed]: :'
#'(--tracelog)--trace[trace open, access and connect system calls]'
'(--tracelog)--trace=-[trace open, access and connect system calls]: :_files'
'(--trace)--tracelog[add a syslog message for every access to files or directories blacklisted by the security profile]'
'(--private-etc)--writable-etc[/etc directory is mounted read-write]'
'--tab[enable shell tab completion in sandboxes using private or whitelisted home directories]'
'--writable-run-user[allow access to /run/user/$UID/systemd and /run/user/$UID/gnupg]'
'--writable-var[/var directory is mounted read-write]'
'--writable-var-log[use the real /var/log directory, not a clone]'
#ifdef HAVE_APPARMOR
'--apparmor[enable AppArmor confinement with the default profile]'
'--apparmor=-[enable AppArmor confinement with a custom profile]: :'
'--apparmor.print=-[print apparmor status name|pid]:firejail:_all_firejails'
#endif
#ifdef HAVE_CHROOT
'(--noroot --overlay --overlay-named --overlay-tmpfs)--chroot=-[chroot into directory]: :_files -/'
#endif
#ifdef HAVE_DBUSPROXY
# FIXME: _xx_bus_names is actually wrong for --dbus-*.{broadcast,call}.
# We can steal some function from https://github.com/systemd/systemd/blob/main/shell-completion/zsh/_busctl
'--dbus-log=-[set DBus log file location]: :_files'
'--dbus-system=-[set system DBus access policy]: :(filter none)'
'--dbus-system.broadcast=-[allow signals on the system DBus according to rule]: :_system_bus_names'
'--dbus-system.call=-[allow calls on the system DBus according to rule]: :_system_bus_names'
'--dbus-system.own=-[allow ownership of name on the system DBus]: :_system_bus_names'
'--dbus-system.see=-[allow seeing name on the system DBus]: :_system_bus_names'
'--dbus-system.talk=-[allow talking to name on the system DBus]: :_system_bus_names'
'--dbus-user=-[set session DBus access policy or none]: :(filter none)'
'--dbus-user.broadcast=-[allow signals on the session DBus according to rule]: :_session_bus_names'
'--dbus-user.call=-[allow calls on the session DBus according to rule]: :_session_bus_names'
'--dbus-user.own=-[allow ownership of name on the session DBus]: :_session_bus_names'
'--dbus-user.see=-[allow seeing name on the session DBus]: :_session_bus_names'
'--dbus-user.talk=-[allow talking to name on the session DBus]: :_session_bus_names'
#endif
#ifdef HAVE_FILE_TRANSFER
'--cat=-[print content of file from sandbox container name|pid]: :_all_firejails'
'--get=-[get a file from sandbox container name|pid]: :_all_firejails'
# --put=name|pid src-filename dest-filename - put a file in sandbox container.
'--put=-[put a file in sandbox container]: :'
'--ls=-[list files in sandbox container name|pid]: :_all_firejails'
#endif
#ifdef HAVE_FIRETUNNEL
'--tunnel=-[connect the sandbox to a tunnel created by firetunnel utility]: :'
#endif
#ifdef HAVE_NETWORK
'--bandwidth=-[set bandwidth limits name|pid]: :_all_firejails'
'--defaultgw=[configure default gateway]: :'
'--dns.print=-[print DNS configuration name|pid]: :_all_firejails'
'--join-network=-[join the network namespace name|pid]: :_all_firejails'
'--mac=-[set interface MAC address]: :(xx\:xx\:xx\:xx\:xx\:xx)'
'--mtu=-[set interface MTU]: :'
'--net=-[enable network namespaces and connect to this bridge or Ethernet interface (or none to disable)]: :->net_or_none'
'--net.print=-[print network interface configuration name|pid]: :_all_firejails'
'--netfilter=-[enable firewall]: :'
'--netfilter.print=-[print the firewall name|pid]: :_all_firejails'
'--netfilter6=-[enable IPv6 firewall]: :'
'--netfilter6.print=-[print the IPv6 firewall name|pid]: :_all_firejails'
'--netmask=-[define a network mask when dealing with unconfigured parent interfaces]: :'
'--netns=-[Run the program in a named, persistent network namespace]: :'
'--netstats[monitor network statistics]'
'--interface=-[move interface in sandbox]: :'
'--ip=-[set interface IP address none|dhcp|ADDRESS]: :(none dhcp)'
'--ip6=-[set interface IPv6 address or use dhcp via dhclient]: :(dhcp)'
'--iprange=-[configure an IP address in this range]: :'
'--scan[ARP-scan all the networks from inside a network namespace]'
'--veth-name=-[use this name for the interface connected to the bridge]: :'
#endif
#ifdef HAVE_OUTPUT
'--output=-[stdout logging and log rotation]: :_files'
'--output-stderr=-[stdout and stderr logging and log rotation]: :_files'
#endif
#ifdef HAVE_OVERLAYFS
'(--chroot --noroot)--overlay[mount a filesystem overlay on top of the current filesystem]'
'--overlay-clean[clean all overlays stored in $HOME/.firejail directory]'
'(--chroot --noroot)--overlay-named=-[mount a filesystem overlay on top of the current filesystem, and store it in name directory]: :_files -/'
'(--chroot --noroot)--overlay-tmpfs[mount a temporary filesystem overlay on top of the current filesystem]'
#endif
#ifdef HAVE_PRIVATE_HOME
'--private-home=-[build a new user home in a temporary filesystem, and copy the files and directories in the list in the new home]: :_files'
#endif
#ifdef HAVE_USERNS
'(--chroot --overlay --overlay-named --overlay-tmpfs)--noroot[install a user namespace with only the current user]'
#endif
#ifdef HAVE_USERTMPFS
'--private-cache[temporary ~/.cache directory]'
'*--tmpfs=-[mount a tmpfs filesystem on directory dirname]: :_files -/'
#endif
'*--nowhitelist=-[disable whitelist for file or directory]: :_files'
'*--whitelist=-[whitelist directory or file]: :_files'
#ifdef HAVE_X11
'--x11[enable X11 sandboxing. The software checks first if Xpra is installed, then it checks if Xephyr is installed. If all fails, it will attempt to use X11 security extension]'
'--x11=-[disable or enable specific X11 server]: :(none xephyr xorg xpra xvfb)'
'--xephyr-screen=-[set screen size for --x11=xephyr]: :(WIDTHxHEIGHT)'
#endif
)
_firejail() {
_arguments -S $_firejail_args
case "$state" in
cpus)
_values -s "," 'cpus' $(_all_cpus)
;;
net_or_none)
local netdevs=($(ip link | awk '{print $2}' | grep '^.*:$' | tr -d ':'))
local net_and_none=(none $netdevs)
_values 'net' $net_and_none
;;
seccomp)
# TODO: syscall groups
_values -s "," 'syscalls' $(firejail --debug-syscalls | cut -d" " -f2)
;;
esac
}
# vim: ft=zsh sw=4 ts=4 et sts=4 ai
|