Micrometer with Prometheus Pushgateway - Add TLS Support - spring-boot

I have a Spring boot application with Prometheus Pushgateway using Micrometer, mainly based on this tutorial:
https://luramarchanjo.tech/2020/01/05/spring-boot-2.2-and-prometheus-pushgateway-with-micrometer.html
pom.xml has following related dependencies:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient_pushgateway</artifactId>
<version>0.16.0</version>
</dependency>
And application.properties file has:
management.metrics.export.prometheus.pushgateway.enabled=true
management.metrics.export.prometheus.pushgateway.shutdown-operation=PUSH
management.metrics.export.prometheus.pushgateway.baseUrl=localhost:9091
It is working fine locally in Dev environment while connecting to Pushgateway without any TLS. In our CI environment, Prometheus Pushgateway has TLS enabled. How do I configure TLS support and configure certs in this Spring boot application?

Due to the usage of TLS, you will need to customize a few Spring classes:
HttpConnectionFactory -> PushGateway -> PrometheusPushGatewayManager
A HttpConnectionFactory, is used by prometheus' PushGateway to create a secure connection, and then, create a PrometheusPushGatewayManager which uses the previous pushgateway.
You will need to implement the prometheus’ interface HttpConnectionFactory, I’m assuming you are able to create a valid javax.net.ssl.SSLContext object (if not, more details in the end¹).
HttpConnectionFactory example:
public class MyTlsConnectionFactory implements io.prometheus.client.exporter.HttpConnectionFactory {
#Override
public HttpURLConnection create(String hostUrl) {
// considering you can get javax.net.ssl.SSLContext or javax.net.ssl.SSLSocketFactory
URL url = new URL(hostUrl);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(sslContext.getSocketFactory());
return connection;
}
}
PushGateway and PrometheusPushGatewayManager:
#Bean
public HttpConnectionFactory tlsConnectionFactory() {
return new MyTlsConnectionFactory();
}
#Bean
public PushGateway pushGateway(HttpConnectionFactory connectionFactory) throws MalformedURLException {
String url = "https://localhost:9091"; // replace by your props
PushGateway pushGateway = new PushGateway(new URL(url));
pushGateway.setConnectionFactory(connectionFactory);
return pushGateway;
}
#Bean
public PrometheusPushGatewayManager tlsPrometheusPushGatewayManager(PushGateway pushGateway,
CollectorRegistry registry) {
// fill the others params accordingly (the important is pushGateway!)
return new PrometheusPushGatewayManager(
pushGateway,
registry,
Duration.of(15, ChronoUnit.SECONDS),
"some-job-id",
null,
PrometheusPushGatewayManager.ShutdownOperation.PUSH
);
}
¹If you face difficulty retrieving the SSLContext from java code, I recommend studying the library https://github.com/Hakky54/sslcontext-kickstart and https://github.com/Hakky54/mutual-tls-ssl (which shows how to apply it with different client libs).
Then, will be possible to generate SSLContext in java code in a clean way, e.g.:
String keyStorePath = "client.jks";
char[] keyStorePassword = "password".toCharArray();
SSLFactory sslFactory = SSLFactory.builder()
.withIdentityMaterial(keyStorePath, keyStorePassword)
.build();
javax.net.ssl.SSLContext sslContext = sslFactory.getSslContext();
Finally, if you need setup a local Prometheus + TLS environment for testing purposes, I recommend following the post:
https://smallstep.com/hello-mtls/doc/client/prometheus

Related

Java MS Graph SDK get GraphClient Using an Existing AccessToken (5.13.0 version)

Our Front End using PKCE flow and fetches a access Token. As per the old implementation (microsoft-graph#2.8.1 version) this below snippet gets a Graph Client using an existing access token. Now I cannot get the same working in the newer MS Graph Java SDK.
IGraphServiceClient client = GraphServiceClient.builder()
.authenticationProvider( request -> request.addHeader("Authorization", "Bearer " + tokenAuthentication.getToken().getTokenValue()) )
.buildClient();
Dependencies I have added to my project
<dependency>
<!-- Include the sdk as a dependency -->
<groupId>com.microsoft.graph</groupId>
<artifactId>microsoft-graph</artifactId>
<version>5.13.0</version>
</dependency>
<dependency>
<!-- This dependency is only needed if you are using the TokenCrendentialAuthProvider -->
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
<version>1.2.5</version>
</dependency>
finally got it working.. see below snippet..
IAuthenticationProvider authProvider = new IAuthenticationProvider() {
#Override
public CompletableFuture<String> getAuthorizationTokenAsync(URL requestUrl) {
CompletableFuture<String> future = new CompletableFuture<>();
future.complete(yourToken);
return future;
}
};
GraphServiceClient<Request> graphClient = GraphServiceClient
.builder()
.authenticationProvider(authProvider)
.buildClient();
return graphClient.me().buildRequest().get();

Is it possible to use gatling to test rsocket protocol over websocket?

I want to create performance test in gatling which check my server in spring. I use rsocket protocol(over websocket). I don't know how to establish connection and send any data through this protocol. Is gatling support it? If it isn't possible using gatling how can i simulate specific number of connection to my server. (maybe different library)
Server code
#Bean
public Mono<RSocketRequester> rSocketRequester(
RSocketStrategies rSocketStrategies,
RSocketProperties rSocketProps) {
return RSocketRequester.builder()
.rsocketStrategies(rSocketStrategies)
.connectWebSocket(getURI(rSocketProps));
}
private URI getURI(RSocketProperties rSocketProps) {
return URI.create(String.format("ws://localhost:%d%s",
rSocketProps.getServer().getPort(), rSocketProps.getServer().getMappingPath()));
}
properties file:
spring.rsocket.server.port=8080
spring.rsocket.server.transport=websocket
spring.rsocket.server.mapping-path=/rsocket
Examples endpoints where i want to send message:
#ConnectMapping
void joinToGame(RSocketRequester rSocketRequester) {
}
#MessageMapping("exampleEndpoint")
public void disconnect() {
}
maven dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-rsocket</artifactId>
</dependency>

How to specify Job executor to only use defined process engines?

I am setting up a Spring boot application with Camunda. I want to use a multi-tenant setup as defined in "https://docs.camunda.org/manual/7.5/user-guide/process-engine/multi-tenancy/#one-process-engine-per-tenant"
I have managed to setup multiple process engines via Java (so not with processes.xml, but coded), but there always seems to be a default Process Engine. How do i achieve a setup with only the process engines i defined?
Extra information:
each process engine uses its own datasource, derived via context
I want to avoid the default process engine, because it needs its own datasource. I dont want to setup a datasource/database for a process engine without a tenant. (if i don't setup a datasource for the default, there will be errors thrown by the job executor not getting a connection)
The setup I've tried is in the following block, but for some reason there always is a "default" processengine.
#Autowired
private ConfigurableListableBeanFactory beanFactory;
#Bean
#Order(4)
public void multipleCamunda(){
log.info("Starting Camunda Multitenant");
this.targetDatasources.entrySet().stream().forEach(entry -> {
String tenant = (String) entry.getKey();
DataSource tenantDatasource = (DataSource) entry.getValue();
SpringProcessEngineConfiguration standaloneProcessEngineConfiguration = new SpringProcessEngineConfiguration();
standaloneProcessEngineConfiguration.setDataSource(tenantDatasource);
standaloneProcessEngineConfiguration.setDatabaseSchemaUpdate("true");
standaloneProcessEngineConfiguration.setProcessEngineName(tenant);
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(fondsDatasource);
standaloneProcessEngineConfiguration.setTransactionManager(dataSourceTransactionManager);
standaloneProcessEngineConfiguration.setHistory(HistoryLevel.HISTORY_LEVEL_FULL.getName());
standaloneProcessEngineConfiguration.setJobExecutorDeploymentAware(true);
// deploy all processes from folder 'processes'
Resource[] resources = new Resource[0];
try {
resources = resourceLoader.getResources("classpath:/bpm/*.bpmn");
} catch (IOException e) {
e.printStackTrace();
}
standaloneProcessEngineConfiguration.setDeploymentResources(resources);
ProcessEngine processEngine = standaloneProcessEngineConfiguration.buildProcessEngine();
RuntimeContainerDelegate.INSTANCE.get().registerProcessEngine(processEngine);
beanFactory.registerSingleton("processEngine" + tenant,processEngine);
log.info("Started process Engine for " + tenant);
});
}
The maven dependencies i use:
<dependency>
<groupId>org.camunda.bpm</groupId>
<artifactId>camunda-engine-cdi</artifactId>
<version>7.10.0</version>
</dependency>
<dependency>
<groupId>org.camunda.bpm.javaee</groupId>
<artifactId>camunda-ejb-client</artifactId>
<version>7.10.0</version>
</dependency>
<dependency>
<groupId>org.camunda.bpm.springboot</groupId>
<artifactId>camunda-bpm-spring-boot-starter-
webapp</artifactId>
<version>3.2.1</version>
</dependency>
I assume that I need to define a #Bean for some kind of ConfigurationBean, but I can't figure out which one and how. Please tell me what configuration bean I need to autowire and how.
** Solution **
In order to stop the default initialization, you need to edit the application.yaml and add
camunda:
bpm:
enabled: false
Setting the same property in a CamdundaBpmProperties somehow doesn't seem to work.
When you have done this, the default startup won't occur and the process engine will start when adding a process engine via the code snippet posted above

Spring Boot Security/LDAP direct bind with user credentials

Is there a way to authenticate a user with Spring Boot Security/LDAP using the credentials instead of first binding with some functional credentials and then trying to bind the user?
I want to not need to use managerDn and managerPassword like in:
auth.ldapAuthentication()
.userSearchBase("")
.userSearchFilter("(samAccountName={0})")
.contextSource()
.url("ldap://<url>/<root>")
.managerDn("functionUser")
.managerPassword("password")
In my application, I implemented a custom UsernamePasswordAuthenticationProvider to authenticate a user against my own database or a remote LDAP depending on a flag set in the user record.
To authenticate against the remote LDAP, I used the code below. It worked for me, perhaps, it will work for you too :).
protected void validateCredentialsAgainstActiveDirectory(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) {
try {
LdapConfiguration config = ...;
/*
* We will create a new LDAP connection on the fly each time an AD user logs in.
* Hence we must disable caching properties to avoid NullPointerException later
* in AbstractContextSource.getAuthenticatedEnv().
*
*/
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(config.getUrl());
contextSource.setCacheEnvironmentProperties(false);
// Authenticate the user against the pre-configured userDnTemplate
BindAuthenticator bindAuthenticator = new BindAuthenticator(contextSource);
bindAuthenticator.setUserDnPatterns(new String[] { config.getUserDnTemplate() });
bindAuthenticator.authenticate(authentication);
} catch (BadCredentialsException ex) {
// Catch downstream exception to return our own message
throw new BadCredentialsException(SpringUtils.getMessage("security.login.error.bad-credentials"));
}
}
FYI, LdapConfiguration is my own custom class for reading configurations from a .yml file. In this file, I configured the url and the DN template of the LDAP server as following. You need to change that to fit your environment.
url: ldap://10.10.10.231:10389/dc=mycompany,dc=com
userDnTemplate: uid={0},ou=people
Don't forget to import the necessary dependencies in your project too.
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
</dependency>

How to use resilience4j on calling method?

I tried to use spring retry for Circuit breaking and retry as below and it is working as expected but issue is unable to configure "maxAttempts/openTimeout/resetTimeout" as env variables (error is should be constants). My question is how use resilience4j to achieve the below requirement?
also please suggest there is a way to pass env variables to "maxAttempts/openTimeout/resetTimeout".
#CircuitBreaker(value = {
MongoServerException.class,
MongoSocketException.class,
MongoTimeoutException.class
MongoSocketOpenException.class},
maxAttempts = 2,
openTimeout = 20000L ,
resetTimeout = 30000L)
public void insertDocument(ConsumerRecord<Long, GenericRecord> consumerRecord){
retryTemplate.execute(args0 -> {
LOGGER.info(String.format("Inserting record with key -----> %s", consumerRecord.key().toString()));
BasicDBObject dbObject = BasicDBObject.parse(consumerRecord.value().toString());
dbObject.put("_id", consumerRecord.key());
mongoCollection.replaceOne(<<BasicDBObject with id>>, getReplaceOptions());
return null;
});
}
#Recover
public void recover(RuntimeException t) {
LOGGER.info(" Recovering from Circuit Breaker ");
}
dependencies used are
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
You are not using resilience4j, but spring-retry.
You should adapt the title of your question.
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.waitDurationInOpenState(Duration.ofMillis(20000))
.build();
CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig);
CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("mongoDB");
RetryConfig retryConfig = RetryConfig.custom().maxAttempts(3)
.retryExceptions(MongoServerException.class,
MongoSocketException.class,
MongoTimeoutException.class
MongoSocketOpenException.class)
.ignoreExceptions(CircuitBreakerOpenException.class).build();
Retry retry = Retry.of("helloBackend", retryConfig);
Runnable decoratedRunnable = Decorators.ofRunnable(() -> insertDocument(ConsumerRecord<Long, GenericRecord> consumerRecord))
.withCircuitBreaker(circuitBreaker)
.withRetry(retry)
.decorate();
String result = Try.runRunnable(decoratedRunnable )
.recover(exception -> ...).get();

Resources