SAS Communities Library

We’re smarter together. Learn from this collection of community knowledge and add your expertise.
BookmarkSubscribeRSS Feed

SAS Viya CLI container image in Azure DevOps, security topics – part 2 ->Certificates & Tokens

Started ‎07-23-2024 by
Modified ‎07-23-2024 by
Views 850

Welcome back! This second post serves as a follow-up to our previous post, where we initiated discussions on DevOps processes with SAS Viya CLI container images, available here, Introducing New SAS Viya CLI container image in Azure DevOps (March 2024 Update). In this two-part blog, we delve deeper into security customizations, addressing aspects such as managing refresh tokens, handling certificates not chained to public Certificate Authorities (CAs), and enhancing security measures within Azure Pipelines and Kubernetes clusters. These enhancements build upon our previous discussions, aiming to fortify your DevOps pipelines against security vulnerabilities while ensuring seamless execution. Let's explore these advancements in detail.

 

Here's an illustrative diagram presenting the functional flow involved.

de_1_SAS-Viya-CLI-container-image.png

 

Managing the Use / Revocation of the Refresh-Token

 

Why does it matter?

 

Access tokens play a pivotal role in facilitating secure communication between various components. However, the limitations of access tokens, such as their short lifespan of 1 hour (default for the SAS Viya) and irreversibility, often pose challenges. .

Here is a link from my colleague Stuart Rogers  that explain the SAS Viya CLI Token Expiry (additional note, starting with 2023.11 the default refresh token expiration is 14 days).

Unlike access tokens, refresh tokens have a longer validity period of 14 days and are revocable, providing better control and flexibility in managing authentication within DevOps pipelines.

In this example, we will show you how to consume the SAS Refresh Token (retrieved from your Azure Key Vault) while running your Azure pipeline.

 

Using the Refresh-Token while executing your DevOps pipeline

 

Step 1: Define the pipeline parameter

 

This Azure Pipeline parameter allows you to specify whether you want to use the refresh token during pipeline execution, providing flexibility in authentication.

- name: USE_REFRESH_TOKEN
    displayName: Do you want to use the refresh token (valid 14 days)?
    type: boolean
    default: true

 

Step 2: Modify pipeline task

 

Enhance the step titled "Get JSON Web Token from Azure Key Vault" to extract the refresh token and its expiry date:

- task: AzureCLI@2
    name: TgetJWTfromAKV
    displayName: Get JSON Web Token from Azure Key Vault
    inputs:
    azureSubscription: '$(KV_SERVICE_CONNECTION)'
    scriptType: 'bash'
    scriptLocation: 'inlineScript'
    inlineScript: |
    az keyvault secret download \
        --name ${{ parameters.AZURE_KEYVAULT_KEY }} \
        --vault-name ${{ parameters.AZURE_KEYVAULT }} \
        --file $PWD/credentials.json
    export JWT_TOKEN=$(cat $PWD/credentials.json | jq -r '.Default."access-token"')
    export REFRESH_TOKEN=$(cat $PWD/credentials.json | jq -r '.Default."refresh-token"')
    export EXPIRY=$(cat $PWD/credentials.json | jq -r '.Default."expiry"')
    echo "##vso[task.setvariable variable=JWT_TOKEN;isOutput=true]$JWT_TOKEN"
    echo "##vso[task.setvariable variable=REFRESH_TOKEN;isOutput=true]$REFRESH_TOKEN"
    echo "##vso[task.setvariable variable=EXPIRY;isOutput=true]$EXPIRY"

 

Step 3: Add Context Variables

 

Add the variable to the context of the container job:

variables:
    - name: JWT_TOKEN
    value: $[ dependencies.getJWTfromAKV.outputs['TgetJWTfromAKV.JWT_TOKEN'] ]
    - name: REFRESH_TOKEN
    value: $[ dependencies.getJWTfromAKV.outputs['TgetJWTfromAKV.REFRESH_TOKEN'] ]
    - name: EXPIRY
    value: $[ dependencies.getJWTfromAKV.outputs['TgetJWTfromAKV.EXPIRY'] ]

 

Step 4: Add pipeline task

 

This step creates a credentials file containing the refresh token, access token, and expiry date, allowing the use of the refresh token mechanism.

- task: Bash@3
    displayName: Generate credentials.json
    enabled: ${{ eq(parameters.USE_REFRESH_TOKEN, true) }}
    inputs:
    targetType: 'inline'
    script: |
    jq -n \
        --arg access-token $JWT_TOKEN \
        --arg expiry $EXPIRY \
        --arg refresh-token $REFRESH_TOKEN \
        '{Default: $ARGS.named}'>~/.sas/credentials.json

Reminder: In my previous post, I was leveraging the JWT_TOKEN environment variable as the authentication mechanism. Now, I will be using the credentials.json file.

With these improvements in place, you should be able to use the refresh token mechanism.

 

Revoke the refresh token after execution

 

Revoking refresh tokens after use is a security practice aimed at mitigating the risk of unauthorized access to resources. Refresh tokens are long-lived credentials used to obtain new access tokens without requiring the user to re-enter their credentials. However, if a refresh token is compromised or falls into the wrong hands, it can be abused to gain ongoing access to sensitive data or perform unauthorized actions.

Here are the steps needed to implement the revocation of the refresh token when you have completed all your DevOps tasks with your SAS Viya CLI.

 

Step 1: Define the parameter

 

Define an Azure Pipeline parameter to determine whether you want to revoke the refresh token after use:

- name: REVOKE_REFRESH_TOKEN
    displayName: Do you want to revoke the refresh token after use ?
    type: boolean

 

Step 2: Add pipeline task

 

Include a step to log out using the SAS Viya CLI and, subsequently, revoke the refresh token:

- task: Bash@3
    displayName: Log out (revoke the Refresh Token)
    enabled: ${{ eq(parameters.REVOKE_REFRESH_TOKEN, true) }}
    inputs:
    targetType: 'inline'
    script: |
        /opt/sas/viya/home/bin/sas-viya auth logout

The log out will sign you out and revoke your refresh token.
Note: If your Access token is still valid it will remain valid until its expiration.

By incorporating these practices, you can effectively manage the use and revocation of refresh tokens within your DevOps processes, enhancing security and control over authentication mechanisms.

 

Handling certificates not chained to a public Certificate Authority

 

By default, the SAS Viya platform is configured to trust the CA certificates that are distributed by mozilla.org. By including this bundle of trusted CA certificates, you will be able to connect to your platform (signed by one of these CA) without any additional configuration.

If you are using a self/site-signed certificates, you will need to add your CA to your trust store in order to validate the certificate that will be send by your SAS Deployment.

When your SAS Viya platform's certificate is not chained to a public CA, it poses a challenge to secure communication. Without proper validation, your system may reject connections, leading to potential disruptions in your pipeline. But fear not, as we've outlined steps to navigate this challenge seamlessly.

The issue of untrusted authority can rear its head at two critical junctures in your pipeline process:

  1. During Credential Generation: Using the sas-viya-cli, you might encounter verification failures when generating credentials. This can halt your DevOps process before it even begins.
  2. In Azure DevOps Pipelines: Similar challenges can arise when running pipelines, necessitating additional steps to ensure smooth execution.

 

During credential generation

 

During the initial stages of setting up your DevOps pipeline, you may encounter verification failures when generating credentials. Let's break down the steps to address this issue:

 

Step 1: Identify verification failure

 

Using the check_certificatescript, you can identify verification failures. Here's a snippet of code illustrating this step (full code is presented in Annex 1):

# Set some variables
SAS_SERVICES_DNS=yoursasviyaurl.company.com
# Can be retrieved automatically if you have configured kubectl
# NS=gelenv; SAS_SERVICES_DNS=$(kubectl -n ${NS} get configmap -o=jsonpath='{.items[*].data.SAS_SERVICES_URL}')
# Example usage: check_certificate <hostname> <port>
check_certificate "$SAS_SERVICES_DNS" 443
# Example usage: check_certificate <hostname> <port> <truststore>
check_certificate "$SAS_SERVICES_DNS" 443 $HOME/trustedcerts.pem

log output:

fradae@cldlgn01:~$ check_certificate "$SAS_SERVICES_DNS" 443
Certificate verification: Failed (Unable to verify the first certificate)
fradae@cldlgn01:~$ check_certificate "$SAS_SERVICES_DNS" 443 $HOME/trustedcerts.pem
Certificate verification: OK

 

Step 2: Download the trust store

 

Downloading the SAS Viya Platform trust store is crucial for establishing secure connections. Use the provided command to retrieve it:

# Set some variables
NS=gelenv
# Extract the trust store file and store it in the $HOME directory
kubectl -n $NS cp $(kubectl get pod -n $NS | grep "sas-logon-app" | head -1 | awk -F" " '{print $1}'):security/trustedcerts.pem ~/trustedcerts.pem

 

Step 3: Utilize the trust store

 

Ensure seamless authentication by mounting the trust store as a volume in your Docker container:

docker run --rm -it -v /$HOME:/security -v $PWD:/cli-home/.sas -e SAS_SERVICES_ENDPOINT $DOCKER_REGISTRY/viya-4-x64_oci_linux_2-docker/sas-viya-cli:latest auth loginCode

Note: Adding /$HOME:/security as a mounted volume ensures that trustedcerts.pem is readily accessible. Without the trust store, login attempts may fail due to unrecognized authorities. Your trust store must be named exactly trustedcerts.pem.

If you try without the trust store, you'll receive the following ERROR message: Login failed due to an error with the security certificate. The certificate is signed by an unknown authority. Run with the ‘–verbose' global option to see additional details.

 

In Azure DevOps pipelines

 

In your Azure DevOps pipelines, additional steps are necessary to ensure smooth execution when certificates are not chained to public CAs.

 

Step 1: Define pipeline parameter

 

Define a parameter to determine whether importing the trust store from your SAS Viya platform is necessary:

- name: PUB_SIGN_CERT
    displayName: TLS certificate is chained to a public CA?
    type: boolean
    default: true

 

Step 2: Incorporate Bash task

 

Add a Bash task to retrieve the trusted certificates just before executing the SAS Viya CLI command:

- task: Bash@3
    displayName: Get the Trusted Certs (if not publicly available)
    enabled: ${{ eq(parameters.PUB_SIGN_CERT, false) }}
    inputs:
    targetType: 'inline'
    script: |
        kubectl -n ${K8S_NAMESPACE} cp $(kubectl get pod -n ${K8S_NAMESPACE} | grep "sas-logon-app" | head -1 | awk -F" " '{print $1}'):security/trustedcerts.pem /tmp/trustedcerts.pem

 

Step 3: Modify Bash task

 

Modify the pipeline step titled "Retrieve all configuration definition list" to utilize the downloaded trust store:

- task: Bash@3
    displayName: Retrieve all configuration definition list
    inputs:
    targetType: 'inline'
    script: |
        if [ -f /tmp/trustedcerts.pem ]; then
        export SSL_CERT_FILE=/tmp/trustedcerts.pem
        fi
        cd /opt/sas/viya/home/bin
        /opt/sas/viya/home/bin/sas-viya-wrapper --output text configuration configurations list

By completing these steps, you ensure secure communication with your SAS Viya platform, even when using certificates not chained to a public CA.

 

Conclusion

 

By effectively managing refresh tokens, handling certificates and enhancing security measures within Azure pipelines and Kubernetes clusters, organizations can fortify their DevOps pipelines against potential security threats. These practices not only ensure security but also ensure the reliability and integrity of your applications and data in cloud environments.

 

Annex 1

#!/bin/bash
 
# Function to check server certificate verification status
check_certificate() {
    hostname="$1"
    port="$2"
    trusted_ca_file="${3:-}"
 
    # Prepare CAfile option if trusted_ca_file is provided
    cafile_option=""
    if [ -n "$trusted_ca_file" ]; then
        cafile_option="-CAfile $trusted_ca_file"
    fi
 
    # Run openssl s_client command and capture output
    output=$(echo X | openssl s_client -connect "$hostname":"$port" -servername "$hostname" $cafile_option 2>&1)
 
    # Look for the "Verify return code" line and extract the return code
    verify_return_code=$(echo "$output" | grep -oP "Verify return code: \K\d+")
 
    # Check the verification status and print detailed information
    case "$verify_return_code" in
        0)
            echo "Certificate verification: OK"
        ;;
        10)
            echo "Certificate verification: Failed (Certificate has expired)"
        ;;
        20)
            echo "Certificate verification: Failed (Unable to get local issuer certificate)"
        ;;
        21)
            echo "Certificate verification: Failed (Unable to verify the first certificate)"
        ;;
        18)
            echo "Certificate verification: Failed (Self-signed certificate)"
        ;;
        *)
            echo "Certificate verification: Failed (Unknown reason, Verify return code: $verify_return_code)"
        ;;
    esac
}

 

Additional Azure DevOps Resources with SAS Viya

 

David Estreich (myself) related posts with Azure DevOps:

Bogdan Teleuca's other posts about SAS Viya with Azure DevOps:

Version history
Last update:
‎07-23-2024 02:08 PM
Updated by:
Contributors

sas-innovate-white.png

Our biggest data and AI event of the year.

Don’t miss the livestream kicking off May 7. It’s free. It’s easy. And it’s the best seat in the house.

Join us virtually with our complimentary SAS Innovate Digital Pass. Watch live or on-demand in multiple languages, with translations available to help you get the most out of every session.

 

Register now!

SAS AI and Machine Learning Courses

The rapid growth of AI technologies is driving an AI skills gap and demand for AI talent. Ready to grow your AI literacy? SAS offers free ways to get started for beginners, business leaders, and analytics professionals of all skill levels. Your future self will thank you.

Get started

Article Tags