Terraform + DigitalOcean + Fortanix DSM = Secure Access

I had the privilege of writing about how Fortanix now provides a Terraform Provider for the Fortanix Data Security Manager (used to be called Self-Defending KMS) from one of my previous blog posts: Automated Cryptographic Key Generation with Terraform

I wanted to take this one step further: If I was able to create a virtual instance on DigitalOcean, generate SSH keys that allows me to login to the virtual instance on Fortanix DSM, secure the keys through Fortanix DSM and login to the virtual instance by accessing the key through Fortanix DSM, and even allow someone else to access my virtual instance without having to share the private key or even have to add another public key manually under the “.ssh/authorized_keys”, wouldn’t that be nice secure play 👍

The rationale for it being I now have complete security not only of the keys that are used to access my virtual instance (and I don’t even need to know what the private key looks like and not needing to even worry about sharing the private key with anyone), but I could also disable the key if the key appears compromised or even disable a user from accessing the system from Fortanix DSM if they should no longer be allowed to access the system for whatever reason (sort of a Zero Trust SSH mechanism, except the keys are also backed by a Fortanix HSM).

And given what I knew about Terraform’s ability to provision virtual instances or droplets in DigitalOcean, all I wanted to achieve was something as simple as:

  • Create a new key in my Fortanix DSM (the private key never leaves the Fortanix HSM)

  • Provision a droplet in DigitalOcean

  • During the provisioning of the droplet, the public key of the previously generated key in Fortanix DSM will be used as my ssh key to log into the instance

  • Login to the virtual instance using the key stored in Fortanix DSM through ssh

  • Allow my team member to access the same droplet using the same key without having to know anything about Fortanix or DigitalOcean behind the scenes

Now I have a Zero Trust SSH mechanism without having reveal where my server is, what my key is and more importantly be able to disable access for my team member if I ever want to boot her off my server access 🤣

Let’s give it a go!


Setting up / Executing Terraform

First part is to declare the providers I will be using within Terraform.

terraform {
  required_providers {
    digitalocean = {
      source  = "digitalocean/digitalocean"
      version = "~> 2.0"
    }
    dsm = {
      version = "0.1.7"
      source  = "fortanix.com/fyoo/dsm"
    }
  }
}

Then, let’s go ahead and declare the credentials required to access my digitalocean and Fortanix DSM access. The following should cover the block if you’re using some form of a variables file (I just have this in a simple variables.tf file):

# Configure the DigitalOcean Provider
provider "digitalocean" {
  token    = var.do_token
}

# Configure the Fortanix DSM Provider
provider "dsm" {
  endpoint = var.dsm_endpoint
  username = var.dsm_username
  password = var.dsm_password
  acct_id  = var.dsm_acct_id
}

Time to revisit the key generation on Fortanix DSM. I’m going to use the Terraform DSM Provider and create a new key. For this we use the “dsm_sobject” resource block and I’ll also need to setup the provider to login using my credentials so the first part would look something like this:

# Create a new SSH key in Fortanix DSM
resource "dsm_sobject" "test_ssh_key" {
  name = "ssh_key_test"
  obj_type = "RSA"
  key_size = 2048
  group_id = var.dsm_ssh_key_group
  description = "Created by Terraform"
}

Nice addition to the DSM Provider as of version 0.1.6 now includes RSA keys converting into a ssh-rsa key as well:

# dsm_sobject.test_ssh_key:
resource "dsm_sobject" "test_ssh_key" {
  .
  .
  .
  ssh_pub_key = "<ssh-rsa public key embedded here>"
  .
  .
  .
}

When using Terraform to provision a new resource, I can also see that a new key was created within the Fortanix DSM UI:

Security Objects

We will then register the public key within DigitalOcean so we can then automatically register it to the instance we will create later:

# Create a new SSH key
resource "digitalocean_ssh_key" "test_ssh" {
  name  = "do-ssh-test-key"
  public_key = "ssh-rsa ${dsm_sobject.test_ssh_key.ssh_pub_key}"
}

Again, I can see the public key do-ssh-test-key registered in DigitalOcean:

ssh key

We can now safely go ahead and create a new droplet in DigitalOcean and allow that particular public key to be used for the root login:

# Create a new droplet in DigitalOcean
resource "digitalocean_droplet" "test_droplet" {
  image = "ubuntu-18-04-x64"
  name  = "do-test-vm"
  region  = "sgp1"
  size  = "s-1vcpu-1gb"
  ssh_keys = [digitalocean_ssh_key.test_ssh.fingerprint]
}

At this point you’ll notice my terraform deployment has completed:

Apply complete! Resources: 5 added, 0 changed, 0 destroyed.

Also my droplet in DigitalOcean has successfully been created:

ssh key

Now let’s take a look at how we log into the system.


Login to the Droplet

You’ll notice that as part of the Terraform script, you have a new file called “pkcs11.conf” created on your directory. Keep this secure for the moment, as you’ll need this configuration file to SSH to the instance.

Let’s get the IP address of the droplet. To find the IP address of the DigitalOcean droplet, you can always type “terraform show”, which would reveal the IP address for you:

# digitalocean_droplet.test_droplet:
resource "digitalocean_droplet" "test_droplet" {
  .
  .
  .
  ipv4_address  = "XXX.YYY.ZZZ.WWW"
  ipv4_address_private = "AAA.BBB.CCC.DDD"
  .
  .
  .
}

What we are looking for is the “ipv4_address”, not the “ipv4_address_private”. This is used internally by DigitalOcean.

We now need to install the Fortanix DSM PKCS#11 module to securely access the key on Fortanix DSM. Installation guide on both Linux and Windows are available here: https://support.fortanix.com/hc/en-us/articles/360018312391-PKCS-11.

Now that I know the IP address of the droplet, it’s time to try and login. If you’re using some Linux or Windows terminal, simply enter the following command:

$ ssh -o PKCS11Provider=/path/to/pkcs11.so root@XXX.YYY.ZZZ.WWW
Enter PIN for 'Fortanix Token':

At this point you can simply type “file://pkcs11.conf” as the PIN. You’ll then be greeted with a familiar warning to accept the fingerprint:

The authenticity of host '<ip> (<ip>)' can't be established.
ED25519 key fingerprint is SHA256:<some fingerprint>.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])?

And you should be logged in. Fortanix DSM UI shows a signature was executed during this process:

logged in

Now I can allow my team member to also login through the Fortanix DSM. Create a new App with their email address (or some distinct name) and with the App API key, your team member now has access to the droplet using the private key stored in Fortanix DSM as I can now grant them access by selecting “Copy API Key”:

copy api key

When I don’t think my team member should have access to the key to access the droplet, simply slide the “Enabled” button off.

Once you have created the new API key, share that to your team member and simply they need to modify the section “my API key” in their pkcs11.conf file:

$ cat pkcs11.conf
api_key = "<my API key>"
api_endpoint = "https://sdkms.fortanix.com"

prevent_duplicate_opaque_objects = true
retry_timeout_millis = 3000

[log]
file = "/tmp/fortanix_pkcs11.log"

And that’s it! If you’re done sharing your droplet with your team mates, feel free to tear all of this down through Terraform and now you have a clean state:

$ terraform destroy

We’ve now successfully created a new droplet on DigitalOcean, created a new key to use to access the droplet without having to ever lose or reveal my private key, and best of all - I was able to share access to the droplet with my team member without ever needing to muck about authorized_keys any more inside the droplet.

Next time, I hope to cover the same workflow with Azure, AWS and GCP - stay tuned and have a safe weekend!

Share this post:

Get our blog updates in your inbox: