|
| 1 | +# Configuring mTLS authentication for the web server |
| 2 | + |
| 3 | +[Mutual TLS (mTLS)][mtls] is a mechanism for mutual authentication between a |
| 4 | +server and client. Configuring mTLS for a bundle server allows a server |
| 5 | +maintainer to limit bundle server access to only the users that have been |
| 6 | +provided with a valid certificate and establishes confidence that users are |
| 7 | +interacting with a valid bundle server. |
| 8 | + |
| 9 | +[mtls]: https://www.cloudflare.com/learning/access-management/what-is-mutual-tls/ |
| 10 | + |
| 11 | +## mTLS limitations |
| 12 | + |
| 13 | +mTLS in the bundle server is configured **server-wide**, so it only provides |
| 14 | +only a limited layer of protection against unauthorized access. Importantly, |
| 15 | +**any** user with a valid client cert/private key pair will be able to access |
| 16 | +**any** content on the bundle server. The implications of this include: |
| 17 | + |
| 18 | +- If the bundle server manages repositories with separately controlled access, |
| 19 | + providing a user with a valid client cert/key for the bundle server may |
| 20 | + accidentally grant read access to Git data the user is not authorized to |
| 21 | + access on the remote host. |
| 22 | +- If the remote host has branch-level security then the bundles may contain Git |
| 23 | + objects reachable from restricted branches. |
| 24 | + |
| 25 | +## Creating certificates |
| 26 | + |
| 27 | +mTLS connections involve the verification of two X.509 certificates: one from |
| 28 | +the server, the other from the client. These certificates may be "self-signed", |
| 29 | +or issued by a separate certificate authority; either will work with the bundle |
| 30 | +server. |
| 31 | + |
| 32 | +For both the server and client(s), both a public certificate (`.pem` file) and a |
| 33 | +private key must be generated. Self-signed pairs can easily be generated with |
| 34 | +OpenSSL; for example: |
| 35 | + |
| 36 | +```bash |
| 37 | +openssl req -x509 -newkey rsa:4096 -days 365 -keyout cert.key -out cert.pem |
| 38 | +``` |
| 39 | + |
| 40 | +The above command will prompt the user to create a password for the 4096-bit RSA |
| 41 | +private key stored in `cert.key` (for no password, use the `-nodes` option), |
| 42 | +then fill in certificate metadata including locality information, company, etc. |
| 43 | +The resulting certificate - stored in `cert.pem` - will be valid for 365 days. |
| 44 | + |
| 45 | +> :rotating_light: If the "Common Name" of the server certificate does not match |
| 46 | +> the bundle web server hostname (e.g. `localhost`), connections to the web |
| 47 | +> server may fail. |
| 48 | +
|
| 49 | +If instead generating a certificate signed by a certificate authority (which can |
| 50 | +itself be a self-signed certificate), a private key and _certificate signing |
| 51 | +request_ must first be generated: |
| 52 | + |
| 53 | +```bash |
| 54 | +openssl req -new -newkey rsa:4096 -days 365 -keyout cert.key -out cert.csr |
| 55 | +``` |
| 56 | + |
| 57 | +The user will be prompted to fill in the same certificate metadata (`-nodes` can |
| 58 | +again be used to skip the password requirement on the private key). Once the |
| 59 | +request is generated (in `cert.csr`), the request can be signed with the CA (in |
| 60 | +the following example, with public certificate `ca.pem` and private key |
| 61 | +`ca.key`): |
| 62 | + |
| 63 | +```bash |
| 64 | +openssl x509 -req -in cert.csr -CA ca.pem -CAkey ca.key -out cert.pem |
| 65 | +``` |
| 66 | + |
| 67 | +This generates the CA-signed certificate `cert.pem`. |
| 68 | + |
| 69 | +### :rotating_light: IMPORTANT: PROTECTING YOUR CREDENTIALS :rotating_light: |
| 70 | + |
| 71 | +It is _extremely_ important that the private keys associated with the generated |
| 72 | +certificates are safeguarded against unauthorized access (e.g. in a password |
| 73 | +manager with minimal access). |
| 74 | + |
| 75 | +If the server-side key is exposed, a malicious site could pose as a valid bundle |
| 76 | +server and mislead users into providing it credentials or other private |
| 77 | +information. Potentially-exposed server credentials should be replaced as soon |
| 78 | +as possible, with the appropriate certificate authority/self-signed cert (_not_ |
| 79 | +the private key) distributed to users that use the server. |
| 80 | + |
| 81 | +If a client-side key is exposed, an unauthorized user or malicious actor will |
| 82 | +gain access to the bundle server and all content contained within it. **The |
| 83 | +bundle server does not provide a mechanism for revoking certificates**, so |
| 84 | +credentials will need to be rolled depending on how client certificates were |
| 85 | +generated: |
| 86 | + |
| 87 | +- If the `--client-ca` used by the bundle web server is a self-signed |
| 88 | + certificate corresponding to a single client, a new certificate/key pair will |
| 89 | + need to be generated and the bundle web server restarted[^1] to use the new |
| 90 | + `--client-ca` file. |
| 91 | +- If the `--client-ca` is a concatenation of self-signed client certificates, |
| 92 | + the compromised certificate will need to be removed from the file and the |
| 93 | + bundle web server restarted. |
| 94 | +- If the `--client-ca` is a certificate authority (a single certificate used to |
| 95 | + sign other certificates), the certificate authority and _all_ client |
| 96 | + certificates will need to be replaced. |
| 97 | + |
| 98 | +## Configuring the web server |
| 99 | + |
| 100 | +To configure the web server, three files are needed: |
| 101 | + |
| 102 | +- If using self-signed client certificate(s), the client certificate `.pem` |
| 103 | + (which may contain one or multiple client certificates concatenated together) |
| 104 | + _or_ the certificate authority `.pem` used to sign client certificate(s). In |
| 105 | + the example below, this is `ca.pem`. |
| 106 | +- The server `.pem` certificate file. In the example below, this is |
| 107 | + `server.pem`. |
| 108 | +- The server private key file. In the example below, this is `server.key`. |
| 109 | + |
| 110 | +The bundle server can then be configured with the `web-server` command to run in |
| 111 | +the background: |
| 112 | + |
| 113 | +```bash |
| 114 | +git-bundle-server web-server start --force --port 443 --cert server.pem --key server.key --client-ca ca.pem |
| 115 | +``` |
| 116 | + |
| 117 | +Alternatively, the web server can be started directly: |
| 118 | + |
| 119 | +```bash |
| 120 | +git-bundle-web-server --port 443 --cert server.pem --key server.key --client-ca ca.pem |
| 121 | +``` |
| 122 | + |
| 123 | +If the contents of any of the certificate or key files change, the web server |
| 124 | +process must be restarted. To reload the background web server daemon, run |
| 125 | +`git-bundle-server web-server stop` followed by `git-bundle-server web-server |
| 126 | +start`. |
| 127 | + |
| 128 | +## Configuring Git |
| 129 | + |
| 130 | +If cloning or fetching from the bundle server via Git, the client needs to be |
| 131 | +configured to both verify the server certificate and send the appropriate client |
| 132 | +certificate information. This configuration can be applied using environment |
| 133 | +variables or `.gitconfig` values. The required configuration is as follows: |
| 134 | + |
| 135 | +| Config (Environment) | Value | |
| 136 | +| --- | --- | |
| 137 | +| [`http.sslVerify`][sslVerify] (`GIT_SSL_NO_VERIFY`) | `true` for config, `false` for environment var. | |
| 138 | +| [`http.sslCert`][sslCert] (`GIT_SSL_CERT`) | Path to the `client.pem` public cert file. | |
| 139 | +| [`http.sslKey`][sslKey] (`GIT_SSL_KEY`) | Path to the `client.key` private key file. | |
| 140 | +| [`http.sslCertPasswordProtected`][sslKeyPassword] (`GIT_SSL_CERT_PASSWORD_PROTECTED`) | `true` | |
| 141 | +| [`http.sslCAInfo`][sslCAInfo] (`GIT_SSL_CAINFO`) | Path to the certificate authority file, including the server self-signed cert _or_ CA.[^2] | |
| 142 | +| [`http.sslCAPath`][sslCAPath] (`GIT_SSL_CAPATH`) | Path to the directory containing certificate authority files, including the server self-signed cert _or_ CA.[^2] | |
| 143 | + |
| 144 | +Configuring the certificate authority information, in particular, can be tricky. |
| 145 | +Git does not have separate `http` configurations for clones/fetches vs. bundle |
| 146 | +URIs; both will use the same settings. As a result, if cloning via HTTP(S) with |
| 147 | +a bundle URI, users will need to _add_ the custom bundle server CA to the system |
| 148 | +store. The process for adding to the system certificate authorities are |
| 149 | +platform-dependent; for example, Ubuntu uses the |
| 150 | +[`update-ca-certificates`][update-ca-certificates] command. |
| 151 | + |
| 152 | +To avoid needing to add the bundle server CA to the trusted CA store, users can |
| 153 | +instead choose to clone via SSH. In that case, only the bundle URI will use the |
| 154 | +`http` settings, so `http.sslCAInfo` can point directly to the standalone server |
| 155 | +CA. |
| 156 | + |
| 157 | +[sslVerify]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpsslVerify |
| 158 | +[sslCert]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpsslCert |
| 159 | +[sslKey]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpsslKey |
| 160 | +[sslKeyPassword]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpsslCertPasswordProtected |
| 161 | +[sslCAInfo]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpsslCAInfo |
| 162 | +[sslCAPath]: https://git-scm.com/docs/git-config#Documentation/git-config.txt-httpsslCAPath |
| 163 | +[update-ca-certificates]: https://manpages.ubuntu.com/manpages/xenial/man8/update-ca-certificates.8.html |
| 164 | + |
| 165 | +[^1]: If using the `git-bundle-server web-server` command _and_ using a |
| 166 | + different `--client-ca` path than the old certificate, the `--force` option |
| 167 | + must be used with `start` to refresh the daemon configuration. |
| 168 | +[^2]: These settings are passed to cURL internally, setting `CURLOPT_CAINFO` and |
| 169 | + `CURLOPT_CAPATH` respectively. |
0 commit comments