SSM Bastion
A jump host with no SSH, reached only through AWS Systems Manager Session Manager.
GovCloudFIPSCMKView source
- Module
- bastion
- Layer
- Network
- Interface
- 11 inputs, 4 outputs
- Used by
- 2 Kaizen apps
Why it matters
Engineers sometimes need to reach a private database to debug or run a migration. This host gives them that access with no SSH keys and no open port 22: every session goes through AWS Systems Manager, which checks IAM permissions and records the session. It switches the operating system to its FIPS crypto policy at boot and requires a customer managed key for its disk.
Use it when
- Operators need psql access to rds-postgres from a laptop.
- You route Cloudflare WARP traffic into the VPC. Set cloudflared_token_ssm_parameter, and tls_passthrough_target for the load balancer.
Reach for something else when
- You only need a shell in a running app container. Turn on ECS Exec for the incident instead. Use ecs-service.
What it creates
aws_iam_role(AmazonSSMManagedInstanceCore and CloudWatchAgentServerPolicy, partition aware ARNs)aws_iam_role_policy(SSM parameter reads and kms:Decrypt, optional)aws_iam_instance_profileaws_security_group(egress only, plus 443 and 80 when TLS passthrough is on)aws_instance(Amazon Linux 2023, private subnet, no public IP, IMDSv2 required, encrypted root volume)
Secure by default
- No SSH key and no port 22 ingress rule.
- No public IP (associate_public_ip_address = false).
- IMDSv2 is required with a hop limit of 1.
- The root volume is encrypted with the required kms_key_arn.
- User data runs update-crypto-policies --set FIPS at first boot.
Commercial and GovCloud
module "bastion" {
source = "git::ssh://git@github.com/the-kaizen-labs/terraform-modules.git//bastion?ref=v1.7.0"
name = local.app_name
vpc_id = module.networking.vpc_id
private_subnet_id = module.networking.private_subnet_ids[0]
ami_id = data.aws_ami.al2023.id
kms_key_arn = aws_kms_key.this.arn
ssm_parameter_arns = [data.aws_ssm_parameter.db_password.arn]
tags = local.tags
}| Setting | Commercial default | Federal setting | Note |
|---|---|---|---|
ami_id | Amazon Linux 2023 AMI | the FIPS enabled Amazon Linux 2023 AMI | The user data also sets the FIPS crypto policy in both cases. |
kms_key_arn | required, no default | required, customer managed KMS key ARN |
How it connects
- From networking: vpc_id, private_subnet_ids[0] (as private_subnet_id)
- From alb: alb_dns_name (as tls_passthrough_target)
- To rds-postgres: security_group_id (as allowed_security_group_ids)
Inputs
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
ami_id | string | yes | AL2023 AMI ID. Use the FIPS-enabled variant for federal posture; the user-data also runs update-crypto-policies --set FIPS as a belt-and-suspenders. | |
kms_key_arn | string | yes | Customer-managed KMS key ARN for the EBS root volume + SSM SecureString decryption | |
name | string | yes | Resource name prefix | |
private_subnet_id | string | yes | Private subnet ID. The bastion has no public IP - operators reach it via SSM Session Manager. | |
vpc_id | string | yes | VPC ID | |
cloudflared_token_ssm_parameter | string | null | no | Name of a SecureString SSM parameter holding a Cloudflare tunnel token. When set, cloudflared is installed at boot and registered as a systemd service acting as a connector for that tunnel. The parameter must exist before the instance boots. |
instance_type | string | "t4g.nano" | no | EC2 instance type |
ssm_parameter_arns | list(string) | [] | no | SSM parameter ARNs the bastion can read (e.g. RDS password). Empty list = no SSM read. |
tags | map(string) | {} | no | Tags |
tls_passthrough_allowed_cidrs | list(string) | [] | no | CIDRs allowed to reach the TLS passthrough ports (443/80). Typically the VPC CIDR so peer tunnel connectors can relay too. |
tls_passthrough_target | string | null | no | DNS name to TCP-passthrough ports 443/80 to (typically the env ALB). When set, an nginx stream proxy is installed at boot and the SG allows 443/80 from tls_passthrough_allowed_cidrs. TLS is not terminated on the bastion. |
Outputs
| Name | Description |
|---|---|
iam_role_arn | Value: aws_iam_role.bastion.arn |
instance_id | Value: aws_instance.bastion.id |
private_ip | Value: aws_instance.bastion.private_ip |
security_group_id | Bastion SG - grant ingress from this on port 5432 to allow psql to RDS |
Used by
| App | Pinned ref |
|---|---|
| ginkgo | v1.3.0 |
| magnolia | v1.2.0 |