AWS RDS PostgreSQL 연결하기: Bastion 서버와 SSH 터널링 활용법

Ma_Seokjae·2025년 4월 7일
post-thumbnail

"로컬 개발 환경에서 AWS Private Subnet에 배치된 RDS(PostgreSQL)와 안전하게 통신하는 방법"


📌 개요

개발 환경에서 RDS는 보안상 Public Access가 불가능한 Private Subnet에 위치합니다. 하지만 로컬에서 DB를 직접 연결해 테스트할 일이 종종 또는 많이 발생합니다.

이 글은 Bastion 서버와 SSH 터널링을 활용해 로컬 ↔ RDS(PostgreSQL) 연결을 성공적으로 구성한 경험을 바탕으로 작성되었습니다.


🧭 전체 구조

🖥 Local PC
   |
   | SSH Tunnel (-L 5433:RDS:5432)
   ↓
🔗 Bastion EC2 (Public Subnet)
   ↓
🗄️ RDS PostgreSQL (Private Subnet)

✅ 왜 SSH Tunnel이 필요한가?

RDS는 VPC 내부 Private Subnet에 있어서 외부에서 직접 접근이 차단됩니다.

Bastion은 Public Subnet에 있으면서 같은 VPC에 연결된 서버이기 때문에, 외부 → Bastion → RDS 경로가 열리는 유일한 방법입니다.

로컬에서 바로 접근하려면 SSH Tunnel로 Bastion을 경유해 RDS까지 안전하게 중계하는 구조가 필요합니다.

🔒 RDS는 외부에서 직접 접근할 수 없다

  • 기본적으로 publicly_accessible = false
  • Private Subnet에 존재 → 외부 인터넷에서 직접 접근 불가

🧱 Bastion 서버란?

  • Public Subnet에 위치한 EC2
  • 외부에서 SSH로 접속 가능
  • 같은 VPC 내부의 RDS와 통신 가능

즉, Bastion이 RDS로 가는 중계 서버 역할을 합니다.

🔁 SSH Tunnel이란?

ssh -i dev-bastion-key.pem \
    -L 5433:<RDS-ENDPOINT>:5432 \
    ec2-user@<BASTION-IP>
  • localhost:5433 → Bastion → RDS 5432 포트로 연결됨

🛠 사전 준비

🧱 Bastion EC2 구성

  • Public Subnet에 위치
  • Internet Gateway + Public IP + 라우팅 테이블 설정 필수
  • 보안 그룹: TCP 22 포트 (SSH) 허용

🧩 RDS 보안 그룹

  • TCP 5432 포트 열려 있어야 함
  • 허용 대상: VPC CIDR (예: 10.10.0.0/16) or Bastion SG
ingress {
  description     = "PostgreSQL access from Bastion SG"
  from_port       = 5432
  to_port         = 5432
  protocol        = "tcp"
  security_groups = [aws_security_group.bastion_sg.id] # Bastion 서버에서만 허용
}

💻 로컬 환경 준비

  • .pem 키 파일
  • PostgreSQL 클라이언트 설치
brew install postgresql
psql --version

사전 준비 요약

  • Bastion EC2: 퍼블릭 서브넷 + IGW + 퍼블릭 IP + SSH 22 포트 허용
  • RDS SG: PostgreSQL 5432 포트를 Bastion SG에서만 허용
  • 로컬: .pem 키 파일 보유, PostgreSQL 클라이언트 설치

🚀 SSH 터널링 연결 & 테스트

1. SSH 터널링 명령어 설명

ssh -i <YOUR_PEM_KEY>.pem \
  -L <LOCAL_PORT>:<RDS_ENDPOINT>:5432 \
  ec2-user@<BASTION_PUBLIC_IP>
인자설명
-i <YOUR_PEM_KEY>.pemBastion EC2 접속용 .pem 키 파일
-LSSH 로컬 포트 포워딩 옵션
<LOCAL_PORT>로컬에서 사용할 포트 (ex: 5433)
<RDS_ENDPOINT>AWS RDS PostgreSQL 엔드포인트 주소
5432PostgreSQL 기본 포트
<BASTION_PUBLIC_IP>Bastion 서버의 퍼블릭 IP 주소

✅ 위 명령어를 실행하면 SSH 연결이 수립되며, localhost:<LOCAL_PORT>를 통해 마치 RDS에 직접 접속하듯 사용할 수 있습니다.


2. 로컬에서 PostgreSQL 연결 테스트

SSH 터널링을 활성화한 터미널은 계속 켜둔 상태에서, 다른 터미널이나 도구에서 아래처럼 접속해야 합니다.

CLI (psql)

psql -h localhost -p <LOCAL_PORT> -U <DB_USERNAME> -d <DB_NAME>

예시:

psql -h localhost -p 5433 -U myuser -d mydatabase

GUI 도구 (DBeaver / IntelliJ / pgAdmin 등)

항목
Hostlocalhost
Port5433 (또는 위에서 지정한 로컬 포트)
Username<DB_USERNAME>
Password<DB_PASSWORD> (Terraform에서 설정한 값 등)
Database<DB_NAME>

🧨 내가 마주한 문제 & 해결 방법

1. ssh: connect to host <IP> port 22: Operation timed out

💥 원인

  • Bastion EC2가 퍼블릭 서브넷에 있어도 Internet Gateway가 연결되지 않음
  • 또는 라우팅 테이블이 IGW로 향하지 않음

✅ 해결

  • Terraform에 IGW 및 퍼블릭 라우팅 테이블 설정 추가
  • 퍼블릭 서브넷에 해당 라우팅 테이블 연결
  • .tf 예시:
resource "aws_internet_gateway" "this" {
  vpc_id = aws_vpc.main.id
  tags = {
    Name = "${var.project_name}-${var.env}-igw"
  }
}

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id

  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.this.id
  }

  tags = {
    Name = "${var.project_name}-${var.env}-public-rtb"
  }
}

resource "aws_route_table_association" "public_subnet_a" {
  subnet_id      = var.public_subnet_a_id
  route_table_id = aws_route_table.public.id
}

2. psql: command not found

💥 원인

  • 로컬(Mac OS)에 PostgreSQL 클라이언트가 설치되어 있지 않음
  • psql 명령어는 터널링된 localhost:5433을 통해 RDS에 접속하기 위해 로컬에서 실행

✅ 해결 (Mac 기준)

  • Homebrew로 PostgreSQL 설치
brew install postgresql
  • 설치 확인
psql --version

3. psql: connection to server at "localhost", port 5433 failed: Connection refused

💥 원인

  • 로컬에서 SSH 터널링을 통해 접속할 포트를 연결하지 않음
  • ssh -L 명령어 실행이 안 됐거나 채널이 종료됨

✅ 해결

  • 아래 명령어로 터널링 활성화 (로컬 터미널에서):
ssh -i dev-bastion-key.pem \
  -L 5433:dev-rds.xxxxxxxxxxxx.ap-northeast-2.rds.amazonaws.com:5432 \
  ec2-user@<bastion-public-ip>
  • 연결 성공 후, 다른 터미널에서 접속:
psql -h localhost -p 5433 -U <username> -d <db_name>

4. server closed the connection unexpectedly

💥 원인

  • SSH 터널이 끊어졌거나
  • RDS 보안 그룹 인바운드 규칙이 잘못되어 Bastion 서버에서의 접속이 차단됨

✅ 해결

  • RDS 보안 그룹 인바운드 규칙을 수정하여, 오직 Bastion 서버에서의 접근만 허용
  • Terraform 코드 예시:
ingress {
  description     = "PostgreSQL access from Bastion SG"
  from_port       = 5432
  to_port         = 5432
  protocol        = "tcp"
  security_groups = [aws_security_group.bastion_sg.id] # Bastion 서버에서만 허용
}

🔐 이렇게 하면 불필요한 VPC 전체 CIDR 허용 없이, Bastion 인스턴스를 통해서만 RDS에 접근할 수 있어 보안적으로 훨씬 안전합니다.


🪟 Windows 환경에서 SSH 터널링으로 AWS RDS(PostgreSQL) 접속하기

Windows 사용자의 경우 macOS/Linux처럼 .pem 키를 바로 사용하는 것이 제한적일 수 있습니다.

따라서 아래 두 가지 방법 중 편한 방법을 선택하시면 좋을 거 같습니다 :)


✅ 방법 1: Git Bash (또는 WSL, Cygwin)

Git Bash가 설치되어 있으면 Linux 명령어를 그대로 사용할 수 있어 간편합니다.

🔧 1. Git Bash 설치

🪪 2. .pem 키 파일 준비

  • AWS EC2 생성 시 받은 bastion-key.pem 파일을 안전한 경로에 저장
  • 예: C:/Users/yourname/.ssh/bastion-key.pem

🛡 3. 키 권한 설정

Git Bash에서 아래 명령어 실행:

chmod 600 /c/Users/yourname/.ssh/bastion-key.pem

Windows에선 권한을 직접 조절할 수 없기 때문에 이 단계는 생략해도 동작하지만 보안 경고를 줄이기 위해 수행 추천

🔌 4. SSH 터널 연결

ssh -i /c/Users/yourname/.ssh/bastion-key.pem \
  -L 5433:<RDS-ENDPOINT>:5432 \
  ec2-user@<BASTION_PUBLIC_IP>

연결에 성공하면 Bastion 서버 셸이 뜹니다. 이 상태에서 터널이 유지되고 있으므로, 다른 터미널을 열어서 다음 DB 연결 명령어를 실행하시면 됩니다 :)

🧪 5. PostgreSQL 연결 (로컬 터미널 or DB 툴에서)

psql -h localhost -p 5433 -U <DB_USERNAME> -d <DB_NAME>

[성공 시 화면]


✅ 방법 2: PuTTY 사용

GUI 기반으로 설정하고 접속할 수 있는 가장 일반적인 방법입니다.

🛠 1. .pem.ppk 변환

  1. PuTTYgen 실행 (PuTTY 설치 시 포함됨)
  2. "Load" 클릭 → .pem 파일 선택 (파일 형식 변경 필요)
  3. "Save private key" 클릭 → .ppk 파일로 저장

🌐 2. PuTTY로 세션 설정

  1. Host Name: ec2-user@<BASTION_PUBLIC_IP>
  2. Port: 22
  3. 왼쪽 메뉴에서 Connection > SSH > Auth
    • Private key file for authentication: 변환된 .ppk 파일 선택
  4. 왼쪽 메뉴에서 Connection > SSH > Tunnels
    • Source port: 5433
    • Destination: <RDS-ENDPOINT>:5432
    • Type: Local 선택 → [Add] 클릭
  5. 세션 이름 저장 후 [Open] 클릭

접속에 성공하면 PuTTY 터미널에 Bastion 서버 셸이 뜨고, 터널도 열려있습니다.

🧪 3. DB 툴에서 연결 테스트

항목
Hostlocalhost
Port5433
Username<DB_USERNAME>
Password<DB_PASSWORD>
Database<DB_NAME>

‼️ 꼭, 인지해야 하는 사항

🚨 .pem.ppk 키 파일은 절대 깃허브나 공유 폴더에 올리면 안 됩니다.

가능한 경우, RDS 접근은 Bastion Security Group을 통해서만 허용하세요.

EC2 대신 [AWS Session Manager]로 Bastion 없이 구성하는 것도 장기적으론 고려해볼 수 있습니다.


📘 마무리 정리

항목설명
BastionPublic Subnet에 위치한 중계 EC2
RDSPrivate Subnet, public 접근 불가
SSH Tunnel로컬 ↔ RDS 간 보안 통신 연결
Key 포인트IGW, 보안 그룹, 퍼블릭 라우팅, 로컬 포트 포워딩
profile
Why not change the code?

0개의 댓글