Achieving Security of Qualys by IaC on GCP

Deploying Qualys Cloud agent in GCP VM via OS Assignment Policy using Terraform in Private VPC

Ankit Gupta
KPMG UK Engineering
5 min readJun 18, 2024

--

Photo by Arno Senoner on Unsplash

This story will teach us how to deploy a Qualys Cloud agent in GCP virtual machine using Terraform via GCP OS config policy assignment in Private Network VPC.

Why we need Security?

Google Cloud provides a secure-by-design foundation — core infrastructure designed, built, and operated with security in mind to support digital sovereignty requirements, But we still need to protect our application & data from security threats.

Why need Qualys ?

With Qualys Cloud Agent you’ll get continuous security updates through the cloud. As soon as changes are discovered on your hosts they’ll be assessed and you’ll know about new security threats right away. All you have to do is install lightweight agents on your hosts. Agents are centrally managed by the cloud agent platform and are self-updating (no reboot needed). Agents continuously collect metadata, beam it to the cloud agent platform where full assessments occur right away. Since the heavy lifting is done in the cloud the agent needs minimal footprint and processing on target systems.

Requirements:

To deploy a Qualys Cloud Agent in GCP VM, we will need the following:

  • Region where you are going deploy the OS Policy
  • GCP Project id
  • Qualys installer package package file.
  • Qualys credential i.e. activation_id & customer_id from Qualys Admin team
  • OS config agent should installed
  • Need a Virtual Machine to test

Manual verification of OSconfig agent:

To manually verify that VM Manager is properly set up, complete the following checks:

Now, Start TF coding:

Define Variables & versions.tf: set customer_id, activation_id

terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "5.27.0"
}
google-beta = {
source = "hashicorp/google-beta"
version = "5.27.0"
}
random = {
source = "hashicorp/random"
version = "3.4.3"
}
}
required_version = ">= 0.13"
}

provider "google" {
}
variable "region" {
type = string
description = "The region coming from the top level"
}
variable "project_id" {
type = string
description = "ID of the project to be assigned"
}
variable "customer_id" {
description = "Value for the qualys instance customer id"
type = string
default = "<colect it from Qualys Admin>"
}
variable "activation_id" {
description = "Value for the qualys instance activation id"
type = string
default = "<colect it from Qualys Admin>"
}
variable "debian_installer_name" {
type = string
default = "QualysCloudAgent_x64.deb"
description = "Qualys agent installer name"
}
variable "windows_installer_name" {
type = string
default = "QualysCloudAgent.exe"
description = "Qualys agent installer name"
}

Put installer file

Copy files in root folder under directory “gcp-qualys-agents”

Create actual GCP os Policy via terraform

This TF code will:

. Create bucket to store package in project

. Copy installer file to bucket

. Create os config policy assigment.

data "google_compute_zones" "available-qualys" {
project = var.project_id
region = var.region
}

resource "google_storage_bucket" "qualys-bucket" {
name = "${var.project_id}-qualys-agent"
project = var.project_id
location = var.region
storage_class = "STANDARD"
uniform_bucket_level_access = true
#checkov:skip=CKV_GCP_62:skipping the logging of the bucket.
public_access_prevention = "enforced"

versioning {
enabled = true
}
}

resource "google_storage_bucket_object" "qualys-object" {
for_each = fileset("${path.module}/gcp-qualys-agents", "*")
name = each.key
bucket = google_storage_bucket.qualys-bucket.name
source = "${path.module}/gcp-qualys-agents/${each.key}"
}


resource "google_os_config_os_policy_assignment" "qualys_linux_based_systems" {
project = var.project_id
instance_filter {
all = false
inclusion_labels {
labels = {
security-scan = "yes"
}
}
inventories {
os_short_name = "debian"
}
inventories {
os_short_name = "ubuntu"
}
}
count = length(data.google_compute_zones.available-qualys.names)
location = data.google_compute_zones.available-qualys.names[count.index]
name = "qualys-installation-linux-x64-${data.google_compute_zones.available-qualys.names[count.index]}"

os_policies {
id = "setup-qualys-linux"
description = "Rollout policy for qualys on Debian x64"

mode = "ENFORCEMENT"

resource_groups {
resources {
id = "qualys-deb-install"

pkg {
desired_state = "INSTALLED"
deb {
source {
allow_insecure = true
gcs {
bucket = google_storage_bucket.qualys-bucket.name
object = var.debian_installer_name
}
}
pull_deps = false
}
}
}

resources {
id = "run-qualys-agent-sh"
exec {
validate {
interpreter = "SHELL"
script = <<EOT
if ( /usr/bin/grep GCP /var/log/qualys/qualys-cloud-agent.log ) ; then
exit 100;
else
exit 101;
fi
EOT
}
enforce {
interpreter = "SHELL"
script = "sudo /usr/local/qualys/cloud-agent/bin/qualys-cloud-agent.sh ActivationId=${var.activation_id} CustomerId=${var.customer_id} ServerUri=https://qagpublic.qg2.apps.qualys.eu/CloudAgent && exit 100"
}
}
}

}
allow_no_resource_group_match = false
}

rollout {
disruption_budget {
percent = 10
}

min_wait_duration = "300s"
}

description = "Rollout policy for qualys cloud agent"
}

resource "google_os_config_os_policy_assignment" "qualys_windows_based_systems" {
project = var.project_id
instance_filter {
all = false
inclusion_labels {
labels = {
security-scan = "yes"
}
}
inventories {
os_short_name = "windows"
}
}
count = length(data.google_compute_zones.available-qualys.names)
location = data.google_compute_zones.available-qualys.names[count.index]
name = "qualys-installation-windows-${data.google_compute_zones.available-qualys.names[count.index]}"

os_policies {
id = "setup-qualys-windows"
description = "Rollout policy for qualys on Windows"

mode = "ENFORCEMENT"

resource_groups {
resources {
id = "copy-qualys-exe"
file {
path = "C:\\${var.windows_installer_name}"
state = "CONTENTS_MATCH"
file {
allow_insecure = true
gcs {
bucket = google_storage_bucket.qualys-bucket.name
object = var.windows_installer_name
}
}
}
}

resources {
id = "run-qualys-agent-exe"
exec {
validate {
interpreter = "POWERSHELL"
script = "$service = Get-Service -Name QualysAgent ; if ($service.Status -eq 'Running') {exit 100} else {exit 101}"
}
enforce {
interpreter = "POWERSHELL"
script = <<EOT
C:\.\QualysCloudAgent.exe CustomerId="{${var.customer_id}}" ActivationId="{${var.activation_id}}" WebServiceUri=https://qagpublic.qg2.apps.qualys.eu/CloudAgent/
exit 100
EOT
}
}
}
}
allow_no_resource_group_match = false
}

rollout {
disruption_budget {
percent = 10
}

min_wait_duration = "300s"
}

description = "Rollout policy for qualys agent on Windows"
}

Testing:

create a VM. It will around 2 min to show complaince status in console.

--

--