SoftEther VPN Server 環境に bat ファイルでリモートデスクトップ接続を自動化

# PowerShell で SoftEther VPN 環境に自動接続
Windows 標準の VPN 接続を利用して、SoftEther VPN Server に接続し、リモートデスクトップ接続でテレワークを実現する際に、接続情報をできるだけ自動化してみる。

2023/02/03
WoL でシャットダウン中の端末を起動するコードを追加したので、追記しました。
## 前提条件
* 接続する先の環境は YAMAHA RTX810 が動作しており NAT 設定済みのため、最低でも同様の環境が必要。
* 常時稼働している SoftEther VPN Server が必要。今回は NAS 上の Windows Server 環境にて稼働している。
* LAN 内にて稼働している Windows 10 もしくは Windows 11 があり、かつリモートデスクトップ接続に対応している必要がある。
* ~~リモートデスクトップ接続する端末は電源が ON の状態が必要。~~
* WoL に対応するには端末に事前の設定が必要。
## SoftEther VPN Server の準備
インストール方法等に関しては省く。適当に探してください。
SoftEther VPN サーバー管理マネージャを利用して作成済みの SoftEther VPN Server に接続する。
まず仮想 HUB を2つ作る。元から存在している DEFAULT の仮想 HUB は残しておいた方が良いとのこと。外部からのアタック接続があった場合に DEFAULT の仮想 HUB が接続に対応し、その先への接続を回避するセキュリティ対策になるらしい。

仮想 HUB 名は何でもいい。ひとつは外部から接続があった際に DHCP で IP アドレスを割り当てるための仮想 HUB として利用する。仮に「VPN」としておく。
![スクリーンショット 2022-09-21 161010.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12937/7d3a3938-ecf9-5cbd-a1ef-5208a9a23dda.jpeg)
次に仮想 HUB 「VPN」に DHCP サーバーの設定をしておく。VPN の管理画面から「仮想 NAT および仮想 DHCP サーバー機能」をクリックする。
![スクリーンショット 2022-09-21 161100.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12937/e31c0a15-ed66-41fd-d3b4-72b06ae24e11.jpeg)
仮想 NAT の設定画面から「SecureNAT の設定」をクリックする。
![スクリーンショット 2022-09-21 161117.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12937/73254598-f340-4cc5-d8e6-cd31c458afc7.jpeg)
SecureNAT の設定画面にて設定を行っていくが、今回はそのままデフォルトで実施しておく。「仮想 NAT 機能を使用する」および「仮想 DHCP サーバー機能を使用する」のチェックボックスが有効になっていることを確認しておく。デフォルトの場合おそらく配布 IP アドレス帯は 192.168.30.10 から 192.168.30.200 までになっているかと思う。
![スクリーンショット 2022-09-21 161201.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12937/df98780c-bff4-0f43-3612-6caefe3c9204.jpeg)
SecureNAT が設定できたら「ユーザーの管理」から VPN 接続してくるユーザーを作成しておく。
![スクリーンショット 2022-09-21 161249.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12937/da1faa3e-a4ee-91fa-2fe4-b1f207892d3b.jpeg)
「新規作成」をクリックし、必要な数のユーザーを作成する。今回は 1 名のみ作成した。
![スクリーンショット 2022-09-21 161316.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12937/58c6e05d-c6be-c247-33b6-8093a31789b0.jpeg)
もうひとつは物理的な LAN 環境に接続するためのブリッジとして作成する。仮に「bridge」としておく。こちらは特に設定はない。
![スクリーンショット 2022-09-21 161426.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12937/40e34872-5230-91d5-145d-b6da876f2a4c.jpeg)
仮想 HUB 「bridge」が作成できたらサーバー管理マネージャから「ローカルブリッジ設定」をクリックし物理 LAN カードとの接続を行っておく。
![スクリーンショット 2022-09-21 165613.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12937/251c69a6-f061-1ace-364e-1258c91c32ae.jpeg)
仮想 HUB 「bridge」を選択し、適切な LAN カードを選択したら「ローカルブリッジを追加」をクリックしてローカルブリッジを追加しておく。ローカルブリッジを設定する物理的な LAN カードは、専用の物を用意した方がよいそうだ。

次に Windows 標準の VPN 接続もしくはスマホ等からのアクセスも可能とするため、「IPsec / L2TP 設定」をクリックする。
「L2TP サーバー機能を有効にする」のチェックボックスを有効にし、「IPsec 事前共有鍵」を適切に設定する。
![スクリーンショット 2022-09-21 161504.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12937/f64a6fc7-f7ff-9205-6185-933456dd6675.jpeg)
次に「レイヤ 3 スイッチ設定」をクリックし、レイヤ 3 スイッチを作成する。
![スクリーンショット 2022-09-21 161531.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12937/f403ddea-c923-1f17-9fc6-1cda3097c228.jpeg)
「新規作成」をクリックし、名前を指定する。今回は「switch」とした。
![スクリーンショット 2022-09-21 161556.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12937/e89fb327-ab67-edc1-b1be-fb07ee9b6a96.jpeg)
作成した仮想レイヤ 3 スイッチに「仮想インターフェイスの追加」で 2 つの仮想インターフェイスを追加する。
![スクリーンショット 2022-09-21 161636.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12937/918bb4df-c446-a033-1857-65de113c22fe.jpeg)
まずは仮想 HUB を「VPN」とした仮想インターフェイスを追加する。仮想インターフェイスの持つ IP アドレスを「192.168.30.254」とした。これは DHCP が配布する IP アドレス帯が 200 までであることを考慮している。
![スクリーンショット 2022-09-21 161852.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12937/f695a875-1b76-1c9c-3dd1-aba6d4f437e8.jpeg)
同様に仮想 HUB を「bridge」とした仮想インターフェイスを追加する。仮想インターフェイスの持つ IP アドレスを「192.168.0.254」とした。これは実際に稼働している物理 LAN にて割り振れる適切な IP アドレスを付与する必要がある。
![スクリーンショット 2022-09-21 161935.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12937/96f0e44a-1fef-dfc9-dd9d-8d42c31a9438.jpeg)
これでレイヤ 3 スイッチを動作開始とすれば稼働を開始するはずである。
試しにコマンドラインから「192.168.0.254」に ping を打ってみるとよい。戻りがなかったら何らかの設定を見直す必要がある。物理 LAN のルーター設定も疑ってみるとよい。

なお、この時点で「VPN」仮想 HUB を経由した SoftEther VPN Server にローカルから VPN 接続が可能なはず。
Windows の「設定」から「ネットワークとインターネット」を開いて「VPN」メニューから「VPN 接続を追加する」をクリックする。
![スクリーンショット 2022-09-22 110842.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12937/84bd7efd-bc86-e839-2211-3b65a2160c02.jpeg)
VPN プロバイダーは「Windows（ビルトイン）」、接続名は適当に分かりやすい名前を付ければよい。サーバー名またはアドレスに SoftEther VPN Server が動作しているサーバー名もしくは IP アドレスを入力し、VPN の種類は「事前共有キーを使った L2TP/IPsec」を選択する。事前共有キーは L2TP/IPsec 設定に入力した物を間違えないように入力しよう。
![スクリーンショット 2022-09-22 111404.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12937/22f83294-bda0-5265-cfb8-8022bdcbfc7c.jpeg)
サインイン情報の種類は「ユーザー名とパスワード」とし、ユーザー名（オプション）欄には、ユーザーの接続用に作成した「仮想 HUB 名\ユーザー名」とすることを間違えないように。パスワード（オプション）も仮想 HUB に作成したユーザーのパスワードだ。

保存をクリックしたら実際に接続してみる。
![スクリーンショット 2022-09-22 111251.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12937/182edce3-2c75-69d6-bf96-5845a1f25343.jpeg)
無事に接続済みとなれば最初の準備は完了だ。
## ルーターにルーティングと NAT を設定する
次はルーター側の設定となる。おそらく現在の状態ではコマンドラインから「192.168.30.1」に ping を打っても返ってはこないはずだ。
設定はルーターの種類ごとに違うため、ここでは RTX810 に設定した内容だけ記載する。
```config
ip route 192.168.30.0/24 gateway 192.168.0.254
```
「192.168.30.*」のネットワークアドレスに向かうパケットを仮想インターフェイスの「bridge」として設定している「192.168.0.254」をゲートウェイとして設定している。
```config
ip filter 200080 pass * 192.168.0.100 udp * 500
ip filter 200081 pass * 192.168.0.100 esp * *
ip filter 200082 pass * 192.168.0.100 udp * 4500
```
UDP 500 および 4500 、esp へのパケットを SoftEther VPN Server が動作しているサーバーに通過させている。
```config
nat descriptor masquerade static 1000 1 192.168.0.100 udp 500
nat descriptor masquerade static 1000 2 192.168.0.100 esp
nat descriptor masquerade static 1000 3 192.168.0.100 udp 4500
```
同様に NAT の設定でも UDP 500 および 4500 、esp へのパケットを SoftEther VPN Server が動作しているサーバーに向けている。

ルーティングとしてはコマンドラインから「192.168.30.1」へ ping が通れば問題ないと思われる。ルーターのフィルタや NAT 設定は適切に読み替えてもらいたい。
## PowerShell で VPN 接続とリモートデスクトップ接続を自動化
実際に外部から接続してくるユーザーにはできるだけ簡易に接続を実施してもらいたい。パソコン等にそれほど詳しくないユーザーに VPN 接続を作成して…を説明するのは難しいとも思う。
また、個人所有の端末を使っていたりした場合、VPN 接続の設定を残してしまうのも気がかりだ。おそらく設定が残っていることを気が付かないまま過ごしてしまうような気がしてならない。

そこで、VPN の接続およびリモートデスクトップ接続までを自動化、1 スクリプトとして作成し、切断後はスクリプト以外残らないように工夫してみた。
VPN 接続はユーザーごとに作成しているため、仮想 HUB からユーザーを削除してしまえば接続はできなくなる。まあ、L2TP 用のパスワードがそのまま記載されているので、完全に安全と言うわけではない。性善説に則って使用することになることから自己責任にて運用されて頂きたい。

実際のスクリプトは以下に置いておく。RDP ファイルに関しては事前に用意しておけば良いため、ここではすべて説明はしない。

https://github.com/shokun0803/vpn_access/blob/main/vpn_access.bat

実際のファイルはバッチファイルとして作成されている。 ps1 拡張子だとダブルクリックで実行してもらえないため、 bat ファイルとして作成した。最初の 1 行目はバッチファイルを PowerShell スクリプトとして実行するための記載である。

```PowerShell
# VPN サーバー接続情報設定
$server = 'VPN'
$ServerAddress = '000.000.000.000'
$hub = 'DEFAULT'
$L2tpPsk = 'password'
```
最初の部分は Windows の VPN 接続を作成するための情報を記載する。先ほどテストで VPN 接続を作って試しているはずであるので、難しくはないと思われる。
`$server`は VPN 接続設定の名前なので分かりやすい名前を付ければ問題ない。
`$ServerAddress`は外部からルーターにアクセスできるグローバル IP アドレスが必要だ。固定 IP アドレスがなければ YAMAHA のルーターの場合、netvolante のアドレスも使える。
`$hub`は SoftEther VPN Server にユーザー接続用に作成した仮想 HUB の名前となる。今回この文章中では「VPS」として作成しているが、外部からのアタックを気にするのであれば適切な名前を付けて作成した方が良いだろう。
`$L2tpPsk`は SoftEther VPN Server の　L2TP 画面で設定したパスワードを記載する。ここがそのまま記載されていることから安全性という意味ではよろしくないため、スクリプトを間違って公開されないよう注意されたし。
```PowerShell
# VPN 接続情報設定
$user = 'username'
$pass = 'password'

$vpnUser = $hub + '\' + $user
```
この部分は SoftEther VPN Server の仮想 HUB に作成したユーザーの情報を記載する。そのため、ユーザーごとに設定が異なることに注意する。
もちろん情報がそのまま記載されていることには違いがないため、情報の公開には注意されたし。
`$vpnUser`の部分は先ほどテストで作成した VPN 接続の際にも注意として記載したが、VPN 接続時のユーザー名には「仮想 HUB 名\ユーザー名」が必要なため接続用ユーザー名を作成している。

**ここから WoL について追記しています（2023/02/03）**
```PowerShell
# WoL 設定情報
$macaddress = '00:00:00:00:00:00'
$broadcastaddress = '192.168.0.255'
$IPAddress = '192.168.0.10'
```
接続先の端末が起動していない場合に WoL （ Wake On LAN ）機能を使って電源を ON にする試みをしている。
`$macaddress`部分には起動したい端末の MacAddress を記載する。 WoL で MagicPacket を投げるには MacAddress が必要なので調べて記載しよう。
`$broadcastaddress`には起動したい端末の所属している LAN 環境のブロードキャストアドレスを記載しておく。 VPN 接続した場合、例えばこの手順の通りに行っていれば`192.168.30.*`の IP アドレス帯が付与されているはずである。接続先の端末の IP アドレスが仮に`192.168.0.10`だったとするとブロードキャストアドレスは`192.168.0.255`になるはずだ。
`$IPAddress`には起動する端末の固定 IP アドレスを記載する。 WoL を実行した際にちゃんと起動しているか`ping`を投げて確認するために使用している。この IP アドレスはリモートデスクトップ接続を実施する際の接続先としても利用している。
**追記ここまで**
```diff_PowerShell
# リモートデスクトップ接続情報設定
$userName = 'user name'
- $pcName = '192.168.0.10'
```
次の部分はリモートデスクトップ接続を自動化するための、ユーザー個人端末の情報となる。
`$userName`は Windows にサインインするためのユーザー名となる。~~り、`$pcName`は固定で割り振られた IP アドレスもしくはコンピューター名でも可能なはずだ。~~
```PowerShell
# VPN 接続を確認
$vpn = Get-VpnConnection

if( $vpn.Name -eq $server ) {
	# VPN 接続
	rasdial.exe $server $vpnUser $pass
} else {
	# Windows の VPN 接続を新規作成
	Add-VpnConnection -Name $server -ServerAddress $ServerAddress -RememberCredential -TunnelType L2TP -L2tpPsk $L2tpPsk -Force

	# VPN 接続
	rasdial.exe $server $vpnUser $pass
}

$A = ipconfig | Select-String $server

if($A.length -eq "0"){
	Write-Host "VPN 接続に失敗しました、終了します。"

	# VPN 接続を削除
	Remove-VpnConnection -Name $server -Force -PassThru
	exit
}
```
ここから実際に VPN 接続を開始することとなる。まずは`Get-VpnConnection`で作成されている VPN 接続の情報を取得する。このスクリプトでしか VPN 接続を行っていない場合、切断時に VPN 接続も削除してしまうため、`if( $vpn.Name -eq $server )`は true にならない。仮に設定が残っていた場合にはそのまま接続を試行する。

VPN 接続が存在していない場合、`Add-VpnConnection`で VPN 接続を作成し、接続を試行する。
なお、接続に失敗した場合を考えて`ipconfig`から接続されている VPN 名を探し出し、存在していなければ失敗したとして VPN 接続を削除、スクリプトを終了する。
**ここから WoL について追記しています（2023/02/03）**
```PowerShell
# 端末の起動状況を確認して起動
$MACWithHyphen = $macaddress.Replace(':', '-')
$rarp_result = Get-NetNeighbor -LinkLayerAddress $MACWithHyphen -AddressFamily IPv4 -ErrorAction Ignore
if( $rarp_result.IPAddress -ne $Null ) {
	$IPAddress = $rarp_result.IPAddress
}
#$IPAddress

try {
	Write-Output "端末の起動を確認します…"
	if( Test-Connection $IPAddress -Quie ) {
		Write-Output "端末はすでに起動しています。引き続きリモートデスクトップ接続を開始します。"
	} else {
		Write-Output "端末が起動していません。端末の起動を試みます。"
		throw
	}
}
```
VPN に接続後、リモートデスクトップ接続を実行したい端末が起動しているか確認をしている。コード内に IP アドレスも MacAddress も記載しているので本来必要ではないが、一応 RARP で IP アドレスの取得を試みている。 
PowerShell で`ping`を打つ方法はいくつかあるようだが、オーソドックスに`Test-Connection` を利用してみた。端末が起動していない場合`false`になるため`throw`で`catch`側に投げるようにしている。
```PowerShell
catch {
	$macAddr = [byte[]]($macaddress.split(":") | ForEach-Object{[Convert]::ToInt32($_, 16)})
	$magicPacket = ([byte[]](@(0xff)*6)) + $macAddr * 16
	$udpClient = new-object System.Net.Sockets.UdpClient
	$WakeUpTarget = $broadcastaddress
	$udpClient.Connect($WakeUpTarget, 9)
	$udpClient.Send($magicPacket, $magicPacket.Length)|out-null
	$udpClient.Close()
	Write-Output "Send MagicPacket."

	Write-Output "60秒間端末の起動を待機しています…";
	Start-Sleep -s 60

	try {
		Write-Output "端末の起動を確認します…"
		if( Test-Connection $IPAddress -Quie ) {
			Write-Output "端末を起動しました。引き続きリモートデスクトップ接続を開始します。"
		} else {
			Write-Output "端末が起動できません。もう一度実施して頂くか、端末がフリーズしていないか確認してください。"
			throw
		}
	}
	catch {
		Write-Output "スクリプトを終了します。"
		# VPN 切断
		rasdial.exe $server /disconnect
		Write-Host "VPN から切断しました。"

		# VPN 接続を削除
		Remove-VpnConnection -Name $server -Force -PassThru
		exit
	}
}
```
ここで MacAddress から MagicPacket を生成してブロードキャストアドレス宛てに投げている。詳細は記載しないので参考サイトを見てほしい。
MagicPacket を投げた後は端末が起動するのを 60 秒ほど待機し、再度`ping`を投げて起動の確認を行っている。ここで起動が確認できない場合は WoL に失敗したかそもそも端末がフリーズ等で応答していない可能性があるため、 VPN を切断し情報を削除してスクリプトを終了している。接続が確認できれば以降のリモートデスクトップ接続にはいる。
**追記ここまで**
```PowerShell
# リモートデスクトップ接続
$ScriptDir = Get-ChildItem env:SCRIPT_DIR
$rdpPass = Join-Path $ScriptDir.Value remoto.rdp

# リモートデスクトップ接続を開始
Start mstsc ./remoto.rdp
```
VPN 接続に成功したらリモートデスクトップ接続を開始するが、実際のスクリプトでは RDP ファイルを動的に生成している。ここではその中身までは省略する。
まずは現在動作しているスクリプトの存在しているディレクトリパスを取得しているのだが、`Get-ChildItem env:SCRIPT_DIR`としているのは、実際にこのスクリプトはバッチファイルとして動作しており、 PowerShell の環境変数が一部取得できないため、最初の 1 行目で設定している環境変数を利用してパスを取得している。

RDP ファイルとしては`remoto.rdp`として決め打ちしているので、何かのファイルと被ってしまうようなら適切に変更するとよい。
実際の接続としては`Start mstsc`としてリモートデスクトップ接続を呼び出し、 RDP ファイルを指定して接続を開始している。 RDP ファイル内にパスワードを含めることも可能ではあるが、ここはあえてパスワードを入力させることとした。仮に別人がスクリプトを入手したとしても、実際に端末へのログインまでは防ぐことができるだろうと思う（ネットワークには接続できているので適切にユーザー管理されていない共有フォルダ等は覗かれてしまうことに注意されたし）。
```PowerShell
# VPN 接続後の切断待機処理
Write-Host "VPN に接続中です。切断するには何かキーを押してください。"

Try
{
	Pause
	exit
}
Finally
{
	# VPN 切断
	rasdial.exe $server /disconnect
	Write-Host "VPN から切断しました。"

	# VPN 接続を削除
	Remove-VpnConnection -Name $server -Force -PassThru

	# RDP ファイルを削除
	Remove-Item -Path $rdpPass
}
```
最後に VPN 接続の切断処理を行っている。スクリプトが Ctrl + c で終了された場合でも切断処理が動作するよう`Try` `Finally`でスクリプト自体を停止状態としておき、スクリプト終了後に`rasdial.exe $server /disconnect`している。
さらに`Remove-VpnConnection`で VPN 接続設定そのものを削除し、最後に自動作成された RDP ファイルを`Remove-Item`として削除している。
RDP ファイルを別途用意し自動作成させない場合には`Remove-Item`は削除しておかないと意図せずファイルを消してしまうため注意が必要だ。

これで外部からでもルーターを経由して SoftEther VPN Server に VPN 接続し、そのままリモートデスクトップ接続が開始されるはすである。スクリプトを終了すると切断し、VPN 接続設定も削除、RDP ファイルまで削除してスクリプトだけが残るという事だ。
## 参考サイト
https://qiita.com/koichiro-h/items/9162990b0bd0ff4e6f1f