使用go添加认证证书

上篇我们手动添加证书很麻烦,这次我们自动生成证书,并用client-go创建mutatingwebhookconfigurations,validatingwebhookconfigurations

package main

import (
	"bytes"
	"context"
	cryptorand "crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"log"
	"math/big"
	"os"
	"time"

	admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
)

func main() {
	var caPEM, serverCertPEM, serverPrivKeyPEM *bytes.Buffer

	ca := &x509.Certificate{
		SerialNumber: big.NewInt(2023),
		Subject: pkix.Name{
			Country:            []string{"CN"},
			Province:           []string{"Beijing"},
			Locality:           []string{"Beijing"},
			Organization:       []string{"sasa.io"},
			OrganizationalUnit: []string{"sasa.io"},
		},
		NotBefore:             time.Now(),
		NotAfter:              time.Now().AddDate(10, 0, 0),
		IsCA:                  true,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		BasicConstraintsValid: true,
	}

	caPrivkey, err := rsa.GenerateKey(cryptorand.Reader, 4096)
	if err != nil {
		fmt.Println(err)
	}

	caBytes, err := x509.CreateCertificate(cryptorand.Reader, ca, ca, &caPrivkey.PublicKey, caPrivkey)
	if err != nil {
		fmt.Println(err)
	}

	caPEM = new(bytes.Buffer)
	_ = pem.Encode(caPEM, &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: caBytes,
	})

	dnsNames := []string{
		"admission-registry",
		"admission-registry.default", "admission-registry.default.svc"}
	commonName := "admission-registry.default.svc"

	cert := &x509.Certificate{
		DNSNames:     dnsNames,
		SerialNumber: big.NewInt(1658),
		Subject: pkix.Name{
			CommonName:   commonName,
			Organization: []string{"sasa.io"},
		},
		NotBefore:    time.Now(),
		NotAfter:     time.Now().AddDate(1, 0, 0),
		SubjectKeyId: []byte{1, 2, 3, 4, 6},
		ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
		KeyUsage:     x509.KeyUsageDigitalSignature,
	}

	serverPrivkey, err := rsa.GenerateKey(cryptorand.Reader, 4096)
	if err != nil {
		fmt.Println(err)
	}

	serverCertBytes, err := x509.CreateCertificate(cryptorand.Reader, cert, ca, &serverPrivkey.PublicKey, caPrivkey)
	if err != nil {
		fmt.Println(err)
	}

	serverCertPEM = new(bytes.Buffer)
	_ = pem.Encode(serverCertPEM, &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: serverCertBytes,
	})

	serverPrivKeyPEM = new(bytes.Buffer)
	_ = pem.Encode(serverPrivKeyPEM, &pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(serverPrivkey),
	})

	err = os.MkdirAll("/etc/webhook/certs/", 0666)
	if err != nil {
		log.Panic(err)
	}

	err = WriteFile("/etc/webhook/certs/tls.cert", serverCertPEM)
	if err != nil {
		log.Panic(err)
	}

	err = WriteFile("/etc/webhook/certs/tls.key", serverPrivKeyPEM)
	if err != nil {
		log.Panic(err)
	}

	log.Println("webhook server tls generated successfully")

	if err = CreateAdmissionConfig(caPEM); err != nil {
		fmt.Println(err)
	}
	log.Println("webhook configuration generated successfully")
}

func WriteFile(filepath string, sCert *bytes.Buffer) error {
	f, err := os.Create(filepath)
	if err != nil {
		return err
	}

	defer f.Close()

	_, err = f.Write(sCert.Bytes())
	if err != nil {
		return err
	}
	return nil
}

func initKubeClient() (*kubernetes.Clientset, error) {
	var (
		err    error
		config *rest.Config
	)
	if config, err = rest.InClusterConfig(); err != nil {
		return nil, err
	}

	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		return nil, err
	}
	return clientset, nil
}

func CreateAdmissionConfig(caCert *bytes.Buffer) error {
	var (
		webhookNamespace, _ = os.LookupEnv("WEBHOOK_NAMESPACE")
		mutationCfgName, _  = os.LookupEnv("MUTATE_CONFIG")
		validateCfgName, _  = os.LookupEnv("VALIDATE_CONFIG")
		webhookService, _   = os.LookupEnv("WEBHOOK_SERVICE")
		validatePath, _     = os.LookupEnv("VALIDATE_PATH")
		mutationPath, _     = os.LookupEnv("MUTATE_PATH")
	)

	clientset, err := initKubeClient()
	if err != nil {
		return err
	}

	ctx := context.Background()

	if validateCfgName != "" {
		validateConfig := &admissionregistrationv1.ValidatingWebhookConfiguration{
			ObjectMeta: metav1.ObjectMeta{
				Name: validateCfgName,
			},
			Webhooks: []admissionregistrationv1.ValidatingWebhook{
				{
					Name: "io.ydzs.admission-registry",
					ClientConfig: admissionregistrationv1.WebhookClientConfig{
						CABundle: caCert.Bytes(),
						Service: &admissionregistrationv1.ServiceReference{
							Name:      webhookService,
							Namespace: webhookNamespace,
							Path:      &validatePath,
						},
					},
					Rules: []admissionregistrationv1.RuleWithOperations{
						{
							Operations: []admissionregistrationv1.OperationType{admissionregistrationv1.Create},
							Rule: admissionregistrationv1.Rule{
								APIGroups:   []string{""},
								APIVersions: []string{"v1"},
								Resources:   []string{"pods"},
							},
						},
					},
					FailurePolicy: func() *admissionregistrationv1.FailurePolicyType {
						pt := admissionregistrationv1.Fail
						return &pt
					}(),
					AdmissionReviewVersions: []string{"v1"},
					SideEffects: func() *admissionregistrationv1.SideEffectClass {
						se := admissionregistrationv1.SideEffectClassNone
						return &se
					}(),
				},
			},
		}
		validateAdmissionClient := clientset.AdmissionregistrationV1().ValidatingWebhookConfigurations()

		_, err := validateAdmissionClient.Get(ctx, validateCfgName, metav1.GetOptions{})
		if err != nil {
			if errors.IsNotFound(err) {
				if _, err = validateAdmissionClient.Create(ctx, validateConfig, metav1.CreateOptions{}); err != nil {
					return err
				}
			} else {
				return err
			}
		} else {
			if _, err = validateAdmissionClient.Update(ctx, validateConfig, metav1.UpdateOptions{}); err != nil {
				return err
			}
		}
	}

	if mutationCfgName != "" {
		mutateConfig := &admissionregistrationv1.MutatingWebhookConfiguration{
			ObjectMeta: metav1.ObjectMeta{
				Name: mutationCfgName,
			},
			Webhooks: []admissionregistrationv1.MutatingWebhook{{
				Name: "io.ydzs.admission-registry-mutate",
				ClientConfig: admissionregistrationv1.WebhookClientConfig{
					CABundle: caCert.Bytes(), // CA bundle created earlier
					Service: &admissionregistrationv1.ServiceReference{
						Name:      webhookService,
						Namespace: webhookNamespace,
						Path:      &mutationPath,
					},
				},
				Rules: []admissionregistrationv1.RuleWithOperations{{Operations: []admissionregistrationv1.OperationType{
					admissionregistrationv1.Create},
					Rule: admissionregistrationv1.Rule{
						APIGroups:   []string{"apps", ""},
						APIVersions: []string{"v1"},
						Resources:   []string{"deployments", "services"},
					},
				}},
				FailurePolicy: func() *admissionregistrationv1.FailurePolicyType {
					pt := admissionregistrationv1.Fail
					return &pt
				}(),
				AdmissionReviewVersions: []string{"v1"},
				SideEffects: func() *admissionregistrationv1.SideEffectClass {
					se := admissionregistrationv1.SideEffectClassNone
					return &se
				}(),
			}},
		}

		mutateAdmissionClient := clientset.AdmissionregistrationV1().MutatingWebhookConfigurations()
		_, err := mutateAdmissionClient.Get(ctx, mutationCfgName, metav1.GetOptions{})
		if err != nil {
			if errors.IsNotFound(err) {
				if _, err = mutateAdmissionClient.Create(ctx, mutateConfig, metav1.CreateOptions{}); err != nil {
					return err
				}
			} else {
				return err
			}
		} else {
			if _, err = mutateAdmissionClient.Update(ctx, mutateConfig, metav1.UpdateOptions{}); err != nil {
				return err
			}
		}

	}
	return nil
}

dockerfile

# Build the webhook binary
FROM golang:1.19 as builder

RUN apt-get -y update && apt-get -y install upx

WORKDIR /workspace
# Copy the Go Modules manifests
COPY go.mod go.mod
COPY go.sum go.sum

# Copy the go source
COPY main.go main.go

# Build
ENV CGO_ENABLED=0
ENV GOOS=linux
ENV GOARCH=amd64
ENV GO111MODULE=on
ENV GOPROXY="https://goproxy.cn"

# cache deps before building and copying source so that we don't need to re-download as much
# and so that source changes don't invalidate our downloaded layer
RUN go mod download && \
    go build -a -o admission-registry main.go && \
    upx admission-registry

FROM alpine:3.9.2
COPY --from=builder /workspace/admission-registry .
ENTRYPOINT ["/admission-registry"]

基于rbac的initcontainer创建证书,跟主容器共享一个目录,使用deployment部署

apiVersion: v1
kind: ServiceAccount
metadata:
  name: admission-registry-sa
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: admission-registry-role
rules:
- verbs: ["*"]
  resources: ["validatingwebhookconfigurations", "mutatingwebhookconfigurations"]
  apiGroups: ["admissionregistration.k8s.io"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admission-registry-rolebinding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: admission-registry-role
subjects:
- kind: ServiceAccount
  name: admission-registry-sa
  namespace: default

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: admission-registry
  labels:
    app: admission-registry
spec:
  selector:
    matchLabels:
      app: admission-registry
  template:
    metadata:
      labels:
        app: admission-registry
    spec:
      serviceAccountName: admission-registry-sa
      initContainers:
      - image: webhook-tls-all-in-one:v1.0
        imagePullPolicy: IfNotPresent
        name: webhook-init
        env:
        - name: WEBHOOK_NAMESPACE
          value: default
        - name: MUTATE_CONFIG
          value: admission-registry-mutate
        - name: VALIDATE_CONFIG
          value: admission-registry
        - name: WEBHOOK_SERVICE
          value: admission-registry
        - name: VALIDATE_PATH
          value: /validate
        - name: MUTATE_PATH
          value: /mutate
        volumeMounts:
          - mountPath: /etc/webhook/certs
            name: webhook-certs
      containers:
      - name: webhook
        image: webhook-validate-mutate:v1.0
        imagePullPolicy: IfNotPresent
        env:
        - name: WHITELIST_REGISTRIES
          value: "docker.io,gcr.io"
        ports:
        - containerPort: 443
        volumeMounts:
        - name: webhook-certs
          mountPath: /etc/webhook/certs
          readOnly: true
      volumes:
        - name: webhook-certs
          emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: admission-registry
  labels:
    app: admission-registry
spec:
  ports: 
    - port: 443
      targetPort: 443
  selector: 
    app: admission-registry

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Neo4j中添加一个节点,可以使用Neo4j的官方Go驱动程序。 首先,我们需要导入以下包: ```go import ( "github.com/neo4j/neo4j-go-driver/v4/neo4j" "log" ) ``` 然后,我们需要创建一个Neo4j驱动程序,用于连接到Neo4j数据库: ```go driver, err := neo4j.NewDriver("neo4j://localhost:7687", neo4j.BasicAuth("username", "password", "")) if err != nil { log.Fatal(err) } defer driver.Close() ``` 请注意,此代码假设您已经安装并运行了Neo4j数据库,并将其连接到了localhost:7687。 接下来,我们可以使用以下代码向Neo4j中添加一个节点: ```go session := driver.NewSession(neo4j.SessionConfig{}) defer session.Close() result, err := session.WriteTransaction(func(transaction neo4j.Transaction) (interface{}, error) { result, err := transaction.Run( "CREATE (n:Person {name: $name, age: $age}) RETURN n", map[string]interface{}{ "name": "John Doe", "age": 30, }) if err != nil { return nil, err } if result.Next() { return result.Record().GetByIndex(0), nil } return nil, result.Err() }) if err != nil { log.Fatal(err) } node := result.(neo4j.Node) fmt.Printf("Created node with ID %d and labels %v\n", node.Id(), node.Labels()) ``` 这将创建一个名为“John Doe”的Person节点,该节点具有一个名为“name”的属性和一个名为“age”的属性。 请注意,此代码使用WriteTransaction方法来启动一个写事务,并将节点添加到事务中。如果事务成功提交,将返回一个包含新节点的结果。 最后,我们可以使用node.Id()和node.Labels()方法获取新节点的ID和标签。 完整的示例代码如下: ```go package main import ( "fmt" "github.com/neo4j/neo4j-go-driver/v4/neo4j" "log" ) func main() { driver, err := neo4j.NewDriver("neo4j://localhost:7687", neo4j.BasicAuth("username", "password", "")) if err != nil { log.Fatal(err) } defer driver.Close() session := driver.NewSession(neo4j.SessionConfig{}) defer session.Close() result, err := session.WriteTransaction(func(transaction neo4j.Transaction) (interface{}, error) { result, err := transaction.Run( "CREATE (n:Person {name: $name, age: $age}) RETURN n", map[string]interface{}{ "name": "John Doe", "age": 30, }) if err != nil { return nil, err } if result.Next() { return result.Record().GetByIndex(0), nil } return nil, result.Err() }) if err != nil { log.Fatal(err) } node := result.(neo4j.Node) fmt.Printf("Created node with ID %d and labels %v\n", node.Id(), node.Labels()) } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值