Skip to content

Commit 02af451

Browse files
authored
Add support for TLS Certificate Compression (RFC 8879) (#13088)
New settings: proxy.config.ssl.server.cert_compression.algorithms proxy.config.ssl.client.cert_compression.algorithms proxy.config.ssl.server.cert_compression.cache is going to be added on next PR. New metrics: proxy.process.ssl.cert_compress.<alg> proxy.process.ssl.cert_compress.<alg>_failure proxy.process.ssl.cert_decompress.<alg> proxy.process.ssl.cert_decompress.<alg>_failure
1 parent 801e8d7 commit 02af451

25 files changed

Lines changed: 930 additions & 7 deletions

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,6 +541,8 @@ check_symbol_exists(SSL_error_description "openssl/ssl.h" HAVE_SSL_ERROR_DESCRIP
541541
check_symbol_exists(SSL_CTX_set_ciphersuites "openssl/ssl.h" TS_USE_TLS_SET_CIPHERSUITES)
542542
check_symbol_exists(SSL_CTX_set_keylog_callback "openssl/ssl.h" TS_HAS_TLS_KEYLOGGING)
543543
check_symbol_exists(SSL_CTX_set_tlsext_ticket_key_cb "openssl/ssl.h" HAVE_SSL_CTX_SET_TLSEXT_TICKET_KEY_CB)
544+
check_symbol_exists(SSL_CTX_add_cert_compression_alg "openssl/ssl.h" HAVE_SSL_CTX_ADD_CERT_COMPRESSION_ALG)
545+
check_symbol_exists(SSL_CTX_set1_cert_comp_preference "openssl/ssl.h" HAVE_SSL_CTX_SET1_CERT_COMP_PREFERENCE)
544546
check_symbol_exists(SSL_get_all_async_fds openssl/ssl.h TS_USE_TLS_ASYNC)
545547
check_symbol_exists(OSSL_PARAM_construct_end "openssl/params.h" HAVE_OSSL_PARAM_CONSTRUCT_END)
546548
check_symbol_exists(TLS1_3_VERSION "openssl/ssl.h" TS_USE_TLS13)

cmake/Findbrotli.cmake

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,28 @@
2121
#
2222
# brotli_FOUND
2323
# brotlicommon_LIBRARY
24+
# brotlidec_LIBRARY
2425
# brotlienc_LIBRARY
2526
# brotli_INCLUDE_DIRS
2627
#
2728
# and the following imported targets
2829
#
2930
# brotli::brotlicommon
31+
# brotli::brotlidec
3032
# brotli::brotlienc
3133
#
3234

3335
find_library(brotlicommon_LIBRARY NAMES brotlicommon)
36+
find_library(brotlidec_LIBRARY NAMES brotlidec)
3437
find_library(brotlienc_LIBRARY NAMES brotlienc)
3538
find_path(brotli_INCLUDE_DIR NAMES brotli/encode.h)
3639

37-
mark_as_advanced(brotli_FOUND brotlicommon_LIBRARY brotlienc_LIBRARY brotli_INCLUDE_DIR)
40+
mark_as_advanced(brotli_FOUND brotlicommon_LIBRARY brotlidec_LIBRARY brotlienc_LIBRARY brotli_INCLUDE_DIR)
3841

3942
include(FindPackageHandleStandardArgs)
40-
find_package_handle_standard_args(brotli REQUIRED_VARS brotlicommon_LIBRARY brotlienc_LIBRARY brotli_INCLUDE_DIR)
43+
find_package_handle_standard_args(
44+
brotli REQUIRED_VARS brotlicommon_LIBRARY brotlidec_LIBRARY brotlienc_LIBRARY brotli_INCLUDE_DIR
45+
)
4146

4247
if(brotli_FOUND)
4348
set(brotli_INCLUDE_DIRS "${brotli_INCLUDE_DIR}")
@@ -49,6 +54,12 @@ if(brotli_FOUND AND NOT TARGET brotli::brotlicommon)
4954
target_link_libraries(brotli::brotlicommon INTERFACE "${brotlicommon_LIBRARY}")
5055
endif()
5156

57+
if(brotli_FOUND AND NOT TARGET brotli::brotlidec)
58+
add_library(brotli::brotlidec INTERFACE IMPORTED)
59+
target_include_directories(brotli::brotlidec INTERFACE ${brotli_INCLUDE_DIRS})
60+
target_link_libraries(brotli::brotlidec INTERFACE brotli::brotlicommon "${brotlidec_LIBRARY}")
61+
endif()
62+
5263
if(brotli_FOUND AND NOT TARGET brotli::brotlienc)
5364
add_library(brotli::brotlienc INTERFACE IMPORTED)
5465
target_include_directories(brotli::brotlienc INTERFACE ${brotli_INCLUDE_DIRS})

doc/admin-guide/files/records.yaml.en.rst

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4280,6 +4280,40 @@ SSL Termination
42804280
``1`` Enables the use of Kernel TLS..
42814281
===== ======================================================================
42824282

4283+
.. ts:cv:: CONFIG proxy.config.ssl.server.cert_compression.algorithms STRING
4284+
:reloadable:
4285+
4286+
A comma-separated list of compression algorithms that |TS| is willing to
4287+
use for TLS Certificate Compression
4288+
(`RFC 8879 <https://datatracker.ietf.org/doc/html/rfc8879>`_) when |TS|
4289+
acts as a TLS server (i.e. accepting connections from clients). When a
4290+
connecting client advertises support for one of these algorithms, |TS| will
4291+
send its certificate in compressed form, reducing handshake size.
4292+
4293+
Supported values: ``zlib``, ``brotli``, ``zstd``. The order determines the
4294+
server's preference. An empty value (the default) disables certificate
4295+
compression.
4296+
4297+
``brotli`` and ``zstd`` are only available when |TS| is compiled with the
4298+
corresponding libraries.
4299+
4300+
Example::
4301+
4302+
proxy.config.ssl.server.cert_compression.algorithms: zlib,brotli
4303+
4304+
.. ts:cv:: CONFIG proxy.config.ssl.client.cert_compression.algorithms STRING
4305+
:reloadable:
4306+
4307+
A comma-separated list of compression algorithms that |TS| advertises for
4308+
TLS Certificate Compression
4309+
(`RFC 8879 <https://datatracker.ietf.org/doc/html/rfc8879>`_) when |TS|
4310+
acts as a TLS client (i.e. connecting to origin servers). When the origin
4311+
supports one of these algorithms, |TS| will accept and decompress the
4312+
certificate.
4313+
4314+
Supported values: ``zlib``, ``brotli``, ``zstd``. An empty value (the
4315+
default) disables certificate compression.
4316+
42834317
Client-Related Configuration
42844318
----------------------------
42854319

doc/admin-guide/monitoring/statistics/core/ssl.en.rst

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,3 +389,69 @@ Stats for Pre-warming TLS Tunnel is registered dynamically. The ``POOL`` in belo
389389
:type: counter
390390

391391
Represents the total number of pre-warming retry.
392+
393+
.. ts:stat:: global proxy.process.ssl.cert_compress.zlib integer
394+
:type: counter
395+
396+
The number of times a server certificate was compressed with zlib during a
397+
TLS handshake.
398+
399+
.. ts:stat:: global proxy.process.ssl.cert_compress.zlib_failure integer
400+
:type: counter
401+
402+
The number of times zlib compression of a server certificate failed.
403+
404+
.. ts:stat:: global proxy.process.ssl.cert_decompress.zlib integer
405+
:type: counter
406+
407+
The number of times a certificate received from an origin server was
408+
decompressed with zlib.
409+
410+
.. ts:stat:: global proxy.process.ssl.cert_decompress.zlib_failure integer
411+
:type: counter
412+
413+
The number of times zlib decompression of a certificate failed.
414+
415+
.. ts:stat:: global proxy.process.ssl.cert_compress.brotli integer
416+
:type: counter
417+
418+
The number of times a server certificate was compressed with Brotli during a
419+
TLS handshake.
420+
421+
.. ts:stat:: global proxy.process.ssl.cert_compress.brotli_failure integer
422+
:type: counter
423+
424+
The number of times Brotli compression of a server certificate failed.
425+
426+
.. ts:stat:: global proxy.process.ssl.cert_decompress.brotli integer
427+
:type: counter
428+
429+
The number of times a certificate received from an origin server was
430+
decompressed with Brotli.
431+
432+
.. ts:stat:: global proxy.process.ssl.cert_decompress.brotli_failure integer
433+
:type: counter
434+
435+
The number of times Brotli decompression of a certificate failed.
436+
437+
.. ts:stat:: global proxy.process.ssl.cert_compress.zstd integer
438+
:type: counter
439+
440+
The number of times a server certificate was compressed with zstd during a
441+
TLS handshake.
442+
443+
.. ts:stat:: global proxy.process.ssl.cert_compress.zstd_failure integer
444+
:type: counter
445+
446+
The number of times zstd compression of a server certificate failed.
447+
448+
.. ts:stat:: global proxy.process.ssl.cert_decompress.zstd integer
449+
:type: counter
450+
451+
The number of times a certificate received from an origin server was
452+
decompressed with zstd.
453+
454+
.. ts:stat:: global proxy.process.ssl.cert_decompress.zstd_failure integer
455+
:type: counter
456+
457+
The number of times zstd decompression of a certificate failed.

include/iocore/net/SSLMultiCertConfigLoader.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ class SSLMultiCertConfigLoader
109109
virtual bool _set_npn_callback(SSL_CTX *ctx);
110110
virtual bool _set_alpn_callback(SSL_CTX *ctx);
111111
virtual bool _set_keylog_callback(SSL_CTX *ctx);
112+
virtual bool _enable_cert_compression(SSL_CTX *ctx);
112113
virtual bool _enable_ktls(SSL_CTX *ctx);
113114
virtual bool _enable_early_data(SSL_CTX *ctx);
114115
};

include/tscore/ink_config.h.cmake.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ const int DEFAULT_STACKSIZE = @DEFAULT_STACK_SIZE@;
186186
#cmakedefine01 HAVE_SSL_ERROR_DESCRIPTION
187187
#cmakedefine01 HAVE_OSSL_PARAM_CONSTRUCT_END
188188
#cmakedefine01 TS_USE_TLS_SET_CIPHERSUITES
189+
#cmakedefine01 HAVE_SSL_CTX_ADD_CERT_COMPRESSION_ALG
190+
#cmakedefine01 HAVE_SSL_CTX_SET1_CERT_COMP_PREFERENCE
189191

190192
#define TS_BUILD_CANONICAL_HOST "@CMAKE_HOST@"
191193

src/iocore/net/CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ add_library(
6161
TLSSessionResumptionSupport.cc
6262
TLSSNISupport.cc
6363
TLSTunnelSupport.cc
64+
TLSCertCompression.cc
6465
UDPEventIO.cc
6566
UDPIOEvent.cc
6667
UnixConnection.cc
@@ -116,6 +117,21 @@ if(TS_USE_LINUX_IO_URING)
116117
target_link_libraries(inknet PUBLIC ts::inkuring)
117118
endif()
118119

120+
# Link cert compression libraries after OpenSSL so that OpenSSL include
121+
# directories appear first in the search order, preventing broad system
122+
# include paths (e.g. from Homebrew's zstd) from shadowing them.
123+
if(HAVE_SSL_CTX_ADD_CERT_COMPRESSION_ALG)
124+
target_sources(inknet PRIVATE TLSCertCompression_zlib.cc)
125+
if(HAVE_BROTLI_ENCODE_H)
126+
target_sources(inknet PRIVATE TLSCertCompression_brotli.cc)
127+
target_link_libraries(inknet PRIVATE brotli::brotlienc brotli::brotlidec)
128+
endif()
129+
if(HAVE_ZSTD_H)
130+
target_sources(inknet PRIVATE TLSCertCompression_zstd.cc)
131+
target_link_libraries(inknet PRIVATE zstd::zstd)
132+
endif()
133+
endif()
134+
119135
if(BUILD_TESTING)
120136
# libinknet_stub.cc is need because GNU ld is sensitive to the order of static libraries on the command line, and we have a cyclic dependency between inknet and proxy
121137
add_executable(

src/iocore/net/P_SSLConfig.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ struct SSLConfigParams : public ConfigInfo {
8888
unsigned char alpn_protocols_array[MAX_ALPN_STRING];
8989
int alpn_protocols_array_size = 0;
9090

91+
char *server_cert_compression_algorithms;
92+
char *client_cert_compression_algorithms;
93+
9194
char *server_tls13_cipher_suites;
9295
char *client_tls13_cipher_suites;
9396
char *server_groups_list;

src/iocore/net/SSLClientUtils.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@
2424
#include "P_SSLNetVConnection.h"
2525
#include "P_TLSKeyLogger.h"
2626
#include "SSLSessionCache.h"
27+
#include "TLSCertCompression.h"
2728
#include "iocore/net/YamlSNIConfig.h"
2829
#include "iocore/net/SSLDiags.h"
2930
#include "tscore/ink_config.h"
31+
#include "tscore/SimpleTokenizer.h"
3032
#include "tscore/Filenames.h"
3133
#include "tscore/X509HostnameValidator.h"
3234

@@ -247,6 +249,18 @@ SSLInitClientContext(const SSLConfigParams *params)
247249
}
248250
#endif
249251

252+
if (params->client_cert_compression_algorithms) {
253+
std::vector<std::string> algs;
254+
SimpleTokenizer tok(params->client_cert_compression_algorithms, ',');
255+
for (const char *token = tok.getNext(); token; token = tok.getNext()) {
256+
algs.emplace_back(token);
257+
}
258+
if (register_certificate_compression_preference(client_ctx, algs) != 1) {
259+
SSLError("invalid client certificate compression algorithm list in %s", ts::filename::RECORDS);
260+
goto fail;
261+
}
262+
}
263+
250264
SSL_CTX_set_verify_depth(client_ctx, params->client_verify_depth);
251265
if (SSLConfigParams::init_ssl_ctx_cb) {
252266
SSLConfigParams::init_ssl_ctx_cb(client_ctx, false);

src/iocore/net/SSLConfig.cc

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ SSLConfigParams::reset()
115115
clientKeyPath = clientCACertFilename = clientCACertPath = cipherSuite = client_cipherSuite = dhparamsFile = serverKeyPathOnly =
116116
clientKeyPathOnly = clientCertPathOnly = nullptr;
117117
ssl_ocsp_response_path_only = nullptr;
118+
server_cert_compression_algorithms = nullptr;
119+
client_cert_compression_algorithms = nullptr;
118120
server_tls13_cipher_suites = nullptr;
119121
client_tls13_cipher_suites = nullptr;
120122
server_groups_list = nullptr;
@@ -151,11 +153,13 @@ SSLConfigParams::cleanup()
151153

152154
ssl_ocsp_response_path_only = static_cast<char *>(ats_free_null(ssl_ocsp_response_path_only));
153155

154-
server_tls13_cipher_suites = static_cast<char *>(ats_free_null(server_tls13_cipher_suites));
155-
client_tls13_cipher_suites = static_cast<char *>(ats_free_null(client_tls13_cipher_suites));
156-
server_groups_list = static_cast<char *>(ats_free_null(server_groups_list));
157-
client_groups_list = static_cast<char *>(ats_free_null(client_groups_list));
158-
keylog_file = static_cast<char *>(ats_free_null(keylog_file));
156+
server_cert_compression_algorithms = static_cast<char *>(ats_free_null(server_cert_compression_algorithms));
157+
client_cert_compression_algorithms = static_cast<char *>(ats_free_null(client_cert_compression_algorithms));
158+
server_tls13_cipher_suites = static_cast<char *>(ats_free_null(server_tls13_cipher_suites));
159+
client_tls13_cipher_suites = static_cast<char *>(ats_free_null(client_tls13_cipher_suites));
160+
server_groups_list = static_cast<char *>(ats_free_null(server_groups_list));
161+
client_groups_list = static_cast<char *>(ats_free_null(client_groups_list));
162+
keylog_file = static_cast<char *>(ats_free_null(keylog_file));
159163

160164
cleanupCTXTable();
161165
reset();
@@ -494,6 +498,13 @@ SSLConfigParams::initialize()
494498
server_groups_list = ats_stringdup(rec_str);
495499
}
496500

501+
if (auto rec_str{RecGetRecordStringAlloc("proxy.config.ssl.server.cert_compression.algorithms")}; rec_str) {
502+
server_cert_compression_algorithms = ats_stringdup(rec_str);
503+
}
504+
if (auto rec_str{RecGetRecordStringAlloc("proxy.config.ssl.client.cert_compression.algorithms")}; rec_str) {
505+
client_cert_compression_algorithms = ats_stringdup(rec_str);
506+
}
507+
497508
// ++++++++++++++++++++++++ Client part ++++++++++++++++++++
498509
client_verify_depth = 7;
499510

0 commit comments

Comments
 (0)