|
| 1 | +# Configuring mTLS authentication for the web server |
| 2 | + |
| 3 | +[Mutual TLS (mTLS)][mtls] is a mechanism for mutual authentication between a server and |
| 4 | +client. Configuring mTLS for a bundle server allows a server maintainer to limit |
| 5 | +bundle server access to only the users that have been provided with a valid |
| 6 | +certificate and establishes confidence that users are interacting with a valid |
| 7 | +bundle server. |
| 8 | + |
| 9 | +[mtls]: https://www.cloudflare.com/learning/access-management/what-is-mutual-tls/ |
| 10 | + |
| 11 | +## Creating certificates |
| 12 | + |
| 13 | +mTLS connections involve the verification of two X.509 certificates: one from |
| 14 | +the server, the other from the client. These certificates may be "self-signed", |
| 15 | +or issued by a separate certificate authority; either will work with the bundle |
| 16 | +server. |
| 17 | + |
| 18 | +For both the server and client(s), both a public certificate (`.pem` file) and a |
| 19 | +private key must be generated. Self-signed pairs can easily be generated with |
| 20 | +OpenSSL; for example: |
| 21 | + |
| 22 | +```bash |
| 23 | +openssl req -x509 -newkey rsa:4096 -days 365 -keyout cert.key -out cert.pem |
| 24 | +``` |
| 25 | + |
| 26 | +The above command will prompt the user to create a password for the 4096-bit RSA |
| 27 | +private key stored in `cert.key` (for no password, use the `-nodes` option), |
| 28 | +then fill in certificate metadata including locality information, company, etc. |
| 29 | +The resulting certificate - stored in `cert.pem` - will be valid for 365 days. |
| 30 | + |
| 31 | +> :rotating_light: If the "Common Name" of the server certificate does not match |
| 32 | +> the bundle web server hostname (e.g. `localhost`), connections to the web |
| 33 | +> server may fail. |
| 34 | +
|
| 35 | +If instead generating a certificate signed by a certificate authority (which can |
| 36 | +itself be a self-signed certificate), a private key and _certificate signing |
| 37 | +request_ must first be generated: |
| 38 | + |
| 39 | +```bash |
| 40 | +openssl req -new -newkey rsa:4096 -days 365 -keyout cert.key -out cert.csr |
| 41 | +``` |
| 42 | + |
| 43 | +The user will be prompted to fill in the same certificate metadata (`-nodes` can |
| 44 | +again be used to skip the password requirement on the private key). Once the |
| 45 | +request is generated (in `cert.csr`), the request can be signed with the CA (in |
| 46 | +the following example, with public certificate `ca.pem` and private key |
| 47 | +`ca.key`): |
| 48 | + |
| 49 | +```bash |
| 50 | +openssl x509 -req -in cert.csr -CA ca.pem -CAkey ca.key -out cert.pem |
| 51 | +``` |
| 52 | + |
| 53 | +This generates the CA-signed certificate `cert.pem`. |
| 54 | + |
| 55 | +## Configuring the web server |
| 56 | + |
| 57 | +To configure the web server, three files are needed: |
| 58 | + |
| 59 | +- If using self-signed client certificate(s), the client certificate `.pem` |
| 60 | + (which may contain one or multiple client certificates concatenated together) |
| 61 | + _or_ the certificate authority `.pem` used to sign client certificate(s). In |
| 62 | + the example below, this is `ca.pem`. |
| 63 | +- The server `.pem` certificate file. In the example below, this is `server.pem`. |
| 64 | +- The server private key file. In the example below, this is `server.key`. |
| 65 | + |
| 66 | +The bundle server can then be configured with the `web-server` command to run in |
| 67 | +the background: |
| 68 | + |
| 69 | +```bash |
| 70 | +git-bundle-server web-server start --force --port 443 --cert server.pem --key server.key --client-ca ca.pem |
| 71 | +``` |
| 72 | + |
| 73 | +Alternatively, the web server can be started directly: |
| 74 | + |
| 75 | +```bash |
| 76 | +git-bundle-web-server --port 443 --cert server.pem --key server.key --client-ca ca.pem |
| 77 | +``` |
| 78 | + |
| 79 | +If the contents of any of the certificate or key files change, the web server |
| 80 | +process must be restarted. To reload the background web server daemon, run |
| 81 | +`git-bundle-server web-server stop` followed by `git-bundle-server web-server |
| 82 | +start`. |
| 83 | + |
| 84 | +## Configuring Git |
| 85 | + |
| 86 | +If cloning or fetching from the bundle server via Git, the client needs to be |
| 87 | +configured to both verify the server certificate and send the appropriate client |
| 88 | +certificate information. This configuration can be applied using environment |
| 89 | +variables or `.gitconfig` values. The required configuration is as follows: |
| 90 | + |
| 91 | +| Config (Environment) | Value | |
| 92 | +| --- | --- | |
| 93 | +| [`http.sslVerify`][sslVerify] (`GIT_SSL_NO_VERIFY`) | `true` for config, `false` for environment var. | |
| 94 | +| [`http.sslCert`][sslCert] (`GIT_SSL_CERT`) | Path to the `client.pem` public cert file. | |
| 95 | +| [`http.sslKey`][sslKey] (`GIT_SSL_KEY`) | Path to the `client.key` private key file. | |
| 96 | +| [`http.sslCertPasswordProtected`][sslKeyPassword] (`GIT_SSL_CERT_PASSWORD_PROTECTED`) | `true` | |
| 97 | +| [`http.sslCAInfo`][sslCAInfo] (`GIT_SSL_CAINFO`) | Path to the certificate authority file, including the server self-signed cert _or_ CA.[^1] | |
| 98 | +| [`http.sslCAPath`][sslCAPath] (`GIT_SSL_CAPATH`) | Path to the directory containing certificate authority files, including the server self-signed cert _or_ CA.[^1] | |
| 99 | + |
| 100 | +Configuring the certificate authority information, in particular, can be tricky. |
| 101 | +Git does not have separate `http` configurations for clones/fetches vs. bundle |
| 102 | +URIs; both will use the same settings. As a result, if cloning via HTTP(S) with |
| 103 | +a bundle URI, users will need to _add_ the custom bundle server CA to the system |
| 104 | +store. The process for adding to the system certificate authorities are |
| 105 | +platform-dependent; for example, Ubuntu uses the |
| 106 | +[`update-ca-certificates`][update-ca-certificates] command. |
| 107 | + |
| 108 | +To avoid needing to add the bundle server CA to the trusted CA store, users can |
| 109 | +instead choose to clone via SSH. In that case, only the bundle URI will use the |
| 110 | +`http` settings, so `http.sslCAInfo` can point directly to the standalone server |
| 111 | +CA. |
| 112 | + |
| 113 | +[sslVerify]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpsslVerify |
| 114 | +[sslCert]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpsslCert |
| 115 | +[sslKey]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpsslKey |
| 116 | +[sslKeyPassword]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpsslCertPasswordProtected |
| 117 | +[sslCAInfo]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpsslCAInfo |
| 118 | +[sslCAPath]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpsslCAPath |
| 119 | +[update-ca-certificates]: https://manpages.ubuntu.com/manpages/xenial/man8/update-ca-certificates.8.html |
| 120 | + |
| 121 | +[^1]: These settings are passed to cURL internally, setting `CURLOPT_CAINFO` and |
| 122 | + `CURLOPT_CAPATH` respectively. |
0 commit comments