Site to Site VPN with RRAS

[FUJITSU Advent Calendar 2018](https://qiita.com/advent-calendar/2018/fujitsu) 7 日目の投稿です。

遅刻しました。
言い訳をすると、7 日は朝から頭痛が酷くてベッドから出られず。。。会社も休みました。
回復したらしたでスマブラを (以下略)

※本エントリは個人の見解であり、組織を代表するものではありません。

# 導入
例によって Azure のお話。
とは言っても今回は Azure からは微妙に離れます。

Azure の VNET 上のシステムと、オンプレあるいはプライベートクラウドをプライベート接続したい、とします。
方法はいくつかあるのですが、今回は Site to Site VPN (以下、S2S-VPN) 、要は IPSec VPN がいい、という場合にお役に立てれば幸いです。

通常というか一般的というか、IPSec したい、という場合に真っ先に思い浮かぶのが、VPN 機器の導入ですね。
しかしまぁ、これが結構なお値段するわけです。冗長化しようとすると単純計算で 2 倍のお金がかかりますし。
さらに、「誰が管理するの」「誰がメンテできるの」っていう運用面での課題も出てきますよね。

そんな時、なかなか高確率で挙がってくるワードが**RRAS**です。
Windows に精通している方はご存知かと思いますが、Windows Server に標準搭載されている VPN が使える機能の略称です。
正式名称:**Routing and Remote Access Service**

Azure VPN Gateway 側も RRAS をちゃんとサポートしているので、Windows Server のライセンスさえあれば実現できそう！って思うのも無理はありません。

検索すると似たようなエントリがたくさんありますが、如何せん情報が古いものが多い印象です。
これが本エントリを書いた理由です。

そんなわけで RRAS の設定やらなにやらをご紹介したいと思います。

# 対象読者
- Azure VPN Gateway と S2S-VPN (IPSec VPN) したい
- 対向は VPN 機器用意できないから RRAS で
- RRAS 構成スクリプト使うとエラーになるんだけど？
- RRAS 構成スクリプト使うと GUI 使えない！困る！

# 前提条件
- RRAS サーバーの OS は、現時点で最新版の Windows Server 2016 とする
- RRAS 側の構築が完了しているものとする (特に以下)
    - プライベート接続したいサブネット
    - RRAS の Global IP を確保済み
    - RRAS サーバ
        - OS が入った状態であること
        - RDP 接続できること
        - プライベートネットワークに接続されていること
        - パッチ当てが終わっていることが望ましい
- Azure 側の構築・設定が完了しているものとする (特に以下)
    - プライベート接続したいサブネット
    - VPN Gateway の Global IP
    - PSK

RRAS にフォーカスするので悪しからず。

# 1. 構成スクリプトの入手
RRAS の構築・設定は MS が公開しているスクリプト (PowerShell) を使います。
以下のページから Git に飛んでダウンロードしましょう。

[サイト間 VPN ゲートウェイ接続用の VPN デバイスと IPsec/IKE パラメーターについて](https://docs.microsoft.com/ja-jp/azure/vpn-gateway/vpn-gateway-about-vpn-devices)

「検証済みの VPN デバイスとデバイス構成ガイド」のテーブルの左端列「ベンダー名」に Microsoft とあります。
その行にある「[構成のサンプル](https://github.com/Azure/Azure-vpn-config-samples/tree/master/Microsoft)」をクリックします。

あれ？って思った方もいることでしょう。
Windows Server 2016 用のスクリプトはありません。
ここでは 2012R2 用のものをダウンロードしましょう。

# 2. 構成スクリプトの修正
ではダウンロードしたスクリプトを 2016 用にカスタマイズします。
このままでも動きますが、**GUIが使えなくなります**。

正直なところ GUI は使えなくても特別問題があるわけではありませんが、CUI で管理することに慣れていない方には抵抗があるかもしれません。

## XSLT を PS1 へ
そもそもなんで XSLT 形式にしたんだ、という話はありますが...。
「こうやってこう使え」的な文書も無いですし、さすがに不親切すぎるんじゃあないですかねぇ MS さん。

というわけで、XML タグや XSL タグを抹消し、PowerShell の変数としましょう。
もっとスマートな変換方法があれば教えてください。(土下座)
変数名はお好きに変えていただいて結構です。
保存するときは拡張子から`.xslt`を消すことも忘れずに。

完成版はいずれ Git に上げておきます。

1~5 行目

```powershell
# Before
<?xml version="1.0" encoding="utf-16"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:template match="/"># Microsoft Corporation
# Windows Azure Virtual Network
...

# After
# Microsoft Corporation
# Windows Azure Virtual Network
...
```

81 行目

```powershell
# Before
Add-VpnS2SInterface -Protocol IKEv2 -AuthenticationMethod PSKOnly -NumberOfTries 3 -ResponderAuthenticationMethod PSKOnly -Name <xsl:value-of select="/Data/SP_AzureGatewayIpAddress"/> -Destination <xsl:value-of select="/Data/SP_AzureGatewayIpAddress"/> -IPv4Subnet @(<xsl:for-each select="/Data/VnetSubnets/Subnet">"<xsl:value-of select="SP_NetworkCIDR"/>:<xsl:value-of select="/Data/SP_AzureNetworkMetric"/>"<xsl:if test="position() != last()">,</xsl:if></xsl:for-each>) -SharedSecret <xsl:value-of select="/Data/SP_PresharedKey"/>

# After
Add-VpnS2SInterface -Protocol IKEv2 -AuthenticationMethod PSKOnly -NumberOfTries 3 -ResponderAuthenticationMethod PSKOnly -Name $SP_AzureGatewayIpAddress -Destination $SP_AzureGatewayIpAddress -IPv4Subnet $SP_Subnet -SharedSecret $SP_PresharedKey
```

85 行目

```powershell
# Before
Set-VpnS2Sinterface -Name <xsl:value-of select="/Data/SP_AzureGatewayIpAddress"/> -InitiateConfigPayload $false -Force

# After
Set-VpnS2Sinterface -Name $SP_AzureGatewayIpAddress -InitiateConfigPayload $false -Force
```

88, 89 行目

```powershell
# Before
Set-PrivateProfileString $env:windir\System32\ras\router.pbk "<xsl:value-of select="/Data/SP_AzureGatewayIpAddress"/>" "IdleDisconnectSeconds" "0"
Set-PrivateProfileString $env:windir\System32\ras\router.pbk "<xsl:value-of select="/Data/SP_AzureGatewayIpAddress"/>" "RedialOnLinkFailure" "1"

# After
Set-PrivateProfileString $env:windir\System32\ras\router.pbk $SP_AzureGatewayIpAddress "IdleDisconnectSeconds" "0"
Set-PrivateProfileString $env:windir\System32\ras\router.pbk $SP_AzureGatewayIpAddress "RedialOnLinkFailure" "1"
```

95 行目

```powershell
# Before
Connect-VpnS2SInterface -Name <xsl:value-of select="/Data/SP_AzureGatewayIpAddress"/>

# After
Connect-VpnS2SInterface -Name $SP_AzureGatewayIpAddress
```

## 変数定義の追加
`xsl:value-of`をすべて抹消して、PowerShell の変数に変えたので、各々の変数を定義しましょう。
前項にてパラメータ名を任意のものに変えた方は読み替えてください。

先頭に追加

```powershell
$SP_AzureGatewayIpAddress = "xxx.xxx.xxx.xxx" # VPN GatewayのGlobal IP
$SP_Subnet = @("xxx.xxx.xxx.xxx/xx:xxx") # Azure 側のサブネット 配列であることに注意 @("xxx", "xxx", ...)
$SP_PresharedKey = "abcdefghijklmnopqrstuvwxyz" # VPN Gateway構築時に入力したPSKと同じもの
```

## テコ入れ
ここまでの修正をして、スクリプトを実行してもインストール、構成は可能です。
が、GUI を使いたい場合はこの修正もしましょう。

ついでに待ち時間(`sleep`)も入れておくと、エラーメッセージが出てこないので心臓にやさしいです。

60行目付近以降

```powershell
# Install RRAS role
Import-Module ServerManager
Install-WindowsFeature RemoteAccess -IncludeManagementTools
Add-WindowsFeature -name DirectAccess-VPN,Routing -IncludeManagementTools
sleep 10 # <- 追加

# Install S2S VPN
Import-Module RemoteAccess
if ((Get-RemoteAccess).VpnS2SStatus -ne "Installed")
{
  Install-RemoteAccess -VpnType VpnS2S -Legacy # <- '-Legacy'を追加
}
sleep 10 # <- 追加

# Add and configure S2S VPN interface
Add-VpnS2SInterface -Protocol IKEv2 -AuthenticationMethod PSKOnly -NumberOfTries 3 -ResponderAuthenticationMethod PSKOnly -Name $AzureGetewayIPAddress -Destination $AzureGetewayIPAddress -IPv4Subnet $Subnets -SharedSecret $PresharedKey
Set-VpnServerConfiguration -TunnelType IKEv2 -EncryptionType MaximumEncryption
Restart-Service RemoteAccess
sleep 10 # <- 追加
Set-VpnS2Sinterface -Name $AzureGetewayIPAddress -InitiateConfigPayload $false -Force

# Set S2S VPN connection to be persistent by editing the router.pbk file (required admin priveleges)
Set-PrivateProfileString $env:windir\System32\ras\router.pbk "$AzureGetewayIPAddress" "IdleDisconnectSeconds" "0"
Set-PrivateProfileString $env:windir\System32\ras\router.pbk "$AzureGetewayIPAddress" "RedialOnLinkFailure" "1"

# Restart the RRAS service
Restart-Service RemoteAccess
sleep 10 # <- 追加

# Dial-in to Azure gateway
Connect-VpnS2SInterface -Name $AzureGetewayIPAddress
```

これでスクリプトの修正は終わりです。
忘れずに保存しましょう。

# 3. 構成スクリプトの実行
あとは RRAS サーバでスクリプトを実行するだけです。
PowerShell 起動時は**管理者として実行**しましょう。

以上で RRAS の構築は完了です。

## 補足
手順通りに構築しても、「VPN は張れてるけど繋がらねぇ...」という場合、RRAS のサービスそのものが原因の可能性は低いです。
パケットキャプチャ等を駆使して原因を特定してください。

# おわりに
RRAS、意外と簡単だなぁというのが初めて構築したときの印象です。
IPSec VPN って聞くと「結構大変そう...」という印象なのですが、まぁやってみるもんですね～。

この RRAS のおかげで Azure と手軽に VPN 接続できます。
みなさまも一度お試しあれ。

ちなみに Azure VM として RRAS を建てるのは、Azure としてサポートしておりませんのでご注意ください。
[Microsoft Azure 仮想マシンのマイクロソフト サーバー ソフトウェアのサポート](https://support.microsoft.com/ja-jp/help/2721672/microsoft-server-software-support-for-microsoft-azure-virtual-machines)

それから、RRAS (Windows) じゃなくて Linux がいい、という場合でも接続できるので、こちらはまたいずれ。