1. CI environments

Lets say you have a CI build on jenkins using tools like Sonar.

pipeline{
    ...
    
    stages{
        stage('Sonar Analysis'){
            withCredentials([ string(credentialsId: 'mySonarJenkinsToken', variable: 'TOKEN')]){
                echo 'running sonar analysis'
                sh"""
                    mvn sonar:sonar -Dsonar.login=$TOKEN
                """
            }
        }
    }
}

another pipeline to deploy changes on your DB with Liquibase

pipeline {
    ...

    stages {
        stage('Update PostgreDB') {
            withCredentials([usernamePassword(credentialsId: 'myPostgreCredentials', usernameVariable: 'USER', passwordVariable: 'PASSWORD')]) {
                echo 'running liquibase'
                sh """
                    mvn compile -Dpostgre.user=$USER -Dpostgre.password=$PASSWORD
                """
            }
        }
    }
}

and you have another CD pipeline to deploy your app on K8s

pipeline {
    ...
    stages {
        stage('Deploying app to K8s') {
            withCredentials([file(credentialsId: 'myKubeConfig', variable: 'KUBECONFIG')]) {
                echo 'deploying app via kubectl'
                sh """
                    kubectl apply -f deployment.yaml --namespace ns-my-namespace
                """
            }
        }
    }
}

As you can see you are using lots of secrets to run all those pipelines, which have to be stored on jenkins. If you use the maven vault plugin and you declare them in your pom.xml with associated secret key from Vault, you don’t need to store them on Jenkins anymore. Then you can have simple pipelines like the following.

pipeline {
    ...

    stages {
        withCredentials([usernamePassword(credentialsId: 'myVaultAppRole', usernameVariable: 'ROLEID', passwordVariable: 'SECRET')]) {
            stage('Sonar Analysis') {
                echo 'running sonar analysis'
                sh """
                    mvn sonar:sonar -DroleId=ROLEID -DsecretId=SECRET
                """
            }

            stage('Update PostgreDB') {
                echo 'running liquibase'
                sh """
                    mvn compile -DroleId=ROLEID -DsecretId=SECRET
                """
            }

            stage('Deploying app to K8s') {
                echo 'deploying app via kubectl'
                sh """
                    mvn vault:pull -D"vault.outputMethod=EnvFile" -D"vault.roleId=$ROLEID" -D"vault.secretId=$SECRET"
                    source .env
                    kubectl apply -f deployment.yaml --namespace ns-my-namespace
                """
            }
        }
    }
}

2. Production environments

Let’s say you have an application running on K8s, and you deploy your app and its secrets via a jenkins CD pipeline. Best way to upload your secrets to K8s is the vault plugins or agents.

But this will require using a new config file where you declare your secrets (agent-config.hcl and secret-template.yaml). Your pipeline will look like this.

stage('vault agent'){
    withEnv(["VAULT_NAMESPACE=myNameSpace"]){}
        withCredentials([usernamePassword(credentialsId: 'appRole', usernameVariable: 'ROLEID', passwordVariable: 'SECRET')]) {
            sh """
                echo ROLEID > roleid
                echo SECRET > secretid
                vault agent -config ./agent-config.hcl
            """
        }
    }
}

Instead with the help of vault-maven-plugin, you can do this.

stage('vault maven plugin'){
    withCredentials([usernamePassword(credentialsId: 'appRole', usernameVariable: 'ROLEID', passwordVariable: 'SECRET')]){
        sh"""
            mvn vault:pull -D"vault.outputMethod=EnvFile" -D"vault.roleId=$ROLEID" -D"vault.secretId=$SECRET"
            source .env #to make kubectl work with KUBECONFIG env variable
            kubectl create secret generic my-app-secret --from-env-file=./.env
        """
    }
}

For other environments like VMs or bare metal servers, or if you do not need to store you secrets on K8s cause you do not need a quick startup, you can use this plugin to run your application via maven, just like you do in local (see next chapter below).

If you are interested in all the best practices for managing your secrets on PROD, see the blog post here,


3. Local development environments

If you want to debug your spring application locally, there are two ways to do this:

  1. In IntelliJ, you can run and debug maven goals, see here.
    If you want to debug, the catch is not to let spring boot plugin to fork its JVM from the initial maven process. see here

    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
            <fork>false</fork>
        </configuration>
    </plugin>
    

    A working demo project is available here.

  2. Or, if you want to fork the JVM and debug only your application, you can set a remote debugger on any IDE by adding spring boot JVM args for the debugger connexion.

    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
            <jvmArguments>-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8282</jvmArguments>
        </configuration>
    </plugin>