Read TUF config from flags and add to helm chart

The values in the local helm chart are for the dev root
This commit is contained in:
Jonny Stoten
2024-06-24 16:53:54 +01:00
parent e0f660b3cc
commit e76057db3e
6 changed files with 136 additions and 42 deletions

View File

@@ -25,6 +25,22 @@ spec:
- --client-ca-file={{ .Values.clientCAFile }}
{{- end }}
- --port={{ .Values.port }}
{{- if .Values.tufRoot }}
- --tuf-root={{ .Values.tufRoot }}
{{- end }}
{{- if .Values.tufMetadataSource }}
- --tuf-metadata-source={{ .Values.tufMetadataSource }}
{{- end }}
{{- if .Values.tufTargetsSource }}
- --tuf-targets-source={{ .Values.tufTargetsSource }}
{{- end }}
{{- if .Values.localPolicyDir }}
- --local-policy-dir={{ .Values.localPolicyDir }}
{{- end }}
{{- if .Values.policyCacheDir }}
- --policy-cache-dir={{ .Values.policyCacheDir }}
{{- end }}
ports:
- containerPort: {{ .Values.port }}
protocol: TCP

View File

@@ -1,6 +1,9 @@
certDir: /certs
clientCAFile: /tmp/gatekeeper/ca.crt
port: 8090
tufRoot: dev
tufMetadataSource: https://docker.github.io/tuf-dev/metadata
tufTargetsSource: https://docker.github.io/tuf-dev/targets
provider:
timeout: 30
tls:

56
main.go
View File

@@ -32,6 +32,24 @@ var (
certDir string
clientCAFile string
port int
tufRoot string
tufoutputPath string
metadataURL string
targetsURL string
policyDir string
policyCacheDir string
)
const (
defaultMetadataURL = "registry-1.docker.io/docker/tuf-metadata:latest"
defaultTargetsURL = "registry-1.docker.io/docker/tuf-targets"
)
var (
defaultTUFOutputPath = filepath.Join("/tuf_temp", ".docker", "tuf")
defaultPolicyCacheDir = filepath.Join("/tuf_temp", ".docker", "policy")
)
var timeoutError = string(utils.GatekeeperError("operation timed out"))
@@ -41,13 +59,47 @@ func init() {
flag.StringVar(&certDir, "cert-dir", "", "path to directory containing TLS certificates")
flag.StringVar(&clientCAFile, "client-ca-file", "", "path to client CA certificate")
flag.IntVar(&port, "port", defaultPort, "Port for the server to listen on")
flag.StringVar(&tufRoot, "tuf-root", "staging", "specify embedded tuf root [dev, staging], default [staging]")
if tufRoot != "dev" && tufRoot != "staging" {
klog.Errorf("invalid tuf root: %s", tufRoot)
os.Exit(1)
}
flag.StringVar(&metadataURL, "tuf-metadata-source", defaultMetadataURL, "source (URL or repo) for TUF metadata")
flag.StringVar(&targetsURL, "tuf-targets-source", defaultTargetsURL, "source (URL or repo) for TUF targets")
flag.StringVar(&tufoutputPath, "tuf-output-path", defaultTUFOutputPath, "local dir to store TUF repo metadata")
flag.StringVar(&policyDir, "local-policy-dir", "", "path to local policy directory (overrides TUF policy)")
flag.StringVar(&policyCacheDir, "policy-cache-dir", defaultPolicyCacheDir, "path to store policy downloaded from TUF")
flag.Parse()
}
func main() {
mux := http.NewServeMux()
mux.Handle("POST /validate", http.TimeoutHandler(handler.Validate(), handlerTimeout, timeoutError))
mux.Handle("POST /mutate", http.TimeoutHandler(handler.Mutate(), handlerTimeout, timeoutError))
validateHandler, err := handler.NewValidateHandler(&handler.ValidateHandlerOptions{
TUFRoot: tufRoot,
TUFOutputPath: tufoutputPath,
TUFMetadataURL: metadataURL,
TUFTargetsURL: targetsURL,
PolicyDir: policyDir,
PolicyCacheDir: policyCacheDir,
})
if err != nil {
klog.ErrorS(err, "unable to create validate handler")
os.Exit(1)
}
mutateHandler, err := handler.NewMutateHandler()
if err != nil {
klog.ErrorS(err, "unable to create validate handler")
os.Exit(1)
}
mux.Handle("POST /validate", http.TimeoutHandler(validateHandler, handlerTimeout, timeoutError))
mux.Handle("POST /mutate", http.TimeoutHandler(mutateHandler, handlerTimeout, timeoutError))
server := &http.Server{
Addr: fmt.Sprintf(":%d", port),

View File

@@ -15,11 +15,13 @@ import (
"k8s.io/klog/v2"
)
func Mutate() http.Handler {
return http.HandlerFunc(mutate)
type mutateHandler struct{}
func NewMutateHandler() (http.Handler, error) {
return &mutateHandler{}, nil
}
func mutate(w http.ResponseWriter, req *http.Request) {
func (h *mutateHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
defer func() {
if r := recover(); r != nil {
klog.Error(string(debug.Stack()))

View File

@@ -1,17 +0,0 @@
package handler
import (
"github.com/docker/attest/pkg/tuf"
"github.com/open-policy-agent/gatekeeper-external-data-provider/internal/embed"
)
func createTufClient(outputPath string) (*tuf.TufClient, error) {
// using oci tuf metadata and targets
metadataURI := "registry-1.docker.io/docker/tuf-metadata:latest"
targetsURI := "registry-1.docker.io/docker/tuf-targets"
// example using http tuf metadata and targets
// metadataURI := "https://docker.github.io/tuf-staging/metadata"
// targetsURI := "https://docker.github.io/tuf-staging/targets"
return tuf.NewTufClient(embed.StagingRoot, outputPath, metadataURI, targetsURI, tuf.NewVersionChecker())
}

View File

@@ -5,14 +5,15 @@ import (
"fmt"
"io"
"net/http"
"path/filepath"
"runtime/debug"
"github.com/docker/attest/pkg/attest"
"github.com/docker/attest/pkg/oci"
"github.com/docker/attest/pkg/policy"
"github.com/docker/attest/pkg/tuf"
intoto "github.com/in-toto/in-toto-golang/in_toto"
"github.com/open-policy-agent/frameworks/constraint/pkg/externaldata"
"github.com/open-policy-agent/gatekeeper-external-data-provider/internal/embed"
"github.com/open-policy-agent/gatekeeper-external-data-provider/pkg/utils"
"k8s.io/klog/v2"
)
@@ -24,11 +25,50 @@ type ValidationResult struct {
Violations []policy.Violation `json:"violations"`
}
func Validate() http.Handler {
return http.HandlerFunc(validate)
type ValidateHandlerOptions struct {
TUFRoot string
TUFOutputPath string
TUFMetadataURL string
TUFTargetsURL string
PolicyDir string
PolicyCacheDir string
}
func validate(w http.ResponseWriter, req *http.Request) {
type validateHandler struct {
opts *ValidateHandlerOptions
}
func NewValidateHandler(opts *ValidateHandlerOptions) (http.Handler, error) {
handler := &validateHandler{opts: opts}
// a TUF client can only be used once, so we need to create a new one for each request.
// we create this one up front to ensure that the TUF root is valid and to pre-load the metadata.
// TODO: this pre-loading works for the root, targets, snapshot, and timestamp roles, but not for delegated roles.
_, err := handler.createTUFClient()
if err != nil {
return nil, err
}
klog.Infof("validate handler initialized with %s TUF root", opts.TUFRoot)
return handler, nil
}
func (h *validateHandler) createTUFClient() (*tuf.TufClient, error) {
var rootBytes []byte
switch h.opts.TUFRoot {
case "dev":
rootBytes = embed.DevRoot
case "staging":
rootBytes = embed.StagingRoot
default:
return nil, fmt.Errorf("invalid tuf root: %s", h.opts.TUFRoot)
}
return tuf.NewTufClient(rootBytes, h.opts.TUFOutputPath, h.opts.TUFMetadataURL, h.opts.TUFTargetsURL, tuf.NewVersionChecker())
}
func (h *validateHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
defer func() {
if r := recover(); r != nil {
klog.Error(string(debug.Stack()))
@@ -36,6 +76,10 @@ func validate(w http.ResponseWriter, req *http.Request) {
}
}()
ctx := req.Context()
debug := true
ctx = policy.WithPolicyEvaluator(ctx, policy.NewRegoEvaluator(debug))
// read request body
requestBody, err := io.ReadAll(req.Body)
if err != nil {
@@ -53,16 +97,19 @@ func validate(w http.ResponseWriter, req *http.Request) {
return
}
results := make([]externaldata.Item, 0)
// create a tuf client
tufOutputPath := filepath.Join("/tuf_temp", ".docker", "tuf")
tufClient, err := createTufClient(tufOutputPath)
tufClient, err := h.createTUFClient()
if err != nil {
utils.SendResponse(nil, err.Error(), w)
utils.SendResponse(nil, fmt.Sprintf("unable to create TUF client: %v", err), w)
return
}
policyOpts := &policy.PolicyOptions{
TufClient: tufClient,
LocalTargetsDir: h.opts.PolicyCacheDir,
LocalPolicyDir: h.opts.PolicyDir,
}
results := make([]externaldata.Item, 0)
for _, key := range providerRequest.Request.Keys {
platform := "linux/amd64"
src, err := oci.ParseImageSpec(key, oci.WithPlatform(platform))
@@ -71,16 +118,7 @@ func validate(w http.ResponseWriter, req *http.Request) {
return
}
opts := &policy.PolicyOptions{
TufClient: tufClient,
LocalTargetsDir: filepath.Join("/tuf_temp", ".docker", "policy"), // location to store policy files downloaded from TUF
LocalPolicyDir: "", // overrides TUF policy for local policy files if set
}
ctx := req.Context()
debug := true
ctx = policy.WithPolicyEvaluator(ctx, policy.NewRegoEvaluator(debug))
result, err := attest.Verify(ctx, src, opts)
result, err := attest.Verify(ctx, src, policyOpts)
if err != nil {
utils.SendResponse(nil, err.Error(), w)
return