APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

BackstageのPermission管理でもPolicy as Codeを

Backstage Permission Framework 拡張Plugin

こんにちは。ACS事業部亀崎です。これまで何回かにわたってBackstageのPermissionについてご紹介してきました。

techblog.ap-com.co.jp

techblog.ap-com.co.jp

techblog.ap-com.co.jp

最初の回でご紹介した通り、BackstageのPermission(RBAC)のPolicy指定は標準ではソースコードで記述する必要があります。(以下のようなイメージです)

class TestPermissionPolicy implements PermissionPolicy {
  async handle(
    request: PolicyQuery,
    user?: PolicyQueryUser,
  ): Promise<PolicyDecision> {
    if (isResourcePermission(request.permission, 'catalog-entity')) {
      return createCatalogConditionalDecision(
        request.permission,
        {
          anyOf: [
            catalogConditions.isEntityOwner({
              claims: user?.info.ownershipEntityRefs ?? [],
            }),
            isInSystem({ systemRef: 'interviewing' }),
          ],
        },
      );
    }

    return { result: AuthorizeResult.ALLOW };
  }
}

さすがにこれを組織ごとに定義するのは大変です。そこで3rd partyから有償無償さまざまな形でPermission Frameworkの拡張機能として、Policy定義を簡単にするPluginが提供されています。私が把握している限りでは主に以下の3つがあるようです。

Spotify Pluginsと Janus-idp RBAC pluginsではBackstage上のGUIからPolicyを定義することができます。Open Policy Agent for Backstageはその名の通りOPAを利用してBackstageのPolicyを定義するというものです。Backstage上のGUIは提供されていませんが、Policy定義言語のRegoを用いてコードでPolicyを定義することができます。

今回は最後のOpen Policy Agent for Backstage についてもう少しご紹介したいと思います。

Open Policy Agent for Backstageのご紹介

Open Policy Agentとは

Open Policy Agent(OPA)とは汎用のポリシー管理サービスです。

www.openpolicyagent.org

CNCFのgraduated projectの1つでOSSで公開・提供されており、Terraform policyやKubernetes Gateway Keeperなど多くのCloud Nativeサービスでも利用されています。

Policyの定義はRegoという言語で記述します。例えば KubernetesのPolicyの例は次のような形式になります。

(Kubernetesの例)

package kubernetes.admission

import rego.v1

deny contains sprintf("image '%s' comes from untrusted registry", [container.image]) if {
    input.request.kind.kind == "Pod"
    some container in input.request.object.spec.containers
    not startswith(container.image, "hooli.com/")
}

(ApplicationのAPI Access controlの例)

package application.authz

import rego.v1

# Only owner can update the pet's information
# Ownership information is provided as part of OPA's input
default allow := false

allow if {
    input.method == "PUT"
    some petid
    input.path = ["pets", petid]
    input.user == input.owner
}

利点

あらためて Backstage OPA Pluginsについて見ていきたいと思います。

github.com

BackstageのPolicy定義にOPA/Regoを利用する利点は、私は以下の点にあると考えています。

1) 汎用ポリシー言語による定義

上記で紹介したようにOPA/RegoはPolicy管理のために作られた汎用の仕組みです。Regoという言語を覚える必要はありますが、一度覚えてしまえば様々な cloud native serviceでも利用できます。PolicyはBackstageだけではなく様々なところで定義していかなればならないものです。そのツール/サービスごとにPolicy定義のやり方が違う場合、それぞれの仕組みや流儀を覚えなければなりません。汎用の仕組みであれば1つの仕組みで対応できます。

2) コードで管理可能

OPAではRegoという言語でPolicyを定義します。この定義はファイルで管理することができます。したがってGitHubなどのソースコード管理で履歴管理することができます。またPull requestなどの仕組みを利用して更新前にレビューなども実施可能です。また、履歴管理するということは誰がいつどういった修正を行ったのかということがすべて記録されるということでもあります。Policyの変更はシステムの運用で重要なイベントでもあると思います。その変更の記録が残されるというのは重要なことではないでしょうか。

こうしたPolicyをコードで管理することを Policy as Code (PaC) と呼び、Infrastructure as Code(IaC)同様次第に普及してきているものです。

3) テスト容易性

OPA/Regoでは定義したPolicyに対する様々なチェックツールが存在します。例えばフォーマットがルールに従っているか、命名規則がルールにあっているかなどをチェックするlinterもあります。また、Policyテストツールも存在します。アプリケーションコードのように修正 Pull requestでそうしたツールを実行し、記述フォーマットや内容に問題がないか事前にチェックすることが容易です。修正を行っていくと過去に定義した部分に悪影響を及ぼしてしまうということもあるかと思いますが、レグレッションテストが実施できるため、安心して修正を行っていくとができるようになります。

GUIのほうが楽なのでは???

OPA以外のPermission PluginはGUIでPolicyを定義することができました。こうしたツールのほうが楽なのでは?と感じる方も多いかと思います。確かに設定そのものはGUIのほうが楽です。しかし、テストの容易性やコード管理によるレビュー実施や記録を残すという点は運用上重要でもあり、そうした点はGUIでの作業では課題が残ると感じています。

こうした点から私はOpen Policy Agentを推したいと思います。

Backstageの Policy定義

では、実際にBackstageのPolicyを定義するとどういった内容になるでしょうか?

以下のものは OPA backstage pluginで紹介されている例です。

package rbac_policy

import rego.v1

# Helper method for constructing a conditional decision
conditional(plugin_id, resource_type, conditions) := {
    "result": "CONDITIONAL",
    "pluginId": plugin_id,
    "resourceType": resource_type,
    "conditions": conditions,
}

default decision := {"result": "ALLOW"}

permission := input.permission.name

claims := input.identity.claims

is_admin if "group:twocodersbrewing/maintainers" in claims

# decision := {"result": "DENY"} if {
#   permission == "catalog.entity.read"
#   not is_admin
# }

# Conditional based on claims (groups a user belongs to) unless they are an admin
decision := conditional("catalog", "catalog-entity", {"anyOf": [{
    "resourceType": "catalog-entity",
    "rule": "IS_ENTITY_OWNER",
    "params": {"claims": claims},
}]}) if {
    permission == "catalog.entity.delete"
    not is_admin
}

# Allow users to only see components unless they are an admin
# decision := conditional("catalog", "catalog-entity", {"anyOf": [{
#   "resourceType": "catalog-entity",
#   "rule": "IS_ENTITY_KIND",
#   "params": {"kinds": ["API"]},
# }]}) if {
#   permission == "catalog.entity.read"
#   not is_admin
# }

# Scaffolder Permissions

# Conditional based on scaffolder template tags unless they are an admin
decision := conditional("scaffolder", "scaffolder-template", {"not": {"anyOf": [{
    "resourceType": "scaffolder-template",
    "rule": "HAS_TAG",
    "params": {"tag": "admin"},
}]}}) if {
    permission == "scaffolder.template.parameter.read"
    not is_admin
}

# Conditional based on scaffolder actionID tags unless they are an admin
decision := conditional("scaffolder", "scaffolder-action", {"not": {"anyOf": [{
    "resourceType": "scaffolder-action",
    "rule": "HAS_ACTION_ID",
    "params": {"actionId": "debug:log"},
}]}}) if {
    permission == "scaffolder.action.execute"
    not is_admin
}

難しいな、と思うかもしれません。しかし、この内容は定義でGUIを利用できる他のPluginを利用した場合でも同様に考えていかなければならないものです。 同じように考えなけれならないのであれば、コードという形で見えるものにしたほうが整理しやすいと私は考えています。

(なお、もう少し簡単に定義できるようにplatt-policy-templateというサンプルテンプレート を用意しています)

まとめ

Backstageを利用していくうえで、アクセス制御というのは重要な要素になってくると思います。運用上の課題も含めて考えていくとOPAがベター(ベスト)な解になるのではと考えています。ぜひご利用をご検討ください。

そして実際ご利用になられる際には「platt-policy-template」をご利用ください。

platt-policy-template

実際の組織運営の場合、「管理者チームには全権を許可しよう」「特定のチームにはアクセスを制限しよう」など様々な条件が追加されていくと思います。 私どもで組織運営でよくある定義サンプルというものを公開いたしました。それが platt-policy-template です。

github.com

2024年10月現在、まず1つのユースケースを登録させていただきました。今後ドキュメントなども整備させていただこうと思っています。(OPA Backstage PluginのREADMEにもEcosystemの1つとしてリンクしていただいております)

さいごに

私達は開発者ポータルBackstageの活用を強く推進しています。「わからないことが多い」「もっと簡単に進められないか」といった声にお応えすべく、Managed Serviceというものもご用意しております。

techblog.ap-com.co.jp

このほか、Backstageだけではなく、Platform Engineeringをイチからはじめていきたい、といったこともご支援させていただいております。ご興味のある方はぜひご連絡ください。

www.ap-com.co.jp