Skip to content

Certificate compression #788

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 3, 2025
Merged

Certificate compression #788

merged 2 commits into from
Aug 3, 2025

Conversation

pluknet
Copy link
Contributor

@pluknet pluknet commented Jul 16, 2025

See also trac ticket #2546 for the previous patch series discussion and some numbers linked from there.

@pluknet pluknet requested a review from arut July 16, 2025 18:53
@pluknet pluknet self-assigned this Jul 16, 2025
@Maryna-f5 Maryna-f5 added this to the nginx-1.29.1 milestone Jul 16, 2025
zstream.next_out = tmp.data;
zstream.avail_out = tmp.len;

rc = deflateInit(&zstream, Z_DEFAULT_COMPRESSION);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the result of compression is cached and certificates are usually small (few kb), I think it would be better to use Z_BEST_COMPRESSION.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Z_DEFAULT_COMPRESSION - is a compromise between two evils: non-efficient and slow compression.
If proven useful, a separate directive can be introduced, similar to gzip_comp_level.

Comment on lines 683 to 684
ngx_log_error(NGX_LOG_WARN, ssl->log, 0,
"\"ssl_certificate_compression\" ignored, not supported");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A similar function ngx_ssl_early_data() has different text:

    ngx_log_error(NGX_LOG_WARN, ssl->log, 0,                                     
                  "\"ssl_early_data\" is not supported on this platform, "       
                  "ignored"); 

The same text exists in ngx_ssl_session_ticket_keys(), but the example above looks more relevant.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is not supported on this platform

Such diagnostic messages initially were used to mean the lack of OS feature,
such as IPv6/unix address family or socket options.
It looks somewhat odd to see this clause in relation to a library.
I believe "ssl_early_data" was the first such precedent.

Further, there are several cases of "is not available on this platform",
a slight modification of the above diagnostic message.

On the other side, it seems to be commonly used now throughout the nginx code to start fighting with it.
Notable exceptions are "ssl_stapling" and "ssl_session_ticket_key", both predate early data.
Ironically, "ssl_ocsp" uses "on this platform" although it has the same feature test as "ssl_stapling".

To sum up, I'm fine to change the message as suggested.
Note though this way it cannot be wrapped as consistently due to long naming,
which can be challenging in certain grep exercises.

#ifdef SSL_OP_NO_RX_CERTIFICATE_COMPRESSION

if (SSL_CTX_compress_certs(ssl->ctx, 0) != 0) {
SSL_CTX_clear_options(ssl->ctx, SSL_OP_NO_TX_CERTIFICATE_COMPRESSION);
Copy link
Contributor

@arut arut Jul 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not disable tx compression in the if (!enable) {...} block above instead of disabling it for all new ssl context and enabling it back here. Also, seems like enabling tx compression is completely safe anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not disable tx compression in the if (!enable) {...} block above instead of disabling it for all new ssl context and enabling it back here.

Because it is meant to be disabled by default, and this is done in a logically separate preliminary change.
Besides that, ngx_ssl_certificate_compression() doesn't fall under the "for all new ssl context" criteria.

Also, seems like enabling tx compression is completely safe anyway.

Depends on what you put in "safe".
OpenSSL documentation quite clearly describes what this option intends to do.

Although the current behaviour is indeed to send precompressed certificates only,
we cannot rely on such an implementation detail that can be changed in future.

Additionally, not using SSL_OP_NO_TX_CERTIFICATE_COMPRESSION as suggested
impends an overhead in parsing and processing the compress_certificate extension
in ClientHello and in the handshake state machine, which can be avoided with no effort.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example, see previous attempts to implement this feature,
where pre-compressed certificates was an option, in addition to using a callback.

@arut
Copy link
Contributor

arut commented Jul 29, 2025

server certificates, which are pre-compressed on on nginx startup.

double "on"

@pluknet
Copy link
Contributor Author

pluknet commented Jul 30, 2025

server certificates, which are pre-compressed on on nginx startup.

double "on"

Rewritten in a slightly different manner, thanks.

@arut
Copy link
Contributor

arut commented Jul 30, 2025

Regarding the first patch. There are 4 cases where certificate compression can be applied:

  1. server-side server certificate
  2. server-side client certificate
  3. proxy-side client certificate
  4. proxy-side server certificate

There are two main reasons why we don't want to enable certificate compression everywhere:

  • OpenSSL misses proper size checks for incoming compressed certificates
  • We don't want to spend CPU time in runtime to (un)compress certificates

We definitely want to disable receiving all compressed certificates due to the reasons above. These are pp. 2 and 4. And this is handled by SSL_OP_NO_RX_CERTIFICATE_COMPRESSION.

Sending compressed certificates is different. For p.1 we are able to pre-compress static server certificates. This is our main case. For case 3. there's no pre-compression though. The problem is that OpenSSL compresses client certificates automatically in runtime, see tls_construct_client_compressed_certificate(). No function calling is needed for this to happen. To avoid overhead, this case should also be explicitly disabled by SSL_OP_NO_TX_CERTIFICATE_COMPRESSION.

The main case 1 is then explicitly enabled when needed.

return NGX_OK;
}

#ifdef SSL_OP_NO_RX_CERTIFICATE_COMPRESSION
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the previous patch the other macro SSL_OP_NO_TX_CERTIFICATE_COMPRESSION was used . While for these purposes they are equally suitable, I suggest using the same macro. Moreover, this block itself uses the TX macro 2 lines below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, thanks.

@arut
Copy link
Contributor

arut commented Jul 31, 2025

Also, I'm not sure what you mean by "legitimate cases".

Other than this and the macro name, the first 2 patches look ok.

Comment on lines 714 to 723
{
return NGX_OK;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see a reason for this block, there's a return NGX_OK below.

Copy link
Contributor Author

@pluknet pluknet Jul 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this raises a question whether we should fail softly due to the optional nature of the feature and/or emit a warning.
Failing softly will make this consistent to SSL_CTX_compress_certs() error handling, although for different reasons.
Unlike in SSL_CTX_compress_certs(), registering the BoringSSL callback as currently implemented can barely fail on memory shortage. OTOH, we cannot rely on this as it may change in future hypothetically.

That said, a viable compromise is to emit a warning and let the user decide.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See updated in 50e7f09

@pluknet
Copy link
Contributor Author

pluknet commented Jul 31, 2025

Also, I'm not sure what you mean by "legitimate cases".

This is written out immediately below:

e.g., when none of compression algorithms is available or if the
resulting compressed size is larger than the original one

Copy link
Contributor

@arut arut left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The OpenSSL patches (1 & 2) look ok. Please remove the BoringSSL patch (3) and merge. We'll get back to this patch later on.

pluknet added 2 commits August 3, 2025 10:12
Certificate compression is supported since OpenSSL 3.2, it is enabled
automatically as negotiated in a TLSv1.3 handshake.

Using certificate compression and decompression in runtime may be
suboptimal in terms of CPU and memory consumption in certain typical
scenarios, hence it is disabled by default on both server and client
sides.  It can be enabled with ssl_conf_command and similar directives
in upstream as appropriate, for example:

    ssl_conf_command Options RxCertificateCompression;
    ssl_conf_command Options TxCertificateCompression;

Compressing server certificates requires additional support, this is
addressed separately.
The ssl_certificate_compression directive allows to send compressed
server certificates.  In OpenSSL, they are pre-compressed on startup.
To simplify configuration, the SSL_OP_NO_TX_CERTIFICATE_COMPRESSION
option is automatically cleared if certificates were pre-compressed.

SSL_CTX_compress_certs() may return an error in legitimate cases,
e.g., when none of compression algorithms is available or if the
resulting compressed size is larger than the original one, thus it
is silently ignored.

Certificate compression is supported in Chrome with brotli only,
in Safari with zlib only, and in Firefox with all listed algorithms.
It is supported since Ubuntu 24.10, which has OpenSSL with enabled
zlib and zstd support.

The actual list of algorithms supported in OpenSSL depends on how
the library was configured; it can be brotli, zlib, zstd as listed
in RFC 8879.
@pluknet pluknet merged commit 251444f into nginx:master Aug 3, 2025
1 check passed
@pluknet pluknet deleted the cert-comp branch August 3, 2025 15:15
@pluknet
Copy link
Contributor Author

pluknet commented Aug 3, 2025

Fur the record, only 2 first commits merged as requested in #788 (review)

@pluknet
Copy link
Contributor Author

pluknet commented Aug 5, 2025

Moved to #823

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants