This repository contains mod_tls
, a module for Apache httpd that uses
rustls to provide a memory safe TLS
implementation in Rust.
The current state is compatible with rustls-ffi v0.14.0 and needs at least Apache 2.4.48 for the necessary infrastructure.
mod_tls
gives you:
- a memory safe TLS via rustls.
- TLS v1.2 and TLS v1.3
- Configuration similar to Apache's own
mod_ssl
. - Similar performance as
mod_ssl
, better in some areas. - Frontend TLS for your clients
- Backend TLS for
mod_proxy
- Frontend OCSP Stapling via
mod_md
mod_tls
currently does not support:
- client certificates
There is a comparison table with mod_ssl functionality.
- rustls-ffi v0.14.0
- Apache 2.4.48 or later
- build system: autoconf/automake
Run the usual autoconf/automake magic incantations. You need a built Apache trunk and specify the --with-apxe=<path>/bin/apxs
on configuration if that is not in your $PATH
. Also, you need rustls-ffi installed.
Run the usual autoconf/automake magic incantations.
> autoreconf -i
> automake
> autoconf
> ./configure --with-apxs=<path to apxs>
> make
mod_tls
adds OCSP responses to TLS handshakes (this is what "Stapling" is), when someone provides these responses. It has no own implementation to retrieve these responses, like mod_ssl
does.
In Apache 2.4.48 there is a new internal API where modules can ask around for someone willing to provide this. mod_md
is currently the only choice here and you need to enable this via:
MDStapling on # provide OCSP responses (for certificates by mod_md)
MDStapleOthers on # provide OCSP for all other server certs as well
You'd want to enable this when you use mod_tls
. To check that this works, you can enable Apache's server-status
handler by mod_status
. On that page, you'll then also see certificate and OCSP information from mod_md
.
If you want to run the test suite, you need:
curl
andopenssl
in your path- Some Python packages:
pytest
,cryptography
> make test
This project has been sponsored in 2020 by the ISRG
(Read what they said about it.). It had been added as an experimental module into the Apache httpd project. However, the rustls-ffi
API proved to be unstable, mostly due to rustls
own ongoing development. This requires rework of mod_tls
on new releases and the schedule of that does not fit into the release schedule of the httpd project.
In September 2024, I chose to give the module a standalone github location with its own releases and removed it from the Apache httpd project again. This will make maintenance and participation easier.
Should rustls one day become stable, we can consider merging it to the httpd project again.
Feature | mod_ssl | mod_tls | Comment |
---|---|---|---|
Frontend TLS | yes | yes | |
Backend TLS (proxy) | yes | yes | |
TLSv1.3 | yes* | yes | *)with recent OpenSSL |
TLSv1.2 | yes | yes | |
TLSv1.1/1.0 | yes* | no | *)if enabled in OpenSSL |
Individual VirtualHost TLS Settings | yes | yes | |
Frontend client certificates | yes | no | |
Backend machine certificate | yes | yes* | *)since v0.8.1 |
Frontend OCSP stapling | yes | yes* | *)via mod_md |
Backend OCSP check | yes | no* | *)stapling will be verified |
TLS version used | min-max | min | |
TLS ciphers | exclusive list | preferred/suppressed | |
TLS ciphers preference | client/server | client/server | whose ordering shall be honored |
TLS sessions | yes | yes | |
SNI strictness | default no | default yes | |
Option EnvVars | exhaustive | limited | See variables |
Option ExportCertData | client+server | server | See variables |
Backend CA | file/dir | file | |
Revocation CRLs | yes | no | |
FIPS | yes* | no | *)depends on OpenSSL |
TLS Renegotiation | yes | no | e.g. varying TLS settings per location |
Encrypted Certificate Keys | yes | no | |
TLS SRP | yes | no | |
TLS SCT | no | no |
mod_tls
has, like all other Apache httpd modules, a number of configuration directives that
you need to use for the module to become active in your server. The whole list is described
below in the directives section.
For the module to become available in your server, it needs to be loaded. The directive for that looks like:
LoadModule tls_module "<modules-path>/mod_tls.so"
On several linux distributions there are mechanisms to do that from the command line, e.g. debian has
the nice a2enmod
command.
When you restart the server afterwards, the module will log in INFO
entry. This lists versions of rustls-ffi
binding and the rustls
library are logged by mod_tls
, like this:
[date time] [tls:info] [pid] mod_tls/0.6.0 (crustls=crustls/0.6.0/rustls/0.19.0), initializing...
If you do not see this, make sure that the log level does not suppress this message. You may add LogLevel tls:info
to your configuration for this.
You can load mod_tls
and other SSL modules like mod_ssl
at the same time. If you have a running mod_ssl
setup, you can load mod_tls
in addition and it will by itself not change anything. You need to add configuration directives to tell the module where it should handle connections.
Clients connect to your server using an IP address and a port number. You apache may listen for new connections on several of those. Most setups use 2 ports, 80 and 443, on all addresses that the server has. This is easy, because should the address of your server change, the apache config will continue to work. For this, somewhere in your server, there are directives like this:
Listen 80
Listen 443
<VirtualHost *:80>
ServerName a.net
...
</VirtualHost>
<VirtalHost *:443>
ServerName b.net
...
</VirtualHost>
This means clients can reach a.net
on port 80 and b.net
on port 443. Both of these do http:
so far.
To have b.net
use encrypted https:
traffic, you need to add SSL directives:
...
<VirtalHost *:443>
ServerName b.net
SSLEngine on
SSLCertificateFile file_with_certificate.pem
SSLCertificateKeyFile file_with_key.pem
...
</VirtualHost>
If you have several VirtualHost *:443
, you need to add the SSLEngine on
in each of them, especially the first one.
With mod_tls
, the configuration is slightly different:
...
TLSEngine 443
<VirtalHost *:443>
ServerName b.net
TLSCertificate file_with_certificate.pem file_with_key.pem
...
</VirtualHost>
You instruct mod_tls
to encrypt all incoming connections on port 443. You add the certificate+key to the VirtualHost
s like with mod_ssl
. If you have certificate and key in the same file (no real reason not to), you can just add the file once.
The certificate and key file formats are the same.
First: you can not mix both modules on the same address and port!
But you can use mod_ssl
on one port and mod_tls
on another. You can also use mod_tls
for incoming connections and mod_ssl
for connections to proxied servers (backends).
Theoretically, if your server has two interfaces (IP addresses addr1
and addr2
), you could use one module on addr1:443
and another on addr2:443
. But you would need to define separate VirtualHost
s for particular addresses. That seems quite an edge configuration, but it is doable.
Certificates and keys are commonly stored in PEM
file, which is a standardized format. This means you can use the same files for mod_ssl
and mod_tls
. The only exception is that mod_tls
does not support encrypted keys.
A certificate file needs to contain the certificate, followed by the certificates that make up the "trust chain" up to, but excluding, the root
certificate. All these are sent to the client on a new connection, as the client is the one who needs to verify trust. The server never verifies itself.
Like in mod_ssl
, you may configure more than one certificate for a VirtualHost
. As in:
<VirtalHost *:443>
ServerName b.net
TLSCertificate cert_A.pem key_A.pem
TLSCertificate cert_B.pem key_B.pem
...
</VirtualHost>
Both certificates need to be valid for host b.net
. But why would one do that?
The latest in SSL security are algorithms that use mathemagical named "Elliptic Curves" (EC). The seem to be pretty strong and are a lot smaller than the RSA
ones used so far. Not all clients might support them, though.
If cert_A
is an EC certificate and cert_B
is RSA, all capable clients will get the first and all legacy clients the second. mod_tls
will use the first one that is compatible.
Certificates obtained by ACME clients, such as certbot
can be used with mod_tls
as well. However their automatic rewriting of Apache httpd configurations does commonly assume a mod_ssl
. So, you have to check their documentation on how to best integrate them.
The ACME support in Apache itself, the module mod_md
, does work with mod_tls
just like with mod_ssl
. For example:
Listen 443
TLSEngine 443
MDomain b.net
<VirtalHost *:443>
ServerName b.net
...
</VirtualHost>
would be the minimal configuration to get a Let's Encrypt certificate for b.net
and serve that via mod_tls
.
The rustls
library supports only TLS versions and ciphers that are nowadays (2021) considered secure for the internet. That means, unless a new weakness is discovered, the default configuration is safe to use. Most people will not have to configure anything besides the port(s) to listen on. And certificates if they do not use mod_md
for that.
For people with special needs, there are ways to tweak protocol versions and ciphers and client orders. And those are described in more detail below.
The general gist of these configuration options is to give admins control, but allow for future enhancements. A server where a new TLS version can be run, should be able to. If some new cipher is added to TLSv1.3 in an attempt to overcome a newly found weakness, it should not require all servers to be reconfigured for its use. If an LTS installation gets new security features, maybe there is a reason for that.
If a cipher is deemed unsuitable by you, use TLSCiphersSuppress
to disable it.
There are performance differences between ciphers, depending on the hardware used/available. In most web server scenarios, the limitations seem to be mostly on the client side (battery life!). Since the set of supported ciphers in rustls
is carefully selected, the module will honor preferences as announced by a client by default.
Since clients always specify their ciphers ordered, the servers preferences normally have no effect. For scenarios where servers should override this (TLSHonorClientOrder off
), use TLSCiphersPrefer
to signal your preferences.
There are two way to name a TLS protocol version in mod_tls
:
- The defined names
TLSv1.2
andTLSv1.3
. - the numeric names
TLSv0xnnnn
withnnnn
being the hexadecimal version value as defined in the RFC standards for TLS.
In mod_tls
one configures the minimum TLS version to use by mentioning that version with an added +
. Having a host only support v1.3 (and higher) would be achieved through:
TLSProtocol TLSv1.3+
There are three ways to name a TLS cipher in mod_tls
:
- The IANA assigned name
which uses
_
to separate parts. Example:TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384
. - The OpenSSL name, using
-
as separator (for 1.2). Example:ECDHE-ECDSA-AES256-SHA384
. Such names often appear in documentation.mod_tls
defines them for all TLS v1.2 ciphers. For TLS v1.3 ciphers, names starting withTLS13_
are also supported. - The IANA assigned identifier, which is a 16-bit numeric value. This is what is used on the wire. Example:
0xc024
. You can use this inmod_tls
configurations asTLS_CIPHER_0xc024
.
The list of TLS ciphers supported in the rustls
library, can be found here.
It is considered a configuration failure to specify a cipher name that is unknown, e.g. not in the IANA registry at the time mod_tls
was last updated. If there is no name in mod_tls
for a cipher you need, use the identifier name, such as TLS_CIPHER_0xnnnn
. Those will always be accepted.
A warning will be written to the log if you configure preference for a known cipher that is not supported by rustls. For example:
TLSCiphersPrefer SSL_RSA_FIPS_WITH_DES_CBC_SHA
will log a WARNING
, because you seem to want something that rustls
is unable to deliver. It is not considered an error, because a preference is no guarantee that a certain cipher is used. Also, should rustls
for security reasons decide to drop a cipher, your mod_tls
configuration will not break.
You may suppress any known cipher without any warning or error. Either rustls
does not support it anyway, or mod_tls
will disable it, the outcome is the same.
If you suppress all ciphers supported for a TLS protocol version, that version is de-facto disabled. The only way this currently could make sense is if you wanted a server that only speaks TLSv1.2. This is not really recommended, but the world is a large place. So now, you know what happens if you do it. (Btw: if you want a server no longer supporting v1.2, you should configure TLSProtocol TLSv1.3+
and not mess with 1.2 ciphers at all).
Like mod_ssl
the module supports variables in the request environment (e.g. forwarded to CGI processing). There is a small set of variables that will always be set and a larger one that is only added when TLSOptions StdEnvVars
is configured.
Variable | TLSOption | Description |
---|---|---|
SSL_TLS_SNI |
* | the server name indicator (SNI) send by the client |
SSL_PROTOCOL |
* | the TLS protocol negotiated (TLSv1.2, TLSv1.3) |
SSL_CIPHER |
* | the name of the TLS cipher negotiated |
SSL_VERSION_INTERFACE |
StdEnvVars | the module version as mod_tls/n.n.n |
SSL_VERSION_LIBRARY |
StdEnvVars | the rustls-ffi version as crustls/n.n.n/rustls/n.n.n |
SSL_SECURE_RENEG |
StdEnvVars | always false since rustls does not support that feature |
SSL_COMPRESS_METHOD |
StdEnvVars | always NULL since rustls does not support that feature |
SSL_CIPHER_EXPORT |
StdEnvVars | always false as rustls does not support such ciphers |
SSL_CLIENT_VERIFY |
StdEnvVars | always NONE as client certificates are not supported |
SSL_SESSION_RESUMED |
StdEnvVars | either Resumed if a known TLS session id was presented by the client or Initial otherwise |
SSL_SERVER_CERT |
ExportCertData | the selected server certificate in PEM format. |
*) NI: Not Implemented
The variable SSL_SESSION_ID
is intentionally not supported as it contains sensitive information.
Client certificates are currently not supported my mod_tls
. The basic infrastructure is there, but
suitable Rust implementations for revocations checks on such certificates (CRL, OCSP) have so far
not been identified.
Offering client certificate authentication without a revocation mechanism is not an option, we feel.
The following configuration directives are available once mod_tls
is loaded into Apache:
TLSEngine [address:]port
defines on which address+port the module shall handle incoming connections.
This is set on a global level, not in individual VirtualHost
s. It will affect all VirtualHost
that match
the specified address/port. You can use TLSEngine
several times to use more than one address/port.
It is similar but different to the SSLEngine directive of mod_ssl. If you have VirtualHost
s, some on port 443, you need to set SSLEngine on
in every VirtualHost
that is defined for *:443
.
TLSCertificate cert_file [key_file]
adds a certificate file (PEM encoded) to a server/virtual host.
If you do not specify a separate key file, the key is assumed to also be found in the first file. You may add more than one certificate to a server/virtual host. The first certificate suitable for a client is then chosen.
The path can be specified relative to the server root.
TLSProtocol version+
specifies the minimum version of the TLS protocol to use.
The default is v1.2+
. Settings this to v1.3+
would disable TLSv1.2.
TLSCipherPrefer cipher(-list)
defines ciphers that are preferred.
This will not disable any ciphers supported by rustls
. If you specify a cipher that is completely unknown, the configuration will fail. If you specify a cipher that is known but not supported by rustls
, a warning will be logged but the server will continue.
TLSCipherSuppress cipher(-list)
defines ciphers that are not used.
This will not disable any unmentioned ciphers supported by rustls
. If you specify a cipher that is completely unknown, the configuration will fail. If you specify a cipher that is known but not supported by rustls
, a warning will be logged but the server will continue.
TLSHonorClientOrder on|off
determines if the order of ciphers supported by the client is honored. This is on
by default.
TLSOptions [+|-]option
is analog to SSLOptions
in mod_ssl
.
This can be set per directory/location and option
can be:
StdEnvVars
: adds more variables to the requests environment, as forwarded for example to CGI processing and other applications.ExportCertData
: adds certificate related variables to the request environment.Defaults
: resets all options to their default values.
See Variables to see exactly which values are set on an option.
Adding variables to a request environment adds overhead, especially when certificates need to be inspected and fields extracted. Therefore most variables are not set by default.
You can configure TLSOptions
per location or generally on a server/virtual host. Prefixing an option with -
disables this option while leaving others unchanged. A +
prefix is the same as writing the option without one.
The Defaults
value can be used to reset any options that are inherited from other locations or the virtual host/server. Example:
<Location /myplace/app>
TLSOptions Defaults StdEnvVars
...
</Location>
TLSProxyEngine on|off
is analog to SSLProxyEngine
.
This can be used in a server/virtual host or <Proxy>
section to enable the module for
outgoing connections using mod_proxy
.
TLSProxyCA file.pem
sets the root certificates to validate the backend server with.
TLSProxyProtocol version+
specifies the minimum version of the TLS protocol to use in proxy connections.
The default is v1.2+
. Settings this to v1.3+
would disable TLSv1.2.
TLSProxyCipherPrefer cipher(-list)
defines ciphers that are preferred for a proxy connection.
This will not disable any ciphers supported by rustls
. If you specify a cipher that is completely unknown, the configuration will fail. If you specify a cipher that is known but not supported by rustls
, a warning will be logged but the server will continue.
TLSProxyCipherSuppress cipher(-list)
defines ciphers that are not used for a proxy connection.
This will not disable any unmentioned ciphers supported by rustls
. If you specify a cipher that is completely unknown, the configuration will fail. If you specify a cipher that is known but not supported by rustls
, a warning will be logged but the server will continue.
TLSProxyMachineCertificate cert_file [key_file]
adds a certificate file (PEM encoded) to a proxy setup. The
certificate is used to authenticate against a proxied backend server.
If you do not specify a separate key file, the key is assumed to also be found in the first file. You may add more than one certificate to a proxy setup. The first certificate suitable for a proxy connection to a backend is then chosen.
The path can be specified relative to the server root.