はじめに
AWS Transfer for SFTPはマネージドなSFTPサーバをサクッと立てることができる素晴らしいサービス。
素のままではVPCのリソースではないが、2020年の1月にVPC経由でEIPをアタッチしてセキュリティグループによりセキュアにアクセスできるようになった。
構成図についてはクラスメソッド先生のこの記事が分かりやすい。
この構成は、マネージメントコンソールでは簡単に作れるが、Terraformで実現しようとするとちょっとクセがある部分があるため、そこも含めてどうやって実装するかを検討する(2020年11月時点の話。将来的にはもっと簡単に作れるようになると思う)。
全体構成
全体構成としては以下のようになる。
ただし、VPC とパブリックなサブネット2つ、SSH のインバウンド通信を許容するセキュリティグループは既に作ってあって、data source で参照する前提とする。
terraform/
├── 01_main.tf
├── 02_variable.tf
├── 03_datasource.tf
├── 11_eip.tf
├── 12_iam.tf
├── 13_s3.tf
├── 14_transfer.tf
└── 15_vpcendpoint.tf
Terraformリソース
EIP
これは特に難しいことはない。パブリックなサブネット2つにアタッチするEIPを用意しておけばよい。
※たぶん1つでも問題ない
###############################################################################
# EIP #
###############################################################################
resource "aws_eip" "sftp1" {
vpc = true
}
resource "aws_eip" "sftp2" {
vpc = true
}
IAM
IAM については、AWS Transfer for SFTP のサーバが CloudWatch Logs にログを出力するための権限と、接続してくるユーザが S3 にアクセスするための権限を用意する必要がある。
いずれも principals は transfer.amazonaws.com
で良い。
################################################################################
# IAM Role for Transfer for SFTP Server #
################################################################################
resource "aws_iam_role" "sftp_server" {
name = local.sftp_server_role_name
assume_role_policy = data.aws_iam_policy_document.sftp_server_assume.json
}
data "aws_iam_policy_document" "sftp_server_assume" {
statement {
effect = "Allow"
actions = [
"sts:AssumeRole",
]
principals {
type = "Service"
identifiers = [
"transfer.amazonaws.com",
]
}
}
}
resource "aws_iam_role_policy_attachment" "sftp_server" {
role = aws_iam_role.sftp_server.name
policy_arn = aws_iam_policy.sftp_server.arn
}
resource "aws_iam_policy" "sftp_server" {
name = local.sftp_server_policy_name
policy = data.aws_iam_policy_document.sftp_server_custom.json
}
data "aws_iam_policy_document" "sftp_server_custom" {
statement {
effect = "Allow"
actions = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
]
resources = [
"*",
]
}
}
################################################################################
# IAM Role for Transfer for SFTP User #
################################################################################
resource "aws_iam_role" "sftp_user" {
name = local.sftp_user_role_name
assume_role_policy = data.aws_iam_policy_document.sftp_user_assume.json
}
data "aws_iam_policy_document" "sftp_user_assume" {
statement {
effect = "Allow"
actions = [
"sts:AssumeRole",
]
principals {
type = "Service"
identifiers = [
"transfer.amazonaws.com",
]
}
}
}
resource "aws_iam_role_policy_attachment" "sftp_user" {
role = aws_iam_role.sftp_user.name
policy_arn = aws_iam_policy.sftp_user.arn
}
resource "aws_iam_policy" "sftp_user" {
name = local.sftp_user_policy_name
policy = data.aws_iam_policy_document.sftp_user_custom.json
}
data "aws_iam_policy_document" "sftp_user_custom" {
statement {
effect = "Allow"
actions = [
"s3:*",
]
resources = [
"${aws_s3_bucket.sftp.arn}",
"${aws_s3_bucket.sftp.arn}/*",
]
}
}
S3バケット
S3 のバケットは、最小権限を考えると個別に用意し、IAMで↑のようにアクセス範囲を絞った方が良い。
なお、外部ユーザのアクセスではあるが、IAMで制御しているため、ACL は private
で問題ない。
################################################################################
# S3 #
################################################################################
resource "aws_s3_bucket" "sftp" {
bucket = local.bucket_name
acl = "private"
}
AWS Transfer for SFTP
いよいよ本題。
サーバ、ユーザ、SSHキーの設定をすることになる。
サーバ
今回の、EIP+セキュリティグループを使う場合は、endpoint_type
に VPC
を設定しよう。
その上で、endpoint_details
で VPC、サブネット、EIPのIPアドレス情報を設定する。
###############################################################################
# Transfer for SFTP #
###############################################################################
resource "aws_transfer_server" "sftp" {
identity_provider_type = "SERVICE_MANAGED"
endpoint_type = "VPC"
endpoint_details {
vpc_id = data.aws_vpc.my.id
subnet_ids = data.aws_subnet_ids.my_vpc.ids
address_allocation_ids = [
aws_eip.sftp1.id,
aws_eip.sftp2.id,
]
}
logging_role = aws_iam_role.sftp_server.arn
}
ユーザ
ユーザの設定では、ホームディレクトリのマッピングをする。
entry
が、sftp 接続をした際のデフォルトパスで、target
が実際のパスになる。
下記の設定をすることで、トップディレクトリが今回のために用意したバケットになり、../ にアクセスしても上位のバケット一覧は取れなくなっていてセキュアだ。
resource "aws_transfer_user" "sftp_user" {
server_id = aws_transfer_server.sftp.id
user_name = local.sftp_user_name
role = aws_iam_role.sftp_user.arn
home_directory_type = "LOGICAL"
home_directory_mappings {
entry = "/"
target = "/${local.bucket_name}"
}
}
SSHキー
SSHキーは、自分で
$ ssh-keygen -P "" -f [鍵ファイル名]
しても良いが、せっかくだから全部Terraformで書いてみよう。
tls_private_key
のプロバイダを使い、公開鍵をSFTPのユーザに設定し、秘密鍵でアクセスすることが可能だ。
local_file
で provisioner
を使っているのは、SSHでは鍵ファイルのパーミッションで自分以外に権限がついているとエラーになるためだ。
resource "aws_transfer_ssh_key" "sftp" {
server_id = aws_transfer_server.sftp.id
user_name = aws_transfer_user.sftp_user.user_name
body = tls_private_key.sftp.public_key_openssh
}
resource "tls_private_key" "sftp" {
algorithm = "RSA"
rsa_bits = 2048
}
resource "local_file" "private_key" {
filename = local.private_key_file
content = tls_private_key.sftp.private_key_pem
provisioner "local-exec" {
command = "chmod 600 ${local.private_key_file}"
}
}
さて、ここで準備が完了したので terraform apply
してアクセスしようとしても到達しない。
ここまで読んで既に気付いている人もいるだろうが、セキュリティグループの設定がここまで登場しない。
この構成のミソは、セキュリティグループはSFTPサーバではなく、あくまでもVPCエンドポイントにアタッチされているということだ。マネージメントコンソールでは、TransferをVPCタイプで起動した際に良い感じにこのアタッチをしてくれているだけだ。
そして、2020年11月時点では、1回の apply ではVPCタイプのVPCエンドポイントの情報を取得することはできないし、自分で定義したものを紐付けることもできない。
VPCエンドポイント
仕方がないので、一旦ここで区切って、
$ terraform import aws_vpc_endpoint.sftp [払い出されたVPCエンドポイントID]
でインポートしてあげよう。
インポートの際には、以下のリソースを準備しておく。
###############################################################################
# VPC Endpoint #
###############################################################################
resource "aws_vpc_endpoint" "sftp" {
# count = 0
vpc_endpoint_type = "Interface"
service_name = "com.amazonaws.ap-northeast-1.transfer.server.c-0002"
vpc_id = data.aws_vpc.my.id
subnet_ids = data.aws_subnet_ids.my_vpc.ids
security_group_ids = [
data.aws_security_group.ssh.id,
]
tags = {
Name = local.sftp_vpce_name
}
}
初回の apply 時にはこのリソースは邪魔になるので count = 0
を入れておき、import が完了したらこの行をコメントアウトして再度 apply することで、エンドポイントにアタッチされたデフォルトのセキュリティグループを消しつつ、SSHのセキュリティグループを設定することが可能だ。ついでに Name で識別子を設定しておこう。
いざ、動かす!
さて、ここまで完了したは後はアクセスするだけだ。
$ sftp -i [HCL:14_transfer.tfで作成した秘密鍵] [ユーザ名]@s-xxxxxxxxxxxxxxxxx.server.transfer.[リージョン].amazonaws.com
Connected to s-xxxxxxxxxxxxxxxxx.server.transfer.ap-northeast-1.amazonaws.com.
sftp>
よし!接続することができた!もちろん、ちゃんと ls
も put
もできるので、色々試してみよう!