본문 바로가기
ASAC

[ASAC 06] AWS VPC와 서브넷 구성 / Bastion Tunneling, NAT

by suhsein 2024. 10. 8.
728x90
이 글은 ASAC 06기를 수강하며 강의 자료 참고 및 추가 자료 수집을 통해 작성된 글입니다.

VPC

VPC (Virtual Private Cloud) : 클라우드 인트라넷 (Intranet)  + 서브넷 구성

 

서브넷

서브넷은 VPC로 제공받은 큰 네트워크를 몇 개의 부분 네트워크로 자른 것이다. (Subnet)

  • Private Subnet
    •  Private Server Subnet
    •  Private Database Subnet
  • Public Subnet

 

주의

Private/Public 서브넷은 단지 이름표에 불과할 뿐,

실제로 Private/Public의 성질을 서브넷에 부여하려면, Route Table 설정이 필요하다.

 

가용성을 위한 서브넷 이중화

Multi AZ(Availability Zone) 설정

=> Private/Public 서브넷을 각각 2개씩 만든다.

한쪽 AZ에서 화재라던가 네트워크 이슈 발생 시 다른 한쪽의 AZ로 커버하기 위함이다.

Multi AZ는 떨어진 영역의 Region들로 선택해야 한다. 

IGW (Internet Gateway)

VPC 내부 서버가 외부와 통신할 수 있는 통로

IGW VPC 단위 외부 IP 할당. (외부 IP <-> 단일 내부 IP 주소 변환. 서브넷 단위가 아닌 VPC 단위이다.)

 

Public Subnet 성질 부여 : IGW + Route Table

IGW만 VPC에 붙인다고 Public Subnet 이 자동으로 외부와 통신할 수 없다. 다음과 같은 과정을 거쳐야 한다.

1. IGW를 VPC에 부착

2. Route Table 설정

 

Route Table

Route table은 일종의 표지판이다. 라우트 테이블은 본래 라우터 안에 존재하며, 라우터는 내외부 네트워크 간 연결에 쓰인다. 

 

Route Table 설정 방법

1. Destination : 요청이 향하는 방향(내부 or 외부)에 따라

2. Target : 어디로 향해야 하는지 설정

 

ex) Destination, 서브넷 내 서버가 0.0.0.0/0 에 요청 전달 → Target, “IGW” 로 가시오

 

 

 

Route Table과 서브넷의 연결 방식

1. 명시적 연결

직접 생성한 Route Table을 명시적으로 특정한 서브넷에 할당

2. 비명시적 연결

디폴트 Route Table - 를 자동(비명시적)으로 서브넷에 할당 

 

그렇기 때문에 Route Table을 지정해주지 않으면 Default Route Table에 따라 라우팅 되게 된다. 

 

인바운드 / 아웃바운드

  1. Outbound (아웃바운드) : 내부 -> 외부. 외부 라이브러리를 다운로드 할 때
    • NAT Gateway / Instance
  2. Inbound(인바운드) : 외부 -> 내부. 외부에서 내부 서버에 접속해서 상태를 볼 때  
    • XShell Tunneling. -> Bastion. Bastion은 XShell 명령을 보낼 수 있는 통로이다.

매우 중요

인바운드와 아웃바운드 규칙 설정은 해당 Security Group 혹은 NACL에 대한 접근을 허용하는 것이다. 그렇지만 그저 허용하는 것일 뿐 실제로 경로 설정(라우팅)을 하지 않는다면 적용되지는 않는다.

 

  • 인바운드/아웃바운드 규칙 설정 = 대문 열기
  • 라우팅 = 경로 안내하기

VPC와 Subnet 중 어느 범위로 규칙 설정을 해야 하는가? 

인바운드 아웃바운드 규칙을 서브넷 단위로도 설정할 수 있지만, 모든 VPC에 대해서 허용하고 세부적인 적용을 라우트 테이블을 통해 설정하는 것이 조금 더 효율적일 것 같다는 의견을 얻을 수 있었다.

 

NAT Gateway / Instance

서브넷 단위의 외부 IP 할당. (서브넷 (다수 내부 IP들)을 외부 IP로 주소 변환)

하나의 외부 IP 뒤에 다수 내부 IP들이 숨어있는 것을 IP Masquerading이라고 한다. 

 

NAT(Network Address Translation) = 주소 변환기

VPC 내부 서브넷 내 모든 서버가 외부와 단방향(아웃바운드)으로 접근 가능하도록 열어주는 Gateway

 

Private Subnet에 Public IP 부여 : NAT + Route Table

  • NAT gateway : AWS 제공. 확장성이 있다. 늘렸다 줄였다가 자동으로
  • NAT instance : EC2 서버 하나를 사서 사용. 확장성 없음.

Bastion

Bastion은 외부 네트워크에서 내부 네트워크로 접근할 수 있는 유일한 통로이다. 

이는 SSH Tunneling이라고 불리며, Private 서브넷 내 위치한 서버에 인바운드 접근을 위해 외부 서버 Bastion을 통해 단방향(인바운드) 연결이 된다.

 

ex) Private 서브넷에 외부 개발자 컴퓨터에서 SSH를 통해 접근이 가능해야 할 때

 

주의 : 위 그림에서 볼 수 있듯이 NAT Instance와 Bastion은 public 서브넷에 위치시킨다. 외부와 연결된 통로가 필요하기 때문이다.

 

방화벽

  1. 인스턴스 단위 방화벽 : SG (security group)
    • security group은 stateful 하다.
  2. 서브넷 단위 방화벽 : NACL (Network ACL)
    • NACL은 stateless 하다.
  • stateful : inbound 규칙만 추가하면 됨. outbound는 기본적으로 모든 경로에 대해서 열려있으므로, 따로 설정이 필요 없다. 
  • stateless : inbound와 outbound 규칙 둘 다 필요. security group과 헷갈려서 inbound로 들어온 IP가 나갈 수가 없는 문제점. (stateless 하기 때문에.)

 

PEM 파일

AWS PEM 파일은 Public Key가 아니라 Private Key이다.

SSH 클라이언트는 Remote에 대한 접근을 위해 Key Pair 상에 Private Key 파일이 필요하다.

OpenSSL로 RSA Key Pair 생성 시 Public Key(.pub)와 Private Key(.pem)를 파일로 생성해준다.

AWS 콘솔을 통해 PEM 키를 생성하면 Public 키를 감추고, Private Key만 다운로드 받도록 해준다.

  • AWS가 노출하지 않고 감춘 Public Key(.pub)는 EC2 인스턴스 생성 시 주입 설정
  • 다운로드 받은 Private Key(.pem)는 위 Public Key 가진 EC2에 접속 시 클라이언트에 설정

 

현업 서브넷 세팅

Private Server Subnet과 Private Database Subnet으로 나눈다.
Database는 보안 때문에 세밀한 서브넷 설정이 필요하다.
NACL이 세밀할 수록, Security Group은 할 일이 적어짐. 
NACL이 잘 설정되어 있다면, SG 설정을 안 해도 되지만... 그래도 항상 이중화 하는 것이 권장된다. 혹시 모를 실수 방지

 

실습 1. Public Subnet 만들고, EC2에 연결하여 접속하기

1. VPC 생성하기

 

먼저 AWS 콘솔의 VPC - Your VPCs 에서 VPC를 생성한다. IPv4 CIDR는 원하는 주소와 서브넷 마스크로 설정한다. (AWS에서 추천해주는 값을 사용해도 무방하다.)

 

2. Subnet 생성하기

이제 VPC를 서브넷들로 쪼개기 위해서 VPC - Subnets (좌측) 을 선택한 후, Create subnet 버튼을 눌러서 서브넷을 만든다.


public subnet 2개와, private subnet 2개를 만들 것이므로, 총 4개 서브넷을 만들기 위해서 2비트를 사용할 것이다.

(2의 2제곱 = 4)

그래서 subnet CIDR block의 서브넷 마스크는 18이 된다.

베이스 IP와 서브넷 마스크를 설정해두면 우측 화살표를 통해서 자동으로 서브넷 블록을 지정할 수 있다.

위 설정대로 총 4개 서브넷들을 생성하였다.

 

3. EC2 인스턴스 생성하기 

AWS 콘솔의 EC2 메뉴에 들어가서 Launch instances 버튼을 클릭하여 인스턴스를 만든다.

 

 


먼저 Linux - t2.micro 선택 (다음 네트워크 세팅에서 선택된 Subnet의 region이 ap-northeast-2a 여야 t2.micro 선택 가능)

Network Settings에서 인스턴스에 public subnet을 연결한다. 그리고 public IP 자동 할당을 활성화 해준다.(필수!)

 

혹은 서브넷 설정에서 다음과 같이 설정하여 해당 서브넷에 대해 public IP를 자동 할당하도록 할 수 있다.

 

서브넷 public IP 자동 할당

물론 EC2 인스턴스 생성 시 설정해주어도 무방하다.

인스턴스에 대한 Security Group은 자동 설정되어 있는 대로 사용한다. 

기본적으로 Inbound 규칙으로 22번 포트에 대한 모든 곳(0.0.0.0/0)으로부터의 접근을 허용한다. 

로컬의 public IP가 아닌 모든 곳으로부터의 접근을 허용하는 이유는, 로컬에서 EC2 인스턴스에 접속하는 것이 아닌 AWS 콘솔에서 EC2 인스턴스에 접속하기 위함이다. 

 

왜 Inbound 규칙이 Anywhere이어야 하는지

따라서 해당 EC2 인스턴스는 어디에서는 SSH 접속이 가능하다.

 

 

public 서브넷이므로, key pair는 생성하지 않고, EC2 인스턴스를 생성을 완료한다.

4. connect 시도 (실패)

 

이제 만들어둔 EC2 인스턴스에서 connect 버튼을 클릭하여 연결을 시도한다.

하지만 연결이 실패하였다. 

그 이유는 다음과 같다.

 

1. VPC가 외부 네트워크와 연결되기 위한 통로인 인터넷 게이트웨이가 설정되지 않았다.

2. 0.0.0.0/0(everywhere)로 들어오는 요청들이 인터넷 게이트웨이로 향하도록 라우팅을 하지 않았다.

 

5. 인터넷 게이트웨이 생성 및 연결

먼저 인터넷 게이트웨이를 생성한다.

Actions - Attach to VPC를 클릭하여 VPC에 인터넷 게이트웨이를 부착한다.

아까 만들어준 VPC에 인터넷 게이트웨이를 부착했다.

 

6. 라우트 테이블 만들기

 

서브넷마다 라우트 테이블 설정이 필요하다. 

Create route table을 클릭하여 라우트 테이블을 생성한다.

public-route-table 이라는 이름을 가진 라우트 테이블을 생성하였다.

Actions - Edit subnet associations 를 클릭하여 라우트 테이블을 적용할 서브넷을 선택한다.

모든 public subnet에 대해서 해당 라우트 테이블을 적용하기 위해 위와 같이 선택하고 저장해주었다.

그리고 여기서 끝이 아니라, 라우팅을 해주어야 한다. 

해당 서브넷들에 대해서 Destination과 Target을 설정해주는 것이다.

 

0.0.0.0/0으로 향하는 모든 요청들을 Internet Gateway로 향하게 하여 VPC 내부 서버가 외부와 통신할 수 있도록 한다.

이때 Internet Gateway는 EC2 인스턴스에 부착한 Internet Gateway로 선택한다.

 

7. connect 시도 (성공)

 

이제 다시 EC2 인스턴스의 connect를 시도해보면, 정상적으로 연결이 된 것을 확인할 수있다.

ping google.com 명령어를 입력해보면 정상적으로 동작한다.

 

실습 2. Bastion Tunneling

 

Bastion을 통한 터널링 접속

 

Private 인스턴스에 접속할 수 있는 Inbound는 오직 Bastion을 통해서만

그래서 필요한 인스턴스는 총 2개이다.

 

1. public subnet에 속하는 Bastion 인스턴스 생성

2. private subnet에 속하는 Private 인스턴스 생

1. local-to-bastion Key Pair 생성

 

먼저 EC2 Key Pairs 탭에서 Key Pair를 생성한다.

 

 

로컬 SSH에서 Bastion 인스턴스에 접속하는 데 사용하기 위해 .pem 형식으로 local-to-bastion 키페어를 생성한다. 

 

2. Bastion EC2 인스턴스 생성

 

EC2에서 Bastion 인스턴스를 생성한다.

먼저 Linux - t2.micro 선택 (다음 네트워크 세팅에서 선택된 Subnet의 region이 ap-northeast-2a 여야 t2.micro 선택 가능)

Network Settings에서 인스턴스에 public subnet을 연결한다. 그리고 public IP 자동 할당을 활성화 해준다.(필수!)

 

아까 생성한 local-to-bastion 키페어를 선택하고, Network Settings에서 실습1에서 만들었던 public subnet을 선택한 후, public IP 자동 할당을 활성화 한다. 

 

보안 그룹은 기본으로 두게 되면, SSH 22번 포트에 대해서 어디에서든 접근할 수 있도록 인바운드 규칙이 설정된다.

(Local의 Public IP에서만 접근할 수 있도록 인바운드 규칙을 설정하지 않는 이유는, AWS 콘솔에서 인스턴스에 접속하기 위함이다.)

 

3. connect 시도 (성공)

 

실습 1에서 이미 VPC에 Internet Gateway를 부착해두었고, public subnet에 대한 Route Table 설정을 완료해뒀기 때문에 성공적으로 연결이 되는 것을 확인할 수 있다.

 

 

실습 2-2. Private Subnet 인스턴스 만들기

1. local-to-private Key Pair 생성

 

이번에는 local에서 private subnet 인스턴스에 접속하기 위한 key pair를 만들어야 한다. 

Create Key Pair 버튼을 눌러 키페어를 생성한다.

아까와 동일하게 pem 키를 생성하면 자동으로 다운로드 된다.

 

2. Private Subnet EC2 인스턴스 생성

EC2에서 Bastion 인스턴스를 생성한다.

먼저 Linux - t2.micro 선택 (다음 네트워크 세팅에서 선택된 Subnet의 region이 ap-northeast-2a 여야 t2.micro 선택 가능)

Network Settings에서 인스턴스에 private subnet을 연결한다. 그리고 public IP 자동 할당을 활성화하지 않는다. 

Bastion을 통해서 private IP로만 접근할 수 있도록 할 것이기 때문에, public IP 자동 할당이 필요없다.

그리고 이번에는 새로운 보안 그룹을 생성하지 않고, Bastion의 보안 그룹을 그대로 적용할 것이다.

 

3. connect 시도 (실패)

EC2에서 private-instance의 private IP 주소를 복사한다.

 

그리고 bastion 인스턴스에 connect 하여 bastion 인스턴스 내에서 private 인스턴스 접속을 시도한다.

ssh ec2-user@{private IP address}

그러나 pem 키가 없기 때문에 연결이 거부되었다.

 

4. Bastion 인스턴스 내에서 .pem 파일 생성하기

접속된 Bastion 인스턴스에서 아까 생성해둔 local-to-private.pem 파일을 복사하여 bastion-to-private.pem 파일을 만들 것이다. 

 

먼저 다운로드 받은 local-to-private.pem 파일을 메모장으로 열어서 전체 키를 복사하였다.

그리고 Bastion 인스턴스 내에서 vi를 통해서 bastion-to-private.pem 파일을 생성하고 붙여넣기 하였다.

 

생성된 bastion-to-private.pem에 권한을 주어야 한다. 

chmod 400 bastion-to-private.pem

 

5. connect 시도 (성공)

위 명령어를 통해서 소유자에 대해 해당 파일의 읽기 권한을 부여했다.

이제 .pem 파일을 사용하여 다시 접속을 시도한다.

 

ssh ec2-user@{private IP address} -i {pem 파일}

 

 

private subnet instance에 정상적으로 connect 된 것을 확인할 수 있다.

 

SSH 관련 옵션 정리

  • i : RSA 인증을 위한 비밀키 (.pem) 지정
  • p : 해당 포트로 SSH 을 접속하겠다
  • f : 백그라운드로 실행 하겠다. 즉, 명령 수행 후 exit 하지 않고 계속 실행한채 냅두겠다.
  • N : 원격 명령어 수행 불가 (단순 포워딩의 목적을 위해) + f 대신에 사용
    • Do not execute a remote command. This is useful for just forwarding ports (protocol version 2 only).
  • L : 터널링을 위해 로컬호스트의 포트로 포워딩하겠다는 뜻
    • 예) ssh -L 4444:google.com:80 → http://localhost:4444 접속하면 구글이 보임
  • v : 상세 모드(verbose)를 켜서 디버깅용 로그를 표시하겠다.

 

6. Bastion Tunneling 시도

마지막으로 터널링을 해보자

 

 

git bash에 접속하여 다음 명령어를 입력하면, bastion으로부터 local로 가는 터널이 열린다. 

"local-to-bastion.pem" 에는 local-to-bastion.pem이 존재하는 경로를 입력한다. 그리고 private instance의 private ip와, bastion instance의 public ip를 각각 채워주면 된다.

 

ssh -i "local-to-bastion.pem" -N -L 33322:{target-private-ip}:22 ec2-user@{bastion-host-public-ip}

 

위 명령어에서, 33322:{target-private-ip}:22 의 의미는, bastion에서 33322번 포트로 접근하여 명령어를 보내면, 터널링을 통해 private의 22번 포트로 명령어가 전달된다는 의미이다. 

 

이제 git bash를 하나 더 켜서 다음을 입력하여 33322번 포트로 명령어를 보내자. 

 

그러나 여기서 명령어를 입력하더라도 외부로 요청하는 명령어는 작동되지 않는다. 외부에서 private-local 인스턴스로 접근하기 위해 public-bastion을 사용했으나, private-local에서 외부로 나가는 명령은 bastion이 아닌 NAT를 통해서 가능하기 때문이다. 따라서 NAT 인스턴스를 만들어서 외부 요청을 할 수 있도록 해야 한다.

 

실습 3. NAT

1. 인스턴스 생성

NAT는 ip 변조를 한다. 변조하는 프로그램이 설치된 OS 이미지를 선택해야 한다. EC2에서 amzn-ami-vpc-nat-2018.03.0.20230807.0-x86_64-ebs 를 검색한다.

그리고 커뮤니티 AMI에서 위 AMI를 선택한다.

키 페어는 local-to-bastion으로 선택하고, 네트워크 설정에서 public subnet 선택 후 퍼블릭 IP 자동 할당을 반드시 활성화 한다.

NAT는 본래 private instance가 외부로부터 라이브러리 등을 요청하여 다운로드 받을 때 사용되므로 이때 사용되는 프로토콜인 HTTP, HTTPS에 대한 모든 인바운드를 허용한다. 

실습에서는 라이브러리를 다운로드 하는 대신 ping 명령어를 보낼 것이므로, ping이 사용하는 프로토콜인 ICMP에 대한모든 인바운드를 허용한다.  

마지막으로, 인스턴스에서 우클릭 후 네트워킹 - 소스 / 대상 확인 변경에 들어간다.

아래 소스/대상 확인에서 중지를 체크해야 정상적으로 동작한다.

 

2. Ping 명령어 시도 (성공)

Bastion Tunneling을 통해서 접속 후, ping 명령어를 날린 결과 위와 같이 잘 동작함을 확인할 수 있다.

 

 

 

 

 

AWS 프리티어는 Free가 아니므로 실습이 끝나고 필요없는 인스턴스들은 바로바로 Terminate 하여 과금을 방지하자.

 

728x90