-
Notifications
You must be signed in to change notification settings - Fork 95
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
base: main
Are you sure you want to change the base?
Changes from all commits
87a55ab
d7ae612
ec3e8f3
5551104
b2b1fa4
36c2ac7
7da7b26
3de6baa
fa48314
d0882e0
6730b53
abab3f8
cdea3af
28b5e6c
c4ec8b2
d574ae7
3093514
fd17ebe
3cac328
fcb6b8d
1432b2d
b843eec
af83ee9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 |
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 | ||||||
|
||||||
* An up to date gcloud SDK installed | ||||||
* [opa](https://www.openpolicyagent.org/docs/latest/) installed | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Tweaking url to point directly to the cli page. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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`. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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> . | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mind replacing this with the Cloud Build equivalent? |
||||||
|
||||||
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 | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||||||
``` |
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; | ||
} | ||
|
||
} |
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 |
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" |
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.