227 lines
8.6 KiB
Markdown
227 lines
8.6 KiB
Markdown
# Java SPIFFE Provider
|
|
|
|
This module provides a Java Security Provider implementation supporting X.509-SVIDs and methods for
|
|
creating SSLContexts that are backed by the Workload API.
|
|
|
|
## Create an SSL Context backed by the Workload API
|
|
|
|
To create an SSL Context that uses a `X509Source` backed by the Workload API, having the environment variable
|
|
` SPIFFE_ENDPOINT_SOCKET` defined with the Workload API endpoint address, and the `ssl.spiffe.accept`
|
|
Security property defined in the `java.security` file containing the list of SPIFFE IDs that the current workload
|
|
will trust for TLS connections:
|
|
|
|
```
|
|
X509Source source = X509Source.newSource();
|
|
SslContextOptions options = SslContextOptions
|
|
.builder()
|
|
.x509Source(source)
|
|
.build();
|
|
|
|
SSLContext sslContext = SpiffeSslContextFactory.getSslContext(options);
|
|
```
|
|
|
|
See [HttpsServer example](src/test/java/io/spiffe/provider/examples/mtls/HttpsServer.java).
|
|
|
|
Alternatively, a different Workload API address can be used by passing it to the X509Source creation method, and a
|
|
`Supplier` of a Set of accepted SPIFFE IDs can be provided as part of the `SslContextOptions`:
|
|
|
|
```
|
|
X509SourceOptions sourceOptions = X509SourceOptions
|
|
.builder()
|
|
.spiffeSocketPath("unix:/tmp/agent.sock")
|
|
.build();
|
|
|
|
X509Source x509Source = X509Source.newSource(sourceOptions);
|
|
|
|
Supplier<Set<SpiffeId>> spiffeIdSetSupplier = () -> Collections.singleton(SpiffeId.parse("spiffe://example.org/test"));
|
|
|
|
SslContextOptions sslContextOptions = SslContextOptions
|
|
.builder()
|
|
.acceptedSpiffeIdsSupplier(spiffeIdSetSupplier)
|
|
.x509Source(x509Source)
|
|
.build();
|
|
|
|
SSLContext sslContext = SpiffeSslContextFactory.getSslContext(sslContextOptions);
|
|
```
|
|
|
|
See [HttpsClient example](src/test/java/io/spiffe/provider/examples/mtls/HttpsClient.java) that defines a Supplier for providing
|
|
the list of SPIFFE IDs from a file.
|
|
|
|
## Plug Java SPIFFE Provider into Java Security architecture
|
|
|
|
Java Security Providers are configured in the master security properties file `<java-home>/jre/lib/security/java.security`.
|
|
|
|
The way to register a java security provider is by specifying the custom `Provider` subclass name and the priority in the
|
|
following format:
|
|
|
|
```
|
|
security.provider.<n>=<className>
|
|
```
|
|
|
|
This declares a provider, and specifies its preference order `n`.
|
|
|
|
### Copy the JAR to the JVM extensions
|
|
|
|
For installing the JAR file containing the provider classes as a bundled extension in the java platform,
|
|
copy `build/libs/java-spiffe-provider-<version>-all.jar` to `<java-home>/jre/lib/ext`.
|
|
|
|
#### Register the SPIFFE Provider
|
|
|
|
The master security properties file can be extended. Create a file `java.security` with the following content:
|
|
|
|
```
|
|
# Add the spiffe provider, change the <n> for the correct consecutive number
|
|
security.provider.<n>=io.spiffe.provider.SpiffeProvider
|
|
|
|
# Configure the default KeyManager and TrustManager factory algorithms
|
|
ssl.KeyManagerFactory.algorithm=Spiffe
|
|
ssl.TrustManagerFactory.algorithm=Spiffe
|
|
|
|
# The list of spiffeIDs that will be authorized, separated by a pipe character
|
|
ssl.spiffe.accept=spiffe://example.org/workload | spiffe://example.org/workload2 | spiffe://example2.org/workload
|
|
```
|
|
|
|
In this `java.security` file:
|
|
|
|
* replace `<n>` following the order of the `# List of Providers` in the master file.
|
|
|
|
* replace the value of the custom property `ssl.spiffe.accept` with the SPIFFE IDs of the workloads that are
|
|
allowed to connect, separated by the pipe character.
|
|
***If the property is not present or if it's empty, no SPIFFE ID will be authorized.***
|
|
|
|
To pass your custom security properties file through the command line via system property when starting the JVM:
|
|
|
|
```
|
|
-Djava.security.properties=<path to java.security>
|
|
```
|
|
|
|
The properties defined in your custom properties file will override the properties in the master file.
|
|
|
|
The property `ssl.spiffe.accept` can also be defined through a System property passed as `-Dssl.spiffe.accept=`;
|
|
|
|
#### Accept all SPIFFE IDs
|
|
|
|
By default, only the SPIFFE IDs defined in the property `ssl.spiffe.accept` are accepted for a TLS connection. Thus,
|
|
if the property is empty or not defined, no SPIFFE ID will be accepted. To accept all SPIFFE IDs it should be used
|
|
the property `ssl.spiffe.acceptAll` and set as `true` in the Security properties file:
|
|
|
|
```
|
|
ssl.spiffe.acceptAll=true
|
|
```
|
|
|
|
or through a System property: `-Dssl.spiffe.acceptAll=true`.
|
|
|
|
It can also be configured when the SSL Context is created programmatically setting as `true` the option `acceptAnySpiffeId`
|
|
in the `SslContextOptions`:
|
|
|
|
```
|
|
SslContextOptions sslContextOptions = SslContextOptions
|
|
.builder()
|
|
.x509Source(x509Source)
|
|
.acceptAnySpiffeId(true)
|
|
.build();
|
|
|
|
SSLContext sslContext = SpiffeSslContextFactory.getSslContext(sslContextOptions);
|
|
```
|
|
|
|
#### Configure Workload API Socket Endpoint
|
|
|
|
The socket endpoint can be configured defining an environment variable named `SPIFFE_ENDPOINT_SOCKET`:
|
|
|
|
```
|
|
export SPIFFE_ENDPOINT_SOCKET=/tmp/agent.sock
|
|
```
|
|
|
|
## Use Cases
|
|
|
|
### Configure a Tomcat connector
|
|
|
|
***Prerequisite***: Having the SPIFFE Provider configured through the `java.security`.
|
|
|
|
A Tomcat TLS connector that uses the `Spiffe` KeyStore can be configured as follows:
|
|
|
|
```
|
|
<Connector
|
|
protocol="org.apache.coyote.http11.Http11NioProtocol"
|
|
port="8443" maxThreads="200"
|
|
scheme="https" secure="true" SSLEnabled="true"
|
|
keystoreFile="" keystorePass=""
|
|
keystoreType="Spiffe"
|
|
clientAuth="true" sslProtocol="TLS"/>
|
|
```
|
|
|
|
### Create mTLS GRPC server and client
|
|
|
|
Prerequisite: Having the SPIFFE Provided configured through the `java.security`.
|
|
|
|
A `GRPC Server` using an SSL context backed by the Workload API:
|
|
|
|
```
|
|
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(SpiffeProviderConstants.ALGORITHM, SpiffeProviderConstants.PROVIDER_NAME);
|
|
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(SpiffeProviderConstants.ALGORITHM, SpiffeProviderConstants.PROVIDER_NAME);
|
|
|
|
SslContextBuilder sslContextBuilder =
|
|
SslContextBuilder
|
|
.forServer(keyManagerFactory)
|
|
.trustManager(trustManagerFactory);
|
|
|
|
Server server = NettyServerBuilder.forPort(9000)
|
|
.sslContext(GrpcSslContexts.configure(sslContextBuilder)
|
|
.clientAuth(ClientAuth.REQUIRE)
|
|
.build())
|
|
.build();
|
|
|
|
server.start();
|
|
```
|
|
|
|
#### Configure it programmatically:
|
|
|
|
The `SpiffeKeyManager` and `SpiffeTrustManager` can be created without resorting to factories, providing the constructors
|
|
with a [X509Source instance](../java-spiffe-core/README.md#x509-source).
|
|
|
|
```
|
|
// create a new X.509 source using the default socket endpoint address
|
|
X509Source x509Source = X509Source.newSource();
|
|
KeyManager keyManager = new SpiffeKeyManager(x509Source);
|
|
|
|
// TrustManager gets the X509Source and the supplier of the Set of accepted SPIFFE IDs.
|
|
TrustManager trustManager = new SpiffeTrustManager(x509Source, () -> SpiffeIdUtils.toSetOfSpiffeIds("spiffe://example.org/workload-client", ','));
|
|
|
|
SslContextBuilder sslContextBuilder =
|
|
SslContextBuilder
|
|
.forServer(keyManager)
|
|
.trustManager(trustManager);
|
|
|
|
Server server = NettyServerBuilder.forPort(9000)
|
|
.addService(new GreetingServiceImpl())
|
|
.sslContext(GrpcSslContexts.configure(sslContextBuilder)
|
|
.clientAuth(ClientAuth.REQUIRE)
|
|
.build())
|
|
.build();
|
|
```
|
|
|
|
For the client, a `ManagedChannel` would be created using the `SpiffeKeyManager` and `SpiffeTrustManager` for configuring
|
|
the GRPC SSL context, analogous to the config for the Server:
|
|
|
|
```
|
|
X509Source x509Source = X509Source.newSource();
|
|
KeyManager keyManager = new SpiffeKeyManager(x509Source);
|
|
TrustManager trustManager = new SpiffeTrustManager(x509Source, () -> SpiffeIdUtils.toSetOfSpiffeIds("spiffe://example.org/workload-server", ','));
|
|
|
|
SslContextBuilder sslContextBuilder = SslContextBuilder
|
|
.forClient()
|
|
.trustManager(trustManager)
|
|
.keyManager(keyManager)
|
|
.clientAuth(ClientAuth.REQUIRE);
|
|
|
|
ManagedChannel channel = NettyChannelBuilder.forAddress("localhost", 9000)
|
|
.sslContext(GrpcSslContexts.configure(sslContextBuilder).build())
|
|
.build();
|
|
```
|
|
|
|
## References
|
|
|
|
[How to Implement a Provider in the Java Cryptography Architecture](https://docs.oracle.com/javase/8/docs/technotes/guides/security/crypto/HowToImplAProvider.html)
|
|
|
|
[Java PKI Programmer's Guide](https://docs.oracle.com/javase/8/docs/technotes/guides/security/certpath/CertPathProgGuide.html)
|