java-spiffe/java-spiffe-provider/README.md

239 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.
The `SSLContext` is configured with a set of SPIFFE IDs that the current workload
will trust for TLS connections:
```
X509Source source = DefaultX509Source.newSource();
Supplier<Set<SpiffeId>> acceptedSpiffeIds = () -> Collections.singleton(SpiffeId.parse("spiffe://example.org/test"));
SslContextOptions options = SslContextOptions
.builder()
.x509Source(source)
.acceptedSpiffeIdsSupplier(acceptedSpiffeIds)
.build();
SSLContext sslContext = SpiffeSslContextFactory.getSslContext(options);
```
Alternatively, a different Workload API address can be used by passing it to the X509Source creation method.
```
X509SourceOptions sourceOptions = X509SourceOptions
.builder()
.spiffeSocketPath("unix:/tmp/agent.sock")
.build();
X509Source x509Source = DefaultX509Source.newSource(sourceOptions);
Supplier<Set<SpiffeId>> acceptedSpiffeIds = () -> Collections.singleton(SpiffeId.parse("spiffe://example.org/test"));
SslContextOptions sslContextOptions = SslContextOptions
.builder()
.acceptedSpiffeIdsSupplier(acceptedSpiffeIds)
.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`.
#### Java 8
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-linux-x86_64.jar` to `<java-home>/jre/lib/ext`.
In the case of testing the provider in Mac OS, the name of the jar will be `java-spiffe-provider-<version>-all-osx-x86_64.jar`.
#### Java 9+
The `java-spiffe-provider` jar should be on the classpath.
### Extend `java.security` properties file
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()
.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);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(SpiffeProviderConstants.ALGORITHM);
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 = DefaultX509Source.newSource();
// KeyManager gets the X.509 cert and private key from the X.509 SVID source
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 = DefaultX509Source.newSource();
KeyManager keyManager = new SpiffeKeyManager(x509Source);
Supplier<Set<SpiffeId>> acceptedSpiffeIds = () -> SpiffeIdUtils.toSetOfSpiffeIds("spiffe://example.org/workload-server", ',');
TrustManager trustManager = new SpiffeTrustManager(x509Source, acceptedSpiffeIds);
SslContextBuilder sslContextBuilder = SslContextBuilder
.forClient()
.trustManager(trustManager)
.keyManager(keyManager)
.clientAuth(ClientAuth.REQUIRE);
ManagedChannel channel = NettyChannelBuilder.forAddress("localhost", 9000)
.sslContext(GrpcSslContexts.configure(sslContextBuilder).build())
.build();
```
### Secure Socket Example:
See [HttpsServer example](src/test/java/io/spiffe/provider/examples/mtls/HttpsServer.java).
## More information
[Java Platform Security Developers Guide](https://docs.oracle.com/en/java/javase/14/security/)
[How to Implement a Provider in the Java Cryptography Architecture](https://docs.oracle.com/en/java/javase/14/security/howtoimplaprovider.html)
[Java PKI Programmer's Guide](https://docs.oracle.com/en/java/javase/14/security/java-pki-programmers-guide.html)