Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a84e0734e9 | |||
| 2f460799c9 | |||
| 8a5f158873 | |||
| e721b4d077 |
264
README.md
Normal file → Executable file
264
README.md
Normal file → Executable file
@ -1,98 +1,238 @@
|
|||||||
# Terraform bastion module
|
# Terraform AWS Bastion Host Module
|
||||||
|
|
||||||
Module to create a bastion host (or stepping stone). The module let you enable this host once needed. Be default the host is not created.
|
## Overview
|
||||||
|
|
||||||
## Terraform version
|
This Terraform module creates an EC2 bastion host (jump server) for secure SSH access to private resources in your AWS VPC. The bastion host can be enabled or disabled as needed and includes features like Elastic IP, custom security groups, and configurable instance specifications.
|
||||||
|
|
||||||
- Terraform 0.12: Pin module to `~> 2+`, submit pull request to branch `develop`
|
## Features
|
||||||
- Terraform 0.11: Pin module to `~> 1.x`, submit pull request to branch `terrafomr011`
|
|
||||||
|
|
||||||
|
- Conditional creation - Enable/disable bastion host via variable
|
||||||
|
- Elastic IP assignment for consistent public IP
|
||||||
|
- Dedicated network interface with security group
|
||||||
|
- Automatic AWS optimized AMI selection
|
||||||
|
- Custom user data support
|
||||||
|
- EBS optimization option
|
||||||
|
- Configurable CIDR-based access control
|
||||||
|
- IMDSv2 enforcement for enhanced security
|
||||||
|
- Flexible tagging support
|
||||||
|
|
||||||
## Example usages:
|
## Resources Created
|
||||||
See also the [full examples](./examples).
|
|
||||||
|
|
||||||
```
|
- EC2 Instance (Amazon Linux optimized AMI)
|
||||||
|
- Elastic IP (EIP)
|
||||||
|
- Network Interface
|
||||||
|
- Security Group with SSH ingress rules
|
||||||
|
- IAM instance profile (optional, via user configuration)
|
||||||
|
|
||||||
module "vpc" {
|
## Usage
|
||||||
source = "git::https://github.com/philips-software/terraform-aws-vpc?ref=2.0.0"
|
|
||||||
|
|
||||||
environment = var.environment
|
### Basic Example
|
||||||
aws_region = var.aws_region
|
|
||||||
}
|
|
||||||
|
|
||||||
# Default bastion
|
```hcl
|
||||||
module "bastion" {
|
module "bastion" {
|
||||||
source = "git::https://github.com/philips-software/terraform-aws-bastion?ref=2.0.0"
|
source = "git@github.com:webuildyourcloud/terraform-aws-bastion.git?ref=tags/0.0.7"
|
||||||
|
|
||||||
|
# Enable bastion creation
|
||||||
enable_bastion = true
|
enable_bastion = true
|
||||||
|
|
||||||
environment = var.environment
|
# Naming
|
||||||
project = var.project
|
environment = "prod"
|
||||||
|
project = "myapp"
|
||||||
|
|
||||||
aws_region = var.aws_region
|
# Network Configuration
|
||||||
key_name = aws_key_pair.bastion_key[0].key_name
|
aws_region = "us-east-1"
|
||||||
subnet_id = element(module.vpc.public_subnets, 0)
|
vpc_id = "vpc-12345678"
|
||||||
vpc_id = module.vpc.vpc_id
|
subnet_id = "subnet-12345678"
|
||||||
|
|
||||||
// add additional tags
|
# Instance Configuration
|
||||||
|
instance_type = "t3.micro"
|
||||||
|
key_name = "my-ssh-key"
|
||||||
|
|
||||||
|
# Security
|
||||||
|
admin_cidr = "203.0.113.0/24"
|
||||||
|
|
||||||
|
# Tags
|
||||||
tags = {
|
tags = {
|
||||||
my-tag = "my-new-tag"
|
ManagedBy = "terraform"
|
||||||
|
Environment = "production"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Inputs
|
### Advanced Example with Custom AMI
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
module "bastion" {
|
||||||
|
source = "git@github.com:webuildyourcloud/terraform-aws-bastion.git?ref=tags/0.0.7"
|
||||||
|
|
||||||
|
enable_bastion = true
|
||||||
|
environment = "dev"
|
||||||
|
project = "research"
|
||||||
|
aws_region = "eu-west-1"
|
||||||
|
vpc_id = module.vpc.vpc_id
|
||||||
|
subnet_id = module.vpc.public_subnets[0]
|
||||||
|
|
||||||
|
# Custom instance configuration
|
||||||
|
instance_type = "t3.small"
|
||||||
|
ebs_optimized = true
|
||||||
|
key_name = "dev-bastion-key"
|
||||||
|
|
||||||
|
# Custom AMI per region
|
||||||
|
amazon_optimized_amis = {
|
||||||
|
"eu-west-1" = "ami-0123456789abcdef0"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Restrict SSH access
|
||||||
|
admin_cidr = "10.0.0.0/8"
|
||||||
|
|
||||||
|
# Custom user data
|
||||||
|
user_data = file("${path.module}/bastion-userdata.sh")
|
||||||
|
|
||||||
|
tags = {
|
||||||
|
Backup = "true"
|
||||||
|
CostCenter = "engineering"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Variables
|
||||||
|
|
||||||
| Name | Description | Type | Default | Required |
|
| Name | Description | Type | Default | Required |
|
||||||
| ----------------------- | ------------------------------------------------------------------------------------------------------------ | :---------: | :-----------: | :------: |
|
|------|-------------|------|---------|----------|
|
||||||
| admin\_cidr | CIDR pattern to access the bastion host | string | `"0.0.0.0/0"` | no |
|
| aws_region | The AWS region | `string` | n/a | yes |
|
||||||
| amazon\_optimized\_amis | Map from region to AMI. By default the latest Amazon Linux is used. | map(string) | `<map>` | no |
|
| project | Name of the project | `string` | n/a | yes |
|
||||||
| aws\_region | The Amazon region. | string | n/a | yes |
|
| environment | Logical name of the environment | `string` | n/a | yes |
|
||||||
| ebs\_optimized | If true, the launched EC2 instance will be EBS-optimized. | bool | `"false"` | no |
|
| key_name | SSH key name for the environment | `string` | n/a | yes |
|
||||||
| enable\_bastion | If true the bastion will be created. Be default the bastion host is not running, needs explicit set to true. | bool | `"false"` | no |
|
| vpc_id | The VPC ID to launch the instance in | `string` | n/a | yes |
|
||||||
| environment | Logical name of the environment. | string | n/a | yes |
|
| subnet_id | Subnet in which the bastion needs to be deployed | `string` | n/a | yes |
|
||||||
| instance\_type | EC2 instance type. | string | `"t2.micro"` | no |
|
| enable_bastion | Enable bastion host creation | `bool` | `false` | no |
|
||||||
| key\_name | SSH key name for the environment. | string | n/a | yes |
|
| instance_type | EC2 instance type | `string` | `"t2.micro"` | no |
|
||||||
| project | Name of the project. | string | n/a | yes |
|
| ebs_optimized | Enable EBS optimization | `bool` | `true` | no |
|
||||||
| subnet\_id | Subnet in which the basion needs to be deployed. | string | n/a | yes |
|
| admin_cidr | CIDR pattern to access the bastion host | `string` | `"0.0.0.0/0"` | no |
|
||||||
| tags | Map of tags to apply on the resources | map(string) | `<map>` | no |
|
| amazon_optimized_amis | Map from region to AMI (uses latest Amazon Linux if not specified) | `map(string)` | `{}` | no |
|
||||||
| user\_data | Used data for bastion EC2 instance | string | `""` | no |
|
| user_data | User data for bastion EC2 instance | `string` | `""` | no |
|
||||||
| vpc\_id | The VPC to launch the instance in (e.g. vpc-66ecaa02). | string | n/a | yes |
|
| tags | Map of tags to apply on the resources | `map(string)` | `{}` | no |
|
||||||
|
|
||||||
## Outputs
|
## Outputs
|
||||||
|
|
||||||
| Name | Description |
|
| Name | Description |
|
||||||
| ------------ | ---------------------------------- |
|
|------|-------------|
|
||||||
| instance\_id | Id of the created instance. |
|
| instance_id | ID of the created bastion instance |
|
||||||
| public\_ip | Public ip of the created instance. |
|
| public_ip | Public IP address of the bastion instance |
|
||||||
|
|
||||||
## Automated checks
|
## Requirements
|
||||||
Currently the automated checks are limited. In CI the following checks are done for the root and each example.
|
|
||||||
- lint: `terraform validate` and `terraform fmt`
|
|
||||||
- basic init / get check: `terraform init -get -backend=false -input=false`
|
|
||||||
|
|
||||||
## Generation variable documentation
|
| Name | Version |
|
||||||
A markdown table for variables can be generated as follow. Generation requires awk and terraform-docs installed.
|
|------|---------|
|
||||||
|
| terraform | >= 0.13 |
|
||||||
|
| aws | Latest |
|
||||||
|
| template | Latest |
|
||||||
|
|
||||||
```
|
## Security Features
|
||||||
.ci/bin/terraform-docs.sh markdown .
|
|
||||||
|
### IMDSv2 Enforcement
|
||||||
|
|
||||||
|
The bastion host is configured with Instance Metadata Service Version 2 (IMDSv2) enforcement:
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
metadata_options {
|
||||||
|
http_endpoint = "enabled"
|
||||||
|
http_tokens = "required"
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Philips Forest
|
This provides enhanced security against SSRF attacks and unauthorized metadata access.
|
||||||
|
|
||||||
This module is part of the Philips Forest.
|
### Security Group Configuration
|
||||||
|
|
||||||
```
|
The module creates a dedicated security group with:
|
||||||
___ _
|
- SSH (port 22) ingress from specified CIDR blocks (configurable via `admin_cidr`)
|
||||||
/ __\__ _ __ ___ ___| |_
|
- All egress traffic allowed (for package updates and outbound connections)
|
||||||
/ _\/ _ \| '__/ _ \/ __| __|
|
|
||||||
/ / | (_) | | | __/\__ \ |_
|
|
||||||
\/ \___/|_| \___||___/\__|
|
|
||||||
|
|
||||||
Infrastructure
|
### Network Configuration
|
||||||
|
|
||||||
|
- Dedicated network interface for better security isolation
|
||||||
|
- Elastic IP for consistent public addressing
|
||||||
|
- Deployed in public subnet for external access
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
1. **Enable Flag**: By default, `enable_bastion` is set to `false`. You must explicitly set it to `true` to create the bastion host
|
||||||
|
2. **SSH Key**: Ensure the SSH key specified in `key_name` exists in the target region before applying
|
||||||
|
3. **Cost Consideration**: The bastion host and Elastic IP incur hourly charges even when idle
|
||||||
|
4. **AMI Selection**: If `amazon_optimized_amis` is not specified, the module automatically selects the latest Amazon Linux AMI
|
||||||
|
5. **Access Control**: Restrict `admin_cidr` to specific IP ranges for production use (avoid `0.0.0.0/0`)
|
||||||
|
6. **User Data**: Default user data template is included at `template/user_data.sh`
|
||||||
|
|
||||||
|
## Example Use Cases
|
||||||
|
|
||||||
|
### Development Environment
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
module "dev_bastion" {
|
||||||
|
source = "git@github.com:webuildyourcloud/terraform-aws-bastion.git"
|
||||||
|
enable_bastion = true
|
||||||
|
environment = "dev"
|
||||||
|
project = "myapp"
|
||||||
|
instance_type = "t3.micro"
|
||||||
|
admin_cidr = "0.0.0.0/0" # Open for development
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Talk to the forestkeepers in the `forest`-channel on Slack.
|
### Production Environment
|
||||||
|
|
||||||
[](https://philips-software-slackin.now.sh)
|
```hcl
|
||||||
# terraform-aws-bastion
|
module "prod_bastion" {
|
||||||
|
source = "git@github.com:webuildyourcloud/terraform-aws-bastion.git"
|
||||||
|
enable_bastion = true
|
||||||
|
environment = "prod"
|
||||||
|
project = "myapp"
|
||||||
|
instance_type = "t3.small"
|
||||||
|
ebs_optimized = true
|
||||||
|
admin_cidr = "203.0.113.0/24" # Restricted to office network
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Accessing the Bastion Host
|
||||||
|
|
||||||
|
After creation, connect to the bastion using:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -i /path/to/key.pem ec2-user@<bastion-public-ip>
|
||||||
|
```
|
||||||
|
|
||||||
|
To tunnel through the bastion to private resources:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -i /path/to/key.pem -A -L 8080:private-server:80 ec2-user@<bastion-public-ip>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. Use restrictive CIDR blocks for `admin_cidr` in production
|
||||||
|
2. Enable EBS optimization for better performance
|
||||||
|
3. Use Session Manager as an alternative to SSH when possible
|
||||||
|
4. Regularly update the bastion AMI for security patches
|
||||||
|
5. Implement CloudWatch logging for SSH sessions
|
||||||
|
6. Consider using AWS Systems Manager Session Manager instead of traditional SSH
|
||||||
|
7. Tag resources appropriately for cost allocation
|
||||||
|
8. Disable bastion when not in use to reduce costs
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Cannot connect to bastion
|
||||||
|
|
||||||
|
- Verify the security group allows your IP in `admin_cidr`
|
||||||
|
- Check the Elastic IP is properly associated
|
||||||
|
- Ensure the SSH key matches the one specified in `key_name`
|
||||||
|
- Verify the subnet has an Internet Gateway attached
|
||||||
|
|
||||||
|
### Instance not created
|
||||||
|
|
||||||
|
- Confirm `enable_bastion` is set to `true`
|
||||||
|
- Check AWS service quotas for EC2 instances
|
||||||
|
- Verify subnet and VPC exist
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
This module is provided as-is for use within your organization.
|
||||||
|
|||||||
0
examples/bastion/.terraform-version
Normal file → Executable file
0
examples/bastion/.terraform-version
Normal file → Executable file
0
examples/bastion/README.md
Normal file → Executable file
0
examples/bastion/README.md
Normal file → Executable file
0
examples/bastion/main.tf
Normal file → Executable file
0
examples/bastion/main.tf
Normal file → Executable file
0
examples/bastion/outputs.tf
Normal file → Executable file
0
examples/bastion/outputs.tf
Normal file → Executable file
0
examples/bastion/terraform.tfvars
Normal file → Executable file
0
examples/bastion/terraform.tfvars
Normal file → Executable file
0
examples/bastion/variables.tf
Normal file → Executable file
0
examples/bastion/variables.tf
Normal file → Executable file
0
examples/bastion/versions.tf
Normal file → Executable file
0
examples/bastion/versions.tf
Normal file → Executable file
7
main.tf
Normal file → Executable file
7
main.tf
Normal file → Executable file
@ -63,8 +63,9 @@ data "template_file" "user_data" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_eip" "this" {
|
resource "aws_eip" "this" {
|
||||||
|
count = var.enable_bastion ? 1 : 0
|
||||||
vpc = true
|
vpc = true
|
||||||
instance = aws_instance.instance.id[0]
|
instance = aws_instance.instance[0].id
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "aws_network_interface" "interface" {
|
resource "aws_network_interface" "interface" {
|
||||||
@ -99,14 +100,14 @@ resource "aws_instance" "instance" {
|
|||||||
user_data = var.user_data == "" ? data.template_file.user_data.rendered : var.user_data
|
user_data = var.user_data == "" ? data.template_file.user_data.rendered : var.user_data
|
||||||
|
|
||||||
network_interface {
|
network_interface {
|
||||||
network_interface_id = aws_network_interface.interface.id
|
network_interface_id = aws_network_interface.interface[0].id
|
||||||
device_index = 0
|
device_index = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata_options {
|
metadata_options {
|
||||||
http_endpoint = "enabled"
|
http_endpoint = "enabled"
|
||||||
http_tokens = "required"
|
http_tokens = "required"
|
||||||
}
|
|
||||||
|
|
||||||
tags = merge(
|
tags = merge(
|
||||||
{
|
{
|
||||||
|
|||||||
0
outputs.tf
Normal file → Executable file
0
outputs.tf
Normal file → Executable file
0
template/user_data.sh
Normal file → Executable file
0
template/user_data.sh
Normal file → Executable file
0
variables.tf
Normal file → Executable file
0
variables.tf
Normal file → Executable file
0
versions.tf
Normal file → Executable file
0
versions.tf
Normal file → Executable file
Loading…
x
Reference in New Issue
Block a user