👥

AWS Virtual Private GatewayでVPN環境を作ってみる

2023/08/30に公開

こんにちはへたれです。
株式会社Aidemyでエンジニアとして、Lab Bankという化学業界の研究室向けSaaSを開発、運用しています。

はじめに

アプリケーション開発時にセキュリティ上の観点から、特定の環境に対してはアクセス元IPアドレスを絞るという措置を取ることも多いです。
出社していれば社内のイントラネットからのアクセス、在宅であればVPNを使ってアクセスする、といった運用をすることが多いと思います。

ただこの方法ですと、以下のような悩みが出てきました。

  • 社内イントラやVPNにアクセスできる人物を案件毎に最低限に絞りたい
  • 在宅勤務をしている業務委託の方をVPNに招き入れるリスクを取りたくない

課題感はあるが頻繁な物理出社は避けたい...
そんなときにAWS Client VPNというVPC環境にVPNを貼るサービスを知り、「これで手軽に構築できるじゃん!」とやってみました。

システム構成

今回用意した環境はこちらです。
AWS環境上にVPN Client Endpointを設置し、NAT Gatewayを通してアクセスすることでグローバルIPを固定にします。
Active Directoryによるユーザベース認証を用いて、エンジニア個人に認証情報を付与します。
証跡ログをCloudwatch Logsに残します。

インフラ構築

せっかくなのでTerraformで構築してみましょう。
まずはネットワークから構築していきます。

tf:network.tf
# ---------------------------------------------
# Network Segment
# ---------------------------------------------

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
}

# Nat Gatewayを設置するsubnet
resource "aws_subnet" "public" {
  vpc_id            = aws_vpc.main.id
  availability_zone = "ap-northeast-1a"
  cidr_block        = cidrsubnet(aws_vpc.main.cidr_block, 8, 1)
}

# VPN Endpointを設置するsubnet
resource "aws_subnet" "private_a" {
  vpc_id            = aws_vpc.main.id
  availability_zone = "ap-northeast-1a"
  cidr_block        = cidrsubnet(aws_vpc.main.cidr_block, 8, 11)
}

# ---------------------------------------------
# Internet GW
# ---------------------------------------------

resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
}

resource "aws_route_table" "to_internet" {
  vpc_id = aws_vpc.main.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main.id
  }
}

# ---------------------------------------------
# NAT GW
# ---------------------------------------------

resource "aws_eip" "natgw" {
  vpc = true
}

resource "aws_nat_gateway" "main" {
  allocation_id = aws_eip.natgw.id
  subnet_id     = aws_subnet.public.id
}

resource "aws_route_table" "to_internet_through_natgw" {
  vpc_id = aws_vpc.main.id
  route {
    cidr_block     = "0.0.0.0/0"
    nat_gateway_id = aws_nat_gateway.main.id
  }
}

# ---------------------------------------------
# Route Table Asoociation
# ---------------------------------------------

resource "aws_route_table_association" "public_to_internet" {
  subnet_id      = aws_subnet.public.id
  route_table_id = aws_route_table.to_internet.id
}

resource "aws_route_table_association" "private_a_to_internet_through_natgw" {
  subnet_id      = aws_subnet.private_a.id
  route_table_id = aws_route_table.to_internet_through_natgw.id
}

Client VPNはActive Directory Serviceを使ってVPNの認証情報を管理することができます。

tf:ad.tf
# ---------------------------------------------
# Active Directory
# ---------------------------------------------

resource "aws_directory_service_directory" "main" {
  name     = "project1.vpn.aidemy.co.jp"
  password = var.ad_admin_password
  size     = "Small"

  vpc_settings {
    vpc_id     = aws_vpc.main.id
    subnet_ids = [aws_subnet.private_a.id]
  }

  lifecycle {
    ignore_changes = [password]
  }
}

Client VPN Endpointを作成する前に、使用する利用する証明書をACMに登録しておいてください。
Client VPN Endpointは3つの認証方法がありますが、今回はActive Directoryでのユーザベース認証を実施するため、サーバサイド証明書のみの登録でOKです。

最後にClient VPN Endpointを作成します。
client_cidr_blockではクライアントに割り当てるIPアドレスの範囲を指定します。
※ 指定可能な範囲についてはENIやNAT Gatewayのあるサブネットと被らず、/22以上のマスク長が必要となります。

今回はaws_ec2_client_vpn_routeリソースでクライアントからのアクセスは全てVPN経由にしていますが、プロジェクト等に応じて必要最低限だけ経由させるといいかもしれません。

vpn.tf
# ---------------------------------------------
# VPN Endpoint
# ---------------------------------------------

resource "aws_ec2_client_vpn_endpoint" "main" {
  server_certificate_arn = var.server_certificate_arn
  vpc_id                 = aws_vpc.main.id
  client_cidr_block      = "118.99.0.0/22"

  // 全ての通信をVPN経由にする
  split_tunnel          = false
  session_timeout_hours = 24
  vpn_port              = 443

  authentication_options {
    type                = "directory-service-authentication"
    active_directory_id = aws_directory_service_directory.main.id
  }

  connection_log_options {
    enabled              = true
    cloudwatch_log_group = aws_cloudwatch_log_group.vpn.name
  }

  security_group_ids = [
    aws_security_group.vpn.id,
  ]
}

# ---------------------------------------------
# Security Settings
# ---------------------------------------------

resource "aws_cloudwatch_log_group" "vpn" {
  name = "/aws/sample/client-vpn"
}

resource "aws_security_group" "vpn" {
  name   = "sample-vpn-endpoint"
  vpc_id = aws_vpc.main.id

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "all"
    cidr_blocks = ["0.0.0.0/0"]
  }

  ingress {
    from_port   = 0
    to_port     = 0
    protocol    = "all"
    cidr_blocks = [aws_subnet.private_a.cidr_block]
  }

}

# ---------------------------------------------
# Traffic Routing
# ---------------------------------------------

// ENIをprivate_aにアタッチ
resource "aws_ec2_client_vpn_network_association" "vpn_to_private_a" {
  client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.main.id
  subnet_id              = aws_subnet.private_a.id
}

// VPN Endpoint Clientからアウトバウンドのアクセス許可
resource "aws_ec2_client_vpn_authorization_rule" "allow_all_users" {
  client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.main.id
  target_network_cidr    = "0.0.0.0/0"
  authorize_all_groups   = true
}

// アウトバウンドアクセスをprivate_aにルーティング
resource "aws_ec2_client_vpn_route" "example" {
  client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.main.id
  destination_cidr_block = "0.0.0.0/0"
  target_vpc_subnet_id   = aws_ec2_client_vpn_network_association.vpn_to_private_a.subnet_id
}

VPNに接続

本来であればActive Directoryのインスタンスを立ち上げて、専用画面でユーザ登録をするところなのですが、メンテナンスコストを考えるとあまりその方法をとりたくありません。
こちらで紹介されている方法を使ってユーザを登録しましょう。

アクセスする端末にVPN Clientをダウンロードしておきましょう。

次にAWSコンソールを開き、Client VPN endpoint一覧からクライアント設定ファイルをダウンロードします。

中身を見るとこんな感じで、VPN Clientのドメインや証明書などが入っています。

sample-vpn.pvpn
client
dev tun
proto udp
remote cvpn-endpoint-XXXXXXX.prod.clientvpn.ap-northeast-1.amazonaws.com 443
remote-random-hostname
resolv-retry infinite
nobind
remote-cert-tls server
cipher AES-256-GCM
verb 3
<ca>
-----BEGIN CERTIFICATE-----
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
-----END CERTIFICATE-----

</ca>
auth-user-pass

reneg-sec 0

AWS VPN Clientを開き、ダウンロードしたクライアント設定ファイルを選択します。

発行したユーザ名とパスワードで認証・接続します。

またCloudwatch Logsにログを残しているので、いつ誰がアクセスしたのか後で調査することも可能です。

おわりに

AWS Client VPNを導入すること、より気軽にVPN環境を立てることができるようになりました。
業務委託の方にも固定IPを割り振ることができ、より適切な環境になったのではないかなと考えています。
ネットワーク通信料金が気になるところではありますが...動作環境等の必要なとき以外は切ってくださいとお願いしながら運用しており、今の所AWSの費用がすごく上がったということもないです。

Aidemyでは一緒にDXを推進していく仲間を大募集中です!
興味を持っていただけましたらぜひご連絡ください。

Aidemy Tech Blog

Discussion