Skip to content
/ mod_tls Public

rustls based TLS for Apache httpd

License

Apache-2.0, Unknown licenses found

Licenses found

Apache-2.0
LICENSE
Unknown
COPYING
Notifications You must be signed in to change notification settings

icing/mod_tls

Repository files navigation

mod_tls - rust based TLS for Apache httpd

This repository contains mod_tls, a module for Apache httpd that uses rustls to provide a memory safe TLS implementation in Rust.

Status

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.

Platforms

  • rustls-ffi v0.14.0
  • Apache 2.4.48 or later
  • build system: autoconf/automake

Installation from source

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

OCSP Stapling with mod_tls

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.

Tests

If you want to run the test suite, you need:

  • curl and openssl in your path
  • Some Python packages: pytest, cryptography
> make test

History

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-ffiAPI 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.

Comparison with mod_ssl

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

Configuration

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.

Loading

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.

Peace and Harmony

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.

Handling 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.

https: with mod_ssl

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.

https: with mod_tls

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 VirtualHosts 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.

https: with mod_ssl and mod_tls?

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 VirtualHosts for particular addresses. That seems quite an edge configuration, but it is doable.

Handling certificates

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.

ACME (Let's Encrypt) certificates

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.

What else to configure for?

security

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.

performance

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.

Protocol Versions

There are two way to name a TLS protocol version in mod_tls:

  1. The defined names TLSv1.2 and TLSv1.3.
  2. the numeric names TLSv0xnnnn with nnnn 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+

Cipher Names

There are three ways to name a TLS cipher in mod_tls:

  1. The IANA assigned name which uses _ to separate parts. Example: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384.
  2. 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 with TLS13_ are also supported.
  3. 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 in mod_tls configurations as TLS_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).

Variables

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

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.

Directives

The following configuration directives are available once mod_tls is loaded into Apache:

TLSEngine

TLSEngine [address:]port defines on which address+port the module shall handle incoming connections.

This is set on a global level, not in individual VirtualHosts. 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 VirtualHosts, some on port 443, you need to set SSLEngine on in every VirtualHost that is defined for *:443.

TLSCertificate

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

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

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

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

TLSHonorClientOrder on|off determines if the order of ciphers supported by the client is honored. This is on by default.

TLSOptions

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

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

TLSProxyCA file.pem sets the root certificates to validate the backend server with.

TLSProxyProtocol

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

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

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

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.

About

rustls based TLS for Apache httpd

Resources

License

Apache-2.0, Unknown licenses found

Licenses found

Apache-2.0
LICENSE
Unknown
COPYING

Stars

Watchers

Forks

Packages

No packages published