Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add module to showcase external authorization using OPA #33

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions images/08-external-authorization.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
875 changes: 875 additions & 0 deletions modules/08-external-authorization/README.md

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions modules/08-external-authorization/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
configMapGenerator:
- name: opa-policy
namespace: workshop
files:
- policy.rego
generatorOptions:
disableNameSuffixHash: true
14 changes: 14 additions & 0 deletions modules/08-external-authorization/opa-ext-authz-serviceentry.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
name: opa-ext-authz-grpc-local
spec:
hosts:
- "opa-ext-authz-grpc.local"
endpoints:
- address: "127.0.0.1"
ports:
- name: grpc
number: 9191
protocol: GRPC
resolution: DNS
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
apiVersion: mutations.gatekeeper.sh/v1
kind: Assign
metadata:
name: opa-istio
spec:
applyTo:
- groups: [""]
kinds: ["Pod"]
versions: ["v1"]
match:
scope: Namespaced
kinds:
- apiGroups: ["*"]
kinds: ["Pod"]
namespaceSelector:
matchLabels:
opa-istio-injection: enabled
location: "spec.containers[name:opa-istio]"
parameters:
pathTests:
- subPath: "spec.containers[name:opa-istio]"
condition: MustNotExist
assign:
value:
image: openpolicyagent/opa:latest-istio
name: opa-istio
args:
- run
- --server
- --addr=localhost:8181
- --diagnostic-addr=0.0.0.0:8282
- --disable-telemetry
- --set
- "plugins.envoy_ext_authz_grpc.addr=:9191"
- --set
- "plugins.envoy_ext_authz_grpc.path=istio/authz/allow"
- --set
- "plugins.envoy_ext_authz_grpc.enable-reflection=true"
- --set
- "decision_logs.console=true"
- --watch
- /policy/policy.rego
volumeMounts:
- mountPath: /policy
name: opa-policy
readinessProbe:
httpGet:
path: /health?plugins
port: 8282
livenessProbe:
httpGet:
path: /health?plugins
port: 8282
---
apiVersion: mutations.gatekeeper.sh/v1
kind: Assign
metadata:
name: opa-policy
spec:
applyTo:
- groups: [""]
kinds: ["Pod"]
versions: ["v1"]
match:
scope: Namespaced
kinds:
- apiGroups: ["*"]
kinds: ["Pod"]
namespaceSelector:
matchLabels:
opa-istio-injection: enabled
location: "spec.volumes[name:opa-policy]"
parameters:
pathTests:
- subPath: "spec.volumes[name:opa-policy]"
condition: MustNotExist
assign:
value:
name: opa-policy
configMap:
name: opa-policy
---
94 changes: 94 additions & 0 deletions modules/08-external-authorization/policy.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package istio.authz

import future.keywords

import input.attributes.request.http as http_request

default allow := false

allow if {
some unprotected_operation in unprotected_operations
unprotected_operation.host = http_destination[0]
unprotected_operation.port = http_destination[1]
unprotected_operation.method = http_request.method
regex.match(unprotected_operation.path, http_request.path)
}

allow if {
some r in roles_for_user
required_roles[r]
}

roles_for_user contains r if {
r := user_roles[user_name][_]
}

required_roles contains r if {
perm := role_perms[r][_]
perm.host = http_destination[0]
perm.port = http_destination[1]
perm.method = http_request.method
perm.path = http_request.path
}

http_destination := split(http_request.host, ":")

user_name := parsed if {
[_, encoded] := split(http_request.headers.authorization, " ")
[parsed, _] := split(base64url.decode(encoded), ":")
}

user_roles := {
"alice": ["guest"],
"bob": ["admin"],
}

role_perms := {
"guest": [{
"host": "frontend.workshop.svc.cluster.local",
"port": "9000",
"method": "GET",
"path": "/",
}],
"admin": [
{
"host": "frontend.workshop.svc.cluster.local",
"port": "9000",
"method": "GET",
"path": "/",
},
{
"host": "frontend.workshop.svc.cluster.local",
"port": "9000",
"method": "POST",
"path": "/products",
},
],
}

unprotected_operations := [
{
"host": "productcatalog.workshop.svc.cluster.local",
"port": "5000",
"method": "GET",
"path": "^/products/$",
},
{
"host": "productcatalog.workshop.svc.cluster.local",
"port": "5000",
"method": "GET",
"path": "^/products/\\d+$",
},
{
"host": "productcatalog.workshop.svc.cluster.local",
"port": "5000",
"method": "POST",
"path": "^/products/\\d+$",
},
{
"host": "catalogdetail.workshop.svc.cluster.local",
"port": "3000",
"method": "GET",
"path": "^/catalogDetail$",
}
]
171 changes: 171 additions & 0 deletions modules/08-external-authorization/policy_test.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package istio.authz

import future.keywords

test_productcatalog_get_products_all_allowed if {
request := {
"attributes": {"request": {"http": {
"host": "productcatalog.workshop.svc.cluster.local:5000",
"method": "GET",
"path": "/products/",
}}},
"parsed_path": ["products"],
}

allow with input as request
}

test_productcatalog_get_products_id_all_allowed if {
request := {
"attributes": {"request": {"http": {
"host": "productcatalog.workshop.svc.cluster.local:5000",
"method": "GET",
"path": "/products/1",
}}},
"parsed_path": [
"products",
"1",
],
}

allow with input as request
}

test_productcatalog_post_products_id_all_allowed if {
request := {
"attributes": {"request": {"http": {
"host": "productcatalog.workshop.svc.cluster.local:5000",
"method": "POST",
"path": "/products/1",
}}},
"parsed_path": [
"products",
"1",
],
}

allow with input as request
}

test_catalogdetail_get_catalogdetail_all_allowed if {
request := {
"attributes": {"request": {"http": {
"host": "catalogdetail.workshop.svc.cluster.local:3000",
"method": "GET",
"path": "/catalogDetail",
}}},
"parsed_path": ["catalogDetail"],
}

allow with input as request
}

test_frontend_get_root_missing_auth_denied if {
request := {
"attributes": {"request": {"http": {
"host": "frontend.workshop.svc.cluster.local:9000",
"method": "GET",
"path": "/",
}}},
"parsed_path": [""],
}

not allow with input as request
}

test_frontend_get_root_no_role_denied if {
request := {
"attributes": {"request": {"http": {
"host": "frontend.workshop.svc.cluster.local:9000",
"method": "GET",
"path": "/",
"headers": {"authorization": "Basic Y2hhcmxpZTpwYXNzd29yZAo="},
}}},
"parsed_path": [""],
}

not allow with input as request
}

test_frontend_get_root_guest_allowed if {
request := {
"attributes": {"request": {"http": {
"host": "frontend.workshop.svc.cluster.local:9000",
"method": "GET",
"path": "/",
"headers": {"authorization": "Basic YWxpY2U6cGFzc3dvcmQK"},
}}},
"parsed_path": [""],
}

allow with input as request
}

test_frontend_get_root_admin_allowed if {
request := {
"attributes": {"request": {"http": {
"host": "frontend.workshop.svc.cluster.local:9000",
"method": "GET",
"path": "/",
"headers": {"authorization": "Basic Ym9iOnBhc3N3b3JkCg=="},
}}},
"parsed_path": [""],
}

allow with input as request
}

test_frontend_post_products_missing_auth_denied if {
request := {
"attributes": {"request": {"http": {
"host": "frontend.workshop.svc.cluster.local:9000",
"method": "POST",
"path": "/products",
}}},
"parsed_path": ["products"],
}

not allow with input as request
}

test_frontend_post_products_no_role_denied if {
request := {
"attributes": {"request": {"http": {
"host": "frontend.workshop.svc.cluster.local:9000",
"method": "POST",
"path": "/products",
"headers": {"authorization": "Basic Y2hhcmxpZTpwYXNzd29yZAo="},
}}},
"parsed_path": ["products"],
}

not allow with input as request
}

test_frontend_post_products_guest_denied if {
request := {
"attributes": {"request": {"http": {
"host": "frontend.workshop.svc.cluster.local:9000",
"method": "POST",
"path": "/products",
"headers": {"authorization": "Basic YWxpY2U6cGFzc3dvcmQK"},
}}},
"parsed_path": ["products"],
}

not allow with input as request
}

test_frontend_post_products_admin_allowed if {
request := {
"attributes": {"request": {"http": {
"host": "frontend.workshop.svc.cluster.local:9000",
"method": "POST",
"path": "/products",
"headers": {"authorization": "Basic Ym9iOnBhc3N3b3JkCg=="},
}}},
"parsed_path": ["products"],
}

allow with input as request
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: productapp
namespace: workshop
spec:
action: CUSTOM
provider:
name: opa-ext-authz-grpc
rules:
- to:
- operation:
paths: ["*"]
---