Sending client security username and password to Spring Boot Admin via Spring Cloud Kubernetes Service Discovery - spring-boot-admin

I am able to discover all services in my namespace and display them on spring boot admin but some of the clients have username/pass protection on some of the actuator endpoints. How can I get this information to spring boot admin via the kubernetes service discovery?

I was able to pass the client username and password by adding a tag in kubernetes service discovery of user.name and user.password for each client.
This isn't secure though because anyone with read access can read the metadata.
Is there a way to set a global user name and password for all clients?

This configuration will define a default user for the actuator. This default user and password is passed to each actuator call:
spring:
boot:
admin:
# AdminServerProperties used by basicAuthHttpHeadersProvider
instance-auth:
default-user-name: myactuatoruser
default-password: myactuatorpw
Remark:
You need at least spring-boot-admin:2.2.3. Check for the appearance of these properties the ConfigurationClass AdminServerProperties.
Otherwise you might define your own HttpHeaderProvider.
This is a copy of the 2.2.3 spring-boot-admin Code:
#Bean
#ConditionalOnMissingBean
public BasicAuthHttpHeaderProvider basicAuthHttpHeadersProvider(AdminServerProperties adminServerProperties) {
AdminServerProperties.InstanceAuthProperties instanceAuth = adminServerProperties.getInstanceAuth();
if (instanceAuth.isEnabled()) {
return new BasicAuthHttpHeaderProvider(instanceAuth.getDefaultUserName(),
instanceAuth.getDefaultPassword(), instanceAuth.getServiceMap());
}
else {
return new BasicAuthHttpHeaderProvider();
}
}

Related

Keycloak server embedded in a Spring Boot application with custom User Storage SPI

I have managed to set up a Keycloak server embedded in a Spring Boot Application successfully, following this tutorial:
Keycloak Embedded in a Spring Boot Application
in order to avoid setting it up manually.
Since I am dealing with an old implementation that already has it's own DB, I decided to use Keycloak's User Storage SPI to connect to an external postgresql DB and use this for authentication instead of Keycloak DB.
To do this, I followed the tutorial on Keycloak documentation, but it envolves a standalone Keycloak server, creating a .jar with the custom provider and injecting it to <pathToKeycloak>/standalone/deployments/.
I have created an implementation that works with a standalone Keycloak server, but now I want to include it to the embedded one. Is it possible to use a Keycloak server Embedded in a Spring Boot Application and also have an embedded custom User Storage Provider, to avoid setting up manually?
If you have already implemented the provider and the provider factory, you only need to declare the provider factory class in the resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory file.
Then you can log in to the administration console and enable user storage provider on the User Federation page.

How do I override per-instance settings in Spring Boot Admin when using Kubernetes discovery

I'm running a Spring Boot Admin server (2.2.2) and using Spring Cloud Kubernetes discovery (with specific service labels to filter) to detect my client apps. None of my client apps are using the explicit Spring Boot Admin Client dependency mechanism.
One of my client apps has a non-standard actuator URL and uses different security credentials to access those endpoints. I understand that I could use static spring cloud discovery with instance metadata to achieve this, but I'd rather use the kubernetes discovery process for all my client apps.
I think by using a custom ServiceInstanceConverter I might be able to override the management context path, but I couldn't see a way to inject custom security credentials via that route.
Is there a better way to customise this kubernetes-driven discovery process? (e.g. can I declare instance metadata somewhere in the client app so that it's picked up even though I'm using Kubernetes discovery - I got the sense from the Spring Boot Admin docs that setting admin properties in clients applied only to the "push registration from client to server" case rather than the "server discovers" case.)
As a related general question, Spring Boot Admin is presumably using some default credential values for accessing actuator endpoints - where are they set up?
Thanks in advance
Alan
I think I figured this out for myself, but in case it's useful to anyone else here is what I did:
Declared custom values for the instance metadata management.context-path, user.name and user.password under the annotations section of the Kubernetes service for my client application.
e.g.
kind: Service
apiVersion: v1
metadata:
name: foo-service
annotations:
# The following are used to support monitoring and administration
user.name: mySpecialUsername
user.password: mySpecialPassword
management.context-path: /foo/manage
From observation it seems that the default credentials assumed by a Spring Boot Administration server are admin/admin.
I don't think it is advisable to put passwords into a service manifest.
The SBA refdoc documents some properties to configure default or per service usernames/passwords.
If this is not an option, you can always add some custom headers to the requests that are sent to the clients:
#Bean
public HttpHeadersProvider customHttpHeadersProvider() {
return (instance) -> {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add("Authorization", "Basic bXlTcGVjaWFsVXNlcm5hbWU6bXlTcGVjaWFsUGFzc3dvcmQ=");
return httpHeaders;
};
}

Keycloak issuer validation and multi-tenancy approach

Let's say we have several micro-services. Each of them uses Keycloak authentication. We have also load balancer based on for ex. nginx which has external URLs and different routes to keycloak (for ex. in OpenShift it can be https://keycloak.rhel-cdk.10.1.2.2.xip.io). But internally this address can be inaccessible. Also having micro-service configuration dependent on the load balancer URL is a bit weird. What what be more appropriate is to use internal keycloak auth URL inside of the micro-services or even short URI. But in this case token will not be validated because of issuer validation problem. How to configure this in good and flexible manner? Can I simply override realmInfoUrl in order to change the validation? Can I define what issuer will be used for client based token.
Another problem is how to better handle multi-tenant scenario? First on the client side I guess we don't have any specific support for multi-tenancy. I should handle this manually by switching between different URLs/headers and use proper Config Resolver. On the server side I need to dynamically provide a proper KeycloakDeployment instance for each case. Any other recommendations?
Unfortunately Keycloak is too restrictive with its token validation according to the issuer ("iss") field in the token. It requires that the URL used to validate the token matches the URL in the "iss" field.
A while ago I have opened a JIRA ticket for that problem (vote for it!): https://issues.jboss.org/browse/KEYCLOAK-5045
In case this helps anyone out during the early stages of development, you can set the Host header to the keycloak url that your backend service will use during the validation of the token. This way, the generated token will contain your Host header url in the issuer field. In my sandbox, I had keycloak running on docker at keycloack:8080 and a functional test calling keycloack via localhost:8095 to request a token (direct grant). Before setting the Host header to keycloack:8080, the issuer field was being set to localhost:8095 and the token was failing the validation with the "Invalid token issuer" error, since the backend service connects to keycloak on keycloak:8080 and TokenVerifier.java does the following check.
public boolean test(JsonWebToken t) throws VerificationException {
if (this.realmUrl == null) {
throw new VerificationException("Realm URL not set");
} else if (!this.realmUrl.equals(t.getIssuer())) {
throw new VerificationException("Invalid token issuer. Expected '" + this.realmUrl + "', but was '" + t.getIssuer() + "'");
} else {
return true;
}
}
Reference: https://github.com/keycloak/keycloak-community/blob/master/design/hostname-default-provider.md

Spring Data Redis: Setting a Password

I have a need to provide a secure connection to Redis from my microservices.
So if I want to add the ability to support authentication so that a client can send an AUTH command with a password. See http://redis.io/topics/security
How can I do this with the Spring Data Redis implementation?
Just set the password on LettuceConnectionFactory or JedisConnectionFactory, depending on which driver you're using. If you use Spring Boot, set spring.redis.password=… in your application configuration (see Common application properties.
With the latest version of Jedis client, setting the password to the JedisConnectionFactory is deprecated. Therefore you should set the Redis authentication password to the RedisStandaloneConfiguration, RedisSentinelConfiguration or RedisClusterConfiguration.

spring cloud consul discovery acl for catalog services

I have ACL on for Consul, and have tried many ways to specify the token to use for service discovery. The config ACL token works fine, and the discovery ACL token works for registration (I can see my services in the Consul UI). I see the code for AgentConsulClient.agentServiceRegister() supports the token with this:
UrlParameters tokenParam = token != null ? new SingleUrlParameters("token", token) : null;
Nothing similar is supported in CatalogConsulClient, as far as I can tell. When called from Spring Cloud's ConsulDiscoveryClient, no token is passed, regardless of how it is set. Logs show the call being made without the token, and getting back a valid response with none of the registered services listed. I don't see how to have ACL on for registration but off for discovery. What am I missing? Is nobody actually using ACL if using discovery? (It works fine in the development environment with no ACL). Do I need to edit the source to add the token support from the agent service to the catalog service? Has anybody had success doing that?
BTW, could not tag this with spring-cloud-consul. Add it if you can.
ACL support for Consul catalog services is in consul-api v1.1.11 and will be (I hope) part of spring-cloud-consul 1.0.3.RELEASE. The 1.0.2.RELEASE version still uses consul-api-1.1.10. Update: confirmed to be in Camden.SR3.
Gradle:
'com.ecwid.consul:consul-api:1.1.11',
'org.springframework.cloud:spring-cloud-consul-dependencies:1.0.3.RELEASE'