投稿日

SORACOMのアクセス管理(IAM)機能「SAMユーザー」の基本と実践例

こんにちは、ソリューションアーキテクトの松永(taketo)です。

IoTプロジェクトではプロジェクトをリードする企業以外にも、ハードウェア開発企業やクラウドシステムの開発を担当する企業などが参画して、組織をまたいだチームで作業することが多くあります。こういったプロジェクトでは、SORACOMのアカウントを所有している方以外にも、協力会社のメンバーの方々へ権限を付与して作業する場面が必要です。

SORACOMではSAM (SORACOM Access Management)というアクセス管理の仕組みを持っており、アカウント所有者以外にもユーザーの払い出しや、アクセス権限を付与することができます。

今回は、実際のプロジェクトを想定し、このSAMを使ってどのように権限設定ができるか例を提示しながら解説していきます。

アクセス管理はIdentity and Access Management(IAM)と呼ばれ、パブリッククラウドやSaaSの一般的な機能として知られています。SORACOMのIAMが「SAM」と言えます。

想定するプロジェクト体制

今回は事業者である企業Aが、システム開発企業(企業B)と協業して開発するプロジェクトを想定します。
企業Aが主体的にSORACOMを利用するため、開発者だけでなく経理や調達/決済部門向けの運用も行います。

システム開発を担当する企業Bには複数名の開発者がおり、プロジェクトのフェーズにより増減することが多々あります。SAMユーザーの追加や削除は、定期的に企業Aのユーザー管理者に依頼するオペレーションとします。
ただし、開発中においては頻繁に権限の付与や削除が発生することがわかったため、権限の制御自体はシステム開発企業(企業B)のプロジェクトマネージャが行えるようにして、企業Aのプロジェクト責任者の手間を省く運用とします。この場合においても、必ず拒否したい操作は企業Aが管理できる仕組みを設定していきます。

想定するプロジェクト体制とロール

また、アプリケーションからのアクセスも想定します。この場合においても、SORACOMのAPIを呼び出すアプリケーション用に作成頂くことができます。例として、以下の操作を行うSIMの情報取得と必要に応じてステータス変更をするケースを考えます。

  • SIM一覧の閲覧
  • SIM情報の閲覧
  • SIMのステータス変更
  • グループ一覧の閲覧
  • グループ情報の閲覧

ユーザーと権限設定方法

SORACOMのユーザーには2種類のユーザー (ルートユーザー・SAMユーザー) が存在します。ルートユーザーは、Operator(アカウント)自体の所有者であり、全ての権限を所有するユーザーです。一方で、SAMユーザーは追加ユーザー向けに利用され、細かい権限管理が可能です。

SAMユーザーへの権限管理は、2つの方法を利用します。

  • ロール権限設定:
    ロール自体に権限設定し、対応するSAMユーザーに適応できます。ロールを定義することで、同じ権限を持ったSAMユーザーへの権限付与作業を簡単に行うことができます。また複数のロールを付与することが可能であるため、今回はデフォルトロールに加え各プロジェクトのメンバーに対応するロール権限を設定します。本プロジェクトでは、企業Aのプロジェクト責任者やユーザー管理者のみ変更ができる仕様として設定します。
  • 直接指定権限設定:
    各SAMユーザーに個別で設定ができる権限設定方法です。今回の例では、ロールの権限では充足しなかった場合にのみ企業Bが利用する設定方法とします。基本的に例外的な権限設定として利用します。
所属(企業/システム)メンバーロール名主な役割/権限
企業A1.プロジェクト責任者(SORACOMアカウント所有者)SORACOMアカウント所有者のため、ルートユーザーとして全権限を所有しています。
・全権限
企業A2.企業A全員company-a-default企業Aでデフォルトとする権限です。基本的に拒否・許可したい操作のみを記述します。
・ユーザー自身のセキュリティ設定(MFA、パスワード)権限・支払いや通知、配送先設定の変更権限拒否
企業A3.ユーザー管理者company-a-user-admin・ユーザー関連の全権限・ロール関連の全権限
企業A4.調達/決済部門company-a-billingSIMなど物品の購入と利用料を把握する権限を所有します。ただし、購入以外は読み取り権限として付与します。
・課金関連の閲覧権限
企業B5.企業B全員company-b-default企業Bでデフォルトとする権限です。
・ロール関連の変更権限拒否・ユーザー関連の追加・削除権限拒否・ユーザー自身のセキュリティ設定(MFA、パスワード)権限・支払いや通知、配送先設定の変更権限拒否・企業B以外のユーザーとアプリケーションへの直接指定権限設定権限
企業B6.プロジェクトマネージャcompany-b-admin企業Aのプロジェクト責任者から開発に関わる権限を移譲する形で活動するため、全権限を保有します。
・全権限
企業B7.開発者company-b-developer開発に必要なAPI呼び出し権限を付与します。
・アプリケーションに必要な権限(ただし、変更権限も付与)
本番システム8.アプリケーションapp-productionアプリケーションから呼び出すAPIのみ権限を付与します。
・アプリケーションに必要な権限
ユーザーリストと主な役割/権限

SAMにはもう一つ権限設定方法として、SAM デフォルト権限設定もあります。
全てのSAMユーザーに適用される権限設定が可能です。今回は、権限変更が必要になった際により柔軟に設定が可能なロールによる設定で企業Aと企業Bへのデフォルト権限設定を実施します。

同一の APIに対して複数の権限設定が存在する場合の挙動について

ロールと直接権限設定を組み合わせて利用することで、同一のAPIに対して複数回権限設定がされている場合の挙動について補足します。

下の例では、Group:getGroupへの権限設定が2つのロールと直接権限指定で設定がされる場合の例です。直接権限設定でAllow(許可)となっておりますが企業BのデフォルトロールでDeny(拒否)が設定されているため、結果Deny(拒否)として扱われます。

本プロジェクトの例としては、企業Aと企業Bのデフォルトロールで必ずDeny(拒否)を定義することにより必ず誤動作を避けたい操作を定義することができます。

つまり、一つでもDeny(拒否)があればその設定が優先されます。

権限設定`Group:getGroup`への権限設定
企業BデフォルトロールDeny+
開発者ロール設定なし(他にAllow/Denyがない場合は暗黙にDeny)+
直接指定権限設定Allow↓
結果Deny

同一APIへの権限設定例 一つでもDenyがある場合は結果Denyとして評価されます

作業手順

それでは早速、ユーザー作成と権限設定を実施していきます。

主な手順は以下の通りです。

  1. SAMユーザーの作成
  2. ロールの作成とパーミッション構文の指定
  3. SAMユーザーへのロールの適応
  4. 直接指定権限設定
  5. 動作確認
  6. 新規ユーザー追加手順

1.SAMユーザーの作成

SAMユーザーの作成を行なっていきます。

具体的な作成手順は公式ドキュメント「SAM ユーザーを作成する」をご確認ください。ここでは権限管理のためにユーザー名の命名規則を下記の通り決めます。

カテゴリ命名規則
ユーザー向けcom-{company}-{FirstName}-{LastName}・com-b-taketo-matsunaga
システム向けapp-{environment}・app-development・app-production
SAMユーザー命名規則

2.ロールの作成とパーミッション構文の指定

上のテーブル「SAMユーザーのリストと主な役割/権限」にて、ロールという列を記載しました。

同じ権限を複数人に付与する場合、事前にこのロールを作成し権限を付与したいSAMユーザーにアタッチすることで同じ権限を簡単に付与することが可能です。詳しい手順については、公式ドキュメントの「ロールを作成する」をご確認ください。

ここから、各ロールに付与する権限をみていきます。また、権限を付与するための構文をパーミッション構文と言いますが、詳細な設定方法はこちらの公式ドキュメントサンプルもありますのでご確認ください。

  1. プロジェクト責任者用:ルートユーザー
  2. 企業A全員:company-a-default
  3. ユーザー管理者:company-a-user-admin
  4. 調達/決済部門用:company-a-billing
  5. 企業B全員:company-b-default
  6. プロジェクトマネージャ:company-b-admin
  7. 開発者:company-b-developer
  8. アプリケーション:app-production

1. プロジェクト責任者用:ルートユーザー

アカウント所有者であるプロジェクト責任者は、必ず全権限を持っています。そのため、権限設定は不要です。

2. 企業A全員:company-a-default

一つのロール作成ですが、下記の通り設定していきます。

  • ユーザー自身のセキュリティ設定(MFA、パスワード)権限
  • 支払いや通知、配送先設定は変更権限拒否

ここでまず注目頂きたいのが、一つめのセキュリティ設定です。

SORACOMではワンタイムパスワードを用いるMFA(多要素認証)をサポートしており、この設定を自身の設定のみ変更できる権限を設定しています。また、ログインの際のパスワードも同様に変更権限を付与しています。ここで、他人の設定を変更できないように明示的に拒否設定もしています。

{
  "statements": [
    {
      "effect": "allow",
      "api": [
        "User:revokeUserMFA",
        "User:revokeUserMFA",
        "User:getUserMFAStatus",
        "User:enableUserMFA",
        "User:verifyUserMFA",
        "User:deleteUserPassword",
        "User:hasUserPassword",
        "User:createUserPassword",
        "User:updateUserPassword"
      ],
      "condition": "pathVariable('user_name')==samUserName"
    },
    {
      "effect": "deny",
      "api": [
        "User:revokeUserMFA",
        "User:revokeUserMFA",
        "User:getUserMFAStatus",
        "User:enableUserMFA",
        "User:verifyUserMFA",
        "User:deleteUserPassword",
        "User:hasUserPassword",
        "User:createUserPassword",
        "User:updateUserPassword"
      ],
      "condition": "not (pathVariable('user_name')==samUserName)"
    },
    {
      "effect": "deny",
      "api": [
        "Payment:*",
        "Email:*",
        "ShippingAddress:*",
        "SystemNotification:*"
      ],
      "condition": "(httpMethod('POST')) or (httpMethod('PUT')) or (httpMethod('DELETE'))"
    }
  ]
}
パーミッション構文記載例の解説

1つ目のMFA設定で基本的なパーミッション構文の書き方を解説致します。

まず、こちらの部分で許可(allow)/拒否(deny)を指定します。

"effect": "allow"

次に、APIをサービス名とオペレーション名 で指定します。どのAPIが必要かは、APIリファレンスを参照してください。

"api": [
        "User:revokeUserMFA",
        "User:revokeUserMFA",
        "User:getUserMFAStatus",
        "User:enableUserMFA",
        "User:verifyUserMFA",
        "User:deleteUserPassword",
        "User:hasUserPassword",
        "User:createUserPassword",
        "User:updateUserPassword"
      ],

最後に、条件の指定で自身の設定である場合のみ権限設定を適応する設定を記載します。

まず、対象とするAPIは例えば”User:enableUserMFA”の場合、そのアクセスパスは /operators/{operator_id}/users/{user_name}/mfa samUserNameとなります。ここで、呼び出しの際にログインしたユーザー名が利用されることがわかります。このパスの変数値を呼び出し時にpathVariable(‘user_name’)として取得し、実際に認証情報からログインしているユーザー名をsamUserNameという変数で取得します。

この2つが同一であるか評価し、同一である場合はアクセスが許可されます。また、上記で指定したUserサービスのAPIは全てのオペレーションでパス変数にユーザー名が指定されますので、ログインしたSAMユーザーの設定のみ変更を許可することができます。

SAMユーザー名以外にも様々な変数(例:samUserName)や関数(例:pathVariable)、そして演算子(例:==)が利用可能ですのでこちらもあわせてご確認ください。

"condition": "pathVariable('user_name')==samUserName"

3.ユーザー管理者:company-a-user-admin

ユーザー管理者はユーザーの作成や権限に関わる変更権限といった非常に強い権限を持ちます。限られたメンバーにのみ付与することが推奨されます。

{
  "statements": [
    {
      "effect": "allow",
      "api": [
        "User:*",
        "Role:*"
      ]
    }
  ]
}

4.調達/決済部門用:company-a-billing

このロールは下記の権限を持たせます。

  • 課金関連の閲覧権限
{
  "statements": [
    {
      "effect": "allow",
      "api": [
        "Payment:*",
        "Email:*",
        "ShippingAddress:*",
        "SystemNotification:*",
        "Role:*"
      ],
      "condition": "httpMethod('GET')"
    },
    {
      "effect": "allow",
      "api": [
        "Billing:*"
      ]
    },
    {
      "effect": "allow",
      "api": [
        "Order:*",
        "Credential:*",
        "Diagnostic:*",
        "EventHandler:*",
        "Group:*",
        "Role:*",
        "User:*",
        "Query:*",
        "Sim:*",
        "Stats:*",
        "Subscriber:*",
        "Log:*"
      ],
      "condition": "httpMethod('GET')"
    }
  ]
}
パーミッション構文記載例の解説

請求関係の閲覧権限について解説します。

まず、下の部分で、支払情報などに対して “condition”: “httpMethod(‘GET’)” を指定することで閲覧権限のみ付与しています。一般的にGETメソッドは何かの情報を閲覧する際に利用され、変更するために利用されるメソッドではないため、 “condition”: “httpMethod(‘GET’)” を指定しています。

また、Billingサービスに関して全てのメソッドを許可しています。ソラコムのBillingサービスには、PostメソッドでCSVを取得することもあるため全て許可しています。なお、特段支払い先などの変更はBillingサービスではなくPaymentサービスを利用するため全権限を付与しても誤って設定変更するなどのご心配はありません。

   {
      "effect": "allow",
      "api": [
        "Payment:*",
        "Email:*",
        "ShippingAddress:*",
        "SystemNotification:*",
        "Role:*"
      ],
      "condition": "httpMethod('GET')"
    },
    {
      "effect": "allow",
      "api": [
        "Billing:*"
      ]
    }

請求情報以外にも、オーダーやSIMの情報もあわせて閲覧権限のみ付与しています。これにより、どのSIMが高額になっているかなど理由も含めて調査できるようにしています。

   {
      "effect": "allow",
      "api": [
        "Order:*",
        "Credential:*",
        "Diagnostic:*",
        "EventHandler:*",
        "Group:*",
        "Role:*",
        "User:*",
        "Query:*",
        "Sim:*",
        "Stats:*",
        "Subscriber:*",
        "Log:*"
      ],
      "condition": "httpMethod('GET')"
    }

5. 企業B全員:company-b-default

企業B向けのデフォルト権限ロールを設定します。

  • ロール関連の変更権限拒否
  • ユーザー関連の変更権限(追加・削除含む)
  • ユーザー自身のセキュリティ設定(MFA、パスワード)権限
  • 支払いや通知、配送先設定は変更権限拒否

ここで注目頂きたい点は、最初の2つのDenyパーミッション構文で権限の変更やユーザーの追加・削除を明示的に拒否している点です。これがなく、誤って許可されている場合は自由に権限を変更し悪意のある操作ができてしまいます。そのため、これは必ず設定してください。

{
  "statements": [
    {
      "effect": "deny",
      "api": [
        "Role:*"
      ],
      "condition": "(httpMethod('POST')) or (httpMethod('PUT')) or (httpMethod('DELETE'))"
    },
    {
      "effect": "deny",
      "api": [
        "User:createUser",
        "User:deleteUser"
      ]
    },
    {
      "effect": "allow",
      "api": [
        "User:revokeUserMFA",
        "User:revokeUserMFA",
        "User:getUserMFAStatus",
        "User:enableUserMFA",
        "User:verifyUserMFA",
        "User:deleteUserPassword",
        "User:hasUserPassword",
        "User:createUserPassword",
        "User:updateUserPassword"
      ],
      "condition": "pathVariable('user_name')==samUserName"
    },
    {
      "effect": "deny",
      "api": [
        "User:revokeUserMFA",
        "User:revokeUserMFA",
        "User:getUserMFAStatus",
        "User:enableUserMFA",
        "User:verifyUserMFA",
        "User:deleteUserPassword",
        "User:hasUserPassword",
        "User:createUserPassword",
        "User:updateUserPassword"
      ],
      "condition": "not (pathVariable('user_name')==samUserName)"
    },
    {
      "effect": "deny",
      "api": [
        "Payment:*",
        "Email:*",
        "ShippingAddress:*",
        "SystemNotification:*"
      ],
      "condition": "(httpMethod('POST')) or (httpMethod('PUT')) or (httpMethod('DELETE'))"
    },
    {
      "effect": "deny",
      "api": [
        "User:updateUserPermission",
        "User:deleteUserPermission"
      ],
      "condition": "not ((pathVariable('user_name') matches 'com-b-.*') or (pathVariable('user_name') matches 'app-.*'))"
    }
  ]
}
パーミッション構文記載例の解説

下の部分のパーミッション構文について補足します。

今回企業Bの方は、同じく企業Bのメンバーやアプリケーションに対して例外的に直接権限指定設定をできるようにしています。その際に、誤って企業Aなどにも設定がされないように条件設定しています。

冒頭でSAMユーザーのネーミング規約をcom-{company}-{FirstName}-{LastName}とapp-{environment}にする点について述べました。これにより、企業Bの方とアプリケーション以外のSAMユーザーへの直接権限指定設定をDeny(拒否)する設定としています。

   {
      "effect": "deny",
      "api": [
        "User:updateUserPermission",
        "User:deleteUserPermission"
      ],
      "condition": "not ((pathVariable('user_name') matches 'com-b-.*') or (pathVariable('user_name') matches 'app-.*'))"
    }
  ]

6.プロジェクトマネージャ:company-b-admin

企業Aのプロジェクト責任者から、権限移譲を受ける形で全ての権限を付与します。

{
  "statements": [
    {
      "api": "*",
      "effect": "allow"
    }
/*    {
 *      "effect": "allow",
 *     "api": [
 *       "User:updateUserPermission",
 *       "User:deleteUserPermission"
 *     ],
 *     "condition": "not ((pathVariable('user_name') matches 'com-b-.*') or (pathVariable('user_name') matches 'app-.*'))"
 *   },
 */
  ]
}
パーミッション構文記載例の解説

“api”: “*”と指定することで全てのAPI操作を許可しています。

コメントアウトしておりますが、企業Bとアプリケーションへの直接権限指定設定は実行可能になっていることに注目ください。開発で必要となった場合は、必要に応じて追加できる権限があります。

#    {
#      "effect": "allow",
#      "api": [
#        "User:updateUserPermission",
#        "User:deleteUserPermission"
#      ],
#      "condition": "not ((pathVariable('user_name') matches 'com-b-.*') or (pathVariable('user_name') matches 'app-.*'))"
#    },

7.開発者:company-b-developer

続いて開発者の権限です。

ここでも同じく、自身のセキュリティ関係のみ変更が可能になっています。アプリケーション用のSAMユーザーには最低限の権限のみ付与しますが、開発者にはそれ以上に変更したい要望がある場合多くSIM/Subscriberサービスには全権限を付与しています。

  • アプリケーションに必要な権限(ただし、変更権限も付与)
{
  "statements": [
    {
      "effect": "allow",
      "api": [
        "Order:*",
        "Credential:*",
        "Diagnostic:*",
        "EventHandler:*",
        "Role:*",
        "Query:*",
        "Stats:*",
        "Log:*"
      ],
      "condition": "httpMethod('GET')"
    },
    {
      "effect": "allow",
      "api": [
        "Group:*",
        "Sim:*",
        "Subscriber:*"
      ]
    },
    {
      "effect": "allow",
      "api": [
        "Order:registerOrderedSim",
        "User:listUsers",
        "User:getUser"
      ]
    },
    {
      "effect": "allow",
      "api": [
        "User:listUserAuthKeys",
        "User:generateUserAuthKey",
        "User:deleteUserAuthKey",
        "User:getUserAuthKey"
      ],
      "condition": "(pathVariable('user_name')==samUserName) or (samUserName matches 'app-.*')"
    }
  ]
}
パーミッション構文記載例の解説

まずは、アプリケーションの機能開発に必要な権限を付与します。

   {
      "effect": "allow",
      "api": [
        "Group:*",
        "Sim:*",
        "Subscriber:*"
      ]
    },

次に、”Order:registerOrderedSim”ですが、注文したSIMを登録するために付与しています。後段でAPIクレデンシャルを生成するための権限を付与しています。少しわかりにくいのですが、”User:listUsers”、と”User:getUser”を付与しているのは、ユーザーコンソールにてAPIキーを生成する際に必要な権限です。

   {
      "effect": "allow",
      "api": [
        "Order:registerOrderedSim",
        "User:listUsers",
        "User:getUser"
      ]
    },
    {
      "effect": "allow",
      "api": [
        "User:listUserAuthKeys",
        "User:generateUserAuthKey",
        "User:deleteUserAuthKey",
        "User:getUserAuthKey"
      ],
      "condition": "(pathVariable('user_name')==samUserName) or (samUserName matches 'app-.*')"
    }

8.アプリケーション:app-production

最後にアプリケーション用のロール設定です。

今回は、必要最低限に下記のAPIのみ許可する設定とします。SIMの操作にはSimサービスと同等の機能を持ったSubscriberサービスもありますが、実際に呼び出す前提のSimサービスのみ指定します。なお、実際にはAPIクレデンシャルが必要になりますが、その発行自体は企業Bのプロジェクトマネージャーまたは開発者が操作可能です。

  • SIM一覧の閲覧
  • SIM情報の閲覧
  • SIMのステータス変更
  • グループ一覧の閲覧
  • グループ情報の閲覧
{
  "statements": [
    {
      "effect": "allow",
      "api": [
        "Sim:listSims",
        "Sim:getSim",
        "Group:listGroups",
        "Group:getGroup",
        "Query:searchSims"
      ]
    },
    {
      "effect": "allow",
      "api": [
        "Sim:activateSim",
        "Sim:deactivateSim",
        "Sim:suspendSim",
        "Sim:setSimToStandby",
        "Sim:terminateSim"
      ],
      "condition": "pathVariable('sim_id') matches '8981100001234567890|8981100001234567891'"
    }
  ]
}
パーミッション構文記載例の解説

2つ目のパーミッション構文について詳しく解説します。

ここでは、プロジェクトで利用している2つのSIMに限ってSIMのステータス変更を許可させる設定をしています。

SIMサービスのAPIにはパス変数にsim_idが連携されますが、複数のSIMのIDを|(パイプ)で区切ることでOR条件として設定しています。

3.SAMユーザーへのロールの適応

作成したSAMユーザーへそれぞれ対応するロールを適応させます。

詳細な手順は、公式ドキュメント「ロール指定 (関連付け)」をご確認ください。下の通り、一つのSAMユーザーに対して複数のロールが適用できます。

4.直接指定権限設定

これまではデフォルト権限設定とロールを使って権限を付与してきました。

企業Bのプロジェクトマネージャーが必要に応じて開発の権限を直接指定権限設定にて付与します。今回は、閉域網サービスを開発するための権限を付与します。

直接指定権限設定で以下の権限を付与することで、VPG(VirtualPrivateGateway)関連の全操作がそのユーザーに付与できます。

{
  "statements": [
    {
      "effect": "allow",
      "api": [
        "VirtualPrivateGateway:*"
      ]
    }
  ]
}

5.動作確認

いよいよ動作確認していきたいと思います。例として、私が企業Bの開発者(ユーザー名:com-b-taketo-matsunaga)として開発者ロール(ロール名:company-b-developer)にVPGの操作権限を持っているユーザーとします。

ユーザーコンソールで操作し、権限設定が働いているかみてみます。下の通りうまく動作していますね。画面操作で許可/拒否されるものはAPIで実行した場合も同様に許可/拒否されます。

デフォルト権限によるBillingが閲覧拒否
app-developerロール権限によるSIMの閲覧許可

6.新規ユーザー追加手順

このIoTプロジェクトでは、ルートユーザーである企業Aのプロジェクト責任者がユーザー追加と削除を行います。

企業Bから依頼を受け、下記のように設定したあとに追加されたユーザーへログイン情報を確認し、メールなどでお伝えしてください。また、初めてのログイン後パスワードを変更頂くように各ユーザーにご依頼ください。

  • ユーザー名
  • 初期パスワード
  • ログインURL

新規ユーザー追加手順

SAMユーザーのログイン情報の確認方法

SAMユーザーの権限の変更後はログアウト後、再ログインが必要です。

さいごに

いかがでしたでしょうか?ちょっと複雑でしたら非常に細かい権限管理ができ、それぞれのメンバーに必要最低限の権限を付与できる仕組みでした。もう少し簡単な例でSAMを使い始めたい方はアクセス権限テンプレートを利用頂くことも可能です。

総合格闘技と言われるほど、IoTプロジェクトでは様々な技術が必要で複数の協力者が参画することがあります。是非適切な権限を付与し、安心して作業できるようにして頂ければ幸いです。

― ソラコム松永(taketo)