I have written a number of services that communicate via REST API calls. The services can be configured to use HTTP or HTTPS. Any given client has security configuration that defines the connection to a server. "Default" configuration properties are set by values in application.yml and this has worked well up to this point.
I've come to realize, however, that this does not work for a more realistic situation. The problem is that I'm attempting to set specific arguments, like a server host/port when a client is launched, and the values I'm setting are being ignored.
An example:
Service A (client) will communicate with Service B (server) for some purpose.
Service A has security configuration set up like the following:
#Configuration
#EnableConfigurationProperties({ClientConnectionProperties.class,
SecurityCertificateProperties.class})
public class ClientSecurityConfiguration {
...
}
#ConfigurationProperties("client.connection")
public class ClientConnectionProperties {
...
}
#ConfigurationProperties("server.ssl")
public class SecurityCertificateProperties {
...
}
application.yml has defnitions for the various properties that are assigned to values in the configuration objects.
client:
connection:
scheme: https
host: localhost
port: 8443
server:
port: 7081
ssl:
enabled: true
protocol: TLS
trust-store-type: JKS
trust-store: classpath:server.truststore
trust-store-password: <password>
key-store-type: JKS
key-store: classpath:server.keystore
key-store-password: <password>
This is all well and good when both client and server are running on localhost. However, I'm testing running them where they reside on different hosts, so I need to override client.connection.host from localhost to the actual host. I do this by specifying -Dclient.connection.host=<host> as a VM argument when launching the client. Unfortunately, this does not work. The client ends up trying to connect to localhost and fails (for obvious reasons).
How can I get my override values set into these configuration items? Is there a way to defer or delay the "default" loading so that my values take effect? Is there some other technique to use to get them in there?
You can override Spring Boot properties by using Spring arguments instead of JVM arguments.
Example:
java -jar your-app.jar --client.connection.host=<host>
Related question: https://stackoverflow.com/a/37053004/12355118
Related
So I have been recently going through few articles on enabling SSL in spring boot on local machine
For e.g. How set up Spring Boot to run HTTPS / HTTP ports
Let's say my Spring boot application YML has following defined (Few other definitions too)
someService(SOAP Based):
username: someUserName
uri: https://qa01-some-service-war.appcloud-np.barclays.intranet/someservice/v1/someservice.wsdl
password:
My Question is what is the bare minimum I require if at runtime I want to call this "some-service" , assuming everything else is properly defined!
Just to add up more, I am facing this exception as of now (Which wouldn't I think if I pass the password field correctly)
org.springframework.ws.soap.client.SoapFaultClientException: The security token could not be authenticated or authorized; nested exception is org.apache.wss4j.common.ext.WSSecurityException: The security token could not be authenticated or authorized
Hope this one helps, These are basic steps one must follow to setup ssl for springboot.
https://www.thomasvitale.com/https-spring-boot-ssl-certificate/
Add these configurations
server:
ssl:
key-store: classpath:keystore.p12
key-store-password: password
key-store-type: pkcs12
key-alias: springboot
key-password: password
port: 8443
I'm trying to configure a Spring Boot web application to use TLS when making connections to a MongoDB instance, running in a Docker container.
I've configured the MongoDB instance in Docker to use a self-signed certificate, and require TLS. The configuration file for MongoDB (mongod.conf) looks like this:
net:
bindIp: 0.0.0.0
port: 27017
tls:
certificateKeyFile: /home/mongodb/ssl/my-self-signed-cert.pem
mode: requireTLS
allowConnectionsWithoutCertificates: true
...
I can connect to the instance with TLS using the Mongo connection string from the command line, getting the Mongo shell. However, I can't get my web application to connect to that same MongoDB instance over TLS.
To try testing the connection, I've tried creating a unit test that will access the DB. The Spring Boot magic causes the test to fail during setup. It seems like this is because autowiring in some of the dependencies (MongoTemplate, I think) seems to test the connection, which requires TLS and then fails.
To set up the TLS connection, I added the server's certificate to the Java truststore, using keytool. However, the connection still fails. I've tried specifying the location of a .jks file created with keytool, as well, in the application.yml file, like so:
...
javax:
net:
ssl:
trustStore: src/main/resources/truststore/trustedcerts.jks
...
Ultimately, my question is: How can I configure TLS-secured connections to MongoDB from my Spring Boot app? Currently, we implement data access using MongoRepository and MongoTemplate.
You have to set the system properties in your java application
System.setProperty ("javax.net.ssl.keyStore",keystorePath);
System.setProperty ("javax.net.ssl.keyStorePassword",keystorePass);
System.setProperty ("javax.net.ssl.trustStore",truststorePath);
System.setProperty ("javax.net.ssl.trustStorePassword","changeit");
I have a JHipster monolithic application (Angular + Java SpringBoot + Tomcat container, everything together) deployed successfully in a EC2. I could set the security groups in order to enable 8443 incoming requests to the Public DNS and I am able to access it from any browser.
After that, I've requested a public certificate from Amazon for a domain I've already acquired with Route53.
So the idea was to use 443 instead of 8443, and the real domain (instead the Public DNS provided by AWS), so in effect I've created a ELB (all in the same VPC, security group and hosted zone). This ELB is listening in 443 and has a redirect to 8443 as default action.
But.. ERR_CONNECTION_REFUSED is what the browser shows..
It is important to mention that since AWS does not allow us to download the certificate (at least I don't see any option for that in the console) in the JDK of the EC2 where the app runs I've installed a custom certificate (generated with keytools) in order to apply it in Tomcat to listening the already mentioned 8443 port.
I also tried running in 8080 instead of 8443 (and of course updating the security groups) but no change..
Could you give me a clue about what I'm missing? So far the unique way I see is to create a new EC2 with a NGINX to act as a reverse proxy (with a rewrite policy maybe) behind the ELB, but I prefer to avoid additional complexity unless absolutely needed.
Additional data:
Tomcat server configuration:
server:
port: 8443
server.ssl.key-store: keystore.p12
server.ssl.key-store-password: thePassword
server.ssl.keyStoreType: PKCS12
server.ssl.keyAlias: theKeyAlias
Security group inbound rules:
Custom TCP 8443 with 172.31.0.0/16 (the same range of the ELB)
HTTPS TCP 443 with 0.0.0.0/0 and ::/0
Also the AWS Certificate is enabled and already issued (CNAME record set was created in Route53)
**UPDATE 1 - 04 February 2019 22:21 (GMT-3) **
Guys, I finally decided to have a NGINX behind the ELB. Also I've realized that communication between NGINX and App Server could be HTTP, therefore my app is gonna listen in port 8080, simplifying a bit the scheme. I've realized also that I need only one certificate in order to have the "browser padlock" and encrypted all traffic between clients and ELB, so no matter if it is not possible to download it (it is not needed to install also in NGINX nor App. Server).
At the Apache level you should add a listener on port 443 which would proxy pass the requests on port 8443. This will make sure that all incoming requests on port 443 of the domain will be passed to the application running on port 8443 of the server
listen 443;
location /{
proxy_pass http://127.0.0.1:8443;
}
Finally issue RESOLVED I could make work fine the NGINX and also I had to change another things:
I've passed from an Application Load Balancer to a Classic Load Balancer. The final scheme is like I've explained in the UPDATE of this topic, I mean:
User connects via HTTP or HTTPS through Classic LB and then it goes to EC2 NGINX listening on port 80.
Then from NGINX to WebApp I've used a proxy_pass in this way:
location / {
proxy_pass http://172.x.y.z:8080;
}
And finally an HTTP forward in NGINX to use HTTPS exclusively:
proxy_set_header X-Forwarded-Proto $scheme;
if ( $http_x_forwarded_proto != 'https' )
{
return 301 https://$host$request_uri;
}
Lijo Abraham, your answer helped me to have a clear direction and this post shows the exactly solution applied (thats why I will green tick this post).
Many thanks and regards.
**UPDATE 1 - 10 February 2019 17:21 (GMT-3) ** Finally I've remade all again using Application ELB this time instead of Classic ELB (the latter deprecated) and everything works as expected, don't know why in the beginning ELB Classic didn't work (probably some error in security groups rules configuration or something kind of that).
We've deployed our Spring MVC web application on Windows Server 2012. Our web-app uses Spring Websockets for updates with stomp.js and sock.js.
Our websocket configuration:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/calcApp");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/add").setAllowedOrigins("*").withSockJS();
}
}
Websocket works on localhost and logs are following:
Opening Web Socket...
Web Socket Opened...
>>> CONNECT
accept-version:1.1,1.0
heart-beat:10000,10000
<<< CONNECTED
version:1.1
heart-beat:0,0
user-name:admin
connected to server undefined
>>> SUBSCRIBE
id:sub-0
destination:/topic/resident
...
Strangely, it doesn't work when I enter external ip, on same machine and browser:
Opening Web Socket...
WebSocket connection to 'ws://192.168.5.50:8080/autopark/add/629/i148hb1c/websocket' failed: WebSocket is closed before the connection is established.
Whoops! Lost connection to undefined
We thought that for external access, there is some firewall and totally disabled it:
But it didn't solve our problem.
How can we solve this issue?
I'm not really sure and not a spring expert.
but it seems you need to call the server by a domain name over its ip addr which is logical.
Since an ip would be used for more than one domain, so it seems the context need to know which context should be called(even if one) in spring context.
in other word, calling the context by ip would confuse the spring context to select/invoke which context/domain, so it refuses the connection.
have atry, binf the 192.168.5.50 to a domain name, then try to call the path using the domain(not ip). Hope it works this way.
The first step in debugging this would be verifying that your application server is actually listening on the external interface.
You can verify what IP your container is bound to by looking for 8080 entries in the output of netstat.
netstat -a -n -o | find "8080"
If you don't see an entry bound to either 0.0.0.0 or the external IP, then we know it is a configuration issue with your application server.
Example for embedded tomcat - How to set a IP address to the tomcat?
Example for standalone tomcat - How do you configure tomcat to bind to a single ip address (localhost) instead of all addresses?
The next step should be verifying an external computer can "see" the port on the external IP. There are various ways to do this, but using the telnet command will suffice.
telnet 192.168.5.50 8080
If this does not work, then we know there is something blocking communication between the two applications
If we get to this point, then there is likely an issue with the configuration of the application itself.
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