開発室ブログ

Alibaba Web API

Alibaba Cloud DNS API を Bash で試す

カメラ。そろそろ子供園の運動会なので望遠が欲しい。
未だに初期型SONY NEX-5なので、レンズ買うなら本体ごと変えるか…と迷っていたところ、もともと気になっていたSONY RX100シリーズに200mmが登場。これは買わざるを得ない。

SONY RX100 VI

少し使ってみた感じ、いろいろなレビューで語られてる印象とそんな変わらない…かな。今のところ、ほぼ自分の理想系。買ってよかったっぽい。 運動会で200mmというのはちと不安ですが、幼児はステージも狭くて近いからいける…はず?

そんな感じでカメラを新調した開発室員が、まーーーた DNS API のお話。
が、ちょっと変化球っぽく Alibaba なんぞを触ってみましたので、ご紹介したいと思います。

中国のクラウドサービス Alibaba Cloud

中国のAlibabaグループ が提供するクラウドサービスです。グループ代表 ジャック・マー氏も有名ですね。日本国内では、AWS・GCP・Azure等がほとんどでしょうから、利用は少ないと思われます。あるとしてもSBクラウドとか、中国向けサービス事業者さん、とかでしょうか。

ちなみに中国語だと 阿里雲 という表記っぽい。ちょっとおもしろい。

Alibaba Cloud DNS

制限は多いですが ベーシックDNS の利用は無料。
Alibaba Cloud DNS – 価格

今回はAPIですが、もちろんGUI管理もできます。HOME画面の Alibaba Cloud DNS からいじります。

使った感じ

結論から言うと、良くできていると思います。
あー、これ思ったよりも全然いいッスね…というのが率直なところ(DNSは)。

中国国内向けの機能なども色々あるっぽい感じ。現地では便利かもしれません。

マニュアルがよい

コレとかですね。 Alibaba Cloud – DNS APIリファレンス – Add Domain Names

日本語化はもちろんですし、読みやすい。そして「丁寧さ」みたいのを感じます。個人的には AWS よりこっちのほうが好き。

APIの応答もちゃんとしてるけど、ちと難しい

エラーハンドリングも細かいので、どこで間違ってるかがわかりやすいです。 ただ仕様が結構ややこしいので、ConoHa API のようにカジュアルにサクッとブン投げる…ようなことはできません。Postmanとかで扱えるのかなコレ?

環境

VSCodeの画面内で、WSLのUbuntu開いてやってました。OpenSSLコマンドは OpenSSL 1.0.2g となっています(別バージョンだと、HMAC生成の時の使い方が違うかも)。
URL Encode のため一部PHPを利用しています。あ、もちろん日本国内からアクセスです。

API利用の準備

Alibabaのユーザー作成は割愛します。 ひとまずカード登録などを行って作成・ログイン完了…という前提です。

RAMユーザーでやる

AWS の IAM のように、Alibaba も Resource Access Management(RAM) を利用してユーザーや権限の管理を行えます。

ユーザー作成と、アクセスキー発行

Alibabaクラウドのホームを開き ‘マネジメント -> Resource Access Management’ と遷移します。右メニューに「ユーザー」メニューがあるので進み、ユーザーを追加します。

登録

右上の新規作成ボタンを押してユーザーを追加しましょう。一番下にチェックを入れると、アクセスキーを自動生成してくれます。

登録

作成すると AccessKeyIDAccessKeySecret が発行されますので、控えておいてください。保存ボタンを押すと、CSVでダウンロードもできます。

RAMユーザーへ DNSサービスのアクセスを許可

ユーザー一覧の 「許可」 から個人用権限付与ポリシーの編集画面を開きまして AliyunDNSFullAccess を選択し、右ボックスへ追加します。 画像のように DNS とかで検索すると出てきます。

権限設定

ひとまずこれでOK。

Alibaba APIの基本? いろいろ

まずDNS APIにはこのような機能があります。高機能。
Alibaba Cloud – DNS APIリファレンス – API概要

で、呼び出し方は全体的にこうなっています。
Alibaba Cloud – DNS APIリファレンス – 呼び出し方法

今回はGETで投げてみます。文字コードはUTF8。
で、ここで大事(面倒)なのが パブリックパラメータ署名メカニズム です。

パブリックパラメータ?

例えば先ほどのドメイン追加マニュアルのサンプルを見ると

http://dns.aliyuncs.com/?Action=AddDomain
&DomainName=Chinese.com
&GroupId=2223
&<Public Request Parameters>

ここに入っている &<Public Request Parameters> ってのが該当します。アクセスに必要な共通パラメータ、という感じでしょうか。

こちらのデータを送ってあげる必要があります。
Alibaba Cloud – DNS APIリファレンス – 呼び出し方法 – Public Parameters

nonceに関して

ノンス…リプレイ攻撃を防ぐため SignatureNonce というパラメータを要求されます。全く同じリクエストを送信されて処理が再実行されることがないように、使い捨てかつランダムな値を入れてあげる…みたいな感じ。
今回は /dev/urandom の値を SHA1ハッシュにして渡すようにしています。

od -vAn -tu2 -N32 < /dev/urandom | sha1sum | cut -c 1-40

こんな感じで。

注意点

なんだくっつけるだけじゃねーかと思ったんですが、パブリックリクエストパラメータとそれぞれのAPI機能のパラメータをマージして、キーをアルファベット順に並べる必要があります(後述)。

署名メカニズム?

こちらです。
Alibaba Cloud – DNS APIリファレンス – 呼び出し方法 – Signature Mechanism

よーしややこしくなってまいりました。
Alibaba の API では HMAC-SHA1 というモノを利用して、送信パラメータ検証と認証を行います。 詳細はマニュアル中に記載されていますが、ざっくり言うと… 変数名でソートされたリクエストパラメータ一式 + α を、ユーザーキーでハッシュして、一緒に送信してね。という話と理解しています。リクエストを受け取った Alibaba も同じ共通鍵でパラメータをハッシュして、クライアント側のシグネチャと一致するかを調べる感じですかね。

HMAC-SHA1ハッシュ時のキー

HMAC-SHA1の仕様的には {USER-SEACRET}&{TOKEN-SEACRET} という感じで、二つの値をアンパサンドでつなげたものをキーとして利用するようです。が、今回はトークンがありませんので、ドキュメントの通り {USER-SEACRET}& という値でハッシュを求めます。 キーをそのまま使わない ってことに注意ですね。

とりあえずドメインの登録までやってみる

コードで見たほうが早い気が。
単純にBashでザクっと書く。

ドメイン登録はコレ。
Alibaba Cloud – DNS APIリファレンス – Add Domain Names

URLエンコード用の小物

なんでもいいですが、今回はPHPでエンコードします。実行権限もつけておく。
(shebang と PHPタグの間に改行とか入れないように…)

#!/usr/bin/php
<?php
mb_internal_encoding('UTF-8');
echo rawurlencode($argv[1]);

Alibaba用スクリプトと同じ階層に入れておきます。

bash

だーっと書きます

#!/bin/bash

# エンドポイント
ENDPOINT='https://dns.aliyuncs.com/'

# 認証情報
API_ACCESS_KEY_ID='TesttEstteSttesT'
API_ACCESS_KEY_SECRET='******SECRET******'

# HMACシグネチャのキーの最後に & 追加を忘れずに
HMAC_KEY="${API_ACCESS_KEY_SECRET}&"

# タイムスタンプ
# Alibaba リクエストサンプルに合わせる
ts=`date -u --iso-8601=second`
ts=`echo "${ts}" | sed -e "s/\+00\:00/Z/g"`

# リクエストパラメータをまとめる
# 順番に注意!
request=''
# [Public] アクセスキーID
request="AccessKeyId=${API_ACCESS_KEY_ID}"
# [Public] アクション
request="${request}&Action=AddDomain"
# 登録ドメイン名
request="${request}&DomainName=example.net"
# [Public] フォーマット
request="${request}&Format=json"
# [Public] シグネチャメソッド
request="${request}&SignatureMethod=HMAC-SHA1"
# [Public] nonce
request="${request}&SignatureNonce=`od -vAn -tu2 -N32 < /dev/urandom | sha1sum | cut -c 1-40`"
# [Public] シグネチャバージョン
request="${request}&SignatureVersion=1.0"
# [Public] タイムスタンプ(URLエンコードする)
request="${request}&Timestamp="$(./urlencode.php ${ts})
# [Public] バージョン
request="${request}&Version=2015-01-09"

# HMAC生成元のデータを作成
# 文字列先頭は固定で、スラッシュ(%2F)以外はエンコードなしってことに注意
StringToSignHead="GET&%2F&"
# URLエンコードしたリクエスト文字列をくっつける
StringToSign=${StringToSignHead}`./urlencode.php $(echo "${request}")`

# HMAC-SHA1シグネチャを生成
# opensslでハッシュして、base64エンコード
# PHPなら base64_encode(hash_hmac('sha1', $StringToSign, $secret, true)) とか
HMAC=`echo -n "${StringToSign}" | openssl dgst -sha1 -binary -hmac "${HMAC_KEY}" | base64`

# リクエストにシグネチャをくっつけて完成
# シグネチャはURLエンコードして送信
full_request='?'$(echo ${request})"&Signature="$(./urlencode.php ${HMAC})

# 投げる
jsonData=`curl -sS "${ENDPOINT}${full_request}" -H 'accept: application/json' `
echo $jsonData | jq .

Format=json にしてるので JSON でお返事がきます。一応 curl で accept: application/json と伝えておきます。

{
  "PunyCode": "example.net",
  "RequestId": "FFFFFFFF-9999-9999-9999-FFFFFFFFFF",
  "DomainName": "example.net",
  "DomainId": "FFFFFFFF-9999-0000-9999-FFFFFFFFFF",
  "DnsServers": {
    "DnsServer": [
      "ns7.alidns.com",
      "ns8.alidns.com"
    ]
  }
}

オッケーですね。画面からチェックしてみましょう。

ドメイン一覧

ふむ。
今回はやりませんが、レコード登録時は DomainId が必要になります。記録しておくなり変数に入れるなりしておきましょう。

まちがってたら…

例えば。上記のコードでリクエストの順番が間違えていた場合、こんな感じの応答でクソ親切に教えてくれます。

{
  "Recommend": "https://error-center.aliyun.com/status/search?Keyword=SignatureDoesNotMatch&source=PopGw",
  "Message": "Specified signature is not matched with our calculation. server string to sign is:GET&%2F&AccessKeyId%3D*********%26Action%3DAddDomain%26DomainName%3Dexample.net%26Format%3Djson%26SignatureMethod%3DHMAC-SHA1%26SignatureNonce%3Db432c9f950d775e6X964d9b57309326f462c43d7%26SignatureVersion%3D1.0%26Timestamp%3D2020-01-01T06%253A05%253A17Z%26Version%3D2015-01-09",
  "RequestId": "FFFFFFFF-9999-0000-9999-FFFFFFFFFF",
  "HostId": "dns.aliyuncs.com",
  "Code": "SignatureDoesNotMatch"
}

検証中、このおかげで動作しない原因が分かったので、大変助かりました。ありがてぇありがてぇ。

Alibaba、意外とよさそう?

日中間ネットワーク環境や中国側の制限、まぁその他諸々ありそうなので… 日本国内で「あえて Alibaba」とはならないと思いますが、もし利用することになっても普通に使いやすそう。
中国のIT技術、良くも悪くもいろいろ語られることが多くなってきましたが、もう相当なレベルなのだろなー、と(少し)実感できました。

RecentPost