mirror of
https://github.com/storytold/irsa-manager.git
synced 2026-10-28 08:47:48 +00:00
webhook manifests
This commit is contained in:
@@ -19,6 +19,11 @@ type SecretBuilder struct {
|
|||||||
secretType corev1.SecretType
|
secretType corev1.SecretType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TlsCredential interface {
|
||||||
|
Certificate() []byte
|
||||||
|
PrivateKey() []byte
|
||||||
|
}
|
||||||
|
|
||||||
func NewSecretBuilder() *SecretBuilder {
|
func NewSecretBuilder() *SecretBuilder {
|
||||||
return &SecretBuilder{
|
return &SecretBuilder{
|
||||||
secretType: corev1.SecretTypeOpaque,
|
secretType: corev1.SecretTypeOpaque,
|
||||||
@@ -34,6 +39,15 @@ func (b *SecretBuilder) WithSSHKey(keyPair selfhosted.KeyPair) *SecretBuilder {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *SecretBuilder) WithCertificate(t TlsCredential) *SecretBuilder {
|
||||||
|
b.data = map[string][]byte{
|
||||||
|
"tls.crt": t.Certificate(),
|
||||||
|
"tls.key": t.PrivateKey(),
|
||||||
|
}
|
||||||
|
b.secretType = corev1.SecretTypeTLS
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
func (b *SecretBuilder) Build(namespacedName types.NamespacedName) (*corev1.Secret, error) {
|
func (b *SecretBuilder) Build(namespacedName types.NamespacedName) (*corev1.Secret, error) {
|
||||||
secret := &corev1.Secret{
|
secret := &corev1.Secret{
|
||||||
ObjectMeta: v1.ObjectMeta{
|
ObjectMeta: v1.ObjectMeta{
|
||||||
@@ -1,11 +1 @@
|
|||||||
package selfhosted
|
package selfhosted
|
||||||
|
|
||||||
import (
|
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
||||||
)
|
|
||||||
|
|
||||||
type WebHook interface {
|
|
||||||
Resources() []client.Object
|
|
||||||
Create()
|
|
||||||
Delete()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package selfhosted
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Webhook interface {
|
||||||
|
Resources() []client.Object
|
||||||
|
}
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ServiceAccount
|
|
||||||
metadata:
|
|
||||||
name: pod-identity-webhook
|
|
||||||
namespace: kube-system
|
|
||||||
---
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: ClusterRole
|
|
||||||
metadata:
|
|
||||||
name: pod-identity-webhook
|
|
||||||
rules:
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- secrets
|
|
||||||
verbs:
|
|
||||||
- create
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- secrets
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- update
|
|
||||||
- patch
|
|
||||||
resourceNames:
|
|
||||||
- "pod-identity-webhook"
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- serviceaccounts
|
|
||||||
verbs:
|
|
||||||
- get
|
|
||||||
- watch
|
|
||||||
- list
|
|
||||||
- apiGroups:
|
|
||||||
- certificates.k8s.io
|
|
||||||
resources:
|
|
||||||
- certificatesigningrequests
|
|
||||||
verbs:
|
|
||||||
- create
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- watch
|
|
||||||
---
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: ClusterRoleBinding
|
|
||||||
metadata:
|
|
||||||
name: pod-identity-webhook
|
|
||||||
roleRef:
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
kind: ClusterRole
|
|
||||||
name: pod-identity-webhook
|
|
||||||
subjects:
|
|
||||||
- kind: ServiceAccount
|
|
||||||
name: pod-identity-webhook
|
|
||||||
namespace: kube-system
|
|
||||||
@@ -6,188 +6,224 @@ import (
|
|||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
rbacv1 "k8s.io/api/rbac/v1"
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
)
|
)
|
||||||
|
|
||||||
var mutatingWebhookConfiguration = v1beta1.MutatingWebhookConfiguration{
|
type baseManifestFactory struct {
|
||||||
TypeMeta: metav1.TypeMeta{
|
deploymentMeta types.NamespacedName
|
||||||
APIVersion: "admissionregistration.k8s.io/v1beta1",
|
serviceMeta types.NamespacedName
|
||||||
Kind: "MutatingWebhookConfiguration",
|
serviceAccountMeta types.NamespacedName
|
||||||
},
|
mutatingWebhookConfigurationMeta types.NamespacedName
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
podLabel map[string]string
|
||||||
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{
|
func serviceNamespacedName() types.NamespacedName {
|
||||||
TypeMeta: metav1.TypeMeta{
|
return types.NamespacedName{
|
||||||
APIVersion: "apps/v1",
|
|
||||||
Kind: "Deployment",
|
|
||||||
},
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "pod-identity-webhook",
|
Name: "pod-identity-webhook",
|
||||||
Namespace: "kube-system",
|
Namespace: WEBHOOK_NAMESPACE,
|
||||||
},
|
}
|
||||||
Spec: appsv1.DeploymentSpec{
|
}
|
||||||
Replicas: 1,
|
|
||||||
Selector: &metav1.LabelSelector{
|
const WEBHOOK_NAMESPACE = "kube-system"
|
||||||
MatchLabels: map[string]string{"app": "pod-identity-webhook"},
|
|
||||||
|
func newBaseManifestFactory() *baseManifestFactory {
|
||||||
|
return &baseManifestFactory{
|
||||||
|
deploymentMeta: types.NamespacedName{
|
||||||
|
Name: "pod-identity-webhook",
|
||||||
|
Namespace: WEBHOOK_NAMESPACE,
|
||||||
},
|
},
|
||||||
Template: corev1.PodTemplateSpec{
|
serviceMeta: serviceNamespacedName(),
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
serviceAccountMeta: types.NamespacedName{
|
||||||
Labels: map[string]string{"app": "pod-identity-webhook"},
|
Name: "pod-identity-webhook",
|
||||||
},
|
Namespace: WEBHOOK_NAMESPACE,
|
||||||
Spec: corev1.PodSpec{
|
},
|
||||||
ServiceAccountName: "pod-identity-webhook",
|
mutatingWebhookConfigurationMeta: types.NamespacedName{
|
||||||
Containers: []corev1.Container{
|
Name: "pod-identity-webhook",
|
||||||
|
Namespace: WEBHOOK_NAMESPACE,
|
||||||
|
},
|
||||||
|
podLabel: map[string]string{"app": "pod-identity-webhook"},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *baseManifestFactory) mutatingWebhookConfiguration() *v1beta1.MutatingWebhookConfiguration {
|
||||||
|
path := "/mutate"
|
||||||
|
failurePolicy := v1beta1.Ignore
|
||||||
|
return &v1beta1.MutatingWebhookConfiguration{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: v1beta1.SchemeGroupVersion.String(),
|
||||||
|
Kind: "MutatingWebhookConfiguration",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: b.mutatingWebhookConfigurationMeta.Name,
|
||||||
|
Namespace: b.mutatingWebhookConfigurationMeta.Namespace,
|
||||||
|
},
|
||||||
|
Webhooks: []v1beta1.MutatingWebhook{
|
||||||
|
{
|
||||||
|
Name: "pod-identity-webhook.amazonaws.com",
|
||||||
|
ClientConfig: v1beta1.WebhookClientConfig{
|
||||||
|
Service: &v1beta1.ServiceReference{
|
||||||
|
Name: b.serviceMeta.Name,
|
||||||
|
Namespace: b.serviceMeta.Namespace,
|
||||||
|
Path: &path,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Rules: []v1beta1.RuleWithOperations{
|
||||||
{
|
{
|
||||||
Name: "pod-identity-webhook",
|
Operations: []v1beta1.OperationType{"CREATE"},
|
||||||
Image: "quay.io/amis/pod-identity-webhook:v0.0.1",
|
Rule: v1beta1.Rule{
|
||||||
ImagePullPolicy: corev1.PullAlways,
|
APIGroups: []string{""},
|
||||||
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"},
|
APIVersions: []string{"v1"},
|
||||||
VolumeMounts: []corev1.VolumeMount{
|
Resources: []string{"pods"},
|
||||||
{
|
},
|
||||||
Name: "webhook-certs",
|
},
|
||||||
MountPath: "/var/run/app/certs",
|
},
|
||||||
ReadOnly: false,
|
FailurePolicy: &failurePolicy,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *baseManifestFactory) deployment() *appsv1.Deployment {
|
||||||
|
replicas := int32(1)
|
||||||
|
return &appsv1.Deployment{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: appsv1.SchemeGroupVersion.String(),
|
||||||
|
Kind: "Deployment",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: b.deploymentMeta.Name,
|
||||||
|
Namespace: b.deploymentMeta.Namespace,
|
||||||
|
},
|
||||||
|
Spec: appsv1.DeploymentSpec{
|
||||||
|
Replicas: &replicas,
|
||||||
|
Selector: &metav1.LabelSelector{
|
||||||
|
MatchLabels: b.podLabel,
|
||||||
|
},
|
||||||
|
Template: corev1.PodTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: b.podLabel,
|
||||||
|
},
|
||||||
|
Spec: corev1.PodSpec{
|
||||||
|
ServiceAccountName: b.serviceAccountMeta.Name,
|
||||||
|
Containers: []corev1.Container{
|
||||||
|
{
|
||||||
|
Name: "pod-identity-webhook",
|
||||||
|
Image: "quay.io/amis/pod-identity-webhook:v0.0.1",
|
||||||
|
ImagePullPolicy: corev1.PullAlways,
|
||||||
|
// Command: []string{}, // Command must be patched
|
||||||
|
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{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Volumes: []corev1.Volume{
|
},
|
||||||
{
|
},
|
||||||
Name: "webhook-certs",
|
}
|
||||||
VolumeSource: corev1.VolumeSource{
|
}
|
||||||
EmptyDir: &corev1.EmptyDirVolumeSource{},
|
|
||||||
},
|
func (b *baseManifestFactory) serviceAccount() *corev1.ServiceAccount {
|
||||||
},
|
return &corev1.ServiceAccount{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: corev1.SchemeGroupVersion.String(),
|
||||||
|
Kind: "ServiceAccount",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: b.serviceAccountMeta.Name,
|
||||||
|
Namespace: b.serviceAccountMeta.Namespace,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *baseManifestFactory) clusterRole() *rbacv1.ClusterRole {
|
||||||
|
return &rbacv1.ClusterRole{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: rbacv1.SchemeGroupVersion.String(),
|
||||||
|
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"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *baseManifestFactory) clusterRoleBinding() *rbacv1.ClusterRoleBinding {
|
||||||
|
return &rbacv1.ClusterRoleBinding{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: rbacv1.SchemeGroupVersion.String(),
|
||||||
|
Kind: "ClusterRoleBinding",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "pod-identity-webhook",
|
||||||
|
},
|
||||||
|
RoleRef: rbacv1.RoleRef{
|
||||||
|
APIGroup: rbacv1.SchemeGroupVersion.Group,
|
||||||
|
Kind: "ClusterRole",
|
||||||
|
Name: "pod-identity-webhook",
|
||||||
|
},
|
||||||
|
Subjects: []rbacv1.Subject{
|
||||||
|
{
|
||||||
|
Kind: "ServiceAccount",
|
||||||
|
Name: b.serviceAccountMeta.Name,
|
||||||
|
Namespace: b.serviceAccountMeta.Namespace,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *baseManifestFactory) service() *corev1.Service {
|
||||||
|
return &corev1.Service{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
APIVersion: corev1.SchemeGroupVersion.String(),
|
||||||
|
Kind: "Service",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: b.serviceMeta.Name,
|
||||||
|
Namespace: b.serviceMeta.Namespace,
|
||||||
|
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: b.podLabel,
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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,108 @@
|
|||||||
|
package webhook
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/goccy/go-yaml"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"k8s.io/api/admissionregistration/v1beta1"
|
||||||
|
appsv1 "k8s.io/api/apps/v1"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
rbacv1 "k8s.io/api/rbac/v1"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBaseManifests(t *testing.T) {
|
||||||
|
b := newBaseManifestFactory()
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
runFunc func() client.Object
|
||||||
|
expected string
|
||||||
|
expectedFunc func() client.Object
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "mutatingwebhookconfiguration",
|
||||||
|
runFunc: func() client.Object {
|
||||||
|
return b.mutatingWebhookConfiguration()
|
||||||
|
},
|
||||||
|
expected: "testdata/mutatingwebhook.yaml",
|
||||||
|
expectedFunc: testMutatingWebhookConfiguration,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "service",
|
||||||
|
runFunc: func() client.Object {
|
||||||
|
return b.service()
|
||||||
|
},
|
||||||
|
expected: "testdata/service.yaml",
|
||||||
|
expectedFunc: testService,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "deployment",
|
||||||
|
runFunc: func() client.Object {
|
||||||
|
return b.deployment()
|
||||||
|
},
|
||||||
|
expected: "testdata/deployment.yaml",
|
||||||
|
expectedFunc: testDeployment,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "serviceaccount",
|
||||||
|
runFunc: func() client.Object {
|
||||||
|
return b.serviceAccount()
|
||||||
|
},
|
||||||
|
expected: "testdata/serviceaccount.yaml",
|
||||||
|
expectedFunc: testServiceAccount,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "clusterrole",
|
||||||
|
runFunc: func() client.Object {
|
||||||
|
return b.clusterRole()
|
||||||
|
},
|
||||||
|
expected: "testdata/clusterrole.yaml",
|
||||||
|
expectedFunc: testClusterRole,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "clusterrolebinding",
|
||||||
|
runFunc: func() client.Object {
|
||||||
|
return b.clusterRoleBinding()
|
||||||
|
},
|
||||||
|
expected: "testdata/clusterrolebinding.yaml",
|
||||||
|
expectedFunc: testClusterRoleBinding,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
actual := tt.runFunc()
|
||||||
|
data, err := os.ReadFile(tt.expected)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
expected := tt.expectedFunc()
|
||||||
|
err = yaml.UnmarshalWithOptions(data, expected, yaml.UseJSONUnmarshaler())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, expected, actual)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMutatingWebhookConfiguration() client.Object {
|
||||||
|
return &v1beta1.MutatingWebhookConfiguration{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testService() client.Object {
|
||||||
|
return &corev1.Service{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDeployment() client.Object {
|
||||||
|
return &appsv1.Deployment{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testServiceAccount() client.Object {
|
||||||
|
return &corev1.ServiceAccount{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testClusterRole() client.Object {
|
||||||
|
return &rbacv1.ClusterRole{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testClusterRoleBinding() client.Object {
|
||||||
|
return &rbacv1.ClusterRoleBinding{}
|
||||||
|
}
|
||||||
@@ -13,30 +13,30 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TlsCredentials struct {
|
type TlsCredential struct {
|
||||||
privateKey []byte
|
privateKey []byte
|
||||||
certificate []byte
|
certificate []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TlsCredentials) CaBundle() string {
|
func (t TlsCredential) CaBundle() string {
|
||||||
return base64.StdEncoding.EncodeToString(t.certificate)
|
return base64.StdEncoding.EncodeToString(t.certificate)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TlsCredentials) Certificate() []byte {
|
func (t TlsCredential) Certificate() []byte {
|
||||||
return t.certificate
|
return t.certificate
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TlsCredentials) PrivateKey() []byte {
|
func (t TlsCredential) PrivateKey() []byte {
|
||||||
return t.privateKey
|
return t.privateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateTlsCredential(serviceNamespacedName types.NamespacedName) (TlsCredentials, error) {
|
func CreateTlsCredential(serviceNamespacedName types.NamespacedName) (TlsCredential, error) {
|
||||||
certificatePeriod := 365 // days
|
certificatePeriod := 365 // days
|
||||||
|
|
||||||
// Generate RSA private key
|
// Generate RSA private key
|
||||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return TlsCredentials{}, err
|
return TlsCredential{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define certificate template
|
// Define certificate template
|
||||||
@@ -56,7 +56,7 @@ func CreateTlsCredential(serviceNamespacedName types.NamespacedName) (TlsCredent
|
|||||||
// Create the certificate
|
// Create the certificate
|
||||||
certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
|
certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return TlsCredentials{}, err
|
return TlsCredential{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode the private key to PEM format
|
// Encode the private key to PEM format
|
||||||
@@ -71,5 +71,5 @@ func CreateTlsCredential(serviceNamespacedName types.NamespacedName) (TlsCredent
|
|||||||
Bytes: certBytes,
|
Bytes: certBytes,
|
||||||
})
|
})
|
||||||
|
|
||||||
return TlsCredentials{privateKey: privPemBytes, certificate: certPemBytes}, nil
|
return TlsCredential{privateKey: privPemBytes, certificate: certPemBytes}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: pod-identity-webhook
|
|
||||||
namespace: kube-system
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: pod-identity-webhook
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: pod-identity-webhook
|
|
||||||
spec:
|
|
||||||
serviceAccountName: pod-identity-webhook
|
|
||||||
containers:
|
|
||||||
- name: pod-identity-webhook
|
|
||||||
image: quay.io/amis/pod-identity-webhook:v0.0.1
|
|
||||||
imagePullPolicy: Always
|
|
||||||
command:
|
|
||||||
- /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:
|
|
||||||
- name: webhook-certs
|
|
||||||
mountPath: /var/run/app/certs
|
|
||||||
readOnly: false
|
|
||||||
volumes:
|
|
||||||
- name: webhook-certs
|
|
||||||
emptyDir: {}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
apiVersion: admissionregistration.k8s.io/v1beta1
|
|
||||||
kind: MutatingWebhookConfiguration
|
|
||||||
metadata:
|
|
||||||
name: pod-identity-webhook
|
|
||||||
namespace: kube-system
|
|
||||||
webhooks:
|
|
||||||
- name: pod-identity-webhook.amazonaws.com
|
|
||||||
failurePolicy: Ignore
|
|
||||||
clientConfig:
|
|
||||||
service:
|
|
||||||
name: pod-identity-webhook
|
|
||||||
namespace: kube-system
|
|
||||||
path: "/mutate"
|
|
||||||
caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURQekNDQWllZ0F3SUJBZ0lVTjVSVnlOTHRhM2pxZkxiS2I5VmFBY0JZaDFnd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0x6RXRNQ3NHQTFVRUF3d2tjRzlrTFdsa1pXNTBhWFI1TFhkbFltaHZiMnN1YTNWaVpTMXplWE4wWlcwdQpjM1pqTUI0WERUSTBNRE13T1RFd016VXlOMW9YRFRJMU1ETXdPVEV3TXpVeU4xb3dMekV0TUNzR0ExVUVBd3drCmNHOWtMV2xrWlc1MGFYUjVMWGRsWW1odmIyc3VhM1ZpWlMxemVYTjBaVzB1YzNaak1JSUJJakFOQmdrcWhraUcKOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXpSYzY0d2V0TzBSVFNOT2V2c3dqd0JpQjZjMFRaKzRubWhYeQpnVEVxNk9MUXZ5R1k4SlBwKzZTZG1qaGFlRGd2SGxmOU1NODNxa2FzdlBzY0Znd0lkOThwQUhwKzhkdEg3RTlNCjZNNCtDZm5LU2V3QmIrQ25YN0lkeGN2Z3hvSE5rMDcvbERhaXJ5TEVTaFprcnphaW9TYW9rb0dIdStaQmhONHYKdVBrK0xiTGVoSW9QUXdYcm9pMHU3dW4yd2NQaExNSENMckZzUzFTQ3pCMm1xdWN1ZFNNOFNOSWpPUGRRVE90VwpNQmNpMmU2MENQTEEzUWVFUkZnbXdzMEVnV3dyc2ZlVjB5ay83SjBTbFJoVStvQWdJajdLSWlxblFjZU54VFl1CmRrMndaL1JDM0wxck40S0xtb1ArS2dWQU9LK3Q4VjlNbVRzVE9pTHJ3WXhnOE14RWVRSURBUUFCbzFNd1VUQWQKQmdOVkhRNEVGZ1FVQzdxOTcxMDk1V3NLdVpFZ0RiRm9SUVZ6M2N3d0h3WURWUjBqQkJnd0ZvQVVDN3E5NzEwOQo1V3NLdVpFZ0RiRm9SUVZ6M2N3d0R3WURWUjBUQVFIL0JBVXdBd0VCL3pBTkJna3Foa2lHOXcwQkFRc0ZBQU9DCkFRRUFsOU92Z2Z6eXZSSVFzVXU5ckFMS3psS2FWa0xDTUczczlpblR1c3M1YkRBY2t2cTM3Y04wQzdzd1Q4TU4KTzhNcU9mS3lHa2xSWHZCT1pCZ2tGYmx6U0xPREhORFpnSXZacmYxM2JrK1lCUG5ZOVdyeWVidzFHd0lUWTdNQwo1ZFhyYm9JOWtaOUY1c0NiMzNtYTZFdVhKVlpvS0JFc2U3Z20yckhsOVF4d253NVNrbDFtN2QxeS9nQS9YUll0CmJIbkkzVjlycHpzb0grZzYrdjBxU1Y2dDVHYVpsbW0veFJ5YzNKRmtvaU5xQk44MlVwMTFNUlhjd04wTlpSN2oKZjVEVFJxVHdSKzAzT0I1T1k2R2tyclkrK1IybGdKUk1VNE91aHl2cjhqY1N6Q0tNY2dZMjJkc2d5aThTYjFYMQorL3hUcC80VVpSaU5IQUlGcnlaMFY1RlpvQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
|
|
||||||
rules:
|
|
||||||
- operations: [ "CREATE" ]
|
|
||||||
apiGroups: [""]
|
|
||||||
apiVersions: ["v1"]
|
|
||||||
resources: ["pods"]
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
apiVersion: admissionregistration.k8s.io/v1beta1
|
|
||||||
kind: MutatingWebhookConfiguration
|
|
||||||
metadata:
|
|
||||||
name: pod-identity-webhook
|
|
||||||
namespace: kube-system
|
|
||||||
webhooks:
|
|
||||||
- name: pod-identity-webhook.amazonaws.com
|
|
||||||
failurePolicy: Ignore
|
|
||||||
clientConfig:
|
|
||||||
service:
|
|
||||||
name: pod-identity-webhook
|
|
||||||
namespace: kube-system
|
|
||||||
path: "/mutate"
|
|
||||||
caBundle: ${CA_BUNDLE}
|
|
||||||
rules:
|
|
||||||
- operations: [ "CREATE" ]
|
|
||||||
apiGroups: [""]
|
|
||||||
apiVersions: ["v1"]
|
|
||||||
resources: ["pods"]
|
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: pod-identity-webhook
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- secrets
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- get
|
||||||
|
- update
|
||||||
|
- patch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- serviceaccounts
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- watch
|
||||||
|
- list
|
||||||
|
- apiGroups:
|
||||||
|
- certificates.k8s.io
|
||||||
|
resources:
|
||||||
|
- certificatesigningrequests
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- watch
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRoleBinding
|
||||||
|
metadata:
|
||||||
|
name: pod-identity-webhook
|
||||||
|
roleRef:
|
||||||
|
apiGroup: rbac.authorization.k8s.io
|
||||||
|
kind: ClusterRole
|
||||||
|
name: pod-identity-webhook
|
||||||
|
subjects:
|
||||||
|
- kind: ServiceAccount
|
||||||
|
name: pod-identity-webhook
|
||||||
|
namespace: kube-system
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: pod-identity-webhook
|
||||||
|
namespace: kube-system
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: pod-identity-webhook
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app: pod-identity-webhook
|
||||||
|
spec:
|
||||||
|
serviceAccountName: pod-identity-webhook
|
||||||
|
containers:
|
||||||
|
- name: pod-identity-webhook
|
||||||
|
image: quay.io/amis/pod-identity-webhook:v0.0.1
|
||||||
|
imagePullPolicy: Always
|
||||||
|
# command:
|
||||||
|
# - /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:
|
||||||
|
- name: webhook-certs
|
||||||
|
mountPath: /var/run/app/certs
|
||||||
|
readOnly: false
|
||||||
|
volumes:
|
||||||
|
- name: webhook-certs
|
||||||
|
emptyDir: {}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
apiVersion: admissionregistration.k8s.io/v1beta1
|
||||||
|
kind: MutatingWebhookConfiguration
|
||||||
|
metadata:
|
||||||
|
name: pod-identity-webhook
|
||||||
|
namespace: kube-system
|
||||||
|
webhooks:
|
||||||
|
- name: pod-identity-webhook.amazonaws.com
|
||||||
|
failurePolicy: Ignore
|
||||||
|
clientConfig:
|
||||||
|
service:
|
||||||
|
name: pod-identity-webhook
|
||||||
|
namespace: kube-system
|
||||||
|
path: "/mutate"
|
||||||
|
rules:
|
||||||
|
- operations: ["CREATE"]
|
||||||
|
apiGroups: [""]
|
||||||
|
apiVersions: ["v1"]
|
||||||
|
resources: ["pods"]
|
||||||
Vendored
@@ -0,0 +1,5 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: pod-identity-webhook
|
||||||
|
namespace: kube-system
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package webhook
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/kkb0318/irsa-manager/internal/manifests"
|
||||||
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AwsWebhook struct {
|
||||||
|
resources []client.Object
|
||||||
|
}
|
||||||
|
|
||||||
|
func secretNamespacedName() types.NamespacedName {
|
||||||
|
return types.NamespacedName{
|
||||||
|
Name: "pod-identity-webhook",
|
||||||
|
Namespace: WEBHOOK_NAMESPACE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWebHook() (*AwsWebhook, error) {
|
||||||
|
factory := newBaseManifestFactory()
|
||||||
|
resources, err := myCertificate(factory)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &AwsWebhook{resources}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func myCertificate(base *baseManifestFactory) ([]client.Object, error) {
|
||||||
|
tlsCredential, err := CreateTlsCredential(serviceNamespacedName())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resources := []client.Object{}
|
||||||
|
secretNamespacedName := secretNamespacedName()
|
||||||
|
secret, err := manifests.NewSecretBuilder().
|
||||||
|
WithCertificate(tlsCredential).
|
||||||
|
Build(secretNamespacedName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
deploy := base.deployment()
|
||||||
|
deploy.Spec.Template.Spec.Containers[0].Command = []string{
|
||||||
|
"/webhook",
|
||||||
|
"--in-cluster",
|
||||||
|
fmt.Sprintf("--namespace=%s", WEBHOOK_NAMESPACE),
|
||||||
|
fmt.Sprintf("--service-name=%s", base.serviceMeta.Name),
|
||||||
|
fmt.Sprintf("--tls-secret=%s", secretNamespacedName.Name),
|
||||||
|
"--annotation-prefix=eks.amazonaws.com",
|
||||||
|
"--token-audience=sts.amazonaws.com",
|
||||||
|
"--logtostderr",
|
||||||
|
}
|
||||||
|
mutate := base.mutatingWebhookConfiguration()
|
||||||
|
mutate.Webhooks[0].ClientConfig.CABundle = []byte(tlsCredential.CaBundle())
|
||||||
|
resources = append(resources,
|
||||||
|
secret,
|
||||||
|
deploy,
|
||||||
|
mutate,
|
||||||
|
base.clusterRole(),
|
||||||
|
base.clusterRoleBinding(),
|
||||||
|
base.serviceAccount(),
|
||||||
|
base.service(),
|
||||||
|
)
|
||||||
|
return resources, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user