[
https://issues.apache.org/jira/browse/HDDS-17353?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Andrey Yarovoy updated HDDS-17353:
----------------------------------
Status: Patch Available (was: Open)
> Ozone client registers the BouncyCastle security provider at start-up
> although it doesn't need it
> -------------------------------------------------------------------------------------------------
>
> Key: HDDS-17353
> URL: https://issues.apache.org/jira/browse/HDDS-17353
> Project: Apache Ozone
> Issue Type: Bug
> Components: Ozone Client
> Reporter: Andrey Yarovoy
> Assignee: Andrey Yarovoy
> Priority: Major
> Labels: pull-request-available
>
> *Problem*
> Every Ozone client registers the BouncyCastle JCE provider at start-up, and a
> plain client uses almost none of it.
> - Client code creates a `SecurityConfig` just to read flags such as "is
> security enabled":
> `BasicRootedOzoneClientAdapterImpl`, `BasicOzoneClientAdapterImpl`,
> `XceiverClientGrpc`, `GrpcOmTransport`.
> It also calls `OzoneSecurityUtil.isSecurityEnabled(conf)`, from `RpcClient`
> and `XceiverClientCreator`.
> - Both of these call `SecurityConfig.initSecurityProvider(conf)`. With the
> default `hdds.security.provider=BC`,
> that runs `Security.addProvider(new BouncyCastleProvider())`.
> - BouncyCastle's jar is signed, so the JVM first verifies the jar, then loads
> the provider's classes.
> What uses the provider:
> - **Services:** key generation (`HDDSKeyGenerator`), certificate requests and
> signing (`CertificateSignRequest`,
> `SelfSignedCertificate`, `DefaultApprover`), and reading certificate paths
> from PEM files
> (`CertificateCodec.generateCertPathFromInputStream`, which uses
> `generateCertPath(in, "PEM")`). Only BouncyCastle
> supports that encoding.
> - **Clients:** one call. `ServiceInfoEx.getCaCertificates()` →
> `OzoneSecurityUtil.convertToX509()` →
> `CertificateCodec.readX509Certificate()` parses each CA certificate OM
> returns with
> `CertificateFactory.getInstance("X.509", "BC")`. Each is a single PEM
> certificate, which the JDK's own X.509
> factory parses as well.
> - **Other lookups:** `addProvider` appends BouncyCastle after the JDK
> providers, so lookups that don't name a
> provider already resolve to a JDK implementation. `STSTokenEncryption` in OM
> uses its own `BouncyCastleProvider`
> instance and doesn't depend on the registration.
> The cost shows up wherever many short-lived client JVMs start: YARN
> `ContainerLocalizer` JVMs, MapReduce tasks, CLI
> commands. A MapReduce job starts about four cold clients one after another on
> its critical path. Measured on a
> secure compose cluster (Kerberos + gRPC TLS, JDK 25), in fresh JVMs:
> - **On its own,** after loading the configuration: registering the provider
> takes **182 ms** median (176–224 ms,
> 9 JVMs) and loads about 1,000 extra classes.
> - **Inside a real cold client** (`FileSystem.get()` on `ofs://`,
> getFileStatus, a 64 KB read), with
> `-Xlog:class+load`: the stretch from loading `SecurityConfig` to the next
> Hadoop class takes **140–176 ms**. It
> starts with about 60 ms of jar verification and loads 498 BouncyCastle
> classes. Another 117 BouncyCastle classes
> load later, when the CA certificates are parsed.
> That's roughly 0.6 s per MapReduce job, from a client that generates no keys
> and signs nothing.
> *Proposed change*
> Register the provider only where it's needed:
> 1. `SecurityConfig`'s constructor and `OzoneSecurityUtil.isSecurityEnabled()`
> no longer call
> `initSecurityProvider()`.
> 2. `SecurityConfig.getProvider()` calls `initSecurityProvider()` before
> returning the provider name. Every service
> path that names the provider goes through it: key generation, certificate
> requests, signing. The explicit calls
> in `HddsServerUtil.getScmSecurityClient*` stay.
> 3. `CertificateCodec`:
> - `readX509Certificate()` / `getX509Certificate()` parse with the JDK's X.509
> factory
> (`CertificateFactory.getInstance("X.509")`);
> - `getCertFactory()` stays BouncyCastle-backed for `generateCertPath(in,
> "PEM")`, and registers the provider
> itself before the lookup, instead of throwing "BouncyCastle JCE provider not
> loaded".
> 4. Optional, for safety: every service registers the provider once at
> start-up (SCM, OM, datanode, Recon, S3
> Gateway). Then any service-side lookup that relies on the provider being
> registered, without going through
> `getProvider()` or `getCertFactory()`, keeps working.
> *Compatibility*
> - Services see no difference if step 4 is done. Without it, the risk is a
> service-side lookup that names BouncyCastle
> without going through `getProvider()` or `getCertFactory()`; none was found
> in `src/main`.
> - Clients parse CA certificates with the JDK instead of BouncyCastle. Ozone's
> certificates are standard X.509
> issued by SCM. Equality and trust checks compare encoded certificates, so the
> parser's provider doesn't matter. A
> certificate that BouncyCastle accepts but the JDK rejects would now fail on
> the client.
> - Applications that relied on Ozone registering BouncyCastle as a side
> effect, and then asked for it by name, would
> need to register it themselves. That was never documented behavior.
> - `hdds.security.provider` keeps its meaning for services.
> *Tests*
> - Unit: after creating a `SecurityConfig` and calling
> `OzoneSecurityUtil.isSecurityEnabled()`,
> `Security.getProvider("BC")` is null. This needs a fresh JVM or a test that
> removes the provider first, because
> registration is JVM-wide.
> - Unit: `CertificateCodec.readX509Certificate()` on an SCM-issued PEM
> certificate gives a certificate equal to the
> one BouncyCastle parses, `getEncoded()` included. `getCertFactory()` works
> when the provider isn't registered yet.
> - Unit: `SecurityConfig.getProvider()` leaves the provider registered.
> - Existing secure integration tests (key and certificate generation, CA
> rotation, gRPC TLS) pass unchanged.
> - Measurement: the cold-client probe on a secure compose cluster shows
> `FileSystem.get()` about 150 ms shorter, and
> no BouncyCastle classes in a client's class-load log.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]