-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbs
More file actions
executable file
·290 lines (255 loc) · 9.38 KB
/
bs
File metadata and controls
executable file
·290 lines (255 loc) · 9.38 KB
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
#!/usr/bin/env fish
# TODO:
# - [x] clear-env
set scriptname (status basename)
set bwrap_args
## Printing+debugging
function msg
echo "$scriptname:" $argv
end
function err
$argv >&2
end
function echodo
err msg ' $' $argv
$argv
end
## Argument validation
function _check_is_env
for v in $argv
if ! set -q v
err msg "nonexistant environment variable: $v"
exit 1
end
end
end
function _check_is_dir
for v in $argv
set p (path resolve $v)
if ! test -d "$p"
err msg "path does not exist: $v"
exit 1
end
end
end
function _check_file_exists
for v in $argv
set p (string split -- : $argv)[1]
if ! test -e "$p"
err msg "path does not exist: $v"
exit 1
end
end
end
function _check_is_username
set v $argv[1]
# regex for unix username: https://unix.stackexchange.com/a/435120
if ! string match -rq '^[a-z_]([a-z0-9_-]{0,31}|[a-z0-9_-]{0,30}\$)$' "$v"
err msg "invalid username: $v"
exit 1
end
end
function _check_is_hostname
set v $argv[1]
# regex for unix username: https://unix.stackexchange.com/a/435120
if ! string match -rq '^(?![0-9]+$)(?!.*-$)(?!-)[a-zA-Z0-9-]{1,63}$' "$v"
err msg "invalid hostname: $v"
exit 1
end
end
argparse -s \
--name $scriptname[1] \
e/env-pass=+'!_check_is_env "$_flag_value"' \
f/file-pass=+'!_check_file_exists "$_flag_value"' \
F/rw-file-pass=+'!_check_file_exists "$_flag_value"' \
r/ro-pwd= \
C/chdir='!_check_is_dir "$_flag_value"' \
n/workdir-name= \
u/username='!_check_is_username "$_flag_value"' \
H/hostname='!_check_is_hostname "$_flag_value"' \
c/clear-env \
h/help \
-- $argv \
# Print help
if set -q _flag_help
echo "A program for sandboxing other programs to improve privacy and security"
echo ""
echo "Usage: $scriptname [OPTIONS] [--] [command...]"
echo ""
echo "Arguments:"
echo " command... the command to execute, along with its arguments (optional, defaults to \$SHELL)"
echo ""
echo "Options:"
echo " -c, --clear-env Don't passthrough any environment variables by default"
echo " -e, --env-pass <ENV_VAR> Passthrough an environment variable"
echo " -f, --file-pass <FILE[:DEST]> Passthrough files and directories to the sandbox as readonly (eg. '~/.config' or './bashrc:~/.bashrc')"
echo " -F, --rw-file-pass <FILE[:DEST]> Same as --file-pass, but allow write operations on the targets"
echo " -r, --ro-pwd Make current working directory read-only inside the sandbox"
echo " -C, --chdir <DIR> Change working directory before executing"
echo " -n, --workdir-name <PATH> Edit the path to the working directory, as it appears in the sandbox (default: basename of working directory)"
echo " -u, --username <NAME> Username for inside the sandbox (default: `user')"
echo " -H, --hostname <NAME> Hostname for inside the sandbox (default: `computer')"
echo " -h, --help Print usage information and exit"
exit
end
set uname user
if set -q _flag_username
set uname "$_flag_username"
end
set hname computer
if set -q _flag_hostname
set hname "$_flag_hostname"
end
#
# Make bubblewrap arguments
#
begin
# Change working directory if requested
if set -q _flag_chdir
cd -- "$_flag_chdir"
end
# Bubblewrap
set -a bwrap_args \
--die-with-parent # the child process can't outlive bwrap
# Clear environment variables
if set -q _flag_clear_env
# Needs to happen early
set -a bwrap_args --clearenv
end
# Unix
set -a bwrap_args \
--dev /dev \
--dir /home/$uname \
--dir /run/$uname/"$(id -u)" \
--dir /tmp \
--dir /var \
--hostname $hname \
--proc /proc \
--ro-bind /usr /usr \
--ro-bind /opt /opt \
--symlink /tmp/bs var/tmp \
--symlink usr/bin /bin \
--symlink usr/lib /lib \
--symlink usr/lib64 /lib64 \
--symlink usr/sbin /sbin \
#
# Make temporary files
#
set tmpdir (mktemp -d)
# /etc/group
set group_fd $tmpdir/group
getent group 65534 | sed 's/nobody/'"$uname"'/; s/65534/'"$(id -g)/" > $group_fd
set -a bwrap_args --ro-bind $group_fd /etc/group
# /etc/passwd (all users)
set passwd_fd $tmpdir/passwd
getent passwd 65534 | sed 's/nobody/'"$uname"'/; s/65534/'"$(id -u)/" > $passwd_fd
set -a bwrap_args --ro-bind $passwd_fd /etc/passwd
# /etc/resolv.conf (dns)
set resolv_fd $tmpdir/resolv.conf
echo "nameserver 1.1.1.1" >$resolv_fd
set -a bwrap_args --ro-bind $resolv_fd /etc/resolv.conf
# Hosts file (localhost)
set hosts_fd $tmpdir/hosts
string join -- \n '# Standard host addresses' \
'127.0.0.1 localhost' \
'::1 localhost ip6-localhost ip6-loopback' \
'ff02::1 ip6-allnodes' \
'ff02::2 ip6-allrouters' \
'# Host address' \
"127.0.1.1 $hname" >$hosts_fd
set -a bwrap_args --ro-bind $hosts_fd /etc/hosts
## Namespace
set -a bwrap_args \
--unshare-all \
--unshare-user \
--unshare-ipc \
--unshare-pid \
--unshare-net \
--unshare-uts \
--unshare-cgroup \
--disable-userns \
## Display
set -a bwrap_args \
--ro-bind /run/user/"$(id -u)"/"$WAYLAND_DISPLAY" /run/user/"$(id -u)"/"$WAYLAND_DISPLAY" \
--ro-bind-try /run/user/"$(id -u)"/pipewire-0 /run/user/"$(id -u)"/pipewire-0 \
--ro-bind-try /run/user/"$(id -u)"/pulse /run/user/"$(id -u)"/pulse
## Process working directory
set -l inner_workdir
set -l pwd_bind '--bind'
if set -q _flag_workdir_name
if string match -q '/*' $_flag_workdir_name
set inner_workdir $_flag_workdir_name
else
set inner_workdir /project/$_flag_workdir_name
end
else
set inner_workdir "/project/$(path basename "$PWD")"
end
if set -q _flag_ro_pwd
set pwd_bind '--ro-bind'
end
# PWD is set above when we handle with _flag_chdir
set -a bwrap_args $pwd_bind "$PWD" "$inner_workdir"
# Setup current working directory
set -a bwrap_args \
--chdir "$inner_workdir" \
--dir "$inner_workdir"
function process_file_pass_arg
# $argv[1] - the flag to pass to bind things (should be --bind or --ro-bind)
# $argv[2..-1] - the dirs to passthrough
set -l homereg "$(string escape --style=regex -- "$HOME")"
set -l bind_flag $argv[1]
for f in $argv[2..-1]
if string match -rq -- ':' $f
set f (string split --max 1 -- : $f)
set f_inside (string replace -r -- "^~" "$HOME" $f[2])
set f_inside (string replace -r -- "^$homereg" '/home/'$uname $f_inside)
set -a -- bwrap_args $bind_flag "$f[1]" "$f_inside"
else if test (count $f) = 1
set f_inside (string replace -r -- "^$homereg" '/home/'$uname $f)
set -a -- bwrap_args $bind_flag "$f" "$f_inside"
end
end
end
# Passthrough files
if set -q _flag_rw_file_pass
process_file_pass_arg --bind $_flag_rw_file_pass
end
if set -q _flag_file_pass
process_file_pass_arg --ro-bind $_flag_file_pass
end
# Networking
set -a bwrap_args --share-net
# Environment
set -a bwrap_args \
--setenv TERM "$TERM" \
# --setenv XDG_RUNTIME_DIR "/run/user/$(id -u)" \
# User
set -a bwrap_args \
--setenv HOME /home/$uname \
--setenv USER $uname \
--uid (id -u) \
--gid (id -g) \
# Always passthrough $TERM (idk why you wouldn't want this)
set -a bwrap_args \
--setenv TERM "$TERM" \
--setenv SHELL "$SHELL" \
# Passthrough environment variables (all _flag_env_pass values are validated to exist)
if set -q _flag_env_pass
for a in $_flag_env_pass
if set -q $a
set -a bwrap_args --setenv $a $$a
end
end
end
end
if contains 0 (count $argv)
set argv $SHELL
end
#
# Run bubblewrap
#
bwrap $bwrap_args \
/bin/sh -c 'exec "$@"' -- $argv
# cleanup
rm -r "$tmpdir"