Skip to content

Commit

Permalink
enhancement!: Make config flag optional (#1462)
Browse files Browse the repository at this point in the history
Make the `--config` flag optional for launching Cerbos. If it's not
specified and `CERBOS_CONFIG` environment variable is not set as well,
a default configuration is applied which sets the policy directory to
`${PWD}/policies`.

This is a breaking change for users who are using a container
orchestrator that uses Docker healthchecks (e.g. ECS) AND has mounted a
custom configuration file at `/conf.default.yaml` because the
container's `CERBOS_CONFIG` environment variable no longer points to it.
They should manually set the `CERBOS_CONFIG` environment variable as a
workaround. Unfortunately it's difficult to keep the old behaviour
without either duplicating the default configuration in two places or
breaking the `healthcheck` command.

Includes a couple of changes to fix deprecated settings in GoReleaser as
well.

Signed-off-by: Charith Ellawala <charith@cerbos.dev>
  • Loading branch information
charithe committed Feb 17, 2023
1 parent 138a1cd commit 1a949a1
Show file tree
Hide file tree
Showing 12 changed files with 92 additions and 43 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
uses: goreleaser/goreleaser-action@v3
with:
version: latest
args: release --config=.goreleaser.yml --rm-dist
args: release --config=.goreleaser.yml --clean
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HOMEBREW_GITHUB_TOKEN: ${{ secrets.HOMEBREW_GITHUB_TOKEN }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/snapshot.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ jobs:
uses: goreleaser/goreleaser-action@v3
with:
version: latest
args: release --config=.goreleaser.yml --rm-dist --snapshot --skip-publish
args: release --config=.goreleaser.yml --clean --snapshot --skip-publish
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TELEMETRY_WRITE_KEY: ${{ secrets.TELEMETRY_WRITE_KEY }}
Expand Down
27 changes: 8 additions & 19 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ builds:
- amd64
- arm64
goarm:
- 6
- 7
- "6"
- "7"
mod_timestamp: '{{ .CommitTimestamp }}'
flags:
- -trimpath
Expand All @@ -35,8 +35,8 @@ builds:
- amd64
- arm64
goarm:
- 6
- 7
- "6"
- "7"
mod_timestamp: '{{ .CommitTimestamp }}'
flags:
- -trimpath
Expand All @@ -54,18 +54,11 @@ archives:
builds:
- cerbos
- cerbosctl
replacements:
darwin: Darwin
linux: Linux
amd64: x86_64
name_template: 'cerbos_{{ .Version }}_{{ title .Os }}_{{ if eq .Arch "amd64" }}x86_64{{ else }}{{ .Arch }}{{ end }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
- id: cerbosctl
builds:
- cerbosctl
name_template: "cerbosctl_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}"
replacements:
darwin: Darwin
linux: Linux
amd64: x86_64
name_template: 'cerbosctl_{{ .Version }}_{{ title .Os }}_{{ if eq .Arch "amd64" }}x86_64{{ else }}{{ .Arch }}{{ end }}{{ if .Arm }}v{{ .Arm }}{{ end }}'
nfpms:
- id: cerbos
package_name: cerbos
Expand Down Expand Up @@ -101,8 +94,6 @@ dockers:
- cerbos
goarch: amd64
use: buildx
extra_files:
- conf.default.yaml
build_flag_templates:
- "--pull"
- "--label=org.opencontainers.image.created={{.Date}}"
Expand All @@ -122,8 +113,6 @@ dockers:
- cerbos
goarch: arm64
use: buildx
extra_files:
- conf.default.yaml
build_flag_templates:
- "--pull"
- "--label=org.opencontainers.image.created={{.Date}}"
Expand Down Expand Up @@ -220,8 +209,8 @@ release:
header: |-
Cerbos {{ .Version }}
---------------------
View the full release notes at https://docs.cerbos.dev/cerbos/latest/releases/v{{ .Version }}.html
View the full release notes at https://docs.cerbos.dev/cerbos/latest/releases/v{{ .Version }}.html
changelog:
sort: asc
Expand Down
3 changes: 1 addition & 2 deletions Dockerfile.cerbos
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ RUN apk add -U --no-cache ca-certificates && update-ca-certificates

FROM scratch
EXPOSE 3592 3593
ENV CERBOS_CONFIG="/conf.default.yaml"
ENV CERBOS_CONFIG="__default__"
VOLUME ["/policies"]
ENTRYPOINT ["/cerbos"]
CMD ["server"]
HEALTHCHECK --interval=1m --timeout=3s CMD ["/cerbos", "healthcheck"]
COPY --from=base /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY cerbos /cerbos
COPY conf.default.yaml /conf.default.yaml

2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ build: generate lint test package

.PHONY: package
package: $(GORELEASER)
@ TELEMETRY_WRITE_KEY=$(TELEMETRY_WRITE_KEY) TELEMETRY_URL=$(TELEMETRY_URL) $(GORELEASER) release --config=.goreleaser.yml --snapshot --skip-publish --rm-dist
@ TELEMETRY_WRITE_KEY=$(TELEMETRY_WRITE_KEY) TELEMETRY_URL=$(TELEMETRY_URL) $(GORELEASER) release --config=.goreleaser.yml --snapshot --skip-publish --clean

.PHONY: docs
docs: confdocs
Expand Down
6 changes: 3 additions & 3 deletions cmd/cerbos/healthcheck/healthcheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ const (

help = `
Performs a healthcheck on a Cerbos PDP. This can be used as a Docker HEALTHCHECK command.
When the path to the Cerbos config file is provided via the '--config' flag or the CERBOS_CONFIG environment variable, the healthcheck will be automatically configured based on the settings from the file.
By default, the gRPC endpoint will be checked using the gRPC healthcheck protocol. This is usually sufficient for most cases as the Cerbos REST API is built on top of the gRPC API as well.
When the path to the Cerbos config file is provided via the '--config' flag or the CERBOS_CONFIG environment variable, the healthcheck will be automatically configured based on the settings from the file.
By default, the gRPC endpoint will be checked using the gRPC healthcheck protocol. This is usually sufficient for most cases as the Cerbos REST API is built on top of the gRPC API as well.
Examples:
Expand All @@ -54,7 +54,7 @@ cerbos healthcheck --kind=http --host-port=10.0.1.5:3592 --no-tls
)

type Cmd struct {
Config string `help:"Cerbos config file" type:"existingfile" group:"config" xor:"hostport,cacert,notls" env:"CERBOS_CONFIG"`
Config string `help:"Cerbos config file" group:"config" xor:"hostport,cacert,notls" env:"CERBOS_CONFIG"`
Kind string `help:"Healthcheck kind (${enum})" default:"grpc" enum:"grpc,http" env:"CERBOS_HC_KIND"`
HostPort string `help:"Host and port to connect to" group:"manual" xor:"hostport" env:"CERBOS_HC_HOSTPORT"`
CACert string `help:"Path to CA cert for validating server cert" type:"existingfile" group:"manual" xor:"cacert" env:"CERBOS_HC_CACERT"`
Expand Down
21 changes: 15 additions & 6 deletions cmd/cerbos/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ import (
const help = `
Examples:
# Start the server
# Start the server
cerbos server --config=/path/to/config.yaml
cerbos server
# Start the server with the Admin API enabled and the 'sqlite' storage driver
cerbos server --config=/path/to/config.yaml --set=server.adminAPI.enabled=true --set=storage.driver=sqlite3 --set=storage.sqlite3.dsn=':memory:'`
cerbos server --set=server.adminAPI.enabled=true --set=storage.driver=sqlite3 --set=storage.sqlite3.dsn=':memory:'`

type LogLevelFlag string

Expand All @@ -49,7 +49,7 @@ func (ll *LogLevelFlag) Decode(ctx *kong.DecodeContext) error {
type Cmd struct {
DebugListenAddr string `help:"Address to start the gops listener" placeholder:":6666"`
LogLevel LogLevelFlag `help:"Log level (${enum})" default:"info" enum:"debug,info,warn,error"`
Config string `help:"Path to config file" type:"existingfile" required:"" placeholder:"./config.yaml" env:"CERBOS_CONFIG"`
Config string `help:"Path to config file" type:"existingfile" optional:"" placeholder:"./config.yaml" env:"CERBOS_CONFIG"`
Set []string `help:"Config overrides" placeholder:"server.adminAPI.enabled=true"`
ZPagesEnabled bool `help:"Enable zpages" hidden:""`
}
Expand Down Expand Up @@ -84,7 +84,11 @@ func (c *Cmd) Run() error {
}

// load configuration
log.Infof("Loading configuration from %s", c.Config)
if c.Config == "" {
log.Info("Loading default configuration")
} else {
log.Infof("Loading configuration from %s", c.Config)
}
if err := config.Load(c.Config, confOverrides); err != nil {
log.Errorw("Failed to load configuration", "error", err)
return err
Expand All @@ -95,7 +99,12 @@ func (c *Cmd) Run() error {
return err
}

return server.Start(ctx, c.ZPagesEnabled)
if err := server.Start(ctx, c.ZPagesEnabled); err != nil {
log.Errorw("Failed to start server", "error", err)
return err
}

return nil
}

func (c *Cmd) Help() string {
Expand Down
20 changes: 11 additions & 9 deletions docs/modules/configuration/pages/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,28 @@ The Cerbos server is configured with a YAML file. Start the server by passing th
NOTE: Config values can reference environment variables by enclosing them between `${}`. E.g. `$$${HOME}$$`.


[source,sh,subs="attributes"]
----
./{app-name} server --config=/path/to/config.yaml --set=server.httpListenAddr=:3592 --set=engine.defaultPolicyVersion=staging
----


== Minimal Configuration
At a minimum, Cerbos requires a storage driver to be configured in order to start. You must provide a configuration file when starting the Cerbos binary. The Cerbos container ships with a default configuration that has a `disk` driver configured to look for policies mounted at `/policies`.
.Default configuration file shipped in the container
At a minimum, Cerbos requires a storage driver to be configured. If no explicit configuration is provided using the `--config` flag, Cerbos defaults to a `disk` driver configured to look for policies in a directory named `policies` in the current working directory.

.Default configuration
[source,yaml,linenums]
----
---
server:
httpListenAddr: ":3592"
grpcListenAddr: ":3593"
engine:
defaultPolicyVersion: "default"
auxData:
jwt:
disableVerification: true
storage:
driver: "disk"
disk:
directory: /policies
directory: "${PWD}/policies"
watchForChanges: true
----

Expand Down
2 changes: 1 addition & 1 deletion conf.default.yaml → internal/config/conf.yaml.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ auxData:
storage:
driver: "disk"
disk:
directory: /policies
directory: "{{ .directory }}"
watchForChanges: true
32 changes: 32 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,25 @@
package config

import (
"bytes"
_ "embed"
"errors"
"fmt"
"html/template"
"io"
"os"
"path/filepath"
"strings"
"sync"

"go.uber.org/config"
)

const DefaultMarker = "__default__"

//go:embed conf.yaml.gotmpl
var defaultConfTmpl string

var ErrConfigNotLoaded = errors.New("config not loaded")

var conf = &Wrapper{}
Expand All @@ -32,6 +41,10 @@ type Validator interface {

// Load loads the config file at the given path.
func Load(confFile string, overrides map[string]any) error {
if confFile == "" || confFile == DefaultMarker {
return loadDefault(overrides)
}

finfo, err := os.Stat(confFile)
if err != nil {
return fmt.Errorf("failed to stat %s: %w", confFile, err)
Expand All @@ -44,6 +57,25 @@ func Load(confFile string, overrides map[string]any) error {
return doLoad(config.File(confFile), config.Static(overrides))
}

func loadDefault(overrides map[string]any) error {
tmpl, err := template.New("conf").Parse(defaultConfTmpl)
if err != nil {
return fmt.Errorf("failed to parse default config template: %w", err)
}

currDir, err := os.Getwd()
if err != nil {
return fmt.Errorf("failed to determine current working directory: %w", err)
}

renderedConf := new(bytes.Buffer)
if err := tmpl.Execute(renderedConf, map[string]string{"directory": filepath.Join(currDir, "policies")}); err != nil {
return fmt.Errorf("failed to render default config template: %w", err)
}

return doLoad(config.Source(renderedConf), config.Static(overrides))
}

func LoadReader(reader io.Reader, overrides map[string]any) error {
return doLoad(config.Source(reader), config.Static(overrides))
}
Expand Down
16 changes: 16 additions & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,3 +210,19 @@ func TestStrictParsing(t *testing.T) {
})
}
}

func TestBuiltInConfig(t *testing.T) {
require.NoError(t, config.Load("", nil))

var serverConf server.Conf
require.NoError(t, config.Get("server", &serverConf))
require.Equal(t, ":3592", serverConf.HTTPListenAddr)
require.Equal(t, ":3593", serverConf.GRPCListenAddr)

cwd, err := os.Getwd()
require.NoError(t, err, "Failed to determine working directory")

var diskDir string
require.NoError(t, config.Get("storage.disk.directory", &diskDir))
require.Equal(t, filepath.Join(cwd, "policies"), diskDir)
}
2 changes: 2 additions & 0 deletions internal/storage/disk/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/cerbos/cerbos/internal/policy"
"github.com/cerbos/cerbos/internal/storage"
"github.com/cerbos/cerbos/internal/storage/index"
"go.uber.org/zap"
)

const DriverName = "disk"
Expand Down Expand Up @@ -47,6 +48,7 @@ func NewStore(ctx context.Context, conf *Conf) (*Store, error) {
return nil, fmt.Errorf("failed to determine absolute path of directory [%s]: %w", conf.Directory, err)
}

zap.S().Named("disk.store").Infof("Initializing disk store from %s", dir)
idx, err := index.Build(ctx, os.DirFS(dir))
if err != nil {
return nil, err
Expand Down

0 comments on commit 1a949a1

Please sign in to comment.