AWS Security Token Serviceを使ってみる

AWS SDKやAWSコマンドラインツールを使う場合、Security Credentialsが必要になります。Security Credentialsの中にはアクセスキーとシークレットアクセスキーが含まれています。AWSのサービスやリソースをAPIを使って管理するときには、この2つのキーを使って認証します。

この2つのキーをどうやって保管するか?が本記事の目標とするところです。

2つのキーをソースコードやもしくはテキストファイル、DB等に格納して使うこともできますが、これだとキーの置き換えをするのが大変です。そこでAWSから提供されているSecurity Token Serviceというサービスを使って、キーの管理を安全に、かつ効率的に行うことを試してみました。

Security Token Serviceとは?
一時的な、かつ制限された特権を持つAWSアカウント、またはIAMユーザのCredential情報を取得可能にする機能です。Security Token Serviceは、以下の3つのアクションが可能です。

AWS SDKでSecurity Token Serviceより権限を取得できる。対応しているSDKは2013/04/16時点では以下。

http://docs.aws.amazon.com/STS/latest/UsingSTS/AccessingSTS.html

AWS SDK for Javaを使って、GetSessionTokenした例が以下。今回は取得した権限でS3のバケットリストを表示させている。

import java.io.IOException;
import java.util.List;

import com.amazonaws.AmazonWebServiceClient;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.auth.BasicSessionCredentials;
import com.amazonaws.auth.PropertiesCredentials;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.Bucket;
import com.amazonaws.services.s3.model.ObjectListing;
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient;
import com.amazonaws.services.securitytoken.model.Credentials;
import com.amazonaws.services.securitytoken.model.GetSessionTokenRequest;
import com.amazonaws.services.securitytoken.model.GetSessionTokenResult;


public class MySecurityTokenSample {

	public static void main(String[] args) throws IOException {
		
		//STSからトークンを取得するために必要なCredential情報
		AWSCredentials credentials = new PropertiesCredentials(
                MySecurityTokenSample.class.getResourceAsStream("AwsCredentials.properties"));
		
		//SecurityTokenServiceClientの設定
		AWSSecurityTokenServiceClient sts = new AWSSecurityTokenServiceClient(credentials);
		GetSessionTokenRequest req = new GetSessionTokenRequest();
		GetSessionTokenResult res = sts.getSessionToken(req);
		Credentials tmpCredentials = res.getCredentials();
		String accessKeyId = tmpCredentials.getAccessKeyId();
		String secretAccessKeyId = tmpCredentials.getSecretAccessKey();

		//取得したアクセスキー、シークレットアクセスキーでS3のバケットリストを表示してみる。
		BasicSessionCredentials c = new BasicSessionCredentials(tmpCredentials.getAccessKeyId(),
				tmpCredentials.getSecretAccessKey(), tmpCredentials.getSessionToken());
		AmazonS3Client s3Client = new AmazonS3Client(c);
		s3Client.setEndpoint("s3-ap-northeast-1.amazonaws.com");
					
		List<Bucket> list = s3Client.listBuckets();
		for(Bucket b : list) {
			System.out.println(b.getName());
		}
	}
}

こちらを実行すると、バケット名のリストが表示された。
トークンを取得するためには、やはりSecurity Credentialsの情報が必要。なので、システムの中に1台だけトークン管理サーバなるものを立てて、そこの中では唯一Credentials情報を持つ。他のサーバで一時的にCredentials情報が必要な場合は、このサーバが何かしらの安全な方法で配ってあげる形が良さそう。取得したトークンは、デフォルトで12時間まで有効。その時間が過ぎると無効になる。以下のように実行することで、その時間も変更できる。

getSessionTokenRequest.setDurationSeconds(7200); 

上記例では、AWSアカウントの権限を取得していたが、以下のようにgetFederatedTokenを使って指定したIAM Userの権限を取得もできる。

import java.io.IOException;
import java.util.List;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.auth.BasicSessionCredentials;
import com.amazonaws.auth.PropertiesCredentials;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.Bucket;
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient;
import com.amazonaws.services.securitytoken.model.Credentials;
import com.amazonaws.services.securitytoken.model.GetFederationTokenRequest;
import com.amazonaws.services.securitytoken.model.GetFederationTokenResult;
import com.amazonaws.services.securitytoken.model.GetSessionTokenRequest;
import com.amazonaws.services.securitytoken.model.GetSessionTokenResult;


public class MySecurityTokenSample2 {

public static void main(String[] args) throws IOException {
		
		//STSからトークンを取得するために必要なCredential情報
		AWSCredentials credentials = new PropertiesCredentials(
                MySecurityTokenSample.class.getResourceAsStream("AwsCredentials.properties"));
		
		//SecurityTokenServiceClientの設定
		AWSSecurityTokenServiceClient sts = new AWSSecurityTokenServiceClient(credentials);
		GetFederationTokenRequest req = new GetFederationTokenRequest();
		req.setName("TVMUser1");
		req.setPolicy("{\"Statement\": [{\"Effect\": \"Allow\",\"Action\": \"s3:*\",\"Resource\": \"*\"}]}");
		GetFederationTokenResult res = sts.getFederationToken(req);
		Credentials tmpCredentials = res.getCredentials();
		
		//取得したアクセスキー、シークレットアクセスキーでS3のバケットリストを表示してみる。
		BasicSessionCredentials c = new BasicSessionCredentials(tmpCredentials.getAccessKeyId(), 
				tmpCredentials.getSecretAccessKey(), tmpCredentials.getSessionToken());
		AmazonS3Client s3Client = new AmazonS3Client(c);
		s3Client.setEndpoint("s3-ap-northeast-1.amazonaws.com");
					
		List<Bucket> list = s3Client.listBuckets();
		for(Bucket b : list) {
			System.out.println(b.getName());
		}
	}
}

TVMUser1というIAM Userで、S3のみにアクセス可能なポリシーをJSONで書いたものを使ってGetFederationTokenしている。
req.setPolicyでは以下のJSONデータを引数に入れている。

{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:*",
      "Resource": "*"
    }
  ]
}

このように、Security Token Serviceを使えば、一時的なCredentials情報を取得できることがわかった!

リンク