VyOSを利用したVPN環境構築(静的ルーティング編)

VyOSを利用したVPN環境構築において、静的ルーティングの場合の設定手順を確認しました。VyOSの設定ファイルの記述などについて説明しています。
2020.03.10

みなさん、こんにちは!
AWS事業本部の青柳@福岡オフィスです。

静的ルーティングによる サイト間VPN接続 を検証する必要があったため、オープンソースのソフトウェアルーターである VyOS を使ってVPN環境を構築しました。

なお、VyOSを利用した動的ルーティング (BGP) によるVPN接続については、下記のブログエントリで紹介されています。

VyOS1.2を利用したVPN環境構築 | Developers.IO
VyOSを利用したVPN環境構築 | Developers.IO

構成

下図のような構成を構築します。

東京リージョンをAWS側、シンガポールリージョンをオンプレミス側にそれぞれ見立ててサイト間VPN接続を構築します。

東京リージョンには 仮想プライベートゲートウェイ (VGW) を配置します。
シンガポールリージョンには カスタマーゲートウェイデバイス (オンプレミス側ルーター) としてVyOSがインストールされたEC2インスタンスを配置します。

各リージョンのプライベートサブネットに、動作検証用のEC2インスタンス (Linux+nginx) を配置します。

構築手順

1. ベース環境の作成

VPN環境の構築を始める前に、まず、ベースとなるネットワーク環境 (VPC、サブネットなど) と動作確認用EC2インスタンスを作成します。

今回はCloudFormationで作成しました。

ベース環境作成CloudFormationテンプレート (クリックすると展開します)

vpn-test-base.yaml

---
AWSTemplateFormatVersion: "2010-09-09"
Description: "VPN TEST: Base environment (VPC and EC2)"

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: "General Information"
        Parameters:
          - SystemName
          - Environment
      - Label:
          default: "Network Configuration"
        Parameters:
          - CidrBlockVPC
          - CidrBlockSubnetPublic1
          - CidrBlockSubnetPrivate1
          - CidrBlockRemoteNetwork
      - Label:
          default: "EC2 Instance Configuration"
        Parameters:
          - EC2ImageID
          - EC2InstanceType
          - EC2KeyName
          - EC2VolumeType
          - EC2VolumeSize

Parameters:
  SystemName:
    Type: String
    AllowedValues:
      - tokyo
      - singapore

  Environment:
    Type: String
    Default: dev
    AllowedValues:
      - dev
      - str
      - prd

  CidrBlockVPC:
    Type: String
    AllowedValues:
      -  10.0.0.0/16
      -  192.168.0.0/16

  CidrBlockSubnetPublic1:
    Type: String
    AllowedValues:
      -  10.0.0.0/24
      -  192.168.0.0/24

  CidrBlockSubnetPrivate1:
    Type: String
    AllowedValues:
      -  10.0.128.0/24
      -  192.168.128.0/24

  CidrBlockRemoteNetwork:
    Type: String
    AllowedValues:
      -  192.168.128.0/24
      -  10.0.128.0/24

  EC2ImageID:
    Type: AWS::SSM::Parameter::Value<String>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2

  EC2InstanceType:
    Type: String
    Default: t3.micro

  EC2KeyName:
    Type: AWS::EC2::KeyPair::KeyName

  EC2VolumeType:
    Type: String
    Default: gp2

  EC2VolumeSize:
    Type: String
    Default: 8

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref CidrBlockVPC
      EnableDnsSupport: true
      EnableDnsHostnames: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-${Environment}-vpc"
        - Key: System
          Value: !Ref SystemName
        - Key: Environment
          Value: !Ref Environment

  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-${Environment}-igw"
        - Key: System
          Value: !Ref SystemName
        - Key: Environment
          Value: !Ref Environment

  VPCGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

  SubnetPublic1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select
        - 0
        - Fn::GetAZs: !Ref AWS::Region
      CidrBlock: !Ref CidrBlockSubnetPublic1
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-${Environment}-public1-subnet"
        - Key: System
          Value: !Ref SystemName
        - Key: Environment
          Value: !Ref Environment

  SubnetPrivate1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select
        - 0
        - Fn::GetAZs: !Ref AWS::Region
      CidrBlock: !Ref CidrBlockSubnetPrivate1
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-${Environment}-private1-subnet"
        - Key: System
          Value: !Ref SystemName
        - Key: Environment
          Value: !Ref Environment

  EIPNatGateway:
    DependsOn:
      - VPCGatewayAttachment
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc

  NatGateway:
    DependsOn:
      - EIPNatGateway
      - SubnetPublic1
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt EIPNatGateway.AllocationId
      SubnetId: !Ref SubnetPublic1
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-${Environment}-natgateway"
        - Key: System
          Value: !Ref SystemName
        - Key: Environment
          Value: !Ref Environment

  RouteTablePublic:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-${Environment}-public-rtb"
        - Key: System
          Value: !Ref SystemName
        - Key: Environment
          Value: !Ref Environment

  RouteTablePrivate:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-${Environment}-private-rtb"
        - Key: System
          Value: !Ref SystemName
        - Key: Environment
          Value: !Ref Environment

  RouteIGW:
    DependsOn:
      - VPCGatewayAttachment
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref RouteTablePublic
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  RouteNatGateway:
    DependsOn:
      - NatGateway
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref RouteTablePrivate
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NatGateway

  RouteTableAssociationPublic1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref SubnetPublic1
      RouteTableId: !Ref RouteTablePublic

  RouteTableAssociationPrivate1:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref SubnetPrivate1
      RouteTableId: !Ref RouteTablePrivate

  IAMRoleSSM:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub "${SystemName}-${Environment}-ssm-role"
      AssumeRolePolicyDocument: |
        {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": {
                "Service": "ec2.amazonaws.com"
              },
              "Action": "sts:AssumeRole"
            }
          ]
        }
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
      Path: /

  IAMInstanceProfileSSM:
    Type: AWS::IAM::InstanceProfile
    Properties:
      InstanceProfileName: !Sub "${SystemName}-${Environment}-ssm-role"
      Roles: 
        - !Ref IAMRoleSSM
      Path: /

  SecurityGroupServer:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub "${SystemName}-${Environment}-server-sg"
      GroupDescription: "Security group for servers"
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: !Ref CidrBlockRemoteNetwork
          Description: "HTTP access from remote network"
        - IpProtocol: icmp
          FromPort: -1
          ToPort: -1
          CidrIp: !Ref CidrBlockRemoteNetwork
          Description: "ICMP access from remote network"
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-${Environment}-server-sg"
        - Key: System
          Value: !Ref SystemName
        - Key: Environment
          Value: !Ref Environment

  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref EC2ImageID
      InstanceType: !Ref EC2InstanceType
      KeyName: !Ref EC2KeyName
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            VolumeType: !Ref EC2VolumeType
            VolumeSize: !Ref EC2VolumeSize
      NetworkInterfaces:
        - DeviceIndex: 0
          SubnetId: !Ref SubnetPrivate1
          GroupSet:
            - !Ref SecurityGroupServer
      IamInstanceProfile: !Ref IAMInstanceProfileSSM
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash -xe
          amazon-linux-extras install -y nginx1
          systemctl enable nginx
          systemctl start nginx
          SYSTEM_NAME=${SystemName}
          echo Hello! I\'m ${!SYSTEM_NAME^^} Web Server! > /usr/share/nginx/html/index.html
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-${Environment}-server"
        - Key: System
          Value: !Ref SystemName
        - Key: Environment
          Value: !Ref Environment

Outputs:
  VPC:
    Value: !Ref VPC
    Export:
      Name: !Sub "${AWS::StackName}::VPC"

  SubnetPublic1:
    Value: !Ref SubnetPublic1
    Export:
      Name: !Sub "${AWS::StackName}::SubnetPublic1"

  SubnetPrivate1:
    Value: !Ref SubnetPrivate1
    Export:
      Name: !Sub "${AWS::StackName}::SubnetPrivate1"

  RouteTablePublic:
    Value: !Ref RouteTablePublic
    Export:
      Name: !Sub "${AWS::StackName}::RouteTablePublic"

  RouteTablePrivate:
    Value: !Ref RouteTablePrivate
    Export:
      Name: !Sub "${AWS::StackName}::RouteTablePrivate"

  IAMRoleSSM:
    Value: !Ref IAMRoleSSM
    Export:
      Name: !Sub "${AWS::StackName}::IAMRoleSSM"

  IAMInstanceProfileSSM:
    Value: !Ref IAMInstanceProfileSSM
    Export:
      Name: !Sub "${AWS::StackName}::IAMInstanceProfileSSM"

  SecurityGroupServer:
    Value: !Ref SecurityGroupServer
    Export:
      Name: !Sub "${AWS::StackName}::SecurityGroupServer"

  EC2Instance:
    Value: !Ref EC2Instance
    Export:
      Name: !Sub "${AWS::StackName}::EC2Instance"

CloudFormationテンプレートは東京・シンガポールの各リージョンで共通です。

各リージョン毎に、パラメータは以下のように指定してください。

パラメータ名 設定値 (東京リージョン) 設定値 (シンガポールリージョン)
SystemName tokyo singapore
CidrBlockVPC 10.0.0.0/16 192.168.0.0/16
CidrBlockSubnetPublic1 10.0.0.0/24 192.168.0.0/24
CidrBlockSubnetPrivate1 10.0.128.0/24 192.168.128.0/24
CidrBlockRemoteNetwork 192.168.128.0/24 10.0.128.0/24
EC2KeyName (任意のキーペア) (任意のキーペア)

このCloudFormationテンプレートで作成される「動作確認用EC2インスタンス」のポイントは以下の通りです:

  • UserDataを使ってnginxを自動的にインストール
  • Systems Managerのセッションマネージャーを使って操作できるように、IAMインスタンスプロファイルを設定
  • 対向リージョンのプライベートサブネットからのICMPおよびHTTPによるアクセスを許可するセキュリティグループ

2. VyOSインスタンスの作成

シンガポールリージョンに、ルーターとなるVyOSインスタンスを作成します。

こちらも同様にCloudFormationで作成します。

VyOSインスタンス作成CloudFormationテンプレート (クリックすると展開します)

vpn-test-vyos.yaml

---
AWSTemplateFormatVersion: "2010-09-09"
Description: "VPN TEST: VyOS EC2 Instance (site-to-site VPN router)"

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: "General Information"
        Parameters:
          - SystemName
          - Environment
      - Label:
          default: "Network Configuration"
        Parameters:
          - CidrBlockAllowAccessRouter
          - IPAddressAllowSSH
      - Label:
          default: "EC2 Instance Configuration"
        Parameters:
          - EC2ImageID
          - EC2InstanceType
          - EC2KeyName
          - EC2VolumeType
          - EC2VolumeSize
      - Label:
          default: "CloudFormation Stack Reference"
        Parameters:
          - StackVPC

Parameters:
  SystemName:
    Type: String
    Default: singapore

  Environment:
    Type: String
    Default: dev
    AllowedValues:
      - dev
      - str
      - prd

  CidrBlockAllowAccessRouter:
    Type: String
    Default: 192.168.128.0/24

  IPAddressAllowSSH:
    Type: String

  EC2ImageID:
    Type: AWS::EC2::Image::Id
    Default: ami-070a39a224bab5d4f    # VyOS (HVM) 1.2.4-9c9395f4-e891-4577-82e9-a6d5bccfb3c9-ami-00c8302b5b62b168a.4 @ ap-southeast-1

  EC2InstanceType:
    Type: String
    Default: t3.large

  EC2KeyName:
    Type: AWS::EC2::KeyPair::KeyName

  EC2VolumeType:
    Type: String
    Default: gp2

  EC2VolumeSize:
    Type: String
    Default: 10

  StackVPC:
    Type: String

Resources:
  SecurityGroupVyOS:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub "${SystemName}-${Environment}-router-sg"
      GroupDescription: "Security group for servers"
      VpcId: {"Fn::ImportValue": !Sub "${StackVPC}::VPC"}
      SecurityGroupIngress:
        - IpProtocol: -1
          FromPort: -1
          ToPort: -1
          CidrIp: !Ref CidrBlockAllowAccessRouter
          Description: "Access by all protocols from allowed network"
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref IPAddressAllowSSH
          Description: "SSH access from my IP address"
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-${Environment}-router-sg"
        - Key: System
          Value: !Ref SystemName
        - Key: Environment
          Value: !Ref Environment

  EC2InstanceVyOS:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref EC2ImageID
      InstanceType: !Ref EC2InstanceType
      KeyName: !Ref EC2KeyName
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            VolumeType: !Ref EC2VolumeType
            VolumeSize: !Ref EC2VolumeSize
      NetworkInterfaces:
        - DeviceIndex: 0
          SubnetId: {"Fn::ImportValue": !Sub "${StackVPC}::SubnetPublic1"}
          GroupSet:
            - !Ref SecurityGroupVyOS
      SourceDestCheck: false
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-${Environment}-router"
        - Key: System
          Value: !Ref SystemName
        - Key: Environment
          Value: !Ref Environment

  EIPVyOS:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc

  EIPAssociationVyOS:
    Type: AWS::EC2::EIPAssociation
    Properties:
      AllocationId: !GetAtt EIPVyOS.AllocationId
      InstanceId: !Ref EC2InstanceVyOS

Outputs:
  SecurityGroupVyOS:
    Value: !Ref SecurityGroupVyOS
    Export:
      Name: !Sub "${AWS::StackName}::SecurityGroupVyOS"

  EC2InstanceVyOS:
    Value: !Ref EC2InstanceVyOS
    Export:
      Name: !Sub "${AWS::StackName}::EC2InstanceVyOS"

  EC2InstanceVyOSPublicIP:
    Value: !GetAtt EC2InstanceVyOS.PublicIp
    Export:
      Name: !Sub "${AWS::StackName}::EC2InstanceVyOSPublicIP"

  EC2InstanceVyOSPrivateIP:
    Value: !GetAtt EC2InstanceVyOS.PrivateIp
    Export:
      Name: !Sub "${AWS::StackName}::EC2InstanceVyOSPrivateIP"

パラメータは以下のように指定してください。

パラメータ名 設定値
IPAddressAllowSSH SSH接続元IPアドレス (例:11.22.33.44/32)
EC2KeyName (任意のキーペア)
StackVPC 「1. ベース環境の構築」で作成したCloudFormationスタック名

マネジメントコンソールで作成する場合は、以下の点に留意してください。

  • AMIは、キーワード「VyOS」で検索してヒットした中から「AWS Marketplace」のものを選択
  • セキュリティグループは以下のインバウンドルールを設定
    • すべてのトラフィック: シンガポールリージョンのプライベートサブネット (192.168.128.0/24)
    • SSH: マイIP
  • インスタンス作成後、「送信元/送信先チェック」を「無効」に設定

3. VPN関連リソースの作成

東京リージョンで、VPN関連のAWSリソースを設定していきます。

(1) カスタマーゲートウェイを作成する

マネジメントコンソールで [VPC]-[カスタマーゲートウェイ] を開きます。
[カスタマーゲートウェイの作成] をクリックします。

項目 設定値
名前 任意の名前 (例:tokyo-dev-cgw)
ルーティング 「静的」
IPアドレス 作成したVyOSインスタンスのパブリックIPアドレス

(「Certificate ARN」「Device」は省略します)

(2) 仮想プライベートゲートウェイを作成して、VPCにアタッチする

マネジメントコンソールで [VPC]-[仮想プライベートゲートウェイ] を開きます。
[仮想プライベートゲートウェイの作成] をクリックします。

項目 設定値
名前 任意の名前 (例:tokyo-dev-vgw)
ASN 「AmazonのデフォルトASN」

作成した仮想プライベートゲートウェイを選択して、[アクション]-[VPCにアタッチ]を選択します。

「tokyo-dev-vpc」を選択して「はい、アタッチします」を選択します。

(3) VPN接続を作成する

マネジメントコンソールで [VPC]-[サイト間のVPN接続] を開きます。
[VPN接続の作成] をクリックします。

項目 設定値
名前タグ 任意の名前 (例:tokyo-dev-vpn)
Target Gateway Type 「Virtual Private Gateway」
仮想プライベートゲートウェイ 作成した仮想プライベートゲートウェイ (tokyo-dev-vgw)
カスタマーゲートウェイ 「既存」
Customer Gateway ID 作成したカスタマーゲートウェイ (tokyo-dev-cgw)
ルーティングオプション 「静的」
静的IPプレフィックス 192.168.128.0/24

(「トンネルオプション」は全てデフォルトのままにします)

作成直後は「状態」が「保留中」になります。
状態が「使用可能」になるまで待ちます。(数分間)

(4) ルートテーブルでルート伝播を有効にする

マネジメントコンソールで [VPC]-[ルートテーブル] を開きます。
プライベートサブネットのルートグループ「tokyo-dev-private-rtb」を選択します。
[ルート伝播] タブを開いて、[ルート伝達の編集] をクリックします。

仮想プライベートゲートウェイ「tokyo-dev-vgw」の [伝播] にチェックを入れて、[保存] をクリックします。

「伝播」欄が「はい」になったことを確認します。

なお、この時点では、実際のルート追加はまだ行われません。

4. VyOSの設定

シンガポールリージョンで、VyOSインスタンスにルーターの設定を行っていきます。

(1) ルーター設定ファイルのテンプレートをダウンロードする

ルーターの設定を行う前に、東京リージョン側で「サイト間VPN接続」画面で [設定のダウンロード] をクリックして、ルーター設定ファイルのテンプレート をダウンロードします。

ここが今回の作業のポイントの一つです。

動的ルーティング (BGPあり) のVPN接続を作成した場合、ここで「ベンダー」に「Vyatta」を選択してVyOS向けの設定ファイルをダウンロードすることができました。(VyattaはVyOSの前身となるソフトウェア)

ところが、静的ルーティングのVPN接続の場合は、同じ画面で「Vyatta」が選択肢に現れないため、ダウンロードすることができません。

そのため、ここでは「Generic」を選択して汎用の設定ファイルを入手することにします。
(汎用と言っても、ダウンロードした設定ファイルが各ベンダーのルーターにそのまま投入できる訳ではなく、設定に必要なパラメーターが列挙されたファイルになっています)

(2) VyOSの設定ファイルを準備する

VyOS向けの設定ファイルをダウンロードすることができませんので、一から記述することにします。

ただし、大部分の設定内容は動的ルーティングの場合と同じですので、動的ルーティング用Vyatta向け設定ファイルを基に記述することができます。

今回、以下のような 静的ルーティング用VyOS向け設定ファイルのテンプレート を用意しました。

# Site-to-Site VPN Configuration for VyOS (v1.2) using static routing

# IKE Configuration
set vpn ipsec ike-group AWS lifetime '28800'
set vpn ipsec ike-group AWS proposal 1 dh-group '2'
set vpn ipsec ike-group AWS proposal 1 encryption 'aes128'
set vpn ipsec ike-group AWS proposal 1 hash 'sha1'
set vpn ipsec ike-group AWS dead-peer-detection action 'restart'
set vpn ipsec ike-group AWS dead-peer-detection interval '15'
set vpn ipsec ike-group AWS dead-peer-detection timeout '30'

# ESP Configuration
set vpn ipsec esp-group AWS compression 'disable'
set vpn ipsec esp-group AWS lifetime '3600'
set vpn ipsec esp-group AWS mode 'tunnel'
set vpn ipsec esp-group AWS pfs 'enable'
set vpn ipsec esp-group AWS proposal 1 encryption 'aes128'
set vpn ipsec esp-group AWS proposal 1 hash 'sha1'

# WAN Interface Configuration
set vpn ipsec ipsec-interfaces interface 'eth0'

# Tunnel Interface Configuration
# (Tunnel #1)
set interfaces vti vti0 address '<<TUNNEL1_LOCAL_INTERNAL_IP_ADDRESS>>/30'
set interfaces vti vti0 description 'VPC tunnel 1'
set interfaces vti vti0 mtu '1436'
# (Tunnel #2)
set interfaces vti vti1 address '<<TUNNEL2_LOCAL_INTERNAL_IP_ADDRESS>>/30'
set interfaces vti vti1 description 'VPC tunnel 2'
set interfaces vti vti1 mtu '1436'

# Tunnel IPsec Configuration
# (Tunnel #1)
set vpn ipsec site-to-site peer <<TUNNEL1_REMOTE_EXTERNAL_IP_ADDRESS>> authentication mode 'pre-shared-secret'
set vpn ipsec site-to-site peer <<TUNNEL1_REMOTE_EXTERNAL_IP_ADDRESS>> authentication pre-shared-secret '<<TUNNEL1_PRE_SHARED_KEY>>'
set vpn ipsec site-to-site peer <<TUNNEL1_REMOTE_EXTERNAL_IP_ADDRESS>> description 'VPC tunnel 1'
set vpn ipsec site-to-site peer <<TUNNEL1_REMOTE_EXTERNAL_IP_ADDRESS>> ike-group 'AWS'
set vpn ipsec site-to-site peer <<TUNNEL1_REMOTE_EXTERNAL_IP_ADDRESS>> local-address '<<ROUTER_LAN_IP_ADDRESS>>'
set vpn ipsec site-to-site peer <<TUNNEL1_REMOTE_EXTERNAL_IP_ADDRESS>> vti bind 'vti0'
set vpn ipsec site-to-site peer <<TUNNEL1_REMOTE_EXTERNAL_IP_ADDRESS>> vti esp-group 'AWS'
# (Tunnel #2)
set vpn ipsec site-to-site peer <<TUNNEL2_REMOTE_EXTERNAL_IP_ADDRESS>> authentication mode 'pre-shared-secret'
set vpn ipsec site-to-site peer <<TUNNEL2_REMOTE_EXTERNAL_IP_ADDRESS>> authentication pre-shared-secret '<<TUNNEL2_PRE_SHARED_KEY>>'
set vpn ipsec site-to-site peer <<TUNNEL2_REMOTE_EXTERNAL_IP_ADDRESS>> description 'VPC tunnel 2'
set vpn ipsec site-to-site peer <<TUNNEL2_REMOTE_EXTERNAL_IP_ADDRESS>> ike-group 'AWS'
set vpn ipsec site-to-site peer <<TUNNEL2_REMOTE_EXTERNAL_IP_ADDRESS>> local-address '<<ROUTER_LAN_IP_ADDRESS>>'
set vpn ipsec site-to-site peer <<TUNNEL2_REMOTE_EXTERNAL_IP_ADDRESS>> vti bind 'vti1'
set vpn ipsec site-to-site peer <<TUNNEL2_REMOTE_EXTERNAL_IP_ADDRESS>> vti esp-group 'AWS'

# Static Routing Configuration
# (Tunnel #1)
set protocols static route <<REMOTE_NETWORK_CIDR>> next-hop <<TUNNEL1_REMOTE_INTERNAL_IP_ADDRESS>>
# (Tunnel #2)
set protocols static route <<REMOTE_NETWORK_CIDR>> next-hop <<TUNNEL2_REMOTE_INTERNAL_IP_ADDRESS>>

<<プレースホルダ名>> の部分を、下表に準じて値を当てはめていくことで設定ファイルを作成します。

ルーター全体の設定

マネジメントコンソールで各値を確認して、該当する項目の値を当てはめます。

プレースホルダ項目 設定値
ROUTER_LAN_IP_ADDRESS VyOSインスタンスのプライベートIPアドレス
LOCAL_NETWORK_CIDR 192.168.128.0/24
(シンガポール側のプライベートサブネットCIDR)
REMOTE_NETWORK_CIDR 10.0.128.0/24
(東京側のプライベートサブネットCIDR)
Tunnel #1 の設定

ダウンロードした汎用設定ファイルの「IPSec Tunnel #1」セクションを参照して、該当する項目の値を当てはめます。

プレースホルダ項目 参照する値
TUNNEL1_PRE_SHARED_KEY #1: Internet Key Exchange Configuration
 > Pre-Shared Key
TUNNEL1_LOCAL_INTERNAL_IP_ADDRESS #3: Tunnel Interface Configuration
 > Inside IP Addresses
  > Customer Gateway
TUNNEL1_REMOTE_INTERNAL_IP_ADDRESS #3: Tunnel Interface Configuration
 > Inside IP Addresses
  > Virtual Private Gateway
TUNNEL1_REMOTE_EXTERNAL_IP_ADDRESS #3: Tunnel Interface Configuration
 > Outside IP Addresses
  > Virtual Private Gateway

以下は、ダウンロードした汎用設定ファイルの抜粋です。

IPSec Tunnel #1
================================================================================
#1: Internet Key Exchange Configuration
(中略)
  - IKE version              : IKEv1 
  - Authentication Method    : Pre-Shared Key 
  - Pre-Shared Key           : XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
  - Authentication Algorithm : sha1
  - Encryption Algorithm     : aes-128-cbc
  - Lifetime                 : 28800 seconds
  - Phase 1 Negotiation Mode : main
  - Diffie-Hellman           : Group 2

(中略)

#3: Tunnel Interface Configuration
(中略)
Outside IP Addresses:
  - Customer Gateway                    : xx.xx.xx.xx
  - Virtual Private Gateway             : xx.xx.xx.xx

Inside IP Addresses
  - Customer Gateway                    : 169.254.xx.xx/30
  - Virtual Private Gateway             : 169.254.xx.xx/30

(後略)
Tunnel #2 の設定

「Tunnel #1」と同様にして、ダウンロードした汎用設定ファイルの「IPSec Tunnel #2」セクションを参照して、該当する項目の値を当てはめます。

プレースホルダ項目 参照する値
TUNNEL2_PRE_SHARED_KEY #1: Internet Key Exchange Configuration
 > Pre-Shared Key
TUNNEL2_LOCAL_INTERNAL_IP_ADDRESS #3: Tunnel Interface Configuration
 > Inside IP Addresses
  > Customer Gateway
TUNNEL2_REMOTE_INTERNAL_IP_ADDRESS #3: Tunnel Interface Configuration
 > Inside IP Addresses
  > Virtual Private Gateway
TUNNEL2_REMOTE_EXTERNAL_IP_ADDRESS #3: Tunnel Interface Configuration
 > Outside IP Addresses
  > Virtual Private Gateway

(3) VyOSに設定ファイルを投入する

VyOSインスタンスにSSHで接続します。(ユーザー名:vyos)

設定モードに遷移します。

$ configure

プロンプトが「#」に変わります。

設定ファイルの内容をコピー&ペーストします。(コメント行も含めて丸ごとコピペして構いません)

# # Site-to-Site VPN Configuration for VyOS (v1.2) using static routing
#
# # IKE Configuration
# set vpn ipsec ike-group AWS lifetime '28800'
# set vpn ipsec ike-group AWS proposal 1 dh-group '2'
# set vpn ipsec ike-group AWS proposal 1 encryption 'aes128'
# set vpn ipsec ike-group AWS proposal 1 hash 'sha1'

(中略)

# # Static Routing Configuration
# # (Tunnel #1)
# set protocols static route <<REMOTE_NETWORK_CIDR>> next-hop <<TUNNEL1_REMOTE_INTERNAL_IP_ADDRESS>>
# # (Tunnel #2)
# set protocols static route <<REMOTE_NETWORK_CIDR>> next-hop <<TUNNEL2_REMOTE_INTERNAL_IP_ADDRESS>>

設定をコミットします。

# commit

コミット結果がエラーとならないことを確認して、設定を保存します。

# save

設定モードから抜けます。

# exit

(4) VyOS側でIPsec状態を確認する

参照モード (プロンプトが「$」) で以下のコマンドを実行します。

$ show vpn ike sa
Peer ID / IP                            Local ID / IP
------------                            -------------
XX.XX.XX.XX                             192.168.0.XX

    Description: VPC tunnel 1

    State  IKEVer  Encrypt  Hash    D-H Group      NAT-T  A-Time  L-Time
    -----  ------  -------  ----    ---------      -----  ------  ------
    up     IKEv1   aes128   sha1_96 2(MODP_1024)   no     3600    28800


Peer ID / IP                            Local ID / IP
------------                            -------------
XX.XX.XX.XX                             192.168.0.XX

    Description: VPC tunnel 2

    State  IKEVer  Encrypt  Hash    D-H Group      NAT-T  A-Time  L-Time
    -----  ------  -------  ----    ---------      -----  ------  ------
    up     IKEv1   aes128   sha1_96 2(MODP_1024)   no     3600    28800
$ show vpn ipsec sa
Connection                    State    Up           Bytes In/Out    Remote address    Remote ID    Proposal
----------------------------  -------  -----------  --------------  ----------------  -----------  ------------------------------------------------
peer-XX.XX.XX.XX-tunnel-vti   up       118 seconds  0B/0B           XX.XX.XX.XX       N/A          AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024
peer-XX.XX.XX.XX-tunnel-vti   up       118 seconds  0B/0B           XX.XX.XX.XX       N/A          AES_CBC_128/HMAC_SHA1_96/PRF_HMAC_SHA1/MODP_1024

いずれのコマンドも「State」欄が「up」になっていることを確認します。

(5) VyOSインスタンスを参照するようにルートテーブルを設定

マネジメントコンソールで [EC2]-[インスタンス] を開きます。
VyOSのインスタンス「singapore-dev-router」を選択して、ENIのIDを確認します。

[VPC]-[ルーティングテーブル] を開きます。
プライベートサブネットのルートテーブル「singapore-dev-private-rbt」を選択します。
[ルート] タブを開いて、[ルートの編集] をクリックします。

[ルートの追加] をクリックして、以下のルートを追加します。

項目 設定値
送信先 10.0.128.0/24 (東京側のプライベートサブネットCIDR)
ターゲット 上で確認した「VyOSインスタンスのENI ID」

[ルートの保存] をクリックして設定を保存します。

5. 仮想プライベートゲートウェイ側の確認

東京リージョンで、仮想プライベートゲートウェイとルートテーブルの状態を確認します。

(1) VPN接続のトンネルが「アップ」になっていることを確認

マネジメントコンソールで [VPC]-[サイト間のVPN接続] を開きます。
[tokyo-dev-vpn] を選択して、[Tunnel Details] タブを開きます。

[Tunnel State] で各トンネルの「スタータス」欄が「アップ」になっていることを確認します。

(2) ルートテーブルにルートが伝播していることを確認

マネジメントコンソールで [VPC]-[ルートテーブル] を開きます。
プライベートサブネットのルートグループ「tokyo-dev-private-rtb」を選択します。
[ルート] タブを開きます。

以下のルートが追加されていることを確認します。

  • 送信先: 192.168.128.0/24
  • ターゲット: 仮想プライベートゲートウェイ
  • ステータス: active
  • 伝播済み: はい

6. 疎通確認を行う

まず、東京リージョン・シンガポールリージョンの各サーバー間でICMPによる疎通確認を行います。
各サーバーのプライベートIPアドレスを確認して、pingを実行しましょう。

(サーバーへはSystems Managerのセッションマネージャーで接続することができます)

東京 → シンガポール

sh-4.2$ ping 192.168.128.151
PING 192.168.128.151 (192.168.128.151) 56(84) bytes of data.
64 bytes from 192.168.128.151: icmp_seq=1 ttl=254 time=71.9 ms
64 bytes from 192.168.128.151: icmp_seq=2 ttl=254 time=71.9 ms
64 bytes from 192.168.128.151: icmp_seq=3 ttl=254 time=71.8 ms
64 bytes from 192.168.128.151: icmp_seq=4 ttl=254 time=71.9 ms
64 bytes from 192.168.128.151: icmp_seq=5 ttl=254 time=72.0 ms
・・・

シンガポール → 東京

sh-4.2$ ping 10.0.128.50
PING 10.0.128.50 (10.0.128.50) 56(84) bytes of data.
64 bytes from 10.0.128.50: icmp_seq=1 ttl=253 time=72.1 ms
64 bytes from 10.0.128.50: icmp_seq=2 ttl=253 time=72.0 ms
64 bytes from 10.0.128.50: icmp_seq=3 ttl=253 time=71.9 ms
64 bytes from 10.0.128.50: icmp_seq=4 ttl=253 time=72.0 ms
64 bytes from 10.0.128.50: icmp_seq=5 ttl=253 time=72.0 ms
・・・

続いて、各サーバー間でアプリケーションによる通信確認を行います。
ここでは、curl コマンドにより双方にHTTPリクエストを行ってみます。

東京 → シンガポール

sh-4.2$ curl http://192.168.128.151
Hello! I'm SINGAPORE Web Server!

シンガポール → 東京

sh-4.2$ curl http://10.0.128.50
Hello! I'm TOKYO Web Server!

正常に通信が行えました!

※ IPsecの状態が正常であることを確認している場合、サーバー間の通信がうまくいかないのは「ルーティングの設定」「セキュリティグループの設定」である可能性が大です。各設定を見直してみてください。

おわりに

ルーターが冗長化されていなかったり、ネットワーク構成がそれほど複雑でない場合は、静的ルーティングで運用している場合も多いかと思います。

そのような環境で検証を行う際の参考になれば幸いです。