Oauth2 security behind load balander - spring

I'm struggling with authorization problem within my local kubernetes cluster when my application are replicated and listen behind load balancer.
architecture-image
I've created some basic application using spring boot and security oauth2 library. I've also launch keycloak (sso) application. They are listening under domains: hello.org (app) and sso.org (keycloak). I thought to add some load balancing before application, but there is the main problem. How to properly complete the login process if the requests are spread evenly over e.g two pods? The authorization process required few redirects, so first request will go to first one, second to another and process will fail cause they don't have shared information about state.
I'm using authorization code flow.
Here is my libraries responsible for security implementation and basic config.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
</dependency>
server:
port: 8080
logging:
level:
org.springframework.security: DEBUG
org.springframework.security.oauth2: DEBUG
org.springframework.security.jwt: DEBUG
keycloak-client:
server-url: http://sso.org/auth
realm: demo
spring:
security:
oauth2:
client:
registration:
keycloak:
client-id: hello-app
client-secret: b3ed7f13-88cc-4de1-9dc0-59c80b310dfb
client-name: Hello Application
authorization-grant-type: authorization_code
redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
scope:
- hello
- openid
- profile
- email
provider:
keycloak:
token-uri: ${keycloak-client.server-url}/realms/${keycloak-client.realm}/protocol/openid-connect/token
authorization-uri: ${keycloak-client.server-url}/realms/${keycloak-client.realm}/protocol/openid-connect/auth
user-info-uri: ${keycloak-client.server-url}/realms/${keycloak-client.realm}/protocol/openid-connect/userinfo
jwk-set-uri: ${keycloak-client.server-url}/realms/${keycloak-client.realm}/protocol/openid-connect/certs
user-name-attribute: preferred_username
filter:
order: 100

I found a way to resolve my problem with sticky session implementation on Load Balancer. It's not the best solution, but for my case is just fine.
Here is my Ingress configuration to enable sticky session.
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: hello-ingress
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/affinity: "cookie"
nginx.ingress.kubernetes.io/session-cookie-name: "INGRESS"
nginx.ingress.kubernetes.io/session-cookie-expires: "172800"
nginx.ingress.kubernetes.io/session-cookie-max-age: "172800"
nginx.ingress.kubernetes.io/session-cookie-path: /
spec:
rules:
- host: hello.org
http:
paths:
- path: /
backend:
serviceName: hello-app
servicePort: 8080
For more information visit: https://kubernetes.github.io/ingress-nginx/examples/affinity/cookie/

Related

Spring boot application - failed to start

I have recently upgraded spring boot from 1.x to 2.2.1. I am able to deploy most of the modules. For one of the modules I am getting below error while deploying in openshift.
***************************_APPLICATION FAILED TO START_***************************__Description:__Failed to bind properties under 'spring.jackson.serialization' to java.util.Map<com.fasterxml.jackson.databind.SerializationFeature, java.lang.Boolean>:__ Reason: No converter found capable of converting from type [java.lang.String] to type [java.util.Map<com.fasterxml.jackson.databind.SerializationFeature, java.lang.Boolean>]__Action:__Update your application's configuration_
I have added jackson-databind and jackson-core dependencies but no luck.
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.11.1</version>
</dependency>
In local environment the deployment works fine.
application.yml:
application:
name: #project.name#
server:
port: 8085
context: /*
sessionAPIUrl: localhost:8087
sessionAPIUri: /AppMS/user/session
account:
host: http://localhost:8080
remote:
dev: http://localhost:8080
test: http://10.8.99.8:8080
staging: https://stg2-tic.myapp.com
prod: https://ss1.myapp.com
uri:
data: AppMS/service/test
javax.persistence.query.timeout: 120000
#mongodb
spring.data.mongodb.authentication-database:
spring.data.mongodb.host:
spring.data.mongodb.port: 22017
spring.data.mongodb.database: ticnf
spring.data.mongodb.username: ******
spring.data.mongodb.password: ******
#JMX setting
endpoints.jmx.unique-names: true
#logging setup
logging.level.org.springframework.web: WARN
logging.level.com.myapp: INFO
# Logging pattern for the console
logging.pattern.console: "%d{dd-MMM-yyyy HH:mm:ss:SSS zzz}, TYPE= %-5p, SESSIONID=%X{sessionID}, CLIENT_IP=%X{UserIPAddress}, REQID=%X{requestID}, SSOUID=%X{ssoUserId}, ticUID=%X{ticUserID}, APP=%X{APP}, REQUESTURI=%X{requestURI}, CLASS=%c{1}, METHOD=%M, MSG=%m%n"
# Logging pattern for file
logging.pattern.file: "%d{dd-MMM-yyyy HH:mm:ss:SSS zzz}, TYPE= %-5p, SESSIONID=%X{sessionID}, CLIENT_IP=%X{UserIPAddress}, REQID=%X{requestID}, SSOUID=%X{ssoUserId}, ticUID=%X{ticUserID}, APP=%X{APP}, REQUESTURI=%X{requestURI}, CLASS=%c{1}, METHOD=%M, MSG=%m%n"
logging.file: logs/ticms/ticms.log
#kafka configuration
kafka:
broker:
address: localhost:9099
zookeeper:
connect: localhost:2191
consumerId: tic.account
tic.secret: where to store this is an question? DB/File?
spring:
profiles: dev
jackson:
serialization:
INDENT_OUTPUT: true
datasource:
driver-class-name: com.ibm.db2.jcc.DB2Driver
url: jdbc:db2://192.0.0.1:9000/sdb1:currentSchema=DEV1;
username: *****
password: *****
platform: db2
schema: classpath:schema-db2-stg.sql
jpa:
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.DB2Dialect
default_schema: DEV1
server:
domainURI: https://stg-us-api.myapp.com/oauth2/v1
log4j.logger.org.hibernate.type: trace
org.hibernate.type: trace
org.springframework.transaction: debug```
Thank you everyone.
When I saw the error message saying "_Update your application's configuration" , I was sure something is wrong in my application.yml. But in local it was deploying fine.
In paas it was failing, hence I tried to check all the paas related config files. paas-pom was ok. FInally I found application-paassit.yml file was missing. This file was mentioned in my config map for that application in openshift.
I added the file and it was solved.

Eureka registration based on host

I'm working with a simple example of a Spring Boot Eureka service registration. I am using spring-boot-starter 1.5.4.RELEASE, spring-cloud-starter-eureka 1.3.1.RELEASE. The eureka server should register the client instance only if the registration request are coming from white-listed servers.
Is there any out of box feature available in Spring Boot Eureka to achieve this requirement.
The username and password for login is more preferred.
Add maven dependency:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
Add username and password and other configurations to your configuration file application.yaml, note that the client.service-url.defaultZone should contain username and password:
security:
basic:
enabled: true
user:
name: user
password: 123456
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://user:123456#${eureka.instance.hostname}:${server.port}/eureka/

monitoring spring boot service (actuator, security enabled) using prometheus

I am currently trying to use PrAG stack to monitor spring boot based microservices. Have 2 spring boot projects 1.5.4 (pom.xml) have the following dependencies configured to get the metrics and transform the metrics to prometheus server:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-hateoas</artifactId>
</dependency>
<dependency>
<groupId>com.moelholm</groupId>
<artifactId>prometheus-spring-boot-starter</artifactId>
<version>1.0.1</version>
</dependency>
Prometheus Scrape Configs:
scrape_configs:
- job_name: 'Test-springboot-actuator'
scrape_interval: 2s
metrics_path: '/prometheus'
static_configs:
- targets: ['localhost:8090']
- job_name: 'secondApp'
scrape_interval: 2s
basic_auth:
username: user
password: pass
metrics_path: '/prometheus'
static_configs:
- targets: ['localhost:8080']
Test project does not use spring security , but the secondAPP uses the spring security.
Second App requires the basic authentication. Prometheus pod does not start,it somehow does not read the credentials that have been set in scrape_config.
Can anyone point me to the right direction?
One thing you can do is allow the metrics endpoint to work when security is enabled. To do that you can use this setting.
endpoints.metrics.sensitive=false
Reference.

Zuul -> Eureka Server, Basic Authentication issue

I am able to hit the service, if the flow doesn't contain Basic Authorization.
If i use Basic Authorization, it throws "message": "Full authentication is required to access this resource"
Below are my observations:
In ZuulFilter, run() method, i get value for
request.getHeader("Authorization") --> Basic c29tOnNvbzz==
but once it reaches the Micro Service, i am getting value as 'null',
request.getHeader("Authorization") --> null
Using Spring Boot version : 1.4.0.RELEASE
This is my flow:
------------------
Zuul -> Service Discovery (Eureka Server) -> Service
Kindly help, not sure where the Authorization header is vanishing.
Eureka Server yml file:
-------------------------
server.port:4001
eureka.instance.hostname=localhost
eureka.client.fetch-registry:false
eureka.client.register-with-eureka:false
eureka.client.serviceUrl.defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
eureka.client.healthcheck.enabled=true
Zuul yml file:
-----------------
server:
port: 8765
info:
component: Edge Server
eureka:
instance:
leaseRenewalIntervalInSeconds: 3
metadataMap:
instanceId: ${spring.application.name}:${random.value}
client:
# Default values comes from org.springframework.cloud.netflix.eurek.EurekaClientConfigBean
registryFetchIntervalSeconds: 5
instanceInfoReplicationIntervalSeconds: 5
initialInstanceInfoReplicationIntervalSeconds: 5
endpoints:
restart:
enabled: true
shutdown:
enabled: true
health:
sensitive: false
zuul.sensitive-headers: Cookie,Set-Cookie,Authorization
logging:
level:
ROOT: WARN
se.callista: INFO
# Get info regarding connection to the cofig server and retries if required
org.springframework.cloud.config.client.ConfigServicePropertySourceLocator: INFO
org.springframework.retry.support.RetryTemplate: DEBUG
# Set INFO to see the allocated port
org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer: INFO
---
eureka:
instance:
preferIpAddress: true
client:
serviceUrl:
defaultZone: http://localhost:4001/eureka,http://localhost:4002/eureka
Authorization is by default a sensitive header, this means Zuul will not forward them. If you leave it out of the sensitive headers, Zuul will forward the header.
zuul.sensitiveHeaders: Cookie,Set-Cookie
It should also be camelCase instead of hyphenated.
Extra info: https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/asciidoc/spring-cloud-netflix.adoc#cookies-and-sensitive-headers
This solved my problem, but is this the only solution we have ?
ctx.addZuulRequestHeader("Authorization",request.getHeader("‌​Authorization"))
Your property for zuul.sensitiveHeaders is wrong. It is camel case not hyphenated.
https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/asciidoc/spring-cloud-netflix.adoc#cookies-and-sensitive-headers

consul health check: 503 Service Unavailable

I'm using spring cloud consul in my application. when I started the application I have no exception (service Registed in consul). but when consul check health I got this error:
HTTP GET https://xxxxxxxxxx:8181/health: 503 Service Unavailable Output: {
"status" : "DOWN"
}
Here is my configuration file:
spring:
application:
name: #pom.artifactId#
cloud:
config:
enabled: false
consul:
config:
enabled: true
host: localhost
port: 8500
discovery:
scheme: https
endpoints:
health:
sensitive: false
restart:
enabled: true
shutdown:
enabled: true
my pom is as follow:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-all</artifactId>
<version>1.0.0.M6</version>
</dependency>
It looks like your configuration is a little bit incorrect.
The latest documentation contains the following configuration example:
spring:
cloud:
consul:
host: localhost
port: 8500
BTW: The latest spring-cloud-consul version is RC1

Resources