Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions 2023-05-22-ssm-demo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Compiled files
*.tfstate
*.tfstate.backup
.terraform.lock.hcl

# Module-specific files
.terraform/

# Ignore override files as they can contain sensitive data
override.tf
override.tf.json
*_override.tf
*_override.tf.json

# Ignore CLI configuration files
.terraformrc
terraform.rc

94 changes: 94 additions & 0 deletions 2023-05-22-ssm-demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Using Border0 to access EC2 instances using SSM

1. First create an SSH service
```
border0 socket create -t ssh -n ec2ssm
```

2. connect to the service. Make sure the machine you're connecting from has the proper IAM permissions to access the EC2 instance. If needed set the correct AWS enviroment variables. For example, if you're using the AWS CLI, you can set the AWS_PROFILE and AWS_REGION variables.

```
AWS_PROFILE=dev AWS_REGION=us-west-2 border0 socket connect ec2ssm --aws_ec2_target i-0f918c9007b111f04
Welcome to Border0.com
ec2ssm - ssh://ec2ssm.border0.app

=======================================================
Logs
=======================================================
207.102.57.34:42860 [Mon, 22 May 2023 19:09:07 UTC] TCP 200 bytes_sent:4125 bytes_received:4944 session_time: 8.09
```

3. Troubleshoot
if needed test if you can connect from the EC2 console or better, from the cli you run `border0 connect` like this. This will make sure you have the proper IAM permissions to access the EC2 instance.

```
$ aws ssm start-session --target i-0f918c9007b111f04 --region us-west-2

Starting session with SessionId: andree@border0.com-03c4342ee6d82f9d0
$ cat /etc/hostname
ip-10-0-2-46
$
```

# Terraform AWS EC2 with SSM Access

This repository contains Terraform scripts for creating an AWS EC2 instance that resides within a private subnet and can be accessed via AWS Systems Manager (SSM).

## Architecture

The Terraform scripts will set up the following resources:

- A VPC with CIDR block `10.0.0.0/16`.
- A public subnet and a private subnet within the created VPC.
- An Internet Gateway attached to the VPC.
- A NAT Gateway deployed into the public subnet.
- Two Route Tables for public and private subnets.
- An IAM role with the `AmazonSSMManagedInstanceCore` policy attached.
- An EC2 instance running Ubuntu 22.04 within the private subnet.

The EC2 instance is placed within the private subnet, thus it can't be accessed directly via SSH. Instead, we use AWS Systems Manager (SSM) to manage and access this instance. This is a secure method that doesn't require SSH keys.

## Prerequisites

1. [Terraform](https://www.terraform.io/downloads.html) installed on your local machine.
2. An AWS account and your AWS credentials (Access Key ID and Secret Access Key) configured via the AWS CLI or environment variables.

## Usage

1. Clone the repository:

```bash
git clone https://github.com/yourusername/your-repository.git
cd your-repository
```

2. Initialize the Terraform working directory:

```bash
terraform init
```

3. Validate the scripts:

```bash
terraform validate
```

4. Review the execution plan:

```bash
terraform plan
```

5. Apply the changes:

```bash
terraform apply
```


If no region is specified, the default region (`us-east-1`) is used.

## Accessing the EC2 Instance

Since the EC2 instance resides in a private subnet, you need to use AWS SSM to connect to it. Go to the AWS SSM console, navigate to `Instances & Nodes > Session Manager` and start a new session to connect to the instance.
248 changes: 248 additions & 0 deletions 2023-05-22-ssm-demo/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
variable "region" {
description = "AWS region"
default = "us-east-1"
}

variable "instance_count" {
description = "Number of instances to create"
default = 1
}


provider "aws" {
region = var.region
}

resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "border0-demo"
}
}

resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
}

resource "aws_subnet" "public" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
map_public_ip_on_launch = true
tags = {
Name = "public_subnet"
}
}

resource "aws_subnet" "private" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.2.0/24"
tags = {
Name = "private_subnet"
}
}

resource "aws_eip" "nat" {
}

resource "aws_nat_gateway" "main" {
allocation_id = aws_eip.nat.id
subnet_id = aws_subnet.public.id
tags = {
Name = "border0-demo"
}
}

resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = {
Name = "public_route_table"
}
}

resource "aws_route_table_association" "public" {
subnet_id = aws_subnet.public.id
route_table_id = aws_route_table.public.id
}

resource "aws_route_table" "private" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main.id
}
tags = {
Name = "private_route_table"
}
}

resource "aws_route_table_association" "private" {
subnet_id = aws_subnet.private.id
route_table_id = aws_route_table.private.id
}

resource "aws_iam_role" "ssm_role" {
name = "ssm_role"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}

resource "aws_iam_instance_profile" "ssm_profile" {
name = "ssm_profile"
role = aws_iam_role.ssm_role.name
}

# Attached the AmazonSSMManagedInstanceCore policy to the role
resource "aws_iam_role_policy_attachment" "ssm_role_policy_attachment" {
role = aws_iam_role.ssm_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

# Ubuntu has the SSM agent pre-installed
data "aws_ami" "ubuntu-image" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-*-22.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477", ] # Canonical
}


# Now create the EC2 instance. Place it in the private subnet and attach the IAM role
resource "aws_instance" "main" {
count = var.instance_count
ami = data.aws_ami.ubuntu-image.id
instance_type = "t2.micro"
subnet_id = aws_subnet.private.id
iam_instance_profile = aws_iam_instance_profile.ssm_profile.name

tags = {
Name = "ssm-border0-demo-${count.index}",
border0 = "group=infra_team,type=ssh"

}
}

# Now we'll create the connector. It will be the ssm client
# and needs ssm client iam permissions

# Firstly, create a new IAM role for the second EC2 instance:

resource "aws_iam_role" "connector_role" {
name = "border0_connector_role"

assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
}
EOF
}

#Then, create an IAM policy for the required permissions:

resource "aws_iam_policy" "connector_policy" {
name = "border0_connector_policy"
description = "Policy for SSM client permissions"

policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssm:StartSession",
"ssm:TerminateSession",
"ec2:Describe*",
"iam:SimulatePrincipalPolicy"

],
"Resource": "*"
}
]
}
EOF
}

#Then, attach the policy to the role:

resource "aws_iam_role_policy_attachment" "connector_policy_attachment" {
role = aws_iam_role.connector_role.name
policy_arn = aws_iam_policy.connector_policy.arn
}

# Create an instance profile for the role:

resource "aws_iam_instance_profile" "connector_profile" {
name = "b0_connector_profile"
role = aws_iam_role.connector_role.name
}


resource "aws_instance" "ssm_client" {
ami = data.aws_ami.ubuntu-image.id
instance_type = "t2.micro"
subnet_id = aws_subnet.public.id
iam_instance_profile = aws_iam_instance_profile.connector_profile.name
associate_public_ip_address = true
vpc_security_group_ids = [aws_security_group.allow_ssh.id]
tags = {
Name = "ssm-border0-connector"
}
}

resource "aws_security_group" "allow_ssh" {
name = "allow_ssh"
description = "Allow SSH inbound traffic"
vpc_id = aws_vpc.main.id

ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}

egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}

tags = {
Name = "allow_ssh"
}
}