Journey to Security/클라우드

[AWS] EC2에 Spring Boot 앱 배포하기

Cordilog 2026. 7. 6. 21:30

이전 실습에서는 EC2 인스턴스를 생성했다. 

이번에는 생성된 인스턴스의 상태를 확인하고, SSH 접속을 해서 Spring Boot 기반 IntraPortal 앱 배포까지 진행한다.

인스턴스가 running 상태로 전환된 시점부터 브라우저에서 로그인 화면이 뜨는 것까지 확인하는 것이 이번 실습의 목표다.

 

1. 인스턴스 생성 확인

EC2 인스턴스 생성 직후에는 콘솔과 CLI 양쪽에서 상태를 확인하는 것이 좋다.

콘솔에서는 UI 렌더링 지연으로 상태가 아직 반영되지 않은 경우가 있고, CLI에서는 쿼리 필터를 잘못 쓰면 결과가 빈 테이블로 나올 수 있다.

 

describe-instances에 태그 필터를 걸어 securedeploy-* 패턴에 해당하는 인스턴스만 조회한다.

--query 옵션은 JMESPath 표현식으로, API 응답 JSON에서 필요한 필드만 추출해 테이블로 정리한다.

여기서는 인스턴스 상태, 퍼블릭 IP, 인스턴스 유형 세 가지만 출력하도록 지정했다.

aws ec2 describe-instances \
  --filters "Name=tag:Name,Values=securedeploy-*" \
  --query "Reservations[].Instances[].{State:State.Name,PublicIP:PublicIpAddress,InstanceType:InstanceType}" \
  --output table \
  --region ap-northeast-2

Staterunning이고 PublicIP가 할당되어 있으므로 인스턴스가 정상 가동 중인 것을 확인할 수 있다.

 

퍼블릭 IP는 인스턴스를 중지했다가 다시 시작하면 변경된다.

고정 IP가 필요한 경우 Elastic IP를 할당해야 하지만, 프리 티어 실습 환경에서는 비용 절감을 위해 동적 IP를 그대로 사용하는 것이 일반적이다.

 

프라이빗 IP 10.0.1.200은 이전의 VPC 실습에서 구성한 퍼블릭 서브넷(10.0.1.0/24) 범위 안에 있으므로, VPC 네트워크 설정이 의도대로 적용된 것을 확인할 수 있다.

프라이빗 IP는 콘솔의 인스턴스 상세 페이지나 --query에 PrivateIpAddress 필드를 추가해서 확인할 수 있다.

 

2. SSH 접속

인스턴스가 running 상태이고 퍼블릭 IP가 할당되었으므로, 키페어 실습에서 준비한 .pem 파일로 SSH 접속을 진행한다.

SSH 접속의 전체 흐름을 먼저 정리하면 다음과 같다.

1️⃣ 접속 명령어

ssh -i "securedeploy-key.pem" ubuntu@43.203.254.182

 

-i 옵션은 인증에 사용할 개인키 파일을 지정한다.

Ubuntu AMI로 생성한 인스턴스의 기본 사용자는 ubuntu이다.

Amazon Linux라면 ec2-user, CentOS라면 centos가 기본 사용자이므로, AMI에 따라 사용자명이 달라진다는 점에 유의해야 한다.

 

2️⃣ 최초 접속 시 호스트 지문 확인

처음 접속하면 서버의 ED25519 호스트 지문(fingerprint)을 신뢰할 것인지 묻는 메시지가 나타난다.

이 지문은 접속 대상 서버의 신원을 확인하는 용도이다.

중간자 공격(MITM)이 없는 한 정상적인 절차이므로 yes를 입력한다.

한 번 승인하면 해당 지문이 ~/.ssh/known_hosts에 저장되어 이후에는 묻지 않는다.

3️⃣ 접속 성공 확인

프롬프트가 다음과 같이 바뀌면 접속에 성공한 것이다.

 

프롬프트의 ip-10-0-1-200은 인스턴스의 프라이빗 IP 10.0.1.200에서 파생된 호스트명이다.

AWS는 인스턴스 생성 시 프라이빗 IP를 기반으로 호스트명을 자동 설정한다.

 

3. IntraPortal 앱 배포

SSH 접속이 완료되었으므로, Git에서 소스를 내려받아 배포 스크립트를 실행한다.

IntraPortal은 Spring Boot 기반 웹 애플리케이션으로, setup.sh 스크립트 한 번으로 빌드부터 서비스 등록, OS 하드닝까지 일괄 처리된다.

전체 배포 구조를 다이어그램으로 정리하면 다음과 같다.

 

1️⃣ 소스 코드 클론 및 배포 스크립트 실행

git clone https://github.com/Cordilog/aws-pentest-lab-hardened.git
cd aws-pentest-lab-hardened
sudo bash setup.sh

 

2️⃣ 배포 결과 확인

스크립트 실행이 완료되면 다음 정보로 정상 동작을 확인한다.

페이지가 뜨면 EC2 인스턴스 생성 확인부터 앱 배포까지 전체 과정이 완료된 것이다.

  • 접속 URL: http://43.203.254.182:8080/login

  • 테스트 계정:
계정 ID / PW 권한
관리자 admin / admin123 전체 관리 권한
개발자 devuser / DevP@ss2024 개발자 권한
일반 사용자 intern / Welcome1! 일반 열람 권한

3️⃣ 적용된 보안 하드닝

setup.sh가 적용하는 하드닝은 앱 레벨과 OS 레벨 두 계층으로 나뉜다.

🔵 앱 하드닝

SQL Injection, Command Injection, CSRF 공격 차단 로직이 코드에 내장되어 있고, 비밀번호 저장에 BCrypt 해싱을 적용한다. 각 공격 유형에 대해 간략히 정리하면 다음과 같다.

  • SQL Injection(SQLi)은 사용자 입력에 SQL 구문을 삽입해 데이터베이스를 조작하는 공격이다. Prepared Statement로 입력값과 쿼리 구조를 분리하면 차단된다.
  • Command Injection(CMDi)은 사용자 입력에 OS 명령어를 삽입해 서버에서 임의 명령을 실행하는 공격이다. 입력값 검증과 화이트리스트 방식으로 차단한다.
  • CSRF(Cross-Site Request Forgery)는 인증된 사용자의 브라우저를 이용해 본인 의사와 무관한 요청을 서버에 보내는 공격이다. 요청마다 고유 토큰을 발급·검증하면 차단된다.
  • BCrypt는 비밀번호를 해시할 때 솔트(salt)와 반복 연산(cost factor)을 적용해, 해시 값이 유출되더라도 원문을 역산하기 어렵게 만드는 단방향 해시 알고리즘이다.

🔵 OS 하드닝

불필요한 cron 작업 제거, SUID 비트가 설정된 불필요한 바이너리 정리, 애플리케이션 전용 시스템 유저 intraportal 분리를 수행한다. 앱을 전용 유저로 실행하면 앱이 침해되더라도 루트 권한으로 확산되는 것을 방지할 수 있다.

 

🔵 Actuator 제한

Spring Boot Actuator 엔드포인트 중 healthinfo만 외부에 노출하고, 접근 시 ROLE_ACTUATOR 권한이 있는 인증된 사용자만 허용한다.

Actuator는 애플리케이션의 내부 상태(메모리 사용량, 환경 변수, 빈 목록 등)를 HTTP 엔드포인트로 노출하는 Spring Boot 기능인데, 무분별하게 열어두면 민감 정보가 유출될 수 있으므로 필요한 항목만 선별적으로 개방하는 것이 원칙이다.