메인 콘텐츠로 이동하기
  1. Posts/

Bastion Host란?

·705 자

서론 #

AWS에서는 하나의 VPC를 여러 서브넷으로 나눌 수 있으며, 각 서브넷에 EC2 인스턴스를 생성할 수 있습니다. 인터넷에 연결되거나 사용자에게 노출되어야 하는 FE/BE 서버는 public이어야 합니다. 그러나 데이터베이스와 같이 인터넷에 공개되어서는 안 되는 인스턴스도 있습니다. 즉, 이러한 인스턴스는 private이어야 합니다. 따라서 public 서브넷private 서브넷 두 가지 유형의 서브넷을 만듭니다.

private 서브넷에 있는 인스턴스에 직접 연결할 수 없기 때문에 private 인스턴스에 접근하기 위해서만 public 인스턴스를 만들어야 합니다. 이 인스턴스를 bastion host라고 합니다. bastion host를 사용하면 public에서 private 인스턴스를 보호할 수 있고 동시에 private 인스턴스의 연결에 대한 이력과 권한을 관리하기 쉽습니다.

이 섹션에서는 AWS에서 Terraform을 사용하여 인스턴스를 구성할 것입니다.

VPC와 서브넷 #

resource "aws_vpc" "default_vpc" {
  # cidr_block은 "10.0.0.0/16"일 필요는 없습니다
  cidr_block = "10.0.0.0/16"
  tags = {
    Name = "terraform_vpc"
  }
}

resource "aws_subnet" "public_subnet" {
  vpc_id     = aws_vpc.default_vpc.id
  cidr_block = "10.0.1.0/24"
  availability_zone = "ap-northeast-2a"
  tags = {
    Name = "terraform_subnet_a"
  }
}
resource "aws_subnet" "private_subnet" {
  vpc_id     = aws_vpc.default_vpc.id
  cidr_block = "10.0.3.0/24"
  availability_zone = "ap-northeast-2a"
  tags = {
    Name = "terraform_subnet_c"
  }
}

서브넷 자체는 public 서브넷과 사설 서브넷으로 구분될 수 없습니다. 서브넷이 public인지 private인지 결정하려면 라우팅 테이블보안 그룹을 정의해야 합니다.

라우팅 테이블 및 인터넷 게이트웨이 #

# VPC가 생성될 때 기본 라우트 테이블이 만들어집니다
resource "aws_route_table" "public_route_table" {
	vpc_id = aws_vpc.default_vpc.id
	tags = {
    Name = "public_route_table"
  }
}

# public 라우팅 테이블에는 인터넷 게이트웨이가 필요합니다
resource "aws_internet_gateway" "default_igw" {
	vpc_id = aws_vpc.default_vpc.id
	tags = {
    Name = "terraform_igw"
  }
}
resource "aws_route" "igw_association" {
	route_table_id = aws_route_table.public_route_table.id
	destination_cidr_block = "0.0.0.0/0"
	gateway_id = aws_internet_gateway.default_igw.id
}

# public 서브넷을 public 라우팅 테이블에 할당
resource "aws_route_table_association" "public_subnet_association" {
	subnet_id = aws_subnet.public_subnet.id
	route_table_id = aws_route_table.public_route_table.id
}

라우팅 테이블은 네트워크 인터페이스, 게이트웨이 로드 밸런서 엔드포인트 또는 기본 로컬 라우트 이외의 대상을 가진 기존 라우트를 포함합니다. 라우팅 테이블은 VPC의 범위 밖 CIDR 블록으로의 기존 라우트를 포함합니다.

한 서브넷은 하나의 라우팅 테이블에만 할당될 수 있습니다. 명시적으로 연결이 정의되지 않은 경우, 서브넷은 VPC 생성 시 만들어진 기본 라우트 테이블에 연결됩니다.

# private 라우팅 테이블도 생성
resource "aws_route_table" "private_route_table" {
	vpc_id = aws_vpc.default_vpc.id
	tags = {
    Name = "private_route_table"
  }
}
# private 서브넷을 private 라우팅 테이블에 할당
resource "aws_route_table_association" "public_subnet_association" {
	subnet_id = aws_subnet.private_subnet.id
	route_table_id = aws_route_table.private_route_table.id
}

Public 라우팅 테이블 #

public route table

10.0.0.0/16은 default_vpc의 CIDR 블록을 나타내며, 동일 VPC 내에서 로컬로 통신한다는 것을 의미합니다. 우리 VPC를 제외한 다른 대상은 default_igw를 통해 public 인터넷으로 나갑니다. 이는 인터넷 게이트웨이입니다.

Private 라우팅 테이블 #

private route table

public_route_table과 비교할 때, private_route_table은 동일 VPC에 위치한 로컬 인스턴스와만 통신할 수 있습니다.

Public/Private Instance #

# public/private 인스턴스를 위한 보안 그룹
resource "aws_security_group" "default_security_group" {
  name       = "default_security_group"
  vpc_id      = aws_vpc.default_vpc.id

  ingress {
    description = "Allow SSH traffic"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  ingress {
    description = "Allow HTTP traffic"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
  ingress {
    description = "Allow HTTPS traffic"
    from_port   = 443
    to_port     = 443
    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"]
  }
}

resource "aws_instance" "test_public_instance" {
  # Amazon Machine Image for ubuntu_22_04
  ami = "ami-058165de3b7202099"
  instance_type = "t2.micro"
  key_name = aws_key_pair.dev_key.key_name
  vpc_security_group_ids = [aws_security_group.default_security_group.id]
  # on public subnet
  subnet_id = aws_subnet.public_subnet.id
  associate_public_ip_address = true
  tags = {
    Name = "test_public_instance"
  }
}
resource "aws_instance" "test_private_instance" {
  # Amazon Machine Image for ubuntu_22_04
  ami = "ami-058165de3b7202099"
  instance_type = "t2.micro"
  key_name = aws_key_pair.dev_key.key_name
  vpc_security_group_ids = [aws_security_group.default_security_group.id]
  # on private subnet
  subnet_id = aws_subnet.private_subnet.id
  # 공용 IP를 할당하지 않도록 합니다
  associate_public_ip_address = false
  tags = {
    Name = "test_private_instance"
  }
}

# 자체 키 페어 생성
resource "aws_key_pair" "dev_key" {
  key_name   = "dev_key"
  # SSH 공개 키 파일 경로
  public_key = file("~/Desktop/server-key/dev_key.pub")
}

Public 인스턴스 #

public instance

Public 인스턴스의 경우, associate_public_ip_address를 true로 설정했기 때문에 공용 IPv4 주소가 있습니다. Terraform에서 aws_eip를 사용하여 인스턴스에 탄력적 IP를 부여할 수도 있습니다.

Private 인스턴스 #

private instance

Private 인스턴스의 경우, 공용 IPv4 주소를 통해 접근할 수 없습니다. 대신 private IPv4 주소를 사용해야 합니다. 그러나 private IP 주소(10.0.3.206)는 공용 인터넷에 위치할 수 없습니다. 따라서 먼저 public 인스턴스(bastion host)에 접근한 다음, 동일 VPC에 위치한 10.0.3.206을 찾을 수 있습니다.

Bastion host를 사용하여 private 인스턴스에 접근 #

이제 bastion host를 통해 private 인스턴스에 접근할 수 있습니다. 이제 매우 간단합니다. bastion host에 원격 SSH 접속을 한 다음, bastion host에서 private 호스트에 다시 SSH 접속을 합니다. 이렇게 하면 private 인스턴스에 접근할 수 있으며 보안이 더욱 강화됩니다.