diff --git a/charts/topaz/README.md b/charts/topaz/README.md index 5fba4fe..7f95d89 100644 --- a/charts/topaz/README.md +++ b/charts/topaz/README.md @@ -373,23 +373,25 @@ decisionLogs: ## Service Ports -Topaz pods expose four ports: +Topaz pods expose the following ports: | Protocol | Default Port | Description | |----------|--------------|-------------| | gRPC | 8282 | gRPC services | | HTTPS | 8383 | REST endpoints and web console | | Health | 8484 | gRPC [health service](https://github.com/grpc/grpc/blob/master/doc/health-checking.md) | -| Metrics | 8585 | Prometheus metrics | +| Metrics | 8585 | Prometheus metrics [optional, enabled by default] | +| Profiler | 8686 | Profiler service [optional, disabled by default] | The default ports can be overridden in `values.yaml`: ```yaml ports: - grpc: 9292 - https: 9393 - health: 9494 - metrics: 9595 + grpc: 8282 + https: 8383 + health: 8484 + metrics: 8585 + profiler: 8686 ``` The metrics service can be disabled if not needed: @@ -399,6 +401,12 @@ metrics: enabled: false ``` +The profiler service can be enabled using: +```yaml +profiler: + enabled: true +``` + ## Authentication By default, anyone with access to the topaz pod can use the gRPC and REST endpoints. That means that any diff --git a/charts/topaz/templates/config.yaml b/charts/topaz/templates/config.yaml index 1e44a52..7c9cb56 100644 --- a/charts/topaz/templates/config.yaml +++ b/charts/topaz/templates/config.yaml @@ -15,6 +15,12 @@ stringData: log_level: {{ .Values.logLevel | default "info" }} grpc_log_level: {{ .Values.grpcLogLevel | default "info" }} + {{- if (.Values.profiler).enabled }} + debug_service: + enabled: true + listen_address: 0.0.0.0:{{ (.Values.ports).profiler | default "8686"}} + {{- end }} + {{- if empty ((.Values.directory).remote).address }} directory: db_path: /db/directory.db diff --git a/charts/topaz/templates/deployment.yaml b/charts/topaz/templates/deployment.yaml index 064f5c4..7390b0c 100644 --- a/charts/topaz/templates/deployment.yaml +++ b/charts/topaz/templates/deployment.yaml @@ -47,6 +47,13 @@ spec: {{- toYaml .Values.securityContext | nindent 12 }} image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} + args: + - run + - -c + - /config/config.yaml + {{- if (.Values.profiler).enabled }} + - --debug + {{- end }} ports: - name: grpc containerPort: {{ (.Values.ports).grpc | default "8282" }} @@ -57,11 +64,16 @@ spec: - name: health containerPort: {{ (.Values.ports).health | default "8484" }} protocol: TCP - {{- if (.Values.metrics).enabled }} + {{- if (.Values.metrics).enabled }} - name: metrics containerPort: {{ (.Values.ports).metrics | default "8585" }} protocol: TCP - {{- end }} + {{- end }} + {{- if (.Values.profiler).enabled }} + - name: profiler + containerPort: {{ (.Values.ports).profiler | default "8686"}} + protocol: TCP + {{- end }} startupProbe: grpc: port: {{ (.Values.ports).health | default "8484" }} diff --git a/charts/topaz/templates/service.yaml b/charts/topaz/templates/service.yaml index f847663..c57e334 100644 --- a/charts/topaz/templates/service.yaml +++ b/charts/topaz/templates/service.yaml @@ -1,3 +1,4 @@ +--- apiVersion: v1 kind: Service metadata: @@ -25,5 +26,11 @@ spec: protocol: TCP name: metrics {{- end }} + {{- if (.Values.profiler).enabled }} + - port: {{ (.Values.ports).profiler | default "8686" }} + targetPort: profiler + protocol: TCP + name: profiler + {{- end }} selector: {{- include "topaz.selectorLabels" . | nindent 4 }} diff --git a/charts/topaz/test/assertions.json b/charts/topaz/test/assertions.json new file mode 100644 index 0000000..2d2be4d --- /dev/null +++ b/charts/topaz/test/assertions.json @@ -0,0 +1,14 @@ +{ + "assertions": [ + { + "check": { + "subject_type": "user", + "subject_id": "rick@the-citadel.com", + "relation": "member", + "object_type": "group", + "object_id": "admin" + }, + "expected": true + } + ] +} diff --git a/charts/topaz/test/data/domain_objects.json b/charts/topaz/test/data/domain_objects.json new file mode 100644 index 0000000..82cff7c --- /dev/null +++ b/charts/topaz/test/data/domain_objects.json @@ -0,0 +1,34 @@ +{ + "objects": [ + { + "type": "resource-creator", + "id": "resource-creators", + "display_name": "Resource creators", + "properties": {} + }, + { + "type": "resource", + "id": "mega-seed", + "display_name": "Mega seed", + "properties": {} + }, + { + "type": "resource", + "id": "portal-gun", + "display_name": "Portal gun", + "properties": {} + }, + { + "type": "resource", + "id": "space-cruiser", + "display_name": "Space cruiser", + "properties": {} + }, + { + "type": "resource", + "id": "time-crystal", + "display_name": "Time crystal", + "properties": {} + } + ] +} diff --git a/charts/topaz/test/data/domain_relations.json b/charts/topaz/test/data/domain_relations.json new file mode 100644 index 0000000..d5c0f16 --- /dev/null +++ b/charts/topaz/test/data/domain_relations.json @@ -0,0 +1,88 @@ +{ + "relations": [ + { + "object_type": "resource-creator", + "object_id": "resource-creators", + "relation": "member", + "subject_type": "user", + "subject_id": "morty@the-citadel.com" + }, + { + "object_type": "resource-creator", + "object_id": "resource-creators", + "relation": "member", + "subject_type": "user", + "subject_id": "rick@the-citadel.com" + }, + { + "object_type": "resource-creator", + "object_id": "resource-creators", + "relation": "member", + "subject_type": "user", + "subject_id": "summer@the-smiths.com" + }, + { + "object_type": "resource", + "object_id": "mega-seed", + "relation": "owner", + "subject_type": "user", + "subject_id": "morty@the-citadel.com" + }, + { + "object_type": "resource", + "object_id": "mega-seed", + "relation": "reader", + "subject_type": "user", + "subject_id": "rick@the-citadel.com" + }, + { + "object_type": "resource", + "object_id": "space-cruiser", + "relation": "owner", + "subject_type": "user", + "subject_id": "rick@the-citadel.com" + }, + { + "object_type": "resource", + "object_id": "space-cruiser", + "relation": "writer", + "subject_type": "user", + "subject_id": "morty@the-citadel.com" + }, + { + "object_type": "resource", + "object_id": "space-cruiser", + "relation": "reader", + "subject_type": "user", + "subject_id": "summer@the-smiths.com" + }, + { + "object_type": "resource", + "object_id": "portal-gun", + "relation": "owner", + "subject_type": "user", + "subject_id": "morty@the-citadel.com" + }, + { + "object_type": "resource", + "object_id": "portal-gun", + "relation": "writer", + "subject_type": "user", + "subject_id": "rick@the-citadel.com" + }, + { + "object_type": "resource", + "object_id": "time-crystal", + "relation": "writer", + "subject_type": "user", + "subject_id": "rick@the-citadel.com" + }, + { + "object_type": "resource", + "object_id": "time-crystal", + "relation": "reader", + "subject_type": "user", + "subject_id": "morty@the-citadel.com" + } + ] +} diff --git a/charts/topaz/test/data/idp_objects.json b/charts/topaz/test/data/idp_objects.json new file mode 100644 index 0000000..2e8edc8 --- /dev/null +++ b/charts/topaz/test/data/idp_objects.json @@ -0,0 +1,184 @@ +{ + "objects": [ + { + "type": "identity", + "id": "CiRmZDQ2MTRkMy1jMzlhLTQ3ODEtYjdiZC04Yjk2ZjVhNTEwMGQSBWxvY2Fs", + "properties": { + "kind": "IDENTITY_KIND_PID", + "provider": "local", + "verified": true + } + }, + { + "type": "identity", + "id": "CiRmZDI2MTRkMy1jMzlhLTQ3ODEtYjdiZC04Yjk2ZjVhNTEwMGQSBWxvY2Fs", + "properties": { + "kind": "IDENTITY_KIND_PID", + "provider": "local", + "verified": true + } + }, + { + "type": "group", + "id": "evil_genius", + "display_name": "evil_genius-group", + "properties": {} + }, + { + "type": "identity", + "id": "CiRmZDM2MTRkMy1jMzlhLTQ3ODEtYjdiZC04Yjk2ZjVhNTEwMGQSBWxvY2Fs", + "properties": { + "kind": "IDENTITY_KIND_PID", + "provider": "local", + "verified": true + } + }, + { + "type": "group", + "id": "admin", + "display_name": "admin-group", + "properties": {} + }, + { + "type": "identity", + "id": "summer@the-smiths.com", + "properties": { + "kind": "IDENTITY_KIND_EMAIL", + "provider": "local", + "verified": true + } + }, + { + "type": "identity", + "id": "CiRmZDA2MTRkMy1jMzlhLTQ3ODEtYjdiZC04Yjk2ZjVhNTEwMGQSBWxvY2Fs", + "properties": { + "kind": "IDENTITY_KIND_PID", + "provider": "local", + "verified": true + } + }, + { + "type": "identity", + "id": "rick@the-citadel.com", + "properties": { + "kind": "IDENTITY_KIND_EMAIL", + "provider": "local", + "verified": true + } + }, + { + "type": "identity", + "id": "beth@the-smiths.com", + "properties": { + "kind": "IDENTITY_KIND_EMAIL", + "provider": "local", + "verified": true + } + }, + { + "type": "group", + "id": "viewer", + "display_name": "viewer-group", + "properties": {} + }, + { + "type": "identity", + "id": "jerry@the-smiths.com", + "properties": { + "kind": "IDENTITY_KIND_EMAIL", + "provider": "local", + "verified": true + } + }, + { + "type": "user", + "id": "jerry@the-smiths.com", + "display_name": "Jerry Smith", + "properties": { + "email": "jerry@the-smiths.com", + "picture": "https://www.topaz.sh/assets/templates/citadel/img/Jerry%20Smith.jpg", + "roles": [ + "viewer" + ], + "status": "USER_STATUS_ACTIVE" + } + }, + { + "type": "user", + "id": "beth@the-smiths.com", + "display_name": "Beth Smith", + "properties": { + "email": "beth@the-smiths.com", + "picture": "https://www.topaz.sh/assets/templates/citadel/img/Beth%20Smith.jpg", + "roles": [ + "viewer" + ], + "status": "USER_STATUS_ACTIVE" + } + }, + { + "type": "identity", + "id": "CiRmZDE2MTRkMy1jMzlhLTQ3ODEtYjdiZC04Yjk2ZjVhNTEwMGQSBWxvY2Fs", + "properties": { + "kind": "IDENTITY_KIND_PID", + "provider": "local", + "verified": true + } + }, + { + "type": "user", + "id": "rick@the-citadel.com", + "display_name": "Rick Sanchez", + "properties": { + "email": "rick@the-citadel.com", + "picture": "https://www.topaz.sh/assets/templates/citadel/img/Rick%20Sanchez.jpg", + "roles": [ + "admin", + "evil_genius" + ], + "status": "USER_STATUS_ACTIVE" + } + }, + { + "type": "user", + "id": "morty@the-citadel.com", + "display_name": "Morty Smith", + "properties": { + "email": "morty@the-citadel.com", + "picture": "https://www.topaz.sh/assets/templates/citadel/img/Morty%20Smith.jpg", + "roles": [ + "editor" + ], + "status": "USER_STATUS_ACTIVE" + } + }, + { + "type": "user", + "id": "summer@the-smiths.com", + "display_name": "Summer Smith", + "properties": { + "email": "summer@the-smiths.com", + "picture": "https://www.topaz.sh/assets/templates/citadel/img/Summer%20Smith.jpg", + "roles": [ + "editor" + ], + "status": "USER_STATUS_ACTIVE" + } + }, + { + "type": "group", + "id": "editor", + "display_name": "editor-group", + "properties": {} + }, + { + "type": "identity", + "id": "morty@the-citadel.com", + "properties": { + "kind": "IDENTITY_KIND_EMAIL", + "provider": "local", + "verified": true + } + } + ] +} diff --git a/charts/topaz/test/data/idp_relations.json b/charts/topaz/test/data/idp_relations.json new file mode 100644 index 0000000..c01f67c --- /dev/null +++ b/charts/topaz/test/data/idp_relations.json @@ -0,0 +1,160 @@ +{ + "relations": [ + { + "object_type": "user", + "object_id": "jerry@the-smiths.com", + "relation": "manager", + "subject_type": "user", + "subject_id": "beth@the-smiths.com" + }, + { + "object_type": "user", + "object_id": "beth@the-smiths.com", + "relation": "manager", + "subject_type": "user", + "subject_id": "rick@the-citadel.com" + }, + { + "object_type": "user", + "object_id": "morty@the-citadel.com", + "relation": "manager", + "subject_type": "user", + "subject_id": "rick@the-citadel.com" + }, + { + "object_type": "user", + "object_id": "summer@the-smiths.com", + "relation": "manager", + "subject_type": "user", + "subject_id": "rick@the-citadel.com" + }, + { + "object_type": "identity", + "object_id": "beth@the-smiths.com", + "relation": "identifier", + "subject_type": "user", + "subject_id": "beth@the-smiths.com" + }, + { + "object_type": "identity", + "object_id": "CiRmZDA2MTRkMy1jMzlhLTQ3ODEtYjdiZC04Yjk2ZjVhNTEwMGQSBWxvY2Fs", + "relation": "identifier", + "subject_type": "user", + "subject_id": "rick@the-citadel.com" + }, + { + "object_type": "identity", + "object_id": "CiRmZDE2MTRkMy1jMzlhLTQ3ODEtYjdiZC04Yjk2ZjVhNTEwMGQSBWxvY2Fs", + "relation": "identifier", + "subject_type": "user", + "subject_id": "morty@the-citadel.com" + }, + { + "object_type": "identity", + "object_id": "CiRmZDI2MTRkMy1jMzlhLTQ3ODEtYjdiZC04Yjk2ZjVhNTEwMGQSBWxvY2Fs", + "relation": "identifier", + "subject_type": "user", + "subject_id": "summer@the-smiths.com" + }, + { + "object_type": "identity", + "object_id": "CiRmZDM2MTRkMy1jMzlhLTQ3ODEtYjdiZC04Yjk2ZjVhNTEwMGQSBWxvY2Fs", + "relation": "identifier", + "subject_type": "user", + "subject_id": "beth@the-smiths.com" + }, + { + "object_type": "identity", + "object_id": "CiRmZDQ2MTRkMy1jMzlhLTQ3ODEtYjdiZC04Yjk2ZjVhNTEwMGQSBWxvY2Fs", + "relation": "identifier", + "subject_type": "user", + "subject_id": "jerry@the-smiths.com" + }, + { + "object_type": "identity", + "object_id": "jerry@the-smiths.com", + "relation": "identifier", + "subject_type": "user", + "subject_id": "jerry@the-smiths.com" + }, + { + "object_type": "identity", + "object_id": "morty@the-citadel.com", + "relation": "identifier", + "subject_type": "user", + "subject_id": "morty@the-citadel.com" + }, + { + "object_type": "identity", + "object_id": "rick@the-citadel.com", + "relation": "identifier", + "subject_type": "user", + "subject_id": "rick@the-citadel.com" + }, + { + "object_type": "identity", + "object_id": "summer@the-smiths.com", + "relation": "identifier", + "subject_type": "user", + "subject_id": "summer@the-smiths.com" + }, + { + "object_type": "group", + "object_id": "admin", + "relation": "member", + "subject_type": "user", + "subject_id": "rick@the-citadel.com" + }, + { + "object_type": "group", + "object_id": "editor", + "relation": "member", + "subject_type": "user", + "subject_id": "morty@the-citadel.com" + }, + { + "object_type": "group", + "object_id": "editor", + "relation": "member", + "subject_type": "user", + "subject_id": "summer@the-smiths.com" + }, + { + "object_type": "group", + "object_id": "editor", + "relation": "member", + "subject_type": "group", + "subject_id": "admin", + "subject_relation": "member" + }, + { + "object_type": "group", + "object_id": "evil_genius", + "relation": "member", + "subject_type": "user", + "subject_id": "rick@the-citadel.com" + }, + { + "object_type": "group", + "object_id": "viewer", + "relation": "member", + "subject_type": "user", + "subject_id": "beth@the-smiths.com" + }, + { + "object_type": "group", + "object_id": "viewer", + "relation": "member", + "subject_type": "user", + "subject_id": "jerry@the-smiths.com" + }, + { + "object_type": "group", + "object_id": "viewer", + "relation": "member", + "subject_type": "group", + "subject_id": "editor", + "subject_relation": "member" + } + ] +} diff --git a/charts/topaz/test/decisions.json b/charts/topaz/test/decisions.json new file mode 100644 index 0000000..acbc6b9 --- /dev/null +++ b/charts/topaz/test/decisions.json @@ -0,0 +1,27 @@ +{ + "assertions": [ + { + "check_decision": { + "policy_context": { + "path": "rebac.check", + "decisions": [ + "allowed" + ] + }, + "identity_context": { + "identity": "rick@the-citadel.com", + "type": "IDENTITY_TYPE_SUB" + }, + "resource_context": { + "object_id": "admin", + "object_type": "group", + "relation": "member" + }, + "policy_instance": { + "name": "policy-rebac" + } + }, + "expected": true + } + ] +} diff --git a/charts/topaz/test/manifest.yaml b/charts/topaz/test/manifest.yaml new file mode 100644 index 0000000..a0bce29 --- /dev/null +++ b/charts/topaz/test/manifest.yaml @@ -0,0 +1,51 @@ +# yaml-language-server: $schema=https://www.topaz.sh/schema/manifest.json +--- + +# model +model: + version: 3 + +# object type definitions +types: + # user represents a user that can be granted role(s) + user: + relations: + manager: user + + permissions: + ### display_name: user#in_management_chain ### + in_management_chain: manager | manager->in_management_chain + + + # group represents a collection of users and/or (nested) groups + group: + relations: + member: user | group#member + + + # identity represents a collection of identities for users + identity: + relations: + identifier: user + + + # resource creator represents a user type that can create new resources + resource-creator: + relations: + member: user | group#member + + permissions: + can_create_resource: member + + + # resource represents a protected resource + resource: + relations: + owner: user + writer: user | group#member + reader: user | group#member + + permissions: + can_read: reader | writer | owner + can_write: writer | owner + can_delete: owner diff --git a/charts/topaz/test/tests.yaml b/charts/topaz/test/tests.yaml index ddaab7d..4198b80 100644 --- a/charts/topaz/test/tests.yaml +++ b/charts/topaz/test/tests.yaml @@ -7,8 +7,18 @@ tests: 8282: 8282 8383: 8383 run: - - ${TOPAZ:-topaz} ds get manifest -H localhost:8282 --stdout --plaintext - - curl -s http://localhost:8383/api/v2/policies > /dev/null + - > + ${TOPAZ:-topaz} ds set manifest charts/topaz/test/manifest.yaml + -H localhost:8282 --plaintext + - > + ${TOPAZ:-topaz} ds import --directory charts/topaz/test/data + -H localhost:8282 --plaintext + - > + ${TOPAZ:-topaz} ds test exec charts/topaz/test/assertions.json --summary + -H localhost:8282 --plaintext + - > + ${TOPAZ:-topaz} az test exec charts/topaz/test/decisions.json --summary + -H localhost:8282 --plaintext - name: topaz-tls secrets: @@ -28,5 +38,15 @@ tests: 8282: 8282 8383: 8383 run: - - ${TOPAZ:-topaz} ds get manifest -H localhost:8282 --stdout --insecure --no-check - - curl -ks https://localhost:8383/api/v2/policies > /dev/null + - > + ${TOPAZ:-topaz} ds set manifest charts/topaz/test/manifest.yaml + -H localhost:8282 --insecure + - > + ${TOPAZ:-topaz} ds import --directory charts/topaz/test/data + -H localhost:8282 --insecure + - > + ${TOPAZ:-topaz} ds test exec charts/topaz/test/assertions.json --summary + -H localhost:8282 --insecure + - > + ${TOPAZ:-topaz} az test exec charts/topaz/test/decisions.json --summary + -H localhost:8282 --insecure diff --git a/charts/topaz/test/tls.values.yaml b/charts/topaz/test/tls.values.yaml index c22c80a..f75ee76 100644 --- a/charts/topaz/test/tls.values.yaml +++ b/charts/topaz/test/tls.values.yaml @@ -2,3 +2,6 @@ tls: grpc: grpc-cert https: gateway-cert + +profiler: + enabled: true diff --git a/charts/topaz/values.yaml b/charts/topaz/values.yaml index ea3bba7..338375a 100644 --- a/charts/topaz/values.yaml +++ b/charts/topaz/values.yaml @@ -192,17 +192,22 @@ auth: # secretKey: api-key # Port configuration. -# The grpc and http ports can be overriden per service in the serviceOverrides section below. ports: + # gRPC services. grpc: 8282 + # REST services and web UI. https: 8383 + # gRPC Health service (https://github.com/grpc-ecosystem/grpc-health-probe). health: 8484 + # Prometheus metrics (GET /metrics). metrics: 8585 + # Profiling service (GET /debug/pprof) + profiler: 8686 # TLS configuration. # To run topaz with TLS, provide the names of Kuebernetes secrets of type kubernetes.io/tls # If not provided, topaz runs without TLS. -# tls: +tls: # grpc: topaz-grpc-cert # https: topaz-https-cert @@ -210,9 +215,10 @@ ports: metrics: # Enable metrics. enabled: true - # Expose zpages UI for grpc metrics. - # The pages are available at /debug/tracez and /debug/rpcz. - zpages: true + +# Profiler configuration +profiler: + enabled: false # Global gRPC configuration. # These settings can be overridden per service in serviceOverrides below. diff --git a/tools/ktest/ktest.py b/tools/ktest/ktest.py index f447583..f88ebb4 100755 --- a/tools/ktest/ktest.py +++ b/tools/ktest/ktest.py @@ -5,7 +5,7 @@ import subprocess from contextlib import contextmanager, ExitStack from os import path -from typing import Iterator, Sequence +from typing import Iterator, Sequence, TextIO import click import git @@ -36,8 +36,8 @@ def __init__(self, test: Test, spec_path: str): self.spec_path = spec_path self.git_root = git_root(__file__) - def run(self): - with self.new_namespace(self.test.name) as ns: + def run(self, teardown: bool = True): + with self.new_namespace(self.test.name, teardown) as ns: self.set_image_pull_secret(ns) for secret in self.test.secrets: @@ -150,7 +150,7 @@ def subprocess(args: str, check=True): @staticmethod @contextmanager - def new_namespace(name: str) -> Iterator["Namespace"]: + def new_namespace(name: str, teardown: bool) -> Iterator["Namespace"]: ns = Namespace(name) if ns.ns_exists(): logger.info("namespace '%s' already exists. deleting it...", name) @@ -161,13 +161,16 @@ def new_namespace(name: str) -> Iterator["Namespace"]: yield ns - echo("🐳", "Deleting namespace:", name) - ns.delete_ns() + if teardown: + echo("🐳", "Deleting namespace:", name) + ns.delete_ns() @click.command() @click.argument("specfile", type=click.File()) -def main(specfile): +@click.option("--include", "-i", multiple=True, help="Only run specified test(s)") +@click.option("--teardown/--no-teardown", default=True, show_default=True) +def main(specfile: TextIO, include: Sequence[str], teardown: bool): """Run tests in a kubernetes cluster. SPECFILE: path to a YAML file with test definitions. @@ -179,9 +182,10 @@ def main(specfile): spec = Spec(**yaml.safe_load(specfile)) spec_path = path.dirname(specfile.name) - for test in spec.tests: + tests = spec.tests if not include else [t for t in spec.tests if t.name in include] + for test in tests: echo("🏁", "Starting test:", test.name) - Runner(test, spec_path).run() + Runner(test, spec_path).run(teardown) def git_root(from_path: str) -> str: