SAS Viya CLI container image in Azure DevOps, security topics – part 2 ->Certificates & Tokens
- Article History
- RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
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.
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:
- 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.
- 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:
- SAS Viya CLI container image in Azure DevOps (March 2024 Update)
- SAS Viya CLI container image in Azure DevOps, security topics – Part 1 -> IP Restrictions
- Unlocking Efficiency with Container Jobs in Azure DevOps
- Streamlining SAS Viya Authentication in Azure DevOps
Bogdan Teleuca's other posts about SAS Viya with Azure DevOps: