企業システムにとって度々に重要な論点となるのが、インターネットを通した通信です。インターネットはあらゆる人がアクセスすることができるため、悪意のある攻撃者から身を守るためのセキュリティはもちろんのこと、通信品質や回線速度といった部分でも懸念が挙げられることがあります。そこで登場するのが、プラットフォームのバックボーン回線を用いた通信を実現するためのサービスです。今回はSAP BTPにより提供されているSAP Private Link Serviceというサービスを活用することで、SAP BTP上で開発したカスタムアプリケーションからAWS上で提供されているAmazon S3に接続し、内容を読み取るという内容を実装していきます。その際には、コードの大部分をSAP Samples (https://github.com/SAP-samples) を活用することで迅速に実装を行うことが可能です。
SAP Private Link Serviceは、SAP BTPのCloud Foundry 環境上にデプロイしたアプリケーションと、お客様自身のIaaSプロバイダーアカウント内の特定のサービスとの間にプライベート接続を確立するためのサービスです。このSAP Private Link Serviceを活用することで、ご利用のIaaSプロバイダーのプライベートリンク機能を再利用し、インターネット経由でのデータ転送を避けながら、プライベートネットワーク接続を介してSAP BTPからIaaSプロバイダーのサービスにアクセスできます。2023/12/10現在では、AWSとAzureについて、機能が提供されています。
アーキテクチャ
SAP BTPのjp10環境 (AWS東京リージョン) のCloud Foundry 環境にデプロイしたJavaアプリケーションから、同環境に構築したSAP Private Link Serviceを経由し、AWS環境、特にAmazon S3サービスに接続していきます。このAWS環境は、SAP BTPと同じく東京リージョン (ap-northeast-1) に構築したものであることに注意が必要です。
今回は、Spring BootベースのJava アプリケーションにおいて、特定のリンク( /bucket/{バケット名}/{ファイル名} ) にアクセスするとAmazon S3から一致するファイルを取得するようなロジックを実装します。この際のアクセス経路としては、SAP Private Link Serviceおよびプライベートネットワークを経由し、AWS IAMサービスにて認証認可を行い、Amazon S3へと到達します。うまくいくと、以下のような結果が得られます。
SAP Private Link Serviceを経由してHello World
以下に今回の検証における実装手順を記します。
「Amazon S3バケット」 と、「AWS IAMユーザーとそれに紐づくアクセスキー」を作成します。具体的なステップはAWSのドキュメントを参照してください。今回の検証においてはIAMユーザーには「AmazonS3FullAccess」というIAMポリシーを付与しましたが、極力必要最小限の権限の付与にとどめてください。
AWSコンソールからS3の管理画面に移動し、ステップ1で作成したS3バケットのルートディレクトリに、index.htmlをアップロードします。index.htmlの内容は任意ですが、例えば以下のように記述してみます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SAP Private Link Service</title>
</head>
<body>
<h1>Hello world from Amazon S3 via SAP Private Link Service!</h1>
<p>Note that both Services are located in Tokyo (ap-northeast-1 / jp10) Region.</p>
</body>
</html>
これでAmazon S3の設定は完了です。なお、以下のようにこのS3バケットへのパブリックアクセスは許可されていません。
パブリックアクセスの拒否設定
SAP BTP Cockpitにログインし、SAP Private Link Serviceのインスタンスを作成します。なお前提条件として、権限(entitlement) がサブアカウントに付与されていない場合は、必要に応じて付与を行なって下さい。「サービスマーケットプレイス」より、SAP Private Link Serviceのインスタンスを立ち上げます。
SAP Private Link Serviceのインスタンスを作成する
必要事項を記入したら、次ページで接続先のAWSサービスの情報を記入する必要があります。今回は東京リージョンのS3に接続するため、以下のように記入します。
{
"serviceName": "com.amazonaws.ap-northeast-1.s3",
"policyDocument": {
"Statement": [
{
"Principal": "*",
"Action": "s3:*Object",
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::<バケット名>/*"
]
}
]
}
}
「serviceName」が接続先のAWSサービス、「policyDocument」は対象サービスへのアクセスポリシーを表しています。本ポリシーでは、ステップ1で作成したバケット内の全てのオブジェクトに対し、全てのユーザーからの全ての操作を許可しています。
なお他のサービスへ接続する際には下記SAPヘルプを参照してserviceNameやその他のプロパティの値を決定して下さい。
https://help.sap.com/docs/private-link/private-link1/supported-services-for-amazon-web-services-in-sap-btp?locale=en-US
これでAWSのS3環境へのプライベート接続設定が完了しました。
Amazon S3へアクセスする主体となる、Javaアプリケーションを作成していきます。
SAP Business Application Studioにアクセスし、新規ターミナルを起動、下記コマンドを用いてSAP SamplesのGitHubリポジトリからアプリケーションをクローンします。
git clone https://github.com/SAP-samples/private-link-aws-services.git
クローン後は、基本的に ./ s3 / README.mdに記載された内容に従ってセットアップを行なっていきます。READMEに記載のある通り、Cloud Foundry環境にS3へのアクセスに用いる認証情報を設定します。Business Application Studioのターミナルで以下を実行します。
cf cups my-service-config -p '{"accountId": "<AWS のアカウントID>", "accessKeyId": "<IAMのAccessKey>", "secretAccessKey": "<IAMのsecretAccessKey>", "region": "ap-northeast-1"}'
その後、ビルドとデプロイを行います。こちらもREADMEに記載の通りですが、以下のコマンドを実行します。
./mvnw package
cf push
デプロイが正常に完了すると、アプリケーションのルートURLがターミナル内に表示されます。そのルートURLにバケットとファイル名を付け足した、以下のようなアクセスしてみます。
https://<アプリケーションのルートURL>/bucket/<バケット名>/index.html
すると、次のようなページが表示されるはずです。
Amazon S3からSAP BTPへのデータ取得に成功
これでSAP BTPからAmazon S3への、SAP Private Link経由でのアクセスは成功です。もう一歩進んで、内容を確認するためにクローンしたアプリケーションを少し改変してindex.htmlの中身を表示するようにしてみましょう。
SAP Samplesはあくまでサンプルソースコードのため、必要に応じて改変ができます。SAP Business Application Studioでアプリケーションの構造を確認すると、「./src/main/ java/com/sap/pls/samples/s3」のディレクトリに存在する「Controller.java」でリクエストを処理していることが確認できます。77~97行目のgetObjectsFromBucketという関数が、今回のファイル取得に対するリクエストを処理しています。ここを以下のように書き換えてみましょう。
@GetMapping(value = "/bucket/{bucketName}/{fileName}", produces = "application/json; charset=UTF-8")
public ResponseEntity getObjectsFromBucket(@PathVariable("bucketName") String bucketName,
@PathVariable("fileName") String fileName) {
GetObjectRequest request = GetObjectRequest.builder()
.bucket(bucketName)
.key(fileName)
.build();
try {
ResponseBytes<GetObjectResponse> response = s3Client.getObjectAsBytes(request);
byte[] data = response.asByteArray();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_HTML);
return new ResponseEntity<>(data, headers, HttpStatus.OK);
} catch (S3Exception e) {
logger.error("Getting objects of the bucket: '{}' with id '{}'. Status: {} Error: {}",
bucketName, e.requestId(), e.statusCode(), e.getMessage());
return ResponseEntity
.status(e.statusCode())
.body(e.getMessage());
}
}
新しく登場したオブジェクトについて、依存関係をインポートする必要がありますが、波線にホバーすると表示されるQuickFix機能を用いて適宜インポートを行なって下さい。
修正が完了したら、再度ビルド、デプロイを行なっていきます。s3のディレクトリで以下のコマンドを再度実行して下さい。
./mvnw package
cf push
ここまで完了すると、先ほどと同じリンクで冒頭にご紹介した画面が見えるようになるはずです。
これで検証は完了です。
IAMユーザーの情報がmy-service-configに残った状態になっています。安全のため、以下のコマンドを実行してアプリケーション本体と合わせて削除しておきましょう。なお、s3-pls-demoは本アプリケーションのデフォルトの名前です。変更した場合には、manufest.yaml内のapplication – nameの値を入力して下さい。
cf delete-service my-service-config -f
cf delete -r s3-pls-demo
本記事では、SAP Private Link Serviceを活用したプライベート接続について取り上げました。ぜひ試してみていただけると嬉しいです。