Skip to content

Commit

Permalink
Merge pull request #54 from joshuamkite/feature/46_51_unified
Browse files Browse the repository at this point in the history
Version 7.0
  • Loading branch information
joshuamkite authored Feb 9, 2022
2 parents b1a08de + 7b08db8 commit fb96dc7
Show file tree
Hide file tree
Showing 19 changed files with 497 additions and 383 deletions.
369 changes: 226 additions & 143 deletions README.md

Large diffs are not rendered by default.

55 changes: 43 additions & 12 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,35 @@
**N.B.**
# 7.0

**Breaking changes with existing deployments using earlier module versions**

- **Change:** Retire deprecated null-resource provider
- **Change:** Retire deprecated template provider (required for darwin_arm64). Fixes [Issue #51](https://github.com/joshuamkite/terraform-aws-ssh-bastion-service/issues/51)
- **Feature:** Support provider default tags as well as explicit tags for all supported resources plus autoscaling group
- **Change:** Update Terraform version to >/=0.15.x/1.0.0
- **Change/Feature:** Change from Launch Configuration to Launch Template. Includes support for spot instances. Fixes [Issue #46](https://github.com/joshuamkite/terraform-aws-ssh-bastion-service/issues/51)
- **Feature:** Enable setting cloudwatch metrics for autoscaling group
- **Feature:** Add unique target group name for bastion host elb
- **Feature:** Enable setting EBS size and name for bastion instance
- **Bugfix:** Update formatting in readme
- **Bugfix:** Spellcheck readme and changelog
- **Change:** Update terraform-docs outputs on documentation
- **Change:** Update internal filenames and references to comply with [upstream template file naming conventions](https://www.terraform.io/language/functions/templatefile); move locals to locals.tf

# 6.1

**Change:** Update readme to specify that we are targeting terraform 13

# 6.0

**Change:** Major version increment as changes accommodate major version increments for both AWS provider and Terraform itself

**Change:** Terraform 0.13 using `terraform 0.13upgrade` linting and provider specification

**Change:** Terraform AWS provider 3.0.0 - `vpc_zone_identifier` argument now conflicts with `vpc_zone_identifier` in Resource: `aws_autoscaling_group`

**Feature:** Single deployment of included example, accommodating above changes

**Change:** `var.aws_profile` is now defaulted to `""` as it is only used for sample policies output

# 5.1

Expand Down Expand Up @@ -75,11 +106,11 @@ The tags given in var.tags are rendered to the Autoscaling group as before

# 4.3

**Feature:** You can now specify a list of one or more security groups to attach to the host instance launch configuration. This can be supplied together with or instead of a whitelisted range of CIDR blocks. **N.B. This is _not_ aws_security_group_rule/source_security_group_id!** If you wish to append your own 'security_group_id' rules then you will need to attach these from a plan caling this module (using output "bastion_sg_id") or as part of a separate security group which you then attach.
**Feature:** You can now specify a list of one or more security groups to attach to the host instance launch configuration. This can be supplied together with or instead of a whitelisted range of CIDR blocks. **N.B. This is _not_ aws_security_group_rule/source_security_group_id!** If you wish to append your own 'security_group_id' rules then you will need to attach these from a plan calling this module (using output "bastion_sg_id") or as part of a separate security group which you then attach.

It may be useful in an enterprise setting to have security groups with rules managed separately from the bastion plan but of course if you do not assign a suitable security group or whitelist then you may not be able to reach the service!

**Change:** The code has been DRYed significantly in locals.tf (to remove unused logic evaluations) and main.tf (to condense 2 seperate aws_launch_configuration and aws_autoscaling_group blocks into one each). This makes code maintenence much easier and less error prone **BUT** it does mean that these resources are now 'new' so if you are deploying over an older version of this plan then you can expect them to be recreated - as lifecycle 'create before destroy' is specified, deployment will be a bit longer but downtime should be brief.
**Change:** The code has been DRYed significantly in locals.tf (to remove unused logic evaluations) and main.tf (to condense 2 separate aws_launch_configuration and aws_autoscaling_group blocks into one each). This makes code maintenance much easier and less error prone **BUT** it does mean that these resources are now 'new' so if you are deploying over an older version of this plan then you can expect them to be recreated - as lifecycle 'create before destroy' is specified, deployment will be a bit longer but downtime should be brief.

**Bugfix:** Previously the Golang code used for obtaining users and ssh public keys limited the number of users returned to 100 _if_ an IAM group was specified. This has now been increased to 1000 and the code change accepted upstream.

Expand Down Expand Up @@ -112,11 +143,11 @@ If you exclude any section then you must replace it with equivalent functionalit
**Feature:** Move from Classic Load Balancer to Network Load Balancer.
* elb_idle_timeout and elb_timeout variables have been removed as they are not supported in this configuration.

* Configurable load balancer variables naming now prefixed 'lb'. Unfortunately the change in load balancer type breaks backward compatibilty with deployments using earlier versions of this module anyway so the opportunity is being taken to update the variable names for future sanity.
* Configurable load balancer variables naming now prefixed 'lb'. Unfortunately the change in load balancer type breaks backward compatibility with deployments using earlier versions of this module anyway so the opportunity is being taken to update the variable names for future sanity.

**Feature:** Security group rules apply 'description' tag

**Change:** New code now in seperate files to assist readabilty. locals also moved to seperate file.
**Change:** New code now in separate files to assist readability. locals also moved to separate file.

**Change:** Security group name for EC2 instance now name_prefix and simplified

Expand All @@ -142,9 +173,9 @@ If you exclude any section then you must replace it with equivalent functionalit

# 3.7

**Feature:** ELB health check port may be optionally set to either port 22 (containerised service; default) or port 2222 (EC2 host sshd). If you are deploying a large number of bastion instances, all of them checking into the same parent account for IAM queries in reponse to load balancer health checks on port 22 causes IAM rate limiting from AWS. Using the modified EC2 host sshd of port 2222 avoids this issue and is recommended for larger deployments. The host sshd is set to port 2222 as part of the service setup so this heathcheck is not entirely invalid. Security group rules are conditionally created to support any combination of access/healthceck on port 2222 or not.
**Feature:** ELB health check port may be optionally set to either port 22 (containerised service; default) or port 2222 (EC2 host sshd). If you are deploying a large number of bastion instances, all of them checking into the same parent account for IAM queries in response to load balancer health checks on port 22 causes IAM rate limiting from AWS. Using the modified EC2 host sshd of port 2222 avoids this issue and is recommended for larger deployments. The host sshd is set to port 2222 as part of the service setup so this healthcheck is not entirely invalid. Security group rules are conditionally created to support any combination of access/healthcheck on port 2222 or not.

**Feature:** Friendlier DNS and hostnaming. You can now define the last part of the hostname. By default this is the vpc ID via the magic default value of 'vpc_id' but you can pass a custom string, or an empty value to omit this. e.g.
**Feature:** Friendlier DNS and host naming. You can now define the last part of the hostname. By default this is the vpc ID via the magic default value of 'vpc_id' but you can pass a custom string, or an empty value to omit this. e.g.

module default: `dev-ap-northeast-1-vpc-1a23b456d7890-bastion-service.yourdomain.com`

Expand All @@ -167,11 +198,11 @@ If you exclude any section then you must replace it with equivalent functionalit

**Feature:** New output: bastion_sg_id gives the Security Group id of the bastion host which may be useful for other services

**Documentation:** update readme to reflect new ouptputs and names; acknowledgements
**Documentation:** update readme to reflect new outputs and names; acknowledgements

# 3.5 (broken, withdrawn)

**Bugfix:** Remove parentheses from the name of the sample policy ouptut to make it parsable when called from module
**Bugfix:** Remove parentheses from the name of the sample policy output to make it parsable when called from module

# 3.4 (broken, withdrawn)

Expand All @@ -189,15 +220,15 @@ If you exclude any section then you must replace it with equivalent functionalit

# 3.1

**Feature (backward compatible):** Improvements to example asssume role policy generation - making it easier to copy and paste from Terraform output to AWS web console
**Feature (backward compatible):** Improvements to example assume role policy generation - making it easier to copy and paste from Terraform output to AWS web console

# 3.0

With version 3 series (backward compatible with version 2) the ability to assume a role in another account has now been integrated with conditional logic. If you supply the ARN for a role for the bastion service to assume in another account ${var.assume_role_arn} then this plan will create an instance profile, role and policy along with each bastion to make use of it. A matching sample policy and trust relationship is given as an output from the plan to assist with application in the other account. If you do not supply this arn then this plan presumes IAM lookups in the same account and creates an appropriate instance profile, role and policies for each bastion in the same AWS account. 'Each bastion' here refers to a combination of environment, AWS account, AWS region and VPCID determined by deployment. Since this is a high availabilty service, it is not envisaged that there would be reason for more than one independent deployment within such a combination.
With version 3 series (backward compatible with version 2) the ability to assume a role in another account has now been integrated with conditional logic. If you supply the ARN for a role for the bastion service to assume in another account ${var.assume_role_arn} then this plan will create an instance profile, role and policy along with each bastion to make use of it. A matching sample policy and trust relationship is given as an output from the plan to assist with application in the other account. If you do not supply this arn then this plan presumes IAM lookups in the same account and creates an appropriate instance profile, role and policies for each bastion in the same AWS account. 'Each bastion' here refers to a combination of environment, AWS account, AWS region and VPCID determined by deployment. Since this is a high availability service, it is not envisaged that there would be reason for more than one independent deployment within such a combination.

Also with version 3 the IAM policy generation and user data have been moved from modules back into the main plan. User data is no longer displayed.

If you are seeking a solution for ECS hosts then you are recommended to either the [Widdix project]((https://github.com/widdix/aws-ec2-ssh)) directly or my [Ansible-galaxy respin of it](https://galaxy.ansible.com/joshuamkite/aws-ecs-iam-users-tags/). This offers a range of features, suitable for a long-lived stateful host built.
If you are seeking a solution for ECS hosts then you are recommended to either the [Widdix project]((https://github.com/widdix/aws-ec2-ssh)) directly or my [Ansible-galaxy re-spin of it](https://galaxy.ansible.com/joshuamkite/aws-ecs-iam-users-tags/). This offers a range of features, suitable for a long-lived stateful host built.

# 2.0

Expand Down
58 changes: 46 additions & 12 deletions examples/full-with-public-ip/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,52 @@ This example shows a complete setup for a new `bastion` service with all needed
* private subnet(s) inside the VPC,
* an internet gateway and route tables.

To create the bastion service, subnets need to already exist
This is currently a limitation of Terraform: https://github.com/hashicorp/terraform/issues/12570
Since Terraform version 0.12.0 you can either:
Comment out the bastion service, apply, uncomment and apply again (as for Terraform 0.11.x)
Or simply run the plan twice - first time will give an error like below, simply run again
## Requirements

Error: Provider produced inconsistent final plan
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 0.13 |

When expanding the plan for
module.ssh-bastion-service.aws_autoscaling_group.bastion-service to include
new values learned so far during apply, provider "aws" produced an invalid new
value for .availability_zones: was known, but now unknown.
## Providers

This is a bug in the provider, which should be reported in the provider's own
issue tracker.
| Name | Version |
|------|---------|
| <a name="provider_aws"></a> [aws](#provider\_aws) | 3.73.0 |

## Modules

| Name | Source | Version |
|------|--------|---------|
| <a name="module_ssh-bastion-service"></a> [ssh-bastion-service](#module\_ssh-bastion-service) | joshuamkite/ssh-bastion-service/aws | n/a |

## Resources

| Name | Type |
|------|------|
| [aws_internet_gateway.bastion](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/internet_gateway) | resource |
| [aws_route.bastion-ipv4-out](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route) | resource |
| [aws_route_table.bastion](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table) | resource |
| [aws_route_table_association.bastion](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route_table_association) | resource |
| [aws_subnet.bastion](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/subnet) | resource |
| [aws_vpc.bastion](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc) | resource |
| [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source |

## Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_aws_region"></a> [aws\_region](#input\_aws\_region) | Default AWS region | `string` | `"eu-west-1"` | no |
| <a name="input_cidr-start"></a> [cidr-start](#input\_cidr-start) | Default CIDR block | `string` | `"10.50"` | no |
| <a name="input_environment_name"></a> [environment\_name](#input\_environment\_name) | n/a | `string` | `"demo"` | no |
| <a name="input_everyone-cidr"></a> [everyone-cidr](#input\_everyone-cidr) | Everyone | `string` | `"0.0.0.0/0"` | no |
| <a name="input_tags"></a> [tags](#input\_tags) | tags aplied to all resources | `map(string)` | `{}` | no |

## Outputs

| Name | Description |
|------|-------------|
| <a name="output_bastion_service_role_name"></a> [bastion\_service\_role\_name](#output\_bastion\_service\_role\_name) | role created for service host asg - if created without assume role |
| <a name="output_bastion_sg_id"></a> [bastion\_sg\_id](#output\_bastion\_sg\_id) | Security Group id of the bastion host |
| <a name="output_lb_arn"></a> [lb\_arn](#output\_lb\_arn) | aws load balancer arn |
| <a name="output_lb_dns_name"></a> [lb\_dns\_name](#output\_lb\_dns\_name) | aws load balancer dns |
| <a name="output_lb_zone_id"></a> [lb\_zone\_id](#output\_lb\_zone\_id) | n/a |
35 changes: 11 additions & 24 deletions examples/full-with-public-ip/main.tf
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@

provider "aws" {
region = var.aws-region
region = var.aws_region
default_tags {
tags = local.default_tags
}
}

data "aws_availability_zones" "available" {
Expand All @@ -8,38 +12,21 @@ data "aws_availability_zones" "available" {
resource "aws_vpc" "bastion" {
cidr_block = "${var.cidr-start}.0.0/16"
enable_dns_hostnames = true

tags = {
Name = "bastion-${var.environment-name}-vpc"
}
}

resource "aws_subnet" "bastion" {
count = 1

count = 1
availability_zone = data.aws_availability_zones.available.names[count.index]
cidr_block = "${var.cidr-start}.${count.index}.0/24"
vpc_id = aws_vpc.bastion.id

tags = {
Name = "bastion-${var.environment-name}-subnet-${count.index}"
}
}

resource "aws_internet_gateway" "bastion" {
vpc_id = aws_vpc.bastion.id

tags = {
Name = "bastion-${var.environment-name}-ig"
}
}

resource "aws_route_table" "bastion" {
vpc_id = aws_vpc.bastion.id

tags = {
Name = "bastion-${var.environment-name}-rt"
}
}

resource "aws_route" "bastion-ipv4-out" {
Expand All @@ -49,8 +36,7 @@ resource "aws_route" "bastion-ipv4-out" {
}

resource "aws_route_table_association" "bastion" {
count = 1

count = 1
subnet_id = aws_subnet.bastion[count.index].id
route_table_id = aws_route_table.bastion.id
}
Expand All @@ -63,8 +49,8 @@ variable "everyone-cidr" {
module "ssh-bastion-service" {
source = "joshuamkite/ssh-bastion-service/aws"
# source = "../../"
aws_region = var.aws-region
environment_name = var.environment-name
aws_region = var.aws_region
environment_name = var.environment_name
vpc = aws_vpc.bastion.id
subnets_asg = flatten([aws_subnet.bastion.*.id])
subnets_lb = flatten([aws_subnet.bastion.*.id])
Expand All @@ -75,4 +61,5 @@ module "ssh-bastion-service" {
aws_subnet.bastion,
aws_internet_gateway.bastion,
]
}
bastion_instance_types = ["t2.micro"]
}
16 changes: 14 additions & 2 deletions examples/full-with-public-ip/vars.tf
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
variable "aws-region" {
variable "aws_region" {
default = "eu-west-1"
description = "Default AWS region"
}
Expand All @@ -8,6 +8,18 @@ variable "cidr-start" {
description = "Default CIDR block"
}

variable "environment-name" {
variable "environment_name" {
default = "demo"
}

variable "tags" {
type = map(string)
description = "tags aplied to all resources"
default = {}
}

locals {
default_tags = {
Name = "bastion-service-${var.environment_name}"
}
}
7 changes: 3 additions & 4 deletions load_balancer.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#######################################################

resource "aws_lb" "bastion-service" {
name = md5(format("${var.service_name}-%s", var.vpc))
name = "${var.service_name}-${var.environment_name}"
load_balancer_type = "network"
internal = var.lb_is_internal
subnets = var.subnets_lb
Expand Down Expand Up @@ -46,7 +46,7 @@ resource "aws_lb_listener" "bastion-host" {
# Target group service
#######################################################
resource "aws_lb_target_group" "bastion-service" {
name = md5(format("${var.service_name}-%s", var.vpc))
name = "${var.service_name}-${var.environment_name}-22"
protocol = "TCP"
port = 22
vpc_id = var.vpc
Expand All @@ -67,7 +67,7 @@ resource "aws_lb_target_group" "bastion-service" {
#######################################################
resource "aws_lb_target_group" "bastion-host" {
count = local.hostport_whitelisted ? 1 : 0
name = "bastion-host"
name = "${var.service_name}-${var.environment_name}-2222"
protocol = "TCP"
port = 2222
vpc_id = var.vpc
Expand All @@ -82,4 +82,3 @@ resource "aws_lb_target_group" "bastion-host" {

tags = var.tags
}

Loading

0 comments on commit fb96dc7

Please sign in to comment.