この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは!DA(データアナリティクス)事業本部 サービスソリューション部の大高です。
SnowflakeはSaaSのサービスではありますが、プライベート接続にも対応しており、AWS・Google Cloud、Azureの各クラウドサービスにおけるプライベート接続機能を利用して、Snowflakeとの通信をプライベート化することができます。
この設定については、これまではSnowflakeのサポートに連絡して有効化をしていただく必要がありましたが、なんと6.15のリリースにおいてAWSとAzureでは自分自身で有効化ができるようになりました。
今回は、このAWS PrivateLink設定のセルフサービス設定を実際に試してみたいと思います。
前提と検証範囲について
前提
この機能の前提として、SnowflakeのエディションはBusiness Critical(またはそれ以上)が必要となります。
SnowflakeはAWSのリージョン「ap-northeast-1」(Tokyo)の環境を利用しており、AWSアカウントについては同様に「ap-northeast-1」の環境が既に利用できる環境が整っているものとします。
検証範囲
今回は、以下のドキュメントを参照しながら進めていきます。
一方で、かなり盛り沢山ではあるので以下のポイントに絞って試してみたいと思います。
- AWS PrivateLink の有効化
- AWS VPC 環境の構成
- ステップ1: VPC エンドポイント(VPCE)を作成および構成する
- ステップ2: VPCネットワークを構成する
ですので、今回は「ステップ3: Amazon S3用 AWS VPC インターフェイスエンドポイントを作成する」については扱いません。
セルフサービス設定をしてみる
AWS PrivateLink の有効化
まずは下記のコマンドを実行し、利用するAWSアカウントの一時的なセキュリティクレデンシャルを取得します。
$ aws sts get-federation-token --name sam
{
"Credentials": {
"AccessKeyId": "XXX",
"SecretAccessKey": "XXX",
"SessionToken": "XXX",
"Expiration": "XXX"
},
"FederatedUser": {
"FederatedUserId": "XXXXXXXXXXXX:sam",
"Arn": "arn:aws:sts::XXXXXXXXXXXX:federated-user/sam"
},
"PackedPolicySize": 0
}
取得した情報から、FederatedUser.FederatedUserId
に含まれる「12桁のAWSアカウントID」と、Credentials.SessionToken
の値「Federatedトークン」を利用して、以下のクエリをSnowflakeで実行します。
なお、実行時にはACCOUNTADMIN
ロールを利用します。
USE ROLE ACCOUNTADMIN;
SELECT SYSTEM$AUTHORIZE_PRIVATELINK(
'XXXXXXXXXXXX',
'{
"Credentials": {
"AccessKeyId": "XXX",
"SecretAccessKey": "XXX",
"SessionToken": "XXX",
"Expiration": "XXX"
},
"FederatedUser": {
"FederatedUserId": "XXXXXXXXXXXX:sam",
"Arn": "arn:aws:sts::XXXXXXXXXXXX:federated-user/sam"
},
"PackedPolicySize": 0
}'
);
成功すると、以下のように表示されます。
プライベートリンクへのアクセスが許可されました。
確認として、上記と同様にSYSTEM$GET_PRIVATELINK
関数を利用して確認してみます。
USE ROLE ACCOUNTADMIN;
SELECT SYSTEM$GET_PRIVATELINK(
'XXXXXXXXXXXX',
'{
"Credentials": {
"AccessKeyId": "XXX",
"SecretAccessKey": "XXX",
"SessionToken": "XXX",
"Expiration": "XXX"
},
"FederatedUser": {
"FederatedUserId": "XXXXXXXXXXXX:sam",
"Arn": "arn:aws:sts::XXXXXXXXXXXX:federated-user/sam"
},
"PackedPolicySize": 0
}'
);
プライベートリンクへのアクセスが許可されました。
大丈夫そうですね。
AWS VPC 環境の構成
次にAWS環境上でVPCエンドポイントを作成しますが、その前にVPCとサブネット、および、サブネット上にEC2インスタンスを立てておきます。
今回は以下のようなCloudFormationテンプレートを利用しました。このテンプレートでは、VPCとパブリックサブネットを作成し、その内部にWindows ServerのEC2インスタンスを作成します。また、その際のEC2インスタンスのキーペアはパラメータストアに保存されるようになっています。
なお、プライベートネットワーク経由でのSnowflake接続確認のためには「プライベートサブネット」がよいのですが、検証の利便性のために「パブリックサブネット」としています。
Windows Serverが用意できたらセキュリティグループにRemote Desktop(RDP)用の通信設定を行い、RDP接続をしてサーバーにアクセスしてブラウザが利用できる状態にしておきます。(後でSnowflakeへのブラウザアクセス確認のために利用します)
simple-windows-server-stack.yml (クリックして展開)
simple-windows-server-stack.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Simple Windows Server Template.'
Parameters:
# ------------------------------------------------------------#
# Common
# ------------------------------------------------------------#
Prefix:
Type: String
Default: "prefix"
# ------------------------------------------------------------#
# Network
# ------------------------------------------------------------#
VpcCidr:
Type: String
Default: "10.0.0.0/16"
PublicSubnetCidr:
Type: String
Default: "10.0.0.0/24"
# ------------------------------------------------------------#
# EC2
# ------------------------------------------------------------#
EC2InstanceName:
Type: String
Default: "ec2"
EC2InstanceAMI:
Type: AWS::EC2::Image::Id
Default: "ami-0ec81697f08841aed" # Windows_Server-2019-Japanese-Full-Base-2022.05.11
EC2InstanceInstanceType:
Type: String
Default: "t3.micro"
EC2InstanceVolumeType:
Type: String
Default: "gp2"
EC2InstanceVolumeSize:
Type: String
Default: "30"
Resources:
# ------------------------------------------------------------#
# Network
# ------------------------------------------------------------#
Vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCidr
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Sub ${Prefix}-vpc
InternetGateway:
Type: AWS::EC2::InternetGateway
DependsOn: Vpc
Properties:
Tags:
-
Key: Name
Value: !Sub ${Prefix}-ig
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref Vpc
InternetGatewayId: !Ref InternetGateway
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: !Ref PublicSubnetCidr
VpcId: !Ref Vpc
AvailabilityZone:
Fn::Select:
- "0"
- Fn::GetAZs: ""
Tags:
- Key: Name
Value: !Sub ${Prefix}-public-subnet
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
Tags:
-
Key: Name
Value: !Sub ${Prefix}-public-rt
RouteViaIg:
Type: AWS::EC2::Route
DependsOn:
- AttachGateway
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet
RouteTableId: !Ref PublicRouteTable
SecurityGroup:
Type: "AWS::EC2::SecurityGroup"
Properties:
VpcId: !Ref Vpc
GroupName: !Sub "${Prefix}-sg"
GroupDescription: "-"
Tags:
- Key: "Name"
Value: !Sub "${Prefix}-sg"
# ------------------------------------------------------------#
# Ec2InstanceProfile
# ------------------------------------------------------------#
Ec2Role:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${Prefix}-ec2-role
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
Ec2InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: !Sub ${Prefix}-ec2-instance-profile
Roles:
- !Ref Ec2Role
# ------------------------------------------------------------#
# EC2Instance
# ------------------------------------------------------------#
EC2KeyPair:
Type: 'AWS::EC2::KeyPair'
Properties:
KeyName: !Sub ${Prefix}-key-pair
EC2Instance:
Type: "AWS::EC2::Instance"
Properties:
Tags:
- Key: Name
Value: !Sub "${Prefix}-${EC2InstanceName}"
KeyName: !Ref EC2KeyPair
ImageId: !Ref EC2InstanceAMI
InstanceType: !Ref EC2InstanceInstanceType
IamInstanceProfile: !Ref Ec2InstanceProfile
DisableApiTermination: false
EbsOptimized: false
BlockDeviceMappings:
- DeviceName: /dev/xvda
Ebs:
DeleteOnTermination: true
VolumeType: !Ref EC2InstanceVolumeType
VolumeSize: !Ref EC2InstanceVolumeSize
SecurityGroupIds:
- !Ref SecurityGroup
SubnetId: !Ref PublicSubnet
UserData: !Base64 |
#! /bin/bash
yum update -y
EC2EIP:
Type: AWS::EC2::EIP
Properties:
InstanceId:
Ref: EC2Instance
Outputs:
VpcId:
Value: !Ref Vpc
PublicSubnetId:
Value: !Ref PublicSubnet
EC2ElasticIp:
Description: EC2 Elastic IP
Value: !Ref EC2EIP
準備ができたら次のステップに進んでいきましょう。
ステップ1: VPC エンドポイント(VPCE)を作成および構成する
Snowflake上でSYSTEM$GET_PRIVATELINK_CONFIG
を呼び出して設定値を取得します。
USE ROLE ACCOUNTADMIN;
SELECT SYSTEM$GET_PRIVATELINK_CONFIG();
すると、以下のようなJSONが取得できるのでprivatelink-vpce-id
、privatelink-account-url
、privatelink_ocsp-url
の値を控えておきます。
{
"privatelink-account-name": "XXXXXXX.ap-northeast-1.privatelink",
"privatelink-vpce-id": "com.amazonaws.vpce.ap-northeast-1.vpce-svc-XXXXXXXXXXXXXXXXX"
"privatelink-account-url": "XXXXXXX.ap-northeast-1.privatelink.snowflakecomputing.com"
"regionless-privatelink-account-url": "XXXXXXXXXXXXXXX.privatelink.snowflakecomputing.com"
"privatelink_ocsp-url": "ocsp.XXXXXXX.ap-northeast-1.privatelink.snowflakecomputing.com"
"privatelink-connection-urls": "[]"
}
次に、AWSの管理コンソールからVPCエンドポイントを作成します。
「サービスカテゴリ」は「その他のエンドポイントサービス」とし、「サービス名」に先程控えておいたprivatelink-vpce-id
の値を入力して「サービスの検証」をします。
無事にサービス名が検証されたら、先程作成したVPC、サブネット、セキュリティグループを指定してエンドポイントを作成します。
また、セキュリティグループは本来であれば別途VPCエンドポイント用のセキュリティグループを作成すべきですが、今回は一旦既存のもの(先程作成したセキュリティグループ)を流用することにします。
このセキュリティグループに対しては、80
ポートと443
ポートの接続許可を追加します。接続元は「VPCエンドポイント」に接続するEC2インスタンスなどです。今回はCloudFormationでEC2インスタンスを作成したので、該当EC2インスタンスのプライベートIPアドレスを指定しました。
追加のCNAME記録の作成
最後にDNSまわりの設定です。Snowflakeにアクセスする際にはXXXXXXX.ap-northeast-1.privatelink.snowflakecomputing.com
のような形式でアクセスしますが、この名前解決のためには権威DNSサーバに該当レコードを登録する必要があります。
今回はEC2インスタンスからのアクセスを行って検証したいと思うので、Route53にプライベートホストゾーンを作成していきます。
ドメイン名は以下のようにap-northeast-1.privatelink.snowflakecomputing.com
とし、「プライベートホストゾーン」を選択します。
VPCには先程作成したVPCを指定して「ホストゾーンの作成」を行います。
ホストゾーンが作成できたら、次にレコードを作成していきます。
作成したVPCエンドポイントのDNSの名が、SnowflakeのSYSTEM$GET_PRIVATELINK_CONFIG
で取得したprivatelink-account-url
、privatelink_ocsp-url
で解決できるようにCNAMEのレコードを2つ作成します。
また、同様にapp.ap-northeast-1.privatelink.snowflakecomputing.com
のレコードも作成します。このレコードはオプションのようなのですが、後述の接続確認時にこのアドレスへのアクセスも行っているでしたので、併せて設定しておきます。(初回はSnowsightへのアクセスがあるようです)
作成したら設定が伝播するのを待って接続確認をしてみましょう。
接続してみる
では、実際に接続をしてみます。
作成したEC2インスタンスのWindowsサーバーにRDP接続をし、ブラウザでアクセスしてみます。
アクセス先はSnowflakeのSYSTEM$GET_PRIVATELINK_CONFIG
で取得したhttps://XXXXXXX.ap-northeast-1.privatelink.snowflakecomputing.com
のようなprivatelink-account-url
です。
無事にアクセスできました!
まとめ
以上、SnowflakeのAWS PrivateLink設定がセルフサービス対応されたので試してみました。
サポートケースを上げずに自分自身で設定できるようになっているのでとても助かりますね。また、私自身がSnowflakeのPrivateLink設定自体をしたことが無かったので勉強にもなりました。DNS周りの設定が少し難しかったですが、こちらのサイトを参考にさせていただきうまく設定することができました。(ありがとうございます!)
どなたかのお役に立てば幸いです。それでは!