LoginSignup
3
0

More than 1 year has passed since last update.

AWS Client VPN[Federation authentication]を使用する

Last updated at Posted at 2022-05-24

方法

AWS ClientVPN と Azure Active Directory (Azure AD) を統合する

構成

image.png

IdP

  • Microsoft Azure

    1. AWS ClientVPNへの接続ユーザをAzureADで制御
    2. Azure ADアカウントを用いてAWS ClientVPN に自動的にサインインさせる

SP

  • AWS Client VPN

    1. セルフサービスポータル URLを作成することで運用コストを下げる
    2. 認証はfederated-authenticationを選択
    3. VPNを作成するVPCはDNS解決、DNSホストを有効にしておく

構築

Azure

Azure Active Directory シングル サインオン (SSO) と AWS ClientVPN の統合を参照する

  • Azure Active Directory > Enterprise applications > New application
    image.png

  • aws clientvpnで検索しアプリケーションを選択し作成する
    image.png
    image.png

  • ユーザを登録
    image.png

  • Set up single sign on > SAML
    image.png

  • パラメータ入力
    名称未設定.png

  • App registrations > AWS ClientVPN
    image.png

  • Manifest > > 応答URLをhttpに更新
    名称未設定2.png

  • Federation Metadata XMLをダウンロードする
    名称未設定3.png


AWS

ダウンロードしたFederation Metadata XMLでSAML用のIAM Identity Providerを作成する

  • IAM設定ファイル
vpn-config
├── common
│   ├── iam
│   │   ├── azure.xml
│   │   ├── iam_saml_provider.tf
│   │   └── main.tf
│   └── server-crt
│       ├── README.md
(snip)
aws_iam_saml_provider.tf
resource "aws_iam_saml_provider" "example_azure" {
  name                   = "azure"
  saml_metadata_document = file("azure.xml")
}
  • AWS ClientVPNで使用するサーバ証明書を作成
    • OpenVPN easy-rsa リポジトリのクローンをローカルコンピュータに作成して、easy-rsa/easyrsa3 フォルダに移動させる
% git clone https://github.com/OpenVPN/easy-rsa.git
% cd easy-rsa/easyrsa3
% ./easyrsa init-pki
% ./easyrsa build-ca nopass
% ./easyrsa build-server-full server nopass
  • サーバー証明書とキーをカスタムフォルダにコピーしてから、カスタムフォルダに移動させる
% mkdir ~/custom_folder/
% cp pki/ca.crt ~/custom_folder/
% cp pki/issued/server.crt ~/custom_folder/
% cp pki/private/server.key ~/custom_folder/
% cp pki/issued/client1.domain.tld.crt ~/custom_folder
% cp pki/private/client1.domain.tld.key ~/custom_folder/
% cd ~/custom_folder/
  • サーバー証明書とキーを ACM にアップロードする
% aws acm import-certificate --certificate fileb://server.crt --private-key fileb://server.key --certificate-chain fileb://ca.crt

VPN設定ファイル

vpn-config
(snip)
│
└── network
    ├── cloudwatch_log_group.tf
    ├── cloudwatch_metric_alarm.tf
    ├── ec2_client_vpn_endpoint.tf
    ├── eip.tf
    ├── internet_gateway.tf
    ├── main.tf
    ├── nat_gateway.tf
    ├── route.tf
    ├── route_table.tf
    ├── route_table_association.tf
    ├── security_group.tf
    ├── subnet.tf
    ├── variables.tf
    └── vpc.tf
cloudwatch_log_group.tf
resource "aws_cloudwatch_log_group" "client_vpn" {
  name              = "client-vpn"
  retention_in_days = 365

  tags = {
    Name        = "client-vpn"
  }
}

resource "aws_cloudwatch_log_stream" "client_vpn" {
  name           = "client-vpn-logstream"
  log_group_name = aws_cloudwatch_log_group.client_vpn.name
}
cloudwatch_metric_alarm.tf
resource "aws_cloudwatch_metric_alarm" "client_vpn" {
  for_each = var.client_vpn_cloudwatch_metrics

  alarm_name          = each.value.alarm_name
  comparison_operator = each.value.comparison_operator
  evaluation_periods  = "2"
  treat_missing_data  = "notBreaching"
  metric_name         = each.value.metric_name
  namespace           = "AWS/ClientVPN"
  period              = "60"
  statistic           = each.value.statistic
  threshold           = each.value.threshold
  alarm_actions       = ["arn:aws:sns:ap-northeast-1:account-id:alert-client-vpn"]
  ok_actions          = ["arn:aws:sns:ap-northeast-1:account-id:alert-client-vpn"]

  dimensions = {
    Endpoint = "cvpn-endpoint-*********"
  }
}
ec2_client_vpn_endpoint.tf
resource "aws_ec2_client_vpn_endpoint" "example" {
  client_cidr_block = "192.168.0.0/20"
  description       = "use tcp"
  dns_servers = [
    "1.1.1.1",
    "8.8.8.8",
  ]
  self_service_portal    = "enabled"
  server_certificate_arn = data.aws_acm_certificate.issued.arn
  split_tunnel           = 
  
  tags = {
    Name        = "example-vpn-federated-authentication"
  }
  transport_protocol = "tcp"

  authentication_options {
    saml_provider_arn              = "arn:aws:iam::account-id:saml-provider/azure"
    self_service_saml_provider_arn = "arn:aws:iam::account-id:saml-provider/azure"
    type                           = "federated-authentication"
  }

  connection_log_options {
    enabled               = true
    cloudwatch_log_group  = aws_cloudwatch_log_group.client_vpn.name
    cloudwatch_log_stream = aws_cloudwatch_log_stream.client_vpn.name
  }
}

resource "aws_ec2_client_vpn_network_association" "example_assoc" {
  for_each = var.vpn_client_nat_route_assoc

  client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.example.id
  subnet_id              = aws_subnet.client_vpn[each.key].id
  security_groups        = [aws_security_group.client_vpn.id]
}


resource "aws_ec2_client_vpn_authorization_rule" "example_authorization" {
  client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.example.id
  target_network_cidr    = "0.0.0.0/0"
  authorize_all_groups   = true
}

resource "aws_ec2_client_vpn_route" "example_route_1a" {

  client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.example.id
  destination_cidr_block = "0.0.0.0/0"
  target_vpc_subnet_id   = aws_subnet.client_vpn["private-1a"].id
}

resource "aws_ec2_client_vpn_route" "example_route_1c" {

  client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.example.id
  destination_cidr_block = "0.0.0.0/0"
  target_vpc_subnet_id   = aws_subnet.client_vpn["private-1c"].id
}

eip.tf
resource "aws_eip" "client_vpn" {
  for_each = var.vpn_client_natgw

  vpc = true

  tags = {
    Name        = each.value.name
  }
}
internet_gateway.tf
resource "aws_internet_gateway" "client_vpn" {
  vpc_id = aws_vpc.client_vpn.id

  tags = {
    Name        = "client-vpn"
  }
}
nat_gateway.tf
resource "aws_nat_gateway" "client_vpn" {
  for_each = var.vpn_client_natgw

  allocation_id = aws_eip.client_vpn[each.key].id
  subnet_id     = aws_subnet.client_vpn[each.key].id

  tags = {
    Name        = each.value.name
  }
}
route.tf
locals {
  public-names = [
    "public-1a",
    "public-1c"
  ]
}

resource "aws_route" "client_vpn_public" {
  for_each = toset(local.public-names)

  route_table_id         = aws_route_table.client_vpn[each.key].id
  gateway_id             = aws_internet_gateway.client_vpn.id
  destination_cidr_block = "0.0.0.0/0"
}

resource "aws_route" "client_vpn_private" {
  for_each = var.vpn_client_nat_route_assoc

  route_table_id         = aws_route_table.client_vpn[each.key].id
  nat_gateway_id         = aws_nat_gateway.client_vpn[each.value.natgw-name].id
  destination_cidr_block = "0.0.0.0/0"
}
route_table.tf
resource "aws_route_table" "client_vpn" {
  for_each = var.client_vpn_subnets
  vpc_id   = aws_vpc.client_vpn.id

  tags = {
    Name        = each.value.name
  }
}
route_table_association.tf
resource "aws_route_table_association" "client_vpn" {
  for_each = var.client_vpn_subnets

  subnet_id      = aws_subnet.client_vpn[each.key].id
  route_table_id = aws_route_table.client_vpn[each.key].id
}
security_group.tf
resource "aws_security_group" "client_vpn" {
  name   = "client-vpn"
  vpc_id = aws_vpc.client_vpn.id

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

  tags = {
    Name        = "client-vpn"
  }
}

resource "aws_security_group_rule" "client_vpn_80" {
  type        = "ingress"
  from_port   = 80
  to_port     = 80
  protocol    = "tcp"
  cidr_blocks = ["0.0.0.0/0"]

  security_group_id = aws_security_group.client_vpn.id
}

resource "aws_security_group_rule" "client_vpn_443" {
  type        = "ingress"
  from_port   = 443
  to_port     = 443
  protocol    = "tcp"
  cidr_blocks = ["0.0.0.0/0"]

  security_group_id = aws_security_group.client_vpn.id
}
subnet.tf
resource "aws_subnet" "client_vpn" {
    for_each = var.client_vpn_subnets
  
    vpc_id            = aws_vpc.client_vpn.id
    availability_zone = each.value.zone
  
    cidr_block              = each.value.cidr
    map_public_ip_on_launch = each.value.launch
  
    tags = {
      Name        = each.value.name
    }
  }
vpc.tf
resource "aws_vpc" "client_vpn" {
    cidr_block       = "10.0.0.0/16"
    instance_tenancy = "default"
  
    tags = {
      Name        = "client-vpn"
    }
  }
variables.tf
variable "vpn_client_nat_route_assoc" {
    type = map(any)
  
    default = {
      private-1a = {
        natgw-name = "public-1a"
      }
      private-1c = {
        natgw-name = "public-1c"
      }
    }
  }
  
  variable "vpn_client_natgw" {
    type = map(any)
  
    default = {
      public-1a = {
        name = "client-vpn-public-1a"
      }
      public-1c = {
        name = "client-vpn-public-1c"
      }
    }
  }
  
  variable "client_vpn_subnets" {
    type = map(any)
  
    default = {
      public-1a = {
        cidr   = "10.0.0.0/24"
        zone   = "ap-northeast-1a"
        launch = "true"
        name   = "client-vpn-public-1a"
      }
      public-1c = {
        cidr   = "10.0.1.0/24"
        zone   = "ap-northeast-1c"
        launch = "true"
        name   = "client-vpn-public-1c"
      }
      private-1a = {
        cidr   = "10.0.2.0/24"
        zone   = "ap-northeast-1a"
        launch = "false"
        name   = "client-vpn-private-1a"
      }
      private-1c = {
        cidr   = "10.0.3.0/24"
        zone   = "ap-northeast-1c"
        launch = "false"
        name   = "client-vpn-private-1c"
      }
    }
  }
  
  variable "client_vpn_cloudwatch_metrics" {
    type = map(any)
  
    default = {
      ActiveConnectionsCount = {
        alarm_name          = "cvpn-endpoint-******************-ActiveConnectionsCount"
        metric_name         = "ActiveConnectionsCount"
        statistic           = "Sum"
        threshold           = "100"
        comparison_operator = "GreaterThanOrEqualToThreshold"
        alarm_description   = "クライアント VPN エンドポイントへのアクティブな接続数"
      }
      AuthenticationFailures = {
        alarm_name          = "cvpn-endpoint-******************-AuthenticationFailures"
        metric_name         = "AuthenticationFailures"
        statistic           = "Sum"
        threshold           = "10"
        comparison_operator = "GreaterThanOrEqualToThreshold"
        alarm_description   = "クライアント VPN エンドポイントの認証失敗数"
      }
      CrlDaysToExpiry = {
        alarm_name          = "cvpn-endpoint-******************-CrlDaysToExpiry"
        metric_name         = "CrlDaysToExpiry"
        statistic           = "Sum"
        threshold           = "30"
        comparison_operator = "GreaterThanOrEqualToThreshold"
        alarm_description   = "クライアント VPN エンドポイントで設定されている証明書失効リスト (CRL) の有効期限が切れるまでの日数"
      }
      SelfServicePortalClientConfigurationDownloads = {
        alarm_name          = "cvpn-endpoint-******************-SelfServicePortalClientConfigurationDownloads"
        metric_name         = "SelfServicePortalClientConfigurationDownloads"
        statistic           = "Sum"
        threshold           = "100"
        comparison_operator = "GreaterThanOrEqualToThreshold"
        alarm_description   = "セルフサービスポータルからの Client VPN エンドポイント設定ファイルのダウンロード数"
      }
      ClientConnectHandlerTimeouts = {
        alarm_name          = "cvpn-endpoint-******************-ClientConnectHandlerTimeouts"
        metric_name         = "ClientConnectHandlerTimeouts"
        statistic           = "Sum"
        threshold           = "100"
        comparison_operator = "GreaterThanOrEqualToThreshold"
        alarm_description   = "クライアント VPN エンドポイントへの接続用のクライアント接続ハンドラの呼び出し時のタイムアウト数"
      }
      ClientConnectHandlerInvalidResponses = {
        alarm_name          = "cvpn-endpoint-******************-ClientConnectHandlerInvalidResponses"
        metric_name         = "ClientConnectHandlerInvalidResponses"
        statistic           = "Sum"
        threshold           = "100"
        comparison_operator = "GreaterThanOrEqualToThreshold"
        alarm_description   = "クライアント VPN エンドポイントへの接続用のクライアント接続ハンドラから返された無効な応答の数"
      }
      ClientConnectHandlerOtherExecutionErrors = {
        alarm_name          = "cvpn-endpoint-******************-ClientConnectHandlerOtherExecutionErrors"
        metric_name         = "ClientConnectHandlerOtherExecutionErrors"
        statistic           = "Sum"
        threshold           = "100"
        comparison_operator = "GreaterThanOrEqualToThreshold"
        alarm_description   = "クライアント VPN エンドポイントへの接続用のクライアント接続ハンドラの実行中に発生した、予期されていなかったエラーの数"
      }
      ClientConnectHandlerThrottlingErrors = {
        alarm_name          = "cvpn-endpoint-******************-ClientConnectHandlerThrottlingErrors"
        metric_name         = "ClientConnectHandlerThrottlingErrors"
        statistic           = "Sum"
        threshold           = "100"
        comparison_operator = "GreaterThanOrEqualToThreshold"
        alarm_description   = "クライアント VPN エンドポイントへの接続用のクライアント接続ハンドラの呼び出し時のスロットリングエラーの数"
      }
      ClientConnectHandlerDeniedConnections = {
        alarm_name          = "cvpn-endpoint-******************-ClientConnectHandlerDeniedConnections"
        metric_name         = "ClientConnectHandlerDeniedConnections"
        statistic           = "Sum"
        threshold           = "100"
        comparison_operator = "GreaterThanOrEqualToThreshold"
        alarm_description   = "クライアント VPN エンドポイントへの接続用のクライアント接続ハンドラによって拒否された接続の数"
      }
      ClientConnectHandlerFailedServiceErrors = {
        alarm_name          = "cvpn-endpoint-******************-ClientConnectHandlerFailedServiceErrors"
        metric_name         = "ClientConnectHandlerFailedServiceErrors"
        statistic           = "Sum"
        threshold           = "100"
        comparison_operator = "GreaterThanOrEqualToThreshold"
        alarm_description   = "クライアント VPN エンドポイントへの接続用のクライアント接続ハンドラの実行中に発生したサービス側のエラーの数"
      }
    }
  }
main.tf
provider "aws" {
  region  = "ap-northeast-1"

  default_tags {
    tags = {
    Service     = "client-vpn"
    Description = "Managed by Terraform"
    }
  }
}

terraform {
  required_version = ">= 0.13"

  backend "s3" {
    bucket  = "***********"
    key     = "network/terraform.tfstate"
    region  = "ap-northeast-1"
  }
}

data "aws_acm_certificate" "issued" {
  domain   = "server"
  statuses = ["ISSUED"]
}

VPN Client

  • AWS Client VPN Self-Service Portalにログインし、接続設定用ファイルと使用OS毎にClientVPNアプリケーションをダウンロードする
    image.png

  • 設定手順

    • AWS Client VPN for Windows

      • 3.[Add Profile (プロファイルの追加)] を選択します。の個所で保存したプロファイルを指定する
    • AWS Client VPN for macOS

      • 3.[Add Profile (プロファイルの追加)] を選択します。の個所で保存したプロファイルを指定する
    • Linux の AWS Client VPN

      • 3.[Add Profile (プロファイルの追加)] を選択します。の個所で保存したプロファイルを指定する
    • IPv6リーク対策のため、プロファイルの最終行に記述

federation-auth-config.ovpn
ifconfig-ipv6 fd15:53b6:dead::2 fd15:53b6:dead::1
route-ipv6 2000::/4
  • 確認
    • 使用中IPがNatGatewayに使用しているEIPであることが確認できればOKです
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0