-
-
Notifications
You must be signed in to change notification settings - Fork 173
Secured Eureka First services on Heroku
The deployment and composition of microservices need to have some additional infrastructure services, like a service registry or a config server around. Each deployed microservice is getting its application configuration from a central config service (CS) and registers itself with a service registry (SR) that is also deployed as a microservice. Basically this guide is based on the Spring Cloud reference documentation [SCRef] and describes in more detail how infrastructure services are setup in a secured and Eureka First way on Heroku. Most examples provided by the Spring team, are based on CloudFoundry - here we deploy to Heroku.
(1) In the scenario described here an Arduino device is sending string messages to a tcp/ip driver component (based on Spring Messaging). (2) The driver transforms and routes the message to the TMS Routing Service. This services calls the Location Service (3) and the Transportation Service (4) to enhance the context information needed to handle the origin message. (5) With all context information available, a decision matrix is called to lookup the correct business flow. The flow is then loaded and processed by the Activiti engine.
All services in all stages (IT, PTA & PROD) must be protected with BASIC authentication as described in the Spring manuals. BASIC auth is sufficient for our needs of service-to-service communication whereas OAuth2 with JWT tokens is the preferred way to interact with web applications.
For BASIC authentication, all microservices have default credentials defined that are overridden with those fetched from the config server.
The default security configuration of the common-service looks like
security:
user:
password: sa
whereas the config server serves the following configuration to the common-service at startup:
security:
user:
password: '{cipher}AQCBEJQaBHFtllY7nn......VoAKLic='
The config server will decrypt the cipher and send it to the microservice.
The SpringBoot starter for Activiti is less flexible to hook into the security configuration, hence we needed to override the security complete with the same security configuration we use for all other microservices.
The term Eureka First Bootstrap is taken from the Spring Cloud reference manual [SCRef] and describes the scenario where a microservice (MSx) first accesses the service registry to get a reference to the config server. After the actual deployed CS instance has been resolved the microservice uses that reference to fetch the application configuration. This chapter describes how to setup all three components MSx, SR and CS locally (stage DEV) as well as on the Heroku Platform.
To make the SR aware of Eureka First Bootstrap we need to enable CS discovery in the SR bootstrap configuration (i.e. bootstrap.yml
). This is not clearly described in the docs. It took me some time to figure out that CS discovery needs to be enabled in SR and not only in the MSx configuration. However the following must be enabled in SR bootstrap configuration:
spring:
cloud:
config:
discovery:
enabled: true
serviceId: openwms-config
username: user
password: sa
Beside enabling the service discovery we define the CS serviceId. This must match the spring.application.name
of the CS service and provide credentials to access the CS. These credentials are overridden in the environment properties of the Heroku application:
Find the complete bootstrap configuration of the OpenWMS.org service registry here.
After all microservices got a reference to the current deployed config server instance, they can access the CS.
The CS does not need any specific configuration to get Eureka First working. It's just the spring.application.name
that must be configured and need to match the serviceId
as configured in SR earlier. We consequently use openwms-config
as the name for CS. Like other services that use the SR, the Eureka serviceUrl
must be configured to register CS with SR. Here we divided the yml configuration file into two sections. The first (default) section is always active and the second section becomes active with the Spring profile "CLOUD" and overrides the defaults. By default (in development) we don't use SSL and go with default BASIC auth credentials. The password and the eureka settings are overridden in a cloud environment like Heroku.
spring:
application:
name: openwms-config
security:
user:
password: sa
encrypt:
keyStore:
location: classpath:/server.jks
alias: livekey
eureka:
client:
serviceUrl:
defaultZone: ${owms.eureka.url}/eureka/
instance:
secure-port-enabled: false
non-secure-port-enabled: true
metadata-map:
username: user
password: ${security.user.password}
---
spring:
profiles: CLOUD
security:
user:
password: '{cipher}AQAVmwzi/JEKNgFSPOXYSabqxP.....A5jxms='
eureka:
instance:
hostname: openwms-configuration.herokuapp.com
secure-port-enabled: true
non-secure-port-enabled: false
statusPageUrl: https://${eureka.instance.hostName}/info
healthCheckUrl: https://${eureka.instance.hostName}/health
homePageUrl: https://${eureka.instance.hostName}/
In contrast to other MS, the CS is shipped with a keystore file that is used to decrypt keys in form of '{cipher:xxx}'. Everytime the config server reads configuration values like this, it tries to decrypt the value with the key stored in the keystore. All keystore passwords are configured as Heroku environment properties of the CS and not stored along the project source code, like it is shown in almost all sample applications!
The following properties need to be set as Heroku env vars of the CS:
encrypt.keyStore.secret
encrypt.keyStore.password
Notice the camelCase naming and be careful not to use kebap-case here!! keyStore instead of key-store. The latter does not work!
A second note to Spring profiles. You may need to define several profiles at startup as a comma separated list. This leads to some trouble, when the config server tries to fetch configuration. Given two profiles CLOUD,H2 the CS searches for config files named <application.name>-CLOUD,H2.yml.
The complete boostrap configuration of the CS can be found here.
All OpenWMS.org microservices use the Eureka First Bootstrap approach to get the configuration from a CS resolved by the SR. Therefor the SR location must be known by a MSx. This piece of bootstrap configuration is necessary for all microservices to lookup the SR:
eureka:
client:
serviceUrl:
defaultZone: ${owms.eureka.url}/eureka
instance:
metadata-map:
username: user
password: ${security.user.password}
protocol: http
We now expand this (default) configuration with another section that gets enabled with Spring profile CLOUD and that looks like this:
---
spring:
profiles: CLOUD
eureka:
instance:
hostname: openwms-common-services.herokuapp.com
secure-port-enabled: true
non-secure-port-enabled: false
statusPageUrl: https://${eureka.instance.hostName}/info
healthCheckUrl: https://${eureka.instance.hostName}/health
homePageUrl: https://${eureka.instance.hostName}/
metadataMap:
username: ${owms.eureka.username}
password: ${owms.eureka.password}
protocol: https
We added some eureka.instance.*
properties to override the default settings. The hostname is now set to the hostname of the deployed cloud application. In production we're going to use secured socket connections only and override the defaults as well. We also need to override some default URL endpoints because we've SSL configured. The SR is passing all values of the metadataMap to all clients of the microservice. The property with key protocol
is a custom property that is used by a clients RestTemplate
to build the correct URL.
Finally we add the CS properties to define what tenant to use (property label
), that the application should fail when the CS is not available and that the CS (with serviceId openwms-config) needs to be discovered at startup.
spring:
cloud:
config:
label: ${owms.tenant:master}
fail-fast: true
discovery:
enabled: true
service-id: openwms-config
Find here a configured RestTemplate
to access the example common-service
.
In this example all microservices, except the tcpip-driver
, are deployed on Heroku cloud platform. The tcpip-driver
need to be instantiated only once and must sit close to the physical hardware devices. Each microservice implementation has been extracted into an own git repository with a separate Jenkins build pipeline and a Procfile. The latter is something like a deployment descriptor for Heroku to tell Heroku how to start the dyno and what Spring profile to activate ("CLOUD")
At startup we're currently loading some initial test data of the demo project ZILE
RefId | Link |
---|---|
SCRef | Spring Cloud Reference Manual |