Terraform module for creating Linux bastion servers in member AWS accounts
create file named 'bastion_linux.json' and populate with below format. Add user names and public ssh keys as required.
{
"keys": {
"development": {
"username1": "public key data ~~~~~~~~~~~~~~~",
"username2": "public key data ~~~~~~~~~~~~~~~"
},
"test": {
"username1": "public key data ~~~~~~~~~~~~~~~",
"username2": "public key data ~~~~~~~~~~~~~~~"
},
"preproduction": {
"username1": "public key data ~~~~~~~~~~~~~~~",
"username2": "public key data ~~~~~~~~~~~~~~~"
},
"production": {
"username1": "public key data ~~~~~~~~~~~~~~~",
"username2": "public key data ~~~~~~~~~~~~~~~"
}
}
}
create a bastion_linux.tf file as below, change options as required
locals {
public_key_data = jsondecode(file("./bastion_linux.json"))
}
module "bastion_linux" {
source = "github.com/ministryofjustice/modernisation-platform-terraform-bastion-linux?ref=v3.0.4"
providers = {
aws.share-host = aws.core-vpc # core-vpc-(environment) holds the networking for all accounts
aws.share-tenant = aws # The default provider (unaliased, `aws`) is the tenant
}
# s3 - used for logs and user public keys
bucket_name = "bastion"
# public keys
public_key_data = local.public_key_data.keys[local.environment]
# logs
log_auto_clean = "Enabled"
log_standard_ia_days = 30 # days before moving to IA storage
log_glacier_days = 60 # days before moving to Glacier
log_expiry_days = 180 # days before log expiration
# bastion
allow_ssh_commands = false
app_name = var.networking[0].application
business_unit = local.vpc_name
subnet_set = local.subnet_set
environment = local.environment
region = "eu-west-2"
# Tags
tags_common = local.tags
tags_prefix = terraform.workspace
}
Passing in a custom KMS key? You'll need to make sure the bastion iam role has permissions to use it.
See aws_kms_key_policy.bastion_s3
in main.tf
for an example.
This module ouputs the bastion iam role object (see outputs.tf
), so you can use it in your own policy.
If you're looking to raise an issue with this module, please create a new issue in the Modernisation Platform repository.
checkov_exclude: CKV_AWS_144
(Ensure S3 bucket has cross-region replication enabled): Enabling cross-region replication for the bastion module might be an overkill. Looking at the use cases provided by Amazon https://aws.amazon.com/s3/features/replication/ none of them seems relevant to our bastion logs. Besides, the checkov_exclude: CKV_AWS_144 needs to be in place anyway because checkov will require replication for the replication bucket as it cannot separate it from the regular non-replication bucket. If in the future we do decide to enable cross-region replication we should use the following terraform module, which supports cross-region replication as an optional configuration: https://github.com/ministryofjustice/modernisation-platform-terraform-s3-bucketcheckov_exclude: CKV_AWS_18
(Ensure the S3 bucket has access logging enabled),tfsec_exclude: AWS002
(Resource 'aws_s3_bucket.default' does not have logging enabled): After a discussion with the team we decided not to enable server access logging for the S3 bucket. The S3 bucket is only used by the bastion server and will not be accessed directly. Therefore, access logs are not important. If in the future we do decide to enable server access logging, we should use the following terraform module, setting thelog_bucket
configuration option: https://github.com/ministryofjustice/modernisation-platform-terraform-s3-bucket
We have S3 versioning enabled in bastion not as much for versioning but more so to allow recovering logs that are accidentally deleted.
We record every SSH session into a separate file named using the current time rounded to the minute. Then, every 10 minutes we copy log files to S3 and delete log files that are older than a day. Deleting log files older than a day is simply a way to clear the local ephemeral storage (also cleared upon restart). Since older logs are already stored in S3 and we don't provide the -delete
option into the aws s3 sync
command, they will be retrievable from S3.
We also have S3 lifecycle management configured to gradually transition logs to cheaper storage. Therefore, logs remain in S3 for 30 days before moving to IA (Infrequent Access) storage. After 60 days they move to Glacier and after 180 days they expire and are permanently deleted.
In order to prevent older versions from being retained forever, in addition to the lifecycle policy for the current version we also configure a separate lifecycle policy for noncurrent versions. Otherwise, older versions would never be moved to cheaper storage and would never be expired/deleted. We use the S3 bucket module for this which allows both current and noncurrent version lifecycle management.
Name | Version |
---|---|
terraform | >= 1.0.1 |
aws | ~> 5.0 |
random | ~> 3.4 |
Name | Version |
---|---|
aws | ~> 5.0 |
aws.share-host | ~> 5.0 |
random | ~> 3.4 |
Name | Source | Version |
---|---|---|
s3-bucket | github.com/ministryofjustice/modernisation-platform-terraform-s3-bucket | 7b2b75c178f855d8c48d3bda4ac53df782288c02 |
Name | Description | Type | Default | Required |
---|---|---|---|---|
allow_ssh_commands | Allow SSH commands to be specified | bool |
n/a | yes |
app_name | Name of application | string |
n/a | yes |
autoscaling_cron | Cron expressions for scale up and scale down | map(string) |
{ |
no |
bucket_name | Bucket used for bucket log storage and user public keys | string |
n/a | yes |
business_unit | Fixed variable to specify business-unit for RAM shared subnets | string |
n/a | yes |
custom_s3_kms_arn | KMS ARN for S3 bucket encryption | string |
"" |
no |
environment | application environment | string |
n/a | yes |
extra_user_data_content | Extra user data content for Bastion ec2 | string |
"" |
no |
instance_name | Name of instance | string |
"bastion_linux" |
no |
log_auto_clean | Enable or not the lifecycle | string |
n/a | yes |
log_expiry_days | Number of days before logs expiration | number |
n/a | yes |
log_glacier_days | Number of days before moving logs to Glacier | number |
n/a | yes |
log_standard_ia_days | Number of days before moving logs to IA Storage | number |
n/a | yes |
public_key_data | User public keys for specific environment | map(any) |
n/a | yes |
region | #Main | string |
n/a | yes |
subnet_set | Fixed variable to specify subnet-set for RAM shared subnets | string |
n/a | yes |
tags_common | MOJ required tags | map(string) |
n/a | yes |
tags_prefix | prefix for name tags | string |
n/a | yes |
volume_size | Size of the volume in gibibytes (GiB) | number |
8 |
no |
Name | Description |
---|---|
bastion_iam_role | IAM role of bastion |
bastion_kms_key | n/a |
bastion_kms_key_alias | n/a |
bastion_launch_template | Launch template of bastion |
bastion_s3_bucket | S3 bucket of bastion |
bastion_security_group | Security group of bastion |
bastion_security_group_map | Security group details of bastion |