cyberark / conjur-spring-boot-sdk

Apache License 2.0
5 stars 7 forks source link

Adding option to use access token instead of API key #73

Open huydd79 opened 1 year ago

huydd79 commented 1 year ago

Is your feature request related to a problem? Please describe.

Conjur Authentication using API key is not good approach in term of security because it cannot help to solve the secret zero problem. Especially when applications are running in platform that can utilise more secure authentication methods (such as K8s, IAM, JWT...). An init/side car container can be run to connect to Conjur using secure methods, get the access token content and share to application in main container for secret request. So this plugin should have the capability of getting this access token content and communicate with conjur instead of doing authentication all the time with API key.

Describe the solution you would like

Short live access token will be shared to application via file. File content will be retrieved by an init or sidecar container which can use better secure authentication methods (instead of API key) as mentioned in this article https://www.conjur.org/blog/three-steps-to-avoiding-the-secret-zero-trap/ This plugin then can get the access token file path from environment variable, reading its content and use access token value directly instead of doing authentication using API Key.

I have tested this in my clone repo by adding custom code in ConjurConnectionManager.java at line 32 Changing from old code

        private void getConnection() {
                try {
                        ApiClient client = Configuration.getDefaultApiClient();
                        AccessToken accesToken = client.getNewAccessToken();
                        if (accesToken == null) {
                                logger.error("Access token is null, Please enter proper environment variables.");
                        }
                        String token = accesToken.getHeaderValue();
                        client.setAccessToken(token);
                        Configuration.setDefaultApiClient(client);
                        logger.debug("Connection with conjur is successful");
                } catch (Exception e) {
                        logger.error(e.getMessage());
                }

        }

To new code

private void getConnection() { 
        String token="";        
        try {
            token = Files.readString(Paths.get(System.getenv("CONJUR_ACCESS_TOKEN_FILE")));
        } catch (Exception e){
            logger.error(e.getMessage());
        }

        try {
            ApiClient client = Configuration.getDefaultApiClient();
            if (token.isBlank()){
                AccessToken accesToken = client.getNewAccessToken();
                if (accesToken == null) {
                    logger.error("Access token is null, Please enter proper environment variables.");
                }
                token = accesToken.getHeaderValue();
                logger.info("Token does not exist, getting new token.");                    
                logger.debug("New token: " + token);
            } else {
                logger.info("Token existed, using current value.");
                logger.debug("Current token: " + token);
            }

            client.setAccessToken(token);
            Configuration.setDefaultApiClient(client);
            logger.debug("Connection with conjur is successful");
        } catch (Exception e) {
            logger.error(e.getMessage());
        }

    }

I also did testing by generate the access token directly from conjur container and share to application, using below scripts.

#!/bin/bash -ex

set -x 
docker-compose run \
  --volume "${PWD}:${PWD}" \
  --workdir "${PWD}" \
  --rm \
  --no-deps \
  client \
  -ec "conjur authn authenticate -H | awk '{print \$3}' | tr -d '\n' > authn-token"
sleep 2

docker-compose run \
  --volume "$(git rev-parse --show-toplevel):/repo" \
  --volume "${PWD}/maven_cache":/root/.m2 \
  --volume "${PWD}/api_key:/api_key" \
  --volume "${PWD}/conjur-dev.pem:/conjur-dev.pem" \
  --volume "${PWD}/authn-token:/authn-token" \
  -e CONJUR_ACCESS_TOKEN_FILE=/authn-token \
  --workdir "/repo" \
  --no-deps \
  --rm \
  --entrypoint /bin/bash \
app \
  -ec 'cd SpringBootExample; mvn spring-boot:run'

set +x

This method will be quicker and more secure for application, solving the secret zero problem.

Describe alternatives you have considered

Using access token will get better response time than doing authentication for every request. We can add in the expiration checking to see if the token has expired before use. If access token is expired or not available, the normal API key authentication can be used.

Additional context

Please let me know your thinkings and discuss on this if needed Also can send email to me via huy.do@cyberark.com