nic-6443 opened a new pull request, #14047: URL: https://github.com/apache/apisix/pull/14047
### Description An SSL object whose `cert` and `key` are secret references can be served with the certificate of one secret version and the key of another, and the handshake then fails with `failed to set PEM priv key: SSL_use_PrivateKey() failed`. The cause is that each field is resolved and cached on its own. `radixtree_sni.set()` and the upstream client cert path call `fetch_secrets(value, true)`, which puts `cert` and `key` into two independent per-URI lrucache entries. The stale-refresh timer then refreshes them one key at a time, and each refresh is its own provider read: vault reads the whole path to return one field, and so do the AWS and GCP managers for JSON secrets. A rotation that lands between the two reads gives a mismatched pair, which then stays cached for the full TTL. The two entries can also drift apart for longer when one refresh fails, or when they were first fetched at different times. This PR fixes it in two places, and both are needed: - `secret.fetch_ssl_secrets()` resolves the `cert`, `key`, `certs` and `keys` references of an ssl object as one unit. They are fetched together, cached as one entry keyed by the sorted set of references, and swapped together when the stale entry is refreshed. The cache has the same TTL, stale-while-revalidate behaviour and `/secrets` version as the per-URI cache, so ssl objects that carry the same references still share one entry and switch together. The server cert path in `radixtree_sni.lua` and `resolve_upstream_client_cert()` now use it. Other fields of the ssl object, and plugin, consumer and `$env://` resolution, work as before. - vault, AWS and GCP keep each container they read (a vault path, a secret id) for one second, and concurrent readers in a worker wait for the read already in flight instead of starting their own. That way a cert and its key in the same container come from one read. This only ever removes provider requests. A behaviour change worth noting: if one of the cert/key references of an ssl object cannot be resolved, the handshake now fails with that resolution error. Before, the literal reference was passed on and the handshake failed later when parsing the PEM. Inline PEM ssl objects are not affected. A cert and key kept in two different containers are still two independent reads. The docs now say to keep them in the same secret. `t/secret/ssl-rotation.t` uses a mock vault that returns the next version of the pair on every read. It covers server handshakes and upstream mTLS requests across several refreshes, plus a cert fetched after its key had been cached separately. On master the handshakes and upstream requests fail. With only the provider change, the separate-age case still fails. With only the unit cache, the rotation cases still fail. ### Checklist - [x] I have explained the need for this PR and the problem it solves - [x] I have explained the changes or the new features added to this PR - [x] I have added tests corresponding to this change - [x] I have updated the documentation to reflect this change - [x] I have verified that this change is backward compatible -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
