diff --git a/Dockerfile b/Dockerfile index 10cc9f8..797d2b2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ COPY . . # --- This block can be replaced by `RUN go mod download` when github.com/docker/attest is public ENV GOPRIVATE="github.com/docker/attest" -RUN --mount=type=secret,id=GITHUB_TOKEN < digest) - +1. To reload the attest-provider image after making changes, run the following command: + +```bash +make reload +``` + 1. Uninstall the external data provider and Gatekeeper. ```bash diff --git a/main.go b/main.go index c51664d..d25a703 100644 --- a/main.go +++ b/main.go @@ -11,10 +11,16 @@ import ( "time" "github.com/open-policy-agent/gatekeeper-external-data-provider/pkg/handler" + "github.com/open-policy-agent/gatekeeper-external-data-provider/pkg/utils" "k8s.io/klog/v2" ) +const ( + handlerTimeout = 15 * time.Second + readHeaderTimeout = 1 * time.Second +) + const ( defaultPort = 8090 @@ -28,6 +34,8 @@ var ( port int ) +var timeoutError = string(utils.GatekeeperError("operation timed out")) + func init() { klog.InitFlags(nil) flag.StringVar(&certDir, "cert-dir", "", "path to directory containing TLS certificates") @@ -38,12 +46,12 @@ func init() { func main() { mux := http.NewServeMux() - mux.HandleFunc("/", handler.Handler) + mux.Handle("POST /", http.TimeoutHandler(handler.Handler(), handlerTimeout, timeoutError)) server := &http.Server{ Addr: fmt.Sprintf(":%d", port), Handler: mux, - ReadHeaderTimeout: time.Duration(15) * time.Second, + ReadHeaderTimeout: readHeaderTimeout, } config := &tls.Config{ @@ -79,25 +87,3 @@ func main() { os.Exit(1) } } - -// TODO: root cause Content-Length header error -// func processTimeout(h http.HandlerFunc, duration time.Duration) http.HandlerFunc { -// return func(w http.ResponseWriter, r *http.Request) { -// ctx, cancel := context.WithTimeout(r.Context(), duration) -// defer cancel() - -// r = r.WithContext(ctx) - -// processDone := make(chan bool) -// go func() { -// h(w, r) -// processDone <- true -// }() - -// select { -// case <-ctx.Done(): -// utils.SendResponse(nil, "operation timed out", w) -// case <-processDone: -// } -// } -// } diff --git a/pkg/handler/handler.go b/pkg/handler/handler.go index fa8b03a..32e719d 100644 --- a/pkg/handler/handler.go +++ b/pkg/handler/handler.go @@ -1,12 +1,12 @@ package handler import ( - "context" "encoding/json" "fmt" "io" "net/http" "path/filepath" + "runtime/debug" "github.com/docker/attest/pkg/attest" "github.com/docker/attest/pkg/oci" @@ -18,12 +18,17 @@ import ( "k8s.io/klog/v2" ) -func Handler(w http.ResponseWriter, req *http.Request) { - // only accept POST requests - if req.Method != http.MethodPost { - utils.SendResponse(nil, "only POST is allowed", w) - return - } +func Handler() http.Handler { + return http.HandlerFunc(handler) +} + +func handler(w http.ResponseWriter, req *http.Request) { + defer func() { + if r := recover(); r != nil { + klog.Error(string(debug.Stack())) + klog.ErrorS(fmt.Errorf("%v", r), "panic occurred") + } + }() // read request body requestBody, err := io.ReadAll(req.Body) @@ -68,7 +73,7 @@ func Handler(w http.ResponseWriter, req *http.Request) { } // verify attestations - ctx := context.TODO() + ctx := req.Context() debug := true ctx = policy.WithPolicyEvaluator(ctx, policy.NewRegoEvaluator(debug)) policy, err := attest.Verify(ctx, opts, resolver) diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index f97a42b..87fcfa2 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -3,7 +3,6 @@ package utils import ( "encoding/json" "net/http" - "os" "github.com/open-policy-agent/frameworks/constraint/pkg/externaldata" "k8s.io/klog/v2" @@ -14,8 +13,7 @@ const ( kind = "ProviderResponse" ) -// sendResponse sends back the response to Gatekeeper. -func SendResponse(results *[]externaldata.Item, systemErr string, w http.ResponseWriter) { +func GatekeeperResponse(results *[]externaldata.Item, systemErr string) []byte { response := externaldata.ProviderResponse{ APIVersion: apiVersion, Kind: kind, @@ -30,18 +28,27 @@ func SendResponse(results *[]externaldata.Item, systemErr string, w http.Respons response.Response.SystemError = systemErr } - klog.InfoS("sending response", "response", response) - body, err := json.Marshal(response) if err != nil { klog.ErrorS(err, "unable to marshal response") - os.Exit(1) + panic(err) } + return body +} + +func GatekeeperError(systemErr string) []byte { + return GatekeeperResponse(nil, systemErr) +} + +// sendResponse sends back the response to Gatekeeper. +func SendResponse(results *[]externaldata.Item, systemErr string, w http.ResponseWriter) { + body := GatekeeperResponse(results, systemErr) + klog.InfoS("sending response", "response", string(body)) w.Header().Set("Content-Type", "application/json") - _, err = w.Write(body) + _, err := w.Write(body) if err != nil { klog.ErrorS(err, "unable to write response") - os.Exit(1) + return } } diff --git a/scripts/generate-tls-cert.sh b/scripts/generate-tls-cert.sh index 16243c3..a8586ab 100755 --- a/scripts/generate-tls-cert.sh +++ b/scripts/generate-tls-cert.sh @@ -12,13 +12,13 @@ generate() { # generate CA key and certificate echo "Generating CA key and certificate for attest-provider..." openssl genrsa -out ca.key 2048 - openssl req -new -x509 -days 1 -key ca.key -subj "/O=Gatekeeper/CN=Gatekeeper Root CA" -out ca.crt + openssl req -new -x509 -days 3650 -key ca.key -subj "/O=Gatekeeper/CN=Gatekeeper Root CA" -out ca.crt # generate server key and certificate echo "Generating server key and certificate for attest-provider..." openssl genrsa -out tls.key 2048 openssl req -newkey rsa:2048 -nodes -keyout tls.key -subj "/CN=attest-provider.${NAMESPACE}" -out server.csr - openssl x509 -req -extfile <(printf "subjectAltName=DNS:attest-provider.%s" "${NAMESPACE}") -days 1 -sha256 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out tls.crt + openssl x509 -req -extfile <(printf "subjectAltName=DNS:attest-provider.%s" "${NAMESPACE}") -days 3650 -sha256 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out tls.crt } mkdir -p "${REPO_ROOT}/certs"