Skip to content

Secured Eureka First services on Heroku

Heiko Scherrer edited this page Nov 9, 2016 · 25 revisions

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.

Scenario

(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.

Secured

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.

Eureka First Bootstrap

Access Eureka

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.

Configure a Eureka First Service Registry

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:

Eureka Properties

Find the complete bootstrap configuration of the OpenWMS.org service registry here.

Configure a Eureka First Config Server

After all microservices got a reference to the current deployed config server instance, they can access the CS.

Access 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.

Configure a Eureka First Microservice

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.

Heroku

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

References

RefId Link
SCRef Spring Cloud Reference Manual