Skip to content

Commit

Permalink
chore: removed dead test & renamed slog back to log
Browse files Browse the repository at this point in the history
Signed-off-by: Bruno Bressi <[email protected]>
  • Loading branch information
puffitos committed Jan 31, 2024
1 parent b0a99f7 commit 82cfaa9
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 74 deletions.
28 changes: 14 additions & 14 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"syscall"
"time"

"github.com/gookit/slog"
log "github.com/gookit/slog"

"github.com/eumel8/cosignwebhook/webhook"

Expand All @@ -36,26 +36,26 @@ func main() {
// set log level
switch *logLevel {
case "fatal":
slog.SetLogLevel(slog.FatalLevel)
log.SetLogLevel(log.FatalLevel)
case "trace":
slog.SetLogLevel(slog.TraceLevel)
log.SetLogLevel(log.TraceLevel)
case "debug":
slog.SetLogLevel(slog.DebugLevel)
log.SetLogLevel(log.DebugLevel)
case "error":
slog.SetLogLevel(slog.ErrorLevel)
log.SetLogLevel(log.ErrorLevel)
case "warn":
slog.SetLogLevel(slog.WarnLevel)
log.SetLogLevel(log.WarnLevel)
case "info":
slog.SetLogLevel(slog.InfoLevel)
log.SetLogLevel(log.InfoLevel)
default:
slog.SetLogLevel(slog.InfoLevel)
log.SetLogLevel(log.InfoLevel)
}

slog.GetFormatter().(*slog.TextFormatter).SetTemplate(logTemplate)
log.GetFormatter().(*log.TextFormatter).SetTemplate(logTemplate)

certs, err := tls.LoadX509KeyPair(tlscert, tlskey)
if err != nil {
slog.Error("failed to load key pair", "error", err)
log.Error("failed to load key pair", "error", err)
}

server := &http.Server{
Expand Down Expand Up @@ -86,23 +86,23 @@ func main() {
// start webhook server in new rountine
go func() {
if err := server.ListenAndServeTLS("", ""); err != nil {
slog.Error("Failed to listen and serve webhook server", "error", err)
log.Error("Failed to listen and serve webhook server", "error", err)
}
}()
go func() {
if err := mserver.ListenAndServe(); err != nil {
slog.Error("Failed to listen and serve monitor server %v", "error", err)
log.Error("Failed to listen and serve monitor server %v", "error", err)
}
}()

slog.Info("Webhook server running", "port", port, "metricsPort", mport)
log.Info("Webhook server running", "port", port, "metricsPort", mport)

// listening shutdown signal
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
<-signalChan

slog.Info("Got shutdown signal, shutting down webhook server gracefully...")
log.Info("Got shutdown signal, shutting down webhook server gracefully...")
_ = server.Shutdown(context.Background())
_ = mserver.Shutdown(context.Background())
}
73 changes: 37 additions & 36 deletions webhook/cosignwebhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"os"
"time"

"github.com/gookit/slog"
log "github.com/gookit/slog"

"github.com/google/go-containerregistry/pkg/authn"
"github.com/prometheus/client_golang/prometheus"
Expand Down Expand Up @@ -69,7 +69,7 @@ type CosignServerHandler struct {
func NewCosignServerHandler() *CosignServerHandler {
cs, err := restClient()
if err != nil {
slog.Errorf("Can't init rest client: %v", err)
log.Errorf("Can't init rest client: %v", err)
}
eb := record.NewBroadcaster()
eb.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: cs.CoreV1().Events("")})
Expand All @@ -83,12 +83,12 @@ func NewCosignServerHandler() *CosignServerHandler {
func restClient() (*kubernetes.Clientset, error) {
restConfig, err := rest.InClusterConfig()
if err != nil {
slog.Errorf("error init in-cluster config: %v", err)
log.Errorf("error init in-cluster config: %v", err)
return nil, err
}
cs, err := kubernetes.NewForConfig(restConfig)
if err != nil {
slog.Errorf("error creating k8sclientset: %v", err)
log.Errorf("error creating k8sclientset: %v", err)
return nil, err
}
return cs, err
Expand All @@ -110,17 +110,17 @@ func (csh *CosignServerHandler) recordNoVerification(p *corev1.Pod) {
func getPod(b []byte) (*corev1.Pod, *v1.AdmissionReview, error) {
arRequest := v1.AdmissionReview{}
if err := json.Unmarshal(b, &arRequest); err != nil {
slog.Error("Incorrect body")
log.Error("Incorrect body")
return nil, nil, err
}
if arRequest.Request == nil {
slog.Error("AdmissionReview request not found")
log.Error("AdmissionReview request not found")
return nil, nil, fmt.Errorf("admissionreview request not found")
}
raw := arRequest.Request.Object.Raw
pod := corev1.Pod{}
if err := json.Unmarshal(raw, &pod); err != nil {
slog.Error("Error deserializing container")
log.Error("Error deserializing container")
return nil, nil, err
}
return &pod, &arRequest, nil
Expand All @@ -132,12 +132,12 @@ func (csh *CosignServerHandler) getPubKeyFromEnv(c *corev1.Container, ns string)
for _, envVar := range c.Env {
if envVar.Name == CosignEnvVar {
if envVar.Value != "" {
slog.Debugf("Found public key in env var for container %q", c.Name)
log.Debugf("Found public key in env var for container %q", c.Name)
return envVar.Value, nil
}

if envVar.ValueFrom != nil && envVar.ValueFrom.SecretKeyRef != nil {
slog.Debugf("Found reference to public key in secret %q for container %q", envVar.ValueFrom.SecretKeyRef.Name, c.Name)
log.Debugf("Found reference to public key in secret %q for container %q", envVar.ValueFrom.SecretKeyRef.Name, c.Name)
return csh.getSecretValue(ns,
envVar.ValueFrom.SecretKeyRef.Name,
envVar.ValueFrom.SecretKeyRef.Key,
Expand All @@ -154,23 +154,23 @@ func (csh *CosignServerHandler) getSecretValue(namespace, secret, key string) (s
defer cancel()
s, err := csh.cs.CoreV1().Secrets(namespace).Get(ctx, secret, metav1.GetOptions{})
if err != nil {
slog.Debugf("Can't get secret %s/%s : %v", namespace, secret, err)
log.Debugf("Can't get secret %s/%s : %v", namespace, secret, err)
return "", err
}
value := s.Data[key]
if len(value) == 0 {
slog.Errorf("Secret value of %q is empty for %s/%s", key, namespace, secret)
log.Errorf("Secret value of %q is empty for %s/%s", key, namespace, secret)
return "", nil
}
slog.Debugf("Found public key in secret %s/%s, value: %s", namespace, secret, value)
log.Debugf("Found public key in secret %s/%s, value: %s", namespace, secret, value)
return string(value), nil
}

func (csh *CosignServerHandler) Healthz(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
_, err := w.Write([]byte("ok"))
if err != nil {
slog.Errorf("Can't write response: %v", err)
log.Errorf("Can't write response: %v", err)
http.Error(w, fmt.Sprintf("could not write response: %v", err), http.StatusInternalServerError) //nolint:gocritic // function returns after call
}
}
Expand All @@ -191,13 +191,13 @@ func (csh *CosignServerHandler) Serve(w http.ResponseWriter, r *http.Request) {

// Url path of admission
if r.URL.Path != "/validate" {
slog.Error("No validate URI")
log.Error("No validate URI")
http.Error(w, "no validate", http.StatusBadRequest)
return
}

if len(body) == 0 {
slog.Error("Empty body")
log.Error("Empty body")
http.Error(w, "empty body", http.StatusBadRequest)
return
}
Expand All @@ -207,14 +207,15 @@ func (csh *CosignServerHandler) Serve(w http.ResponseWriter, r *http.Request) {

pod, arRequest, err := getPod(body)
if err != nil {
slog.Errorf("Error getPod in %s/%s: %v", pod.Namespace, pod.Name, err)
log.Errorf("Error getPod in %s/%s: %v", pod.Namespace, pod.Name, err)
http.Error(w, "incorrect body", http.StatusBadRequest)
return
}

ctx := r.Context()
kc, err := newKeychainForPod(ctx, pod)
if err != nil {
log.Errorf("Error intializing k8schain %s/%s: %v", pod.Namespace, pod.Name, err)
http.Error(w, "Failed initializing k8schain", http.StatusInternalServerError)
return
}
Expand All @@ -229,7 +230,7 @@ func (csh *CosignServerHandler) Serve(w http.ResponseWriter, r *http.Request) {

err = csh.verifyContainer(pod.Spec.InitContainers[i], pubKey)
if err != nil {
slog.Errorf("Error verifying init container %s/%s/%s: %v", pod.Namespace, pod.Name, pod.Spec.InitContainers[0].Name, err)
log.Errorf("Error verifying init container %s/%s/%s: %v", pod.Namespace, pod.Name, pod.Spec.InitContainers[0].Name, err)
deny(w, err.Error(), arRequest.Request.UID)
return
}
Expand All @@ -243,7 +244,7 @@ func (csh *CosignServerHandler) Serve(w http.ResponseWriter, r *http.Request) {
}
err = csh.verifyContainer(pod.Spec.Containers[i], pubKey)
if err != nil {
slog.Errorf("Error verifying container %s/%s/%s: %v", pod.Namespace, pod.Name, pod.Spec.Containers[i].Name, err)
log.Errorf("Error verifying container %s/%s/%s: %v", pod.Namespace, pod.Name, pod.Spec.Containers[i].Name, err)
deny(w, err.Error(), arRequest.Request.UID)
return
}
Expand Down Expand Up @@ -272,7 +273,7 @@ func newKeychainForPod(ctx context.Context, pod *corev1.Pod) (authn.Keychain, er

kc, err := k8schain.NewInCluster(ctx, opt)
if err != nil {
slog.Errorf("Error intializing k8schain %s/%s: %v", pod.Namespace, pod.Name, err)
log.Errorf("Error intializing k8schain %s/%s: %v", pod.Namespace, pod.Name, err)
return nil, err
}
return kc, nil
Expand All @@ -282,66 +283,66 @@ func newKeychainForPod(ctx context.Context, pod *corev1.Pod) (authn.Keychain, er
// If no public key is found, it returns an empty string.
func (csh *CosignServerHandler) getPubKeyFor(c corev1.Container, ns string) string { //nolint:gocritic // better for garbage collection
if c.Image == "" {
slog.Debugf("Container %q has no image, skipping verification", c.Name)
log.Debugf("Container %q has no image, skipping verification", c.Name)
return ""
}
if len(c.Env) == 0 {
slog.Debugf("Container %q has no env vars, skipping verification", c.Name)
log.Debugf("Container %q has no env vars, skipping verification", c.Name)
return ""
}
pubKey, err := csh.getPubKeyFromEnv(&c, ns)
if err != nil {
slog.Debugf("Could not find pub key in container's %q environment: %v", c.Name, err)
log.Debugf("Could not find pub key in container's %q environment: %v", c.Name, err)
}

// If no public key get here, try to load default secret
if pubKey == "" {
pubKey, err = csh.getSecretValue(ns, "cosignwebhook", CosignEnvVar)
if err != nil {
slog.Debugf("Could not find pub key from default secret: %v", err)
log.Debugf("Could not find pub key from default secret: %v", err)
}
}

// Still no public key, we don't care. Otherwise, POD won't start if we return with 403
// In future versions this should block the start of the container
if pubKey == "" {
slog.Debugf("No public key found, returning")
log.Debugf("No public key found, returning")
return ""
}

slog.Debugf("Found public key for container %q", c.Name)
log.Debugf("Found public key for container %q", c.Name)
return pubKey
}

// verifyContainer verifies the signature of the container image
func (csh *CosignServerHandler) verifyContainer(c corev1.Container, pubKey string) error { //nolint:gocritic // better for garbage collection
slog.Debugf("Verifying container %s", c.Name)
log.Debugf("Verifying container %s", c.Name)

// Lookup image name of current container
image := c.Image
refImage, err := name.ParseReference(image)
if err != nil {
slog.Errorf("Error parsing image reference: %v", err)
log.Errorf("Error parsing image reference: %v", err)
return fmt.Errorf("could parse image reference for image %q", image)
}

// Encrypt public key
publicKey, err := cryptoutils.UnmarshalPEMToPublicKey([]byte(pubKey))
if err != nil {
slog.Errorf("Error unmarshalling public key: %v", err)
log.Errorf("Error unmarshalling public key: %v", err)
return fmt.Errorf("public key for image %q malformed", image)
}

// Load public key to verify
cosignLoadKey, err := signature.LoadECDSAVerifier(publicKey.(*ecdsa.PublicKey), crypto.SHA256)
if err != nil {
slog.Errorf("Error loading ECDSA verifier: %v", err)
log.Errorf("Error loading ECDSA verifier: %v", err)
return errors.New("failed creating key verifier")
}

// Verify signature on remote image with the presented public key
remoteOpts := []ociremote.Option{ociremote.WithRemoteOptions(remote.WithAuthFromKeychain(csh.kc))}
slog.Debugf("Verifying image %q with public key %q", image, pubKey)
log.Debugf("Verifying image %q with public key %q", image, pubKey)
_, _, err = cosign.VerifyImageSignatures(
context.Background(),
refImage,
Expand All @@ -354,26 +355,26 @@ func (csh *CosignServerHandler) verifyContainer(c corev1.Container, pubKey strin

// Verify Image failed, needs to reject container start
if err != nil {
slog.Errorf("Error verifying signature: %v", err)
log.Errorf("Error verifying signature: %v", err)
return fmt.Errorf("signature for %q couldn't be verified", image)
}

// count successful verifies for prometheus metric
verifiedProcessed.Inc()
slog.Infof("Image %q verified successfully", image)
log.Infof("Image %q verified successfully", image)
return nil
}

// deny prevents the container from starting
func deny(w http.ResponseWriter, msg string, uid types.UID) {
resp, err := json.Marshal(admissionReview(http.StatusForbidden, false, "Failure", msg, uid))
if err != nil {
slog.Errorf("Can't encode response: %v", err)
log.Errorf("Can't encode response: %v", err)
http.Error(w, fmt.Sprintf("could not encode response: %v", err), http.StatusInternalServerError)
return
}
if _, err := w.Write(resp); err != nil {
slog.Errorf("Can't write response: %v", err)
log.Errorf("Can't write response: %v", err)
http.Error(w, fmt.Sprintf("could not write response: %v", err), http.StatusInternalServerError)
}
}
Expand All @@ -382,12 +383,12 @@ func deny(w http.ResponseWriter, msg string, uid types.UID) {
func accept(w http.ResponseWriter, msg string, uid types.UID) {
resp, err := json.Marshal(admissionReview(http.StatusOK, true, "Success", msg, uid))
if err != nil {
slog.Errorf("Can't encode response: %v", err)
log.Errorf("Can't encode response: %v", err)
http.Error(w, fmt.Sprintf("could not encode response: %v", err), http.StatusInternalServerError)
return
}
if _, err := w.Write(resp); err != nil {
slog.Errorf("Can't write response: %v", err)
log.Errorf("Can't write response: %v", err)
http.Error(w, fmt.Sprintf("could not write response: %v", err), http.StatusInternalServerError)
}
}
Expand Down
24 changes: 0 additions & 24 deletions webhook/cosignwebhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,27 +139,3 @@ func Test_getPubKeyFromEnv(t *testing.T) {
})
}
}

func TestCosignServerHandler_verifyPodContainer(t *testing.T) {
// tests := []struct {
// name string
// pod *corev1.Pod
// wantErr bool
//}{
// {
// name: "1 container, signed image, pub key present",
// },
//}
//
//for _, tt := range tests {
// t.Run(tt.name, func(t *testing.T) {
// c := fake.NewSimpleClientset()
// csh := &CosignServerHandler{
// cs: c,
// }
// if err := csh.verifyContainer(tt.); (err != nil) != tt.wantErr {
// t.Errorf("verifyContainer() error = %v, wantErr %v", err, tt.wantErr)
// }
// })
//}
}

0 comments on commit 82cfaa9

Please sign in to comment.