Name resolution failure with Spring Gateway and Eureka - java

Good day,
At this moment I am working on a very simple gateway that (for now) only needs to redirect incoming HTTP POST and GET requests.
THE SETUP:
The Eureka Server: the location where my Spring Boot microservices are registered;
The Spring Gateway: maps all incoming HTTP POST and GET requests and routes them to the proper microservice;
The Spring Boot microservices: doing just some thingies as requested :)
Note: I'm kinda new to this gateway stuff, just you know :).
The microservice is registered fine with the Eureka server. Its webbased GUI shows me that the instance "MY-MICRO-SERVICE" is registered with the Eureka server. Other (Spring Boot) services can use that name ("MY-MICRO-SERVICE") without issues, so for them it works fine. Just this gateway can't handle the instance name; it seems it only accepts IP addresses (which I just want to prevent, as the microservice can change from servers and therefor their IP address). And the Eureka server is not configured to only allow/use IP addresses.
THE ISSUE:
All runs smooth when the Gateway has a route that holds an IP address of the microservice. But what I want is to let the Gateway resolve the service ID from the Eureka server. And if I do that, it throws me a java.net.UnknownHostException: MY-MICRO-SERVICE: Temporary failure in name resoultion.
THE QUESTION:
Now why can't I use the name of the Spring application "MY-MICRO-SERVICE" (being the registered Spring Boot microservice) in the Spring Gateway (while that construction works fine when used in other microservices)? Can't a Yaml config file handle such instance names, just only IP addresses?
THE DETAILS
The gateway is mostly configured via a yaml config file. There is only one simple Java class that kicks off the gateway application. The routing is all set in the yaml config file.
The Spring Gateway application class
#SpringBootApplication
#EnableEurekaClient
public class MyGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(MyGatewayApplication.class, args);
}
}
The Gateway Yaml configuration file (application.yml)
spring:
application:
name: my-gateway
cloud:
gateway:
discovery:
locator:
lowerCaseServiceId: true
enabled: true
globalcors:
corsConfigurations:
'[/**]':
allowedOrigins: "*"
allowedMethods:
- GET
- POST
routes:
- id: my_route
uri: http://MY-MICRO-SERVICE
predicates:
- Path=/test/**
server:
port: 8999
info:
app:
properties: dev
The Error
java.net.UnknownHostException: MY-MICRO-SERVICE: Temporary failure in name resolution
at java.base/java.net.Inet6AddressImpl.lookupAllHostAddr(Native Method) ~[na:na]
at java.base/java.net.InetAddress$PlatformNameService.lookupAllHostAddr(InetAddress.java:929) ~[na:na]
at java.base/java.net.InetAddress.getAddressesFromNameService(InetAddress.java:1515) ~[na:na]
at java.base/java.net.InetAddress$NameServiceAddresses.get(InetAddress.java:848) ~[na:na]
at java.base/java.net.InetAddress.getAllByName0(InetAddress.java:1505) ~[na:na]
at java.base/java.net.InetAddress.getAllByName(InetAddress.java:1364) ~[na:na]
at java.base/java.net.InetAddress.getAllByName(InetAddress.java:1298) ~[na:na]
at java.base/java.net.InetAddress.getByName(InetAddress.java:1248) ~[na:na]
at io.netty.util.internal.SocketUtils$8.run(SocketUtils.java:146) ~[netty-common-4.1.36.Final.jar:4.1.36.Final]
...

Issue has been fixed.
I changed the "http" to "lb" protocol and that fixed my issue. To my understanding, "lb" stands for LoadBalancing. I have no loadbalancer active on my local machine, but anyway: this works.
- POST
routes:
- id: my_route
uri: lb://MY-MICRO-SERVICE
predicates:
- Path=/test/**

Related

Ingress not working on Kubernetes Infrastructure

I created a microservices infrastructure on Kubernetes (version: 1.20.9-gke.1001) on Google Cloud Platform using the Spring Cloud.
First I created the following deployments: Eureka (service discovery), Zuul (API Gateway), Zipkin (Distributed tracing system), User Service and Auth Service.
Then I created the following services: eureka-service with “Cluster IP” type which allows other pods to connect to Eureka, zipkin-service with “Cluster IP” type which allows other pods to connect to Zipkin and loadbalancer-service with “External load balancer” type which is connected to the Zuul.
Finally I tried to create an Ingress using the attached yaml file but at every request I tried to execute, I received the following error: “response 404 (backend NotFound), service rules for the path non-existent”. While if I try to invoke the APIs using the external IP of the loadbalancer-service the backend works correctly.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: project-ingress
annotations:
kubernetes.io/ingress.class: "gce"
spec:
rules:
- host: project.test.com
http:
paths:
- path: /*
pathType: ImplementationSpecific
backend:
service:
name: loadbalancer-service
port:
number: 8765
Do you have any idea why the Ingress is not working?
Also I would need to expose the services with HTTPS, could you kindly explain to me how to use an existing security certificate in the Ingress?
Thanks, this is my first experience with Kubernetes and of course any advice on how to improve the infrastructure is welcome.

Spring Boot UnknownHostException while connecting to RDS throgh VPC peering

I am getting the UnknownHostException while trying to deploy a Spring Boot application to a K8s Cluster in AWS EKS.
Scenario:
I have a AWS EKS Cluster running on a VPC (A) and the RDS is running on a VPC (B).
I have created the VPC peering connection between the two VPCs, enabling DNS resolution and configuring the routing tables accordingly.
I also created a K8s Service mapping the external RDS endpoint.
If I try to connect to the RDS instance from EKS Cluster using a busybox like this:
$ kubectl run -i --tty --rm debug --image=busybox --restart=Never -- sh
with any of the following options, it works:
/ # nc mysql-service 3306
/ # nc mysql-service.default.svc.cluster.local 3306
/ # nc xxxxxxx.xxxxxxx.us-east-1.rds.amazonaws.com 3306
But when I deploy my Spring Boot application it doesn't work.
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:488)
at com.mysql.cj.core.exceptions.ExceptionFactory.createException(ExceptionFactory.java:54)
at com.mysql.cj.core.exceptions.ExceptionFactory.createException(ExceptionFactory.java:93)
at com.mysql.cj.core.exceptions.ExceptionFactory.createException(ExceptionFactory.java:133)
at com.mysql.cj.core.exceptions.ExceptionFactory.createCommunicationsException(ExceptionFactory.java:149)
at com.mysql.cj.mysqla.io.MysqlaSocketConnection.connect(MysqlaSocketConnection.java:83)
at com.mysql.cj.mysqla.MysqlaSession.connect(MysqlaSession.java:144)
at com.mysql.cj.jdbc.ConnectionImpl.connectWithRetries(ConnectionImpl.java:1676)
... 58 common frames omitted
Caused by: java.net.UnknownHostException: mysql-service
Any suggestions?
Thanks!
I figured it out! The whole VPC Peering configuration is working. The K8s external service is also working. The problem was in my Spring Boot application.
I had configured the data source URL with an environment variable which was causing the error. For some reason, Spring doesn't resolve the host configured with an environment variable.
I just changed the spring configuration from:
spring:
datasource:
url: jdbc:mysql://${DB_HOST}:3306/db_name
to:
spring:
datasource:
url: jdbc:mysql://mysql-service:3306/db_name
and now it works!

OAuth2 spring security with google callback not working

i have developed the micro service application, in that application there is a service call user service which is running on port 8281. This service handle the authentications. when i test the service in local environment this is worked fine. But if i call this service using zuul api gateway this is not working. following property in the application.yml file is use to get the redirect url in the local environment. This is worked fine.
security:
oauth2:
client:
registration:
google:
redirectUri: "http://localhost:8281/oauth2/callback/google"
But if i change this property as follow for connect with zuul api gateway.
security:
oauth2:
client:
registration:
google:
redirectUri: "http://localhost:8080/api/user/oauth2/callback/google"
This is not working and is throw this error message [authorization_request_not_found]. localhost:8080 is the zuul api gateway. zuul has configure to forward request to user service like this.
zuul:
prefix: /api
routes:
auth-service:
path: /user/**
serviceId: USER-SERVICE
stripPrefix: true
sensitiveHeaders: Cookie,Set-Cookie
so why this error is thrown ?

Springboot Microservice is registered with Eureka but cannot hit it

Basic question so just want to ensure I understand it all correctly.
I have created a discovery server:
#SpringBootApplication
#EnableEurekaServer
public class DisocveryServiceApplication {
public static void main(String[] args) {
SpringApplication.run(DisocveryServiceApplication.class, args);
}
and registered microservices with it successfully; If I hit localhost:8761 I can see my discovery service has found the microservices. The microservices run fine if i hit them on their designated port. For example, I have one called creds and if i hit localhost:9000 it returns. However, My understanding is I should now be able to hit localhost:8761/creds and it will show the same output but this isnt working.
Am I misunderstanding? Any suggestions on what I should try?
creds bootstrap.yml:
spring:
application:
name: creds
creds application.yml
server:
port: 9000
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
discover application.yml
server:
port: ${PORT:8761}
eureka:
client:
registerWithEureka: false
fetchRegistry: false
server:
waitTimeInMsWhenSyncEmpty: 0
Another #EnableEurekaClient annotated Spring boot webservice can access your creds webservice by using an injected RestTemplate with http://creds/..., where creds is the spring.application.name registered with Eureka.
If you want to access the creds webservice from the outside of your web application, then what you want is a proxy like Zuul http://github.com/Netflix/zuul.
Just registering micro service to Eureka server wont make sure that you can access the microservice under a gateway. Eureka Server is not a gateway server , its just a service registry. You can think Eureka as just one more service that holds information about all the other services in the cluster. It doesnt do anything extra other than getting information to the Clients registered.
You may need a Gateway service for routing your request under the Eureka Server. Zuul Proxy routes a request coming to it to the under lying microservices using its service id or the URL configured.
Add this dependency in your classpath.
dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
<version>1.0.4.RELEASE</version>
</dependency>
Add this config in your properties filezuul:
routes:
serv1:
path: /serv1/**
serviceId: http://localhost:8080/serv1
serv2:
path: /serv2/**
serviceId: serv2
This will create a dynamic router that routes your request to appropriate service instances. This also provides a server end load balancer for your services

Spring OAuth Authorization Server behind Spring Cloud Zuul Proxy

I am currently developing a application based on a micro service architecture. We use a API-Gateway implemented using Spring Cloud Netfix's Zuul Server to route the requests to our micro services.
To realize single sign on for all our services I am currently working on an OAuth2 server set up using Spring Cloud Security. The server is basically just copy and past of the implementation in Dave Syer's Repo: https://github.com/dsyer/spring-security-angular/tree/master/oauth2/authserver
The main difference is that I want to route the requests to my OAuth server through the Zuul Proxy. This way I will not have to directly expose my OAuth Server and can add and remove Login Server dynamically.
The problem is I do not seam to understand how to correctly configure this setup. When I try to access a protected resource on the OAuth server I am forwarded to the login page. This of course is as expected. But I can not figure out how to set the hostname and port used when forwarding. What I want to happen is the server to forward to an endpoint on the Zuul server that will get proxied back to the OAuth server. (The Zuul API-Gateway should be the only server the client ever talks to. Everything else will be hidden.)
As it is the host and port are read from the HttpServletRequest in LoginUrlAuthenticationEntryPoint. But the request the server sees is the request send by the Zuul proxy. So I am forwarded to an internal IP not an endpoint on the proxy.
I tried to set the URL of the login page in WebSecurityConfigurerAdapter.configure(HttpSecurity) to the absolut URL of my Zuul Proxy. But this just caused my application to complain about too many redirects. (Might have caused a loop there.)
What would be the best way to set this up?
Do I have to implement some kind of own forwarding strategy by overriding a bean?
Is there a configuration option I am missing?
Is my idea itself wrong? (In his answer to How to avoid redirect to another host with Zuul? Dave Syer says you would not normally proxy this but does not explain why.)
Update: POC can be found here https://github.com/kakawait/uaa-behind-zuul-sample
Did you try following setup (on zuul server):
zuul:
routes:
uaa-service:
path: /uaa/**
stripPrefix: false
security:
# Disable Spring Boot basic authentication
basic:
enabled: false
oauth2:
sso:
loginPath: /login
client:
accessTokenUri: https://<zuul hostname>/uaa/oauth/token
userAuthorizationUri: https://<zuul hostname>/uaa/oauth/authorize
...
Basically it works on my project only thing I have to do is to disable CSRF protection on /uaa/oauth/token route.
Auth server should be on
server:
# Use different context-path to avoid session cookie overlapping
context-path: /uaa
Tested using Spring-Cloud.Brixton.M3
Thank to #thomas-letsch, you should tweak you security like following (sample)
public void configure(HttpSecurity http) throws Exception {
http.logout().and()
.antMatcher("/**").authorizeRequests()
.antMatchers("/index.html", "/home.html", "/", "/uaa/oauth/**").permitAll()
.anyRequest().authenticated().and()
.csrf().csrfTokenRepository(getCSRFTokenRepository()).ignoringAntMatchers("/uaa/‌​oauth/token").and()
.addFilterAfter(createCSRFHeaderFilter(), CsrfFilter.class);
}
As far as I understand your question, spring-cloud-security (for the EnableOauth2Sso part) and spring-cloud (for zuul), this is not possible to proxy the calls to the authorization server using zuul.
The main reason being that spring-cloud-security secures the Gateway independently (and before accounting for) Zuul routing's logic.
Which means that the (sample configuration from Dave Syer's OAuth2 example) spring.oauth2.client.* configuration
spring:
oauth2:
client:
accessTokenUri: http://localhost:9999/uaa/oauth/token
userAuthorizationUri: http://localhost:9999/uaa/oauth/authorize
clientId: acme
clientSecret: acmesecret
is considered before allowing any access to the Zuul's routes zuul.routes.*
Moreover this setup enables the client agent to store two Cookies: one for the Gateway and one for the Authorization Server.
I hope this helps.

Categories