forward proxy를 이용하여 private network linode 외부 통신 허용 구성 (with terraform)

백준호·2024년 2월 26일

vpc private subnet에 위치하는 linode 인스턴스는 기본적으로 외부와 통신할 수 없다. 외부 통신을 위해 public subnet에 linode 인스턴스를 위치시키는 것은 외부에 인스턴스의 public ip를 노출하는 것이기 때문에 보안 상 취약할 수 있다.

linode 인스턴스를 private subnet에 위치시킨 채 외부 통신을 하기 위한 방법 중 하나는 트래픽을 forward proxy로 전달하여 외부 통신을 하는 것이다. 이를 terraform으로 구성하겠다.

아키텍처 다이어그램

  • VPC (Virtual Private Cloud) 내 두개의 subnet 대역이 존재한다.
  • 10.0.1.0/24를 public subnet 대역으로 정하고 내부에 forward proxy 서버를 배치한다.
  • 10.0.2.0/24를 private subnet 대역으로 정하고 내부에 외부 통신 접근이 제한된 두개의 linode 인스턴스를 배치한다.
  • forward proxy 서버는 1 to 1 nat를 이용하여 외부와 통신이 가능하고, private subnet에 위치한 두 인스턴스는 외부와의 통신을 forward proxy를 통해 수행한다.
  • 반대로 외부에서 private subnet에 위치한 인스턴스에 접근은 제한된다.

1 to 1 NAT?

static NAT(network address translation) 이라고도 부르며 외부 공인 IP와 사설IP를 1:1로 매핑하는 것을 이야기 한다.

사설 IP와 공인 IP가 1:1로 매칭이 되어 있기 때문에 내부에서 외부로 먼저 나가는 경우와 달리 외부에서 내부로 먼저 들어오는 경우도 가능하다.

Forward Proxy?

프록시는 “대리자”라는 영문 뜻처럼 클라이언트가 자신을 통해서 다른 네트워크 서비스에 간접적으로(대신) 접속할 수 있게 해주는 시스템을 의미한다.

포워드 프록시는 클라이언트 대신 특청 요청을 수행하고 응답을 받아 클라이언트에게 전달하는 역할을 수행하는 서버이다.

포워드 프록시가 외부 통신을 대신 해주기 때문에 방화벽을 설치하여 보안을 강화할 수 있고, 서버에 정보를 저장해두고 바로 클라이언트에게 전달하는 캐싱 역할을 수행할 수 있다.

terraform variables

variable "public_cidr_blocks" {
  description = "The public CIDR blocks for the VPC"
  type        = list(string)
  default     = ["10.0.1.0/24"]
}

variable "backend_cidr_blocks" {
  description = "The backend CIDR blocks for the VPC"
  type        = list(string)
  default     = ["10.0.2.0/24"]
}

variable "nat_ip" {
  description = "The NAT IP address in public subnet"
  type        = string
  default     = "10.0.1.2"
}

변수로 subnet ip 대역, forward proxy 서버가 사용할 private ip를 지정한다. private ip는 public_cidr_blocks 대역 내에 존재하는 유효한 ip를 할당해야 한다.

VPC, Subnet

resource "linode_vpc" "vpc" {
  label       = "vpc"
  region      = "jp-osa"
  description = "vpc"
}

resource "linode_vpc_subnet" "public_subnet" {
  count  = length(var.public_cidr_blocks)
  vpc_id = linode_vpc.vpc.id
  label  = "public-subnet-${count.index + 1}"
  ipv4   = var.public_cidr_blocks[count.index]
}

resource "linode_vpc_subnet" "backend_subnet" {
  count  = length(var.backend_cidr_blocks)
  vpc_id = linode_vpc.vpc.id
  label  = "backend-subnet-${count.index + 1}"
  ipv4   = var.backend_cidr_blocks[count.index]
}

위에서 정의한 변수를 기반으로 vpc, subnet을 구성한다.

Linode

resource "linode_instance" "nat" {
  label     = "nat"
  image     = "linode/ubuntu22.04"
  region    = "jp-osa"
  type      = "g6-nanode-1"
  root_pass = var.instance_password

  interface {
    purpose = "public"
  }

  interface {
    purpose   = "vpc"
    subnet_id = linode_vpc_subnet.public_subnet[0].id
    ipv4 {
      vpc     = var.nat_ip
      nat_1_1 = "any"
    }
    primary = true
  }

  stackscript_id = linode_stackscript.init_forward_proxy.id
}

resource "linode_instance" "backend" {
  count     = 2
  label     = "backend${count.index}"
  image     = "linode/ubuntu22.04"
  region    = "jp-osa"
  type      = "g6-nanode-1"
  root_pass = var.instance_password

  interface {
    purpose   = "vpc"
    subnet_id = linode_vpc_subnet.backend_subnet[0].id
  }

  stackscript_id = linode_stackscript.add_proxy.id
}

forward proxy 인스턴스와 private subnet에 위치시킬 두개의 linode 인스턴스를 정의한다.

Stack Script

가장 중요한 부분이다.

#!/bin/bash

sudo apt update -y && sudo apt install apache2 -y
sudo a2enmod proxy proxy_http proxy_connect
sudo cat <<EOF > /etc/apache2/sites-available/fwd-proxy.conf
Listen 8080
<VirtualHost *:8080>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html
    ProxyRequests On
    ProxyVia On
    <Proxy "*">
        Require ip ${subent}
    </Proxy>
</VirtualHost>
EOF
sudo chown root:root /etc/apache2/sites-available/fwd-proxy.conf
sudo chmod 0644 /etc/apache2/sites-available/fwd-proxy.conf
sudo a2ensite fwd-proxy
sudo systemctl restart apache2

먼저 forward proxy 서버 생성 시 실행할 스크립트이다. apache를 이용하여 forward proxy를 구현할 것이기에 apache와 proxy 관련 패키지를 설치한다.

다음으로 apache proxy config 파일을 생성하여 적용시킨다.

프록시 서버는 8080포트를 듣고 있으며, private subnet 대역에서 8080포트로 프록시 요청이 왔을 때 apache 프록시 서버가 대신 통신을 수행한다.

#!/bin/bash

echo "export http_proxy=${proxy}:8080" >> /etc/environment

private subnet에 배치된 linode 인스턴스에서 실행할 스크립트이다.

통신을 대신해줄 proxy 서버 ip와 port를 환경변수에 등록한다.

전체 코드

https://github.com/junho100/forward-proxy-linode

실습

terraform을 이용하여 인프라를 생성하고 private subnet에 배치된 linode 인스턴스에서 curl http://example.com 명령어를 이용하여 인터넷 통신을 확인한다.

다음으로 proxy 서버에 접속하여 tcpdump -i eth1 dst port 8080 명령어를 이용해 정상적으로 트래픽이 forward proxy를 통해 흘러가는지 확인한다.

우측 화면을 보면 네트워크 트래픽이 감지되는 것을 볼 수 있다.

forward proxy를 통한 외부 통신 직접 구성의 한계점

AWS NAT Gateway처럼 네트워크 트래픽 자체를 NAT로 흐르게 하는 방식이 아니라 직접 백엔드 서버의 프록시를 지정해주는 방식이기 때문에 추가적으로 백엔드 서버에 프록시 환경변수 설정을 해야하는 부분이 문제이다.

가용성을 위해 더 많은 서버, forward proxy 서버를 구성하게 되었을 때 부하 분산과 대상 프록시 설정이 복잡해질 수 있다.

관련 자료

[Linode] VPC #3 - NAT 게이트웨이 구성하기

Configure a Forward Proxy to Enable Internet Access within a VPC

🌐 Reverse Proxy / Forward Proxy 정의 & 차이 정리

profile
회고하는 개발자

0개의 댓글