{"id":930,"date":"2024-06-20T11:13:31","date_gmt":"2024-06-20T11:13:31","guid":{"rendered":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2024\/06\/20\/a-golden-path-to-secure-cloud-provisioning-with-the-infrastructure-cloud\/"},"modified":"2024-06-20T11:13:31","modified_gmt":"2024-06-20T11:13:31","slug":"a-golden-path-to-secure-cloud-provisioning-with-the-infrastructure-cloud","status":"publish","type":"post","link":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/2024\/06\/20\/a-golden-path-to-secure-cloud-provisioning-with-the-infrastructure-cloud\/","title":{"rendered":"A golden path to secure cloud provisioning with The Infrastructure Cloud"},"content":{"rendered":"<p>There are three telling security statistics in modern cloud and hybrid environments:<\/p>\n<p>Data breaches cost an average of <a href=\"https:\/\/www.ibm.com\/reports\/data-breach\">$4.5 million per incident<\/a><br \/>\n<a href=\"https:\/\/www.verizon.com\/business\/resources\/reports\/dbir\/\">74% of breaches<\/a> involve a human element<br \/>\n<a href=\"https:\/\/www.verizon.com\/business\/resources\/reports\/dbir\/\">9 out of 10 web application breaches<\/a> result from stolen credentials<\/p>\n<p>These three stats compel security and platform teams to closely examine their approach to cloud infrastructure security workflows from Day-0 through Day-N. There are three primary challenges every security team must solve pertaining to remote infrastructure access and credential\/secrets management in the cloud:<\/p>\n<p>How will we manage and secure human and machine access credentials to cloud accounts and resources?<br \/>\nHow will we enable humans to remotely access cloud resources with just-in-time credentials when the need arises (e.g. a \u201cbreak glass\u201d emergency situation or to debug an application)?<br \/>\nHow will we enable workloads running on the cloud to access secrets?<\/p>\n<p>What if organizations could address those challenges within a single repeatable workflow? Better yet, what if they could wire up all the necessary configurations at provision-time, so every time a new cloud infrastructure resource is created, security teams can rest assured that the standard patterns of Security Lifecycle Management are applied by default.<\/p>\n<p>These kinds of \u201cgolden\u201d approaches to infrastructure and security automation workflows lie at the core of <a href=\"https:\/\/www.hashicorp.com\/blog\/introducing-the-infrastructure-cloud\">The Infrastructure Cloud from HashiCorp<\/a>, which delivers Infrastructure and Security Lifecycle Management (ILM and SLM) via the <a href=\"https:\/\/www.hashicorp.com\/cloud\">HashiCorp Cloud Platform (HCP)<\/a>. This post illustrates using HCP services to build a secure provisioning workflow that automates solutions to the aforementioned challenges. <\/p>\n<p>This diagram represents the \u201cgolden path\u201d workflow pattern modeled in this post:<\/p>\n<p>While this example uses Microsoft Azure, the same workflow can be applied to any cloud provider, as well as to multi-cloud and hybrid cloud environments. You can follow along with building this golden path by using the full source code found in <a href=\"https:\/\/github.com\/bfbarkhouse\/hashistack-secure-infra-workflow\/tree\/main\/azure\">this GitHub repository<\/a>.<\/p>\n<h3>Securing SSH access by default<\/h3>\n<p>In this example scenario, an application development team needs to deploy their app to a Linux-based virtual machine (VM) running in Azure. The application needs to access a sensitive text string in order to execute (this could be an API token, database connection string, etc.) Also, despite a broad preference for <a href=\"https:\/\/www.hashicorp.com\/resources\/what-is-mutable-vs-immutable-infrastructure\">immutable infrastructure<\/a>, developers and site reliability engineers may need SSH access to the VM to debug the application in case of an emergency.<\/p>\n<p>The first step in creating the dev team\u2019s VM is to generate an admin SSH key-pair. The public key must be trusted by the VM and the private key must be stored securely with role-based access controls (RBAC) and auditing.<\/p>\n<p>This golden path will use the core ILM component of The Infrastructure Cloud, <a href=\"https:\/\/www.terraform.io\/\">HashiCorp Terraform<\/a>, to define and orchestrate all the pieces of this end-to-end secure provisioning workflow. Terraform is HashiCorp\u2019s <a href=\"https:\/\/www.hashicorp.com\/resources\/what-is-infrastructure-as-code\">infrastructure as code<\/a> solution and <a href=\"https:\/\/developer.hashicorp.com\/terraform\/cloud-docs\">HCP Terraform<\/a> is a SaaS platform that manages Terraform provisioning <em>runs<\/em> in a consistent and reliable environment. Key benefits of HCP Terraform include: <\/p>\n<p>Secure management of shared state and secret data<br \/>\nAccess controls for approving changes to infrastructure<br \/>\nA private registry for sharing approved Terraform modules<br \/>\nDetailed policy controls for governing the contents of Terraform configurations<br \/>\nDay 2 visibility and optimization features like drift detection and a workspace explorer<\/p>\n<p>All the infrastructure created in this post will be codified with Terraform and committed to GitHub. This walkthrough uses a <a href=\"https:\/\/www.redhat.com\/en\/topics\/devops\/what-is-gitops\">GitOps<\/a> workflow that integrates GitHub and HCP Terraform to trigger terraform plan and terraform apply as new commits are made. Configuring your <a href=\"https:\/\/developer.hashicorp.com\/terraform\/tutorials\/cloud-get-started\/cloud-workspace-create\">HCP Terraform workspace <\/a>and <a href=\"https:\/\/developer.hashicorp.com\/terraform\/tutorials\/cloud-get-started\/cloud-vcs-change\">VCS connection<\/a> is outside the scope of this blog.<\/p>\n<p>Terraform lets you define <em>resources<\/em> that represent infrastructure artifacts. One such resource is called <a href=\"https:\/\/registry.terraform.io\/providers\/hashicorp\/tls\/latest\/docs\/resources\/private_key\">tls_private_key<\/a>, which generates an SSH key pair:<\/p>\n<p>#Create SSH keypair<br \/>\nresource &#8220;tls_private_key&#8221; &#8220;ssh_key&#8221; {<br \/>\n  algorithm = &#8220;RSA&#8221;<br \/>\n  rsa_bits  = 4096<br \/>\n}<\/p>\n<p>Next, you need to put your keys somewhere safe. <a href=\"https:\/\/www.vaultproject.io\/\">HashiCorp Vault<\/a> is a centralized, API-driven secrets management system that handles the security lifecycle aspects of The Infrastructure Cloud. All the data in Vault is encrypted and protected with access-control list (ACL) policies. To avoid the burden of spinning up and managing your own Vault cluster, this post uses <a href=\"https:\/\/developer.hashicorp.com\/hcp\/docs\/vault\/what-is-hcp-vault\">HCP Vault Dedicated<\/a>, the HashiCorp-managed version of <a href=\"https:\/\/www.hashicorp.com\/products\/vault\/pricing\">Vault Enterprise<\/a>. <\/p>\n<p>Terraform integrates with external systems via plugins known as <a href=\"https:\/\/developer.hashicorp.com\/terraform\/language\/providers\">providers<\/a>. Often a set of credentials are needed to access the external system APIs called by a provider. This introduces the complexity of securing those credentials, also called the <a href=\"https:\/\/developer.hashicorp.com\/vault\/tutorials\/app-integration\/secure-introduction\">&#8220;secure introduction&#8221; or &#8220;secret zero&#8221; problem<\/a>. Fortunately, HCP Terraform presents information about a Terraform workload to an external system \u2014 such as its <a href=\"https:\/\/developer.hashicorp.com\/terraform\/cloud-docs\/workspaces\">workspace<\/a>, <a href=\"https:\/\/developer.hashicorp.com\/terraform\/cloud-docs\/users-teams-organizations\/organizations\">organization<\/a>, or whether it\u2019s a <em>plan<\/em> or <em>apply<\/em> \u2014 and allows other external systems to verify that the claims are genuine. This is called <a href=\"https:\/\/developer.hashicorp.com\/terraform\/cloud-docs\/workspaces\/dynamic-provider-credentials\/workload-identity-tokens\">workload identity<\/a>. Each workload is issued a JSON Web Token (JWT) that is signed by HCP Terraform\u2019s private key, and expires at the end of the plan or apply timeout. <\/p>\n<p>HCP Terraform\u2019s workload identity can be integrated with Vault to generate dynamic credentials for the Vault provider in your HCP Terraform runs. <a href=\"https:\/\/developer.hashicorp.com\/terraform\/cloud-docs\/workspaces\/dynamic-provider-credentials\">Dynamic provider credentials<\/a> improve your security posture by letting you provision new, temporary credentials for each run. This workflow eliminates the need to manually manage and rotate credentials across an organization.<\/p>\n<p>To configure dynamic provider credentials for Vault, establish the <a href=\"https:\/\/developer.hashicorp.com\/terraform\/cloud-docs\/workspaces\/dynamic-provider-credentials\/vault-configuration\">trust between HCP Terraform and HCP Vault Dedicated<\/a>, set up a Vault role and policy, and configure your HCP Terraform workspace with the <a href=\"https:\/\/developer.hashicorp.com\/terraform\/cloud-docs\/workspaces\/dynamic-provider-credentials\/vault-configuration#configure-terraform-cloud\">required variables<\/a> shown here:<\/p>\n<p>#Upload the policy<br \/>\nvault policy write tfc-workload tfc-workload-policy.hcl<\/p>\n<p>#Configure TF workload identity auth to Vault<br \/>\nvault auth enable jwt<br \/>\nvault write auth\/jwt\/config <br \/>\n    oidc_discovery_url=&#8221;https:\/\/app.terraform.io&#8221; <br \/>\n    bound_issuer=&#8221;https:\/\/app.terraform.io&#8221;<\/p>\n<p>#Create the role<br \/>\nvault write auth\/jwt\/role\/tfc @vault-jwt-auth-role.json<\/p>\n<p>You can now use Terraform\u2019s <a href=\"https:\/\/registry.terraform.io\/providers\/hashicorp\/vault\/latest\/docs\">Vault provider<\/a> to create a new <a href=\"https:\/\/developer.hashicorp.com\/vault\/docs\/secrets\/kv\">key-value secrets engine<\/a> path and stash the SSH keys there:<\/p>\n<p>#Store All SSH keys in Vault KV<br \/>\nresource &#8220;vault_kv_secret_v2&#8221; &#8220;example&#8221; {<br \/>\n  mount = &#8220;kv&#8221;<br \/>\n  name = &#8220;ssh\/${var.vm_name}&#8221;<br \/>\n  data_json = jsonencode(<br \/>\n    {<br \/>\n      public_key_openssh  = tls_private_key.ssh_key.public_key_openssh,<br \/>\n      private_key_openssh = tls_private_key.ssh_key.private_key_openssh,<br \/>\n      public_key_pem      = tls_private_key.ssh_key.public_key_pem,<br \/>\n      private_key_pem     = tls_private_key.ssh_key.private_key_pem<br \/>\n      username            = var.vm_admin<br \/>\n    }<br \/>\n  )<br \/>\n}<\/p>\n<p>Next, load the public key onto the machine. Your target cloud in this post is Microsoft Azure, so use <a href=\"https:\/\/registry.terraform.io\/providers\/hashicorp\/azurerm\/latest\/docs\">Terraform\u2019s Azure provider<\/a> to create the VM and its associated networking components. The Azure provider\u2019s resource called <a href=\"https:\/\/registry.terraform.io\/providers\/hashicorp\/azurerm\/latest\/docs\/resources\/linux_virtual_machine\">azurerm_linux_virtual_machine<\/a> has an <a href=\"https:\/\/registry.terraform.io\/providers\/hashicorp\/azurerm\/latest\/docs\/resources\/linux_virtual_machine#admin_ssh_key\">admin_ssh_key<\/a> block that defines the admin username and SSH public key. The VM resource configuration references the key-pair created earlier:<\/p>\n<p>admin_ssh_key {<br \/>\n    username   = var.vm_admin_username #adminuser<br \/>\n    public_key = tls_private_key.ssh_key.public_key_openssh<br \/>\n  }<\/p>\n<p>When HCP Terraform applies the code, it will create SSH keys, store them in Vault, and provision the VM with the public key added to the SSH authorized_keys file \u2014 all in a single codified configuration.<\/p>\n<h3>Human access to the virtual machine<\/h3>\n<p>The next step is to enable human operator access. This involves another product in the Infrastructure Cloud, <a href=\"https:\/\/www.boundaryproject.io\/\">HashiCorp Boundary<\/a>. Boundary is a modern privileged access management (PAM) solution, consisting of a control plane and workers that proxy sessions to infrastructure. You can use the HashiCorp-managed <a href=\"https:\/\/developer.hashicorp.com\/hcp\/docs\/boundary\">HCP Boundary<\/a> to run the control plane, register workers, and configure host endpoints with a default port and a protocol to establish a session (known as <em>targets<\/em>). <\/p>\n<p>The <a href=\"https:\/\/registry.terraform.io\/providers\/hashicorp\/boundary\/latest\/docs\">Boundary Terraform provider<\/a> orchestrates the steps required to enable human access to the VM, all within the provisioning workflow. Now register the new VM as an <a href=\"https:\/\/developer.hashicorp.com\/boundary\/docs\/common-workflows\/manage-targets\">SSH target<\/a> in Boundary and configure a <a href=\"https:\/\/developer.hashicorp.com\/boundary\/docs\/concepts\/credential-management\">connection between Boundary and Vault<\/a> to let Boundary <a href=\"https:\/\/developer.hashicorp.com\/boundary\/docs\/concepts\/credential-management\">inject<\/a> the SSH private key into the user\u2019s session:<\/p>\n<p>#Get the Boundary project<br \/>\ndata &#8220;boundary_scope&#8221; &#8220;project&#8221; {<br \/>\n  name     = var.boundary_project_name<br \/>\n  scope_id = var.boundary_scope_id<br \/>\n}<\/p>\n<p>resource &#8220;vault_token&#8221; &#8220;example&#8221; {<br \/>\n  policies  = [&#8220;tfc-workload&#8221;]<br \/>\n  no_parent = true<br \/>\n  renewable = true<br \/>\n  period    = &#8220;24h&#8221;<br \/>\n  metadata = {<br \/>\n    &#8220;purpose&#8221; = &#8220;boundary credential store token&#8221;<br \/>\n  }<br \/>\n}<br \/>\n#Create credential store<br \/>\nresource &#8220;boundary_credential_store_vault&#8221; &#8220;example&#8221; {<br \/>\n  name        = &#8220;HCP Vault&#8221;<br \/>\n  description = &#8220;HCP Vault Credential Store&#8221;<br \/>\n  address     = var.vault_addr<br \/>\n  namespace   = var.vault_namespace<br \/>\n  token       = vault_token.example.client_token<br \/>\n  scope_id    = data.boundary_scope.project.id<br \/>\n}<\/p>\n<p>#Create credential library for the SSH keys<br \/>\nresource &#8220;boundary_credential_library_vault&#8221; &#8220;example&#8221; {<br \/>\n  name                = &#8220;${var.vm_name}-sshkeys&#8221;<br \/>\n  description         = &#8220;VM SSH keys&#8221;<br \/>\n  credential_store_id = boundary_credential_store_vault.example.id<br \/>\n  path                = vault_kv_secret_v2.example.path<br \/>\n  http_method         = &#8220;GET&#8221;<br \/>\n  credential_type     = &#8220;ssh_private_key&#8221;<br \/>\n  credential_mapping_overrides = {<br \/>\n    private_key_attribute = &#8220;private_key_pem&#8221;<br \/>\n  }<br \/>\n}<br \/>\n#Add the VM to Boundary<br \/>\nresource &#8220;boundary_host_catalog_static&#8221; &#8220;example&#8221; {<br \/>\n  name        = &#8220;azure-vm-catalog&#8221;<br \/>\n  description = &#8220;Azure VM Host Catalog&#8221;<br \/>\n  scope_id    = data.boundary_scope.project.id<br \/>\n}<br \/>\nresource &#8220;boundary_host_static&#8221; &#8220;example&#8221; {<br \/>\n  name            = var.vm_name<br \/>\n  host_catalog_id = boundary_host_catalog_static.example.id<br \/>\n  address = azurerm_linux_virtual_machine.example.private_ip_address<br \/>\n}<\/p>\n<p>resource &#8220;boundary_host_set_static&#8221; &#8220;example&#8221; {<br \/>\n  name            = &#8220;azure-vm-host-set&#8221;<br \/>\n  host_catalog_id = boundary_host_catalog_static.example.id<br \/>\n  host_ids = [<br \/>\n    boundary_host_static.example.id<br \/>\n  ]<br \/>\n}<br \/>\nresource &#8220;boundary_target&#8221; &#8220;example&#8221; {<br \/>\n  name         = &#8220;${var.vm_name}-ssh&#8221;<br \/>\n  description  = &#8220;${var.vm_name}-SSH&#8221;<br \/>\n  type         = &#8220;ssh&#8221;<br \/>\n  default_port = &#8220;22&#8221;<br \/>\n  scope_id     = data.boundary_scope.project.id<br \/>\n  host_source_ids = [<br \/>\n    boundary_host_set_static.example.id<br \/>\n  ]<br \/>\n  injected_application_credential_source_ids = [<br \/>\n    boundary_credential_library_vault.example.id<br \/>\n  ]<br \/>\n  egress_worker_filter = var.boundary_egress_filter<br \/>\n  enable_session_recording = true<br \/>\n  storage_bucket_id        = var.boundary_session_bucket<br \/>\n}<br \/>\nresource &#8220;boundary_alias_target&#8221; &#8220;example&#8221; {<br \/>\n  name                      = &#8220;boundary_alias_target&#8221;<br \/>\n  description               = &#8220;Alias to target using host boundary_host_static.example&#8221;<br \/>\n  scope_id                  = &#8220;global&#8221;<br \/>\n  value                     = &#8220;ssh.${var.vm_name}.boundary&#8221;<br \/>\n  destination_id            = boundary_target.example.id<br \/>\n  authorize_session_host_id = boundary_host_static.example.id<br \/>\n}<\/p>\n<p>The snippet above shows <a href=\"https:\/\/developer.hashicorp.com\/boundary\/docs\/configuration\/session-recording\">session recording<\/a> is enabled on the Boundary target. This is a critical enterprise feature that records every SSH session initiated to that host for later review by security teams and auditors.<\/p>\n<p>By design, the Azure VM does not have a public IP, it&#8217;s on a private subnet. The users who need access to it reside outside the private network, so this is where Boundary\u2019s <a href=\"https:\/\/developer.hashicorp.com\/boundary\/docs\/concepts\/connection-workflows\/multi-hop\">multi-hop<\/a> proxying capability comes in. If you place a <a href=\"https:\/\/developer.hashicorp.com\/boundary\/docs\/concepts\/workers\">Boundary worker<\/a> into the same Azure virtual network (VNet) as the VM, you can leverage it as a <a href=\"https:\/\/www.nginx.com\/resources\/glossary\/reverse-proxy-server\/\">reverse proxy<\/a>. <\/p>\n<p>The Boundary worker in Azure establishes a persistent outbound connection to an upstream worker that resides in HCP. The worker in Azure is configured as an <a href=\"https:\/\/developer.hashicorp.com\/boundary\/docs\/configuration\/worker#:~:text=Ingress%20worker%20filters%20determine%20which%20workers%20you%20connect%20with%20to%20initiate%20a%20session%2C%20and%20egress%20worker%20filters%20determine%20which%20workers%20are%20used%20to%20access%20targets.\">egress worker<\/a> and given an azure tag and an egress worker filter is set on the Boundary target. The filter matches workers that have the tag azure. When users connect to the Boundary target, Boundary will route the connection only to the workers that match the tags set in the target filter. Here is a diagram to help you visualize the routing:<\/p>\n<p>Boundary workers can run on VMs, bare-metal servers, and containers. In this example, the worker will run as a container. This is an efficient way to run Boundary workers since containers are lightweight, fast, and can scale as demand changes. Spin up your Boundary worker in an Azure Container Instance using Terraform:<\/p>\n<p>resource &#8220;boundary_worker&#8221; &#8220;az_worker&#8221; {<br \/>\n  scope_id = &#8220;global&#8221;<br \/>\n  name = &#8220;azure boundary worker 1&#8221;<br \/>\n}<\/p>\n<p>resource &#8220;azurerm_subnet&#8221; &#8220;cg&#8221; {<br \/>\n  name                 = &#8220;container-group-subnet&#8221;<br \/>\n  resource_group_name  = var.resource_group<br \/>\n  virtual_network_name = azurerm_virtual_network.example.name<br \/>\n  address_prefixes     = [&#8220;10.0.3.0\/24&#8221;]<br \/>\n  delegation {<br \/>\n    name = &#8220;delegation&#8221;<\/p>\n<p>    service_delegation {<br \/>\n      name    = &#8220;Microsoft.ContainerInstance\/containerGroups&#8221;<br \/>\n      actions = [&#8220;Microsoft.Network\/virtualNetworks\/subnets\/join\/action&#8221;, &#8220;Microsoft.Network\/virtualNetworks\/subnets\/prepareNetworkPolicies\/action&#8221;]<br \/>\n    }<br \/>\n  }<br \/>\n}<\/p>\n<p>resource &#8220;azurerm_container_group&#8221; &#8220;container&#8221; {<br \/>\n  name                = &#8220;boundary-worker-group&#8221;<br \/>\n  location            = var.az_location<br \/>\n  resource_group_name = var.resource_group<br \/>\n  ip_address_type = &#8220;Private&#8221;<br \/>\n  subnet_ids      = [azurerm_subnet.cg.id]<br \/>\n  os_type         = &#8220;Linux&#8221;<br \/>\n  restart_policy  = &#8220;Never&#8221;<\/p>\n<p>  container {<br \/>\n    name   = &#8220;boundary-worker&#8221;<br \/>\n    image  = &#8220;hashicorp\/boundary-enterprise&#8221;<br \/>\n    cpu    = 1<br \/>\n    memory = 2<\/p>\n<p>    ports {<br \/>\n      port     = 9202<br \/>\n      protocol = &#8220;TCP&#8221;<br \/>\n    }<br \/>\n    environment_variables = { &#8220;HCP_BOUNDARY_CLUSTER_ID&#8221; = var.hcp_boundary_cluster_id, &#8220;WORKER_ACTV_TOKEN&#8221; = boundary_worker.az_worker.controller_generated_activation_token }<br \/>\n    volume {<br \/>\n      name       = &#8220;boundary-config&#8221;<br \/>\n      mount_path = &#8220;\/boundary&#8221;<br \/>\n      git_repo {<br \/>\n        url = &#8220;https:\/\/github.com\/bfbarkhouse\/hashistack-secure-infra-workflow&#8221;<br \/>\n      }<br \/>\n    }<br \/>\n    commands = [<br \/>\n      &#8220;\/bin\/sh&#8221;, &#8220;-c&#8221;, &#8220;mv \/boundary\/hashistack-secure-infra-workflow\/azure\/boundary-worker-config.hcl \/boundary\/config.hcl; rm -rf \/boundary\/hashistack-secure-infra-workflow; \/usr\/local\/bin\/docker-entrypoint.sh server -config \/boundary\/config.hcl&#8221;<br \/>\n    ]<br \/>\n  }<br \/>\n}<\/p>\n<p>You now have a fully automated, codified, and secured access workflow for operator access to the VM. When a <a href=\"https:\/\/developer.hashicorp.com\/boundary\/docs\/concepts\/security\/permissions\">properly authorized<\/a> user logs into a <a href=\"https:\/\/developer.hashicorp.com\/boundary\/tutorials\/hcp-getting-started\/hcp-getting-started-desktop-app\">Boundary client<\/a>, they will see the VM as an available SSH target. When they click the Connect button, Boundary pulls the SSH private key from the Vault key-value path where it&#8217;s stored and injects it into the session using <a href=\"https:\/\/developer.hashicorp.com\/boundary\/docs\/concepts\/workers#protocol-decryption\">protocol decryption<\/a>. The key is never exposed to the user. The user is then proxied and authenticated to the VM within a recorded and time-restricted session. Since Boundary exposes APIs, it can be easily integrated into service management solutions as part of an approval workflow. <\/p>\n<p>Here you can see the target listed in the <a href=\"https:\/\/developer.hashicorp.com\/boundary\/docs\/api-clients\/desktop\">Boundary Desktop<\/a> UI:<\/p>\n<h3>Automatic application secret retrieval<\/h3>\n<p>Now that you\u2019ve solved two of the original challenges, it\u2019s time to address the final one: the workload running on the VM needs to retrieve a secret at runtime. <\/p>\n<p>Having a central secrets management system is a core tenet of modern IT security, but it&#8217;s only as effective as the level of adoption by developers. Developers could implement <a href=\"https:\/\/developer.hashicorp.com\/vault\/docs\/get-started\/developer-qs\">application logic<\/a> to authenticate to Vault, retrieve secrets, and manage the lifecycle of the Vault token. However, this requires application refactoring to make it \u201cVault-aware\u201d, and developers often don\u2019t have the bandwidth or Vault knowledge needed to refactor. Security teams requiring developers to refactor hundreds (or thousands) of applications creates significant cost and time implications, establishing barriers to adoption of secrets management.<\/p>\n<p>To help lower the barrier to adoption by providing a more scalable and simpler way for applications to integrate with Vault, HashiCorp created <a href=\"https:\/\/developer.hashicorp.com\/vault\/docs\/agent-and-proxy\/agent\">Vault Agent<\/a>. Vault Agent is a client daemon mode of the Vault binary. It has several uses, but one of its primary functions is to automatically authenticate to Vault, manage the lifecycle of the token, and fetch secrets from Vault, whether those are static secrets, dynamic credentials, or certificates. Your dev team\u2019s VM will leverage Vault Agent to render secrets for use by applications without the need for custom application logic.<\/p>\n<p>To install Vault Agent on the VM, adhering to the principles of immutability and codification, you will use another component in The Infrastructure Cloud: <a href=\"https:\/\/www.packer.io\/\">HashiCorp Packer<\/a>. Packer will codify installation and configuration of the Vault Agent into the VM image, so it&#8217;s up and running immediately after provisioning the VM. The <a href=\"https:\/\/developer.hashicorp.com\/packer\/tutorials\/docker-get-started\/get-started-install-cli\">Packer CLI<\/a> builds the Azure VM image from an <a href=\"https:\/\/developer.hashicorp.com\/packer\/docs\/templates\/hcl_templates\">HCL template<\/a>, pushes the image artifact to Azure, and registers the image metadata to <a href=\"https:\/\/developer.hashicorp.com\/hcp\/docs\/packer\">HCP Packer<\/a>. <\/p>\n<p>HCP Packer is a hosted artifact registry that tracks the metadata of your golden images, with features including versioning, release channels, ancestry tracking, revocation, and Terraform integrations. This <a href=\"https:\/\/www.packer.io\/use-cases\/integrate-with-terraform\">Terraform integration<\/a> is important because it lets developers reference a <a href=\"https:\/\/developer.hashicorp.com\/packer\/tutorials\/cloud-production\/golden-image-with-hcp-packer#\">golden image<\/a> by querying HCP Packer rather than hard-coding the image identifier. This ensures that only fully tested, compliant, and patched images are used to provision VMs and containers.<\/p>\n<p>Your VM will use an image built from a Packer HCL template using Azure\u2019s standard Canonical Ubuntu 22.04 LTS as the base. With the help of Packer\u2019s <a href=\"https:\/\/developer.hashicorp.com\/packer\/docs\/provisioners\/shell\">shell provisioner<\/a>, it will layer on the Vault Agent binary, its configuration file, a <a href=\"https:\/\/systemd.io\/\">systemd<\/a> unit file, and a <a href=\"https:\/\/developer.hashicorp.com\/vault\/docs\/agent-and-proxy\/agent\/template\">Consul template file<\/a> used to render the secret out to a text file:<\/p>\n<p>#Stage Vault<br \/>\n  provisioner &#8220;shell&#8221; {<br \/>\n    execute_command = &#8220;chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh &#8216;{{ .Path }}'&#8221;<br \/>\n    inline = [<br \/>\n      &#8220;wget -O- https:\/\/apt.releases.hashicorp.com\/gpg | sudo gpg &#8211;dearmor -o \/usr\/share\/keyrings\/hashicorp-archive-keyring.gpg&#8221;,<br \/>\n      &#8220;echo &#8220;deb [signed-by=\/usr\/share\/keyrings\/hashicorp-archive-keyring.gpg] https:\/\/apt.releases.hashicorp.com $(lsb_release -cs) main&#8221; | sudo tee \/etc\/apt\/sources.list.d\/hashicorp.list&#8221;,<br \/>\n      &#8220;apt update &amp;&amp; sudo apt install vault&#8221;<br \/>\n    ]<br \/>\n    inline_shebang = &#8220;\/bin\/sh -x&#8221;<br \/>\n  }<br \/>\n  provisioner &#8220;shell&#8221; {<br \/>\n    execute_command = &#8220;chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh &#8216;{{ .Path }}'&#8221;<br \/>\n    inline          = [&#8220;mkdir \/vault&#8221;]<br \/>\n  }<br \/>\n  provisioner &#8220;file&#8221; {<br \/>\n    destination = &#8220;\/tmp\/agent-config.hcl&#8221;<br \/>\n    source      = &#8220;.\/agent-config.hcl&#8221;<br \/>\n  }<br \/>\n  provisioner &#8220;file&#8221; {<br \/>\n    destination = &#8220;\/tmp\/app-secret.ctmpl&#8221;<br \/>\n    source      = &#8220;.\/app-secret.ctmpl&#8221;<br \/>\n  }<br \/>\n  provisioner &#8220;file&#8221; {<br \/>\n    destination = &#8220;\/tmp\/vault.service&#8221;<br \/>\n    source      = &#8220;.\/vault.service&#8221;<br \/>\n  }<br \/>\n  provisioner &#8220;shell&#8221; {<br \/>\n    execute_command = &#8220;chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh &#8216;{{ .Path }}'&#8221;<br \/>\n    inline          = [&#8220;mv \/tmp\/agent-config.hcl \/etc\/vault.d&#8221;]<br \/>\n  }<br \/>\n  provisioner &#8220;shell&#8221; {<br \/>\n    execute_command = &#8220;chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh &#8216;{{ .Path }}'&#8221;<br \/>\n    inline          = [&#8220;mv \/tmp\/app-secret.ctmpl \/etc\/vault.d&#8221;]<br \/>\n  }<br \/>\n  provisioner &#8220;shell&#8221; {<br \/>\n    execute_command = &#8220;chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh &#8216;{{ .Path }}'&#8221;<br \/>\n    inline          = [&#8220;mv \/tmp\/vault.service \/usr\/lib\/systemd\/system&#8221;]<br \/>\n  }<br \/>\n  provisioner &#8220;shell&#8221; {<br \/>\n    execute_command = &#8220;chmod +x {{ .Path }}; {{ .Vars }} sudo -E sh &#8216;{{ .Path }}'&#8221;<br \/>\n    inline          = [&#8220;systemctl enable vault.service&#8221;]<br \/>\n  }<br \/>\n}<\/p>\n<p>Keeping in mind the need to secure credentials across the workflow, you need to decide how Vault Agent should authenticate to Vault. There needs to be an initial credential\/identity to log in to Vault and get the token to use on subsequent requests for secrets. <\/p>\n<p>One benefit of using a cloud platform to host infrastructure is that the platform creates <a href=\"https:\/\/learn.microsoft.com\/en-us\/entra\/identity\/managed-identities-azure-resources\/overview\">identities for resources<\/a> and lets external systems validate those identities. Using <a href=\"https:\/\/developer.hashicorp.com\/vault\/docs\/auth\/azure\">Vault\u2019s Azure Authentication Method<\/a>, you can configure <a href=\"https:\/\/developer.hashicorp.com\/vault\/docs\/agent-and-proxy\/autoauth\/methods\/azure\">Vault Agent auto-auth<\/a> with the VM\u2019s Azure-managed identity JWT token and map the identity to a Vault role that has ACL policy access to the application secret. This resolves the \u201c<a href=\"https:\/\/developer.hashicorp.com\/vault\/tutorials\/app-integration\/secure-introduction\">secure introduction<\/a>\u201d problem by proving that the VM is a legitimate recipient for the secret and avoids managing yet another set of credentials. To configure the integration, log in to Vault, set up the Azure Authentication Method, and create a role for the agent:<\/p>\n<p>#Upload the policy<br \/>\nvault policy write vault-agent vault-agent-policy.hcl<\/p>\n<p>#Configure Azure auth method for Vault Agent<br \/>\nvault auth enable azure<br \/>\nvault write auth\/azure\/config <br \/>\n    tenant_id=$YOUR_TENANT_ID <br \/>\n    resource=https:\/\/management.azure.com\/ <br \/>\n    client_id=$YOUR_CLIENT_ID <br \/>\n    client_secret=$YOUR_CLIENT_SECRET<\/p>\n<p>#Create the role<br \/>\nvault write auth\/azure\/role\/vault-agent-role <br \/>\n    policies=&#8221;vault-agent&#8221; <br \/>\n    bound_subscription_ids=$YOUR_SUBSCRIPTION_ID<\/p>\n<p>In the example configuration above, any resource from the Azure subscription can use the role &#8220;vault-agent-role&#8221; and access secrets based on its ACL policy &#8220;vault-agent&#8221;. You can, of course, define finer-grained bindings. <\/p>\n<p>Here\u2019s the auto-auth snippet from the agent config file to include in the Packer image:<\/p>\n<p>auto_auth {<br \/>\n  method {<br \/>\n    type = &#8220;azure&#8221;<br \/>\n    namespace = &#8220;admin&#8221;<br \/>\n    config = {<br \/>\n      authenticate_from_environment = true<br \/>\n      role = &#8220;vault-agent-role&#8221;<br \/>\n      resource = &#8220;https:\/\/management.azure.com\/&#8221;<br \/>\n    }<br \/>\n  }<\/p>\n<p>After <a href=\"https:\/\/developer.hashicorp.com\/packer\/docs\/commands\/build\">building the image<\/a> and <a href=\"https:\/\/developer.hashicorp.com\/hcp\/docs\/packer\/store\/push-metadata?page=packer&amp;page=store-image-metadata&amp;page=packer-template-configuration\">registering it in HCP Packer<\/a>, the next step is to configure Terraform to provision the VM from it. First you need to query the latest version from HCP Packer using the <a href=\"https:\/\/registry.terraform.io\/providers\/hashicorp\/hcp\/latest\/docs\">HCP provider<\/a>:<\/p>\n<p># Locate the Packer built image<br \/>\ndata &#8220;hcp_packer_artifact&#8221; &#8220;secure-infra-workflow&#8221; {<br \/>\n  bucket_name   = var.packer_bucket_name<br \/>\n  channel_name  = var.packer_channel_name<br \/>\n  platform      = var.packer_platform<br \/>\n  region        = var.packer_region<br \/>\n}<\/p>\n<p>Then set an environment variable on the VM that tells Vault Agent the address of the Vault cluster to connect to. By using variables injected at provision time, you can use the same VM image across different environments such as dev, test, staging, and production. Use <a href=\"https:\/\/registry.terraform.io\/providers\/hashicorp\/template\/latest\/docs\/data-sources\/cloudinit_config\">cloud-init<\/a> to do this:<\/p>\n<p>#Injecting Vault cluster address to .env file used by systemd<br \/>\ndata &#8220;template_cloudinit_config&#8221; &#8220;vault-config&#8221; {<br \/>\n  gzip = true<br \/>\n  base64_encode = true<br \/>\n  part {<br \/>\n    content_type = &#8220;text\/cloud-config&#8221;<br \/>\n    content = &#8220;bootcmd: [echo VAULT_ADDR=${var.vault_addr} &gt;&gt; \/etc\/vault.d\/vault.env]&#8221;<br \/>\n  }<br \/>\n}<\/p>\n<p>The code to provision the Linux VM resource <a href=\"https:\/\/developer.hashicorp.com\/hcp\/docs\/packer\/store\/reference\">references the image you looked up in HCP Packer<\/a> and inserts the cloud-init config into the VM\u2019s <a href=\"https:\/\/registry.terraform.io\/providers\/hashicorp\/azurerm\/latest\/docs\/resources\/linux_virtual_machine#custom_data\">custom_data<\/a>. Notice the VM has a system-assigned identity:<\/p>\n<p>resource &#8220;azurerm_linux_virtual_machine&#8221; &#8220;example&#8221; {<br \/>\n  name                = var.vm_name<br \/>\n  resource_group_name = var.resource_group<br \/>\n  location            = var.az_location<br \/>\n  size                = var.vm_size<br \/>\n  source_image_id = data.hcp_packer_artifact.secure-infra-workflow.external_identifier<br \/>\n  admin_username      = var.vm_admin<br \/>\n  custom_data = data.template_cloudinit_config.vault-config.rendered<br \/>\n identity {<br \/>\n    type = &#8220;SystemAssigned&#8221;<br \/>\n }<\/p>\n<h3>Putting it all together<\/h3>\n<p>All the pieces to solve the three security challenges are now codified with Terraform. Run an HCP Terraform plan in your workspace and review the output:<\/p>\n<p>Now you have one provisioning workflow to create and store SSH keys, create a VM for an application from a golden image, access secrets, and register the VM into a remote access system.<\/p>\n<p>Note that there is a <a href=\"https:\/\/developer.hashicorp.com\/packer\/tutorials\/hcp\/setup-tfc-run-task\">Terraform run task<\/a> configured that validates the Packer image being used is valid and not revoked:<\/p>\n<p>Apply the plan and provision everything. Log into the VM through Boundary to verify Vault Agent rendered the application secret to the file as expected:<\/p>\n<h2>Getting started with an iterative approach<\/h2>\n<p>While it might seem daunting to adopt all the components presented in this blog post, you can get started with an iterative approach. Signing up for the HashiCorp Cloud Platform gives you immediate access to <a href=\"https:\/\/portal.cloud.hashicorp.com\/sign-up\">free-tier versions<\/a> of all the products used in this tutorial.<\/p>\n<p>It makes sense to start your <a href=\"https:\/\/www.hashicorp.com\/infrastructure-cloud\">Infrastructure Cloud journey<\/a> by getting your secrets under management with HCP Vault Dedicated, then move on to adopting infrastructure as code with HCP Terraform and scale up to codified image management with HCP Packer and identity-based access management with HCP Boundary. The investment to enable a secure provisioning workflow will be well worth it to strengthen and speed up your organization\u2019s ILM and SLMSecurity Lifecycle Management practices. <\/p>\n<p>For more tutorials on using HashiCorp solutions, visit our <a href=\"https:\/\/developer.hashicorp.com\/\">HashiCorp Developer<\/a> education site and watch our <a href=\"https:\/\/www.youtube.com\/playlist?list=PL81sUbsFNc5YMJ7Diq1fB54KqedNNJk1O\">demo video series<\/a>. If you\u2019d like to discuss your specific infrastructure and security transformation challenges, our sales and solutions engineers <a href=\"https:\/\/www.hashicorp.com\/contact-sales\">are here to listen<\/a>.<\/p>\n<p><em>The full source code referenced in this post can be found in the<a href=\"https:\/\/github.com\/bfbarkhouse\/hashistack-secure-infra-workflow\/tree\/main\/azure\"> Secure Infrastructure Workflow GitHub repo<\/a>.<\/em><\/p>","protected":false},"excerpt":{"rendered":"<p>There are three telling security statistics in modern cloud and hybrid environments: Data breaches cost an average of $4.5 million [&hellip;]<\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[6],"tags":[],"class_list":["post-930","post","type-post","status-publish","format-standard","hentry","category-terraform"],"_links":{"self":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/930","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/comments?post=930"}],"version-history":[{"count":0,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/posts\/930\/revisions"}],"wp:attachment":[{"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/media?parent=930"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/categories?post=930"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rssfeedtelegrambot.bnaya.co.il\/index.php\/wp-json\/wp\/v2\/tags?post=930"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}