AWS上にEC2でDHCPサーバを構築する

ある方がDHCPサーバをAWS上で構築する、ということを耳にして「あれ？それって大丈夫なの？」と思ったので調べてみました。

# DHCPサーバをAWS上で構築するということに何故私は反応したのか
Azureは明確に「DHCPサーバ立てないでね。」と言っていました。
しかし現在この制限は削除されています。
https://learn.microsoft.com/ja-jp/azure/virtual-network/virtual-networks-faq#can-i-deploy-a-dhcp-server-in-a-virtual-network

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/c06f4e47-c0d0-4605-bb5d-5cc67c9efdc7.png)

この公式サイトの<font color="red">赤色下線部分</font>の通り、<font color="red">**以前はDHCPサーバの構築はご法度でした。**</font>
この記憶があったので冒頭の「DHCPサーバをAWS上で構築する」に「Azureだと明示的にDHCPサーバの構築はNGだけど、AWSは大丈夫なのかな？」という疑問が湧きました。

# AWS上ではどうなのか？
公式サイトによると
https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/subnet-sizing.html
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/a8a0c448-825a-4ac1-aba5-302235dad74e.png)
<font color="red">赤色下線部分</font>の通り「ブロードキャストはサポートしない。」と言っています。

# そもそもAWS上に構築したいDHCPサーバの用途って何なの？
確認したところ、**オンプレミスに構築している各拠点のDHCPサーバを1台に集約しているサーバの再構築を今回はAWS上で行いたい**ということでした。
ということは当然**DHCPリレー機能をオンプレミス側のルーターなりUTMなりのネットワーク機器で利用する**んですよね。

# DHCPリレーについて
私もあんまり良くわかってなかったので調べてみたのですが、DHCPはブロードキャストを使いますがDHCPリレーはリレーされたらブロードキャスト通信からユニキャスト通信に切り替わります。
そりゃそうですよね。
だってDHCPクライアントのブロードキャストを受け取るネットワーク機器にリレー先が/32で書いてあるので、/32宛のユニキャスト通信になりますよね。

# AWS上でDHCPサーバ(オンプレミスからのDHCPリレーを受け取るサーバ)を構築して良いのか？
AWS上でブロードキャストはサポートされませんが、ユニキャストは大丈夫ですね。
ということはDHCPリレーで指定される(言い換えればDHCPリレー先としての)DHCPサーバは構築しても良い、ということですね。

# 実際にやってみた
はい。
恒例ですね。
やってみましょう。

## 構成図
![構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/b9d8c972-63ab-432f-aa94-111b7fe7cd1e.png)
今回の構成図はこんな感じです。
UbuntuがDHCPサーバで、FortiGateリレーエージェントです。

## Terraformのコード
いちいちGUIで構築するのは面倒なんで、今回も久しぶりにTerraformでやってみました。
今回は少しだけこだわってみて、DHCPサーバ機能のインストールとDHCPサーバ設定も全部Terraform内で完結させました。

### variables.tf
```variables.tf
# ---------------------------
# Variables - 変数設定
# ---------------------------

# region
# ap-northeast-1 東京リージョン
# ap-south-1 ムンバイリージョン
variable "region" {
  default = "ap-south-1"
}

# 環境種別(本番:prd,ステージング:stg,開発:dev)
variable "env_type" {
  default = "dev"
}

# システム名
variable "sys_name" {
  default = "dhcp-relay01"
}

# availability_zone
variable "availability_zone" {
  type = object({
    a = string
    b = string #ムンバイリージョンで利用
    c = string
  })
  default = {
    a = "ap-south-1a" # ムンバイ(インド)のアベイラビリティゾーン
    b = "ap-south-1b" # ムンバイ(インド)のアベイラビリティゾーン
    c = "ap-south-1c" # ムンバイ(インド)のアベイラビリティゾーン
  }
}

# ------------------------------
# VPC 関連
# ------------------------------

# vpc address
variable "vpc_address_pre" {
  default = "10.0."
}

variable "vpc_address_suf" {
  default = "0.0/23"
}

# private subnet suffix address01
variable "private_sn_address_suf01" {
  default = "1.0/24"
}

# public subnet suffix address01
variable "public_sn_address_suf01" {
  default = "0.0/24"
}

# VPC Endpoint
variable "vpc_endpoints" {
  type    = list(any)
  default = ["ssm", "ssmmessages", "ec2messages"]
}

# ------------------------------
# VPN (On-Premises Network) 関連
# ------------------------------

# FortiGate-60F Global IP Address
variable "FG60F_gip_address" {
  default = "126.28.192.130"
}

# FortiGate-60F Local IP Address01
variable "FG60F_local_address01" {
  type = string
  default = "172.16.3.0/24"
}

# ------------------------------
# EC2 作成
# ------------------------------

# Linux EC2のインスタンスタイプ
variable "ec2_instance_type_lin" {
    default = "t2.micro"
}

# Windows EC2のインスタンスタイプ
variable "ec2_instance_type_win" {
    default = "m5.large"
}
```
今回もムンバイです。
FortiGateとVPN張るので、FortiGateのLAN側IPアドレス、```172.16.3.0/24```が入っています。

### main.tf
```main.tf
# ---------------------------
# main
# ---------------------------

terraform {
  required_version = ">= 1.4" # Terraformのバージョンを1.4以上に指定
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "= 5.22.0" # AWSプロバイダのバージョンを5.22.0に固定
    }
  }
}

# プロバイダー設定
provider "aws" {
  region = var.region
}
```
いつも通り何の変哲もないmain.tfです。

### vpc.tf
```vpc.tf
# ---------------------------
# VPC 構築
# ---------------------------

# VPC
resource "aws_vpc" "vpc" {
  cidr_block           = "${var.vpc_address_pre}${var.vpc_address_suf}"
  enable_dns_hostnames = true
  enable_dns_support   = true
  tags = {
    Name = "${var.env_type}-${var.sys_name}-vpc"
  }
}

# ---------------------------
# Public Subnet 構築
# ---------------------------

resource "aws_subnet" "sn_public_1a" {
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = "${var.vpc_address_pre}${var.public_sn_address_suf01}"
  availability_zone = var.availability_zone.a
  tags = {
    Name = "${var.env_type}-${var.sys_name}-sn-public-1a"
  }
}

# ---------------------------
# Private Subnet 構築
# ---------------------------

resource "aws_subnet" "sn_private_1b" {
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = "${var.vpc_address_pre}${var.private_sn_address_suf01}"
  availability_zone = var.availability_zone.b
  tags = {
    Name = "${var.env_type}-${var.sys_name}-sn-private-1b"
  }
}

# ---------------------------
# Internet Gateway 作成
# ---------------------------
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.vpc.id
  tags = {
    Name = "${var.env_type}-${var.sys_name}-igw"
  }
}

# ---------------------------
# Route table 作成
# ---------------------------

# Public Subnet 用Route Table 作成
resource "aws_route_table" "rt_sn_public_1a" {
  vpc_id = aws_vpc.vpc.id
  # Default GatewayをInternet Gatewayに向ける
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw.id
  }
  tags = {
    Name = "${var.env_type}-${var.sys_name}-rt-sn-public-1a"
  }
}

# Private Subnet 用Route Table 作成
resource "aws_route_table" "rt_sn_private_1b" {
  vpc_id = aws_vpc.vpc.id
  # Default GatewayをInternet Gatewayに向ける
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_nat_gateway.nat_gateway_1a.id
  }
  # VPN先のルートテーブルを追加する
  route {
    cidr_block = "${var.FG60F_local_address01}"
    gateway_id = aws_vpn_gateway.vgw.id
  }
  tags = {
    Name = "${var.env_type}-${var.sys_name}-rt-sn-private-1b"
  }
}

# Public Subnet とDefault Route table の関連付け
resource "aws_route_table_association" "associate_rt_sn_public_1a_sn_public_1a" {
  subnet_id      = aws_subnet.sn_public_1a.id
  route_table_id = aws_route_table.rt_sn_public_1a.id
}

# Private Subnet とDefault Route table の関連付け
resource "aws_route_table_association" "associate_rt_sn_private_1b_sn_private_1b" {
  subnet_id      = aws_subnet.sn_private_1b.id
  route_table_id = aws_route_table.rt_sn_private_1b.id
}
```
今回もVPCのネットワークアドレスは```10.0.0.0/23```です。

### vpc_endpoint.tf
```vpc_endpoint.tf
# ---------------------------
# VPC Endpoint
# ---------------------------

# SSM用 VPC endpointの作成
resource "aws_vpc_endpoint" "interface" {
  for_each            = toset(var.vpc_endpoints)
  vpc_id              = aws_vpc.vpc.id
  service_name        = "com.amazonaws.${var.region}.${each.value}"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = [aws_subnet.sn_public_1a.id]
  private_dns_enabled = true
  security_group_ids  = [aws_security_group.sg_ssm.id]
  tags                = { "Name" = "${var.env_type}-${var.sys_name}-vpc-endpoint-${each.value}" }
}
```
今回もSSM使うのでSSM用のVPC エンドポイントを作成しています。

### iam.tf
```iam.tf
# ---------------------------
# IAM
# ---------------------------

# IAM ROLE 定義
resource "aws_iam_role" "ec2_role" {
  name               = "${var.env_type}-${var.sys_name}-iam-role"
  assume_role_policy = data.aws_iam_policy_document.ec2_assume_role.json
}

# IAMポリシー定義
data "aws_iam_policy_document" "ec2_assume_role" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["ec2.amazonaws.com"]
    }
  }
}

# IAM ROLEのインスタンスプロフィールの作成
resource "aws_iam_instance_profile" "instance_prof" {
  name = "${var.env_type}-${var.sys_name}-instance-profile-ssm"
  role = aws_iam_role.ec2_role.name
}

# ---------------------------
# FOR SSM
# ---------------------------
# SSM用ポリシーをEC2ロールに設定
resource "aws_iam_role_policy_attachment" "ssm_control" {
  role       = aws_iam_role.ec2_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
```
SSM使うのに必要なIAM設定です。
### security_group.tf
```security_group.tf
# ---------------------------
# Security Group
# ---------------------------

# ---------------------------
# EC2用 Security Group作成
# ---------------------------

#Linux EC2 用SG
resource "aws_security_group" "sg_ec2_linux01" {
  name   = "${var.env_type}-${var.sys_name}-sg-ec2-linux01"
  vpc_id = aws_vpc.vpc.id
  tags   = {
    Name = "${var.env_type}-${var.sys_name}-sg-ec2-linux01"
  }

  # インバウンドルール
  # from SSM
  ingress {
    description     = "AllowHttpsInBoundFromVPC for SSM"
    from_port       = 443
    to_port         = 443
    protocol        = "tcp"
    cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
  }

  ingress {
    description     = "AllowICMPv4InBoundFromVPC"
    from_port       = -1
    to_port         = -1
    protocol        = "icmp"
    cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
  }

  ingress {
    description     = "AllowInBoundFromOn-Premises-General-Subnet"
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    cidr_blocks = ["${var.FG60F_local_address01}"]
  }

  # アウトバウンドルール
  egress {
    description = "AllowAnyOutBound"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# Windows Server 用SG
resource "aws_security_group" "sg_ec2_windows01" {
  name   = "${var.env_type}-${var.sys_name}-sg-windows01"
  vpc_id = aws_vpc.vpc.id
  tags   = {
    Name = "${var.env_type}-${var.sys_name}-sg-windows01"
  }

  # インバウンドルール
  # from VPC ICMPv4
  ingress {
    description     = "AllowICMPv4InBoundFromVPC"
    from_port       = -1
    to_port         = -1
    protocol        = "icmp"
    cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
  }

  # For RDPアクセス
  ingress {
    description     = "AllowInBoundFromOn-Premises-General-Subnet"
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    cidr_blocks = ["${var.FG60F_local_address01}"]
  }

  # アウトバウンドルール
  egress {
    description = "AllowAnyOutBound"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# ---------------------------
# SSM用 Vpc endpoint Security Group作成
# ---------------------------

resource "aws_security_group" "sg_ssm" {
  name   = "${var.env_type}-${var.sys_name}-sg-ssm"
  vpc_id = aws_vpc.vpc.id
  tags   = {
    Name = "${var.env_type}-${var.sys_name}-sg-ssm"
  }

  # インバウンドルール
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
  }

  # アウトバウンドルール
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_security_group" "sg_ssmmessages" {
  name   = "${var.env_type}-${var.sys_name}-sg-ssmmessages"
  vpc_id = aws_vpc.vpc.id
  tags   = {
    Name = "${var.env_type}-${var.sys_name}-sg-ssmmessages"
  }

  # インバウンドルール
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
  }

  # アウトバウンドルール
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_security_group" "sg_ec2messages" {
  name   = "${var.env_type}-${var.sys_name}-sg-ec2messages"
  vpc_id = aws_vpc.vpc.id
  tags   = {
    Name = "${var.env_type}-${var.sys_name}-sg-ec2messages"
  }

  # インバウンドルール
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
  }

  # アウトバウンドルール
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
```
SSMS関連とFortiGateのLAN側IPアドレスを通すインバウンドルールを設定しています。

### vpn.tf
```vpn.tf
# ---------------------------
# VPN Gateway 作成
# ---------------------------
resource "aws_vpn_gateway" "vgw" {
  vpc_id = aws_vpc.vpc.id
  tags = {
    Name = "${var.env_type}-${var.sys_name}-vgw"
  }
}

# ---------------------------
# Customer Gateway 作成 (On-Premises 側ルーター)
# ---------------------------
resource "aws_customer_gateway" "cgw" {
  bgp_asn    = 65000 # default 
  ip_address = "${var.FG60F_gip_address}"
  type       = "ipsec.1"
  tags = {
    Name = "${var.env_type}-${var.sys_name}-cgw"
  }
}
```
VPN接続に必要なカスタマーゲートウェイとVPNゲートウェイの構築です。

### vpn_connection.tf
```vpn_connection.tf
# ------------------------------
# VPN 接続作成
# ------------------------------

resource "aws_vpn_connection" "vpn_connection" {
  vpn_gateway_id           = aws_vpn_gateway.vgw.id
  customer_gateway_id      = aws_customer_gateway.cgw.id
  type                     = "ipsec.1"
  static_routes_only       = true

# tunnel1 phase1
  tunnel1_phase1_encryption_algorithms = ["AES256"]
  tunnel1_phase1_integrity_algorithms  = ["SHA2-256"]
  tunnel1_phase1_dh_group_numbers      = [14]

# tunnel1 phase2
  tunnel1_phase2_encryption_algorithms = ["AES256"]
  tunnel1_phase2_integrity_algorithms  = ["SHA2-256"]
  tunnel1_phase2_dh_group_numbers      = [14]
  tunnel1_ike_versions                 = ["ikev2"]

  tags = {
    Name = "${var.env_type}-${var.sys_name}-vpn-connection"
  }
}

resource "aws_vpn_connection_route" "on-premises-general-subnet" {
  destination_cidr_block = "${var.FG60F_local_address01}"
  vpn_connection_id      = aws_vpn_connection.vpn_connection.id
}
```
VPN接続用の設定です。
デフォルトの設定だとProposal交換にちょっと時間がかかるので、こっちから指定します。
もちろんFortiGate側も設定値合わせるのでVPN接続処理はかなり高速です。

### ec2.tf
```ec2.tf
# -------------------------------
# EC2作成
# -------------------------------

# -------------------------------
# Amazon Linux EC2を作成
# -------------------------------

resource "aws_instance" "ec2_linux01" {
  ami                    = "ami-0f918f7e67a3323f0"
  instance_type          = var.ec2_instance_type_lin
  subnet_id              = aws_subnet.sn_private_1b.id
  vpc_security_group_ids = [aws_security_group.sg_ec2_linux01.id]
  key_name               = var.key_name
  associate_public_ip_address = false
  iam_instance_profile   = aws_iam_instance_profile.instance_prof.name

  instance_market_options {
    market_type = "spot"
    spot_options {
      max_price = "0.0015"
      spot_instance_type = "one-time"
    }
  }

  root_block_device {
    volume_size = 30
    volume_type = "gp3"
    encrypted   = false
  }

  user_data = <<-EOF
    #!/bin/bash
    hostnamectl set-hostname ${var.env_type}-${var.sys_name}-ec2-Ubuntu
    apt update
    apt install -y kea-dhcp4-server
    
    NIC_NAME=$(ip -o link show | awk -F': ' '!/lo/ {print $2; exit}')

    mv /etc/kea/kea-dhcp4.conf /etc/kea/kea-dhcp4.conf_org

    cat <<EOFF > /etc/kea/kea-dhcp4.conf
     {
      "Dhcp4": {
        "interfaces-config": {
        "interfaces": [ \"$NIC_NAME\" ]
        },
        "control-socket": {
         "socket-type": "unix",
         "socket-name": "/run/kea/kea4-ctrl-socket"
        },
        "lease-database": {
         "type": "memfile",
         "lfc-interval": 3600
        },
        "expired-leases-processing": {
         "reclaim-timer-wait-time": 10,
         "flush-reclaimed-timer-wait-time": 25,
         "hold-reclaimed-time": 3600,
         "max-reclaim-leases": 100,
         "max-reclaim-time": 250,
         "unwarned-reclaim-cycles": 5
        },
        "renew-timer": 900,
        "rebind-timer": 1800,
        "valid-lifetime": 3600,
        "option-data": [
         {
          "name": "domain-name-servers",
          "data": "172.16.3.1"
         }
        ],
       "subnet4": [
        {
         "id": 1,
         "subnet": "172.16.3.0/24",
         "pools": [ { "pool": "172.16.3.101 - 172.16.3.200" } ],
         "option-data": [
          {
           "name": "routers",
           "data": "172.16.3.1"
          }
         ]
        }
       ]
      }
     }
    EOFF

    systemctl restart kea-dhcp4-server
    systemctl enable kea-dhcp4-server
  EOF
  tags = {
    Name = "${var.env_type}-${var.sys_name}-ec2-Ubuntu24.04"
  }

  depends_on = [
    aws_route_table_association.associate_rt_sn_private_1b_sn_private_1b,
    aws_nat_gateway.nat_gateway_1a
  ]
}

# -------------------------------
# Windows Server EC2を作成
# -------------------------------

# -------------------------------
# Windows Server 用のKey pair作成
# -------------------------------
variable "key_name" {
  default = "kp-ec2-01"
}
variable "key_path" {
  # - Windowsの場合はフォルダを"\\"で区切る（エスケープする必要がある）実行中のtfファイルと同じ場所にキーファイルを出力する
  default = ".\\"
}
resource "tls_private_key" "kp-windows-ec2-01" {
  algorithm = "RSA"
  rsa_bits  = 2048
}

# クライアントPCにKey pair（秘密鍵と公開鍵）を作成
# - Windowsの場合はフォルダを"\\"で区切る（エスケープする必要がある）
# - [terraform apply] 実行後はクライアントPCの公開鍵は自動削除される
locals {
  public_key_file  = "${var.key_path}\\${var.key_name}.id_rsa.pub"
  private_key_file = "${var.key_path}\\${var.key_name}.id_rsa"
}

resource "local_file" "kp_windows_ec2_01_pem" {
  filename = "${local.private_key_file}"
  content  = "${tls_private_key.kp-windows-ec2-01.private_key_pem}"
}

# 上記で作成した公開鍵をAWSのKey pairにインポート
resource "aws_key_pair" "kp_windows_ec2_01" {
  key_name   = "${var.key_name}"
  public_key = "${tls_private_key.kp-windows-ec2-01.public_key_openssh}"
}

# 20250620時点のAMI
#Microsoft Windows Server 2025 Base:ami-036940a1a7418c22f
#Microsoft Windows Server 2022 Base:ami-073586faab6ea2875
#Microsoft Windows Server 2019 Base:ami-0c47b14fff9e56d8e
#Microsoft Windows Server 2016 Base:ami-01a1a664e189101ef

resource "aws_instance" "ec2_windows01" { #spot_priceを設定する。ムンバイリージョンではAvailability Zone CではSpot Instanceは使えない。
  ami                    = "ami-036940a1a7418c22f"   # Windows Server 2025 Base
  instance_type          = var.ec2_instance_type_win
  subnet_id              = aws_subnet.sn_private_1b.id
  vpc_security_group_ids = [aws_security_group.sg_ec2_windows01.id]

  # スポットインスタンス設定
  instance_market_options {
    market_type = "spot"

    spot_options {
      max_price            = "0.11"     # 上限価格
      spot_instance_type   = "one-time"
      # valid_until        = "2025-06-30T12:00:00Z"  # 必要なら終了期限を設定
    }
  }

  # ネットワーク & IAM
  associate_public_ip_address = false
  key_name                    = var.key_name
  iam_instance_profile        = aws_iam_instance_profile.instance_prof.name

  # ルートボリューム
  root_block_device {
    volume_size = 100          # GiB
    volume_type = "gp3"
    encrypted   = false
  }

  # インスタンス用タグ
  tags = {
    Name = "${var.env_type}-${var.sys_name}-ec2-windows01"
  }

  # ルートボリューム用タグ
  volume_tags = {
    Snapshot = "false"
  }

  depends_on = [
    aws_route_table_association.associate_rt_sn_private_1b_sn_private_1b,
    aws_nat_gateway.nat_gateway_1a
  ]
}
```
今回はLinux OSをUbuntuにしました。
Amazon Linux 2023だとDHCPサーバとして利用するkea-dhcp4-serverが見つからないと言われたため、Ubuntuにしました。
yumでもdnfでも見つからなかったんですが、確かyumはdnfのエイリアスなんで参照先リポジトリ変えようかなとも思ったんですが、冒頭の
**パブリッククラウド上でDHCPサーバ構築ってひょっとしてNG？なのでAmazon Linuxのリポジトリから敢えて削除してる？**
とも思ったので、素直にUbuntuにしました。

他にポイントが3つ
1. 構築順序の制限を行っており、ルートテーブルのサブネットへの関連付けとNAT Gateway構築後にEC2を構築する、という順序にしている
1. ヒアドキュメント内でkea-dhcp4-serverの設定ファイルも構築している
1. ヒアドキュメント内でエスケープ文字を使わないとデプロイできないのですが、このエスケープ文字が仇になり設定ファイルをこの部分のみ編集しなければならない
です。

1.ですがこうすることでkea-dhcp4-serverのインストールが確実に行われます。
2. に関しては面倒だったのでここまで練り上げました。
3. ですが、これは２．の反射効で、ここんなことある？って思いましたがとりあえずこの程度なら良いかなという事で今回は許容しました。
この辺に詳しい人、教えてください。

### AWS上にこのTerraformのコードで構築してみる
```terraform apply```を実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/1adb098a-47a8-42ab-8db8-25afe8d63398.png)
画像のようにゴリゴリゴリっとTerraform Planが実行され
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/daacd194-6399-4f1e-b190-e345fcf69197.png)
最後に```yes```を入力してエンターキーを押します。
<font color="red">赤枠</font>の通り30個もリソース作るんですよね。
えらいこっちゃです。

# 構築確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/062579b5-2eba-44aa-b048-fd7bf37248ed.png)
一番最後にOutput.tfでNAT Gatewayに割り当てたエラスティックIPアドレスを出力します。
```13.202.202.41```
ですね。
EC2達がちゃんとNAT Gatewayからインターネットに接続されているか確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/6529f6b3-a183-4f6c-a12c-bb6bc5464632.png)
Ubuntuは間違いないですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/b136ca65-6ad2-40f4-b8b3-1cdfeac678a3.png)
Windowsも間違いないですね。

# VPN接続
ちゃちゃっとやっちゃいましょう。
AWSコンソールから必要なパラメーターを入力し
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/1e6174e9-4583-440b-bf76-58035e711d1a.png)
設定ファイルをダウンロードします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/2d38dd12-5765-46d5-b18b-cbb2172c1f0f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/79fec167-4a0a-43f7-a904-2bec638f3c6a.png)

やいのやいの書いてますが、Terraformで設定した通りにFortiGate側は設定すればOKです。
今回は検証なので1本しかトンネルもアップさせません。
このテキストファイルから抜き出す必要なパラメーターはAWS側のグローバルIPアドレスとPSK、仮想インターフェースのIPアドレスとセグメント情報くらいですかね。

AWS-FortiGate間のインターネットVPN接続は調べたらたくさん出てくると思うので雑に書いてしまいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/541280f9-a546-4e1e-b327-6cd3b154cb88.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/774fa727-abdb-4ee2-bf86-354d25b5aa27.png)

こんなもんですかね。

# VPN接続確認
どうやってやりましょうかね。
とりあえずICMPで良いですかね。

## 接続先確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/9f44d818-0ded-4e7a-9498-e58616c956ef.png)
Ubuntuは```10.0.1.148```ですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/d322081c-4e55-4215-881c-05fcc5ae4321.png)
Windowsは```10.0.1.86```ですね。
別にどこからローカルIPアドレスを確認してもらっても全然問題ないです。
お好きなところからどうぞ。

## FortiGate側トンネル確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/4c870ebf-03b4-4cb3-9059-88c931750825.png)
はい。
バッチリUpしてますね。

## AWS側トンネル確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/a9c2e604-c766-4b53-88b6-845553400ebd.png)
こちらも1本目Upしてますね。
検証なので面倒ですから2本目は放置です。

## ICMPv4通信確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/0fc6c997-f013-4d86-b8f8-5151cb047ce2.png)
FortiGate(```172.16.3.1```)からUbuntu(```10.0.1.148```)もWindows(```10.0.1.86```)も綺麗にICMP返ってきてくれていますね。

# Ubuntuのkea-dhcp4-serverの設定変更及びサービス再起動
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/157d5942-9db0-4319-bd7a-f04d73b4688e.png)
さっき述べたTerraform内でヒアドキュメント作成したのでその反射効の部分です。
赤文字ばっかりで<font color="yellow">黄色枠</font>にしました。この部分を
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/b0d937a9-4c69-4226-895a-81be7433a99e.png)
このように変えます。
\を削除するだけですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/7dbe11d8-768b-4f03-871a-f8f48e46614d.png)
```sudo systemctl restart kea-dhcp4-server```
```sudo systemctl status kea-dhcp4-server```
で綺麗に```active (running)```になりましたね。

# DHCPリレー設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/747640c3-db80-4857-b990-c2fd097478b3.png)
はい。
今回の肝の設定値ですね。
```DHCPサーバ```のスイッチを```オン```にして、
```モード```を```リレー```にします。
そして```タイプ```を```レギュラー```にし、
```DHCPサーバIP```にUbuntuのローカルIPアドレス```10.0.1.148```を入力します。
最後に画面最下部の[OK]をクリックします。

# DHCPリレー設定動作確認
構成図のRaspberry PiにSSHでログインします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/2a78c24e-9f81-44f7-8afe-e719572cb1fa.png)
はい。
ご覧の通り有線LANのNICはDHCPクライアントですがDHCPサーバからの応答が無かった場合に自動的に付与されるリンクローカルIPアドレスになっていますね。
eth0のインターフェースのUp/downを行い、しばらく待つと・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/e4e26fd3-8713-4c8d-b226-fdf3250071e0.png)
<font color="Orange">**はい！
想定通りのIPアドレスが払い出されましたね！
完璧ですね！**</font>
嬉しいので久しぶりに集中線付けちゃいましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/9c979f86-6a30-4b4b-8b0d-d07dfc5ae50f.png)
<font color="Orange">**いや、ホントに設計通り、想定通りに動作すると気持ちが良いですね！
素晴らしいですね！**</font>

# まとめ
1. AWS上でDHCPサーバはDHCPリレーで使うなら立てても良い
1. 実際に立ててみても問題なく動作する

ということでした。
個人的には今回の検証でTerraformを久々に触って、Raspberry Piもかなり久々に触ってメジャーバージョンアップをゴリゴリやり倒したのでめちゃくちゃ時間かかったのですが、非常に充実した検証でした。

本日はここまで。