はじめに
お久しぶりです、なじむです。
前回の更新からだいぶ間が空いてしまいました。端的に言うと凄い忙しく、休日はHPを回復するだけの日々を送っていました。
さてさて、コロナの影響を受けて弊社でも在宅勤務が始まりました。それに伴い、WorkSpacesを触ってみたので、その備忘録として記事を起こします。セキュリティ面は甘々かもしれませんが、先ずは動くよ、というところまでです。
やりたいこと
今回は以下のような観点で構築しました。
- WorkSpacesを利用して、自宅からでも作業できる環境を構築したい!
- WorkSpacesにてブラウザからGoogle Drive等にアクセスされてしまうとデータの持ち出しにつながるため、URLフィルタリングをしたい!
- WorkSpacesから自宅PCへのクリップボードコピーがされてしまうとデータの持ち出しにつながる可能性があるため、それは防ぎたい
前提
作業者がAdministratorAccessを持っていることを想定しています。
全体概要図
全体の構成としては以下のような形です。
- Private Subnet, Public Subnetはそれぞれ2つずつ作成
- WorkSpacesとそれに必要なAD環境(MSAD, 管理用Windows Server)はPrivate Subnetに構築
- クリップボードコピー等はMSADのグループポリシーで制御
- WorkSpacesから外部接続する場合はProxyサーバを経由して接続
- URLフィルタリングはProxyサーバにSquidを用いて実装
実装の流れ
通知までの大きな流れとしては以下です(絵を描けば良かった…)
- VPCを構築する
- AWS Management Microsoft ADを構築する
- 管理用Windows Serverを構築する
- Security Groupsの作成(Proxyサーバ、WorkSpaces用)
- Proxyサーバ(Squid)を構築する
- WorkSpacesを構築する
- セキュリティ周りの実装
実装
VPCを構築する
VPCの構築に関しては以下のyamlを流してください。
※使いまわしなのでNATGateway等々があるのはご容赦ください。
決めるパラメータは以下の通りです。
東京リージョンでWorkSpacesを使用する場合、ap-northeast-1dは使用できないので注意が必要です。AWS Developer Forumにも上がっていましたが、実際、私も構築できずVPCから再作成することになりました。
※TokyoリージョンでWorkSpacesが構築できない
パラメータ名 | 説明 | 記入例 |
---|---|---|
Product | 製品名を入力。特になければtest等を入力。 | test |
Environment | dev, stg, prdから選択 | dev |
VPCCidrBlock | VPCのCidrを入力 | 10.0.0.0/16 |
PubSubnet1Cidr | Public Subnet1のCidrを入力 | 10.0.0.0/24 |
PubSubnet1AZ | Public Subnet1のAZを入力 | ap-northeast-1a |
PubSubnet2Cidr | Public Subnet2のCidrを入力 | 10.0.1.0/24 |
PubSubnet2AZ | Public Subnet2のAZを入力 | ap-northeast-1c |
PriSubnet1Cidr | Private Subnet1のCidrを入力 | 10.2.0.0/24 |
PriSubnet1AZ | Private Subnet1のAZを入力 | ap-northeast-1a |
PriSubnet2Cidr | Private Subnet2のCidrを入力 | 10.4.0.0/24 |
PriSubnet2AZ | Private Subnet2のAZを入力 | ap-northeast-1c |
【yaml】VPC.yaml
---
AWSTemplateFormatVersion: 2010-09-09
Description: VPC
#------------------------------
# Parameters: Set your argv.
#------------------------------
Parameters:
# Common
Product:
Type: String
Description: "[Required]Specify the product name"
Environment:
Type: String
Description: "[Required]Specify the environment name(prd, stg, dev)"
AllowedValues:
- prd
- stg
- dev
# VPC parameter
VPCCidrBlock:
Type: String
Description: VPC Cidr Block. (Ex) 10.0.0.0/16
AllowedPattern: '((\d{1,3})\.){3}\d{1,3}/\d{1,2}'
Default: 10.0.0.0/16
# Public Subnet1 parameter
PubSubnet1Cidr:
Type: String
Description: Public Subnet Cidr Block. (Ex) 10.0.0.0/24
AllowedPattern: '((\d{1,3})\.){3}\d{1,3}/\d{1,2}'
Default: 10.0.0.0/24
PubSubnet1AZ:
Type: AWS::EC2::AvailabilityZone::Name
Description: Select the AvailabilityZone of Public Subnet.
Default: ap-northeast-1a
# Public Subnet2 parameter
PubSubnet2Cidr:
Type: String
Description: Public Subnet Cidr Block. (Ex) 10.0.1.0/24
AllowedPattern: '((\d{1,3})\.){3}\d{1,3}/\d{1,2}'
Default: 10.0.1.0/24
PubSubnet2AZ:
Type: AWS::EC2::AvailabilityZone::Name
Description: Select the AvailabilityZone of Public Subnet.
Default: ap-northeast-1c
# Private Subnet1 parameter
PriSubnet1Cidr:
Type: String
Description: Private Subnet Cidr Block. (Ex) 10.0.2.0/23
AllowedPattern: '((\d{1,3})\.){3}\d{1,3}/\d{1,2}'
Default: 10.0.2.0/23
PriSubnet1AZ:
Type: AWS::EC2::AvailabilityZone::Name
Description: Select the AvailabilityZone of Private Subnet.
Default: ap-northeast-1a
# Private Subnet2 parameter
PriSubnet2Cidr:
Type: String
Description: Private Subnet Cidr Block. (Ex) 10.0.4.0/23
AllowedPattern: '((\d{1,3})\.){3}\d{1,3}/\d{1,2}'
Default: 10.0.4.0/23
PriSubnet2AZ:
Type: AWS::EC2::AvailabilityZone::Name
Description: Select the AvailabilityZone of Private Subnet.
Default: ap-northeast-1c
#------------------------------
# Metadata: Your resource list
#------------------------------
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: System Configuration
Parameters:
- Product
- Environment
- Label:
default: Network Configuration
Parameters:
- VPCCidrBlock
- PubSubnet1Cidr
- PubSubnet1AZ
- PubSubnet2Cidr
- PubSubnet2AZ
- PriSubnet1Cidr
- PriSubnet1AZ
- PriSubnet2Cidr
- PriSubnet2AZ
#------------------------------
# Resources: Your resource list
#------------------------------
Resources:
# IAM
## Role - VPC FlowLogs
RoleVPCFlowLogs:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${Product}-${Environment}-iam-role-vpcflowlogs
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service: vpc-flow-logs.amazonaws.com
ManagedPolicyArns:
- !Ref ManagedPolicy1
Path: "/"
## IAM Policy
ManagedPolicy1:
Type: AWS::IAM::ManagedPolicy
Properties:
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
- logs:DescribeLogGroups
- logs:DescribeLogStreams
Resource:
- '*'
# VPC
## VPC
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Sub ${VPCCidrBlock}
EnableDnsSupport: true
EnableDnsHostnames: true
InstanceTenancy: default
Tags:
- Key: Name
Value: !Sub ${Product}-${Environment}-vpc
## LogGroup
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/vpc/FlowLogs/${Product}-${Environment}-vpc
RetentionInDays: 30
## VPCFlowLogs
VPCFlowLogs:
Type: AWS::EC2::FlowLog
Properties:
DeliverLogsPermissionArn: !GetAtt RoleVPCFlowLogs.Arn
LogGroupName: !Ref LogGroup
ResourceId: !Ref VPC
ResourceType: VPC
TrafficType: ALL
#------------------------------
# Subnet
#------------------------------
# Public Subnet1
PubSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Ref PubSubnet1Cidr
AvailabilityZone: !Ref PubSubnet1AZ
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${Product}-${Environment}-pubsubnet1
# Public Subnet2
PubSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Ref PubSubnet2Cidr
AvailabilityZone: !Ref PubSubnet2AZ
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub ${Product}-${Environment}-pubsubnet2
# Private Subnet1
PriSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Ref PriSubnet1Cidr
AvailabilityZone: !Ref PriSubnet1AZ
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: !Sub ${Product}-${Environment}-prisubnet1
# Private Subnet2
PriSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Ref PriSubnet2Cidr
AvailabilityZone: !Ref PriSubnet2AZ
MapPublicIpOnLaunch: false
Tags:
- Key: Name
Value: !Sub ${Product}-${Environment}-prisubnet2
#------------------------------
# DHCP Options
#------------------------------
DHCPOptions:
Type: AWS::EC2::DHCPOptions
Properties:
DomainName: ap-northeast-1.compute.internal
DomainNameServers:
- AmazonProvidedDNS
Tags:
- Key: Name
Value: !Sub ${Product}-${Environment}-dopt
DHCPOptionsAssociation1:
Type: AWS::EC2::VPCDHCPOptionsAssociation
Properties:
VpcId: !Ref VPC
DhcpOptionsId: !Ref DHCPOptions
#------------------------------
# Internet Gateway
#------------------------------
# Internetgateway
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${Product}-${Environment}-igw
# VPCGatewayAttachment
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
#------------------------------
# NAT Gateway
#------------------------------
# EIP for NATGatewayA
Pub1NATGatewayEIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
# NATGateway for privatesubnet
Pub1NATGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt Pub1NATGatewayEIP.AllocationId
SubnetId: !Ref PubSubnet1
Tags:
- Key: Name
Value: !Sub ${Product}-${Environment}-natgwpri1
- Key: Stack
Value: !Sub ${Product}-${Environment}
#------------------------------
# Route Table
#------------------------------
# Public Subnet Route Table
PubRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${Product}-${Environment}-pubrttbl
# PrivateRoute Update
PubRoute1:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PubRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
# Route Table association for Public Subnet
PubRouteTableAssociation3:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PubRouteTable
SubnetId: !Ref PubSubnet1
PubRouteTableAssociation4:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PubRouteTable
SubnetId: !Ref PubSubnet2
# Private Subnet Route Table
PriRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${Product}-${Environment}-prirttbl
# PrivateRoute Update
PriRoute1:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PriRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref Pub1NATGateway
# Route Table association for Private Subnet
PriRouteTableAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PriRouteTable
SubnetId: !Ref PriSubnet1
PriRouteTableAssociation2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PriRouteTable
SubnetId: !Ref PriSubnet2
AWS Management Microsoft ADを構築する
WorkSpacesの利用にはAD環境が必要になります。既存のADを使用することもできますが、今回は新規でDirectory ServiceのManaged Microsoft ADを使用してAD環境を構築します。この手順まで実施できれば、最低限、WorkSpacesを使用する条件は整います。
MSADの構築に関しては以下のyamlを流してください。
決めるパラメータは以下の通りです。ドメインAdminのパスワード(AdminPassword)はSecrets Managerに保存するようにしています。
パラメータ名 | 説明 | 記入例 |
---|---|---|
Product | 製品名を入力。特になければtest等を入力。 | test |
Environment | dev, stg, prdから選択 | dev |
VpcId | 先のVPC.yamlで作成したVPCIDを選択してください | vpc-xxxxxxxxxx |
SubnetIds | 先のVPC.yamlで作成したPrivate Subnetを2つ選択してください | subnet-xxxxxx, subnet-xxxxxx |
DirectoryName | 作成するドメイン名を入力してください | test.local |
AdminPassword | ドメインAdminのパスワードを入力してください | p@ssw0rd |
DirectoryService-MSAD.yaml
---
AWSTemplateFormatVersion: 2010-09-09
Description: Directory Service - Microsoft Active Directory
#------------------------------
# Parameters: Set your argv.
#------------------------------
Parameters:
# Common
Product:
Type: String
Description: "[Required]Specify the product name"
Environment:
Type: String
Description: "[Required]Specify the environment name(prd, stg, dev)"
AllowedValues:
- prd
- stg
- dev
# Network
VpcId:
Type: AWS::EC2::VPC::Id
Description: "[Required]Specify the VPC ID."
SubnetIds:
Type: List<AWS::EC2::Subnet::Id>
Description: The subnets where Directory Service can be created.
# MSAD Settings
DirectoryName:
Type: String
Description: The Active Directory name.
AdminPassword:
Type: String
Description: The Active Directory Admin's password.
NoEcho: true
#------------------------------
# Metadata: Your resource list
#------------------------------
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: System Configuration
Parameters:
- Product
- Environment
- Label:
default: Network Configuration
Parameters:
- VpcId
- SubnetIds
- Label:
default: MSAD Configuration
Parameters:
- DirectoryName
- AdminPassword
#------------------------------
# Resources: Your resource list
#------------------------------
Resources:
# Secrets Manager
MSADPassword:
Type: AWS::SecretsManager::Secret
Properties:
Name: !Sub "/${Product}/${Environment}/MSADPassword"
Description: !Sub "Active Directory(${DirectoryName}) Admin's password"
SecretString: !Ref AdminPassword
# Directory Service
MSAD:
Type: AWS::DirectoryService::MicrosoftAD
Properties:
Edition: Standard
EnableSso: false
Name: !Ref DirectoryName
CreateAlias: true
Password: !Sub "{{resolve:secretsmanager:${MSADPassword}:SecretString}}"
VpcSettings:
VpcId: !Ref VpcId
SubnetIds: !Ref SubnetIds
管理用Windows Serverを構築する
MSADを構築した後は、MSADの設定を実施する管理用Windowsが必要になります。その管理用端末をWindows Server 2019で構築します。
Windows Serverの構築に関しては以下のyamlを流してください。
決めるパラメータは以下の通りです。今回はSSM経由でログインするため、セキュリティグループはdefault(defaultのSGに所属しているものからのみアクセスを許可)を使用します。
※t3.mediummは小さすぎるのでもう少し大きくても良いかもしれません。
パラメータ名 | 説明 | 記入例 |
---|---|---|
Product | 製品名を入力。特になければtest等を入力。 | test |
Environment | dev, stg, prdから選択 | dev |
SubnetId | 先のVPC.yamlで作成したPrivate Subnetを1つ選択してください | subnet-xxxxxx |
ImageId | Windows Server 2019のAMI IDを入力してください | ami-0677267cf17ab376d |
KeyName | Administatorのパスワード復号に使用するキーペアを選択してください | test.pem |
SecurityGroups | VPCを作成した際に作成されるdefaultのSGを選択してください | sg-xxxxxxxxxx |
EC2-MSADManage.yaml
---
AWSTemplateFormatVersion: 2010-09-09
Description: EC2 - Windows Server - Manage MSAD
#------------------------------
# Parameters: Set your argv.
#------------------------------
Parameters:
# Common
Product:
Type: String
Description: "[Required]Specify the product name"
Environment:
Type: String
Description: "[Required]Specify the environment name(prd, stg, dev)"
AllowedValues:
- prd
- stg
- dev
# EC2
SubnetId:
Type: AWS::EC2::Subnet::Id
Description: The subnet where the windows server can be created.
ImageId:
Type: AWS::EC2::Image::Id
Description: The image id.
Default: ami-0677267cf17ab376d # Microsoft Windows Server 2019 Base
KeyName:
Type: AWS::EC2::KeyPair::KeyName
Description: The name of an existing Amazon EC2 key pair to enable RDP.
SecurityGroups:
Type: List<AWS::EC2::SecurityGroup::Id>
Description: The list of SecurityGroupIds in your Virtual Private Cloud (VPC)
#------------------------------
# Metadata: Your resource list
#------------------------------
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: System Configuration
Parameters:
- Product
- Environment
- Label:
default: EC2 Configuration
Parameters:
- SubnetId
- ImageId
- KeyName
- SecurityGroups
#------------------------------
# Resources: Your resource list
#------------------------------
Resources:
# IAM
## Instance Profile
InstanceProfileMSADManage:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: !Sub "${Product}-${Environment}-iam-MSADManage"
Roles:
- !Ref IAMRoleMSADManage
## IAMRole
IAMRoleMSADManage:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${Product}-${Environment}-iam-role-MSADManage"
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service: ec2.amazonaws.com
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
Path: "/"
# EC2
WindowsServer:
Type: AWS::EC2::Instance
Metadata:
AWS::CloudFormation::Init:
config:
files:
c:\\cfn\\cfn-hup.conf:
content: !Sub |
[main]
stack=${AWS::StackId}
region=${AWS::Region}
c:\\cfn\\hooks.d\\cfn-auto-reloader.conf:
content: !Sub |
[cfn-auto-reloader-hook]
triggers=post.update
path=Resources.WindowsServer.Metadata.AWS::CloudFormation::Init
action=cfn-init.exe -v --stack ${AWS::StackName} --resource WindowsServer --region ${AWS::Region}
services:
windows:
cfn-hup:
enabled: true
ensureRunning: true
files:
- c:\\cfn\\cfn-hup.conf
- c:\\cfn\\hooks.d\\cfn-auto-reloader.conf
Properties:
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeSize: 30
VolumeType: gp2
Encrypted: true
DisableApiTermination: true
CreditSpecification:
CPUCredits: standard
ImageId: !Ref ImageId
InstanceInitiatedShutdownBehavior: stop
InstanceType: t3.medium
IamInstanceProfile: !Ref InstanceProfileMSADManage
KeyName: !Ref KeyName
SecurityGroupIds: !Ref SecurityGroups
SubnetId: !Ref SubnetId
Tags:
- Key: Name
Value: !Sub "${Product}-${Environment}-MSADManage"
UserData:
Fn::Base64: !Sub |
<powershell>
# Rename Hostname
Rename-Computer -NewName "${Product}-${Environment}-MSADManage" -Force -Restart
cfn-init.exe -v --stack ${AWS::StackName} --resource WindowsServer --region ${AWS::Region}
cfn-signal.exe -e $lastexitcode --stack ${AWS::StackName} --resource WindowsServer --region ${AWS::Region}
</powershell>
管理用Windows Serverへのログイン
セキュリティグループでインバウンド通信を許可していないため、SSM経由でのログインとなります。SSMでWindowsにログインする際はポートフォワーディングが必要になるため注意してください。またローカルのpowershellでssm agentを操作するためには別途プラグインが必要になります。
(参考)Install the Session Manager Plugin on Windows
PS> aws ssm start-session --target i-xxxxxxxxxx `
--document-name AWS-StartPortForwardingSession `
--parameters '{\"portNumber\":[\"3389\"],\"localPortNumber\":[\"13389\"]}'
Starting session with SessionId: username-xxxxxxxxxx
Port 13389 opened for sessionId username-xxxxxxxxxx
ポートフォワーディングをした後、先ほど設定したポート(今回はlocalhost:13389)にRDP接続すると、管理用Windows Serverにログインできます。
管理用Windows Serverの設定
詳細な手順は省略しますが、実施することは以下2点です。グループポリシーの設定などは後述します。
- 管理用Windows Serverのドメイン参加(作成したMSADのドメインに参加する)
- 役割と機能の追加
- Group Policy Management
- Remote Server Administration Tools
- Role Administration Tools
- AD DS and AD LDS Tools
- DNS
- Role Administration Tools
(参考)Install the Active Directory Administration Tools on Windows Server 2019
Security Groupsの作成(Proxyサーバ、WorkSpaces用)
※この項目はAmazon WorkSpaces の IP アドレスとポートの要件から読んだ情報を自分なりに整理してみたものですが、完璧かどうか自信はありません。
Proxyサーバ、WorkSpacesに設定するセキュリティグループを設定します。
WorkSpacesに関してはここで新規で作成し、アウトバウンドを制限しますが、デフォルトで付与されているグループ(d-xxxxxxxxxx_workspacesMembers)でアウトバウンド通信を全て許可しているため、アウトバウンドのルールを削除してください。
ここで新規で作成しますが、既存の
- Proxyサーバ
- インバウンド通信 :WorkSpaceからの3128のみを許可
- アウトバウンド通信:全て許可
- WorkSpaces
- インバウンド通信 :基本的に許可しない
- アウトバウンド通信:Amazon WorkSpaces の IP アドレスとポートの要件の通信を許可、Proxyサーバへの3128を許可
セキュリティグループの構築に関しては以下のyamlを流してください。
決めるパラメータは以下の通りです。
パラメータ名 | 説明 | 記入例 |
---|---|---|
Product | 製品名を入力。特になければtest等を入力。 | test |
Environment | dev, stg, prdから選択 | dev |
VpcId | 先のVPC.yamlで作成したVPCIDを選択してください | vpc-xxxxxxxxxx |
MSADSecurityGroup | MSADを構築した際に作成されるデフォルトのセキュリティグループ(d-xxxxxxxxxx_controllers)を指定してください | sg-xxxxxxxxxx |
SecurityGroup.yaml
---
AWSTemplateFormatVersion: 2010-09-09
Description: SecurityGroups - WorkSpaces
#------------------------------
# Parameters: Set your argv.
#------------------------------
Parameters:
# Common
Product:
Type: String
Description: "[Required]Specify the product name"
Environment:
Type: String
Description: "[Required]Specify the environment name(prd, stg, dev)"
AllowedValues:
- prd
- stg
- dev
# Network
VpcId:
Type: AWS::EC2::VPC::Id
Description: "[Required]Specify the VPC ID."
MSADSecurityGroup:
Type: AWS::EC2::SecurityGroup::Id
Description: Directory Service securityGroup Id.
#------------------------------
# Metadata: Your resource list
#------------------------------
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: System Configuration
Parameters:
- Product
- Environment
- Label:
default: Network Configuration
Parameters:
- VpcId
- MSADSecurityGroup
#------------------------------
# Resources: Your resource list
#------------------------------
Resources:
# Squid
SecurityGroupSquid:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "${Product}-${Environment}-ec2-securitygroup-squid"
GroupDescription: WorkSpaces access for Squid.
SecurityGroupIngress:
- SourceSecurityGroupId: !Ref SecurityGroupWorkSpaces
Description: TCP Access from WorkSpaces
IpProtocol: TCP
FromPort: 3128
ToPort: 3128
VpcId: !Ref VpcId
Tags:
- Key: Name
Value: !Sub "${Product}-${Environment}-ec2-securitygroup-squid"
# WorkSpaces
SecurityGroupWorkSpaces:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub "${Product}-${Environment}-ec2-securitygroup-workspaces"
GroupDescription: WorkSpaces securitygroup.
SecurityGroupEgress:
# workspaces client access
- CidrIp: 18.178.102.247/32
Description: PCoIP health check drp-nrt.amazonworkspaces.com.
IpProtocol: TCP
FromPort: 4172
ToPort: 4172
- CidrIp: 18.178.102.247/32
Description: PCoIP health check drp-nrt.amazonworkspaces.com.
IpProtocol: UDP
FromPort: 4172
ToPort: 4172
- CidrIp: 54.250.251.0/24
Description: PCoIP gateway.
IpProtocol: TCP
FromPort: 4172
ToPort: 4172
- CidrIp: 54.250.251.0/24
Description: PCoIP gateway.
IpProtocol: UDP
FromPort: 4172
ToPort: 4172
# Directory Service access
- DestinationSecurityGroupId: !Ref MSADSecurityGroup
Description: DNS access to Directory Service.
IpProtocol: TCP
FromPort: 53
ToPort: 53
- DestinationSecurityGroupId: !Ref MSADSecurityGroup
Description: DNS access to Directory Service.
IpProtocol: UDP
FromPort: 53
ToPort: 53
- DestinationSecurityGroupId: !Ref MSADSecurityGroup
Description: Kerberos authentication access to Directory Service.
IpProtocol: TCP
FromPort: 88
ToPort: 88
- DestinationSecurityGroupId: !Ref MSADSecurityGroup
Description: Kerberos authentication access to Directory Service.
IpProtocol: UDP
FromPort: 88
ToPort: 88
- DestinationSecurityGroupId: !Ref MSADSecurityGroup
Description: NTP access to Directory Service.
IpProtocol: UDP
FromPort: 123
ToPort: 123
- DestinationSecurityGroupId: !Ref MSADSecurityGroup
Description: RPC access to Directory Service.
IpProtocol: TCP
FromPort: 135
ToPort: 135
- DestinationSecurityGroupId: !Ref MSADSecurityGroup
Description: Netlogon access to Directory Service.
IpProtocol: UDP
FromPort: 137
ToPort: 138
- DestinationSecurityGroupId: !Ref MSADSecurityGroup
Description: Netlogon access to Directory Service.
IpProtocol: TCP
FromPort: 139
ToPort: 139
- DestinationSecurityGroupId: !Ref MSADSecurityGroup
Description: LDAP access to Directory Service.
IpProtocol: TCP
FromPort: 389
ToPort: 389
- DestinationSecurityGroupId: !Ref MSADSecurityGroup
Description: LDAP access to Directory Service.
IpProtocol: UDP
FromPort: 389
ToPort: 389
- DestinationSecurityGroupId: !Ref MSADSecurityGroup
Description: SMB access to Directory Service.
IpProtocol: TCP
FromPort: 445
ToPort: 445
- DestinationSecurityGroupId: !Ref MSADSecurityGroup
Description: SMB access to Directory Service.
IpProtocol: UDP
FromPort: 445
ToPort: 445
- DestinationSecurityGroupId: !Ref MSADSecurityGroup
Description: Dynamic ports for RPC access to Directory Service.
IpProtocol: TCP
FromPort: 1024
ToPort: 65535
# Security Group info
VpcId: !Ref VpcId
Tags:
- Key: Name
Value: !Sub "${Product}-${Environment}-ec2-securitygroup-workspaces"
SecurityGroupWorkSpacesEgress:
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId: !Ref SecurityGroupWorkSpaces
SourceSecurityGroupId: !Ref SecurityGroupSquid
Description: Proxy access.
IpProtocol: TCP
FromPort: 3128
ToPort: 3128
WorkSpaces環境へのセキュリティグループの適用
WorkSpacesには追加のセキュリティグループを設定することが可能です。先ほど作成したWorkSpaces用のセキュリティグループを以下の手順を参考にしながら付与します。
WorkSpaces のセキュリティグループ
Proxyサーバ(Squid)を構築する
Proxyサーバとして使用するSquidを構築します。事前準備として、S3バケット(${Product}-${Environment}-squidlog)を作成しておいてください。Squidのaccess.logをS3に保存するためのバケットとなります。
Proxyサーバの構築に関しては以下のyamlを流してください。
決めるパラメータは以下の通りです。
各WorkSpacesからProxyサーバに対してアクセスが必要なため、セキュリティグループは事前に作成したものを指定します。
SquidのログをS3に保存しておきたかったため、インスタンスプロファイルにはAmazonSSMManagedInstanceCore以外にも、S3へのアクセス許可を付与しました。
EIPはWorkSpacesのProxyに指定するIPを固定するために指定しました。本来ならProxyは冗長化してELBやRoute53を指定した方が良いと思います。
パラメータ名 | 説明 | 記入例 |
---|---|---|
Product | 製品名を入力。特になければtest等を入力。 | test |
Environment | dev, stg, prdから選択 | dev |
SubnetId | 先のVPC.yamlで作成したPublic Subnetを1つ選択してください | subnet-xxxxxx |
ImageId | Amazon Linux 2のAMI IDを入力してください | ami-052652af12b58691f |
SecurityGroupId | Squidに付与するSGを選択してください | sg-xxxxxxxxxx |
EC2-Squid.yaml
---
AWSTemplateFormatVersion: 2010-09-09
Description: EC2 - Squid
#------------------------------
# Parameters: Set your argv.
#------------------------------
Parameters:
# Common
Product:
Type: String
Description: "[Required]Specify the product name"
Environment:
Type: String
Description: "[Required]Specify the environment name(prd, stg, dev)"
AllowedValues:
- prd
- stg
- dev
# EC2
SubnetId:
Type: AWS::EC2::Subnet::Id
Description: The subnet where the windows server can be created.
ImageId:
Type: AWS::EC2::Image::Id
Description: The AmazonLinux2 image id.
Default: ami-052652af12b58691f # Amazon Linux 2 AMI (HVM), SSD Volume Type
SecurityGroupId:
Type: AWS::EC2::SecurityGroup::Id
Description: The SecurityGroupId for squid server.
#------------------------------
# Metadata: Your resource list
#------------------------------
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: System Configuration
Parameters:
- Product
- Environment
- Label:
default: EC2 Configuration
Parameters:
- SubnetId
- ImageId
- SecurityGroupId
#------------------------------
# Resources: Your resource list
#------------------------------
Resources:
# IAM
## Instance Profile
InstanceProfileSquid:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: !Sub "${Product}-${Environment}-iam-WorkSpaces-Squid"
Roles:
- !Ref IAMRoleSquid
## IAMRole
IAMRoleSquid:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${Product}-${Environment}-iam-WorkSpaces-Squid"
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: sts:AssumeRole
Principal:
Service: ec2.amazonaws.com
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
- !Ref ManagedPolicy1
Path: "/"
## S3 put policy
ManagedPolicy1:
Type: AWS::IAM::ManagedPolicy
Properties:
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- s3:ListAllMyBuckets
- s3:AbortMultipartUpload
- s3:RestoreObject
- s3:GetBucketNotification
- S3:GetBucketPolicy
Resource: arn:aws:s3:::*
- Effect: Allow
Action:
- s3:ListBucket
- s3:GetBucketLocation
- s3:PutBucketNotification
Resource: !Sub "arn:aws:s3:::${Product}-${Environment}-squidlog"
- Effect: Allow
Action:
- s3:PutObject
- s3:GetObject
Resource: !Sub "arn:aws:s3:::${Product}-${Environment}-squidlog/*"
# Squid
SquidServer:
Type: AWS::EC2::Instance
Metadata:
AWS::CloudFormation::Init:
config:
packages:
yum:
squid: []
files:
/etc/cfn/cfn-hup.conf:
content: !Sub |
[main]
stack=${AWS::StackId}
region=${AWS::Region}
/etc/cfn/hooks.d/cfn-auto-reloader.conf:
content: !Sub |
[cfn-auto-reloader-hook]
triggers=post.update
path=Resources.SquidServer.Metadata.AWS::CloudFormation::Init
action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource SquidServer --region ${AWS::Region}
services:
sysvinit:
cfn-hup:
enabled: true
ensureRunning: true
files:
- /etc/cfn/cfn-hup.conf
- /etc/cfn/hooks.d/cfn-auto-reloader.conf
squid:
enabled: true
ensureRunning: true
Properties:
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
VolumeSize: 8
VolumeType: gp2
Encrypted: true
DisableApiTermination: true
CreditSpecification:
CPUCredits: standard
ImageId: !Ref ImageId
InstanceInitiatedShutdownBehavior: stop
InstanceType: t3.small
IamInstanceProfile: !Ref InstanceProfileSquid
SecurityGroupIds:
- !Ref SecurityGroupId
SubnetId: !Ref SubnetId
Tags:
- Key: Name
Value: !Sub "${Product}-${Environment}-Squid"
UserData:
Fn::Base64: !Sub |
#!/bin/bash
# set hostname
hostnamectl set-hostname "${Product}-${Environment}-Squid"
# start cloud Init
/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource SquidServer --region ${AWS::Region}
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource SquidServer --region ${AWS::Region}
# EIP for Squid
SquidEIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
EIPAssociation:
Type: AWS::EC2::EIPAssociation
Properties:
InstanceId: !Ref SquidServer
EIP: !Ref SquidEIP
Proxy(Squid)の設定
上述のCFnでProxyサーバを構築すると、Squidの設定ファイルは/etc/squid/squid.confにあるかと思います。Blacklistの設定やログフォーマット、世代数の設定を追記していきます。
squidの設定
/etc/squid/acl/blacklist.txtを作成し、その中にアクセスを拒否したいドメイン名を記載します。その後、squid.confにてhttp_access denyの設定やsshもproxy経由で許可する設定を加えます。
ログに関してはフォーマットの修正と、ローカルに保存する世代数の設定を実施しています。ローテーション自体は次のcronの設定で実施しています。
# cd /etc/squid/
# pwd
/etc/squid/
# mkdir acl
# vi acl/blacklist.txt
接続を拒否したいサイトのドメイン名を記載。
(例)
example.com
# vi squid.conf
# diff squid.conf squid.conf.default
14,17d13
< # user settings: listfile
< acl Blacklist dstdomain "/etc/squid/acl/blacklist.txt" # Blacklistを定義
<
< acl SSL_ports port 22 # user settings: ssh # sshもproxy経由でアクセスできるよう許可
21d16
< acl Safe_ports port 22 # user settings: ssh # sshもproxy経由でアクセスできるよう許可
41,43d35
< # user settings: Blacklist
< http_access deny Blacklist # Blacklistで定義したファイルに記載されているドメインへのアクセスを拒否
<
82,87d73
<
< # user settings: logformat
< logformat squid %tl %>a %Ss/%03>Hs %<st %rm %ru %[un %Sh/%<a %mt # Squidのログフォーマットを修正
<
< # user settings: logrotate
< logfile_rotate 8 # Squidのログのローカルに保存する世代数を設定
# systemctl restart squid
#
cronの設定
cronで日次でsquidログのローテーション、ローテーションした後に、ローテーションした5分後にログファイルをS3にコピーしています。
# crontab -l
0 0 * * * /usr/sbin/squid -k rotate
5 0 * * * /usr/bin/aws s3 cp /var/log/squid/access.log.0 s3://your_bucket_name/$(date +\%Y)/$(date +\%m)/$(date +\%d)/access.log
#
WorkSpacesを構築する
セキュリティ周りの実装
WorkSpacesにアクセスできるIPを制限する
WorkSpacesにアクセスできるIPを制限することができます。こちらは以下が参考になりました。
いつも拝見させて頂いているClassmethod社のDevelopers.IOの記事を参考リンクとして記載させて頂きます。
(AWS)WorkSpaces の IP アクセスコントロールグループ
(Developers.IO)【新機能】WorkSpacesにIPベースのアクセス制御機能が追加されました!
グループポリシーの制御
WorkSpaces環境におけるグループポリシーの制御は以下が参考になりました。
Classmethod社のDevelopers.IOはクリップボードコピーの制御のみの記載でしたが、ここで勘所を掴めたら、その他の設定もすぐにできるかと思います。
(AWS)グループポリシーを使用した Windows WorkSpaces の管理
(Developers.IO)グループポリシーを使用してWorkSpacesの制御をしてみた
WorksDocsを使用するのであれば:WorkDocsにアクセスできるIPを制限する
WorkSpaces上でのファイル共有などを考えた場合、WorkDocsを使用するのが簡単だと思います。
しかし、WorkSpacesからWorkDocsにデータを格納し、自宅PCからWorkDocsにアクセスして情報漏洩なんてことになったら目も当てられません。WorkDocsにもIP制限をすることができるため、もし使用する場合は制限すると良いかもしれません。
(AWS)IP allow list
(Developers.IO)Amazon WorkDocsでIPアドレス制限が可能になりました
まとめ
今回はWorkSpacesを用いた環境構築というテーマで記事を書きました。
最後がリンクを張っただけじゃん、というのは大目に見て頂ければと思います。
- セキュリティ面で穴があるかもしれない
- 耐障害性をもっと考えた方が良いかもしれない
- コストを抑えられる方法があるかもしれない
- もっと楽に構築できる方法があるかもしれない
…etc
考え出すともっと良い方法があるのではないかと考えられますが、自分の構築メモと頭の整理用にまとめてみました。
在宅勤務等を駆使して、コロナを乗り切りましょう!