Kubernetes Cluster: EKS cluster running Kubernetes 1.19+ Node Resources: Minimum 2 CPU cores and 4GB RAM per node where sensors will run Network Access: Outbound HTTPS (443) access to Qualys Cloud Platform Storage: Persistent storage for sensor data and logs (recommended: 10GB minimum)
Qualys Subscription: Active Qualys VMDR or Container Security subscription Service Account: Qualys service account with Container Security permissions Kubernetes Access: kubectl access with cluster-admin privileges Helm: Helm 3.x installed and configured
For Helm Chart Deployment: Customer ID: Your Qualys Customer ID Activation ID: Obtained from Qualys console Gateway URL: Your Qualys platform gateway URL CMSQAG Public URL: Your Qualys container management service URL AWS EKS Cluster ARN: Your cluster's AWS Resource Name (for AWS deployments) Cluster Name: Your cluster name (for self-managed K8s deployments)
For QScanner Integration: Access Token: Qualys API access token Pod: Your Qualys pod identifier (e.g., US2, US3, EU1, etc.)
The Qualys Container Sensor deploys using the unified helm chart 'qualys-tc' which can install multiple sensor types.
Available Sensor Components: Cluster Sensor: Provides cluster-level visibility and scanning QCS Sensor: Container Security sensor for vulnerability scanning and SCA Admission Controller: Policy enforcement at container admission time Runtime Sensor: Real-time runtime protection and monitoring
Deployment Resources: DaemonSet: Runs on each node to scan running containers Deployment: Central sensor management component ConfigMaps: Configuration and policy definitions Secrets: Secure credential storage Services: Internal communication endpoints
QCS Sensor Features: SCA Scanning: Software Composition Analysis (performScaScan=true) Storage Driver: Enhanced container filesystem access (enableStorageDriver=true) Console Logs: Real-time logging output (enableConsoleLogs=true) Storage Options: Persistent or non-persistent storage modes
# Install Helm (if not already installed)
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# Verify Helm installation
helm version# Add Qualys Helm repository
helm repo add qualys-helm-chart https://qualys.github.io/Qualys-Helm-Charts/
# Update repository
helm repo updateGet your AWS EKS cluster ARN:
# Get your cluster ARN
aws eks describe-cluster --name <your-cluster-name>
# Look for output similar to:
# "arn": "arn:aws:eks:us-west-2:123456789012:cluster/your-cluster-name"Deploy all Qualys sensors using the official qualys-tc Helm chart:
# Install Qualys Container Sensor with all components enabled
helm upgrade --install qualys-tc qualys-helm-chart/qualys-tc \
--set global.customerId=<your-customer-id> \
--set global.activationId=<your-activation-id> \
--set global.cmsqagPublicUrl=<your-cmsqag-public-url> \
--set global.gatewayUrl=<your-gateway-url> \
--set clusterSensor.enabled=true \
--set qcsSensor.enabled=true \
--set admissionController.enabled=true \
--set runtimeSensor.enabled=true \
--set qcsSensor.qualys.args.performScaScan=true \
--set qcsSensor.qualys.args.enableStorageDriver=true \
--set qcsSensor.qualys.args.withoutPersistentStorage=true \
--set qcsSensor.qualys.args.enableConsoleLogs=true \
--set qcsSensor.qualys.args.storageDriverType=overlay \
--set global.clusterInfoArgs.cloudProvider=AWS \
--set global.clusterInfoArgs.AWS.arn=<your-cluster-arn> \
--namespace qualys \
--create-namespaceFor self-managed Kubernetes clusters:
# Install for self-managed K8s cluster
helm upgrade --install qualys-tc qualys-helm-chart/qualys-tc \
--set global.customerId=<your-customer-id> \
--set global.activationId=<your-activation-id> \
--set global.cmsqagPublicUrl=<your-cmsqag-public-url> \
--set global.gatewayUrl=<your-gateway-url> \
--set clusterSensor.enabled=true \
--set qcsSensor.enabled=true \
--set admissionController.enabled=true \
--set runtimeSensor.enabled=true \
--set qcsSensor.qualys.args.performScaScan=true \
--set qcsSensor.qualys.args.enableStorageDriver=true \
--set qcsSensor.qualys.args.withoutPersistentStorage=true \
--set qcsSensor.qualys.args.enableConsoleLogs=true \
--set qcsSensor.qualys.args.storageDriverType=overlay \
--set global.clusterInfoArgs.cloudProvider=SELF_MANAGED_K8S \
--set global.clusterInfoArgs.SELF_MANAGED_K8S.clusterName=<your-cluster-name> \
--namespace qualys \
--create-namespaceYou can enable specific sensors based on your requirements:
# Deploy only Cluster Sensor and QCS Sensor
helm upgrade --install qualys-tc qualys-helm-chart/qualys-tc \
--set global.customerId=<your-customer-id> \
--set global.activationId=<your-activation-id> \
--set global.cmsqagPublicUrl=<your-cmsqag-public-url> \
--set global.gatewayUrl=<your-gateway-url> \
--set clusterSensor.enabled=true \
--set qcsSensor.enabled=true \
--set admissionController.enabled=false \
--set runtimeSensor.enabled=false \
--set global.clusterInfoArgs.cloudProvider=AWS \
--set global.clusterInfoArgs.AWS.arn=<your-cluster-arn> \
--namespace qualys \
--create-namespace# Check deployment status
helm status qualys-tc -n qualys
# Verify all sensor pods are running
kubectl get pods -n qualys
# Check DaemonSets (for Runtime and QCS sensors)
kubectl get daemonset -n qualys
# Check Deployments (for Cluster sensor and Admission Controller)
kubectl get deployment -n qualys
# View specific sensor logs
kubectl logs -l app.kubernetes.io/name=cluster-sensor -n qualys --tail=50
kubectl logs -l app.kubernetes.io/name=qcs-sensor -n qualys --tail=50
kubectl logs -l app.kubernetes.io/name=runtime-sensor -n qualys --tail=50Global Configuration: global.customerId: Your unique Qualys customer identifier global.activationId: Activation token for sensor registration global.gatewayUrl: Qualys platform gateway for API communication global.cmsqagPublicUrl: Container management service endpoint
QCS Sensor Configuration: qcsSensor.qualys.args.performScaScan=true: Enables Software Composition Analysis qcsSensor.qualys.args.enableStorageDriver=true: Provides deeper filesystem access qcsSensor.qualys.args.withoutPersistentStorage=true: Uses ephemeral storage qcsSensor.qualys.args.storageDriverType=overlay: Specifies storage driver type
Cloud Provider Settings: For AWS: Use cloudProvider=AWS and provide cluster ARN For Self-managed: Use cloudProvider=SELF_MANAGED_K8S and provide cluster name
QScanner is a command-line utility that scans for vulnerabilities with inline vulnerability reports directly in your command-line interface. It integrates into existing workflows with zero installation required.
Key QScanner Features: Zero Installation: Standalone executable with no setup required Versatile Image Scanning: Supports Docker, Containerd, Cri-O, Podman, and remote registries Immediate Results: Instant vulnerability reports in console with multiple output formats Fast Performance: Utilizes local caching for quicker scans Policy Enforcement: Centralized policy management through Qualys Portal Comprehensive Security: Enterprise-grade vulnerability management
# Download QScanner (replace with your platform-specific binary)
curl -L -o qscanner https://downloads.qualys.com/qscanner/linux/qscanner
chmod +x qscanner
# Verify installation
./qscanner --helppipeline {
agent any
environment {
QUALYS_ACCESS_TOKEN = credentials('qualys-access-token')
QUALYS_POD = 'US2'
IMAGE_NAME = "myapp:${BUILD_NUMBER}"
}
stages {
stage('Build') {
steps {
script {
// Build your container image
sh "docker build -t ${IMAGE_NAME} ."
}
}
}
stage('Security Scan with QScanner') {
steps {
script {
// Download QScanner if not available
sh '''
if [ ! -f "./qscanner" ]; then
curl -L -o qscanner https://downloads.qualys.com/qscanner/linux/qscanner
chmod +x qscanner
fi
'''
// Run QScanner on the built image
sh """
./qscanner image ${IMAGE_NAME} \
--access-token \$QUALYS_ACCESS_TOKEN \
--pod \$QUALYS_POD
"""
}
}
post {
always {
// Archive any scan output files
archiveArtifacts artifacts: '*.json', allowEmptyArchive: true
}
}
}
stage('Deploy') {
when {
expression {
currentBuild.result == null || currentBuild.result == 'SUCCESS'
}
}
steps {
// Deploy only if scan passes
sh 'kubectl apply -f k8s-manifests/'
}
}
}
}pipeline {
agent any
environment {
QUALYS_ACCESS_TOKEN = credentials('qualys-access-token')
QUALYS_POD = 'US2'
IMAGE_NAME = "myapp:${BUILD_NUMBER}"
REGISTRY = "your-ecr-registry.amazonaws.com"
}
stages {
stage('Build & Push') {
steps {
script {
// Build and push to registry
sh "docker build -t ${REGISTRY}/${IMAGE_NAME} ."
sh "docker push ${REGISTRY}/${IMAGE_NAME}"
}
}
}
stage('Security Scan') {
steps {
script {
// Download QScanner
sh '''
if [ ! -f "./qscanner" ]; then
curl -L -o qscanner https://downloads.qualys.com/qscanner/linux/qscanner
chmod +x qscanner
fi
'''
// Get image ID from the pushed image
def imageId = sh(
script: "docker images ${REGISTRY}/${IMAGE_NAME} --format '{{.ID}}' | head -1",
returnStdout: true
).trim()
// Run QScanner on the image
sh """
./qscanner image ${imageId} \
--access-token \$QUALYS_ACCESS_TOKEN \
--pod \$QUALYS_POD
"""
}
}
post {
always {
// Archive any generated results
archiveArtifacts artifacts: '*.json', allowEmptyArchive: true
}
failure {
emailext(
subject: "Security scan failed for ${IMAGE_NAME}",
body: "Container image ${IMAGE_NAME} failed security scan. Check Jenkins logs for details.",
to: "${env.CHANGE_AUTHOR_EMAIL}"
)
}
}
}
stage('Deploy to EKS') {
when {
branch 'main'
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' }
}
steps {
withKubeConfig([credentialsId: 'eks-kubeconfig']) {
sh """
helm upgrade --install myapp ./helm-chart \
--set image.repository=${REGISTRY}/myapp \
--set image.tag=${BUILD_NUMBER} \
--namespace production
"""
}
}
}
}
}pipeline {
agent any
environment {
QUALYS_ACCESS_TOKEN = credentials('qualys-access-token')
QUALYS_POD = 'US2'
}
stages {
stage('Parallel Security Scanning') {
parallel {
stage('Scan Development Images') {
steps {
script {
def devImages = ['dev-app:latest', 'dev-api:latest']
devImages.each { image ->
// Get image ID
def imageId = sh(
script: "docker images ${image} --format '{{.ID}}' | head -1",
returnStdout: true
).trim()
// Scan each image
sh """
./qscanner image ${imageId} \
--access-token \$QUALYS_ACCESS_TOKEN \
--pod \$QUALYS_POD
"""
}
}
}
}
stage('Scan Production Images') {
steps {
script {
def prodImages = ['prod-app:latest', 'prod-api:latest']
prodImages.each { image ->
// Get image ID
def imageId = sh(
script: "docker images ${image} --format '{{.ID}}' | head -1",
returnStdout: true
).trim()
sh """
./qscanner image ${imageId} \
--access-token \$QUALYS_ACCESS_TOKEN \
--pod \$QUALYS_POD
"""
}
}
}
}
}
}
}
}# Using access token and pod identifier directly
./qscanner image myapp:latest --access-token $QUALYS_ACCESS_TOKEN --pod $QUALYS_POD
# Using image ID instead of name
./qscanner image ab0d83586b6e --access-token $QUALYS_ACCESS_TOKEN --pod $QUALYS_POD
# Using environment variables
export QUALYS_ACCESS_TOKEN=your-access-token
export QUALYS_POD=US2
./qscanner image myapp:latest --access-token $QUALYS_ACCESS_TOKEN --pod $QUALYS_POD# Scan by image name
./qscanner image nginx:latest --access-token $QUALYS_ACCESS_TOKEN --pod US2
# Scan by image ID (more reliable in CI/CD)
./qscanner image ab0d83586b6e --access-token $QUALYS_ACCESS_TOKEN --pod US2
# Check available options
./qscanner --help
./qscanner image --help# Check sensor connectivity in cluster
kubectl exec -it deployment/qualys-tc -n qualys -- \
qualys-sensor status
# View detailed logs
kubectl logs deployment/qualys-tc -n qualys -f
# Check resource usage
kubectl top pods -n qualys# Update Helm repository
helm repo update qualys-helm-chart
# Check for available updates
helm search repo qualys-helm-chart/qualys-tc --versions
# Upgrade sensor
helm upgrade qualys-tc qualys-helm-chart/qualys-tc \
--namespace qualys \
--reuse-values# Check QScanner version
./qscanner --version
# Download latest version
curl -L -o qscanner-new https://downloads.qualys.com/qscanner/linux/qscanner
chmod +x qscanner-new
mv qscanner-new qscannerStore Qualys credentials securely in Jenkins credential store Use dedicated service accounts with minimal required permissions Enable network policies to restrict sensor communication Regularly rotate API credentials Monitor sensor logs for security events
Use QScanner's local caching for faster CI/CD builds Implement parallel scanning for multiple images Configure appropriate resource limits for Helm deployments Consider dedicated nodes for security scanning workloads
Implement monitoring and alerting for sensor health Automate sensor updates through GitOps workflows Maintain documentation for incident response procedures Regular backup of sensor configurations and policies Use centralized policy management in Qualys Portal
Cache QScanner binary in your build agents Use policy enforcement to maintain consistent security standards Generate multiple output formats for different stakeholders Integrate scan results with notification systems Archive scan results for compliance and audit purposes
# Check network connectivity from cluster
kubectl exec -it deployment/qualys-tc -n qualys -- \
nslookup qualysapi.qg2.apps.qualys.com
# Verify cluster configuration
helm get values qualys-tc -n qualys# Test basic QScanner functionality
./qscanner --help
# Run with a simple test image to verify connectivity
./qscanner image hello-world:latest --access-token $QUALYS_ACCESS_TOKEN --pod US2
# Check if image exists locally
docker images | grep myappIncrease resource limits in Helm values Use faster storage classes for persistent volumes Optimize QScanner caching in CI/CD environments
Qualys Container Security Documentation: https://docs.qualys.com/en/cs/latest/ QScanner Documentation: https://docs.qualys.com/en/qscanner/latest/ Official Qualys Helm Charts: https://artifacthub.io/packages/helm/qualys-helm-chart/qualys-tc Qualys API Reference: https://www.qualys.com/docs/qualys-api-vmpc-user-guide.pdf EKS Best Practices Guide: https://aws.github.io/aws-eks-best-practices/