Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Introduction to Open Policy Agent

Kengo Suzuki
September 11, 2018

Introduction to Open Policy Agent

皆様の組織には何かしらのポリシーがあると思います。単なる組織内ルールにとどまらない、法的要件や技術的制約化の守りごとを定義したポリシーがあるかと思います。これらを手順書レベルやサービス内定義するのではなく、OPAをサイドカーなりでは知らせることで、柔軟で詳細なアクセス制御を、サービスのりコンパイルなしで実施できるようになります。今回はこのOPAについてお話したいと思います。

Kengo Suzuki

September 11, 2018
Tweet

More Decks by Kengo Suzuki

Other Decks in Technology

Transcript

  1. What I will talk 1. Why Policy Engine 2. What’s

    Policy Engine 3. Use-Case of Policy Engine
  2. What I will talk 1. Why Policy Engine 2. What’s

    Policy Engine 3. Use-Case of Policy Engine
  3. What I will talk 1. Why Policy Engine 2. What’s

    Policy Engine 3. Use-Case of Policy Engine
  4. kind: role version: v3 metadata: name: backend spec: allow: logins:

    - backend node_labels: '*': '*' rules: - resources: - session verbs: - list - read deny: node_labels: sre-only: true kind: role version: v3 metadata: name: frontend spec: allow: logins: - frontend node_labels: application: - “^front-web(\d+)$” service: - web rules: - resources: - session verbs: - list - read deny: {} kind: role version: v3 metadata: name: sre spec: allow: logins: - sre - admin node_labels: '*': '*' rules: - resources: - '*' verbs: - '*' kind: role version: v3 metadata: name: outsource spec: allow: logins: - outsource node_labels: env: - stg - dev deny: {} "MMPX"MM
  5. kind: role version: v3 metadata: name: backend spec: allow: logins:

    - backend node_labels: '*': '*' rules: - resources: - session verbs: - list - read deny: node_labels: sre-only: true kind: role version: v3 metadata: name: frontend spec: allow: logins: - frontend node_labels: application: - “^front-web(\d+)$” service: - web rules: - resources: - session verbs: - list - read deny: {} kind: role version: v3 metadata: name: sre spec: allow: logins: - sre - admin node_labels: '*': '*' rules: - resources: - '*' verbs: - '*' kind: role version: v3 metadata: name: outsource spec: allow: logins: - outsource node_labels: env: - stg - dev deny: {} %FOZTPNFOPEFT
  6. kind: role version: v3 metadata: name: backend spec: allow: logins:

    - backend node_labels: '*': '*' rules: - resources: - session verbs: - list - read deny: node_labels: sre-only: true kind: role version: v3 metadata: name: frontend spec: allow: logins: - frontend node_labels: application: - “^front-web(\d+)$” service: - web rules: - resources: - session verbs: - list - read deny: {} kind: role version: v3 metadata: name: sre spec: allow: logins: - sre - admin node_labels: '*': '*' rules: - resources: - '*' verbs: - '*' kind: role version: v3 metadata: name: outsource spec: allow: logins: - outsource node_labels: env: - stg - dev deny: {} "MMPXTPNFOPEF
  7. kind: role version: v3 metadata: name: backend spec: allow: logins:

    - backend node_labels: '*': '*' rules: - resources: - session verbs: - list - read deny: node_labels: sre-only: true kind: role version: v3 metadata: name: frontend spec: allow: logins: - frontend node_labels: application: - “^front-web(\d+)$” service: - web rules: - resources: - session verbs: - list - read deny: {} kind: role version: v3 metadata: name: sre spec: allow: logins: - sre - admin node_labels: '*': '*' rules: - resources: - '*' verbs: - '*' kind: role version: v3 metadata: name: outsource spec: allow: logins: - outsource node_labels: env: - outsource env: - stg - dev deny: {} "MMPXTPNFFOW
  8. What I will talk 1. Why Policy Engine 2. What’s

    Policy Engine 3. Use-Case of Policy Engine
  9. Policy Engine - “It compares the request coming from the

    enforcement component against policy in order to determine whether the request is authorized or not” - “decouples the policy implementation from the business logic so that administrators can define policy without changing the application while still keeping up with the size, complexity, and dynamic nature of modern applications”
  10. Data - Base Document 1PMJDZ&OHJOF %FDJTJPO-BZFS %BUB %PDVNFOU 1PMJDZ 3VMF

     - In memory - can store in Disk - Represented in JSON - Restful HTTP API - Data API
  11. curl -X PUT --data-binary @data.json localhost:8181/v1/data/servers { "servers": [ {"id":

    "s1", "name": "app", "protocols": ["https", "ssh"], "ports": ["p1", "p3"]}, {"id": "s2", "name": "db", "protocols": ["mysql"], "ports": [“p1”, "p2"]}, {"id": "s3", "name": "cache", "protocols": ["memcache"], "ports": [“p1”, “p2"]}, {"id": "s4", "name": "dev", "protocols": ["http"], "ports": ["p1", "p3"]} ], "networks": [ {"id": "n1", "public": false}, {"id": "n2", "public": false}, {"id": "n3", "public": true} ], "ports": [ {"id": "ssh", "networks": ["n1"]}, {"id": “db", "networks": ["n2"]}, {"id": "web", "networks": ["n3"]} ] }
  12. PUT https://example.com/v1/data/servers HTTP/1.1 Content-Type: application/json-patch+json { "servers": [ {"id": "s1",

    "name": "app", "protocols": ["https", "ssh"], "ports": ["mgt", “web"]}, {"id": "s2", "name": "db", "protocols": ["mysql"], "ports": [“mgt”, "db"]}, {"id": "s3", "name": "cache", "protocols": ["memcache"], "ports": [“mgt”, “db"]}, {"id": "s4", "name": "dev", "protocols": ["http"], "ports": ["mgt", "web"]} ], "networks": [ {"id": "n1", "public": false}, {"id": "n2", "public": false}, {"id": "n3", "public": true} ], "ports": [ {"id": "mgt", "networks": ["n1"]}, {"id": “db", "networks": ["n2"]}, {"id": "web", "networks": ["n3"]} ] }
  13. Policy (Rule) 1PMJDZ&OHJOF %FDJTJPO-BZFS %BUB %PDVNFOU 1PMJDZ 3VMF  -

    Written in Rego - purpose-built - declarative - Represented in JSON - Restful HTTP API - Policy API
  14. curl -X PUT --data-binary @server-rule.rego localhost:8181/v1/policies/servers package opa.example import data.servers

    import data.networks import data.ports public_servers[s] { s = servers[_] s.ports[_] = ports[i].id ports[i].networks[_] = networks[j].id networks[j].public = true } violations[server] { server = servers[_] server.protocols[_] = "http" public_servers[server] = true }
  15. package opa.example import data.servers import data.networks import data.ports public_servers[s] {

    s = servers[_] s.ports[_] = ports[i].id ports[i].networks[_] = networks[j].id networks[j].public = true } violations[server] { server = servers[_] server.protocols[_] = "http" public_servers[server] = true }
  16. package opa.example import data.servers import data.networks import data.ports public_servers[s] {

    s = servers[_] s.ports[_] = ports[i].id ports[i].networks[_] = networks[j].id networks[j].public = true } violations[s] { s = servers[_] s.protocols[_] = "http" public_servers[s] }
  17. What I will talk 1. Why Policy Engine 2. What’s

    Policy Engine 3. Use-Case of Policy Engine
  18. [ { "labels": { "app": "my-example-app", "id": "1780d507-aea2-45cc-ae50-fa153c8e4a5a" }, "decision_id":

    "4ca636c1-55e4-417a-b1d8-4aceb67960d1", "revision": "W3sibCI6InN5cy9jYXRhbG9nIiwicyI6NDA3MX1d", "path": "http/example/authz/allow", "input": { "method": "GET", "path": "/salary/bob" }, "result": "true", "requested_by": "[::1]:59943", "timestamp": "2018-01-01T00:00:00.000000Z" } ]
  19. { "labels": { "app": "my-example-app", "id": "1780d507-aea2-45cc-ae50-fa153c8e4a5a" }, "bundle": {

    "name": "http/example/authz", "active_revision": "TODO", "last_successful_download": "2018-01-01T00:00:00.000Z", "last_successful_activation": "2018-01-01T00:00:00.000Z" } }
  20. - Payment Service - Everyone can see their own payment

    - Manager can see her/his member’s payment - HR Dep can see all employee’s payment Use-Case: Web-Service
  21. version: '2' services: api_server: image: openpolicyagent/demo-restful-api:0.2 ports: - 5000:5000 environment:

    - OPA_ADDR=http://opa:8181 - POLICY_PATH=/v1/data/httpapi/authz opa: image: openpolicyagent/opa:0.9.0 ports: - 8181:8181 command: - "run" - "--server" - "--log-level=debug" IUUQTXXXPQFOQPMJDZBHFOUPSHEPDTIUUQBQJBVUIPSJ[BUJPOIUNM
  22. # Grab basic information. We assume user is passed on

    a form. http_api_user = request.form['user'] orig_path_list = request.path.split("/") # Remove empty entries from list that are a result of the split # Example: "<some_prefix>/finance/salary/" will become ["", "finance", "salary", ""] http_api_path_list = [x for x request.path.split("/") if x] input_dict = { # create input to hand to OPA "input": { "user": request.form['user'], "path": [x for x request.path.split("/") if x], "method": request.method } } # ask OPA for a policy decision # (in reality OPA URL would be constructed from environment) rsp = requests.post("http://127.0.0.1:8181/v1/data/httpapi/authz", data=json.dumps(input_dict)) if rsp.json()["allow"]: # HTTP API allowed else:
  23. version: '2' services: api_server: image: openpolicyagent/demo-restful-api:0.2 ports: - 5000:5000 environment:

    - OPA_ADDR=http://opa:8181 - POLICY_PATH=/v1/data/httpapi/authz opa: image: openpolicyagent/opa:0.9.0 ports: - 8181:8181 command: - "run" - "--server" - "--log-level=debug" IUUQTXXXPQFOQPMJDZBHFOUPSHEPDTIUUQBQJBVUIPSJ[BUJPOIUNM
  24. package httpapi.authz # HTTP API request import input as http_api

    default allow = false # Allow users to get their own salaries. allow { http_api.method = "GET" http_api.path = ["finance", "salary", username] username = http_api.user } # Allow managers to get their member' salaries. # bob is alice's manager, and betty is charlie's. members = {"alice": [], "charlie": [], "bob": ["alice"], "betty": [“charlie"]} allow { http_api.method = "GET" http_api.path = ["finance", "salary", username] members[http_api.user][_] = username } # Allow HR members to get anyone's salary. hr = [ “david" ] allow { http_api.method = "GET" http_api.path = ["finance", "salary", _] hr[_] = http_api.user } DVSMVTFSBMJDFQBTTXPSEMPDBMIPTUpOBODFTBMBSZBMJDF 4VDDFTTVTFSBMJDFJTBVUIPSJ[FE
  25. package httpapi.authz # HTTP API request import input as http_api

    default allow = false # Allow users to get their own salaries. allow { http_api.method = "GET" http_api.path = ["finance", "salary", username] username = http_api.user } # Allow managers to get their member' salaries. # bob is alice's manager, and betty is charlie's. members = {"alice": [], "charlie": [], "bob": ["alice"], "betty": [“charlie"]} allow { http_api.method = "GET" http_api.path = ["finance", "salary", username] members[http_api.user][_] = username } # Allow HR members to get anyone's salary. hr = [ “david" ] allow { http_api.method = "GET" http_api.path = ["finance", "salary", _] hr[_] = http_api.user } DVSMVTFSCPCQBTTXPSEMPDBMIPTUpOBODFTBMBSZBMJDF 4VDDFTTVTFSCPCJTBVUIPSJ[FE
  26. package httpapi.authz # HTTP API request import input as http_api

    default allow = false # Allow users to get their own salaries. allow { http_api.method = "GET" http_api.path = ["finance", "salary", username] username = http_api.user } # Allow managers to get their member' salaries. # bob is alice's manager, and betty is charlie's. members = {"alice": [], "charlie": [], "bob": ["alice"], "betty": [“charlie"]} allow { http_api.method = "GET" http_api.path = ["finance", "salary", username] members[http_api.user][_] = username } # Allow HR members to get anyone's salary. hr = [ “david" ] allow { http_api.method = "GET" http_api.path = ["finance", "salary", _] hr[_] = http_api.user } DVSMVTFSEBWJEQBTTXPSEMPDBMIPTUpOBODFTBMBSZBMJDF 4VDDFTTVTFSEBWJEJTBVUIPSJ[FE DVSMVTFSEBWJEQBTTXPSEMPDBMIPTUpOBODFTBMBSZCPC 4VDDFTTVTFSEBWJEJTBVUIPSJ[FE
  27. # OPA-docker-auth plugin % docker run -d --restart=always -v $PWD/policies:/policies

    -v /run/docker/plugins:/run/ docker/plugins openpolicyagent/opa-docker-authz:0.2 -policy-file /policies/example.reg # Inside the rule (Deny any request % cat policies/example.rego package docker.authz default allow = false # Run Docker Daemon w/ opa-docker-auth plugin as Access Authorization plugin % /usr/bin/dockerd -H fd:// —authorization-plugin=opa-docker-authz # Watch % docker ps Error response from daemon: authorization denied by plugin opa-docker-authz: request rejected by administrative policy
  28. # OPA-docker-auth plugin % docker run -d --restart=always -v $PWD/policies:/policies

    -v /run/docker/plugins:/run/docker/plugins openpolicyagent/opa-docker-authz:0.2 -policy-file /policies/example.reg # Inside the rule (Deny any request % cat policies/example.rego package docker.authz default allow = false # Run Docker Daemon w/ opa-docker-auth plugin as Access Authorization plugin % /usr/bin/dockerd -H fd:// —authorization-plugin=opa-docker-authz # Watch % docker ps Error response from daemon: authorization denied by plugin opa-docker-authz: request rejected by administrative policy
  29. # OPA-docker-auth plugin % docker run -d --restart=always -v $PWD/policies:/policies

    -v /run/docker/plugins:/run/docker/plugins openpolicyagent/opa-docker-authz:0.2 -policy-file /policies/example.reg # Inside the rule (Deny any request % cat policies/example.rego package docker.authz default allow = false # Run Docker Daemon w/ opa-docker-auth plugin as Access Authorization plugin % /usr/bin/dockerd -H fd:// —authorization-plugin=opa-docker-authz # Watch % docker ps Error response from daemon: authorization denied by plugin opa-docker-authz: request rejected by administrative policy
  30. # OPA-docker-auth plugin % docker run -d --restart=always -v $PWD/policies:/policies

    -v /run/docker/plugins:/run/docker/plugins openpolicyagent/opa-docker-authz:0.2 -policy-file /policies/example.reg # Inside the rule (Deny any request % cat policies/example.rego package docker.authz default allow = false # Run Docker Daemon w/ opa-docker-auth plugin as Access Authorization plugin % /usr/bin/dockerd -H fd:// —authorization-plugin=opa-docker-authz # Watch % docker ps Error response from daemon: authorization denied by plugin opa-docker-authz: request rejected by administrative policy
  31. # OPA-docker-auth plugin % docker run -d --restart=always -v $PWD/policies:/policies

    -v /run/docker/plugins:/run/docker/plugins openpolicyagent/opa-docker-authz:0.2 -policy-file /policies/example.reg # Inside the rule (Deny any request % cat policies/example.rego package docker.authz default allow = false # Run Docker Daemon w/ opa-docker-auth plugin as Access Authorization plugin % /usr/bin/dockerd -H fd:// —authorization-plugin=opa-docker-authz # Watch % docker ps Error response from daemon: authorization denied by plugin opa-docker-authz: request rejected by administrative policy
  32. # OPA-docker-auth plugin % docker run -d --restart=always -v $PWD/policies:/policies

    -v /run/docker/plugins:/run/docker/plugins openpolicyagent/opa-docker-authz:0.2 -policy-file /policies/example.reg # Inside the rule (Deny any request % cat policies/example.rego package docker.authz default allow = false # Run Docker Daemon w/ opa-docker-auth plugin as Access Authorization plugin % /usr/bin/dockerd -H fd:// —authorization-plugin=opa-docker-authz # Watch % docker ps Error response from daemon: authorization denied by plugin opa-docker-authz: request rejected by administrative policy
  33. % cat policies/example.rego package docker.authz default allow = false #

    allow if the user is granted read/write access. allow { user_id = input.Headers["Authz-User"] user = users[user_id] not user.readOnly } # users defines permissions for the user. In this case, we define a single # attribute 'readOnly' that controls the kinds of commands the user can run. users = { "alice": {"readOnly": false}, }
  34. % docker ps % docker logs 2018/08/12 13:08:39 Querying OPA

    policy data.docker.authz.allow. Input: { "AuthMethod": "", "Body": null, "Headers": { "User-Agent": "Go-http-client/1.1" }, "Method": "GET", "Path": "/containers/ 8690a3302bb73d29c7cd182b91058e7549e0fbb10f0f025c604d8f793f63bb aa/json", "User": “alice" } 2018/08/12 13:08:39 Returning OPA policy decision: true
  35. - Why Policy Engine - RBACΛ࢝Ίͱͨ͠ϧʔϧద༻ͷਏΈ - What’s Policy Engine

    - Policy Engineొ৔ - Unified, Declartive & Context-AwareͳOPAొ৔ - Use Case of Policy Engine - HTTP API, Docker - ୺຤ϕʔεͷೝՄʹܨ͛ΒΕΔͱ͍Ζ͍ΖḿΓͦ͏ ·ͱΊ