Akatsuki Hackers Lab | 株式会社アカツキ(Akatsuki Inc.)

Akatsuki Hackers Labは株式会社アカツキが運営しています。

【LT会】Akatsuki Geek Live 2021 開催レポート!Vol.2

みなさまこんにちは!アカツキ新卒採用担当の藤元です。

11/19(金)に、エンジニアを志す学生さま向けイベント「Akatsuki Geek Live2021 Vol.2」を開催しました!

7月に開催した「Akatsuki Geek Live2021 Vol.1」に負けないほどの大盛況で、なんと約80名の方にお越しいただきました。ありがとうございます!!

今回も、アカツキメンバーはもちろん、内定者や学生さんにもご登壇いただきました。

参加者のみなさまからは「予想以上にギークな話が聞けた!」や「アカツキの和気藹々とした雰囲気を感じれた」など満足度の高い評価をいただきました。

今回は登壇者がオフラインで集まったこともあり、メンバー同士の雰囲気もより伝わったのかな〜と思います。

今回は、イベント当日の様子をレポートします。

当日投影された資料も見られるので、ぜひ登壇者のギークネスを感じ取っていただけたらと思います!

「Akatsuki Geek Live」とは?

続きを読む

GCPのサービスアカウントキーなしでgcloudやbqコマンドをAWSから利用してみた

本記事は Akatsuki Advent Calendar 2021の9日目です。
前回は ぐんそう さんの 非エンジニアのためのGit/GitHub講座 でした。

概要

この記事では以下の内容について書きます。

  • GCPのWorkload Identity連携を利用して、AWSのEC2インスタンスからサービスアカウントキーを使わずにGCPを利用する方法
    • EC2インスタンスロールとGCPのサービスアカウントを紐付ける方法
  • gcloud CLIツールでWorkload Identity連携を利用する方法
  • gsutilコマンドやbqコマンドを使っていたのだけれどどうすれば良いの?

特にgcloud CLI周りについて、これまでAWS上でサービスアカウントキーを利用してシェルスクリプトによる自動化を行っていたが、サービスアカウントキーの使用をやめてセキュリティ向上を図りつつクレデンシャル管理の手間をなくしたいというニーズがあり、今回 Workload Identity 連携が利用できないか調べてみました。

最初にお断りしておきますが、Cloud Storageにアクセスするgsutilコマンドや、BigQueryにアクセスするbqコマンドついては現時点では正式な手順がなく、(パッチを当てたりして)無理やり動かしてみたという内容です。またその理由・将来的にどうなるかの予想についても書いてみます。 追記: 後述するように、現在ではすでにbqコマンド等もWorkload Identityに対応しています。

AWSとGCPのWorkload Identity連携のセットアップ

Workload Identity 連携は、GCP以外の認証情報(例えばAWSのIAM)をGCPのサービスアカウントに紐付けることで、永続するキーなしでGCPへのアクセスを認可するものです。技術的にはOpenID Connectを利用しています。
AWSであれば例えばEC2インスタンスにIAMロールをアタッチすることで、AWS・GCP共にキー管理を不要にできます。

最初にこの連携をセットアップしていきます。ここでは簡単のためにWebコンソールでセットアップする手順を書きます。(実際にはTerraform等のIaCを利用することが多いでしょう。)

まずは 「IAMと管理 > Workload Identity 連携」から「プールを追加」。

プールを追加

適当にプールの名前やID、説明を入力します。なおこのうちIDは後で変更できません。

プールの作成

次にプールにAWSをプロバイダとしてを追加します。AWSは他のOIDCプロバイダと違って専用の設定が用意されているので、アカウントIDとプロバイダ名(識別用の適当なID)を入力するだけでOKです。

プールにプロバイダを追加

AWSを選択した場合、次のステップでAWS用のマッピングが自動的に設定されます。

プロバイダの属性の設定

具体的には attribute.aws_role という属性でAWSのロールを使いやすく変換したものをマッピングするようになっています。長いCEL式でちょっと読みにくいですね。IAMロールをアタッチしたEC2インスタンス上で

aws sts get-caller-identity
を実行すると、 "Arn" として
"arn:aws:sts::...:assumed-role/<ロール名>/<EC2インスタンスID>"
という文字列が返ってくるはずです。先のCEL式は、 "assumed-role" が含まれる場合は、最後の "/<EC2インスタンスID>" 部分を削る、ということをしています。含まれない場合はArnそのままです。
これを利用して、例えば上記スクリーンショットの下部ようにCEL式を記述することで、EC2インスタンスに特定のIAMロール role-name がアタッチされている時のみWorkload Identity認証を許可するといった制御ができます。

最後に、このプールにサービスアカウントを紐付けて許可します。あらかじめ利用させたい権限*1を持つサービスアカウントを作成しておきます。

アクセスを許可
「アクセスを許可」で、そのサービスアカウントを設定します。また、ここでもロールとサービスアカウントの紐付けが可能です。こちらであれば、複数のロールとサービスアカウントの対応付けが可能です。
サービスアカウントとの紐付け

設定が終わると、構成ファイルと呼ばれるJSONをダウンロードすることができます。これをアプリケーションに渡すことで、Workload Identityによる認証が可能になります。サービスアカウントキーと違ってこれは機密情報を含まずセキュアに管理する必要がありませんので、気軽に取り扱うことができます。

構成JSONファイルのダウンロード

GOOGLE_APPLICATION_CREDENTIALS で利用する

Google Cloud SDKを利用して開発されたPythonやGo等のアプリケーションを利用する場合、標準的な方法で認証を行なっていれば、EC2インスタンス上にこのファイルを配置してGOOGLE_APPLICATION_CREDENTIALS 環境変数にこのファイルのパスを渡すだけでOKなはずです。

gcloud CLIで利用する

さて、gcloud コマンドでシェルスクリプトなどを書いている場合、どうすれば良いでしょうか?

gcloud コマンドは、gcloud auth login でWeb認証したり、 gcloud auth activate でサービスアカウントキーを登録し、それをデフォルトアカウントとして利用します。

Workload Identity を利用する場合、gcloud auth activate に構成ファイルを渡しても失敗します。正解は、以下のように auth login の --cred-file オプションに構成ファイルを渡すというものです。

gcloud auth login --cred-file=clientLibraryConfig.json

これで、以降はデフォルトでgcloudコマンドが Workload Identity を利用してくれます!

gsutilコマンドやbqコマンドでWorkload Identity認証するには…?

追記: Cloud SDK 380.0.0以降、gcloud auth login しておけば bq コマンドでも標準で Workload Identity に対応するようになりました。下記の記述は古くなっています。
Google Cloud CLI - Release Notes  |  Google Cloud CLI Documentation

これがある意味でこの記事の本題かもしれません。

上記の方法で gcloud コマンドは利用可能になりました。が、それと同じデフォルトアカウントを利用するgsutilコマンドやbqコマンドも使いたいですよね。
しかし実際にやってみると、

$ gsutil ls
ServiceException: 401 Anonymous caller does not have storage.buckets.list access to the Google Cloud project.

$ bq ls
ERROR: (bq) Your current active account [...] does not have any valid credentials

と認証エラーと思しきメッセージが出て失敗してしまいます。何か回避策はないでしょうか? *2

gsutil コマンドの代替

まず gsutil ですが、alphaステージではありますが代替となるgcloud alpha storageコマンドが用意されていました。こちらであればきちんと認証が行えます。(gcloudコマンドの一部ですからね)*3
下記のようにcp, ls, rmといったサブコマンドがあります。

$ gcloud alpha storage
ERROR: (gcloud.alpha.storage) Command name argument expected.

Available groups for gcloud alpha storage:

Available commands for gcloud alpha storage:

      cp                      *(ALPHA)*  Upload, download, and copy Cloud
                              Storage objects.
      ls                      *(ALPHA)*  List Cloud Storage buckets and objects.
      rm                      *(ALPHA)*  Delete objects and buckets.

ところで、実は gsutil コマンドはアクセス先のプラグインに対応しており、デフォルトでS3などにもアクセスできます。
gcloud alpha storageもこれを継承しており、ちゃんとS3にもアクセスできます。

gcloud alpha storage ls s3://  # バケット一覧
gcloud alpha storage cp s3://bucket-name/file gs://bucket-name/

ということで、将来的に gsutil はこちらに移行していくことになると思われるので、これを待つか先行して試用することになります*4

bq コマンドは…?

では bq コマンドはどうでしょうか? gcloud alpha bq というコマンドはあり、データセットやテーブルのCRUD操作はできるようですが、 bq query に相当するクエリを実行するようなコマンドはなさそうでした。

そもそもなぜGoogle Cloud SDKの一部であるはずのbqコマンドはエラーを出してしまうのでしょう?
SDKはPythonで書かれていますので、直接ソースコードを読むことができます。調べてみましょう。

まず bq コマンドの実体はgoogle-cloud-sdk内の bin/bq ですが、これは適切なPythonを探してbin/bootstrapping/bq.py を実行します。
このbootstrappingは、まずデフォルトの認証情報を取得しています。

    store.Load()  # Checks if there are active credentials

その後、認証情報にはauth login、GKEやGCEのメタデータ、サービスアカウント、Application Default Credentialなど様々な認証方法がありますが、この種類ごとに分岐しています。

なんだか雲行きが怪しくなってきました。

分岐先では種類に応じたコマンドラインオプションを生成し、それを platform/bq/bq.pyrun_mainに渡しています。
その先でさらに渡されたオプションを元に認証を再度やり直しますが、そこで使われている認証ライブラリはthird_party/oauth2client_4_0 で、Google Cloud SDKの認証系とは別物*5であり、Workload Identity認証にも非対応です。

つまるところ、bqコマンドはSDKに入ってはいるものの、内部は完全に別の作りになっていて、起動部分で強引に認証方法の辻褄合わせをしているために、引き渡せないでいたわけです。起動と言ってもただのPythonの関数呼び出しなのでCLIオプション形式にする必要すらないはずなのですが、bqの中身は意地でも触りたくなかったのでしょうか…?

皮肉なことに、上記の

    store.Load()  # Checks if there are active credentials

を実行した時点で、実はWorkload Identityの認証は内部的に解決されており、返り値としてAPI呼び出しに必要なトークンはすでに取得できています。

ならばあとは引き渡してやれば良いのだろう? ということで、簡単なパッチを書いてみました(本記事の末尾に置いておきます)。
やっていることは、上記 store.Load() で取れていたトークンをオブションとしてそのまま bq.py に渡し、API呼び出し時に使うというだけです。
これで bq コマンドが Workload Identity 対応になってしまいました(笑)。

$ bq ls
       datasetId
 ---------------------
...

カスタムパッチを実運用に投入するのは微妙なので、これも将来的にgcloud bqに統合されていくのを待つことになるのでしょうが、こんなに簡単なら暫定で bq コマンドでも対応してくれないかな…という気持ちになりました。

まとめ

本記事では、Workload Identityによる認証を利用して、AWS上でgcloud CLIを使う方法を書きました。
AWS以外のOIDCプロバイダでも利用可能なはずです(GitHub Actions等の利用例は多く公開されていますね)。
現状、gsutilやbqについては残念な状況ですが、alphaコマンドを使ったり簡単なパッチ適用で半ば無理やり動かすことはできました。
簡単かつセキュアになるので、ぜひ早くもっと利用しやすくなればなと期待しています。

追記: Cloud SDK 380.0.0以降、gcloud auth login しておけば bq コマンドでも標準で Workload Identity に対応するようになりました。
Google Cloud CLI - Release Notes  |  Google Cloud CLI Documentation

bqコマンドをWorkload Identity対応にするパッチ

diff -ur google-cloud-sdk/bin/bootstrapping/bq.py google-cloud-sdk-patched/bin/bootstrapping/bq.py
--- google-cloud-sdk/bin/bootstrapping/bq.py    1980-01-01 08:00:00.000000000 +0000
+++ google-cloud-sdk-patched/bin/bootstrapping/bq.py    2021-11-12 17:06:00.747650228 +0000
@@ -38,7 +38,7 @@
   args = []
   if cmd_args and cmd_args[0] not in ('version', 'help'):
     # Check for credentials only if they are needed.
-    store.Load()  # Checks if there are active credentials
+    cred = store.Load()  # Checks if there are active credentials

     project, account = bootstrapping.GetActiveProjectAndAccount()
     adc_path = config.Paths().LegacyCredentialsAdcPath(account)
@@ -57,8 +57,11 @@
                 '--service_account_credential_file', single_store_path,
                 '--service_account_private_key_file', p12_key_path]
       else:
-        # Don't have any credentials we can pass.
-        raise store.NoCredentialsForAccountException(account)
+        if len(cred.token) > 0:
+          args = ['--access_token', cred.token]
+        else:
+          # Don't have any credentials we can pass.
+          raise store.NoCredentialsForAccountException(account)

     _MaybeAddOption(args, 'project_id', project)

diff -ur google-cloud-sdk/platform/bq/bq_auth_flags.py google-cloud-sdk-patched/platform/bq/bq_auth_flags.py
--- google-cloud-sdk/platform/bq/bq_auth_flags.py       1980-01-01 08:00:00.000000000 +0000
+++ google-cloud-sdk-patched/platform/bq/bq_auth_flags.py       2021-11-12 16:26:43.453653046 +0000
@@ -44,3 +44,7 @@
     'service_account_credential_file', None,
     'Only for the gcloud wrapper use.'
 )
+flags.DEFINE_string(
+    'access_token', '',
+    'Only for the gcloud wrapper use.'
+)
diff -ur google-cloud-sdk/platform/bq/credential_loader.py google-cloud-sdk-patched/platform/bq/credential_loader.py
--- google-cloud-sdk/platform/bq/credential_loader.py   1980-01-01 08:00:00.000000000 +0000
+++ google-cloud-sdk-patched/platform/bq/credential_loader.py   2021-11-12 16:29:54.002440206 +0000
@@ -61,6 +61,16 @@
     raise NotImplementedError()


+class AccessTokenCredentialLoader(CredentialLoader):
+  """Use the specified access token."""
+
+  def __init__(self, access_token):
+    self._access_token = access_token
+
+  def _Load(self):
+    return oauth2client_4_0.client.AccessTokenCredentials(self._access_token, _CLIENT_USER_AGENT)
+
+
 class CachedCredentialLoader(CredentialLoader):
   """Base class to add cache capability to credential loader.

@@ -276,6 +286,10 @@
         credential_cache_file=FLAGS.credential_file,
         read_cache_first=True,
         credential_file=FLAGS.application_default_credential_file)
+
+  if FLAGS.access_token:
+    return AccessTokenCredentialLoader(FLAGS.access_token)
+
   raise app.UsageError(
       'bq.py should not be invoked. Use bq command instead.')

*1:セキュリティ向上のために最小限に絞りましょう。

*2:自明な解決策は、スクリプトをPython等で書き直してApplication Default Credentialを使うことです。規模が大きいと大変ですけど。。

*3:余談ですが、Python3がインストールされていないとエラーで動作しません。

*4:alphaですから、いきなりインターフェイスが変わったりするかもしれないので注意しましょう、念の為。

*5:SDK内部に持ってはいるんですが。。。

アカツキのサーバーサイドインターンに参加しました

初めまして、2021年の9/6 ~ 9/24の3週間、アカツキのサーバーサイドインターンに参加させていただきました、竹下といいます。この記事では、私が取りくんだこと、学んだことについて書かせていただきたいと思います。

自己紹介

慶應義塾大学 総合政策学部の学部3年生で、普段は個人や仲間内、アルバイトなどでWebサービス開発を行っています。使う言語などに強いこだわりがあるわけではないですが、Rustで開発をすることがひそかなマイブームです。 過去には趣味でオレオレWAFを実装したりしていました。

今回取り組んだこと

今回のインターンで私が配属させていただいたプロジェクトは、Ruby on Railsによるゲームのサーバーチームでした。その中で、アップデート後に処理が重くなってしまったエンドポイントについて、速度改善をするというタスクにチャレンジしました。

エンドポイントの役割としては、スマホゲームではおなじみの「ミッション機能(一定の条件をクリアすると報酬が貰える)」の一部です。デイリーミッションやコンプリート系のミッションなどといった、いつのまにか達成しているようなミッションを含めてミッション進捗を確認するために叩かれます。

続きを読む

Akatsuki Summer Internship 2021 の Go/GCP コースで運用改善を行いました

はじめに

はじめまして、otofune と申します。

9/6 から 9/24 の3週間、株式会社アカツキでプラットフォームエンジニアリングを行うチームでのインターンシップに参加させて頂きました。

aktsk.jp

といっても祝日が2日あり、新型コロナワクチンの摂取が被って副反応で1日休み、さらに社内イベントもあり、実質2週間でした。

その中で、私は GAE/Go 上で動作するサービスについて、運用に関わる以下の成果を出すことができました。

  1. API レスポンスのリファクタリング
  2. 蓄積されたアクセスログを解析し利用状況を可視化

この記事では関わったサービスについて軽く説明したあと、行った改善について紹介し、終わりに全体を通した感想を書きます。

続きを読む

株式会社アカツキのインターンでお世話になった話

こんにちは。 東京工科大学のコンピュータサイエンス学部というところの3年生をやっております(就職するなら23卒ということになります)。 宮川です。

この度、アカツキのインターンを受けましたのでその記録を残します。 ブログ執筆経験が浅くつたない文章になると思いますがご容赦下さい。

  • エントリーと選考
  • やったこと
    • 練習問題
    • クライアント改善課題
    • 新規機能実装
  • 何を得たか
    • コミュニケーションスキル
    • 作業を管理するスキル
    • 目的を意識するということ
  • 感想

エントリーと選考

アカツキのインターンを知ったのは就活サイトがきっかけです。

プログラミングのインターンを受けたい。でも1day仕事体験とかではなく数週間にわたってやりたい。あとお給料出ると嬉しい。そんな欲張りな条件で探した結果アカツキのインターンに出会いました。 ゲーム開発のインターンということで、私はゲーム開発に興味があるので良さげと思いました。

でもUnityなんだろうな...。私はゲーム制作に興味はあるもののUnityはあまり得意ではないのです。 ですが募集要項を見てみると..... Cocos2d-x & C++!? 私はそれに一目惚れして、アカツキのインターン選考が非常に厳しいらしいことも知らず、募集人数1人程度の「ヤバいインターン」にダメ元でエントリーしました。

────そして何故か選考に合格してしまったのです。(めちゃくちゃ嬉しかったです。)

続きを読む