Skip to content

Commit

Permalink
feat: add devops support (bcgov#8)
Browse files Browse the repository at this point in the history
Signed-off-by: Jason C. Leach <[email protected]>
  • Loading branch information
jleach authored Nov 22, 2023
1 parent d2220be commit abda09b
Show file tree
Hide file tree
Showing 15 changed files with 468 additions and 7 deletions.
57 changes: 57 additions & 0 deletions .github/actions/docker-build/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Docker Build
description: Build Docker Image
inputs:
context:
description: Docker context path
required: true
default: dist
dockerfile:
description: Dockerfile path
required: true
registry:
description: Docker registry
required: true
image_name:
description: Docker image name
required: true
docker_user:
description: Docker user
required: true
docker_password:
description: Docker password
required: true
runs:
using: composite
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v2

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ${{ inputs.registry }}
username: ${{ inputs.docker_user }}
password: ${{ inputs.docker_password }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v3
with:
images: ${{ inputs.registry }}/${{ inputs.image_name }}
tags: |
type=sha,prefix=
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: ${{ inputs.context }}
file: ${{ inputs.dockerfile }}
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
51 changes: 51 additions & 0 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Test & Build
on:
workflow_dispatch:
push:
branches:
- main
paths:
- "src/**"

# defaults:
# run:
# working-directory: ./web
env:
REGISTRY: ghcr.io
jobs:
quality:
runs-on: ubuntu-latest
permissions:
packages: read
contents: read
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v4
with:
python-version: "3.11"

# - name: Run tests
# run: |
# pip install -r requirements.txt
# pytest

build:
# if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
needs: [quality]
steps:
- uses: actions/checkout@v4

- name: Build Docker image
uses: ./.github/actions/docker-build
with:
registry: ${{ env.REGISTRY }}
image_name: "${{ github.repository }}/controller"
context: ./
dockerfile: "Dockerfile"
docker_user: ${{ github.actor }}
docker_password: ${{ secrets.GITHUB_TOKEN }}
5 changes: 5 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"bracketSpacing": false,
"singleAttributePerLine": true,
"printWidth": 160
}
27 changes: 27 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
ARG python_version=3.9.18
FROM python:${python_version}-slim-bullseye AS build

COPY requirements.txt /tmp/requirements.txt

RUN apt-get update && \
apt-get install -y --no-install-recommends \
build-essential \
curl && \
rm -rf /var/lib/apt/lists/*

RUN pip --disable-pip-version-check \
--no-cache-dir install \
-r /tmp/requirements.txt && \
rm -rf /tmp/requirements.txt

FROM build

RUN curl -ILv https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 \
-o /usr/bin/jq && \
chmod +x /usr/bin/jq

WORKDIR /opt/controller

COPY src /opt/controller/

ENTRYPOINT ["gunicorn", "-b", "0.0.0.0:5000", "controller:server"]
30 changes: 28 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ When run, this program will act as a "controller" to an ACA-py agent. It uses Fl

## Running

### Local Development

First, create a `.env` file in the root of your folder by copying env.sample to `.env`, populate the values with your own. For Android Attestation you will need a Google OAuth JSON key in `/src` configured for your app.

```bash
Expand All @@ -49,7 +51,7 @@ python controller.py
The output should look something like this:

```bash
vscode ➜ /work (main) $ python src/controller.py
vscode ➜ /work (main) $ python src/controller.py
* Serving Flask app 'controller'
* Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
Expand All @@ -68,6 +70,30 @@ npx ngrok http 5000

Finally, whatever public endpoint is provided from, in this case `ngrok` needs to be provided to Traction as the controller endpoint. This can be done by going to Settings -> Tenant Profile and entering the URL in the `WebHook URL` field.

### OpenShift Cluster

The general command to deploy this to an OpenShift cluster is:

```bash
helm template <RELEASE> ./devops/charts/controller
--set-string wallet_id=<WALLET_ID> \
--set-string wallet_key=<WALLET_KEY> \
--set-file google_oauth_key.json=<PATH_TO_GOOGLE_OAUTH_KEY>| \
oc apply -n <NAMESPACE> -f -
```

And example command to deploy to the `e79518-dev` namespace is:

```bash
helm template bcwallet ./devops/charts/controller
--set-string wallet_id=123-456-789 \
--set-string wallet_key=abc-def-ghi \
--set-file google_oauth_key.json=./google_oauth_key.json| \
oc apply -n e79518-dev -f -
```

The release name can be anything you want, but it must be unique to the namespace. When deploying to a shared namespace like `e79518-dev`, it is recommended use the a meaningful release name that will help reason about what the controller is doing.

## Notes Below Here

https://developer.android.com/google/play/integrity/verdicts#device-integrity-field. You can then distinguish between MEETS_BASIC_INTEGRITY and MEETS_STRONG_INTEGRITY
Expand Down Expand Up @@ -136,4 +162,4 @@ After successfully completing these steps, you can trust the attestation object.
- [x] Verify the package info matches our app
- [x] Verify the device integrity fields
- [ ] Verify the app integrity fields (left commented out while in development)
- [ ] Verify the nonce in the verdict payload matches the nonce the controller sent to the device
- [ ] Verify the nonce in the verdict payload matches the nonce the controller sent to the device
24 changes: 24 additions & 0 deletions devops/charts/controller/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
apiVersion: v2
name: attestation-controller
description: A Helm chart for Kubernetes

# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application

# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.0.1

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "0.0.1"
62 changes: 62 additions & 0 deletions devops/charts/controller/templates/_helpers.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "attestation-controller.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "attestation-controller.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}

{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "attestation-controller.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}

{{/*
Common labels
*/}}
{{- define "attestation-controller.labels" -}}
helm.sh/chart: {{ include "attestation-controller.chart" . }}
{{ include "attestation-controller.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "attestation-controller.selectorLabels" -}}
app.kubernetes.io/name: {{ include "attestation-controller.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{/*
Create the name of the service account to use
*/}}
{{- define "attestation-controller.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "attestation-controller.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
67 changes: 67 additions & 0 deletions devops/charts/controller/templates/deployment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{include "attestation-controller.fullname" .}}
labels: {{- include "attestation-controller.labels" . | nindent 4}}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "attestation-controller.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "attestation-controller.labels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
volumes:
- name: google-oauth-key
secret:
secretName: google-oauth-key
imagePullSecrets:
{{- toYaml .Values.imagePullSecrets | nindent 8 }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
resources:
{{- toYaml .Values.resources | nindent 12 }}
ports:
- containerPort: 5000
name: http
volumeMounts:
- name: google-oauth-key
mountPath: {{.Values.env.GOOGLE_AUTH_JSON_PATH }}
subPath: {{.Values.env.GOOGLE_AUTH_JSON_PATH | base}}
# livenessProbe:
# httpGet:
# path: /api/v1/ehlo
# port: http
# initialDelaySeconds: 60
# periodSeconds: 3
# readinessProbe:
# httpGet:
# path: /api/v1/ehlo
# port: http
# initialDelaySeconds: 60
# timeoutSeconds: 3
envFrom:
- secretRef:
name: traction-creds
env:
- name: PORT
value: {{.Values.service.targetPort | quote}}
- name: TRACTION_BASE_URL
value: {{.Values.env.TRACTION_BASE_URL | quote}}
- name: APPLE_ATTESTATION_ROOT_CA_URL
value: {{.Values.env.APPLE_ATTESTATION_ROOT_CA_URL | quote}}
- name: GOOGLE_AUTH_JSON_PATH
value: {{.Values.env.GOOGLE_AUTH_JSON_PATH | quote}}
17 changes: 17 additions & 0 deletions devops/charts/controller/templates/netpol.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: {{include "attestation-controller.fullname" .}}
labels: {{- include "attestation-controller.labels" . | nindent 4}}
annotations: {{- toYaml .Values.route.annotations | nindent 4}}
spec:
podSelector:
matchLabels: {{- include "attestation-controller.selectorLabels" . | nindent 6}}
ingress:
- from:
- namespaceSelector:
matchLabels:
network.openshift.io/policy-group: ingress
policyTypes:
- Ingress
20 changes: 20 additions & 0 deletions devops/charts/controller/templates/routes.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
kind: Route
apiVersion: route.openshift.io/v1
metadata:
name: {{include "attestation-controller.fullname" .}}
labels: {{- include "attestation-controller.labels" . | nindent 4}}
annotations: {{- toYaml .Values.route.annotations | nindent 4}}
spec:
{{- if .Values.route.host }}
host: {{ .Values.route.host }}
{{- end }}
to:
kind: Service
name: {{include "attestation-controller.fullname" .}}
weight: 100
port:
targetPort: {{.Values.service.targetPort}}
tls:
termination: edge
insecureEdgeTerminationPolicy: Redirect
wildcardPolicy: None
Loading

0 comments on commit abda09b

Please sign in to comment.