Add some docs on security configuration
This commit is contained in:
@@ -201,3 +201,46 @@ IMPORTANT: The per-channel interceptors you pass in must either be bean instance
|
||||
== Observability
|
||||
|
||||
Spring gRPC provides an autoconfigured interceptor that can be used to provide observability to your gRPC clients.
|
||||
|
||||
== Security
|
||||
|
||||
If your remote gRPC server expects requests to be authenticated you will need to configure the client to provide authentication credentials.
|
||||
|
||||
=== Mutual TLS
|
||||
|
||||
Mutual TLS (mTLS) is a security protocol that requires both the client and the server to present certificates to each other.
|
||||
A Spring gRPC client can use mTLS by configuring the client in `application.properties`.
|
||||
The mechanism is through the use of https://docs.spring.io/spring-boot/reference/features/ssl.html#features.ssl.bundles[SSL Bundles] (from Spring Boot).
|
||||
Here's an example:
|
||||
|
||||
[source,properties]
|
||||
----
|
||||
spring.grpc.client.channels.my-channel.ssl.bundle=sslclient
|
||||
spring.grpc.client.channels.my-channel.negotiation-type=TLS
|
||||
spring.ssl.bundle.jks.sslclient.keystore.location=classpath:client.jks
|
||||
spring.ssl.bundle.jks.sslclient.keystore.password=secret
|
||||
spring.ssl.bundle.jks.sslclient.keystore.type=JKS
|
||||
spring.ssl.bundle.jks.sslclient.key.password=password
|
||||
----
|
||||
|
||||
The first two lines configure a channel named `my-channel` so that it has an SSL bundle named `sslclient`.
|
||||
The rest is the configuration of the SSL bundle itself, in this case using JKS encoding (other options are available).
|
||||
|
||||
=== HTTP Headers
|
||||
|
||||
Spring gRPC provides a couple of interceptor that can be used to provide security to your gRPC clients.
|
||||
There is one for Basic HHTP authentication and one for OAuth2 (bearer tokens).
|
||||
Here's an example of creating a channel that uses Basic HTTP authentication:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
@Lazy
|
||||
Channel basic(GrpcChannelFactory channels) {
|
||||
return channels.createChannel("my-channel", ChannelBuilderOptions.defaults()
|
||||
.withInterceptors(List.of(new BasicAuthenticationInterceptor("user", "password"))));
|
||||
}
|
||||
----
|
||||
|
||||
Usage of the bearer token interceptor is similar.
|
||||
You can look at the implementation of those interceptors to see how to create your own for custom headers.
|
||||
@@ -163,3 +163,67 @@ A `GrpcExceptionHandler` can be used to handle exceptions of a specific type, re
|
||||
If you include `spring-grpc-test` in your project, your gRPC server in a `@SpringBootTest` will be started in-process (i.e. not listening on a network port).
|
||||
All clients that connect to any server via the autoconfigured `GrpcChannelFactory` will be able to connect to it.
|
||||
You can switch the in-process server off by setting `spring.grpc.in-process.enabled` to `false`.
|
||||
|
||||
== Security
|
||||
|
||||
== Netty
|
||||
|
||||
The netty-based server supports TLS and mTLS out of the box.
|
||||
To configure the server you can configure an SSL Bundle in the `application.properties` or `application.yml` file.
|
||||
An example would be:
|
||||
|
||||
[source,properties]
|
||||
----
|
||||
spring.grpc.server.ssl.bundle=ssltest
|
||||
spring.ssl.bundle.jks.ssltest.keystore.location=classpath:test.jks
|
||||
spring.ssl.bundle.jks.ssltest.keystore.password=secret
|
||||
spring.ssl.bundle.jks.ssltest.keystore.type=JKS
|
||||
spring.ssl.bundle.jks.ssltest.key.password=password
|
||||
----
|
||||
|
||||
Here we configure a bundle named "ssltest" that uses a JKS keystore, similar to what you do with TLS support for https://docs.spring.io/spring-boot/how-to/webserver.html#howto.webserver.configure-ssl[Spring Boot in other areas].
|
||||
It is then applied to the gRPC server using the `spring.grpc.server.ssl.bundle` property.
|
||||
To use self-signed certificates, for testing purposes only, you also need to set `spring.grpc.server.ssl.secure=false`.
|
||||
|
||||
== Servlet
|
||||
|
||||
The servlet-based server supports any security configuration that the servlet container supports, including Spring Security.
|
||||
This means that you can easily implement your favourite authentication and authorization mechanisms.
|
||||
The server will reject unauthenticated requests with a 401 status code and unauthenticated requests with an invalid token with a 403 status code, as with a normal HTTP API.
|
||||
It will also send an appropriate `WWW-Authenticate` header, e.g. with the value `Bearer` to indicate that it is expecting a token (for example).
|
||||
The gRPC response will also contain a `Status` with the appropriate error code and message.
|
||||
For authorization checking, e.g. role-based access control, your `BindableService` beans can be annotated with `@PreAuthorize`.
|
||||
|
||||
N.B. if you customize the gRPC server call executors, you will need to ensure that you wrap them in a `DelegatingSecurityContextExecutor` (from Spring Security).
|
||||
Spring gRPC handles this for the default configuration.
|
||||
|
||||
Spring gRPC will automatically configure the gRPC server interceptors, and https://docs.spring.io/spring-boot/reference/web/spring-security.html[Spring Boot will provide defaults] for an `AuthenticationManager` and a `UserDetailsService`.
|
||||
Spring Boot will also provide default configuration for an OAuth2 resource server, if you set the classpath up correctly (following the https://docs.spring.io/spring-boot/reference/web/spring-security.html#web.security.oauth2.server[Spring Boot documentation]) which will be used to validate the token.
|
||||
You will want to provide your own `SecurityFilterChain` because the one provided by Spring Boot will enable CSRF protection for all endpoints, which is not compatible with gRPC.
|
||||
Here's an example with HTTP Basic authentication:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
return http.httpBasic(Customizer.withDefaults())
|
||||
.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated())
|
||||
.csrf(csrf -> csrf.disable())
|
||||
.build();
|
||||
}
|
||||
----
|
||||
|
||||
Disabling CSRF is fine if you have no other HTTP endpoints, but if you do you will need to be careful to disable CSRF protection for gRPC requests.
|
||||
To help you do that you can use a `GrpcServletRequest` to check if the request is a gRPC request and disable CSRF protection for it.
|
||||
Example:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
return http
|
||||
... // configure endpoint paths and authentication patterns
|
||||
.csrf(csrf -> csrf.ignoringRequestMatchers(GrpcServletRequest.all()))
|
||||
.build();
|
||||
}
|
||||
----
|
||||
|
||||
Reference in New Issue
Block a user