[2019年度版] 自己署名証明書のRD GatewayでWindowsサーバに接続
しばたです。
2016年に弊社西澤によりシングルのRD Gateway環境を構築し自己証明書を使い接続する内容の記事が公開されましたが、諸事情により私も同様の環境を構築する必要があったためOSや作業手順を新たに更新してみました。
今回構築する環境
今回構築する環境は前回と同様にシングルのRD Gatewayサーバーと接続対象となるEC2インスタンス1台とします。
Active Directoryは使わないワークグループ環境、EC2インスタンスのOSはWindows Server 2019と最新のOSを採用します。
図としてはざっくり以下の様な感じです。
環境構築のためのCloudFormationテンプレート
今回の環境を作るためのCloudFormationテンプレートを公開します。
上図を構成する最低限の定義しかありませんのであくまでも参考情報とお考えください。
01. ネットワーク構成テンプレート
- VPC
- IGW
- Subnet
- NACL
- RouteTable
の定義が記述されています。
01. ネットワーク構成テンプレート
AWSTemplateFormatVersion: 2010-09-09
Parameters:
SystemName:
Description: "System name of each resource names."
Type: String
Default: "contoso"
EnvironmentName:
Description: "Environment name of each resource names."
Type: String
Default: "prd"
AZ1:
Description: "AZ1(1a)"
Type: AWS::EC2::AvailabilityZone::Name
Default: "ap-northeast-1a"
AZ2:
Description: "AZ2(1c)"
Type: AWS::EC2::AvailabilityZone::Name
Default: "ap-northeast-1c"
Outputs:
VPC1:
Value:
Ref: VPC1
Export:
Name:
Fn::Sub: "${SystemName}-${EnvironmentName}-vpc"
PublicSubnet1:
Value:
Ref: PublicSubnet1
Export:
Name:
Fn::Sub: "${SystemName}-${EnvironmentName}-public-subnet-a"
PublicSubnet2:
Value:
Ref: PublicSubnet2
Export:
Name:
Fn::Sub: "${SystemName}-${EnvironmentName}-public-subnet-c"
PrivateSubnet1:
Value:
Ref: PrivateSubnet1
Export:
Name:
Fn::Sub: "${SystemName}-${EnvironmentName}-private-subnet-a"
PrivateSubnet2:
Value:
Ref: PrivateSubnet2
Export:
Name:
Fn::Sub: "${SystemName}-${EnvironmentName}-private-subnet-c"
IGW1:
Value:
Ref: IGW1
Export:
Name:
Fn::Sub: "${SystemName}-${EnvironmentName}-vgw"
Resources:
# VPC
VPC1:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 192.168.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value:
Fn::Sub: "${SystemName}-${EnvironmentName}-vpc"
# Subnet
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone:
Ref: AZ1
VpcId:
Ref: VPC1
CidrBlock: 192.168.11.0/24
MapPublicIpOnLaunch: False
Tags:
- Key: Name
Value:
Fn::Sub: "${SystemName}-${EnvironmentName}-public-subnet-a"
PublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone:
Ref: AZ2
VpcId:
Ref: VPC1
CidrBlock: 192.168.12.0/24
MapPublicIpOnLaunch: False
Tags:
- Key: Name
Value:
Fn::Sub: "${SystemName}-${EnvironmentName}-public-subnet-c"
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone:
Ref: AZ1
VpcId:
Ref: VPC1
CidrBlock: 192.168.21.0/24
MapPublicIpOnLaunch: False
Tags:
- Key: Name
Value:
Fn::Sub: "${SystemName}-${EnvironmentName}-private-subnet-a"
PrivateSubnet2:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone:
Ref: AZ2
VpcId:
Ref: VPC1
CidrBlock: 192.168.22.0/24
MapPublicIpOnLaunch: False
Tags:
- Key: Name
Value:
Fn::Sub: "${SystemName}-${EnvironmentName}-private-subnet-c"
# NACL (Default only)
NACL1:
Type: AWS::EC2::NetworkAcl
Properties:
Tags:
- Key: Name
Value:
Fn::Sub: "${SystemName}-${EnvironmentName}-nacl"
VpcId:
Ref: VPC1
NACLEntry1:
Type: AWS::EC2::NetworkAclEntry
Properties:
Egress: true
CidrBlock: 0.0.0.0/0
Protocol: -1
RuleAction : allow
RuleNumber : 100
NetworkAclId:
Ref: NACL1
NACLEntry2:
Type: AWS::EC2::NetworkAclEntry
Properties:
Egress: false
CidrBlock: 0.0.0.0/0
Protocol: -1
RuleAction : allow
RuleNumber : 100
NetworkAclId:
Ref: NACL1
NACLAssociation11:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId:
Ref: PublicSubnet1
NetworkAclId:
Ref: NACL1
NACLAssociation12:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId:
Ref: PublicSubnet2
NetworkAclId:
Ref: NACL1
NACLAssociation21:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId:
Ref: PrivateSubnet1
NetworkAclId:
Ref: NACL1
NACLAssociation22:
Type: AWS::EC2::SubnetNetworkAclAssociation
Properties:
SubnetId:
Ref: PrivateSubnet2
NetworkAclId:
Ref: NACL1
# Public RouteTable (Default + IGW)
PublicRouteTable1:
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Ref: VPC1
Tags:
- Key: Name
Value:
Fn::Sub: "${SystemName}-${EnvironmentName}-public-rtb"
PublicRoute1:
Type: AWS::EC2::Route
Properties:
RouteTableId:
Ref: PublicRouteTable1
DestinationCidrBlock: 0.0.0.0/0
GatewayId:
Ref: IGW1
PublicRouteAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId:
Ref: PublicRouteTable1
SubnetId:
Ref: PublicSubnet1
PublicRouteAssociation2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId:
Ref: PublicRouteTable1
SubnetId:
Ref: PublicSubnet2
# Private RouteTable (Default)
PrivateRouteTable1:
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Ref: VPC1
Tags:
- Key: Name
Value:
Fn::Sub: "${SystemName}-${EnvironmentName}-private-rtb"
PrivateRouteAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId:
Ref: PrivateRouteTable1
SubnetId:
Ref: PrivateSubnet1
PrivateRouteAssociation2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId:
Ref: PrivateRouteTable1
SubnetId:
Ref: PrivateSubnet2
# Internet Gateway
IGW1:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value:
Fn::Sub: "${SystemName}-${EnvironmentName}-igw"
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId:
Ref: VPC1
InternetGatewayId:
Ref: IGW1
02. セキュリティグループ用テンプレート
- RD Gateway用にHTTPSを開けるグループ(
rdgateway-sg
) - RD Gateway <--> 接続対象サーバー間の通信用グループ (
rdsession-sg
) - 初期環境構築用にRDPポートを開放するグループ (
rdp-sg
)
が定義されています。
02. セキュリティグループ用テンプレート
AWSTemplateFormatVersion: 2010-09-09
Parameters:
SystemName:
Description: "System name of each resource names."
Type: String
Default: "contoso"
EnvironmentName:
Description: "Environment name of each resource names."
Type: String
Default: "prd"
Outputs:
RDGatewaySG:
Value:
Ref: RDGatewaySG
Export:
Name:
Fn::Sub: "${SystemName}-${EnvironmentName}-rdgateway-sg"
RDSessionSG:
Value:
Ref: RDSessionSG
Export:
Name:
Fn::Sub: "${SystemName}-${EnvironmentName}-rdsession-sg"
Resources:
# RDGateway
RDGatewaySG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName:
Fn::Sub: "${SystemName}-${EnvironmentName}-rdgateway-sg"
GroupDescription:
Fn::Sub: "${SystemName}-${EnvironmentName}-rdgateway-sg"
VpcId:
Fn::ImportValue:
Fn::Sub: "${SystemName}-${EnvironmentName}-vpc"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value:
Fn::Sub: "${SystemName}-${EnvironmentName}-rdgateway-sg"
# 接続対象サーバー用
RDSessionSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName:
Fn::Sub: "${SystemName}-${EnvironmentName}-rdsession-sg"
GroupDescription:
Fn::Sub: "${SystemName}-${EnvironmentName}-rdsession-sg"
VpcId:
Fn::ImportValue:
Fn::Sub: "${SystemName}-${EnvironmentName}-vpc"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3389
ToPort: 3389
SourceSecurityGroupId:
Ref: RDGatewaySG
- IpProtocol: udp
FromPort: 3389
ToPort: 3389
SourceSecurityGroupId:
Ref: RDGatewaySG
Tags:
- Key: Name
Value:
Fn::Sub: "${SystemName}-${EnvironmentName}-rdsession-sg"
# 環境構築時に一時的にRDPのポートを開くためのグループ
RDPSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName:
Fn::Sub: "${SystemName}-${EnvironmentName}-rdp-sg"
GroupDescription:
Fn::Sub: "${SystemName}-${EnvironmentName}-rdp-sg"
VpcId:
Fn::ImportValue:
Fn::Sub: "${SystemName}-${EnvironmentName}-vpc"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3389
ToPort: 3389
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value:
Fn::Sub: "${SystemName}-${EnvironmentName}-rdp-sg"
03. EC2インスタンス用テンプレート
- RD Gateway + EIP
- 接続対象サーバー
の定義が記述されています。
インスタンスタイプはt2.medium固定、EC2キーペアは事前に作成しておいてください。
どちらも最新の日本語版Windows Server 2019で、RD Gatewayの構築手順は次節で説明します。
03. EC2インスタンス用テンプレート
AWSTemplateFormatVersion: 2010-09-09
Parameters:
SystemName:
Description: "System name of each resource names."
Type: String
Default: "contoso"
EnvironmentName:
Description: "Environment name of each resource names."
Type: String
Default: "prd"
WindowsLatestAmi:
Type : AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/ami-windows-latest/Windows_Server-2019-Japanese-Full-Base
AllowedValues:
- /aws/service/ami-windows-latest/Windows_Server-2016-Japanese-Full-Base
- /aws/service/ami-windows-latest/Windows_Server-2019-Japanese-Full-Base
KeyPairName:
Description: "EC2 Keypair name."
Default: "your-keypair"
Type: String
Resources:
# RDGateway
RDGateway:
Type: AWS::EC2::Instance
Properties:
ImageId:
Ref: WindowsLatestAmi
InstanceType: t2.medium
KeyName:
Fn::Sub: "${KeyPairName}"
DisableApiTermination: False
EbsOptimized: False
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeSize: 30
VolumeType: gp2
Encrypted: False
SecurityGroupIds:
- Fn::ImportValue:
Fn::Sub: "${SystemName}-${EnvironmentName}-rdgateway-sg"
SubnetId:
Fn::ImportValue:
Fn::Sub: "${SystemName}-${EnvironmentName}-public-subnet-a"
Tags:
- Key: Name
Value:
Fn::Sub: "${SystemName}-${EnvironmentName}-rdgateweay"
# EIP
RDGatewayEIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
InstanceId:
Ref: RDGateway
# 接続対象サーバー
RDSession:
Type: AWS::EC2::Instance
Properties:
ImageId:
Ref: WindowsLatestAmi
InstanceType: t2.medium
KeyName:
Fn::Sub: "${KeyPairName}"
DisableApiTermination: False
EbsOptimized: False
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeSize: 30
VolumeType: gp2
Encrypted: False
SecurityGroupIds:
- Fn::ImportValue:
Fn::Sub: "${SystemName}-${EnvironmentName}-rdsession-sg"
SubnetId:
Fn::ImportValue:
Fn::Sub: "${SystemName}-${EnvironmentName}-private-subnet-a"
Tags:
- Key: Name
Value:
Fn::Sub: "${SystemName}-${EnvironmentName}-rdsession"
RD Gatewayサーバーの環境設定
ここからRD Gatewayサーバーの環境構築を行います。
基本的に各種作業はすべてPowerShellで行い、設定した結果がどうなるかGUIで表示確認していきます。
作成したRD GatewayサーバーにRDPで接続します。
(前節で説明したrdp-sg
セキュリティグループをよしなに設定して接続してください)
0. 事前準備
必須ではないのですがわかりやすさのためにコンピューター名を変えておきます。
# わかりやすさのためにコンピューター名を変更し再起動
Rename-Computer GATEWAY01 -Restart
1. 自己証明書の作成
前回の記事ではmakecert
コマンドを使って自己証明書を作成していますが、Windows Server 2016以降であればNew-SelfSignedCertificate
コマンドレットで証明書の有効期間も指定できる様になっています。
このため本記事ではNew-SelfSignedCertificate
をつかって証明書の作成を行います。
SubjectやDNSNameにはインスタンスタイプのパブリックDNS名を指定しています。
こちらは環境に応じて変更してください。
# PowerShell 5.1以降
$params = @{
Subject = 'CN=ec2-.ap-northeast-1.compute.amazonaws.com' # 今回はCNのみ設定。環境に合わせて変更してください
DnsName = 'ec2-.ap-northeast-1.compute.amazonaws.com' # DNS名は環境に合わせて変更してください
TextExtension = @("2.5.29.37={text}1.3.6.1.5.5.7.3.1") # サーバー証明書のみ
CertStoreLocation = "cert:\LocalMachine\My"
KeyAlgorithm = 'RSA'
KeyLength = 2048
KeyExportPolicy = 'Exportable'
NotAfter = (Get-Date).AddYears(5) # あまりよろしくないけど有効期間5年
}
$cert = New-SelfSignedCertificate @params
$cert
このコマンドにより証明書がローカルコンピューター\個人
に作成されます。
また、この証明書をあとでクライアントで利用するため任意のディレクトリにエクスポートしておきます。
# 作成した証明書を rdgateway.cer という名前でエクスポート
Export-Certificate -FilePath '.\rdgateway.cer' -Cert "Cert:\LocalMachine\My\$($cert.Thumbprint)"
2. 接続用ユーザー、グループの作成
本来RD Gatewayを含めたRemote Desktop Serviceの各機能はActive Directory環境で使う前提となっています。
本記事の環境はワークグループ環境であるため、RD Gatewayに接続するためのユーザーと接続先サーバーに接続するためのユーザーを別々に管理する必要があります。
前回の記事ではRD Gatewayに接続するユーザーをローカルAdministratorsで設定していましたが、今回は接続専用のユーザーとグループを設けることにします。
Windows Server 2016以降であればローカルユーザー、グループを設定するコマンドレットが用意されていますので以下の様にして
- グループ :
GatewayUsers
- ユーザー :
User01
を作成することができます。
# 接続専用のグループとユーザーを作成
$password = ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force # 認証情報は適宜変更してください
New-LocalUser -Name User01 -Password $password
New-LocalGroup -Name GatewayUsers
Add-LocalGroupMember -Group GatewayUsers -Member User01
3. RD Gateway機能のインストール
ここから本題に入っていきます。
Install-WindowsFeature
コマンドレットを使いRD Gatewayの機能と管理ツールをインストールします。
# RD Gateway機能のインストール
Install-WindowsFeature RDS-Gateway -IncludeManagementTools
これによりRD Gatewayの機能とIISなどの周辺機能、RDゲートウェイマネージャーが追加されます。
機能の追加後に再起動は不要です。
4. RD Gateway SSL証明書の設定
次に「1. 自己証明書の作成」で作成した自己証明書をRD Gatewayに登録します。
前回はRDゲートウェイマネージャーのGUIから登録していましたが今回はPowerShellを使いコマンドで登録します。
RDSを管理するためのRemoteDesktopServices
モジュールをインポートするとRDSに関わる各種設定に対してRDS:\
ドライブからアクセスできる様になります。
Set-Item
コマンドレットを使い以下の様にするとRD GatewayのSSL証明書を更新することができます。
# RDSモジュールのインポート
Import-Module RemoteDesktopServices
# 証明書のセット
Set-Item -Path 'RDS:\GatewayServer\SSLCertificate\Thumbprint' -Value $cert.Thumbprint
設定した結果をGUIから確認するとちゃんと反映されていることがわかります。
5. RD Gateway 接続承認ポリシー(CAP)の設定
次に接続承認ポリシー(CAP)を設定します。
CAPは「RD Gatewayに誰が接続できるか」を決めるポリシーとなります。
本記事では極力単純な設定とし、「2. 接続用ユーザー、グループの作成」で作成したGatewayUsers
グループを接続可能にする設定にしておきます。
PowerShellで次の様してNew-Item
で新しいCAP設定を作成します。
# RDSモジュールのインポート (前項でしている場合は不要)
Import-Module RemoteDesktopServices
# 接続承認ポリシー作成
New-Item -Path 'RDS:\GatewayServer\CAP' -Name 'CAP01' -UserGroups "GatewayUsers@$(HOSTNAME)" -AuthMethod 1
登録した結果をGUIから確認してみると下図の様になります。
6. RD Gateway リソース承認ポリシー(RAP)の設定
続けてリソース承認ポリシー(RAP)を作成します。
RAPは「どのリソースにアクセス可能にするか」を決めるポリシーとなります。
こちらもCAPと同様にPowerShellで設定していきます。
単純にGatewayUsers
グループがどのリソースにもアクセス可能な設定としています。
# RDSモジュールのインポート (前項でしている場合は不要)
Import-Module RemoteDesktopServices
# リソース承認ポリシー作成
New-Item -Path 'RDS:\GatewayServer\RAP' -Name 'RAP01' -UserGroups "GatewayUsers@$(HOSTNAME)" -ComputerGroupType 2
登録した結果をGUIから確認してみると下図の様になります。
以上でRD Gatewayの設定は完了です。
最後にRD Gatewayのサービス(TSGateway
)を再起動して変更した設定を反映させます。
Stop-Service TSGateway
Start-Service TSGateway
クライアントからの接続確認
今回はWindows 10クライアントから実際に接続してみます。
1. 自己証明書のインポート
RD Gatewayを利用するには信頼されたサーバーに対してHTTPS接続する必要があり、本来であればSSL証明書は公に信頼されている必要があります。
今回は自己証明書を使っていますので、あまりよろしくないですが、自己証明書を信頼されたルート証明書機関にインポートして無条件に信頼する様にします。
本記事では細かい手順は端折りますが、接続元クライアントに「1. 自己証明書の作成」でエクスポートした証明書をインポートしておいてください。
2. 対象サーバーへ接続
これでクライアント側の準備は完了しましたので、実際に接続対象サーバーへ接続してみます。
リモートデスクトップクライアントを起動し、前節で構築したRDゲートウェイサーバーを使用する設定にします。
その上で接続先に接続対象サーバー(本記事では192.168.21.74
)を指定します。
ユーザーは対象サーバーのユーザーを指定します。
今回は接続対象サーバーに一切設定をしていないため、デフォルトユーザーであるadministrator
を指定しておきます。
ここで「接続」ボタンをクリックし接続を開始すると最初にRD Gatewayサーバーに接続するための認証情報の入力を要求されます。
ここの認証情報は「2. 接続用ユーザー、グループの作成」で作成したユーザー(user01
)を指定します。
何度か同様の入力を求められるので「このアカウントを記憶する」にチェックを付けておいた方が良いでしょう。
認証情報が間違っていなければ接続が継続され、
続けて対象サーバーに対する認証情報の入力を求められます。
パスワードを入力し間違いがなければ下図の様にRDP接続できます。
これで無事接続が完了しました。
最後に
以上です。
Windows Server 2016以降であれば全ての作業をWindows Serverの標準機能でPowerShellから行うことが可能になっています。
本記事ではやりませんでしたがその気になればUserDataにスクリプトを仕込んですべての工程を自動化することもできるかと思います。
追記
自己証明書ではなくLet's Encryptの証明書を使ってRD Gatewayを構築する手順を別記事にまとめました。
よろしければこちらもご覧ください。