How to register more than one service per eureka client on eureka server? - netflix-eureka

I am starting learning Eureka APIs. I created a Eureka Server using spring boot and corresponding clients.
For starting I created a Eureka Client and registered with Server. The client was exposing a get service with no path
#GetMapping("/")
This works fine. As as soon as I get server Instance using DiscoveryClient as below, i can hit the service
List<ServiceInstance> list = client.getInstances(service);
if (list != null && list.size() > 0) {
URI uri = list.get(0).getUri();
String url=uri.toString();
if (url != null) {
return (new RestTemplate()).getForObject(url, String.class);
}
}
But I was not sure how to configure a service that has a path e.g.
#GetMapping("/greetings")
For now I can hardcode it as
List<ServiceInstance> list = client.getInstances(service);
if (list != null && list.size() > 0) {
URI uri = list.get(0).getUri();
String url=uri.toString()+"/greetings";
if (url != null) {
return (new RestTemplate()).getForObject(url, String.class);
}
}
Or register this as service name in bootstrap.yml at client as
spring:
application:
name: eurekaClient2/greetings
But is there some other way to do this?
What if i want to expose a get service and a post service?
How do i configure in that case?

From Eureka server, you will not get endpoints provided by a service whose instances are registered in Eureka server.
Response from Eureka server only contains information about availability of application, like hostname, IP address, etc.
Clients will have to hardcode the endpoints (or read them from properties) of other services they want to call. Eureka only provides the hostname (and other related details) of the registered instances.
spring.application.name is used as service-id. A service registers itself to Eureka server registry using this service-id.

Related

Keycloak authorization policy evaluation with spring cloud gateway

I am trying to use keycloak for authorization in spring cloud gateway. Keycloak does not provide any spring based adapters for policy enforcement for reactive stack.However, it does provide an endpoint for policy evaluation.
http://localhost:8080/realms/myrealm/protocol/openid-connect/token -- POST
Request:
grant_type:urn:ietf:params:oauth:grant-type:uma-ticket
response_mode:decision
audience:b2b
permission:spm_audit#GET
Header:
Authorization : bearer <JWT>
# spm_audit is the resource that I have created in keycloak and GET is the scope(using HTTP methods as api scopes).
RESPONSE:
{
"result": true
}
My problem is that above endpoint does not accept URI as permission in request body and I don't have any resource-name to request URL mapping at gateway.
One possible solution could be to use gateway's route id as resource name and pass it in permission
cloud:
gateway:
routes:
- id: spm_audit
uri: http://localhost:8001
predicates:
- Path=/gateway/spm/api/v1/registrations/{regUUID}/audit
filters:
- StripPrefix=1
metadata:
custom_scope: "test scope"
#Fetch the route info in auth manager
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); //(ServerWebExchange exchange)
route.getId();
The problem with this approch is that the route matching filters are applied after authorization filter and exchange.getAttribute(GATEWAY_ROUTE_ATTR) is coming as null, plus I will have to map all api paths in route configuration and will end up with a huge configuration file.
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, #Qualifier("keycloKWebClient")WebClient kycloakWebClient) {
http
.authorizeExchange()
.pathMatchers(
"/gateway/*/public/**")
.permitAll()
.and()
.authorizeExchange()
.anyExchange()
.access(keyalokAuthManager(kycloakWebClient))....#this is where I call policy evaluation api
https://www.keycloak.org/docs/latest/authorization_services/index.html#_service_authorization_api
What about using spring-security for resource-servers with a JWT decoder? It would be far more efficient as it would save many round trips to authorization-server (JWT decoder validates access-token with authorization-server public key downloaded once when policy enforcer requires a call to authorization-server for each and every incoming "non public" request).
You can map Keycloak "roles" to spring "granted authorities" and apply Role Based Access Control either with:
http.authorizeExchange().pathMatchers("/protected-route/foo").hasAuthority("ROLE_IN_KEYCLOAK") in your Java conf
#PreAuthorize("hasAnyAuthority('ROLE_IN_KEYCLOAK')") on your components methods.
For that, all you have to do is provide with an authentication converter with custom authorities converter:
interface AuthenticationConverter extends Converter<Jwt, Mono<JwtAuthenticationToken>> {}
interface AuthoritiesConverter extends Converter<Map<String, Object>, Collection<? extends GrantedAuthority>> {}
#Bean
AuthoritiesConverter authoritiesConverter() {
return claims -> {
final var realmAccess = (Map<String, Object>) jwt.getClaims().getOrDefault("realm_access", Map.of());
final var realmRoles = (Collection<String>) realmAccess.getOrDefault("roles", List.of());
// concat client roles to following stream if your app uses client roles in addition to realm ones
return realmRoles.stream().map(SimpleGrantedAuthority::new).toList();
}
}
#Bean
public AuthenticationConverter authenticationConverter(AuthoritiesConverter authoritiesConverter) {
return jwt -> Mono.just(new JwtAuthenticationToken(jwt, authoritiesConverter.convert(jwt.getClaims())));
}
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, AuthenticationConverter authenticationConverter) {
http.oauth2ResourceServer().jwt()
.jwtAuthenticationConverter(authenticationConverter);
}
You should also have a look at this repo: https://github.com/ch4mpy/spring-addons
There is a spring-addons-webflux-jwt-resource-server spring-boot (2.7 or later) starter which would save you quite some configuration hassle. Tutorials in this repo are using servlet variants of the libs, but the 4 starters (servlet/reactive with JWT-decoder/introspection) work the same and you should easily find what to adapt for your reactive app.

stompjs socket connection in kubernetes

I'm using stompjs, sock-client to establish a socket connection from a react-app to a springboot app. Both front-end and back-end are hosted in the same kubernetes cluster, but in different namespaces. Both are load balanced.
When running locally, this works fine:
UI:
const endpoint = 'http://localhost:8080/websocket';
let socket = new SockJs(endpoint);
stompClient = StompJs.over(socket);
stompClient.connect({}, this.onStompConnectSuccess, this.onStompErrorCallback);
Server side:
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket").setAllowedOrigins("http://localhost:3000").withSockJS();
}
However what URL do i use for the hosted versions? Setting the load-balanced URLs gives an error
Whoops! Lost connection to https://load-balanced-url/websocket

Flutter - How to Implement Network Service Discovery

I want to discover the deices which are running a specified service exposed to the local network, but I don't know how to.
For example I want local IP address and port number of the devices running service '_googlecast._tcp.'.
Is there any way to achieve this in Flutter ?
Thanks in advance
Check multicast DNS package: A Dart package to do service discovery over multicast DNS (mDNS), Bonjour, and Avahi.
Essentially, create a mDNS Client and get PTR record for the service, as:
const String name = '_googlecast._tcp.local';
final MDnsClient client = MDnsClient();
await client.start();
// Get the PTR recod for the service.
await for (PtrResourceRecord ptr in client
.lookup<PtrResourceRecord>(ResourceRecordQuery.serverPointer(name))) {
// Use the domainName from the PTR record to get the SRV record,
// which will have the port and local hostname.
// Note that duplicate messages may come through, especially if any
// other mDNS queries are running elsewhere on the machine.
await for (SrvResourceRecord srv in client.lookup<SrvResourceRecord>(
ResourceRecordQuery.service(ptr.domainName))) {
// Domain name will be something like "io.flutter.example#some-iphone.local._dartobservatory._tcp.local"
final String bundleId =
ptr.domainName; //.substring(0, ptr.domainName.indexOf('#'));
print('Dart observatory instance found at '
'${srv.target}:${srv.port} for "$bundleId".');
}
}
client.stop();
print('Done.');

feign request microservice using domain url

I am invoking microservice api using feign like this now:
Response<List<AppResponse>> apps = appController.getApps();
And this is server side:
#RequestMapping(value = "/app")
#FeignClient(name = "soa-service")
public interface IAppController {
#GetMapping(value = "/list")
Response<List<AppResponse>> getApps();
}
Because the client side and server side registerd to eureka(the eureka could find the internal registed ip address),the invoke works fine.My question is : when the client and server not in one network(maybe the client not registed to eureka and deploy to external net). Is it possible to invoke microservice using domain url like "www.api.example.com/app/list"?
ps:I know one solution to change my invoke using okhttpclient,but the problem is: I must change all old feign invoke to new okhttp rest invoke.
#RequestMapping(value = "/app")
#FeignClient(name = "soa-service", url = "http://www.api.example.com/app/list")
public interface IAppController {
#GetMapping(value = "/list")
Response<List<AppResponse>> getApps();
}

SSL certificate related issue while calling rest servcies

From client (eg: https://localhost:8080/) we are passing the certificate related values and calling the rest services ( hosted on different port - https://localhost:446/serviceName).
The issue is like, when we try to pass the certificate , SSL handshake is happening correctly (no error on debug) , but the certificate value is not passed to the service hosted on another port. Certificate value is accessed in server code by referring to (X509Certificate)httpReq.getAttribute("javax.servlet.request.X509Certificate");
Note : We use Spring boot application which intenally runs on tomcat server.And desired CA authorised certificates, keystore and truststore are present in resource path in both the projects (client and service hosted). In rest service project config file, the client-auth is set to false.
Sample code snippet used to call rest service:
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(restserviceTruststore)
.loadKeyMaterial(restserviceKeyStore, password).build();
HttpClient client = HttpClients.custom() .setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
.setSslcontext(sslContext).build();
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(client));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
HttpEntity<String> request = new HttpEntity<>(XML, headers);
response = restTemplate.postForObject(endpointURL, request, String.class);
Question:
1) From client what keystore and trustore should we need to pass to SSLContext? Is it server's keystore /truststore or clients?
2)What are the exact steps to be followed to resolve this issue.