|
1
|
<title>Securing a Repository with TLS</title> |
|
2
|
|
|
3
|
<h2>Using TLS-Encrypted Communications with Fossil</h2> |
|
4
|
|
|
5
|
If you are storing sensitive information in a repository accessible over |
|
6
|
a network whose integrity you do not fully trust, you should use TLS to |
|
7
|
encrypt all communications with it. This is most true for repositories |
|
8
|
accessed over the Internet, especially if they will be accessed from |
|
9
|
edge networks you do not control, since that admits of various forms of |
|
10
|
[https://en.wikipedia.org/wiki/Man-in-the-middle_attack|man in the |
|
11
|
middle attack]. |
|
12
|
|
|
13
|
TLS protects the credentials used to access the server, prevents |
|
14
|
eavesdropping, prevents in-flight data modification, prevents server |
|
15
|
identify spoofing, and more. |
|
16
|
|
|
17
|
There are two major aspects to this, both of which have to be addressed |
|
18
|
in different ways. Those are the subjects of the next two major |
|
19
|
sections. |
|
20
|
|
|
21
|
|
|
22
|
<h2 id="client">Client-Side Configuration</h2> |
|
23
|
|
|
24
|
You can build Fossil against [https://www.openssl.org/ | |
|
25
|
OpenSSL] to allow it to clone and sync with a remote |
|
26
|
Fossil repository via <tt>https</tt> URIs. |
|
27
|
|
|
28
|
|
|
29
|
<h3 id="openssl-bin">Building Against OpenSSL Automatically</h3> |
|
30
|
|
|
31
|
The <tt>configure</tt> script will attempt to find OpenSSL on your |
|
32
|
system automatically. It first tries asking the <tt>pkg-config</tt> |
|
33
|
system where the OpenSSL development files are, and if that fails, it |
|
34
|
falls back to looking through a list of likely directories. |
|
35
|
|
|
36
|
If it can't find the files it needs, the most common solution is to |
|
37
|
install the OpenSSL development package on your system via your OS's |
|
38
|
package manager. Examples: |
|
39
|
|
|
40
|
* <b>RHEL & Fedora</b>: <tt>sudo dnf install openssl-devel</tt> |
|
41
|
* <b>Debian & Ubuntu</b>: <tt>sudo apt install libssl-dev</tt> |
|
42
|
* <b>FreeBSD</b>: <tt>su -c 'pkg install openssl'</tt> |
|
43
|
* <b>macOS</b>: <tt>sudo brew install openssl</tt> |
|
44
|
* <b>Cygwin</b>: Install <tt>openssl-devel</tt> via Cygwin's |
|
45
|
<tt>setup-*.exe</tt> program |
|
46
|
|
|
47
|
The macOS case requires explanation. Apple last shipped OpenSSL |
|
48
|
develpoment files in OS X 10.6 (Snow Leopard), choosing to deprecate it |
|
49
|
from that point forward. (Apple wants you to use their proprietary |
|
50
|
platform-specific encryption methods instead.) Since macOS has no |
|
51
|
built-in package manager, a number have sprung up out of the FOSS world. |
|
52
|
It is not known to this author whether Fossil's current build system can |
|
53
|
find OpenSSL as installed with any of these other package managers, so |
|
54
|
unless you have a particular reason to avoid it, we recomend that you |
|
55
|
use [https://brew.sh|Homebrew] on macOS to install OpenSSL as above. |
|
56
|
Fossil's build system will seek it out and use it automatically. |
|
57
|
|
|
58
|
|
|
59
|
<h3 id="openssl-src">Building Against a Non-Platform Version of |
|
60
|
OpenSSL</h3> |
|
61
|
|
|
62
|
The Fossil build system has a few other methods for finding OpenSSL when |
|
63
|
the automatic methods fail or when you'd prefer that Fossil use a |
|
64
|
different version of OpenSSL than the one Fossil's build system picks on |
|
65
|
its own. |
|
66
|
|
|
67
|
A good reason to do this is when the Fossil build system finds a |
|
68
|
functioning version of OpenSSL which is nevertheless unsuitable. One |
|
69
|
common case is that your OS is sufficiently outdated that the platform |
|
70
|
version of OpenSSL can no longer communicate with remote systems |
|
71
|
adhering to the latest advice on secure communications. An old OpenSSL |
|
72
|
might not support any of the |
|
73
|
[https://en.wikipedia.org/wiki/Cipher_suite|cipher suites] the remote |
|
74
|
Fossil repository's HTTPS proxy is willing to offer, for example, so |
|
75
|
that even though both sides are speaking a variant of TLS/SSL, the peers |
|
76
|
cannot come to an agreement on the cryptography. |
|
77
|
|
|
78
|
If you've installed the OpenSSL development files somewhere that |
|
79
|
Fossil's build system cannot find on its own, you can clue it in by |
|
80
|
passing the <tt>--with-openssl</tt> option to the <tt>configure</tt> |
|
81
|
script. Type <tt>./configure --help</tt> for details. |
|
82
|
|
|
83
|
Another option is to download the source code to OpenSSL and build |
|
84
|
Fossil against that private version of OpenSSL: |
|
85
|
|
|
86
|
<pre> |
|
87
|
cd compat # relative to the Fossil source tree root |
|
88
|
tar xf /path/to/openssl-*.tar.gz |
|
89
|
ln -fs openssl-x.y.z openssl |
|
90
|
cd openssl |
|
91
|
./config # or, e.g. ./Configure darwin64-x86_64-cc |
|
92
|
make -j11 |
|
93
|
cd ../.. |
|
94
|
./configure --with-openssl=tree |
|
95
|
make -j11 |
|
96
|
</pre> |
|
97
|
|
|
98
|
That will get you a Fossil binary statically linked to this in-tree |
|
99
|
version of OpenSSL. |
|
100
|
|
|
101
|
Beware, taking this path typically opens you up to new problems, which |
|
102
|
are conveniently covered in the next section! |
|
103
|
|
|
104
|
|
|
105
|
<h3 id="certs">Certificates</h3> |
|
106
|
|
|
107
|
To verify the identify of a server, TLS uses |
|
108
|
[https://en.wikipedia.org/wiki/X.509#Certificates|X.509 certificates], a |
|
109
|
scheme that depends on a trust hierarchy of so-called |
|
110
|
[https://en.wikipedia.org/wiki/Certificate_authority | Certificate |
|
111
|
Authorities]. The tree of trust relationships ultimately ends in the |
|
112
|
CA roots, which are considered the ultimate arbiters of who to trust in |
|
113
|
this scheme. |
|
114
|
|
|
115
|
The question then is, what CA roots does Fossil trust? |
|
116
|
|
|
117
|
If you are using a self-signed certificate, Fossil will initially not |
|
118
|
know that it can trust your certificate, so you'll be asked if you want |
|
119
|
to accept the certificate the first time you communicate with the |
|
120
|
server. Verify the certificate fingerprint is correct, then answer |
|
121
|
"always" if you want Fossil to remember your decision. |
|
122
|
|
|
123
|
If you are cloning from or syncing to Fossil servers that use a |
|
124
|
certificate signed by a well-known CA or one of its delegates, Fossil |
|
125
|
still has to know which CA roots to trust. When this fails, you get an |
|
126
|
error message that looks like this: |
|
127
|
|
|
128
|
<pre> |
|
129
|
Unable to verify SSL cert from fossil-scm.org |
|
130
|
subject: CN = sqlite.org |
|
131
|
issuer: C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 |
|
132
|
sha256: bf26092dd97df6e4f7bf1926072e7e8d200129e1ffb8ef5276c1e5dd9bc95d52 |
|
133
|
accept this cert and continue (y/N)? |
|
134
|
</pre> |
|
135
|
|
|
136
|
In older versions, the message was much longer and began with this line: |
|
137
|
|
|
138
|
<pre> |
|
139
|
SSL verification failed: unable to get local issuer certificate |
|
140
|
</pre> |
|
141
|
|
|
142
|
Fossil relies on the OpenSSL library to have some way to check a trusted |
|
143
|
list of CA signing keys. There are two common ways this fails: |
|
144
|
|
|
145
|
# The OpenSSL library Fossil is linked to doesn't have a CA |
|
146
|
signing key set at all, so that it initially trusts no certificates |
|
147
|
at all. |
|
148
|
|
|
149
|
# The OpenSSL library does have a CA cert set, but your Fossil server's |
|
150
|
TLS certificate was signed by a CA that isn't in that set. |
|
151
|
|
|
152
|
A common reason to fall into the second trap is that you're using |
|
153
|
certificates signed by a local private CA, as often happens in large |
|
154
|
enterprises. You can solve this sort of problem by getting your local |
|
155
|
CA's signing certificate in PEM format and pointing OpenSSL at it: |
|
156
|
|
|
157
|
<pre> |
|
158
|
fossil set --global ssl-ca-location /path/to/local-ca.pem |
|
159
|
</pre> |
|
160
|
|
|
161
|
The use of <tt>--global</tt> with this option is common, since you may |
|
162
|
have multiple repositories served under certificates signed by that same |
|
163
|
CA. However, if you have a mix of publicly-signed and locally-signed |
|
164
|
certificates, you might want to drop the <tt>--global</tt> flag and set |
|
165
|
this option on a per-repository basis instead. |
|
166
|
|
|
167
|
A common way to run into the broader first problem is that you're on |
|
168
|
FreeBSD, which does not install a CA certificate set by default, even as |
|
169
|
a dependency of the OpenSSL library. If you're using a certificate |
|
170
|
signed by one of the major public CAs, you can solve this by installing |
|
171
|
the <tt>ca_root_nss</tt> package. That package contains the Mozilla NSS |
|
172
|
certificate bundle, which gets installed in a location that OpenSSL |
|
173
|
checks by default, so you don't need to change any Fossil settings. |
|
174
|
(This is the same certificate set that ships with Firefox, by the way.) |
|
175
|
|
|
176
|
The same sort of thing happens with the Windows build of OpenSSL, but |
|
177
|
for a different core reason: Windows does ship with a stock CA |
|
178
|
certificate set, but it's not in a format that OpenSSL understands how |
|
179
|
to use. Rather than try to find a way to convert the data format, you |
|
180
|
may find it acceptable to use the same Mozilla NSS cert set. I do not |
|
181
|
know of a way to easily get this from Mozilla themselves, but I did find |
|
182
|
a [https://curl.se/docs/caextract.html | third party source] for the |
|
183
|
<tt>cacert.pem</tt> file. I suggest placing the file into your Windows |
|
184
|
user home directory so that you can then point Fossil at it like so: |
|
185
|
|
|
186
|
<pre> |
|
187
|
fossil set --global ssl-ca-location %userprofile%\cacert.pem |
|
188
|
</pre> |
|
189
|
|
|
190
|
This can also happen if you've linked Fossil to a version of OpenSSL |
|
191
|
[#openssl-src|built from source]. That same <tt>cacert.pem</tt> fix can |
|
192
|
work in that case, too. |
|
193
|
|
|
194
|
<blockquote> |
|
195
|
OpenSSL 3.2.0 or greater is able to use the stock CA certificates |
|
196
|
managed by Windows, and Fossil 2.25 (still in development as of |
|
197
|
2024-07-15) takes advantage of this feature. This <em>possibly</em> |
|
198
|
eliminates the need to manually install the Mozilla certificate package, |
|
199
|
for example when connecting to Fossil servers secured by the widely-used |
|
200
|
Let's Encrypt certificates. Run the following command to check if the |
|
201
|
feature is supported: |
|
202
|
|
|
203
|
<pre> |
|
204
|
fossil tls-config show -v |
|
205
|
</pre> |
|
206
|
|
|
207
|
(See the "OpenSSL-winstore" section, requires Fossil 2.25 or greater.) |
|
208
|
</blockquote> |
|
209
|
|
|
210
|
When you build Fossil on Linux platforms against the binary OpenSSL |
|
211
|
package provided with the OS, you typically get a root cert store along |
|
212
|
with the platform OpenSSL package, either built-in or as a hard |
|
213
|
dependency. |
|
214
|
|
|
215
|
|
|
216
|
<h4>Client-Side Certificates</h4> |
|
217
|
|
|
218
|
You can also use client side certificates to add an extra layer of |
|
219
|
authentication, over and above Fossil's built in user management. If you |
|
220
|
are particularly paranoid, you'll want to use this to remove the ability |
|
221
|
of anyone on the internet from making any request to Fossil. Without |
|
222
|
presenting a valid client side certificate, the web server won't invoke |
|
223
|
the Fossil CGI handler. |
|
224
|
|
|
225
|
Configure your server to request a client side certificate, and set up a |
|
226
|
certificate authority to sign your client certificates. For each person |
|
227
|
who needs to access the repository, create a private key and certificate |
|
228
|
signed with that CA. |
|
229
|
|
|
230
|
The PEM encoded private key and certificate should be stored in a single |
|
231
|
file, simply by concatenating the key and certificate files. Specify the |
|
232
|
location of this file with the <tt>ssl-identity</tt> setting, or the |
|
233
|
<tt>--ssl-identity</tt> option to the <tt>clone</tt> command. |
|
234
|
|
|
235
|
If you've password protected the private key, the password will be |
|
236
|
requested every time you connect to the server. This password is not |
|
237
|
stored by fossil, as doing so would defeat the purpose of having a |
|
238
|
password. |
|
239
|
|
|
240
|
If you attempt to connect to a server which requests a client |
|
241
|
certificate, but don't provide one, fossil will show an error message |
|
242
|
which explains what to do to authenticate with the server. |
|
243
|
|
|
244
|
|
|
245
|
<h2 id="server">Server-Side Configuration</h2> |
|
246
|
|
|
247
|
Before Fossil's built-in HTTP server gained [./ssl-server.md | TLS support], |
|
248
|
system administrators that wanted to add this |
|
249
|
had to put it behind a reverse proxy that would do the translation. |
|
250
|
Since advantages remain for delegating TLS to another layer in the |
|
251
|
stack, instructions for doing so continue to be included in our |
|
252
|
documentation, such as: |
|
253
|
|
|
254
|
* <a id="stunnel" href="./server/any/stunnel.md">Serving via stunnel</a> |
|
255
|
* <a id="althttpd" href="./server/any/althttpd.md">Serving via stunnel + althttpd</a> |
|
256
|
* <a id="nginx" href="./server/debian/nginx.md#tls">Serving via SCGI with nginx on Debian</a> |
|
257
|
|
|
258
|
|
|
259
|
<h2 id="enforcing">Enforcing TLS Access</h2> |
|
260
|
|
|
261
|
To use TLS encryption in cloning and syncing to a remote Fossil |
|
262
|
repository, be sure to use the <tt>https:</tt> URI scheme in |
|
263
|
<tt>clone</tt> and <tt>sync</tt> commands. If your server is configured |
|
264
|
to serve the repository via both HTTP and HTTPS, it's easy to |
|
265
|
accidentally use unencrypted HTTP if you forget the all-important 's'. |
|
266
|
|
|
267
|
As of Fossil 2.9, using an <tt>http://</tt> URI with <tt>fossil |
|
268
|
clone</tt> or <tt>sync</tt> on a site that forwards to HTTPS will cause |
|
269
|
Fossil to remember the secure URL. However, there's a |
|
270
|
[https://en.wikipedia.org/wiki/Trust_on_first_use | TOFU problem] with |
|
271
|
this: it's still better to use <tt>https://</tt> from the start. |
|
272
|
|
|
273
|
As of Fossil 2.8, there is a setting in the Fossil UI under Admin → |
|
274
|
Access called "Redirect to HTTPS," which is set to "Off" by default. |
|
275
|
Changing this only affects web UI access to the Fossil repository. It |
|
276
|
doesn't affect clones and syncs done via the <tt>http</tt> URI scheme. |
|
277
|
|
|
278
|
In Fossil 2.7 and earlier, there was a much weaker form of this setting |
|
279
|
affecting the <tt>/login</tt> page only. If you're using this setting, |
|
280
|
you should migrate to the new setting as soon as possible, because the |
|
281
|
old setting allows multiple ways of defeating it. |
|
282
|
|
|
283
|
<b id="rloop">WARNING:</b> Enabling HTTPS redirects at the Fossil repo |
|
284
|
level while running Fossil behind an HTTPS proxy can result in an |
|
285
|
infinite redirect loop. It happens when the proxy mechanism presents |
|
286
|
"<tt>http</tt>" URIs to Fossil, so Fossil issues a redirect, so the browser |
|
287
|
fetches the page again, causing Fossil to see an "<tt>http</tt>" URI again, so |
|
288
|
it issues a redirect...'round and 'round it goes until the web browser |
|
289
|
detects it's in a redirect loop and gives up. This problem prevents you |
|
290
|
from getting back into the Admin UI to fix it, but there are several |
|
291
|
ways to fix it: |
|
292
|
|
|
293
|
# <b>Reset via CLI.</b> You can turn the setting back off from the |
|
294
|
CLI with the command "<tt>fossil -R /path/to/repo.fossil set |
|
295
|
redirect-to-https 0</tt>". (Currently doesn't work.) |
|
296
|
|
|
297
|
# <b>Backup first.</b> This setting is stored in the Fossil |
|
298
|
repository, so if you make a backup first <i>on the server</i>, you |
|
299
|
can restore the repo file if enabling this feature creates a |
|
300
|
redirect loop. |
|
301
|
|
|
302
|
# <b>Download, fix, and restore.</b> You can copy the remote |
|
303
|
repository file down to a local machine, use <tt>fossil ui</tt> to |
|
304
|
fix the setting, and then upload it to the repository server |
|
305
|
again. |
|
306
|
|
|
307
|
It's best to enforce TLS-only access at the front-end proxy level |
|
308
|
anyway. It not only avoids the problem entirely, it can be significantly |
|
309
|
more secure. The [./server/debian/nginx.md#tls | nginx-on-Debian proxy guide] shows one way |
|
310
|
to achieve this. |
|
311
|
|
|
312
|
|
|
313
|
<h2>Terminology Note</h2> |
|
314
|
|
|
315
|
This document is called <tt>ssl.wiki</tt> for historical reasons. The |
|
316
|
TLS protocol was originally called SSL, and it went through several |
|
317
|
revisions before being replaced by TLS. Years before this writing, SSL |
|
318
|
finally became entirely obsolete due to weaknesses in the protocol fixed |
|
319
|
in the later TLS series of protocols. |
|
320
|
|
|
321
|
Some people still use the term "SSL" when they actually mean "TLS," but |
|
322
|
in the Fossil project, we always use "TLS" except when we must preserve |
|
323
|
some sort of historical compatibility, as with this document's name in |
|
324
|
order to avoid broken external URLs. The Fossil TLS-related settings |
|
325
|
also often use "<tt>ssl</tt>" in their names for the same reason. |
|
326
|
|
|
327
|
This series of protocols is also called "HTTPS" after the URI scheme |
|
328
|
used to specify "HTTP over TLS." |
|
329
|
|