Upgrade to Pro — share decks privately, control downloads, hide ads and more …

HCL Notes で管理する AWS リソース

HCL Notes で管理する AWS リソース

ノーツコンソーシアム( https://www.notescons.gr.jp/ )では、会員に向けて「デモ環境」を提供しています。
「デモ環境」は HCL Notes と HCL Domino を使用する会員の「顧客」や「社内の人」に向けて新しい Notes と Domino を使用したデモやを行うことを想定したものです。
この資料では「デモ環境」を作成したり状況を確認するといった「要求」を AWS へ送信する方法と、要求した結果の成否を判定する方法について説明するものです。
要求する手段として AWS CLI などのコマンドラインインターフェースや C++ などの言語用に提供されている AWS SDK を使用せず、 API オペレーション を採用しています。

2022/12/8 開催の DominoHub 2022 で使用した資料です。

Haruyuki Nakano

December 09, 2022
Tweet

More Decks by Haruyuki Nakano

Other Decks in Education

Transcript

  1. AWS への要求とは • リソースの作成/削除 ec2 create-vpc ec2 run-instances cloudformation create-stack

    • リソースの一覧の取得 ec2 describe-instances • 特定のリソースの情報の取得 ec2 describe-instances –instance-id <インスタンスID> ec2 get-password-data –instance-id <インスタンスID>
  2. AWS マネジメントコンソールを使わず Notes アプリから AWS へ要求する手段 • コマンドラインツール AWS の

    API をコマンドラインから操作できるようにするツール AWS CLI, PowerShell Tools等 • AWS SDK AWS のサービスをプログラムなどから操作できるようにするための開発キット C++, Java, JavaScript等 • API オペレーション 「https:/weather.acme.com/?country=jp&city=tokyo」と要求すると 「{“date”: “2022/12/08”, “forecasts”: “sunny”}」などと返すような仕組み
  3. (脳内ひとりごと) • コマンドラインツールはインストール必要。ツールのバージョン管 理はしたくないし実行環境が固定されるし、コマンドファイルの扱 いは環境依存のコードになりがち… • SDK の Java で作ると

    Nomad で使えない ※Nomad には Java の実行環境がない • API オペレーションは NotesHTTPRequest クラスが活用できるし、 そうすれば Windows 版クライアントやサーバーだけでなく Mac 版クライアントやスマホ(Nomad Mobile)でも実行できそう
  4. CloudFormation テンプレートの作成(2) 「Parameters:」(一部抜粋) InstanceType: Description: EC2 Instance type Type: String

    Default: t2.large パラメータの「キー」や入力時にパラメータを省略した 場合のデフォルト値などを指定
  5. CloudFormation テンプレートの作成(3) 「Resources:」の例 EC2Instance: Type: AWS::EC2::Instance Properties: ImageId: !Ref AMIID

    InstanceType: !Ref InstanceType KeyName: !Ref KeyPair NetworkInterfaces: - GroupSet: - !Ref SecurityGroupID AssociatePublicIpAddress: true DeviceIndex: 0 DeleteOnTermination: true SubnetId: !Ref SubnetID Route53RecordSet: Type: AWS::Route53::RecordSet Properties: HostedZoneName: !Join [".", [!Ref HostedZoneName, ""] ] Comment: A record for instance Name: !Join [".", [!Ref AWS::StackName, !Ref HostedZoneName, ""] ] Type: A TTL: !Sub ${TTL} ResourceRecords: - !GetAtt EC2Instance.PublicIp • Notes と Domino が入ったイメージ(AMI)が作成済みで、 それを使用して EC2 インスタンスを作成 • ユーザーが分かりやすいホスト名で EC2 インスタンスへア クセスできるよう、 DNS レコードを Route53 へ追加
  6. LotusScript で NotesHTTPRequest クラスを使用した アクション要求のイメージ Dim ss as New NotesSession

    Dim req as NotesHTTPRequest Dim res as NotesJSONNavigator set req = ss.CreateHTTPRequest() req.PreferJSONNavigator = True req.Setheaderfield “headerItem”, “headerValue” set res = req.Get( "url" ) If 0 = InStr( req.Responsecode, "200" ) Then Print "Error" End If よくある LotusScript のパターンが そのまま使えるんでしょ? と考えてましたが…
  7. LotusScript で NotesHTTPRequest クラスを使用した アクション要求のイメージ Dim ss as New NotesSession

    Dim req as NotesHTTPRequest Dim res as NotesJSONNavigator set req = ss.CreateHTTPRequest() req.PreferJSONNavigator = True req.Setheaderfield "headerItem", "headerValue" set res = req.Get( "url" ) If 0 = InStr( req.Responsecode, "200" ) Then Print "Error" End If AWS は甘くなかった… orz
  8. アクションを要求するために 認証情報が必要 アクションを要求したユーザーを AWS が特定するため認証情報 を渡す必要がある 【参考】認証情報を渡す際に使用される方法(AWS 以外): • HTTP

    ヘッダー “Authorization:” にユーザー名とパスワードをコロンでつなげ た値を base64 エンコードしてできた文字列をセットする • ユーザー名とパスワードを本文にセットし、POST メソッドで要求すると返って くる「認証トークン」を保持しておき、次回以降のアクション要求時に HTTP ヘッダー “Authorization: ” へセットする
  9. AWS CLI や SDK では自動で行われている リクエストの署名 • リクエストを送るとき、API リクエストの送信者を AWS

    が特 定できるようリクエストに署名する • 署名する際に「AWS アクセスキー」を使用する • 一部のAPIオペレーションは署名が不要のものもある
  10. API オペレーションでは計算が必須 AWS 署名バージョン 4 の署名 署名作成のタスク: 1. 正規リクエストの作成 2.

    署名文字列の作成 3. 署名の計算 4. リクエストへの署名の追加 https://docs.aws.amazon.com/ja_jp/general/latest/gr/sigv4_signing.html
  11. AWS 署名バージョン 4 の署名 > 署名作成のタスク 1. 正規リクエストの作成 正規リクエストは、以下を改行で区切って連結した文字列: (1)

    HTTP リクエストメソッド GET、PUT、POSTなど (2) 正規 URI パラメータ / ※空の場合はスラッシュを指定 (3) 正規クエリ文字列 ※後述 (4) 正規ヘッダー 署名に含める HTTP ヘッダーのリスト ヘッダー名:小文字に変換、前後にあるスペースを除去 ヘッダー値:連続するスペースを単一のスペースに変換 ヘッダー名、コロン(:)、ヘッダー値、改行文字の順に正規ヘッダーへ追加 (5) 署名に含めるヘッダー 正規ヘッダーに含めたヘッダーのリスト すべてのヘッダー名を小文字に変換、文字コードでソート、ヘッダー名をセミコロンで区切る (6) ペイロードのハッシュ値 リクエストの本文のペイロードから作成したハッシュ値 署名文字列を作成するとき、ペイロードのハッシュに使用した署名アルゴリズムを指定 ※SHA256を使用した場合、署名アルゴリズムとして “AWS4-HMAC-SHA256” を指定 ペイロードのハッシュ値は、小文字の16進文字列で表わす必要あり
  12. AWS 署名バージョン 4 の署名 > 署名作成のタスク > 1. 正規リクエストの作成 (3).

    正規クエリ文字列 クエリ文字列(例): ※実際のクエリ文字列は改行しません Action=CreateStack& Parameters.member.1.ParameterKey=InstanceType& Parameters.member.1.ParameterValue=t3.large& Parameters.member.2.ParameterKey=AMIID& Parameters.member.2.ParameterValue=ami-000000aaaa00aaaa0& StackName=UserShortName01a& TemplateURL=https%3A%2F%2Fs3-ap-northeast-1.amazonaws.com%2Fcf-templates-ap-northeast-1%2Fcf.domino1201FP1.xxxx& Version=2010-05-15 正規クエリへ追加するパラメータ(例): • すべてのアクションに共通して必要なパラメータは”Action”と”Version”の2つ(青太字) • アクション「CreateStack」に必要なパラメータは“StackName”のみ(赤太字) • CloudFormation テンプレートのURL(上記は AWS S3 にアップロード済み)を”TemplateURL”で示す(黒太字) • CloudFormation テンプレートへ渡すパラメータのキーを ”Parameters.member.n.ParameterKey”、 値 を ”Parameters.member.n.ParameterValue” で始まるパラメータで示す。※nは数字
  13. AWS 署名バージョン 4 の署名 > 署名作成のタスク > 1. 正規リクエストの作成 (3).

    正規クエリ文字列 クエリ文字列(例): ※実際のクエリ文字列は改行しません Action=CreateStack& Parameters.member.1.ParameterKey=InstanceType& Parameters.member.1.ParameterValue=t3.large& Parameters.member.2.ParameterKey=AMIID& Parameters.member.2.ParameterValue=ami-000000aaaa00aaaa0& StackName=UserShortName01a& TemplateURL=https%3A%2F%2Fs3-ap-northeast-1.amazonaws.com%2Fcf-templates-ap-northeast-1%2Fcf.domino1201FP1.xxxx& Version=2010-05-15 正規クエリ文字列の作成ルール: a. パラメータ名(上記太字)は、文字コードの昇順にソートする b. 各パラメータ名と値を URI エンコードする c. URI エンコードしたパラメータ名、等号文字(=)、URI エンコードしたパラメータ値の順で正規クエリ文字列へ追加する d. パラメータとパラメータの間にアンパサンド文字(&)を追加する
  14. AWS 署名バージョン 4 の署名 > 署名作成のタスク > 1. 正規リクエストの作成 (6).

    ペイロードのハッシュ値 ハッシュ値を求めるサンプルコード(Java) ※ハッシュアルゴリズムに SHA-256 を指定 public static String getHashedString( String orgString ) throws Exception { String hashedStr = ""; MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update(orgString.getBytes()); byte[] cipher_byte = md.digest(); StringBuilder sb = new StringBuilder(2 * cipher_byte.length); for(byte b: cipher_byte) { sb.append(String.format("%02x", b&0xff) ); } hashedStr = sb.toString(); return hashedStr; }
  15. AWS 署名バージョン 4 の署名 > 署名作成のタスク 1. 正規リクエストの作成 正規リクエスト(例): GET

    / Action=CreateStack&Parameters.member.1.ParameterKey=InstanceType&Parameters.member.1.ParameterValue=t3.large&Parameters.me mber.2.ParameterKey=AMIID&Parameters.member.2.ParameterValue=ami -000000aaaa00aaaa0&StackName=UserShortName01a&Template URL=https%3A%2F%2Fs3-ap-northeast-1.amazonaws.com%2Fcf-templates-ap-northeast-1%2Fcf.domino1201FP1.xxxx&Version=2010-05- 15 host:cloudformation.ap-northeast-1.amazonaws.com x-amz-date:20221208T065641Z host;x-amz-date e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 ペイロードは空っぽでもハッシュ値は生成される
  16. AWS 署名バージョン 4 の署名 > 署名作成のタスク 2. 署名文字列の作成 署名文字列は、以下を改行で区切って連結した文字列: 1

    アルゴリズム 正規リクエストでダイジェストの計算に使用しているハッ シュアルゴリズム ※ SHA256 では AWS4-HMAC-SHA256 2 リクエスト日時 リクエストを要求する日時 ISO8601形式を使用、正規リクエストの作成で使用した値と同じ 3 認証情報スコープ 日付、対象とするリージョン、リクエストしているサービス、 小文字の終了文字列(”aws4_request”)をスラッシュ文字で 区切った文字列 4 正規リクエストのハッシュ 1つ前のタスク「1. 正規リクエストの作成」で作成した正規 リクエストのハッシュ値 ※ハッシュ値を求めるコードは「(6).ペーロードのハッシュ値」を使用
  17. AWS 署名バージョン 4 の署名 > 署名作成のタスク 2. 署名文字列の作成 署名文字列(例): AWS4-HMAC-SHA256

    20221208T065641Z 20221208/ap-northeast-1/cloudformation/aws4_request bc16905d349cb24baf05fd74aed5c6a49f6755eb23a45a7cda94a97c485acf2c
  18. AWS 署名バージョン 4 の署名 > 署名作成のタスク 3. 署名の計算 ① 署名キーの取得:

    シークレットアクセスキーを使い、認証情報スコープで使用した値(日付、対象 とするリージョン、リクエストしているサービス、小文字の終了文字列)につい てハッシュを計算する ② 署名の計算: 取得した署名キーと1つ前のタスク「2. 署名文字列の作成」で作成した署名文 字列を使用してハッシュを計算し、バイナリ値を16進表現へ変換する
  19. AWS 署名バージョン 4 の署名 > 署名作成のタスク > 3. 署名の計算 ①.

    署名キーの取得 署名キーを得るための疑似コード: HMAC( HMAC( HMAC( HMAC( "AWS4" + “SampleSecretAccessKey”, "20221208" ), “ap-northeast-1“ ), “cloudformation“ ), "aws4_request“ ) 【HMAC】 Hash Based Message Authentication Code の略で 暗号鍵とハッシュ関数を組み合わせて不可逆 メッセージを生成する、メッセージ認証の暗号 化技術のひとつ
  20. AWS 署名バージョン 4 の署名 > 署名作成のタスク > 3. 署名の計算 ②.

    署名の計算 署名を得るための疑似コード: HexEncode( HMAC( 前頁<①. 署名キーの取得>で得た署名キー, タスク<2. 署名文字列の作成>で得た署名文字列 ) ) HexEncode はバイナリ値を16進表現に変換する
  21. 【参考】①②のコードサンプル 署名の計算(Java) static byte[] HmacSHA256(String data, byte[] key) throws Exception

    { String algorithm="HmacSHA256"; final SecretKeySpec keySpec = new SecretKeySpec(key, algorithm); final Mac mac = Mac.getInstance(algorithm); mac.init(keySpec); return mac.doFinal(data.getBytes("UTF-8")); } static byte[] getSignatureKey(String key, String dateStamp, String regionName, String serviceName) throws Exception { byte[] kSecret = ("AWS4" + key).getBytes("UTF-8"); byte[] kDate = HmacSHA256(dateStamp, kSecret); byte[] kRegion = HmacSHA256(regionName, kDate); byte[] kService = HmacSHA256(serviceName, kRegion); byte[] kSigning = HmacSHA256("aws4_request", kService); return kSigning; } public static String getHashedSignature(String key, String dateStamp, String regionName, String serviceName, String stringToSign) throws Exception { String signature = ""; byte[] signatureKey = getSignatureKey(key, dateStamp, regionName, serviceName); byte[] signatureByte = HmacSHA256(stringToSign, signatureKey); BigInteger bi = new BigInteger(1, signatureByte); signature = String.format("%0" + (signatureByte.length << 1) + "x", bi); return signature; }
  22. AWS 署名バージョン 4 の署名 > 署名作成のタスク 3. 署名の計算 署名(例): bfe81de8b4f4b85cda4418d9eafd3bb6269808ab0f029e2dff5970de5736c8ec

    前ページの Java コードを LS2J で呼び出して署名を計算させる(LotusScript) UseLSX "*javacon" Use "hashLib" Dim js As New javasession Dim jc As JavaClass Dim jo As JavaObject Set jc = js.Getclass("HashLib") Set jo = jc.Createobject signature = jo.getHashedSignature( AWS_SECRET_ACCESS_KEY, dateStamp, regionName, service, StringToSign )
  23. AWS 署名バージョン 4 の署名 > 署名作成のタスク 4. リクエストへの署名の追加 署名をリクエストに追加する方法(どちらか一方) •

    HTTP ヘッダー(Authorization) • クエリ文字列 ※「https://◦△□.com/?•=▲」の?以降の文字列
  24. AWS 署名バージョン 4 の署名 > 署名作成のタスク 4. リクエストへの署名の追加 署名をリクエストに追加する方法(どちらか一方) •

    HTTP ヘッダー(Authorization) • クエリ文字列 ※「https://◦△□.com/?•=▲」の?以降の文字列
  25. AWS 署名バージョン 4 の署名 > 署名作成のタスク 4. リクエストへの署名の追加 Authorization ヘッダーの構成:

    Authorization: アルゴリズム Credential=アクセスキーID/認証情報スコープ, SignedHeaders=署名付きヘッダー, Signature=署名 アルゴリズムと credential の間にカンマはないが、SignedHeaders と Signature は前の値とカンマで区切る アルゴリズム AWS4-HMAC-SHA256 アクセスキーID/認証情報スコープ AKIDEXAMPLE/20221019/ap-northeast-1/cloudformation/aws4_request 署名付きヘッダー host;x-amz-date 署名 bfe81de8b4f4b85cda4418d9eafd3bb6269808ab0f029e2dff5970de5736c8ec
  26. リクエストの送信先 AWS サービスエンドポイント サービスエンドポイントの一般的な構文: サービスコード.リージョンコード.amazonaws.com 例えば https://cloudformation.ap-northeast-1.amazonaws.com はアジアパシフィック(東京)の CloudFormation サービスのエンドポイントです

    サービスコード 「CloudFormation」のサービスコードは “cloudformation” リージョンコード 「アジアパシフィック(東京)」のリージョンコードは ”ap-northeast-1” ※リージョンコードを含まない(あるいはサポートされていない)サービス もある
  27. リクエストの内容をサービスへ伝える クエリ文字列 HTTP リクエストのURL: プロトコル://AWS サービスエンドポイント/?クエリ文字列 URL(例): ※クエリ文字列の詳細は署名作成のタスク「1. 正規リクエストの作成 >

    (3).正規クエリ文字列」を参照 https://cloudformation.ap-northeast-1.amazonaws.com/ ?Action=CreateStack &Parameters.member.1.ParameterKey=InstanceType &Parameters.member.1.ParameterValue=t3.large &Parameters.member.2.ParameterKey=AMIID &Parameters.member.2.ParameterValue=ami-000000aaaa00aaaa0 &StackName=UserShortName01a &TemplateURL=https%3A%2F%2Fs3-ap-northeast-1.amazonaws.com%2Fcf-templates-ap-northeast-1%2Fcf.domino1201FP1.xxxx &Version=2010-05-15
  28. LotusScript で NotesHTTPRequest クラスを使用した アクション要求のコード(例) Sub httpReq( url, hAuthorization$, hHost$,

    hDate$ ) Dim ss as New NotesSession Dim req as NotesHTTPRequest Dim nav as NotesJSONNavigator set req = ss.CreateHTTPRequest() req.PreferJSONNavigator = True req.SetHeaderField “Authorization”, hAuthorization req.SetHeaderField “Host”, hHost req.SetHeaderField “X-Amz-Date”, hDate set nav = req.Get( url ) ‘※以降の処理は「レスポンスの処理」へつづく End Sub 3つの HTTP リクエストヘッダーをセットする (Authorization, Host, X-Amz-Date) HTTPリクエストのレスポンスがJSON形式のとき PreferJSONNavigator プロパティに True を設定する ※ PregerJSONNavigator は 10.0.1 FP2 以降のバージョンで使用可能 Get メソッドで要求し、レスポンスをセットする NotesHTTPRequest クラスと NotesJSONNavigator クラスは 10.0.1 以降のバージョンで使用可能
  29. HTTP リクエストの レスポンスコード set nav = req.Get( url ) If

    0 = InStr( req.ResponseCode, “200” ) Then Print “失敗しました:” & req.Responsecode End If ResponseCode プロパティには、 Get メソッドが成功すると “HTTP/1.1 200 OK“ がセットされます。 署名の日付が不一致だったり、AWS への認証で拒否されたり、指定 したアクションが無効だったなど Get メソッドが失敗した場合は "HTTP/1.1 400 Bad Request" や "HTTP/1.1 403 Forbidden" の ように “200” が含まれない。
  30. JSON データの解析に使用するクラス NotesJSONNavigator Dim nav as NotesJSONNavigator : req.PreferJSONNavigator =

    True : set nav = req.Get( url ) : ‘※次ページへつづく HTTP リクエストのレスポンスが JSON で戻るため NotesJSONNavigator へセットする
  31. JSON 上の特定の位置にある要素の値を取得するクラス NotesJSONElement Dim elm As NotesJSONElement Dim pointer As

    String Dim stackId As String pointer = "/CreateStackResponse/CreateStackResult/StackId" Set elm = nav.GetElementByPointer( pointer ) If elm Is Nothing Then Print "ポインタ「" & pointer & "」が見つかりません。" Else stackId = elm.Value End If 指定したポインタにある要素を NotesJSONElement へセット JSON 上のポインタ JSON オブジェクトに特定のポインタがあれば、その値を保持する NotesJSONElement クラスは 10.0.1 以降のバージョンで使用可能
  32. アクション「DescribeInstances」を要求して返される XML形式のレスポンス(例) <?xml version="1.0" encoding="UTF-8"?> <DescribeInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/"> <requestId>040cb45c-2e5d-4980-96fd-334330e22ddd</requestId> <reservationSet> <item>

    <instancesSet> <item> <instanceId>i-05c35fb2b5d6c97e5</instanceId> <privateIpAddress>10.0.101.60</privateIpAddress> <ipAddress>43.206.101.248</ipAddress> </item> </instancesSet> </item> </reservationSet> </DescribeInstancesResponse>
  33. XML ? JSON ? 戻り値の型はドキュメントに記載なし?! (苦し紛れの)コード Dim ret As Variant

    : req.PreferStrings = False req.PreferJSONNavigator = False ret = req.Get( url ) : If isArray( ret ) Then set nav = ss.CreateJSONNavigator( ret ) response = ret.Stringify() Else response = ret End If : Exit Sub Stringify メソッドは 11.0 以降のバージョンで使用可能 PreferJSONNavigator が False の場合に JSON が戻るとバイト配列がセットされた XML が戻ると文字列がセットされた 【注意】 上のコードは筆者の環境では動作しますが、決してお勧めしません。事前に戻り値のタイプ を調べ、戻り値が適切なデータ型にセットされるコードを書きましょう
  34. 戻り値の型に関する Document は無い?けれど常に JSON で返せますか...? リクエストヘッダーに Accept: application/json をセットすると JSON

    で返す、といった 情報が某海外QAサイトにあったので アクション DescribeInstances で試したところ XML が戻りました...残念!!!