Private Subnet에 있는 AWS RDS에 접근하기 1. Bastion + SSH with. DBeaver
개요
AWS RDS DB를 public subnet에 둘 수 있다면 RDS 엔드포인트만으로 접근할 수 있기 때문에 개발하는 입장에서는 행복해질 수 있다. 그러나 당연하게 AWS에서도 보안상으로 민감한 데이터를 갖고 있는 DB의 경우 public subnet에 두는것을 권장하지 않는다. (https://aws.amazon.com/ko/blogs/database/best-practices-for-creating-a-vpc-for-amazon-rds-for-db2/ Best practices for Amazon VPC design 챕터 참고) 그렇다면 DB를 private subnet으로 옮기게 되면 어떻게 될까? 이 경우 외부에서는 RDS 엔드포인트에 직접 접근할 수 없게 된다.
때문에 private VPC에 접근하기 위한 방법으로는 꽤 다양한 방법이 있다.
1. EC2 bastion Host + ssh 통신을 이용한 점프서버로 RDS와 연결하기 : 가장 일반적이고 이해하기 쉬움
2. SSM을 이용해 RDS와 연결하기 : 키 없이 접근, 보안성과 감사 기능 강화
3. 접근제어 어플리케이션을 이용해 RDS와 연결하기 : 예: Teleport, StrongDM 등
내가 앞으로 진행할 내용만 정리했지만, AWS privateLink라던가, sso를 이용하는 방법 등 정말로 방법이 많다.
1번 방식부터 정리해보자. 모든 인프라는 terraform으로 작성했다.
1. EC2 bastion Host + ssh 통신을 이용한 점프 서버로 RDS와 연결하기
위는 실제 구축한 구조도이다. 작업 순서를 정리하면 이렇게 된다.
- EC2를 Public Subnet에 띄워서 Bastion으로 사용
- Bastion EC2의 Security Group을 RDS에 접근할 수 있도록 정의
- SSh key 발급 후 SSH로 Bastion EC2 접속 확인
- DBeaver SSH 터널링으로 RDS에 연결
- localhost:[사전 정의한 port]로 RDS에 접속
EC2를 띄우기 전에 pem 키부터 생성하자. pem 키는 Terraform으로 발급받을 수 없다.
AWS 콘솔 → EC2 → 네트워크 및 보안 → 키페어 → [키 페어 생성] 클릭 → 필요한 내용 작성 후 키 페어 생성을 하면 pem 키 다운로드
bastion 방식을 끝까지 사용하겠다면, 절대 이 pem키를 분실하면 안된다...
2. Terraform 코드
VPC, public/private subnet, RDS까지는 이미 존재 한다고 가정한다. 그래도 생각보다 필요한게 많다.
security group
module "rds_sg" {
source = "terraform-aws-modules/security-group/aws"
name = "postgres"
description = "Security Group for RDS Postgres"
vpc_id = data.terraform_remote_state.vpc.outputs.vpc.vpc_id
ingress_with_source_security_group_id = [
{
description = "Allow from Bastion SG"
from_port = 5432
to_port = 5432
protocol = "tcp"
source_security_group_id = module.bastion_sg.security_group_id
}
]
egress_rules = ["all-all"]
}
module "bastion_sg" {
source = "terraform-aws-modules/security-group/aws"
name = "bastion"
description = "Security group for Bastion Host"
vpc_id = data.terraform_remote_state.vpc.outputs.vpc.vpc_id
ingress_rules = ["ssh-tcp"]
egress_rules = ["all-all"]
}
module "ssh_access_sg" {
source = "terraform-aws-modules/security-group/aws"
name = "ssh"
description = "Allow SSH access from Bastion"
vpc_id = data.terraform_remote_state.vpc.outputs.vpc.vpc_id
number_of_computed_ingress_with_source_security_group_id = 1
computed_ingress_with_source_security_group_id = [
{
rule = "ssh-tcp"
description = "Allow SSH from Bastion"
source_security_group_id = module.bastion_sg.security_group_id
}
]
}
bastion ec2
variable "ssh_key_name" {
type = string
description = "Name of the existing EC2 key pair to use for SSH"
default = "bastion-key"
}
data "aws_ami" "amazon_linux" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
resource "aws_instance" "bastion" {
ami = data.aws_ami.amazon_linux.id
instance_type = "t3.micro"
subnet_id = data.terraform_remote_state.vpc.outputs.vpc.public_subnets[0]
vpc_security_group_ids = [module.bastion_sg.security_group_id]
key_name = var.ssh_key_name
associate_public_ip_address = true
tags = {
Name = "bastion-host"
Environment = terraform.workspace
}
}
outputs.tf 를 작성해두면 terraform apply 후 ec2 public ip를 터미널에 찍어준다.
output "bastion_public_ip" {
value = aws_instance.polanotes_bastion[0].public_ip
}
Changes to Outputs:
- bastion_public_ip = "x.xx.xxx.xxx"
여기까지 모든 준비가 끝났고 ssh -i key.pem ec2-user@[ec2 public ip] 로 접속이 된다면 아래와 같이 나타난다.
Last login: Tue Apr 15 07:51:28 2025 from
, #_
~\_ ####_ Amazon Linux 2
~~ \_#####\
~~ \###| AL2 End of Life is 2026-06-30.
~~ \#/ ___
~~ V~' '->
~~~ / A newer version of Amazon Linux is available!
~~._. _/
_/ _/ Amazon Linux 2023, GA and supported until 2028-03-15.
_/m/' https://aws.amazon.com/linux/amazon-linux-2023/
3. DBeaver SSH 터널링으로 연결
ssh 터널링은 ssh 명령어로도 가능하다. 하지만 DBeaver에서도 해당기능을 제공해서 DBeaver에 있는 걸 썼다.
DBeaver에서 Create -> SSH 탭으로 이동해서 아래와 같이 작성한다. pem key의 위치는 기억해 둬야한다.
위와 같이 점프서버로 SSH 터널링을 뚫어주고
localhost로 접근하면 정상적으로 동작한다.
cmd 명령어로 해결하려면 다음과 같이 쓰고, DBeaver에서 localhost에 연결하면 된다.
ssh -i ~/.ssh/key.pem -L [localhost post]:[rds endpoint]:[rds port] ec2-user@[ec2 public ip]
4. EC2 bastion Host + ssh 의 문제점
가장 큰 문제는 pem 키를 어떻게 관리할 것인가?이다. pem키를 공유해야하는데, 그래서 개인이 관리하는 서버라면 상관없지만 팀 단위라면 가능할 것 같지 않다. 보안상으로도 문제가 있고...
그리고 별도의 설정을 하지 않으면, ssh 연결이 자주 끊긴다. 이건 ssh 기본 설정 때문이긴한데... /etc/ssh/sshd_config 파일의 ClientAliveInterval 옵션을 수정하면 된다고느 하는데, 굳이 서버의 글로벌 ssh 속성이기 때문에 이 설정을 건드리고 싶지 않았다.
또, 누가 언제 접속했는지 알 수 없다. 그리고 뭘했는지도 알 수 없다. 그래서 이 방식은 내 요구사항에 맞지 않는 방법이었다. 결국 다른 방식을 찾아가게 됐다.
마치며
이 방식의 가장 큰 장점은 간편함이다. 그리고 간편함을 얻고, 운영 서버에서의 모든 보안적인 문제를 잃었다.
생각보다 글이 길어져 시리즈물이 될 것 같다.
최종적으로는 누가 접근했는지 어떤 쿼리를 날렸는지 등이 기록해둬야 보안감사 같은게 진행될 때 관리자가 지적받을 일이 없다.
그래서 접근제어 어플리케이션까지 진행할 수밖에 없는 상황같다.
천천히 해보면 어떻게 할 수 있지 않을까...?