Configuring OKTA – OIDC authentication for AWS EKS clusters

You can configure OKTA as an identity provider (IdP) to provide strong user authentication to your EKS clusters.

Required components include:

Integrating OIDC within AWS EKS

Integrating OpenID Connect (OIDC) within Amazon Web Services (AWS) Elastic Kubernetes Service (EKS) involves three steps.

  1. Creating a OKTA OIDC application
  2. Associate an OIDC Identity provider to EKS cluster.
  3. Configuring the kubectl CLI for OIDC.

1. Creating a OKTA OIDC application:

You can log in with an Okta user. Okta supports the authorization code flow with PKCE and this section explains how to set up it.

Open your Okta organization and create an application with the following options:

  • Application type: Native
  • Initiate login URI: http://localhost:8000
  • Login redirect URIs:
    • http://localhost:8000
    • http://localhost:18000 (used if the port 8000 is already in use)
  • Allowed grant types: Authorization Code
  • Client authentication: Use PKCE (for public clients)

Replace the following variables in the later sections.

VariableValue
ISSUER_URLhttps://YOUR_ORGANIZATION.okta.com
YOUR_CLIENT_IDrandom string

You do not need to set YOUR_CLIENT_SECRET.

If you need groups claim for access control, see jetstack/okta-kubectl-auth and #250.

2. Associate an OIDC Identity provider to EKS cluster.

  1. Open the Amazon EKS console at https://console.aws.amazon.com/eks/home#/clusters.
  2. Select your cluster.
  3. Select the Configuration tab, and then select the Authentication tab.
  4. On the OIDC Identity Providers page, select Associate Identity Provider.
  5. On the Associate OIDC Identity Provider page, enter or select the following options, and then select Associate.
    • For Name, enter a unique name for the provider.
    • For Issuer URL, enter the URL for your provider. This URL must be accessible over the internet.
    • For Client ID, enter the OIDC identity provider’s client ID (also known as audience).
    • For Username claim, enter the claim to use as the username.
    • For Groups claim, enter the claim to use as the user’s group.

Refer:

https://docs.aws.amazon.com/eks/latest/userguide/authenticate-oidc-identity-provider.html

3. Configuring the kubectl CLI for OIDC

1. install kubelogin

brew install int128/kubelogin/kubelogin

2. setup kubectl to do OIDC authentication by setting Client-ID & IssuerURL in ~/.kube/config

 users:
- name: oidc
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1beta1
      command: kubectl
      args:
      - oidc-login
      - get-token
      - --oidc-issuer-url=ISSUER_URL
      - --oidc-client-id=YOUR_CLIENT_ID

3. Bind a Cluster Role to an OKTA account.

kubectl create clusterrolebinding oidc-cluster-admin --clusterrole=cluster-admin --user=https://iam-signin-stage.demo.net#00uo2cxe6sjhjhrbux0h7

4. verify the authentication

kubectl get pods --user=oidc

Kubectl executes kubelogin before calling the Kubernetes APIs. Kubelogin automatically opens the browser, and you can log in to the provider.

After authentication, kubelogin returns the credentials to kubectl and kubectl then calls the Kubernetes APIs with these credentials.

% kubectl get pods
Open http://localhost:8000 for authentication
NAME                          READY   STATUS    RESTARTS   AGE
echoserver-86c78fdccd-nzmd5   1/1     Running   0          26d

MTLS/Mutual TLS Authentication Chrome

Lets say you have a server which requires you to authenticate with your certificates which is shared with them. It is easy to make request via curl as shown below, however via chrome/mozilla browser, we need to import the certifcates into certificate-store.

Curl command:

curl --cert my-cert.pem --key my-key.pem https://my-mtls.server.net/api

How do we make the MTLS request through browser ?.

  1. Convert the my-cert.pem & my-key.pem into pfx/pkcs12 format using below commands- Commands tested in ubuntu 16.04
cat cert.pem key.pem > pkcs12.pem  # combine both private & public key.
openssl pkcs12 -in pkcs12.pem -export -out pkcs12.p12 # convert it into pkcs12 format


2.  Import the pkcs12.p12 into chrome certificate store using chrome import wizard as shown in the below screenshots.

  1. Now, when you access the API via browser, https://my-mtls.server.net/api you will be asked to choose the certificate by chrome as shown below.

manage-certificates-chrome-8

javax.net.ssl.SSLPeerUnverifiedException: SSL peer failed hostname validation for a name: null

Problem:

we have integrated Single Sign-On – SAML using  Spring-security-saml module in our application. spring-security-saml module internally uses Jakarta-commons-HTTP(http://jakarta.apache.org/httpcomponents/httpclient-3.x/) to connect to the IDP and get the metadata from IDP. As you see in the below bean configuration, we have custom Keystore configured for Jakarta TLSConnectionFactory. It was unable to make HTTPS connection to the IDP and get the latest metadata since our IDP has changed their public certificate.

@Bean
public ProtocolSocketFactory socketFactory() {
return new TLSProtocolSocketFactory(keyManager(), null, "default");
}

@Bean
public KeyManager keyManager() {
DefaultResourceLoader loader = new DefaultResourceLoader();
Resource storeFile = loader.getResource("classpath:/saml/samlKeystore.jks");
String storePass = keyStorePwd;
Map<String, String> passwords = new HashMap<String, String>();
passwords.put(keyStoreAlias, keyStorePwd);
String defaultKey = keyStoreAlias;
return new JKSKeyManager(storeFile, storePass, passwords, defaultKey);

}
""2019-06-14 07:40:07 - Error retrieving metadata from https://iam-sso.goole.net/oamfed/idp/metadata
"javax.net.ssl.SSLPeerUnverifiedException: SSL peer failed hostname validation for name: null
        at org.opensaml.ws.soap.client.http.TLSProtocolSocketFactory.verifyHostname(TLSProtocolSocketFactory.java:233)
        at org.opensaml.ws.soap.client.http.TLSProtocolSocketFactory.createSocket(TLSProtocolSocketFactory.java:186)
        at org.springframework.security.saml.trust.httpclient.TLSProtocolSocketFactory.createSocket(TLSProtocolSocketFactory.java:97)
        at org.apache.commons.httpclient.HttpConnection.open(HttpConnection.java:707)
        at org.apache.commons.httpclient.MultiThreadedHttpConnectionManager$HttpConnectionAdapter.open(MultiThreadedHttpConnectionManager.java:1361)
        at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:387)
        at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
        at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
        at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)
        at org.opensaml.saml2.metadata.provider.HTTPMetadataProvider.fetchMetadata(HTTPMetadataProvider.java:250)
        at org.opensaml.saml2.metadata.provider.AbstractReloadingMetadataProvider.refresh(AbstractReloadingMetadataProvider.java:255)
        at org.opensaml.saml2.metadata.provider.AbstractReloadingMetadataProvider.doInitialization(AbstractReloadingMetadataProvider.java:236)
        at org.opensaml.saml2.metadata.provider.AbstractMetadataProvider.initialize(AbstractMetadataProvider.java:407)
        at org.springframework.security.saml.metadata.ExtendedMetadataDelegate.initialize(ExtendedMetadataDelegate.java:167)
        at org.springframework.security.saml.metadata.MetadataManager.initializeProvider(MetadataManager.java:412)
        at org.springframework.security.saml.metadata.MetadataManager.refreshMetadata(MetadataManager.java:238)
        at org.springframework.security.saml.metadata.CachingMetadataManager.refreshMetadata(CachingMetadataManager.java:86)
        at org.springframework.security.saml.metadata.MetadataManager.afterPropertiesSet(MetadataManager.java:142)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:659)
        at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:659)
        at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
        at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:234)
        at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addAsRegistrationBean(ServletContextInitializerBeans.java:182)
        at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addAsRegistrationBean(ServletContextInitializerBeans.java:177)
        at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addAdaptableBeans(ServletContextInitializerBeans.java:159)
        at org.springframework.boot.web.servlet.ServletContextInitializerBeans.(ServletContextInitializerBeans.java:80)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.getServletContextInitializerBeans(EmbeddedWebApplicationContext.java:241)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.selfInitialize(EmbeddedWebApplicationContext.java:228)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.access$000(EmbeddedWebApplicationContext.java:89)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext$1.onStartup(EmbeddedWebApplicationContext.java:213)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.createEmbeddedServletContainer(EmbeddedWebApplicationContext.java:168)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:134)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:537)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693)
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
        at org.springframework.boot.web.support.SpringBootServletInitializer.run(SpringBootServletInitializer.java:151)
        at org.springframework.boot.web.support.SpringBootServletInitializer.createRootApplicationContext(SpringBootServletInitializer.java:131)
        at org.springframework.boot.web.support.SpringBootServletInitializer.onStartup(SpringBootServletInitializer.java:86)
        at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:169)
        at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5303)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
        at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:753)
        at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:729)
        at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:717)
        at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1129)
        at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1871)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
"2019-06-14 07:40:07 - Next refresh cycle for metadata provider 'https://iam-sso.google.net/oamfed/idp/metadata' will occur on '2019-06-14T07:45:07.062Z' ('2019-06-14T07:45:07.062Z' local time)
""2019-06-14 07:40:07 - Metadata provider failed to properly initialize, fail-fast=true, halting
"org.opensaml.saml2.metadata.provider.MetadataProviderException: org.opensaml.saml2.metadata.provider.MetadataProviderException: Error retrieving metadata from https://iam-sso.google.net/oamfed/idp/metadata
        at org.opensaml.saml2.metadata.provider.AbstractReloadingMetadataProvider.refresh(AbstractReloadingMetadataProvider.java:267)
        at org.opensaml.saml2.metadata.provider.AbstractReloadingMetadataProvider.doInitialization(AbstractReloadingMetadataProvider.java:236)
        at org.opensaml.saml2.metadata.provider.AbstractMetadataProvider.initialize(AbstractMetadataProvider.java:407)
        at org.springframework.security.saml.metadata.ExtendedMetadataDelegate.initialize(ExtendedMetadataDelegate.java:167)
        at org.springframework.security.saml.metadata.MetadataManager.initializeProvider(MetadataManager.java:412)
        at org.springframework.security.saml.metadata.MetadataManager.refreshMetadata(MetadataManager.java:238)
        at org.springframework.security.saml.metadata.CachingMetadataManager.refreshMetadata(CachingMetadataManager.java:86)
        at org.springframework.security.saml.metadata.MetadataManager.afterPropertiesSet(MetadataManager.java:142)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:659)
        at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
        at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:208)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1138)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:659)
        at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
        at org.springframework.boot.web.servlet.ServletContextInitializerBeans.getOrderedBeansOfType(ServletContextInitializerBeans.java:234)
        at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addAsRegistrationBean(ServletContextInitializerBeans.java:182)
        at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addAsRegistrationBean(ServletContextInitializerBeans.java:177)
        at org.springframework.boot.web.servlet.ServletContextInitializerBeans.addAdaptableBeans(ServletContextInitializerBeans.java:159)
        at org.springframework.boot.web.servlet.ServletContextInitializerBeans.(ServletContextInitializerBeans.java:80)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.getServletContextInitializerBeans(EmbeddedWebApplicationContext.java:241)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.selfInitialize(EmbeddedWebApplicationContext.java:228)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.access$000(EmbeddedWebApplicationContext.java:89)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext$1.onStartup(EmbeddedWebApplicationContext.java:213)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.createEmbeddedServletContainer(EmbeddedWebApplicationContext.java:168)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:134)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:537)
        at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693)
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:303)
        at org.springframework.boot.web.support.SpringBootServletInitializer.run(SpringBootServletInitializer.java:151)
        at org.springframework.boot.web.support.SpringBootServletInitializer.createRootApplicationContext(SpringBootServletInitializer.java:131)
        at org.springframework.boot.web.support.SpringBootServletInitializer.onStartup(SpringBootServletInitializer.java:86)
        at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:169)
        at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5303)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)
        at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:753)
        at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:729)
        at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:717)
        at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:1129)
        at org.apache.catalina.startup.HostConfig$DeployDirectory.run(HostConfig.java:1871)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)

Solution:

Looks like the IDP has changed their Public certificate which is not available in our local keystore (samlKeystore.jks). I manually downloaded their public certificate using OpenSSL command and imported the same using keytool utility.

Get the public certificate using OpenSSL command:

openssl s_client -showcerts -connect iam-sso.google.net:443 </dev/null 2>/dev/null|openssl x509 -outform PEM >mycertfile.pem

Import it into the Keystore:

keytool -import -alias "new-public-cert" -keystore /usr/share/tomcat8/webapps/ROOT/WEB-INF/classes/saml/samlKeystore.jks -file mycertfile.pem

 

 

java.util.zip.ZipException: error in opening zip file

This issue is mainly because of the corrupted JARS in your deployment war file.

unzip <artifact-name>.war

find <artifact-folder>  -name "*jar" | xargs -L 1 zip -T | grep error | grep invalid

The above command list out the JARS with bad signature or corrupted one.

Example:-

find /usr/share/tomcat8/webapps/ -name "*jar" | xargs -L 1 zip -T | grep -i error

Once you find the corrupted JARS, either manually replaces the JARS in the lib folder or Create a new build and deploy it.

Use of uninitialized value $u in substitution (s///) at /usr/share/perl5/Git/SVN.pm line 101

When I try to migrate SVN repository to GIT repository , I was getting the below error.

Use of uninitialized value $u in substitution (s///) at /usr/share/perl5/Git/SVN.pm line 101

Open the file /usr/share/perl5/Git/SVN.pm and go to line 101.

$u =~ s!^\Q$url\E(/|$)!! or die 
        "$refname: '$url' not found in '$u'\n"; 

to this: 

if(!$u) { 
        $u = $pathname; 
}else { 
        $u =~ s!^\Q$url\E(/|$)!! or die 
        "$refname: '$url' not found in '$u'\n"; 
}

GitLab Spring-Boot Heroku Continuous Integration and Deployment – part 2 (Review Apps)

In the previous post Gitlab-CI-Spring-Boot-Heroku-Continuous-integration-and-Deployment/, Every commit to master branch , Gitlab CI will build and deploy the application to two different environments (dev and stage). This is great. How about creating a new dynamic environment for every new merge request (branch) ? . The Repository owner can review the merge request and provide you the immediate feedback. Yes ! That is called “Review Apps” Review Apps

Objective:

  • Developers create a new branch “feature-xyz” and start implementing the features which he is supposed to complete as part of the Agile development process.
  • Developers commits the code, test it locally before pushing the branch to remote repository.
  • As soon as the branch (feature-xyz) is pushed to repository, Gitlab CI will trigger the pipeline ,which will deploy the feature branches into a  new dynamic environment.

merge-request-environment

Merge Request Environment

When a merge request is created , the corresponding dynamic environment is shown in the page as shown above.

dynamic environments

All the dynamic environments are grouped under review

environments-review

All the deployed environments are grouped under “Review”

As I have four feature branches in my repository , four environments get deployed by Gitlab CI. Each dynamic environment will be deleted once the corresponding branch is deleted. It can also be stopped manually from the Gitlab UI.

 

The complete gitlab-ci.yml is shown below.


variables:
REVIEW_APP_NAME: "$CI_COMMIT_REF_SLUG-$CI_PROJECT_NAME"
stages:
– build
– review
– deploy
build:
stage: build
image: maven:3.3.9-jdk-8
script:
– mvn clean package
tags:
– docker
– shared
review:
stage: review
image: ruby:2.3
script:
– apt-get update -qy
– apt-get install -y ruby-dev curl
– gem install dpl
– echo "$REVIEW_APP_NAME " $REVIEW_APP_NAME
– >-
curl
-n
-X POST https://api.heroku.com/apps
-d '
{
"name": "'"$REVIEW_APP_NAME"'",
"region": "us",
"stack": "heroku-16"
}
'
-H "Content-Type: application/json"
-H "Accept: application/vnd.heroku+json; version=3"
-H "Authorization: Bearer $HEROKU_API_KEY"
– dpl –provider=heroku –app=$REVIEW_APP_NAME –api-key=$HEROKU_API_KEY
environment:
name: review/$CI_COMMIT_REF_NAME
url: https://$CI_COMMIT_REF_SLUG-$CI_PROJECT_NAME.herokuapp.com
on_stop: stop_review
only:
– branches
except:
– master
tags:
– docker
– shared
stop_review:
stage: review
script:
– echo "environment is getting deployed !"
– >-
curl
-n
-X DELETE https://api.heroku.com/apps/$REVIEW_APP_NAME
-H "Content-Type: application/json"
-H "Accept: application/vnd.heroku+json; version=3"
-H "Authorization: Bearer $HEROKU_API_KEY"
variables:
GIT_STRATEGY: none
when: manual
environment:
name: review/$CI_COMMIT_REF_NAME
action: stop
deploy_dev:
stage: deploy
image: ruby:2.3
script:
– apt-get update -qy
– apt-get install -y ruby-dev
– gem install dpl
– dpl –provider=heroku –app=vel-ci-api-dev –api-key=$HEROKU_API_KEY
environment:
name: dev
url: https://vel-ci-api-dev.herokuapp.com
only:
– master
tags:
– docker
– shared
deploy_staging:
stage: deploy
image: ruby:2.3
script:
– apt-get update -qy
– apt-get install -y ruby-dev
– gem install dpl
– dpl –provider=heroku –app=vel-ci-api-stg –api-key=$HEROKU_API_KEY
environment:
name: staging
url: https://vel-ci-api-stg.herokuapp.com
only:
– master
tags:
– docker
– shared
deploy_local:
stage: deploy
script:
– curl -s https://raw.githubusercontent.com/velmuruganvelayutham/shell-scripts/master/deploy.sh | bash -s
environment:
name: local-vm
url: http://dev.mock-labs.com
when: manual
only:
– master
tags:
– shell-osboxes

view raw

.gitlab-ci.yml

hosted with ❤ by GitHub

Review : Responsible for creating a new environment for every new branch

Stop_Review : Responsible for deleting the environment whenever the branch is deleted or manually stopped from Gitlab UI.

Gitlab Source Code of this repo

 

GitLab Spring-Boot Heroku Continuous Integration and Deployment – part 1

Gitlab has in built pipeline for continuous integration and deployment. For every commit to the master branch , Gitlab CI will build and deploy my code to two(dev and staging) different Environments.

Prerequisites 

  1. Gitlab free account https://gitlab.com/
  2. Heroku free account https://www.heroku.com

 

Below is the .gitlab-ci.yml I use to build and deploy the application dev and staging environments

before_script:
 - echo "Execute scripts which are required to bootstrap the application. !"

after_script:
 - echo "Clean up activity can be done here !."
 
cache:
 paths:
 - /root/.m2/repository
 
stages:
 - build
 - deploy

build:
 stage: build
 image: maven:3.3.9-jdk-8
 script:
 - mvn clean package
 tags:
 - docker
 
deploy_dev:
 stage: deploy
 image: ruby:2.3
 script:
 - apt-get update -qy
 - apt-get install -y ruby-dev
 - gem install dpl
 - dpl --provider=heroku --app=vel-ci-api-dev --api-key=$HEROKU_API_KEY
 environment:
 name: dev
 url: https://vel-ci-api-dev.herokuapp.com
 only:
 - master
 tags:
 - docker
 
deploy_staging:
 stage: deploy
 image: ruby:2.3
 script:
 - apt-get update -qy
 - apt-get install -y ruby-dev
 - gem install dpl
 - dpl --provider=heroku --app=vel-ci-api-stg --api-key=$HEROKU_API_KEY
 environment:
 name: staging
 url: https://vel-ci-api-stg.herokuapp.com
 only:
 - master
 tags:
 - docker

I have two stages , build and deploy

build stage has only one job – build

deploy stage has two jobs – deploy_dev , deploy_staging

pipe-line-success

Pipeline-stages-builds

All the jobs in the same stage will be run parallel.

Below are the environments it gets deployed the application.

environments

Environments

 

Application URL’s

https://vel-ci-api-dev.herokuapp.com — Dev Environment.

https://vel-ci-api-stg.herokuapp.com/ — Staging Environment.

 

 

InitializationError java.lang.IllegalStateException: Failed to transform class with name Processor.java. Reason: java.io.IOException: invalid constant type: 18

When we upgraded our application to jdk-8 , I was getting the below error during my gradle test task.

initializationError java.lang.IllegalStateException: Failed to transform class with name Processor.java. Reason: java.io.IOException: invalid constant type: 18

The issue was one of my class was using javasssit library which is not compatible with java-8. So we have upgraded javassist to the latest version.

 

compile(group: ‘org.javassist’, name: ‘javassist’, version: ‘3.20.0-GA’)

Reference:

https://github.com/brettwooldridge/HikariCP/issues/83

https://github.com/bitronix/btm/issues/38

 

 

 

steps to upgrade jdk-8 & gradle-2.11

Upgrading java using update-alternatives


  1. Download the latest java-8 binary zip from official oracle downloads
  2. Create a new folder  under /opt
     mkdir -p /opt/jdk-8
  3. Extract the gzip file to the /opt/jdk-8 directory
     tar -zxf jdk-8u5-linux-x64.tar.gz -C /opt/jdk-8
  4. Setting Oracle JDK as the default JVM
    update-alternatives --install /usr/bin/java java  /opt/jdk-8/jdk1.8.0_74/bin/java 2000
    update-alternatives --install /usr/bin/javac  javac /opt/jdk-8/jdk1.8.0_74/bin/javac 2000
  5. Configure java if more than one java version is available already.
     update-alternatives --config java

    choose the correct number if prompted .

  6.  Verify the installation
     java –version

Upgrading gradle manually.


  1. Download the latest gradle binary file from gradle-2.11-all.zip
  2. Create a new folder  under /opt
     mkdir -p /opt/gradle-2.11
  3. Extract the gzip file to the /opt/gradle-2.11 directory
     tar -zxf gradle-2.11.all.zip -C /opt/gradle-2.11
  4. Create a symbolic link to the original file.
     ln -s /opt/gradle-2.11/bin/gradle /usr/local/bin/gradle
  5. Verify the installation
     gradle –version

Note:
gradle –version -> may still use the older version of java  if  JAVA_HOME is not  points to the latest jdk-8 directory.

 export JAVA_HOME=/opt/jdk-8/jdk1.8.0_74
 export PATH=$PATH:$JAVA_HOME/bin

reference:
https://www.digitalocean.com/community/tutorials/how-to-manually-install-oracle-java-on-a-debian-or-ubuntu-vps