共有アカウントをOkta × Terraformで管理してみる


この記事は[株式会社ビットキー 情シス Advent Calendar 2023](https://qiita.com/advent-calendar/2023/bitkey-corp-eng) の1日目の投稿です。

はじめまして。株式会社ビットキーの日浦です。
2019年に入社し、現在はビットキーで情シス的な仕事を担当しています。
SREやCREなどサービス開発や運用に関わってきた経験をCorporate領域にどう活かすか試行錯誤中です。

# 前提

ビットキーでは従業者のID管理にOktaを利用しています。機能が多く使いこなすのが大変なのですが、日々いろいろ試しています。

アクセスコントロールに関するセキュリティポリシーを考える際、**SaaS側の機能有無に依存せず、Okta上の設定のみで完結できる**という点が最も大きな利用メリットかなと考えています。
ex）SaaS側に接続元IPアドレス制限の機能がなくても、Okta側のアクセスポリシーで設定・制御可能

しかし、IDaaS製品を利用してID管理を進める際、やはりSAML認証が使えないSaaSというものが存在します。最上位のEnterpriseプランでしか使えなかったり、そもそもSAML認証に対応していなかったり。

そのような場合、ID/PWを従業員間で共有する「共有アカウント」を使うことがあるのですが、管理運用上の課題が存在します。

# 共有アカウントの課題

- ID/PWを「教える」という運用が前提となるため安全性が低い
- 「誰が使えるのか」を正確に管理するのが実質不可能
- SaaS側の機能有無に依存することになる
    ex)組織のセキュリティポリシーとして「オフィス外からのアクセス」を禁止している場合など

こういった場合、OktaではSecure Web Authentication（以下SWA）を利用することで組織のセキュリティポリシーに沿った安全なSSOを利用することができます。

# Secure Web Authentication（SWA）とは

むちゃくちゃ噛み砕いて説明すると、ID/PWで認証するアプリについてOkta上でSAML認証のアプリと同じように振る舞わせることができるようになり、Oktaの認証によるSSOも可能になります。使い勝手はほぼ他の認証方法のアプリと大差ありません。

- アプリとして登録できる
- ユーザーやグループがアサインできる
- 認証ポリシーにより、アクセスコントロールが可能
- ID/PWをOkta上に登録することで、SaaS上でのユーザーによる入力をスキップできる

https://help.okta.com/ja-jp/content/topics/apps/apps-about-swa.htm

## SWAを使ってみる

設定方法や使い方は下記のクラウドネイティヴさんのブログがとてもわかりやすいです。

https://blog.cloudnative.co.jp/11793/

Okta上でSSOログインするアプリとの違いを具体的に説明すると、登録時に下記のような工程が必要になります。

1. ブラウザの拡張機能で `Okta Browser Plugin` をインストールする
2. Okta管理画面上にアプリを登録する際に下記を登録する
    - ID/PW
    - IDパスワードを入力するHTMLのセレクタ
    - ログインボタンのHTMLのセレクタ
    ※ SaaSのログイン画面をDevelopet Toolなどで確認が必要
        

1.については全社員を対象にChromeの管理機能やJamf PROから一度やってしまえば良いのですが、2.のDOMの調査についてはアプリを登録する際に毎回実施する必要があります。

# SWAをTerraformでIaCしてみる

そんな便利極まりないSWAですが、残念ながら私のような怠惰な情シスには画面からポチポチ登録していく作業がしんどいときもあります。
画面から登録状況を確認するのもめんどくさいし、他の人が管理画面で既存のSWAアプリ参照しながら真似して登録するのも大変なので、再現性を高めるためにソースコードで管理しましょう。

OktaをTerraformで構成管理する方法については下記の記事が大変参考になりますので、まずは写経することをおすすめします。

https://zenn.dev/shimosyan/books/bb0ba712133779061804

以下は `Sample App` という架空のSaaSにSWAでログインするための記載例です。

## ソースコード

まずは `okta_app_shared_credentials` を定義します。

```sample_app.tf
resource "okta_app_shared_credentials" "sample_app" {
  label           = "Sample App" // 任意の名前
  status          = "ACTIVE"
  username_field  = "#email"    // ID入力部分のセレクタ
  password_field  = "#password" // PW入力部分のセレクタ
  button_field    = "#button"   // ログインボタン入力部分のセレクタ
  shared_username = "example@example.com" // ログインID
  shared_password = var.PW_SAMPLE_APP // パスワード
  url             = "https://example.com/" // ログインページのURL
  logo            = "../terraform/assets/example.png"
  admin_note      = "created by terraform. サンプルアプリだよ"
}
```

次に、このSaaSを利用できる人を定義するOktaグループと、グループルールを記述します
ビットキーでは、原則アプリケーションに直接人やグループをアサインせず、「App_Assign_hogehoge」というアサイン用のグループを作成しています。

```sample_app.tf
resource "okta_group" "App_Assign_sample_app" {
  name        = "App_Assign_sample_app"
  description = "created by terraform"
}

resource "okta_app_group_assignments" "App_Assign_sample_app" {
  app_id = okta_app_shared_credentials.sample_app.id
  group {
    id       = okta_group.App_Assign_sample_app.id
    priority = 1
  }
}

resource "okta_group_rule" "App_Assign_sample_app" {
  name   = "App_Assign_sample_app"
  status = "ACTIVE"
  group_assignments = [
    "${okta_group.App_Assign_sample_app.id}"
  ]
  expression_type = "urn:okta:expression:1.0"
  expression_value = join("", [
    "isMemberOfAnyGroup(",
    "\"${okta_group.team-corporate-engineering.id}\"",
    ")"
  ])
}
```

`App_Assign_sample_app` というグループを作成し、 `okta_app_group_assignments` でグループを `Sample App`にアサインします。
`okta_group_rule` も同時に作成し、デフォルトでアサインするグループ（ `Sample App` にSSOできるグループ）を指定しています。

ビットキーでは「組織図から作成されたグループ」か「Okta Access Requestによってアサインされるグループ」を利用することが多いです。

最後に、 `Sample App` にログインするためのPWを変数として定義します。

```sample_app.tf
variable "PW_SAMPLE_APP" {
  sensitive = true
}
```

ソースコード上に直接パスワードを記載することもできますが、クレデンシャル情報をソースコードに記載するのは避けましょう。
ただしTerraformの仕様上、stateには記録されているため下記を行うようにしましょう。
- ローカルには保存せず、AWS S3などに保存し、そちらのアクセス管理を徹底する
- .gitignoreに記述しておく
   
    > .tfstate
    *.tfstate.*
    > 

※ Oktaを利用したAWSのアクセス管理は後日書くかも



また、`sesitive = true` を指定することでplanやapplyの出力でもマスキングされ、CI/CDの実行環境でも出力されないようになります。



以下、詳細は割愛しますが、GitHub Actionsのサンプルコードです。

```tf-plan.yml
name: terraform-plan

on:
  pull_request:
    paths:
      - ".github/workflows/tf-plan.yml"
      - "terraform/**"
  workflow_dispatch:

env:
  TF_VAR_OKTA_API_TOKEN: ${{ secrets.OKTA_API_TOKEN }}
  AWS_ROLE_ARN: ${{ secrets.AWS_ROLE_ARN }}
  AWS_DEFAULT_REGION: ap-northeast-1
  TF_VAR_PW_SAMPLE_APP: ${{ secrets.PW_SAMPLE_APP }}

jobs:
  terraform-plan:
    name: Terraform plan
    runs-on:  ubuntu-latest
    timeout-minutes: 5
    permissions:
      id-token: write
      contents: read
    defaults:
      run:
        shell: bash
        working-directory: ./terraform/

    steps:
    - name: Checkout
      uses: actions/checkout@v2

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-region: ${{ env.AWS_DEFAULT_REGION }}
        role-to-assume: ${{ env.AWS_ROLE_ARN }}

    - name: Setup Terraform
      uses: hashicorp/setup-terraform@v2

    - name: Init Terraform
      run: |
        terraform init 

    - name: Test Terraform
      run: |
        terraform plan 
```

```sample_app.tf
  TF_VAR_PW_SAMPLE_APP: ${{ secrets.PW_SAMPLE_APP }}
```

GitHubの環境変数に `Sample App`のログインパスワードを格納することで、CI/CDのタイミングで読み込んで、Oktaにapplyしてくれるようになります。

# Terraformで管理するメリット

> そもそも登録がめんどくさい

コード化されているため、コピペでSWAアプリケーションを量産可能です。
同一のSaaSに対して複数の共有アカウントが存在する場合などに、登録作業や定期的なパスワードの変更作業のコストが激減します。
個人的にはSWA特有のセレクタを調べる作業を毎回やらなくて良い点やSaaS側の変更によりセレクタを更新しないといけない場合などにコード化の恩恵を感じています。

> ログイン情報をOktaで見ないといけない、というのがめんどくさい

地味な不満ではありますが、Oktaの管理画面は必要な情報に到達するまでの遷移数や読み込みの待ち時間が長いと感じているので、ローカルでサクッと確認できるのはありがたいです。

> ID/PWというモロな認証情報をどうやって管理する？
> 

ビットキーにおいてはSaaSの共有アカウントのIDは基本的にメーリスグループを利用しています。当然ですがやろうと思えばパスワードと同じようにGitHubの環境変数に隠蔽することは可能です。

# 継続課題（2023年11月現在）

- SWAはGoogle Authenticatorのような多要素認証（MFA）でワンタイムパスワードを要求するタイプのSaaSには対応していません。
    - ただし、登録メールアドレスに対してログインの都度、認証コードを送ってくるタイプのサービスについては、メーリスに届いた認証メールをSlackなど利用者が見れる場所に転送する運用は可能です。
- TerraformのOkta Providerが未対応なため、**ID入力後に別ページに遷移してパスワードを入力するタイプのログイン**には対応できません
    - ただし、Oktaの管理画面上から `Template Two Page Plugin App` を選んで手動作成することでSSOを実現することは可能です。

# 最後に

OktaのSWAを利用することで、今までID/PWを共有して運用してたSaaSを安全に利用できるようになりました。Oktaの認証ポリシーの制御対象にもなっているので「社用PCからしかログインできない」状態も実現可能です。

また、利用者目線ではIDもパスワード「知る必要がない」ことも安全性の向上に繋がっています。

Oktaへの登録がめんどくさいSaaSもコード化することで容易にOkta Applicationを量産できるようになるため、Okta SWAを利用する際は是非Terraformの利用を検討してみてください。