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]

Reply via email to