Skip to content
Draft
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
41 changes: 40 additions & 1 deletion .github/workflows/pull-request.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
name: PR Check
on: [pull_request]

# When a new revision is pushed to a PR, cancel all in-progress CI runs for that
# PR. See https://docs.github.com/en/actions/using-jobs/using-concurrency
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
go-lint:
name: Go Lint
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- name: Check out code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
Expand All @@ -17,3 +23,36 @@ jobs:
make generate
git diff --exit-code

examples:
name: Check examples
runs-on: ubuntu-24.04
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Check out code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- id: go_version
name: Read go version
run: echo "go_version=$(cat .go-version)" >> $GITHUB_OUTPUT
- uses: actions/setup-go@44694675825211faa026b3c33043df3e48a5fa00 # v6.0.0
with:
go-version: ${{ steps.go_version.outputs.go_version }}
cache-dependency-path: go.sum
- run: make build-linux
- run: make image-quick
env:
VERSION: dev # predictable image tag below
- name: start docker compose example
working-directory: examples/docker
run: |
eval $(./init.sh) # tls
docker compose up --quiet-pull --wait --wait-timeout 300 --detach
env:
OCP_IMAGE: docker.io/openpolicyagent/opa-control-plane:dev
- name: smoke test curl
run: |
curl --retry 12 --retry-delay 10 --retry-all-errors --fail http://127.0.0.1:8282/v1/bundles -H "Authorization: bearer sesame"
- name: dump logs
run: docker compose logs
working-directory: examples/docker
if: failure()
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ GENERATE ?= 1
GOFLAGS ?= "-buildmode=exe"
GO := CGO_ENABLED=$(CGO_ENABLED) GOFLAGS="$(GOFLAGS)" go

VERSION := $(shell ./build/get-build-version.sh)
VERSION ?= $(shell ./build/get-build-version.sh)

DOCKER := docker

Expand Down
3 changes: 3 additions & 0 deletions config/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,9 @@
"ConfigSQLDatabase": {
"additionalProperties": false,
"properties": {
"credentials": {
"type": "string"
},
"driver": {
"type": "string"
},
Expand Down
17 changes: 17 additions & 0 deletions examples/docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,23 @@ The `docker-compose.yml` definition in this repository starts the following serv
3. Prometheus
4. Localstack (S3), with an init service that creates be bucket "bundles"

## TLS

The OCP instance uses mTLS for its database connections. The certificate of OCP is provided via environment variables, `OCP_TLS_CERT` and `OCP_TLS_KEY`.
For convenience, you can generate and export them via

```sh
eval $(./init.sh)
```

This will generate the certs needed for the examples (via `tls/gencerts.sh`), and set the env vars for OCP accordingly.

> [!NOTE]
> The TLS setup needs to be prepared **first**, before calling `docker compose up`.
>

## Metrics

When it's running, you can go to http://127.0.0.1:9090 to examine the published Prometheus metrics.
Enter `ocp_` in the expression field to see completion options for the various metrics in the expression field to see completion options for the various metrics in the expression field to see completion options for the various metrics.

Expand Down
21 changes: 15 additions & 6 deletions examples/docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ services:
- ./ocp.yml:/ocp.yml
ports:
- 8282:8282
environment:
- OCP_TLS_CERT
- OCP_TLS_KEY

localstack:
image: localstack/localstack:latest
Expand All @@ -33,15 +36,24 @@ services:
command: ["/init-s3.sh"]
depends_on:
- localstack
restart: "no"
healthcheck: # override healthcheck defined in image
test:
- CMD-SHELL
- exit 0

db:
image: postgres:latest
image: postgres:18-alpine
restart: always
environment:
POSTGRES_PASSWORD: sesame
ports:
- 5432:5432
volumes:
- pgdata:/var/lib/postgresql/data
- ./init-pg.sh:/init-pg.sh
- ./tls:/etc/ssl/postgresql/
- ./pg_hba.conf:/etc/postgresql/pg_hba.conf
entrypoint: "/init-pg.sh"
command: -c ssl=on -c ssl_cert_file=/etc/ssl/postgresql/server-cert.pem -c ssl_key_file=/etc/ssl/postgresql/server-key.pem -c hba_file=/etc/postgresql/pg_hba.conf

prometheus:
image: prom/prometheus:latest
Expand All @@ -66,6 +78,3 @@ services:
AWS_ACCESS_KEY_ID: doesntmatter
AWS_SECRET_ACCESS_KEY: doesntmatter
AWS_REGION: us-east-1

volumes:
pgdata:
21 changes: 21 additions & 0 deletions examples/docker/init-pg.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -Eeo pipefail


pUID=$(id -u postgres)
pGID=$(id -g postgres)

if [ -z "$pUID" ]
then
echo "Unable to find postgres user id, required in order to chown key material"
exit 1
fi

if [ -z "$pGID" ]
then
echo "Unable to find postgres group id, required in order to chown key material"
exit 1
fi

chown "$pUID":"$pGID" -R /etc/ssl/postgresql/
/usr/local/bin/docker-entrypoint.sh "$@"
1 change: 1 addition & 0 deletions examples/docker/init-s3.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ done
echo "LocalStack S3 is ready. Creating bucket..."
awslocal --endpoint-url=http://localstack:4566 s3 mb s3://bundles

sleep infinity
8 changes: 8 additions & 0 deletions examples/docker/init.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash
(
pushd tls
./gencerts.sh
popd
) >/dev/null 2>&1
echo "export OCP_TLS_CERT=\$(cat tls/client-cert.pem)"
echo "export OCP_TLS_KEY=\$(cat tls/client-key.pem)"
15 changes: 10 additions & 5 deletions examples/docker/ocp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@ bundles:
credentials: s3-creds
requirements:
- source: git-policies
secrets:
s3-creds:
type: aws_auth
access_key_id: doesnotmatter
secret_access_key: doesnotmatter
sources:
git-policies:
git:
Expand All @@ -30,3 +25,13 @@ database:
sql:
driver: postgres
dsn: postgresql://postgres:sesame@db:5432
credentials: pg-tls
secrets:
s3-creds:
type: aws_auth
access_key_id: doesnotmatter
secret_access_key: doesnotmatter
pg-tls:
type: tls_cert
tls_cert: ${OCP_TLS_CERT}
tls_key: ${OCP_TLS_KEY}
3 changes: 3 additions & 0 deletions examples/docker/pg_hba.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
hostnossl all all all reject
hostssl all all all scram-sha-256
local postgres postgres trust
3 changes: 3 additions & 0 deletions examples/docker/tls/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*.srl
*.cnf
*.pem
31 changes: 31 additions & 0 deletions examples/docker/tls/gencerts.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/bash
# taken from
# https://github.com/dexidp/dex/blob/2d1ac74ec0ca12ae4d36072525d976c1a596820a/examples/k8s/gencert.sh#L22

cat <<EOF >req.cnf
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name

[req_distinguished_name]

[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = opa.example.com
IP.1 = 127.0.0.1
EOF

openssl genrsa -out ca-key.pem 2048
openssl req -x509 -new -nodes -key ca-key.pem -days 3650 -out ca.pem -subj "/CN=my-ca"

openssl genrsa -out client-key.pem 2048
openssl req -new -key client-key.pem -out csr.pem -subj "/CN=my-client"
openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem -days 3650

openssl genrsa -out server-key.pem 2048
openssl req -new -key server-key.pem -out csr.pem -subj "/CN=my-server" -config req.cnf
openssl x509 -req -in csr.pem -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 3650 -extensions v3_req -extfile req.cnf
14 changes: 14 additions & 0 deletions examples/docker/tls/req.cnf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name

[req_distinguished_name]

[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = opa.example.com
IP.1 = 127.0.0.1
24 changes: 12 additions & 12 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,13 @@ func (*Root) unmarshal(raw *Root) error {
raw.Stacks[name].Name = name
}

if raw.Database != nil && raw.Database.AWSRDS != nil && raw.Database.AWSRDS.Credentials != nil {
raw.Database.AWSRDS.Credentials.value = raw.Secrets[raw.Database.AWSRDS.Credentials.Name]
if raw.Database != nil {
if raw.Database.AWSRDS != nil && raw.Database.AWSRDS.Credentials != nil {
raw.Database.AWSRDS.Credentials.value = raw.Secrets[raw.Database.AWSRDS.Credentials.Name]
}
if raw.Database.SQL != nil && raw.Database.SQL.Credentials != nil {
raw.Database.SQL.Credentials.value = raw.Secrets[raw.Database.SQL.Credentials.Name]
}
}

return nil
Expand Down Expand Up @@ -791,14 +796,8 @@ func scopesEqual(a, b []Scope) bool {
if len(a) != len(b) {
return false
}
for i := range a {
var found bool
for j := range b {
if a[i].Equal(b[j]) {
found = true
}
}
if !found {
for i := range b {
if !slices.ContainsFunc(a, func(x Scope) bool { return x.Equal(b[i]) }) {
return false
}
}
Expand Down Expand Up @@ -1033,8 +1032,9 @@ type Database struct {
}

type SQLDatabase struct {
Driver string `json:"driver"`
DSN string `json:"dsn"`
Driver string `json:"driver"`
DSN string `json:"dsn"`
Credentials *SecretRef `json:"credentials,omitempty"`

_ struct{} `additionalProperties:"false"`
}
Expand Down
4 changes: 1 addition & 3 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package config_test

import (
"context"
"reflect"
"strings"
"testing"
Expand Down Expand Up @@ -31,14 +30,13 @@ func TestParseSecretResolve(t *testing.T) {
}
}
}`))

if err != nil {
t.Fatal(err)
}

t.Setenv("OPACTL_PASSWORD", "passw0rd")

value, err := result.Sources["foo"].Git.Credentials.Resolve(context.Background())
value, err := result.Sources["foo"].Git.Credentials.Resolve(t.Context())
if err != nil {
t.Fatal(err)
}
Expand Down
45 changes: 45 additions & 0 deletions internal/config/secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package config

import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"errors"
"fmt"
Expand Down Expand Up @@ -207,6 +209,14 @@ func (s *Secret) Typed(context.Context) (any, error) {

return value, nil

case "tls_cert":
var value SecretTLSCert
if err := decode(m, &value); err != nil {
return nil, err
}

return value, nil

default:
return nil, fmt.Errorf("unknown secret type %q", s.Value["type"])
}
Expand Down Expand Up @@ -254,6 +264,41 @@ type SecretPassword struct {
Password string `json:"password"` // Password for authentication.
}

type SecretTLSCert struct {
RootCA string `json:"root_ca"` // Root CA certificate (PEM encoded)
TLSCert string `json:"tls_cert"` // TLS certificate (PEM encoded)
TLSKey string `json:"tls_key"` // TLS key (PEM encoded)
}

func (value *SecretTLSCert) ToConfig(ctx context.Context) (*tls.Config, error) {
tlsCfg := &tls.Config{}

// Root CA
if value.RootCA != "" {
rootCAs := x509.NewCertPool()
ok := rootCAs.AppendCertsFromPEM([]byte(value.RootCA))
if !ok {
return nil, errors.New("failed to append root CA certificate")
}
tlsCfg.RootCAs = rootCAs
}

// Client Certificate and Key
if value.TLSCert != "" && value.TLSKey != "" {
cert, err := tls.X509KeyPair([]byte(value.TLSCert), []byte(value.TLSKey))
if err != nil {
return nil, err
}

tlsCfg.Certificates = []tls.Certificate{cert}
}

// Enforce TLS 1.2 or higher
tlsCfg.MinVersion = tls.VersionTLS12

return tlsCfg, nil
}

// we use this one so we don't need duplicate tags on every struct
func decode(input any, output any) error {
config := &mapstructure.DecoderConfig{
Expand Down
Loading
Loading