mirror of
https://github.com/storytold/irsa-manager.git
synced 2026-10-18 07:33:15 +00:00
webhook base manifests
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"k8s.io/api/admissionregistration/v1beta1"
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
)
|
||||
|
||||
var mutatingWebhookConfiguration = v1beta1.MutatingWebhookConfiguration{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "admissionregistration.k8s.io/v1beta1",
|
||||
Kind: "MutatingWebhookConfiguration",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod-identity-webhook",
|
||||
Namespace: "kube-system",
|
||||
},
|
||||
Webhooks: []v1beta1.MutatingWebhook{
|
||||
{
|
||||
Name: "pod-identity-webhook.amazonaws.com",
|
||||
ClientConfig: v1beta1.WebhookClientConfig{
|
||||
Service: &v1beta1.ServiceReference{
|
||||
Name: "pod-identity-webhook",
|
||||
Namespace: "kube-system",
|
||||
Path: "/mutate",
|
||||
},
|
||||
CABundle: []byte("${CA_BUNDLE}"),
|
||||
},
|
||||
Rules: []v1beta1.RuleWithOperations{
|
||||
{
|
||||
Operations: []v1beta1.OperationType{"CREATE"},
|
||||
Rule: v1beta1.Rule{
|
||||
APIGroups: []string{""},
|
||||
APIVersions: []string{"v1"},
|
||||
Resources: []string{"pods"},
|
||||
},
|
||||
},
|
||||
},
|
||||
FailurePolicy: (*v1beta1.FailurePolicyType)(nil),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var deployment = appsv1.Deployment{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "apps/v1",
|
||||
Kind: "Deployment",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod-identity-webhook",
|
||||
Namespace: "kube-system",
|
||||
},
|
||||
Spec: appsv1.DeploymentSpec{
|
||||
Replicas: 1,
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"app": "pod-identity-webhook"},
|
||||
},
|
||||
Template: corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Labels: map[string]string{"app": "pod-identity-webhook"},
|
||||
},
|
||||
Spec: corev1.PodSpec{
|
||||
ServiceAccountName: "pod-identity-webhook",
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
Name: "pod-identity-webhook",
|
||||
Image: "quay.io/amis/pod-identity-webhook:v0.0.1",
|
||||
ImagePullPolicy: corev1.PullAlways,
|
||||
Command: []string{"/webhook", "--in-cluster", "--namespace=kube-system", "--service-name=pod-identity-webhook", "--tls-secret=pod-identity-webhook", "--annotation-prefix=eks.amazonaws.com", "--token-audience=sts.amazonaws.com", "--logtostderr"},
|
||||
VolumeMounts: []corev1.VolumeMount{
|
||||
{
|
||||
Name: "webhook-certs",
|
||||
MountPath: "/var/run/app/certs",
|
||||
ReadOnly: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Volumes: []corev1.Volume{
|
||||
{
|
||||
Name: "webhook-certs",
|
||||
VolumeSource: corev1.VolumeSource{
|
||||
EmptyDir: &corev1.EmptyDirVolumeSource{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var serviceAccount = corev1.ServiceAccount{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "ServiceAccount",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod-identity-webhook",
|
||||
Namespace: "kube-system",
|
||||
},
|
||||
}
|
||||
|
||||
type ClusterRole struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
Rules []rbacv1.PolicyRule `json:"rules,omitempty"`
|
||||
}
|
||||
|
||||
var clusterRole = ClusterRole{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "rbac.authorization.k8s.io/v1",
|
||||
Kind: "ClusterRole",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod-identity-webhook",
|
||||
},
|
||||
Rules: []rbacv1.PolicyRule{
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"secrets"},
|
||||
Verbs: []string{"create", "get", "update", "patch"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{""},
|
||||
Resources: []string{"serviceaccounts"},
|
||||
Verbs: []string{"get", "watch", "list"},
|
||||
},
|
||||
{
|
||||
APIGroups: []string{"certificates.k8s.io"},
|
||||
Resources: []string{"certificatesigningrequests"},
|
||||
Verbs: []string{"create", "get", "list", "watch"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
type ClusterRoleBinding struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
RoleRef rbacv1.RoleRef `json:"roleRef"`
|
||||
Subjects []rbacv1.Subject `json:"subjects"`
|
||||
}
|
||||
|
||||
var clusterRoleBinding = ClusterRoleBinding{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "rbac.authorization.k8s.io/v1",
|
||||
Kind: "ClusterRoleBinding",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod-identity-webhook",
|
||||
},
|
||||
RoleRef: rbacv1.RoleRef{
|
||||
APIGroup: "rbac.authorization.k8s.io",
|
||||
Kind: "ClusterRole",
|
||||
Name: "pod-identity-webhook",
|
||||
},
|
||||
Subjects: []rbacv1.Subject{
|
||||
{
|
||||
Kind: "ServiceAccount",
|
||||
Name: "pod-identity-webhook",
|
||||
Namespace: "kube-system",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var service = corev1.Service{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "Service",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod-identity-webhook",
|
||||
Namespace: "kube-system",
|
||||
Annotations: map[string]string{
|
||||
"prometheus.io/port": "443",
|
||||
"prometheus.io/scheme": "https",
|
||||
"prometheus.io/scrape": "true",
|
||||
},
|
||||
},
|
||||
Spec: corev1.ServiceSpec{
|
||||
Ports: []corev1.ServicePort{
|
||||
{
|
||||
Port: 443,
|
||||
TargetPort: intstr.FromInt(443),
|
||||
},
|
||||
},
|
||||
Selector: map[string]string{
|
||||
"app": "pod-identity-webhook",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
type TlsCredentials struct {
|
||||
privateKey []byte
|
||||
certificate []byte
|
||||
}
|
||||
|
||||
func (t *TlsCredentials) CaBundle() string {
|
||||
return base64.StdEncoding.EncodeToString(t.certificate)
|
||||
}
|
||||
|
||||
func (t *TlsCredentials) Certificate() []byte {
|
||||
return t.certificate
|
||||
}
|
||||
|
||||
func (t *TlsCredentials) PrivateKey() []byte {
|
||||
return t.privateKey
|
||||
}
|
||||
|
||||
func CreateTlsCredential(serviceNamespacedName types.NamespacedName) (TlsCredentials, error) {
|
||||
certificatePeriod := 365 // days
|
||||
|
||||
// Generate RSA private key
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return TlsCredentials{}, err
|
||||
}
|
||||
|
||||
// Define certificate template
|
||||
template := x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
Subject: pkix.Name{
|
||||
CommonName: serviceNamespacedName.Name + "." + serviceNamespacedName.Namespace + ".svc",
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(0, 0, certificatePeriod),
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
}
|
||||
|
||||
// Create the certificate
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
|
||||
if err != nil {
|
||||
return TlsCredentials{}, err
|
||||
}
|
||||
|
||||
// Encode the private key to PEM format
|
||||
privPemBytes := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
|
||||
})
|
||||
|
||||
// Encode the certificate to PEM format
|
||||
certPemBytes := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: certBytes,
|
||||
})
|
||||
|
||||
return TlsCredentials{privateKey: privPemBytes, certificate: certPemBytes}, nil
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
func TestCreateTlsCredentials(t *testing.T) {
|
||||
creds, err := CreateTlsCredential(types.NamespacedName{
|
||||
Name: "pod-identity-webhook",
|
||||
Namespace: "kube-system",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create TLS credentials: %v", err)
|
||||
}
|
||||
|
||||
certBlock, _ := pem.Decode(creds.certificate)
|
||||
if certBlock == nil {
|
||||
t.Fatal("Failed to decode PEM block containing the certificate")
|
||||
}
|
||||
cert, err := x509.ParseCertificate(certBlock.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse certificate: %v", err)
|
||||
}
|
||||
|
||||
keyBlock, _ := pem.Decode(creds.privateKey)
|
||||
if keyBlock == nil {
|
||||
t.Fatal("Failed to decode PEM block containing the private key")
|
||||
}
|
||||
key, err := x509.ParsePKCS1PrivateKey(keyBlock.Bytes)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to parse private key: %v", err)
|
||||
}
|
||||
|
||||
// Verify public keys are equivalent
|
||||
if !publicKeysEqual(cert.PublicKey, &key.PublicKey) {
|
||||
t.Fatal("Public key in certificate does not match public key in private key")
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to compare public keys
|
||||
func publicKeysEqual(pub1, pub2 interface{}) bool {
|
||||
rsaPub1, ok1 := pub1.(*rsa.PublicKey)
|
||||
rsaPub2, ok2 := pub2.(*rsa.PublicKey)
|
||||
|
||||
if !ok1 || !ok2 {
|
||||
return false
|
||||
}
|
||||
return rsaPub1.N.Cmp(rsaPub2.N) == 0 && rsaPub1.E == rsaPub2.E
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer/yaml"
|
||||
)
|
||||
|
||||
type WebhookManifests struct {
|
||||
Manifests []*unstructured.Unstructured
|
||||
}
|
||||
|
||||
func Read() ([]*unstructured.Unstructured, error) {
|
||||
files := []string{
|
||||
"auth.yaml",
|
||||
"deployment.yaml",
|
||||
"mutatingwebhook-ca-bundle.yaml",
|
||||
"mutatingwebhook.yaml",
|
||||
"service.yaml",
|
||||
}
|
||||
objs := []*unstructured.Unstructured{}
|
||||
for _, f := range files {
|
||||
tmp, err := ReadFile(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objs = append(objs, tmp...)
|
||||
}
|
||||
return objs, nil
|
||||
|
||||
}
|
||||
|
||||
func ReadFile(filePath string) ([]*unstructured.Unstructured, error) {
|
||||
yamlContent, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// YAMLデコーダの準備
|
||||
decUnstructured := yaml.NewDecodingSerializer(unstructured.UnstructuredJSONScheme)
|
||||
// マニフェストを分割
|
||||
documents := strings.Split(string(yamlContent), "---")
|
||||
objs := make([]*unstructured.Unstructured, len(documents))
|
||||
for i, doc := range documents {
|
||||
if strings.TrimSpace(doc) == "" {
|
||||
continue
|
||||
}
|
||||
// 各マニフェストを解析
|
||||
obj := &unstructured.Unstructured{}
|
||||
_, _, err := decUnstructured.Decode([]byte(doc), nil, obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objs[i] = obj
|
||||
}
|
||||
return objs, nil
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package webhook
|
||||
Reference in New Issue
Block a user