일단 VPN이란 무엇인지 간단히 알아보도록 하겠습니다.
VPN은 Virtual Private Network의 약자입니다. 모든 인터넷의 데이터는 마음만 먹으면 그 안의 데이터의 내용을 들여다볼 수 있습니다. 이러다 보면 보안 기능이 강화된 네트워크가 필요했고, 이런 니즈때문에 VPN을 사용합니다.
쉽게 말하면 VPN을 통해서 사용자와 은행 사이에 VPN터널을 뚫는 것입니다. 이 VPN은 통신 프로토콜을 통해서 강화되기 때문에 외부에서는 데이터 확인이 불가능합니다.
우선 VPN을 많이 사용하는 예시는 또한, 회사 사이의 VPN장비를 두어 하나의 네트워크를 사용하는 것 같은 효과를 거둘 수 있게 됩니다. 그리고 회사의 네트워크에 침입, 침투할 수 없게 됩니다.
그리고 일반 사용자가 DNS서버를 우회할 때도 VPN을 사용합니다. DNS에서 접속을 차단해 버리면 접속이 불가능해집니다. DNS서버에서 도박, 마약, 총기 관련 서버 홈페이지의 일반 사용자의 접속을 정책적으로 차단합니다.
DNS서버는 Domain Name System으로서 데이터베이스 시스템입니다. 호스트의 도메인 이름을 IP주소로 변환하거나 반대의 경우를 수행할 수 있도록 개발된 데이터베이스 시스템입니다. DNS는 범국제적 단위로 웹사이트 IP주소와 도메인 주소를 이어주는 환경/시스템입니다.
즉 VPN을 사용하면 외국에 있는 DNS서버를 통해서는 이런 우리나라 DNS서버에서 막는 홈페이지를 들어갈 수 있게 됩니다. 말했듯이 VPN은 특정 서버와 나의 1:1터널을 뚫어주기 때문에 매우 (개인)보안에 취약합니다.
그리고 기본 가정에 있는 공유기는 VPN기능을 내장하고 있습니다. 집에서 내 PC를 켜놓은 상태인데, 외부에서 내 PC에 들어가서 파일을 복사, 메일확인을 할 수 있게 됩니다. 그럼 말했듯이 외부에서 통신 데이터를 확인하거나 들여다 볼 수 없게 됩니다.
/network/versions.tf
terraform {
required_version = "~> 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.0"
}
}
}
/network/variables.tf
variable "config_file" {
description = "The path of configuration YAML file."
type = string
default = "./config.yaml"
}
/network/terraform.tf
terraform {
backend "remote" {
hostname = "app.terraform.io"
organization = "fastcampus-devops"
workspaces {
name = "terraform-lab-network"
}
}
}
###################################################
# Local Variables
###################################################
locals {
context = yamldecode(file(var.config_file)).context
config = yamldecode(templatefile(var.config_file, local.context))
}
###################################################
# Providers
###################################################
provider "aws" {
region = "ap-northeast-2"
}
/network/config.yaml
context:
region: "apne2"
vpc: "apne2-fastcampus"
cidrs:
"primary": "10.222.0.0/16"
vpc:
name: "${vpc}"
cidr: "${cidrs.primary}"
subnet_groups:
"public":
map_public_ip_on_launch: true
subnets:
- { cidr: "10.222.0.0/24", az_id: "${region}-az1" }
- { cidr: "10.222.1.0/24", az_id: "${region}-az2" }
"private":
subnets:
- { cidr: "10.222.2.0/24", az_id: "${region}-az1" }
- { cidr: "10.222.3.0/24", az_id: "${region}-az2" }
/network/main.tf
locals {
common_tags = {
Project = "Network"
Owner = "posquit0"
}
}
###################################################
# VPC
###################################################
module "vpc" {
source = "tedilabs/network/aws//modules/vpc"
version = "0.24.0"
name = local.config.vpc.name
cidr_block = local.config.vpc.cidr
internet_gateway_enabled = true
dns_hostnames_enabled = true
dns_support_enabled = true
tags = local.common_tags
}
###################################################
# Subnet Groups
###################################################
module "subnet_group" {
source = "tedilabs/network/aws//modules/subnet-group"
version = "0.24.0"
for_each = local.config.subnet_groups
name = "${module.vpc.name}-${each.key}"
vpc_id = module.vpc.id
map_public_ip_on_launch = try(each.value.map_public_ip_on_launch, false)
subnets = {
for idx, subnet in try(each.value.subnets, []) :
"${module.vpc.name}-${each.key}-${format("%03d", idx + 1)}/${regex("az[0-9]", subnet.az_id)}" => {
cidr_block = subnet.cidr
availability_zone_id = subnet.az_id
}
}
tags = local.common_tags
}
###################################################
# Route Tables
###################################################
module "route_table__public" {
source = "tedilabs/network/aws//modules/route-table"
version = "0.24.0"
name = "${module.vpc.name}-public"
vpc_id = module.vpc.id
subnets = module.subnet_group["public"].ids
ipv4_routes = [
{
cidr_block = "0.0.0.0/0"
gateway_id = module.vpc.internet_gateway_id
},
]
tags = local.common_tags
}
module "route_table__private" {
source = "tedilabs/network/aws//modules/route-table"
version = "0.24.0"
name = "${module.vpc.name}-private"
vpc_id = module.vpc.id
subnets = module.subnet_group["private"].ids
ipv4_routes = []
tags = local.common_tags
}
이렇게 간단히 워크스페이스 디렉토리를 config.yaml을 통해서 관리해서 main.tf에서 subnet그룹과, routetable을 만들었습니다.
그리고 terraform backend에 state를 지정해 주었습니다. 마지막으로 outputs를 보시겠습니다.
/network/outputs.tf
output "vpc" {
value = module.vpc
}
output "subnet_groups" {
value = module.subnet_group
}
그리고 이 의존성을 바탕으로 ec2-instance코드를 작성해 보겠습니다.
/ec2-instance/config.yaml
context: {}
remote_states:
"network":
organization: "fastcampus-devops"
workspace: "terraform-lab-network"
/ec2-instance/terraform.tf
terraform {
backend "remote" {
hostname = "app.terraform.io"
organization = "fastcampus-devops"
workspaces {
name = "terraform-lab-openvpn"
}
}
}
###################################################
# Local Variables
###################################################
locals {
context = yamldecode(file(var.config_file)).context
config = yamldecode(templatefile(var.config_file, local.context))
}
###################################################
# Providers
###################################################
provider "aws" {
region = "ap-northeast-2"
}
/ec2-instance/versions.tf
terraform {
required_version = "~> 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.0"
}
}
}
/ec2-instance/remote-state.tf
locals {
remote_states = {
"network" = data.terraform_remote_state.this["network"].outputs
}
vpc = local.remote_states["network"].vpc
subnet_groups = local.remote_states["network"].subnet_groups
}
###################################################
# Terraform Remote States (External Dependencies)
###################################################
data "terraform_remote_state" "this" {
for_each = local.config.remote_states
backend = "remote"
config = {
organization = each.value.organization
workspaces = {
name = each.value.workspace
}
}
}
이는 terraform_remote_state를 활용해서 network폴더에서 backend에 올린 state파일을 활용해서 local변수에 변수들을 network변수들을 주입해 주었습니다. (vpc, subnet_groups,...)
/ec2-instance/security-groups.tf
module "sg__ssh" {
source = "tedilabs/network/aws//modules/security-group"
version = "0.24.0"
name = "${local.vpc.name}-ssh"
description = "Security Group for SSH."
vpc_id = local.vpc.id
ingress_rules = [
{
id = "ssh"
protocol = "tcp"
from_port = 22
to_port = 22
cidr_blocks = ["0.0.0.0/0"]
description = "Allow SSH from anywhere."
},
]
egress_rules = []
tags = local.common_tags
}
module "sg__openvpn" {
source = "tedilabs/network/aws//modules/security-group"
version = "0.24.0"
name = "${local.vpc.name}-openvpn"
description = "Security Group for OpenVPN."
vpc_id = local.vpc.id
ingress_rules = [
{
id = "openvpn/all"
protocol = "udp"
from_port = 1194
to_port = 1194
cidr_blocks = ["0.0.0.0/0"]
description = "Allow OpenVPN from anywhere."
},
]
egress_rules = [
{
id = "all/all"
description = "Allow to communicate to the Internet."
protocol = "-1"
from_port = 0
to_port = 0
cidr_blocks = ["0.0.0.0/0"]
},
]
tags = local.common_tags
}
간단히 openvpn, ssh을 위한 sg를 설정해 주었습니다.
/ec2-instance/ec2.tf
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
owners = ["099720109477"] # Canonical
}
resource "aws_instance" "private" {
ami = data.aws_ami.ubuntu.image_id
instance_type = "t2.micro"
subnet_id = local.subnet_groups["private"].ids[0]
key_name = "fastcampus"
vpc_security_group_ids = [
module.sg__ssh.id,
]
tags = {
Name = "${local.vpc.name}-private"
}
}
locals {
openvpn_userdata = templatefile("${path.module}/files/openvpn-userdata.sh", {
vpc_cidr = local.vpc.cidr_block
public_ip = aws_eip.openvpn.public_ip
})
common_tags = {
"Project" = "openvpn"
}
}
resource "aws_instance" "openvpn" {
ami = data.aws_ami.ubuntu.image_id
instance_type = "t2.micro"
subnet_id = local.subnet_groups["public"].ids[0]
key_name = "fastcampus"
user_data = local.openvpn_userdata
associate_public_ip_address = false
vpc_security_group_ids = [
module.sg__ssh.id,
module.sg__openvpn.id,
]
tags = {
Name = "${local.vpc.name}-openvpn"
}
}
그리고 privatesubnet에 ec2인스턴스 하나, openvpn은 public subnet에 하나 올렸습니다.
그리고 openvpn은 ec2에 userdata를 주입해서 그 안에 docker container를 돌렸습니다.
/ec2-instance/files/openvpn-userdata.sh
#!/bin/bash
sudo apt-get update
sudo apt-get install -y \
apt-transport-https \
ca-certificates \
curl \
gnupg \
lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
usermod -aG docker ubuntu
## Run openvpn-ldap-otp container
docker run \
--name openvpn \
--volume openvpn-data:/etc/openvpn \
--detach=true \
-p 1194:1194/udp \
--cap-add=NET_ADMIN \
-e "OVPN_SERVER_CN=${public_ip}" \
-e "OVPN_ENABLE_COMPRESSION=false" \
-e "OVPN_NETWORK=172.22.16.0 255.255.240.0" \
-e "OVPN_ROUTES=172.22.16.0 255.255.240.0, ${split("/", vpc_cidr)[0]} ${cidrnetmask(vpc_cidr)}" \
-e "OVPN_NAT=true" \
-e "OVPN_DNS_SERVERS=${cidrhost(vpc_cidr, 2)}" \
-e "USE_CLIENT_CERTIFICATE=true" \
wheelybird/openvpn-ldap-otp:v1.4
## Wait to ready OpenVPN Server
until echo "$(docker exec openvpn show-client-config)" | grep -q "END PRIVATE KEY" ;
do
sleep 1
echo "working..."
done
## Generate OpenVPN client configuration file
docker exec openvpn show-client-config > fastcampus.ovpn
우분투에 docker를 설치하고, gpg키를 신뢰하도록 하고, 그리고 docker패키지를 설치합니다. 그리고 ubuntu유저에게 docker사용자 그룹에게 주었습니다. 그리고 openvpn-ldap-otp container를 띄웁니다. 이는 나중에 배울거긴 하지만, 1194 udp포트로 컨테이너를 실행합니다. 그리고 환경변수들을 주입해서 컨테이너를 실행합니다. 그리고 $를 보시면 템플릿파일의 context값을 다 참조하는 것을 보실 수 있습니다.
/ec2-instance/eip.tf
resource "aws_eip" "openvpn" {
tags = merge(
{
"Name" = "${local.vpc.name}-openvpn"
},
local.common_tags,
)
}
resource "aws_eip_association" "openvpn" {
instance_id = aws_instance.openvpn.id
allocation_id = aws_eip.openvpn.id
}
'DevOps > AWS Architecture' 카테고리의 다른 글
[ Docker && Kubernetes ] - 도커 이미지와 컨테이너 (0) | 2022.08.27 |
---|---|
[ Docker && Kubernetes ] - 개요 (0) | 2022.08.27 |
[ DevOps ] - (테라폼을 이용한 인프라 관리) 테라폼 워크스페이스 디렉토리 구성 전략 (0) | 2022.08.03 |
[ DevOps ] - (테라폼을 이용한 인프라 관리) 테라폼 Provisioner와 EC2 Userdata (0) | 2022.08.03 |
[ DevOps ] - (패커를 이용한 머신 이미지 관리) - 디버깅하는 방법 (Debugging) (0) | 2022.08.02 |