はじめに
S3にFTPでファイルの送受信を行いたいと考えたとき、マネージドなFTP
のサービスであるTransfer Familyが第一候補にあがります。
Transfer Familyにファイルアップロードを行うにあたって必要になるのがユーザー管理です。
ユーザー管理には以下の3つのタイプがあります。
- AWS Transfer Familyのサービス管理
- AWS Directory Service for Microsoft Active Directory (AD管理)
- カスタム ID プロバイダー
ADを使えばユーザー管理やアクセス権限の制御はできるものの、例えばユーザー数が少ない場合には少し過剰な気もします。また、FTP(SFTPではない)を利用する場合には カスタムIDプロバイダー しか利用することができません。そのため、今回は以下のような想定で3.のカスタムIDプロバイダーによる認証を実装してみます。
- FTPを利用
- 利用者が数ユーザーのみと小規模
やること
今回はLambdaとSecretsManagerを利用した認証の仕組みを作成します。
IAMポリシーの作成
まずは、FTPを利用するユーザーの権限を管理するために、IAMポリシーを作成します。
IAMポリシーのダッシュボードから、ポリシーを作成 を押します。
ポリシーをJSON形式で記載するので、JSON タブを選択します。
FTPユーザーに「どのS3バケットの読み書き権限を与えるか」をポリシーで定義します。
以下のJSONの「bucketname」部分をユーザーにアクセスさせたいバケット名に置換して、マネジメントコンソールに貼り付けます。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid":"ReadWriteS3",
"Action": [
"s3:ListBucket"
],
"Effect": "Allow",
"Resource": ["arn:aws:s3:::bucketname"]
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:GetObjectVersion",
"s3:GetObjectACL",
"s3:PutObjectACL"
],
"Resource": ["arn:aws:s3:::bucketname/*"]
}
]
}
貼り付けたら、次のステップ をを押します。
ここで記載したS3バケットに対して、FTPユーザーはアクセス権限が付与されます。
タグの追加画面はデフォルトのままで、もう一度 次のステップ:確認 を押します。
ポリシーに名前をつけます。
ここでは、「UserA」というFTPユーザーに対してのポリシーを想定しています。
ポリシーの作成をクリックして作成完了です。
ユーザーごとに権限を変える場合は、ここでユーザーの数だけポリシーを作成すればOKです。
IAMロールの作成
次に、IAMロールを作成します。
LambdaでユーザーIDとパスワードの認証を行った後、IAMロールをReturnすることで、ユーザーがどのAWSリソースを操作できるのか、認可を行うために利用します。
信頼されたエンティティタイプで、AWSのサービス にチェックを入れます。
ユースケースでは、他のAWSサービスのユースケースで「Transfer」を選択します。
そして 次へ を押します。
次に、このIAMロールに許可されるポリシーを追加します。
AWSTransferFullAccess を検索して、チェックを入れましょう。
これだけだと、TransferFamilyに対する許可しか付与されていません。
S3のアクセス権限を付与するため、先程作成したFTPTransfer-UserA-Policyも追加しましょう。
追加ができたら 次へ を押します。
そして、このIAMロールに名前をつけます。
ここでは FTPTransder-UserA-Role としています。
ステップ2: 許可を追加する で、2つのポリシーが付与されていることを確認します。
2つのポリシーが確認できたら ロールの作成 を押します。
ロール名をクリックすると詳細が表示されるので、ARN欄の値をコピーしておきましょう。
これは、Lambda関数でFTPユーザーに権限を払い出す際に利用します。
Secret Managerの設定
いよいよSecretsManagerの設定です。
新しいシークレットを保存する を押します。
そして、利用するシークレットの値を保存していきます。
ここでは、UserAというFTPユーザーのデータを保存します。
以下の通り設定しましょう。
- シークレットのタイプ
- その他のシークレットのタイプ
- キー/値
- Password / パスワードの値
- Role / 先程コピーしたIAMロールのARN
設定できたら 次へ を押します。
Lambda関数の作成
ここまでで、FTPユーザーであるUserAの各種設定ができました。
UserAがどのS3バケットを見ることができるのか、どんなパスワードでログインできるのか、といった情報などの設定ができているので、ここからはLambda関数で認証認可の部分を作っていきます。
実行権限の設定
Lambdaは既に空のLambdaが作成済みである想定です。
デフォルトのままだと、LambdaはSecretsManagerにアクセスする権限がないため、ここではLambdaのIAMロールにSecretsManagerへの実行権限を付与していきます。
Lambda関数を作成した後、設定 -> アクセス権限 から 実行ロール 欄を確認します。
ロール名を押して、Lambdaに付与されているIAMロールを編集していきましょう。
SecretsManagerReadWriteにチェックを入れて、ポリシーをアタッチ を押します。
これで、LambdaがSecretsManagerから値を取得することができるようになりました。
それでは、Lambdaの実装を行います。
以下のコードをコピペしましょう。
import json
import boto3
from botocore.exceptions import ClientError
REGION_NAME = "ap-northeast-1"
def lambda_handler(event, context):
secret_name = "FTP/{}".format(event["username"])
# Screts Managerのクライアントを作成
session = boto3.session.Session()
client = session.client(
service_name='secretsmanager',
region_name=REGION_NAME
)
try:
get_secret_value_response = client.get_secret_value(
SecretId=secret_name
)
except ClientError as e:
# For a list of exceptions thrown, see
# https://docs.aws.amazon.com/secretsmanager/latest/apireference/API_GetSecretValue.html
raise e
# 取得したシークレットの値を使いやすいように加工
secret = json.loads(get_secret_value_response['SecretString'])
PASSWORD = secret['Password']
ROLE = secret['Role']
# パスワードチェック
if event["password"] == PASSWORD:
return { 'Role': ROLE }
else:
return {}
実行していることは単純です。
ユーザーがFTPクライアントから接続する際に、ユーザーIDとパスワードを入力します。
それぞれの値がLambdaのeventにusername
password
として渡されます。
このLambda関数では、渡されたusername
の値をキーとして、SecretsManagerからpassword
とS3へのアクセス権限を定義したRole
の値を取得します。
そして、ユーザーから入力されたパスワードの値と、SecretsManagerから取得したPassword
の値が等しいかを確認し、正しい場合はRoleをReturnしています。
# 取得したシークレットの値を使いやすいように加工
secret = json.loads(get_secret_value_response['SecretString'])
PASSWORD = secret['Password']
ROLE = secret['Role']
# パスワードチェック
if event["password"] == PASSWORD:
return { 'Role': ROLE }
else:
return {}
動作確認
FTPクライアントからTransferFamilyに対して接続を行い、ユーザーIDにUserA
、パスワードはSecretsManagerに設定したパスワードを入力し、ログイン確認を行いましょう。
事前にポリシーで定義したバケットにしかアクセス権限はないため、FTPクライアント側にバケットのパスを設定しておくことを忘れずに。
検証を通じてわかったこと
今回の記事では、Secret Managerを利用してセキュアにFTPのユーザー認証を行いました。
ですが、検証目的だったら以下に記載のような超シンプルなコードでも動きます。
def lambda_handler(event, context):
if event["username"] == "hoge" and event["password"] == "hogehoge":
return { 'Role': 'arn:aws:iam::{ARN}:role/{RoleName}' }
else:
return {}
まずはお手軽に検証を行った上で、最終的にはSecret Managerを使ってセキュアにするのが良いかもしれませんね。
参考URL
AWS Transfer Family のカスタムIDプロバイダーで AWS Secrets Manager を使用して パスワード認証を有効にしてみる | DevelopersIO