Skip to main content

Tutorial: your first Polaris deployment on AWS

In this tutorial you deploy Polaris with a hello-world workload, see Polaris check that it runs inside a confidential VM, watch Polaris learn it and turn on enforcement. You then see enforcement withdraw access to a key in KMS and restore it, and finish by removing everything you created.

You follow one fixed path with every choice made for you, so you can repeat the tutorial from scratch whenever you like. When you deploy your own workload later, the how-to guides walk through the choices.

Plan for about an hour, most of it spent waiting while Polaris learns the workload.

What this costs

The tutorial runs two m6a instances in US East (Ohio), a NAT gateway, six private network endpoints and a small Lambda function until you tear it down at the end. AWS adds 10 percent of the instance price for SEV-SNP, and the KMS key carries a small monthly fee until you schedule it for deletion. The AWS reference lists every item.

Before you start​

You need:

  • An AWS account where you can create VPCs, EC2 instances, IAM roles, Secrets Manager secrets, KMS keys, ECR repositories, Lambda functions, EventBridge rules, SSM parameters and CloudWatch alarms. Those are administrator-level permissions, so you may need your cloud administrator to grant them or to give you an account for this. The full list of actions is in IAM permissions.
  • Ideally, a sandbox account: a separate AWS account that runs nothing else, so nothing you create or delete can touch other work. In a shared account, this tutorial still keeps everything under its own name.
  • A machine with bash 4 or later, the AWS CLI version 2, Terraform 1.5 or later, Docker (running), OpenSSL, Python 3 with PyYAML and curl. Node.js 18 or later too, if you run the optional check in step 8. macOS ships bash 3.2, and brew install bash installs a newer one.
  • The Polaris tooling and the Polaris machine image, from your Fr0ntierX subscription. The Polaris SDK is needed only for the optional check in step 8.

The Polaris tooling is a folder of scripts and Terraform. Its top folder holds scripts/, terraform/, manifest/ and Demo/, and every command in this tutorial runs from there.

Draft

How you receive the Polaris tooling, the Polaris machine image and the Polaris SDK is being finalized together with the AWS Marketplace offer. This step will name exactly what you download or subscribe to. Until then, the tooling copies the Polaris images from Fr0ntierX's private registry, so Docker on your machine also needs a login to ghcr.io with read access that Fr0ntierX grants you.

Check that each tool answers:

bash --version | head -1
aws --version
terraform version
docker info --format '{{.ServerVersion}}'
openssl version
python3 -c 'import yaml; print(yaml.__version__)'
node --version

All seven print a version, with bash at 4 or higher. If docker info fails, start Docker and run it again. If the Python line fails, install PyYAML with python3 -m pip install pyyaml.

Then check that you are in the top folder of the tooling:

ls

The list includes scripts, terraform, manifest and Demo. The tooling keeps the deployment's state here for the later steps.

Step 1: Sign in to AWS​

Sign in with the AWS CLI the way your organization does it, for example with aws configure sso or aws configure, and then check which account you are in:

aws sts get-caller-identity --query Account --output text

It prints your 12-digit account ID. Make sure it is the account you mean to use.

If you signed in with aws login, the AWS CLI works but Terraform cannot read that session, and step 6 stops with No valid credential sources found. Add a profile that hands the session to Terraform, at the end of ~/.aws/config:

[profile polaris]
region = us-east-2
credential_process = aws configure export-credentials --profile default --format process

Then run export AWS_PROFILE=polaris in the terminal you use for this tutorial.

Step 2: Create the KMS key​

Polaris gates access to a KMS key that you own. Create one in Ohio for this tutorial:

aws kms create-key --region us-east-2 \
--description 'Polaris tutorial key' \
--query KeyMetadata.Arn --output text

It prints the key's ARN, which starts with arn:aws:kms:us-east-2:. Copy it for step 5, and check that the key is ready:

aws kms describe-key --region us-east-2 --key-id <key ARN> \
--query 'KeyMetadata.[KeyState,KeyManager,KeyUsage]' --output text

It prints Enabled CUSTOMER ENCRYPT_DECRYPT.

Step 3: Create a key pair​

The key pair lets the tooling reach the deployment over SSH.

aws ec2 create-key-pair --region us-east-2 \
--key-name polaris-tutorial \
--query KeyMaterial --output text > ~/.ssh/polaris-tutorial.pem
chmod 400 ~/.ssh/polaris-tutorial.pem

Check that the file starts with a private key header:

head -1 ~/.ssh/polaris-tutorial.pem

It prints -----BEGIN RSA PRIVATE KEY-----.

Step 4: Find your public address​

SSH to the deployment opens to your address alone, so look it up:

curl -s https://checkip.amazonaws.com

It prints an address such as 203.0.113.4, which you use in the next step.

Step 5: Write the deployment config​

Create a file named polaris-tutorial.yaml in the top folder of the Polaris tooling, next to scripts/ rather than inside it, with this content. Replace the two placeholders in angle brackets, and keep the /32 after your address: the setting takes address ranges, and /32 makes the range your address alone.

apiVersion: polaris/v1
kind: DeploymentConfig

metadata:
name: polaris-tutorial

cloud:
provider: aws
aws:
region: us-east-2
kms_key_arn: "<key ARN from step 2>"
key_pair_name: polaris-tutorial
ssh_allowed_cidrs:
- "<your address from step 4>/32"

tee:
cpu: sev-snp
gpu: none

features:
attestation_mode: full
ima: true
ebpf: true

workload:
dir: Demo/hello-world
port: 8080
env:
MESSAGE: "Hello from my first Polaris deployment"
security:
read_only: true

compute:
cvm_machine_type: m6a.xlarge
pm_machine_type: m6a.large

polaris:
docker_repo: polaris-tutorial

The tooling finds the private key from step 3 by its name, ~/.ssh/polaris-tutorial.pem.

The workload is a hello-world web server that ships with the tooling, in its Demo/hello-world folder. It answers requests to / with the MESSAGE you set, which keeps its behavior simple and lets Polaris learn it quickly. The tooling builds it and pushes it, with the Polaris images, to registries in your account under polaris-tutorial/, so the cleanup in step 12 removes only what this tutorial created.

attestation_mode: full, together with ima and ebpf, turns on every layer Polaris has, including the runtime and kernel checks.

Draft

When your subscription provides the Polaris machine image through AWS Marketplace, this file also needs an ami_id line under aws. This step will show how to look up the value.

Step 6: Deploy​

./scripts/deploy.sh deploy --config polaris-tutorial.yaml --promotion-window 0

Normally the Policy Manager refuses to enforce a layer until it has been clean under advise for a day. --promotion-window 0 turns that wait off, so that step 10 works within the hour. Leave it out for a real deployment.

The tooling checks your KMS key, builds the workload, creates the network and both VMs, and waits for them to start, printing each step as it goes. Near the end it waits several minutes for the Policy Manager to come up and checks its identity. Expect it to take several minutes, with long stretches of no output, and the wait for the Policy Manager gives up after 15 minutes. Let it finish: interrupting it while Terraform is creating resources leaves a half-built deployment.

It ends with a block headed Polaris Deployment Complete:

Endpoint: https://<address>
...
Deployment Name: polaris-tutorial

Copy the endpoint address, and check that both VMs are running:

./scripts/deploy.sh status --config polaris-tutorial.yaml

It lists polaris-tutorial-cvm and polaris-tutorial-pm, both running, and repeats the endpoint.

Step 7: Send a first request​

curl -k https://<address>/

It prints Hello from my first Polaris deployment, after a trip through the Polaris proxy to your workload.

The -k flag tells curl to skip its certificate check, because the proxy's certificate is self-signed and the hardware report vouches for it in place of a public authority. You check that report in the next step, and that check is what tells you the deployment is confidential. Until you have made it, treat a plain curl as a connectivity test and keep sensitive data out of it.

If curl cannot connect, the workload VM is still starting its services, which deploy says takes 2 to 3 minutes. If you get a 503 with attestation pending in the message, the proxy is holding traffic until the Policy Manager's first verdict. In either case, wait 30 seconds and try again.

For up to two minutes after the proxy starts, it lets requests through while it waits for that first verdict. From then on it holds traffic only while a layer you enforce fails, and every layer starts out advisory, so for now it serves every request.

Step 8: Check the environment is confidential​

This step is optional. It checks the hardware report the way a real client does before it sends data. The Policy Manager makes the same hardware check on every cycle whether or not you run it, and nothing later in the tutorial depends on it, so if you don't have the Polaris SDK yet, go on to step 9.

Install the Polaris SDK in a new folder:

mkdir polaris-verify && cd polaris-verify
npm init -y
npm install @polaris/sdk
Draft

The SDK's package name and where you install it from will be confirmed with the AWS Marketplace offer. @polaris/sdk is not published on the public npm registry, so install the package Fr0ntierX gives you in its place, and do not install that name from the public registry.

Create verify.mjs:

import { verifyProxy } from "@polaris/sdk";

const result = await verifyProxy(process.argv[2], {
mode: "snp-direct",
allowSelfSigned: true,
});

console.log("valid:", result.valid);
console.log("measurement:", result.claims?.measurement);
console.log("errors:", result.errors);

The proxy's certificate is self-signed, so allowSelfSigned lets the SDK connect. The SDK still checks that the key securing your connection is the one the hardware report vouches for, which is the check that matters here. It prints a security warning about allowSelfSigned, which you can expect.

Run it against your endpoint:

node verify.mjs https://<address>
cd ..

It prints valid: true, a 96-character measurement and an empty list of errors.

Needs a recent SDK

AWS hands the workload VM only the VLEK, the key that signs the report, and not AMD's intermediate and root certificates above it. The SDK adds those from its own copies since polaris-sdk#3. An SDK from before that prints valid: false with the error certificate table is missing the ARK certificate. If you see that error, ask Fr0ntierX for a newer SDK. The Policy Manager makes the same hardware check on every cycle and is not affected either way.

The SDK checks that the report is signed by AMD hardware, that it describes a confidential VM, and that it is bound to the TLS connection you are using. Verifying a Deployment explains what each check means.

Step 9: Watch Polaris learn the workload​

A new deployment starts in an advisory state, and Polaris watches the healthy workload to learn what normal looks like.

The tooling saved a read-only token for this deployment next to its Terraform files, and you use it to watch:

./scripts/deploy.sh watch \
--endpoint https://<address> \
--token "$(cat terraform/modules/aws/terraform-polaris-tutorial.readonly.token)"

It prints one snapshot with an Overall result, a line for each layer (L1 platform, L2 image, L3 runtime, L3.1 kernel), an Alerts count, and under Autolearn convergence a line per learner showing how many stable cycles it has seen. Run it again every few minutes.

At first some layers show findings and Alerts is high, which is expected while the allowlists are still empty. Learning needs 20 clean report cycles, so it takes at least 10 minutes, and a little longer while the workload settles.

Wait until Overall and every layer show pass and Alerts reads 0.

Soon after learning finishes, a single alert can appear for a file the VM's scheduled jobs touch for the first time, such as unknown executable "/etc/pam.d/cron" at 17 minutes past the hour, when the VM's hourly jobs run. It clears on the next cycles. Run watch again until it reads clean, and move on only then.

Step 10: Turn on enforcement​

Move the runtime layer, l3, from advisory to enforcing:

./scripts/deploy.sh promote --config polaris-tutorial.yaml --layers l3

The tooling reaches the Policy Manager over SSH through the workload VM, which is why step 5 opened SSH to your address. It changes the l3 setting alone, keeps everything Polaris learned, and reads the setting back to confirm it took, printing confirmed: l3 now enforce. If the upload fails, it stops with an error and the deployment keeps its previous settings. If only the read-back fails, run the same command again: it prints the setting the deployment has before changing anything.

It also prints five warnings from the Policy Manager, one for each system folder such as /usr/bin/, saying that the host's allowlist accepts the whole folder. They are expected: the boot-time and runtime measurements still flag any file there that Polaris does not know.

From now on, any file the system reads as root for the first time fails l3 and withdraws the key, even when it is routine system work. About two minutes after promote, a live run saw this for the man page cache under /var/cache/man/: the key was gone for 30 seconds and came back on its own. Machine images built since 29 October 2026 empty that cache. An older image does this once on each new VM, 15 minutes after it boots when the image is more than a week old.

If promote refuses because an allowlist is still empty, learning is still running. Go back to step 9 and wait. If it refuses because the layer has not been clean for a day, the deploy in step 6 ran without --promotion-window 0.

Step 11: See enforcement work​

The workload VM's IAM role holds a grant on your KMS key while its checks pass. Count the grants:

aws kms list-grants --region us-east-2 --key-id <key ARN> \
--query 'length(Grants)' --output text

It prints 1.

Now run a program Polaris has never seen on the workload VM. This copies a standard tool to a new name and runs it once:

ssh -i ~/.ssh/polaris-tutorial.pem \
-o StrictHostKeyChecking=yes \
-o UserKnownHostsFile=terraform/modules/aws/terraform-polaris-tutorial.known_hosts \
ubuntu@<address> 'cp /bin/sleep /tmp/polaris-probe && /tmp/polaris-probe 1'

The tooling pinned the VM's SSH host keys when it deployed, and these options have SSH use that pin directly.

Wait 30 seconds, so the workload VM can send its next report, and count the grants again:

aws kms list-grants --region us-east-2 --key-id <key ARN> \
--query 'length(Grants)' --output text

It prints 0. The runtime layer saw an unknown program, and the Policy Manager withdrew access to your key. The verdict comes with the next report, and the grant leaves the list within seconds of it. If it still prints 1, wait another 30 seconds and count again. KMS can take longer to stop honoring a removed grant, in some cases several minutes, as How your key is gated explains.

Wait a minute and count once more. It prints 1 again, because the unknown program has not run again, the next check passed and access came back on its own.

All of this happened in KMS itself, and CloudTrail records the same two events as RetireGrant and CreateGrant, made by the Policy Manager's instance.

Step 12: Tear it down​

./scripts/deploy.sh destroy --config polaris-tutorial.yaml

The tooling revokes the deployment's grants on your key and then removes the VMs and their disks, the network, the roles, the secrets and the grant retirer. Check that nothing is left running:

./scripts/deploy.sh status --config polaris-tutorial.yaml

It prints No Polaris instances running. The EC2 console, including EC2 Global View, keeps listing both instances for a while with the state Terminated. They cost nothing, and AWS removes them from the list on its own.

Then check that no grant is left on your key:

aws kms list-grants --region us-east-2 --key-id <key ARN> \
--query "Grants[?starts_with(Name,'polaris-')].[GrantId,Name]" --output text

It prints nothing. The Policy Manager keeps running for a few minutes after destroy revokes the grants, and it can create one again in that time. If the list shows any, revoke them by hand as Tear down a deployment explains.

destroy takes a few minutes, most of it removing the network endpoints. It also schedules the Policy Manager's identity key for deletion, which AWS completes after seven days.

Three things remain on purpose. Your KMS key stays, because in a real deployment your data is encrypted under it. The container registries the tooling created under polaris-tutorial/ stay so that a later deployment can reuse them, and the key pair stays because you created it yourself. Since all three were for the tutorial, remove them:

aws kms schedule-key-deletion --region us-east-2 \
--key-id <key ARN> --pending-window-in-days 7
for repo in polaris-proxy polaris-policy-manager polaris-sentinel hello-world; do
aws ecr delete-repository --region us-east-2 --force \
--repository-name "polaris-tutorial/$repo"
done
aws ec2 delete-key-pair --region us-east-2 --key-name polaris-tutorial
rm -f ~/.ssh/polaris-tutorial.pem

KMS waits at least seven days before it deletes a key, and you can cancel the deletion until then. The loop deletes only the polaris-tutorial/ registries. Other deployments in the account keep theirs.

The tooling folder still holds the deployment's Terraform state, its config file and its tokens. Once destroy has finished, delete them as well:

cd terraform/modules/aws
rm -f terraform-polaris-tutorial.tfstate* \
terraform-polaris-tutorial.tfvars \
terraform-polaris-tutorial.token \
terraform-polaris-tutorial.readonly.token \
pm-identity-polaris-tutorial.json \
pm-snp-baseline-polaris-tutorial.yaml
cd ../../..

What you did​

You deployed a workload into a confidential VM, watched Polaris check its hardware and learn the workload, and enforced one layer. You then watched Polaris withdraw and restore access to a key in KMS.

Next:

  • The how-to guides cover deploying and running your own workload.
  • Polaris on AWS explains why it works this way.
  • The AWS reference lists regions, permissions, config keys and what Polaris offers on AWS today.