|
be8ed97…
|
wyoung
|
1 |
# Forcing Use of Fossil’s RBAC over SSH |
|
be8ed97…
|
wyoung
|
2 |
|
|
d8f4707…
|
wyoung
|
3 |
Andy Bradford posted a [clever solution][sshfc] to the problem of |
|
be8ed97…
|
wyoung
|
4 |
Fossil’s RBAC system [being ignored](../../caps/#webonly) over `ssh://` |
|
be8ed97…
|
wyoung
|
5 |
URLs: use OpenSSH’s `ForceCommand` feature to route the sync transfer |
|
be8ed97…
|
wyoung
|
6 |
protocol data over `fossil http` rather than `fossil test-http`. |
|
be8ed97…
|
wyoung
|
7 |
|
|
be8ed97…
|
wyoung
|
8 |
The setup for this is complicated, but it’s a worthy option when you |
|
be8ed97…
|
wyoung
|
9 |
need encrypted communications between the client and server, you already |
|
be8ed97…
|
wyoung
|
10 |
have SSH set up, and [the HTTPS alternative](../../ssl.wiki) is |
|
be8ed97…
|
wyoung
|
11 |
unworkable for some reason. |
|
be8ed97…
|
wyoung
|
12 |
|
|
be8ed97…
|
wyoung
|
13 |
|
|
be8ed97…
|
wyoung
|
14 |
## 1. Force remote Fossil access through a wrapper script <a id="sshd"></a> |
|
be8ed97…
|
wyoung
|
15 |
|
|
be8ed97…
|
wyoung
|
16 |
Put something like the following into the `sshd_config` file on the |
|
be8ed97…
|
wyoung
|
17 |
Fossil repository server: |
|
be8ed97…
|
wyoung
|
18 |
|
|
be8ed97…
|
wyoung
|
19 |
``` ssh-config |
|
8a1ba49…
|
wyoung
|
20 |
Match Group fossil |
|
8a1ba49…
|
wyoung
|
21 |
X11Forwarding no |
|
8a1ba49…
|
wyoung
|
22 |
AllowTcpForwarding no |
|
8a1ba49…
|
wyoung
|
23 |
AllowAgentForwarding no |
|
8a1ba49…
|
wyoung
|
24 |
ForceCommand /home/fossil/bin/wrapper |
|
be8ed97…
|
wyoung
|
25 |
``` |
|
be8ed97…
|
wyoung
|
26 |
|
|
be8ed97…
|
wyoung
|
27 |
This file is usually found in `/etc/ssh`, but some OSes put it |
|
be8ed97…
|
wyoung
|
28 |
elsewhere. |
|
be8ed97…
|
wyoung
|
29 |
|
|
be8ed97…
|
wyoung
|
30 |
The first line presumes that we will put all users who need to use our |
|
be8ed97…
|
wyoung
|
31 |
Fossil repositories into the `fossil` group, as we will do |
|
be8ed97…
|
wyoung
|
32 |
[below](#perms). You could instead say something like: |
|
be8ed97…
|
wyoung
|
33 |
|
|
be8ed97…
|
wyoung
|
34 |
``` ssh-config |
|
8a1ba49…
|
wyoung
|
35 |
Match User alice,bob,carol,dave |
|
be8ed97…
|
wyoung
|
36 |
``` |
|
be8ed97…
|
wyoung
|
37 |
|
|
be8ed97…
|
wyoung
|
38 |
You have to list the users allowed to use Fossil in this case because |
|
be8ed97…
|
wyoung
|
39 |
your system likely has a system administrator that uses SSH for remote |
|
be8ed97…
|
wyoung
|
40 |
shell access, so you want to *exclude* that user from the list. For the |
|
be8ed97…
|
wyoung
|
41 |
same reason, you don’t want to put the `ForceCommand` directive outside |
|
be8ed97…
|
wyoung
|
42 |
a `Match` block of some sort. |
|
be8ed97…
|
wyoung
|
43 |
|
|
be8ed97…
|
wyoung
|
44 |
You could instead list the exceptions: |
|
be8ed97…
|
wyoung
|
45 |
|
|
be8ed97…
|
wyoung
|
46 |
``` ssh-config |
|
8a1ba49…
|
wyoung
|
47 |
Match User !evi |
|
be8ed97…
|
wyoung
|
48 |
``` |
|
be8ed97…
|
wyoung
|
49 |
|
|
9d4a132…
|
wyoung
|
50 |
This would permit only [Evi the System Administrator][evi] to bypass this |
|
be8ed97…
|
wyoung
|
51 |
mechanism. |
|
9d4a132…
|
wyoung
|
52 |
|
|
9d4a132…
|
wyoung
|
53 |
[evi]: https://en.wikipedia.org/wiki/Evi_Nemeth |
|
d8f4707…
|
wyoung
|
54 |
|
|
d8f4707…
|
wyoung
|
55 |
If you have a user that needs both interactive SSH shell access *and* |
|
d8f4707…
|
wyoung
|
56 |
Fossil access, exclude that user from the `Match` rule and use Fossil’s |
|
d8f4707…
|
wyoung
|
57 |
normal `ssh://` URL scheme for those cases. This user will bypass the |
|
d8f4707…
|
wyoung
|
58 |
Fossil RBAC, but they effectively have Setup capability on those |
|
d8f4707…
|
wyoung
|
59 |
repositories anyway by having full read/write access to the DB files via |
|
d8f4707…
|
wyoung
|
60 |
the shell. |
|
be8ed97…
|
wyoung
|
61 |
|
|
be8ed97…
|
wyoung
|
62 |
|
|
be8ed97…
|
wyoung
|
63 |
## 2. Rewrite the sync command with that wrapper <a id="wrapper"></a> |
|
be8ed97…
|
wyoung
|
64 |
|
|
be8ed97…
|
wyoung
|
65 |
When Fossil syncs over SSH, it attempts to launch a remote Fossil |
|
be8ed97…
|
wyoung
|
66 |
instance with certain parameters in order to set up the HTTP-based sync |
|
be8ed97…
|
wyoung
|
67 |
protocol over that SSH tunnel. We need to preserve some of this command |
|
be8ed97…
|
wyoung
|
68 |
and rewrite other parts to make this work. |
|
be8ed97…
|
wyoung
|
69 |
|
|
be8ed97…
|
wyoung
|
70 |
Here is a simpler variant of Andy’s original wrapper script: |
|
be8ed97…
|
wyoung
|
71 |
|
|
be8ed97…
|
wyoung
|
72 |
``` sh |
|
8a1ba49…
|
wyoung
|
73 |
#!/bin/bash |
|
8a1ba49…
|
wyoung
|
74 |
set -- $SSH_ORIGINAL_COMMAND |
|
8a1ba49…
|
wyoung
|
75 |
while [ $# -gt 1 ] ; do shift ; done |
|
8a1ba49…
|
wyoung
|
76 |
export REMOTE_USER="$USER" |
|
8a1ba49…
|
wyoung
|
77 |
ROOT=/home/fossil |
|
8a1ba49…
|
wyoung
|
78 |
exec "$ROOT/bin/fossil" http "$ROOT/museum/$(/bin/basename "$1")" |
|
be8ed97…
|
wyoung
|
79 |
``` |
|
be8ed97…
|
wyoung
|
80 |
|
|
be8ed97…
|
wyoung
|
81 |
The substantive changes are: |
|
be8ed97…
|
wyoung
|
82 |
|
|
be8ed97…
|
wyoung
|
83 |
1. Move the command rewriting bits to the start. |
|
be8ed97…
|
wyoung
|
84 |
|
|
be8ed97…
|
wyoung
|
85 |
2. Be explicit about executable paths. You might extend this idea by |
|
be8ed97…
|
wyoung
|
86 |
using chroot, BSD jails, Linux containers, etc. |
|
be8ed97…
|
wyoung
|
87 |
|
|
be8ed97…
|
wyoung
|
88 |
3. Restrict the Fossil repositories to a single flat subdirectory under |
|
be8ed97…
|
wyoung
|
89 |
the `fossil` user’s home directory. This scheme is easier to secure |
|
be8ed97…
|
wyoung
|
90 |
than one allowing subdirectories, since you’d need to take care of |
|
be8ed97…
|
wyoung
|
91 |
`../` and such to prevent a sandbox escape. |
|
be8ed97…
|
wyoung
|
92 |
|
|
be8ed97…
|
wyoung
|
93 |
4. Don’t take the user name via the SSH command; to this author’s mind, |
|
be8ed97…
|
wyoung
|
94 |
the user should not get to override their Fossil user name on the |
|
be8ed97…
|
wyoung
|
95 |
remote server, as that permits impersonation. The identity you |
|
9d4a132…
|
wyoung
|
96 |
present to the SSH server must be the same identity that the Fossil |
|
be8ed97…
|
wyoung
|
97 |
repository you’re working with knows you by. Since the users |
|
be8ed97…
|
wyoung
|
98 |
selected by “`Match`” block above are dedicated to using only Fossil |
|
be8ed97…
|
wyoung
|
99 |
in this setup, this restriction shouldn’t present a practical problem. |
|
be8ed97…
|
wyoung
|
100 |
|
|
be8ed97…
|
wyoung
|
101 |
The script’s shebang line assumes `/bin/sh` is POSIX-compliant, but that |
|
be8ed97…
|
wyoung
|
102 |
is not the case everywhere. If the script fails to run on your system, |
|
d8f4707…
|
wyoung
|
103 |
try changing this line to point at `bash`, `dash`, `ksh`, or `zsh`. Also |
|
d8f4707…
|
wyoung
|
104 |
check the absolute paths for local correctness: is `/bin/basename` |
|
d8f4707…
|
wyoung
|
105 |
installed on your system, for example? |
|
be8ed97…
|
wyoung
|
106 |
|
|
be8ed97…
|
wyoung
|
107 |
Under this scheme, you clone with a command like: |
|
be8ed97…
|
wyoung
|
108 |
|
|
8a1ba49…
|
wyoung
|
109 |
$ fossil clone ssh://HOST/repo.fossil |
|
be8ed97…
|
wyoung
|
110 |
|
|
be8ed97…
|
wyoung
|
111 |
This will clone the remote `/home/fossil/museum/repo.fossil` repository |
|
be8ed97…
|
wyoung
|
112 |
to your local machine under the same name and open it into a “`repo/`” |
|
be8ed97…
|
wyoung
|
113 |
subdirectory. Notice that we didn’t have to give the `museum/` part of |
|
be8ed97…
|
wyoung
|
114 |
the path: it’s implicit per point #3 above. |
|
be8ed97…
|
wyoung
|
115 |
|
|
be8ed97…
|
wyoung
|
116 |
This presumes your local user name matches the remote user name. Unlike |
|
d8f4707…
|
wyoung
|
117 |
with `http[s]://` URLs, you don’t have to provide the `USER@` part to |
|
d8f4707…
|
wyoung
|
118 |
get authenticated access |
|
d8f4707…
|
wyoung
|
119 |
since this scheme doesn’t permit anonymous cloning. Only |
|
9d4a132…
|
wyoung
|
120 |
if these two user names are different do you need to add the `USER@` bit to the |
|
be8ed97…
|
wyoung
|
121 |
URL. |
|
be8ed97…
|
wyoung
|
122 |
|
|
be8ed97…
|
wyoung
|
123 |
|
|
d8f4707…
|
wyoung
|
124 |
## 3. Set permissions <a id="perms"></a> |
|
be8ed97…
|
wyoung
|
125 |
|
|
be8ed97…
|
wyoung
|
126 |
This scheme assumes that the users covered by the `Match` rule can read |
|
be8ed97…
|
wyoung
|
127 |
the wrapper script from where you placed it and execute it, and that |
|
be8ed97…
|
wyoung
|
128 |
they have read/write access on the directory where the Fossil |
|
be8ed97…
|
wyoung
|
129 |
repositories are stored. |
|
be8ed97…
|
wyoung
|
130 |
|
|
be8ed97…
|
wyoung
|
131 |
You can achieve all of this on a Linux box with: |
|
be8ed97…
|
wyoung
|
132 |
|
|
be8ed97…
|
wyoung
|
133 |
``` shell |
|
8a1ba49…
|
wyoung
|
134 |
sudo adduser fossil |
|
8a1ba49…
|
wyoung
|
135 |
for u in alice bob carol dave ; do |
|
8a1ba49…
|
wyoung
|
136 |
sudo adduser $u |
|
8a1ba49…
|
wyoung
|
137 |
sudo gpasswd -a fossil $u |
|
8a1ba49…
|
wyoung
|
138 |
done |
|
8a1ba49…
|
wyoung
|
139 |
sudo -i -u fossil |
|
8a1ba49…
|
wyoung
|
140 |
chmod 710 . |
|
8a1ba49…
|
wyoung
|
141 |
mkdir -m 750 bin |
|
8a1ba49…
|
wyoung
|
142 |
mkdir -m 770 museum |
|
8a1ba49…
|
wyoung
|
143 |
ln -s /usr/local/bin/fossil bin |
|
be8ed97…
|
wyoung
|
144 |
``` |
|
be8ed97…
|
wyoung
|
145 |
|
|
be8ed97…
|
wyoung
|
146 |
You then need to copy the Fossil repositories into `~fossil/museum` and |
|
d8f4707…
|
wyoung
|
147 |
make them readable and writable by group `fossil`. These repositories |
|
d8f4707…
|
wyoung
|
148 |
presumably already have Fossil users configured, with the necessary |
|
d8f4707…
|
wyoung
|
149 |
[user capabilities](../../caps/), the point of this article being to |
|
d8f4707…
|
wyoung
|
150 |
show you how to make Fossil-over-SSH pay attention to those caps. |
|
d8f4707…
|
wyoung
|
151 |
|
|
d8f4707…
|
wyoung
|
152 |
You must also permit use of `REMOTE_USER` on each shared repository. |
|
d8f4707…
|
wyoung
|
153 |
Fossil only pays attention to this environment variable in certain |
|
d8f4707…
|
wyoung
|
154 |
contexts, of which “`fossil http`” is not one. Run this command against |
|
d8f4707…
|
wyoung
|
155 |
each repo to allow that: |
|
d8f4707…
|
wyoung
|
156 |
|
|
d8f4707…
|
wyoung
|
157 |
``` shell |
|
8a1ba49…
|
wyoung
|
158 |
echo "INSERT OR REPLACE INTO config VALUES ('remote_user_ok',1,strftime('%s','now'));" | |
|
8a1ba49…
|
wyoung
|
159 |
fossil sql -R museum/repo.fossil |
|
d8f4707…
|
wyoung
|
160 |
``` |
|
d8f4707…
|
wyoung
|
161 |
|
|
d8f4707…
|
wyoung
|
162 |
Now you can configure SSH authentication for each user. Since Fossil’s |
|
d8f4707…
|
wyoung
|
163 |
password-saving feature doesn’t work in this case, I suggest setting up |
|
d8f4707…
|
wyoung
|
164 |
SSH keys via `~USER/.ssh/authorized_keys` since the SSH authentication |
|
d8f4707…
|
wyoung
|
165 |
occurs on each sync, which Fossil’s default-enabled autosync setting |
|
d8f4707…
|
wyoung
|
166 |
makes frequent. |
|
be8ed97…
|
wyoung
|
167 |
|
|
be8ed97…
|
wyoung
|
168 |
Equivalent commands for other OSes should be readily discerned from the |
|
be8ed97…
|
wyoung
|
169 |
script above. |
|
be8ed97…
|
wyoung
|
170 |
|
|
be8ed97…
|
wyoung
|
171 |
[sshfc]: forum:/forumpost/0d7d6c3df41fcdfd |
|
be8ed97…
|
wyoung
|
172 |
|
|
be8ed97…
|
wyoung
|
173 |
<div style="height:50em" id="this-space-intentionally-left-blank"></div> |