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

Open Policy Agent Example #207

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
87a55ab
initial add of the OPA on cloud run sample
aelawrence Aug 10, 2023
d7ae612
chore(deps): update golang docker tag to v1.21 (#203)
renovate-bot Aug 10, 2023
ec3e8f3
update readme to include links
aelawrence Aug 11, 2023
5551104
updating yaml filename
aelawrence Aug 28, 2023
b2b1fa4
shorten service name
aelawrence Aug 28, 2023
36c2ac7
build the container before trying to deploy :)
aelawrence Aug 28, 2023
7da7b26
update dockerfile path
aelawrence Aug 28, 2023
3de6baa
attempt to create and build opa bundle
aelawrence Aug 29, 2023
fa48314
use curl
aelawrence Aug 29, 2023
d0882e0
add launch stage annotation
aelawrence Aug 29, 2023
6730b53
now building/pushing docker images
aelawrence Aug 29, 2023
abab3f8
expanding tests
aelawrence Aug 30, 2023
cdea3af
allow allUsers; fix tests
aelawrence Aug 30, 2023
28b5e6c
delete docker image when finished
aelawrence Aug 30, 2023
c4ec8b2
Merge branch 'GoogleCloudPlatform:main' into feature/opa-cloudrun-exa…
lawrenae Aug 30, 2023
d574ae7
Merge branch 'main' into feature/opa-cloudrun-example
lawrenae Sep 6, 2023
3093514
Merge branch 'main' into feature/opa-cloudrun-example
pattishin Sep 21, 2023
fd17ebe
Merge branch 'main' into feature/opa-cloudrun-example
pattishin Oct 20, 2023
3cac328
Merge branch 'main' into feature/opa-cloudrun-example
glasnt Nov 2, 2023
fcb6b8d
Merge branch 'main' into feature/opa-cloudrun-example
glasnt Nov 9, 2023
1432b2d
Merge branch 'main' into feature/opa-cloudrun-example
glasnt Feb 2, 2024
b843eec
Merge branch 'main' into feature/opa-cloudrun-example
pattishin Feb 22, 2024
af83ee9
Merge branch 'main' into feature/opa-cloudrun-example
glasnt Jun 19, 2024
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ For all Cloud Run code samples, see the [Cloud Run sample browser](https://cloud
| gcloud as a service | Use `gcloud` and `gsutil` in a service | [Go][gcloud_report] |
| VPC Testing | Egress and ingress settings with VPC | [Python][vpc_sample] |
| Multi-container Hello Nginx| Multi-containers with nginx | [YAML][multicontainer_hello_nginx_sample] |
| Open Policy Agent | Open Policy Agent Multi-container | [YAML][multicontainer_open_policy_agent_sample] |
| Markdown Preview | 2 tier secure microservices for Markdown rendering | [Go][markdown_preview_go], [Nodejs][markdown_preview_nodejs], [Python][markdown_preview_python], [Java][markdown_preview_java] |

[job_go]: https://github.com/GoogleCloudPlatform/golang-samples/tree/main/run/jobs
Expand Down Expand Up @@ -87,6 +88,7 @@ For all Cloud Run code samples, see the [Cloud Run sample browser](https://cloud
[vpc_sample]: vpc-sample
[gcloud_report]: gcloud-report
[multicontainer_hello_nginx_sample]: multi-container/hello-nginx-sample
[multicontainer_open_policy_agent_sample]: multi-container/open-policy-agent-sample
[idtoken_request_go]: https://github.com/GoogleCloudPlatform/golang-samples/blob/master/functions/security/idtoken.go
[idtoken_request_nodejs]: https://github.com/googleapis/google-auth-library-nodejs/blob/master/samples/idtokens-serverless.js
[idtoken_request_python]: https://github.com/GoogleCloudPlatform/python-docs-samples/blob/main/auth/service-to-service/auth.py
Expand Down
5 changes: 5 additions & 0 deletions multi-container/open-policy-agent-sample/Dockerfile.nginx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM nginx:1.20.0-alpine

RUN mkdir /etc/nginx/templates && mkdir /usr/share/nginx/html/bundles
COPY nginx.conf.template /etc/nginx/templates
COPY bundle.tar.gz /usr/share/nginx/html/bundles
50 changes: 50 additions & 0 deletions multi-container/open-policy-agent-sample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Running OPA as a side car in Cloud Run

In this tutorial, we will use the [same example](http://openpolicyagent.org/docs/latest/http-api-authorization/) that is documented in the official OPA website, but substitute using [Cloud Run side cars](https://cloud.google.com/run/docs/deploying#sidecars) instead of Docker Compose.

We'll end up with a deployment like this:

![deployment diagram](cloud-run-opa-deployment-diagram.png)

The goal is to use a simple HTTP web server that accepts any HTTP GET request that you issue and echoes the OPA decision back as text. OPA will fetch policy bundles from a simple bundle server. OPA, the bundle server, and the web server will be run as containers in Cloud Run. In a production environment, you likely will have a different bundle server, but it's convenient to package it together here.

# Prerequsites
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# Prerequsites
# Prerequisites


* An up to date gcloud SDK installed
* [opa](https://www.openpolicyagent.org/docs/latest/) installed
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* [opa](https://www.openpolicyagent.org/docs/latest/) installed
* [opa](https://www.openpolicyagent.org/docs/latest/cli/) installed

Tweaking url to point directly to the cli page.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on adding:

* A text editor
* a Google Cloud project with the Cloud Run API enabled

# 1. Create and push a custom bundle server image

* Follow [Step 1](https://www.openpolicyagent.org/docs/latest/http-api-authorization/#1-create-a-policy-bundle) in the original tutorial, resulting in creating two files called `example.rego` and `bundle.tar.gz`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on itemizing these step by step here? It'll make the README easier to follow and the steps on the site could change later down the line without us realizing.

* Create a custom bundle server with [Dockerfile.nginx](./Dockerfile.nginx) and [nginx.conf.template](./nginx.conf.template) with the following command:

```bash
docker build -f Dockerfile.nginx -t us-central1-docker.pkg.dev/<CLOUD_PROJECT>/docker/<IMAGE_NAME> .
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mind replacing this with the Cloud Build equivalent? Dockerfile can still be leveraged.


docker push us-central1-docker.pkg.dev/<CLOUD_PROJECT>/docker/<IMAGE_NAME>
```

Note: We assume Google Cloud Artifact Registry in this tutorial but any registry accessible to Cloud Run will work
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, do you mind moving this up the prereqs or in a section of required gcp apis like the sister sample https://github.com/GoogleCloudPlatform/cloud-run-samples/tree/main/multi-container/hello-nginx-sample#enable-required-apis?


# 2. Update opa-service.yml

Read through [Step 2](https://www.openpolicyagent.org/docs/latest/http-api-authorization/#2-bootstrap-the-tutorial-environment-using-docker-compose) in the original tutorial, but instead of creating a `docker-compose.yml` file and using `docker-compose` to create the service, we're instead going to use `opa-service.yaml` [reference docs](https://cloud.google.com/run/docs/reference/yaml/v1) to deploy three containers to a single cloud run deployment.

* Update the bundle server image path ([line 39](./opa-service.yml#39)) to the path of your custom image above.
* use gcloud to deploy:

```bash
gcloud beta run services replace opa-service.yaml
```

# 3. Verify

Follow [steps 3-5](https://www.openpolicyagent.org/docs/latest/http-api-authorization/#3-check-that-alice-can-see-her-own-salary) in the original tutorial to verify the policies with one small change:

any time you see `localhost:5000` replace it with the Cloud Run URL. For e.g.

```bash
curl --user alice:password <CLOUD_RUN_URL>/finance/salary/alice
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions multi-container/open-policy-agent-sample/nginx.conf.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
server {
listen ${NGINX_PORT};
server_name _;

location / {
root /usr/share/nginx/html;
index index.html index.htm;
}

error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}

}
49 changes: 49 additions & 0 deletions multi-container/open-policy-agent-sample/opa-service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
annotations:
run.googleapis.com/launch-stage: BETA
name: "SERVICE_NAME"
labels:
cloud.googleapis.com/location: "REGION"
spec:
template:
metadata:
annotations:
run.googleapis.com/container-dependencies: '{"opa":["bundle-server"], "api-server":["opa"] }'
spec:
containers:
- image: "openpolicyagent/demo-restful-api:0.3"
name: api-server
env:
- name: "OPA_ADDR"
value: "http://localhost:8181" #opa agent
- name: "POLICY_PATH"
value: "/v1/data/httpapi/authz"
startupProbe:
tcpSocket:
port: 5000
ports:
- containerPort: 5000
- image: "openpolicyagent/opa:0.55.0"
name: opa
command:
- "opa"
- "run"
- "--server"
- "--log-format=json-pretty"
- "--set=decision_logs.console=true"
- "--set=services.nginx.url=http://localhost:8888" #bundle_server
- "--set=bundles.nginx.service=nginx"
- "--set=bundles.nginx.resource=bundles/bundle.tar.gz"
startupProbe:
tcpSocket:
port: 8181
- image: "us-central1-docker.pkg.dev/<PROJECT_ID>/docker/<IMAGE_NAME>"
name: bundle-server
env:
- name: "NGINX_PORT"
value: "8888"
startupProbe:
tcpSocket:
port: 8888
93 changes: 93 additions & 0 deletions multi-container/open-policy-agent-sample/tests.cloudbuild.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
steps:
- id: "create-example-opa-bundle"
waitFor: ["-"]
name: 'gcr.io/gcp-runtimes/ubuntu_18_0_4'
dir: "${_SAMPLE_DIR}"
script: |
#!/bin/bash
cat <<EOF >> example.rego
package httpapi.authz

# bob is alice's manager, and betty is charlie's.
subordinates := {"alice": [], "charlie": [], "bob": ["alice"], "betty": ["charlie"]}

default allow := false

# Allow users to get their own salaries.
allow {
input.method == "GET"
input.path == ["finance", "salary", input.user]
}

# Allow managers to get their subordinates' salaries.
allow {
some username
input.method == "GET"
input.path = ["finance", "salary", username]
subordinates[input.user][_] == username
}
EOF
cat example.rego

#get opa binary and build
curl -L -o opa https://openpolicyagent.org/downloads/v0.55.0/opa_linux_amd64_static
chmod a+x ./opa
./opa build example.rego -o bundle.tar.gz

- id: "build-bundle-server"
waitFor: ["create-example-opa-bundle"]
name: 'gcr.io/cloud-builders/docker'
dir: "${_SAMPLE_DIR}"
args: ['build', '-f', 'Dockerfile.nginx', '-t', 'us-central1-docker.pkg.dev/$PROJECT_ID/docker/opa-sample-test', '.']

- id: "push-bundle-server"
waitFor: ["build-bundle-server"]
name: 'gcr.io/cloud-builders/docker'
args: ['push', 'us-central1-docker.pkg.dev/$PROJECT_ID/docker/opa-sample-test']

- id: "e2e-test"
waitFor: ["push-bundle-server"]
name: "gcr.io/cloud-builders/gcloud"
dir: "${_SAMPLE_DIR}"
script: |
#!/bin/bash
echo "Run e2e-test ..."
./tests/e2e-test.sh || touch /workspace/e2e-failed
env:
- "BUILD_ID=$BUILD_ID"
- "_REGION=$_REGION"
- "_SERVICE_NAME=$_SERVICE_NAME"
- "PROJECT_ID=$PROJECT_ID"
- "IMAGE_NAME=opa-sample-test"

- id: "e2e-cleanup"
waitFor: ["e2e-test"]
name: "gcr.io/cloud-builders/gcloud"
dir: "${_SAMPLE_DIR}"
script: |
#!/bin/bash
export MC_SERVICE_NAME="${_SERVICE_NAME}-$BUILD_ID"
gcloud run services delete "${MC_SERVICE_NAME}" --quiet --region "${_REGION}"
echo gcloud artifacts docker images delete us-central1-docker.pkg.dev/${PROJECT_ID}/docker/opa-sample-test --quiet --project ${PROJECT_ID}
gcloud artifacts docker images delete us-central1-docker.pkg.dev/${PROJECT_ID}/docker/opa-sample-test --quiet --project ${PROJECT_ID}
env:
- "BUILD_ID=$BUILD_ID"
- "_REGION=$_REGION"
- "_SERVICE_NAME=$_SERVICE_NAME"
- "IMAGE_NAME=opa-bundle"
- "PROJECT_ID=$PROJECT_ID"

- id: "report-status"
waitFor: ["e2e-cleanup"]
name: "gcr.io/cloud-builders/gcloud"
script: |
#!/bin/bash
if [[ -f /workspace/e2e-failed ]]
then
echo "Step e2e-test failed"
exit 1
fi
substitutions:
_SERVICE_NAME: "opa-sample-testing"
_REGION: "us-central1"
_SAMPLE_DIR: "multi-container/open-policy-agent-sample"
62 changes: 62 additions & 0 deletions multi-container/open-policy-agent-sample/tests/e2e-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/bin/bash
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -eux pipefail

export SERVICE_NAME="${_SERVICE_NAME}-$BUILD_ID"

# Substituting the env vars in cloud run yaml file
sed -i -e s/SERVICE_NAME/${SERVICE_NAME}/g -e s/REGION/${_REGION}/g -e s/\<PROJECT_ID\>/${PROJECT_ID}/g -e s/\<IMAGE_NAME\>/${IMAGE_NAME}/g opa-service.yaml

# Note that nginx_config secret has already been created within project.
# Deploy multi-container service "nginx-example" that includes nginx proxy.
gcloud run services replace opa-service.yaml --region ${_REGION} --quiet

# Wait till deployment completes
sleep 10

# Retrieve multi-containter service url.
URL=$(gcloud run services describe ${SERVICE_NAME} --region ${_REGION} --format 'value(status.url)')

# Retrieve service deployment status.
STATUS=$(gcloud run services describe ${SERVICE_NAME} --region ${_REGION} --format 'value(status.conditions[0].type)')

if [[ -z "${URL}" && "${STATUS}" != "Ready" ]]
then
echo "No Cloud Run opa sample url found. Step e2e-test failed."
exit 1
fi

#allow all users
gcloud run services add-iam-policy-binding ${SERVICE_NAME} --member=allUsers --role roles/run.invoker --region ${_REGION}

# check that it's responding at all
RESULT=`curl ${URL}`

if [[ $RESULT != *"Error: user Anonymous is not authorized to GET url /"* ]]; then
echo "No Cloud Run opa sample found deployed. Step e2e-test failed."
exit 1
fi


RESULT=`curl --user alice:password ${URL}/finance/salary/alice`
echo $RESULT
if [[ $RESULT != *"Success: user alice is authorized"* ]]; then
echo "opa not functioning properly. Step e2e-test failed."
exit 1
fi

echo "Cloud Run opa sample successully deployed and nginx successfully proxied request."
exit 0