pkg/webhook/packagebundle_webhook.go (85 lines of code) (raw):
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package webhook
import (
"context"
"encoding/base64"
"fmt"
"net/http"
"os"
"github.com/go-logr/logr"
admissionv1 "k8s.io/api/admission/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
"github.com/aws/eks-anywhere-packages/api/v1alpha1"
"github.com/aws/eks-anywhere-packages/pkg/bundle"
"github.com/aws/eks-anywhere-packages/pkg/signature"
)
const (
PublicKeyEnvVar = "EKSA_PUBLIC_KEY"
)
type packageBundleValidator struct {
Client client.Client
BundleClient bundle.Client
decoder *admission.Decoder
log logr.Logger
}
func NewPackageBundleValidator(mgr ctrl.Manager) packageBundleValidator {
client := mgr.GetClient()
return packageBundleValidator{
Client: client,
BundleClient: bundle.NewManagerClient(client),
log: mgr.GetLogger().WithName("webhook"),
decoder: admission.NewDecoder(mgr.GetScheme()),
}
}
func InitPackageBundleValidator(mgr ctrl.Manager) error {
handler := NewPackageBundleValidator(mgr)
mgr.GetWebhookServer().
Register("/validate-packages-eks-amazonaws-com-v1alpha1-packagebundle",
&webhook.Admission{Handler: &handler})
return nil
}
func (v *packageBundleValidator) Handle(_ context.Context, request admission.Request) admission.Response {
pb := &v1alpha1.PackageBundle{}
err := v.decoder.Decode(request, pb)
if err != nil {
return admission.Errored(http.StatusInternalServerError,
fmt.Errorf("decoding request: %w", err))
}
err = v.isPackageBundleValid(pb)
resp := &admission.Response{
AdmissionResponse: admissionv1.AdmissionResponse{Allowed: err == nil},
}
if err != nil {
reason := fmt.Sprintf("package %s failed validation with error: %v", pb.Name, err.Error())
resp.AdmissionResponse.Result = &metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusBadRequest,
Message: reason,
Reason: metav1.StatusReason(reason),
}
}
return *resp
}
func (v *packageBundleValidator) isPackageBundleValid(pb *v1alpha1.PackageBundle) error {
if !pb.IsValidVersion() {
v.log.Info("Invalid bundle name (should be in the format vx-xx-xxxx where x is a digit): " + pb.Name)
return fmt.Errorf("Invalid bundle name (should be in the format vx-xx-xxxx where x is a digit): " + pb.Name)
}
keyOverride := os.Getenv(PublicKeyEnvVar)
domain := signature.EksaDomain
if keyOverride != "" {
domain = signature.Domain{Name: signature.DomainName, Pubkey: keyOverride}
}
valid, digest, yml, err := signature.ValidateSignature(pb, domain)
if err != nil {
return err
}
if !valid {
v.log.Info("Invalid signature", "Error", err, "Digest", base64.StdEncoding.EncodeToString(digest[:]), "Manifest", string(yml))
return fmt.Errorf("The signature is invalid for the configured public key: " + domain.Pubkey)
}
return nil
}