Oracle Cloud「REST API」を利用する(OCI-CURL)

Oracle Cloud「REST API」を利用する(OCI-CURL)

目次

はじめに
REST APIを利用するためにインストールするもの
OCI-CURLのダウンロードと設定
OCI-CURLの実行
おわりに

はじめに

Oracle Cloud の管理画面上で操作できることは Oracle Cloud Infrastructure API(OCI API)で同様、もしくはそれ以上の細かな操作が可能です。
Webの管理画面は簡略化している部分もあり、画面上で操作できない細かな設定などもOCI APIでは操作が可能なため非常に便利です。

ただ、CUI上の操作の場合は、REST APIでは無く簡単に利用ができるOCI-CLI(コマンド・ライン・インターフェース)を使うことが多いのではないでしょうか。

私がOCIのREST APIを利用しようと思ったきっかけは、OCI-CLIだとどうしても上手く動かないコマンドがありました。かなりハマったのですが色々試行した結果、OCI APIで期待通りの値が得られました。

これを機にREST APIを利用する方法を今回は記載したいと思います。
以下は、OCI APIを利用するために参考にしたドキュメントになります。

API Documentation
https://docs.cloud.oracle.com/iaas/api/
…ちなみによく利用しそうな Compute関連は「Core Services API」の項目にあります

API各サービスのエンドポイント
https://docs.cloud.oracle.com/iaas/Content/API/Concepts/apiref.htm

Oracle Cloud Infrastructure (OCI) REST call walkthrough with curl
https://www.ateam-oracle.com/oracle-cloud-infrastructure-oci-rest-call-walkthrough-with-curl
…curlを使用してREST APIをコールする方法です

OCI CURL
https://docs.oracle.com/cd/E97706_01/Content/Functions/Tasks/functionscreatinglocalocicurl.htm
https://docs.cloud.oracle.com/iaas/Content/API/Concepts/signingrequests.htm

REST APIを利用するための準備

まずは実行する環境に以下をインストールしておきましょう

  • openssl
  • curl

次にAPIを利用する際にはOCI上にアイデンティティユーザーとそのユーザーにAPIキーを登録する必要があります。OCI画面上でアイデンティティユーザーとグループを作成し利用したいAPIに応じて必要なポリシーを当てましょう。例としてコンパートメント内のWAFのリソースに対して管理権限をグループに与える場合は以下となりますよね。

allow group <グループ名> to manage waas-policy in compartment <コンパートメント名>

また、APIを利用する際には秘密鍵と公開鍵のペアが必要になります。
秘密鍵/公開鍵ペアの作成はドキュメントを参考にして以下のコマンドで作成しました。

# ディレクトリ作成
mkdir ~/.oci

# パスフレーズ無しで秘密鍵を生成
openssl genrsa -out ~/.oci/oci_api_key.pem 2048

# ファイルのパーミッションを適正な権限に変更
chmod go-rwx ~/.oci/oci_api_key.pem

# 秘密鍵を使い公開鍵を生成
openssl rsa -pubout -in ~/.oci/oci_api_key.pem -out ~/.oci/oci_api_key_public.pem

出来上がった公開鍵「oci_api_key_public.pem」の中身をアイデンティティユーザーのAPIキーに登録します。

APIキーの登録

OCI-CURLの設定

まず、「OCI-CURL」の説明をする前に、REST API を利用するためには、実際にどのようなコマンドになるか確認してみました。以下はJSON形式にてwafログの取得する例です。

curl -X GET -sS https://waas.ap-tokyo-1.oraclecloud.com/20181116/waasPolicies/<waasPolicyId>/wafLogs?waasPolicyId=<waasPolicyId>&logType=PROTECTION_RULES&timeObservedGreaterThanOrEqualTo=<FromDate>&timeObservedLessThan=<ToDate> -H date: <CurrentDate> -H Authorization: Signature version="1",keyId="<TenancyOCID>/<AuthUserOCID>/<FingerPrint>",algorithm="rsa-sha256",headers="(request-target) date host",signature="<Signature>"

<signature>に入る値などもopensslで電子署名を生成しbase64でエンコードしないといけないため、手でAPIをコールするためには難易度が高そうですね、、
改めてこのコマンドを簡単に実行できるようにOracleが便利なBashを用意しています。

公式で公開しているBashはこちらになりますが、私の環境でどうも上手くいかなかったため少し改変しましたので以下にその内容を記載します。


# Version: 1.0.2 ( IT PORT Ver.)
# Usage:
# oci-curl <host> <method> [file-to-send-as-body] <request-target> [extra-curl-args]
#
# ex:
# oci-curl iaas.us-ashburn-1.oraclecloud.com get "/20160918/instances?compartmentId=some-compartment-ocid"
# oci-curl iaas.us-ashburn-1.oraclecloud.com post ./request.json "/20160918/vcns"

arg1=$1
arg2=$2
arg3=$3
arg4=$4
arg5=$5

function oci-curl {
    # TODO: update these values to your own
	local tenancyId="OCIDテナンシID";
	local authUserId="アイデンティティユーザーのOCID";
	local keyFingerprint="APIキーのフィンガープリント";
	local privateKeyPath="APIキーの秘密鍵の場所";

    local alg=rsa-sha256
    local sigVersion="1"
    local now="$(LC_ALL=C \date -u "+%a, %d %h %Y %H:%M:%S GMT")"
    local host=$arg1
    local method=$arg2
    local extra_args
    local keyId="$tenancyId/$authUserId/$keyFingerprint"
    
    case $method in

        "get" | "GET")
            local target=$arg3
            extra_args=$arg4
            local curl_method="GET";
            local request_method="get";
            ;;

        "delete" | "DELETE")
            local target=$arg3
            extra_args=$arg4
            local curl_method="DELETE";
            local request_method="delete";
            ;;

        "head" | "HEAD")
            local target=$arg3
            extra_args=("--head" $arg4)
            local curl_method="HEAD";
            local request_method="head";
            ;;

        "post" | "POST")
            local body=$arg3
            local target=$arg4
            extra_args=$arg5
            local curl_method="POST";
            local request_method="post";
            local content_sha256="$(openssl dgst -binary -sha256 < $body | openssl enc -e -base64)";
            local content_type="application/json";
            local content_length="$(wc -c < $body | xargs)";
            ;;

        "put" | "PUT")
            local body=$arg3
            local target=$arg4
            extra_args=$arg5
            local curl_method="PUT"
            local request_method="put"
            local content_sha256="$(openssl dgst -binary -sha256 < $body | openssl enc -e -base64)";
            local content_type="application/json";
            local content_length="$(wc -c < $body | xargs)";
            ;;

        *) echo "invalid method"; return;;
    esac

    # This line will url encode all special characters in the request target except "/", "?", "=", and "&", since those characters are used 
    # in the request target to indicate path and query string structure. If you need to encode any of "/", "?", "=", or "&", such as when
    # used as part of a path value or query string key or value, you will need to do that yourself in the request target you pass in.
    local escaped_target="$(echo $( rawurlencode "$target" ))"
    
    local request_target="(request-target): $request_method $escaped_target"
    local date_header="date: $now"
    local host_header="host: $host"
    local content_sha256_header="x-content-sha256: $content_sha256"
    local content_type_header="content-type: $content_type"
    local content_length_header="content-length: $content_length"
    local signing_string="$request_target\n$date_header\n$host_header"
    local headers="(request-target) date host"
    local curl_header_args
    curl_header_args=(-H "$date_header")
    local body_arg
    body_arg=()

    if [ "$curl_method" = "PUT" -o "$curl_method" = "POST" ]; then
        signing_string="$signing_string\n$content_sha256_header\n$content_type_header\n$content_length_header"
        headers=$headers" x-content-sha256 content-type content-length"
        curl_header_args=("${curl_header_args[@]}" -H "$content_sha256_header" -H "$content_type_header" -H "$content_length_header")
        body_arg=(--data-binary @${body})
    fi

    local sig=$(printf '%b' "$signing_string" | \
                openssl dgst -sha256 -sign $privateKeyPath | \
                openssl enc -e -base64 | tr -d '\n')

    curl "${extra_args[@]}" "${body_arg[@]}" -X $curl_method -sS https://${host}${escaped_target} "${curl_header_args[@]}" \
         -H "Authorization: Signature version=\"$sigVersion\",keyId=\"$keyId\",algorithm=\"$alg\",headers=\"${headers}\",signature=\"$sig\""
  
}

# url encode all special characters except "/", "?", "=", and "&"
function rawurlencode {
  local string="${1}"
  local strlen=${#string}
  local encoded=""
  local pos c o

  for (( pos=0 ; pos<strlen ; pos++ )); do
     c=${string:$pos:1}
     case "$c" in
        [-_.~a-zA-Z0-9] | "/" | "?" | "=" | "&" ) o="${c}" ;;
        * )               printf -v o '%%%02x' "'$c"
     esac
     encoded+="${o}"
  done

  echo "${encoded}"
}

oci-curl

修正したポイントは3つです。(ソースをハイライトした箇所です)

  • テナンシOCID/ユーザーのOCID/APIキーのフィンガープリント/APIキーの秘密鍵の場所の指定
  • コマンドの引数を一旦変数に格納するように変更
  • スクリプト末尾に「oci-curl」関数の呼び出しを追加

コマンドの引数を「oci-curl」関数の中で使用していたのですが、私のマシン?ではどうも引数の値が取得出来ていなかったようなので、一旦変数の中に引数の値を入れました。また、末尾に関数の呼び出しを追加しています。
スクリプトが出来たら上記のBashを任意の場所に配置します。( 利用するユーザーのPATHが通る場所に実行スクリプトを配置するのが便利です。)

OCI-CURLの実行

さて、ようやく実行出来る状態になりました。
OCI-CURLを実行する際は「oci-curl <ホスト> <メソッド> <リクエストターゲット> 」もしくは「oci-curl <ホスト> <メソッド> <ファイル> <リクエストターゲット> 」というような形で引数を指定して実行します。基本的にGETDELETEHEADメソッドの場合は3つの引数を指定します。

第一引数ホスト(APIエンドポイント)
第二引数メソッド(GET/DELETE/HEAD)
第三引数リクエストターゲット+パラメーター

例としてコンパートメント内のインスタンスのリスト一覧を取得してみます。まず第一引数のホストですが、公式ドキュメントによると「Core Services API」の東京リージョンのエンドポイントは以下となります。

API Reference and Endpoint
iaas.ap-tokyo-1.oraclecloud.com

次に「Listinstances」のAPIリファレンスを参照してみます。インスタンスの情報を取得する「Listinstances」には以下が記載されておりました。

APIリファレンス
  • ① 利用するメソッドとリクエストターゲットが記載されています。
  • ② 「Parameters」の記載に「Required : yes」となっているものは必須なので必ず付ける必要がありました。

上記2点を踏まえてインスタンスのリストを取得する際には、メソッドはGET、リクエストターゲットは「/20160918/instances/」+「?compartmentId=コンパートメントのOCID」となることが分かりましたのでoci-curlコマンドで繋げてみたいと思います。

oci-curl iaas.ap-tokyo-1.oraclecloud.com get "/20160918/instances/?compartmentId=ocid1.compartment.oc1..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

このコマンドでインスタンスのリストがREST APIを利用して取得できました。
また、POSTPUTメソッドに関しては、データを送信する必要があるため以下のような引数となります。

第一引数ホスト(APIエンドポイント)
第二引数メソッド(POST/PUT)
第三引数POSTするデータ(JSON形式のファイル)やPUTするファイル(オブジェクトストレージにアップするファイルなど)
第四引数リクエストターゲット+パラメーター

「PutObject」のドキュメントを参考に試しにオブジェクトストレージにファイルをアップロード(PutObject)してみます。東京リージョンのオブジェクトストレージ用のAPIエンドポイントは以下になります。

objectstorage.ap-tokyo-1.oraclecloud.com

アップロードするファイルを用意してoci-curlコマンドでバケットにファイルをアップロードします。

# アップロードするファイルを用意します
echo "Hello" > ./test.txt

# oci-curlでファイルをアップロードします
oci-curl objectstorage.ap-tokyo-1.oraclecloud.com put ./test.txt "/n/<ネームスペース>/b/<バケット名>/o/<ファイル名>"

<ネームスペース>はアカウントの作成時に割り当てられた、一意で編集不能なシステム生成文字列です。Web管理画面上では以下の箇所に記載されています。<バケット名>はOCIDでは無くバケットの名前を指定します。

おわりに

REST APIを利用するシーンとしては、Webアプリ等でAPIを実装する場合が考えられますが、SDKの用意が無い場合はこちらのドキュメントを参考にチャレンジしてみるのはいかがでしょうか。
基本的にBashなどであれば、OCI-CLIを利用する方が簡単で敷居が低いと感じましたが、REST APIを利用する必要がある場合は、本記事が皆様の参考になりますと幸いです。