Zuul proxy same request to another port - java

I want to be able to proxy with Zuul running on a Spring Boot server listening to port X to port Y where an nginx is listening on the same machine. I want to send the same URL, parameters, etc.
The reason I am doing this is that using a ZuulFilter which is doing some checks.
For example I want to get to localhost:8943/humans/get/1 and send the request to localhost:8080/humans/get/1
Thanks in advance.

Something like this should work.
zuul:
routes:
humans:
path: /humans/**
url: http://localhost:8080

I've managed to do it with the following configuration:
zuul:
routes:
services:
path: /**
url: http://localhost:8043/

Related

send REST message to pode in Openshift

I have an openshift namespace (SomeNamespace), in that namespace I have several pods.
I have a route associated with that namespace (SomeRoute).
In one of pods I have my spring application. It has REST controllers.
I want to send message to that REST controller, how can I do it?
I have a route URL: https://some.namespace.company.name. What should I find next?
I tried to send requests to https://some.namespace.company.name/rest/api/route but it didn't work. I guess, I must somehow specify pod in my URL, so route will redirect requests to concrete pod but I don't know how I can do it.
You don't need to specify the pod in the route.
The chain goes like this:
Route exposes a given port of a Service
Service selects some pod to route the traffic to by its .spec.selector field
You need to check your Service and Route definitions.
Example service and route (including only the related parts of the resources):
Service
spec:
ports:
- name: 8080-tcp
port: 8080
protocol: TCP
targetPort: 8080
selector:
<label-key>: <label-value>
Where label-key and label-value is any label key-value combination that selects your pod with the http application.
Route
spec:
port:
targetPort: 8080-tcp <port name of the service>
to:
kind: Service
name: <service name>
When your app exposes some endpoint on :8080/rest/api, you can invoke it with <route-url>/rest/api
You can try it out with some example application (some I found randomly on github, to verify everything works correctly on your cluster):
create a new app using s2i build from github repository: oc new-app registry.redhat.io/openjdk/openjdk-11-rhel7~https://github.com/redhat-cop/spring-rest
wait until the s2i build is done and the pod is started
expose the service via route: oc expose svc/spring-rest
grab the route url: oc get route spring-rest -o jsonpath='{.spec.host}'
invoke the api: curl -k <route-url>/v1/greeting
response should be something like: {"id":3,"content":"Hello, World!"}
Routes are an OpenShift-specific way of exposing a Service outside the cluster.
But, if you are developing an app that will be deployed onto OpenShift and Kubernetes, then you should use Kubernetes Ingress objects.
Using Ingress means that your app’s manifests are more portable between different Kubernetes clusters.
From the official Kubernetes docs:
An Ingress may be configured to give Services externally-reachable URLs, load balance traffic, terminate SSL / TLS, and offer name-based virtual hosting.
Ingress exposes HTTP and HTTPS routes from outside the cluster to services within the cluster.
Traffic routing is controlled by rules defined on the Ingress resource.
So, if you want to reach your REST controllers:
from within the k8s cluster. Create a k8s Service to expose an application running on a set of Pods as a network service:
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: your-namespace
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 8080
This specification creates a new Service object named "my-service", which targets TCP port 8080 on any Pod with the app=MyApp label.
You can reach the REST controller using this URL:
http://my-service
externally. Create an Ingress resource to configure externally-reachable URLs (a k8s Service 'my-service' should exist):
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-name
namespace: your-namespace
spec:
rules:
- host: "foo.bar.com"
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: my-service
port:
number: 80
You can reach the REST controller using this URL:
http://foo.bar.com

Camel SpringBoot rest servlet connection refused (camel 2.22.0)

I'm having troubles getting any of the simplest springboot servlet rest api examples to work on my machine. I'm just trying to create the simplest possible test api to practice the framework. I have the following code in my Routes RouteBuilder class:
#Component
public class Routes extends RouteBuilder {
#Override
public void configure() {
restConfiguration()//Bind the api servlet to the localhost port 8080
.component("servlet").host("localhost").port(8080)
.bindingMode(RestBindingMode.auto);
rest("/api")//Log any get requests
.get()
.route().to("log:DEBUG?showBody=true&showHeaders=true");
}
}
However, when I try to invoke this code with curl, I get the following error:
curl -X GET http://localhost:8080/api
curl: (7) Failed to connect to localhost port 8080: Connection refused
I'm using camel 2.22.0 and SpringBoot 2.0.4.RELEASE. I'm running this on Ubuntu 20.04 LTS.
EDIT:
I did the changes suggested, but I still get the same Connection refused by curl. My code now looks like this:
restConfiguration()//Bind the api servlet to the localhost port 8080
.component("servlet").host("localhost").port(8080)//Use camel default context path
.bindingMode(RestBindingMode.auto);
rest("/api")//Log any get requests
.get()
.route().to("log:DEBUG?showBody=true&showHeaders=true");
curl -X GET http://localhost:8080/camel/api -> Connection refused
I also now have the following in my application.yml:
server:
port: 8080 #Specify port for camel servlet
max-http-header-size: 32768 # Maximum size in bytes of the HTTP message header.
By default, Camel uses the context path /camel/*.
So, your curl command should look like this:
curl -X GET http://localhost:8080/camel/api
You can control the context path in the following ways.
With restConfiguration
restConfiguration()//Bind the api servlet to the localhost port 8080
.component("servlet").host("localhost").port(8080)
.contextPath("/test/*")
.bindingMode(RestBindingMode.auto);
With application.properties
camel.component.servlet.mapping.context-path=/test/*
For me, only the latter works.
Something worth mentioning here.
You are using the servlet component in your rest definition. In this case, Camel ignores the port configuration and uses the underlying servlet component. As you are using spring-boot, the tomcat port is being used, which by default happens to be 8080.
If for some reason, you change the tomcat port, your rest service port will change.
For example, if you change the server port in the application.properties.
server.port=8180
Your rest service uses that port, ignoring the definition in the restConfiguration.
curl -X GET http://localhost:8180/camel/api
Rest DSL docs

Error - "com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server"

I am receiving this error - "com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server" while running the client application in spring-boot while trying for a authentication module using Spring Boot in Microservice.
Below is my server and client application.properties file:
Server - application.properties
spring.application.name=demoEureka
#This is a eureka server so no need to register
eureka.client.register-with-eureka=false
#This is a eureka server no need to fetch registry
eureka.client.fetch-registry=false
#eureka.client.enabled=false
#Register url for client
server.port=8085
eureka.instance.hostname=localhost
eureka.client.service-url.defaultZone: http://localhost:8085/eureka/
#timeout
eureka.server.wait-time-in-ms-when-sync-empty=0
spring.security.basic.enabled=true
spring.security.user.name=atul
spring.security.user.password=atul
#All url come with prefix/api will interpret
zuul.prefix=/api
#zuul.routes.middleware.path=/cart/**
zuul.routes.cart.path=/cart/**
#zuul.routes.middleware.url=http://localhost:8081/
zuul.routes.cart.url=http://localhost:8081/
#server.port=8098
Client - application.properties
spring.application.name=cart
server.port=8081
#Eureka server url for registering
#This is eureka client
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
#register url to server
eureka.client.serviceUrl.defaultZone=http://localhost:8085/eureka/
eureka.instance.hostname=localhost
#eureka.client.serviceUrl.defaultZone=http://atul:atul#localhost:8085/eureka
Please help me in removing the error and successful execution of program.
check flowing:
service is start
network is ok
system proxy

Configuring Spring Cloud Config Server and Spring Cloud Vault for production

I am attempting to setup a Spring Cloud Config Server backed by Spring Cloud Vault secret management. I'm relatively new to Spring but I have tried following instructions and examples here:-
http://cloud.spring.io/spring-cloud-vault-config/
Everything works fine provided you follow the default settings like http, localhost and 8200 for the vault endpoint and tls_disable = 1 to switch off SSL. However these are not practical settings for any real environment and there are few examples anywhere that help with this. Can anyone help with a working example?
I have Successfully set up vault with TLS enable. I have successfully set up a config server that connects using a self signed cert. I can even inject a secret value into the config server and expose it via #Value and #PostConstruct.
All of this is working. However when I try to leverage Spring Conig endpoints to access vault, I get the following:-
{
"timestamp": 1486413850574,
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.web.client.ResourceAccessException",
"message": "I/O error on GET request for \"http://127.0.0.1:8200/v1/secret/myapp\": Connection refused: connect; nested exception is java.net.ConnectException: Connection refused: connect",
"path": "/myapp/default"
}
Config server is using default values even though I have set overrides in the bootstrap.yml.:-
server:
port: 8888
spring:
profiles:
active: vault
spring.cloud.vault:
host: myhost.mydomain.com
port: 8200
scheme: https
authentication: TOKEN
token: 0f1887c3-d8a8-befd-a5a2-01e4e066c50
ssl:
trust-store: configTrustStore.jks
trust-store-password: changeit
As you can see it should be pointing to myhost.mydomain.com not
127.0.0.1 and it should be using https, not http as the protocol scheme.
I'm not sure why it uses these defaults for config server endpoints but uses correct settings during spring cloud vault startup. I'm using all the latest stable builds of Spring Dalsten.M1 and Spring Cloud Vault 1.0.0.M1. I realize these are milestone releases. I've also tried Camden and Brixton combos with no luck. I can supply code if required.
Any help greatly appreciated.
As I mention in my response to spensergibb, I have had some success in resolving this myself. Based on his comments I will clarify my intent as it will help with a common understanding of the issue. I am attempting to do two things:-
Stand up a configuration server that uses Vault as a backend, (as opposed to the default GIT backend) and expose the Vault API to client applications (over TLS) so that they can retrieve their own secrets. I do not want all my client applications to connect to Vault directly. I want them to get their configuration from a config server by having the config server connect to Vault. Until last night I was unable to achieve this goal, unless I set everything up as default with TLS disabled and using loopback address, port 8200 for the Vault software etc. Obviously defaults are not practical for any of our deployed environments. I will mention that the link posted by spencergibb does help me understand why this was not working but the subtlety of the reason is why I missed it before. Read on for my explanation.
I want the config server to configure itself from Vault directly. That is, connect to Vault via Spring Cloud Vault Config. This worked right away for me as described in the documentation. However this goal is somewhat trivial as I do not have a real use case at this time. But I wanted to understand if it could be done since I saw no real reason why not and it seemed like good first steps in integrating Vault.
The distinction between these two capabilities helped me understand that the problem derives from the fact that Spring Cloud Config Server and Spring Cloud Vault appear to be using two different beans to inject the Vault configuration properties. Spring Cloud Config Server uses VaultEnvironmentRepository annotated with #ConfigurationProperties("spring.cloud.config.server.vault") and Spring Cloud Vault uses VaultProperties annotated with #ConfigurationProperties("spring.cloud.vault").
This caused me to add two different configs to my bootstrap yml.
server:
port: 8888
spring:
profiles:
active: local, vault
application:
name: quoting-domain-configuration-server
cloud:
vault:
host: VDDP03P-49A26EF.lm.lmig.com
port: 8200
scheme: https
authentication: TOKEN
token: 0f1997c3-d8a8-befd-a5a2-01e4e066c50a
ssl:
trust-store: configTrustStore.jks
trust-store-password: changeit
config:
server:
vault:
host: VDDP03P-49A26EF.lm.lmig.com
port: 8200
scheme: https
authentication: TOKEN
token: 0f1997c3-d8a8-befd-a5a2-01e4e066c50a
Note the same config details. Just different yml paths. This is the subtle point I missed given that I started by getting goal number 1 to work first and assuming the same config would work for both goals. (Note: Token and password are contrived).
This almost worked except for an SSL handshake error. As you can see there are no SSL attributes set on the spring.cloud.config.server.vault path. The VaultProperties bean does not support them. I was not sure how to deal with this (perhaps another non-vault specific bean that I could not find). My solution was to simply force the cert configuration myself like this:-
#SpringBootApplication
#EnableConfigServer
public class Application
{
public static void main(String[] args)
{
System.setProperty("javax.net.ssl.trustStore",
Application.class.getResource("/configTrustStore.jks").getFile());
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
SpringApplication.run(Application.class, args);
}
}
This SSL solution is pretty ugly. I'm sure there must be a better way to do this part. So I am open to other suggestions. However once I completed all above steps everything now works.
Thanks for your write up. I was struggling to get this working. I was able to get a client service to connect to a Spring Cloud Config server and a Vault server but I was not able to get a Spring Cloud Config server to connect to a Vault server.
I even struggled after copying your configuration into my Spring Cloud Config server. While I eventually got it working with your configuration I was able to pare it down quite a bit. The key was that the token does not belong in the Spring Cloud Config server. It belongs in the client.
I was trying http://localhost:8888/{application}/default in the browser but got the following:
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Thu May 11 14:21:31 EDT 2017
There was an unexpected error (type=Bad Request, status=400).
Missing required header: X-Config-Token
I used PostMan to send the request with a X-Config-Token header containing the Vault token and it worked.
Here is my final config.
server:
port: ${PORT:8888}
management:
context-path: /manage
security:
enabled: true
spring:
profiles:
active: git,vault
application:
name: config-server
cloud:
config:
server:
git:
order: 1
uri: file:///temp/config-server/config
vault:
order: 0
host: localhost
port: 8200
scheme: http
So it looks like you need to add the token to the client. Maybe using spring.cloud.config.token.
instead
#SpringBootApplication
#EnableConfigServer
public class Application
{
public static void main(String[] args)
{
System.setProperty("javax.net.ssl.trustStore",
Application.class.getResource("/configTrustStore.jks").getFile());
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
SpringApplication.run(Application.class, args);
}
}
write
bootstrap yml ->
javax.net.ssl.trustStore: /configTrustStore.jks
javax.net.ssl.trustStorePassword: changeit
Although I am answering late. But I was able to configure spring cloud config server to use Vault as backend with CERT authentication via certificates. And moreover you do not require to send X-Config-Token in GET request. So your config-server GET requests will work in the same way it works with GITHUB as backend.As my implementation will get the token on the fly and change the incoming request by appending header. I would recommend to check all the steps in my tutorial and github repo.
Here is my tutorial : https://medium.com/#java.developer.raman/enable-spring-config-server-to-use-cert-authentication-with-vault-as-back-end-ff84e1ef2de7?sk=45a26d7f1277437d91a5cff3d5997287
And GitHub repository: https://github.com/java-developer-raman/config-server-vault-backend

How do I change the host and port of paths in a servlet?

When I send a GET request to
Host: 1.1.1.1
Port: 9999
Path: /hello
I want a servlet that catches this request and sends a GET to
Host: 127.0.0.1
Port: 4444
Path: /hello?proxy=1.1.1.1
What's the easiest way to do this in Java? This will be used for integration tests, so I'd like to avoid having to change any of the main code's logic. Alternatively, it doesn't need to be a servlet. It can be a mock http service as well. I tried using simpleframework for this approach, but, and correct me if I'm wrong, it only picks up localhost.
Note I will need to redirect several GET requests with different hosts.
You could use Tuckey's UrlRewriteFilter. There is good doc and examples on their site.
you can just redirect this request to your new host like:
response.sendRedirect("http://(your ip):4444/hello?proxy='1.1.1.1'");

Categories

Resources