LoginSignup
9
1

More than 1 year has passed since last update.

OCI Bastionのセッション作成を自動化するスクリプトを作る

Posted at

はじめに

最近、OCIのBastionに触っています。Bastion(日本語で要塞)はサーバーへのアクセスの踏み台になってくれるサービスです。自前で踏み台サーバーを作るのと比べて、プライベートなネットワークで完結し、無料枠であることが利点としてあります。

しかし、使っているとTTLの短さやセッション作成の手間が気になってきます。そこで、何とか楽に使えないか考えていきます。

Bastionの接続方法

Bastionの使い方の詳細はOCIのドキュメントにあります。

概要を記載します。
Bastionを使ったサーバーへの接続方法としては以下の種類があります。

  • SSH接続
  • ポート転送
  • リモートデスクトップ接続

今回はSSH接続を使います。
サーバーへの接続の流れは以下の通りです。

  1. OCIコンソールにアクセスする。
  2. セッションを作成する。
  3. 作成されたセッションIDを確認する。
  4. ターミナル(サーバーに接続するためのツール)を開く。
  5. セッションIDを使ってサーバーに接続する。
  6. TTLが切れたら、再度セッションを作成する。

大変だなーと思いますので、何とか楽にできないか考えます。

出来上がり

以下のファイルを作りました。

  • セッションを作成するスクリプト
  • SSHのconfigファイル

今回のスクリプトを使うと以下のようになります。

  1. ターミナルを開く。
  2. サーバーに接続する。
  3. TTLが切れたら、再度セッションを作成する。

接続のコマンドは以下の1文で済みます。

$ ssh <ホスト名/IPアドレス>

スクリプトのフロー

作成するスクリプトのフローは以下の通りです。
以下の2点に気を付けました。

  • OCIのCLIはインストールしたくないのでREST APIを使ってcurlで実現したい。
  • OCIのREST APIでは作成を待機するオプションがないので待機する処理を入れる。

image.png

セッションを作成するスクリプトを作成する

セッションを作成して、そのOCIDを取得するshellファイルは以下の通りです。自分で使う用なので、荒いのは申し訳ないです。。

get-session-ocid.sh
#!/bin/bash

#必要なパラメータは外部のファイルに書くようにしました。
ENV_FILE=$1
source $ENV_FILE

function create_session(){
  rest_api="/20210331/sessions"
  publicKeyContent=$(cat ${publicKey} | awk '{print substr($0, 1, length($0)-1)}')
  body="{\"bastionId\": \"${bastionId}\", \"displayName\": \"${displayName}\", \"keyDetails\": {\"publicKeyContent\": \"${publicKeyContent}\n\"}, \"sessionTtlInSeconds\": \"${sessionTtlInSeconds}\", \"targetResourceDetails\": {\"sessionType\": \"${sessionType}\", \"targetResourceId\": \"${targetResourceId}\", \"targetResourceOperatingSystemUserName\": \"${targetResourceOperatingSystemUserName}\"}}"
  content_sha256="$(echo "$body" | openssl dgst -binary -sha256 | openssl enc -e -base64)"
  content_sha256_header="x-content-sha256: $content_sha256"
  content_length=$(echo "$body" | wc -c | xargs)
  content_length_header="content-length: $content_length"
  headers="(request-target) date host"
  headers=$headers" x-content-sha256 content-type content-length"
  content_type_header="content-type: application/json"
  now=`date -u "+%a, %d %h %Y %H:%M:%S GMT"`
  date_header="date: $now"
  host_header="host: $bastionHost"
  request_target="(request-target): post $rest_api"
  signing_string="$request_target\n$date_header\n$host_header"
  signing_string="$signing_string\n$content_sha256_header\n$content_type_header\n$content_length_header"
  signature=`printf '%b' "$signing_string" | openssl dgst -sha256 -sign $privateKeyPath | openssl enc -e -base64 | tr -d '\n'`
  response=$(echo "${body}" | curl -X POST --data-binary @- -sS https://$bastionHost$rest_api -H "date: $now" -H "x-content-sha256: $content_sha256" -H "content-type: application/json" -H "content-length: $content_length" -H "Authorization: Signature version=\"1\",keyId=\"$tenancy_ocid/$user_ocid/$fingerprint\",algorithm=\"rsa-sha256\",headers=\"$headers\",signature=\"$signature\"")
  echo $response | sed -e 's/^.*"id" : "\([^"]*\)".*$/\1/'
}

function is_session_alive(){
  sessionId=$1
  rest_api="/20210331/sessions/${sessionId}"
  now=`date -u "+%a, %d %h %Y %H:%M:%S GMT"`
  date_header="date: $now"
  host_header="host: $bastionHost"
  request_target="(request-target): get $rest_api"
  signing_string="$request_target\n$date_header\n$host_header"
  headers="(request-target) date host"
  signature=`printf '%b' "$signing_string" | openssl dgst -sha256 -sign $privateKeyPath | openssl enc -e -base64 | tr -d '\n'`
  response=$(curl -X GET -sS https://$bastionHost$rest_api -H "date: $now" -H "Authorization: Signature version=\"1\",keyId=\"$tenancy_ocid/$user_ocid/$fingerprint\",algorithm=\"rsa-sha256\",headers=\"$headers\",signature=\"$signature\"")
  lifecycleState=$(echo $response | sed -e 's/^.*"lifecycleState":"\([^"]*\)".*$/\1/')
  echo $lifecycleState >&2
  if [ -n "$lifecycleState" ] && [ "$lifecycleState" = 'ACTIVE' ];then
    echo true
  else
    echo false
  fi
}

sessionId=$(create_session)
until $(is_session_alive $sessionId);do
  sleep 3
done
echo $sessionId

以下に参考にしたドキュメントを載せておきます。

パラメータを定義したファイルには以下の情報を書きました。

envfile.sh
#
# セッションに関する情報
#
export displayName="..." #OCIコンソールから見えるセッション名
export publicKey="..." #SSH接続に用いる公開鍵のパス
export sessionType="MANAGED_SSH" #セッションの種類
export targetResourceId="..." #SSH接続するサーバーのOCID
export targetResourceOperatingSystemUserName="..." #SSH接続する際のユーザー名
export sessionTtlInSeconds="..." #セッションのTTL

#
# Bastionに関する情報
#
export bastionId="..." #BastionのOCID
export bastionHost="bastion...." #Bastionのエンドポイント

#
# APIに関する情報
#
export compartmentId="..." #コンパートメントのOCID
export tenancy_ocid="..." #テナンシのOCID
export user_ocid="..." #ユーザーのOCID
export privateKeyPath="..." #APIを使用するための秘密鍵
export fingerprint="..." #鍵のフィンガープリント

パラメータについて補足します。

SSHのconfigファイルを作成する

作成したスクリプトを実行するconfigファイルを作成します。
元になる設定はセッションをOCIコンソールから作成したときに見ることができます。以下の構造になっています。

ssh -i <privateKey> -o ProxyCommand="ssh -i <privateKey> -W %h:%p -p 22 <セッションのOCID>@host.<Bastionのエンドポイント>" -p 22 <ユーザー名>@<サーバーのIPアドレス>

上記のコマンドを実行するのは長くて面倒なので、configファイルにします。
以下の通りです。

~/.ssh/config
StrictHostKeyChecking no #面倒なのでOFFにしています。
UserKnownHostsFile=/dev/null #トラブルの原因になったことがあるのでOFFにしています。
ServerAliveInterval 50 #Bastionだと操作しないと切られるので設定します。

Host <ホスト名>
  HostName <サーバーのIPアドレス>
  User <ユーザー名>
  IdentityFile <privateKey>
  ProxyCommand ssh -i <privateKey> -W %h:%p -p 22 $(/path/to/get-session-ocid.sh /path/to/envfile.sh)@host.<Bastionのエンドポイント>

無理やりですが、セッションのOCIDをスクリプトから取得するようにしています。

SSHコマンドの実行

以上で準備はできました。
後は、コマンド1文でサーバーにSSH接続することができます。

$ ssh <ホスト名>

苦労した点&今後の方針

curlのデバッグオプション

curlを使ってコードを書いているとミスしたときに、原因を特定するのに苦労しました。
デバッグで使用したオプションを記載します。

  • v,vvvなどのverboseオプション
    リクエストのパスやヘッダーを確認できます。
  • traceオプション
    今回はデータに改行コードがついていたりついていなかったりで詰まりました。バイナリでデータを表示されるので、どこがおかしいかわかりやすくなります。

※参考
https://qiita.com/yasuhiroki/items/a569d3371a66e365316f

さらに楽に接続したい

今回作ったものでは以下の点が気になります。

  • SSH接続で毎回セッションを作成するので時間がかかる
  • TTLが切れたら切断されてしまう

そのため、今後はRemoteCommandで定期的にセッションを作成し、tmux,screen,autosshなどで自動再接続みたいにしたいなと思っています。

おわりに

今回はセッションをcurlで作成しました。できれば二度とやりたくないと思うほど面倒でした。CLIを使わないと大変だということを思い知りました。
まだ課題はあるので、勉強します。

9
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
1