Project/Cloud

DevSecOps CI/CD 파이프라인 프로젝트 - Bastion EC2 + Jenkins EC2 (4)

luckyd8 2026. 2. 2. 15:35

 

Bastion EC2, Jenkins EC2 구축 과정을 정리하였다. 

 

AWS 아키텍처 


EC2 인스턴스는 Bastion Host과 Jenkins, 총 2대를 생성한다. 

Jenkins는 AWS에서 별도의 관리형 서비스로 제공되지 않기 때문에 EC2 인스턴스를 통해 서버를 직접 생성한 뒤, Jenkins를 설치해 운영한다. 다만 Jenkins 서버(Jenkins가 설치된 EC2 인스턴스)는 프라이빗 서브넷에 배치되어 있어 외부 접속이 불가능하다. 

이때 사용되는 것이 Bastion Host 이다. 
Bastion Host는 퍼블릭 서브넷에 배치되어 외부에서 SSH로 접속 가능하며, 이를 통해 프라이빗 서브넷 내부의 Jenkins 서버에 안전하게 접근 가능하도록 한다. 

 

 

Bastion Host란?

Bastion Host는 외부에서 네부 네트워크로 들어올 수 있는 유일한 진입 관문이다. 
보안이 강화된 인프라와 외부 인터넷 사이에서 중개 서버 역할을 수행하며, 내부망으로 향하는 모든 인바운드 트래픽은 반드시 Bastion Host를 거쳐 들어온다.

 

여기서 헷갈리지 말아야 할 점은, Bastion Host는 AWS에서 별도로 제공하는 리소스가 아니라는 것이다.

퍼블릭 서브넷에 위치해 내부 리소스로의 접근을 중개하도록 역할을 부여받은 EC2 인스턴스 자체를 Bastion Host라고 칭한다.


외부 인터넷과 내부망 사이의 보안 경계선 역할로, 인바운드 트래픽의 접근 제어 및 모니터링을 담당한다. 또한 SSH, RDP 등 내부 자원에 대한 안전한 접속 환경을 제공한다. 

본 프로젝트에서는 Jenkins 서버로의 접근을 위한 진입점으로 사용되었으며, SSH 키 기반 인증을 통해 접속 가능하도록 설계했다. 
Bastion Host는 사전에 등록된 SSH 공개키를 기반으로 외부 접속자의 신원 검증을 수행한 후, 인증에 성공한 사용자만 내부 리소스에 접근 가능하도록 동작한다. 

 

더보기

🤔 프로젝트를 진행하면서 PL님께 피드백을 받았다.

"Bastion Host를 굳이 사용할 필요가 있을까?
Jenkins를 퍼블릭 서브넷에 두고 Security Group만 잘 설정해도 충분하지 않나요?"

당시 퍼블릭/프라이빗 분리 구조를 너무 당연한 기본 구조로 받아들여서, Jenkins를 퍼블릭에 둔다는 선택지는 아예 고려해보지 못했었다.
결론적으로 피드백을 들었음에도 아키텍처는 그대로 진행되었다. 팀원들과 상의한 결과 보안적인 면에서 더 확실히 하고자 결정했기 때문이다. 

1. AWS 공식 문서와 실무 예제에서는 Bastion을 외부 점근 지점(퍼블릭 서브넷)으 두고, 애플리케이션·DB 등의 주요 워크로드는 프라이빗 서브넷에 배치하는 사례가 많다는 점 (이를 AWS에서 제시하는 보편적인 보안 패턴으로 해석함)

2. Bastion host + security group의 이중 보안 구조가 보안성이 더 높을 것이라는 판단

와 같은 이유로 아키텍처는 그대로 유지되었다. 

 

Security Group이란?

보안그룹(Security Group)은 VPC 내 리소스에 대한 인바운드 및 바운드 트래픽을 제어하는 가상 방화벽 역할의 리소스이다. 
이때, 인바운드는 외부에서 내부 리소스로 유입되는 트래픽을,아웃바운드는 내부 리소스에서 외부로 나가는 트래픽을 의미한다.


보안그룹은 프로토콜 유형과 포트, 소스(IP 또는 보안 그룹) 등을 지정해 트래픽 허용 여부를 판단한다. 
본 프로젝트의 Bastion Host 인스턴스를 예로 들면,



이는 외부에서 Bastion Host로 향하는 SSH(22) 포트의 접속 시도를 허용한다는 의미이다. 

IP가 0.0.0.0/0이므로 모든 IP에서의 접속 시도를 허용한다. 

(다만, 실제 Bastion Host에 접속 가능 여부는 EC2 내부의 SSH 키 기반 인증 결과에 따라 결정된다.)

 

Bastion EC2 생성

EC2 인스턴스 이름을 설정한다. (Daegok-Bastion)
AMI는 AWS에서 기본으로 제공하는 Amazon Linux 2023 최신 버전을 사용하였으며, 인스턴스 아키텍처 역시 별도 변경 없이 기본 설정대로 유지했다. 

 

새 키 페어를 생성한 후, 해당 키 페어 이름을 작성한다. 

(해당 단계의 스크린샷은 누락되음) 키 페어 유형 : RSA, 프라이빗 키 파일 형식 : .pem 으로 설정 후 진행헀다. 
키 페어를 생성하면 프라이빗 키 파일(.pem)을 로컬에 다운로드하게 되며, 해당 키에 대응하는 공개키는 EC2 인스턴스 내부에 자동 등록된다. 이후 SSH 접속 시 해당 프라이빗 키를 사용하므로, 키 파일은 반드시 안전하게 보관해야 한다.

네트워크 설정은 다음과 같이 진행한다. 
VPC: Jenkins 인프라에서 사용 중인 VPC 선택(Daegok-VPC-Jenkins), 서브넷: 퍼블릭 서브넷, 퍼블릭 IP: 활성화

보안 그룹을 설정한다. (스크린샷 참고)
Bastion Host는 jenkins 접속을 위한 중개 서버이므로, 외부에서의 접근은 SSH(22) 포트만 허용하도록 설정한다. 
본 프로젝트에서는 고정 IP를 사용하는 한경이 아니었기 때문에, 소스 IP는 0.0.0.0/0으로 설정했다.

보안 그룹 차원에서는 모든 접속 시도를 허용하지만, 실제 접속 가능 여부는 SSH 키 기반 인증을 통과한 사용자로 제한되므로, Bastion Host의 역할은 여전히 유효하다고 판단했다.

 

Jenkins EC2 생성

EC2 인스턴스 이름 설정 (Daegok-Jenkins) 후 AMI와 인스턴스 아키텍처는 Bastion EC2와 동일하게 기본 설정대로 진행한다. 

 

Jenkins 용 새 키 페어를 생성하고, 키 페어 이름을 작성한다. 
키 페어 생성 옵션(RSA / .pem) 역시 Bastion EC2와 동일하게 설정하였다. 

네트워크 설정은 

VPC: Jenkins 인프라에서 사용 중인 VPC(Daegok-VPC-Jenkins), Subnet: 프라이빗 서브넷, 퍼블릭 IP는 비활성화 한다. 

보안 그룹을 설정한다. (스크린샷 참고)

Jenkins 인스턴스는 외부에서 직접 접근하지 않고 Bastion Host를 경유한 트래픽만 허용하므로, SSH(22) + 소스(Bastion SG) , TCP 8080 + 소스(Bastion SG) 을 설정한다. 


8080은 Bastion Host를 통해 SSH 포트 포워딩 방식으로 접근하기 위해 허용한 규칙이다.