了解apiserver(一)

apiserver主要做的就是认证鉴权准入,它还允许在接受之前可以修改请求,这叫mutating,然后支持对修改后的请求做validating鉴别,最后做admission准入,它还支持限流,apiserver是控制面的一个组件,它提供集群的rest api接口,包括认证授权准入以及集群状态变更等,还是各个组件通信的中央枢纽只有apiserver才能操作etcd,下面图片总体概括了apiserver的工作流程,首先肯定要有一个http handler,然后经过认证授权,在准入的时候apiserver支持用户以一种插件的形式自己增加修改请求的webhook,然后apiserver自己有 object schema validation做检验,然后还支持用户自己定义validation插件做校验
在这里插入图片描述下图是一个更细的流程
在这里插入图片描述认证插件
• X509证书
• 使用X509客户端证书只需要API Server启动时配置–client-ca-file=SOMEFILE。在证书认证时,其CN域用作用户名,而组织机构域则用作group名。

• 静态Token文件
• 使用静态Token文件认证只需要API Server启动时配置–token-auth-file=SOMEFILE。
• 该文件为csv格式,每行至少包括三列token,username,userid,token,user,uid,"group1,group2,group3”

• 引导Token
• 为了支持平滑地启动引导新的集群,Kubernetes 包含了一种动态管理的持有者令牌类型, 称作 启动引导令牌(Bootstrap
Token)。
• 这些令牌以 Secret 的形式保存在 kube-system 名字空间中,可以被动态管理和创建。
• 控制器管理器包含的 TokenCleaner 控制器能够在启动引导令牌过期时将其删除。
• 在使用kubeadm部署Kubernetes时,可通过kubeadm token list命令查询。

• 静态密码文件
• 需要API Server启动时配置–basic-auth-file=SOMEFILE,文件格式为csv,每行至少三列password, user, uid,后面是可选的group password,user,uid,"group1,group2,group3”

• ServiceAccount
• ServiceAccount是Kubernetes自动生成的,并会自动挂载到容器的/ru/secrets/kubernetes.io/serviceaccount目录中。

• OpenID
• OAuth 2.0的认证机制
• Webhook 令牌身份认证
• --authentication-token-webhook-config-file 指向一个配置文件,其中描述 何访问远程的 Webhook 服务。
• --authentication-token-webhook-cache-ttl 用来设定身份认证决定的缓存间。 默认时长为 2 分钟。

• 匿名请求
• 如果使用AlwaysAllow以外的认证模式,则匿名请求默认开启,但可用–anonymous-auth=false禁止匿名请求。

证书认证方式,二进制安装kubernetes的时候生成kube.config文件的方式

生成证书
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=caconfig.json -profile=kubernetes admin-csr.json | cfssljson -bare admin
设置集群参数
 config set-cluster kubernetes --certificateauthority=ca.pem --embed-certs=true --server=https://192.168.40.180:6443 --kubeconfig=kube.config
 设置客户端认证参数
 kubectl config set-credentials admin --clientcertificate=admin.pem --client-key=admin-key.pem --embed-certs=true --kubeconfig=kube.config
 设置上下文
 kubectl config set-context kubernetes --cluster=kubernetes --user=admin --kubeconfig=kube.config
 kubectl config use-context kubernetes --kubeconfig=kube.config
 授权
  kubectl create clusterrolebinding kube-apiserver:kubeletapis --clusterrole=system:kubelet-api-admin --user kubernetes

静态token的方式

mkdir -p /etc/kubernetes/auth
[root@k8s-master1 manifests]# cat /etc/kubernetes/auth/static-token 
sasa-token,sasa,1000,"group1,group2,group3"
cp static-token /etc/kubernetes/auth
cp ./kube-apiserver.yaml /etc/kubernetes/manifests/kube-apiserver.yaml
curl https://192.168.34.2:6443/api/v1/namespaces/default -H "Authorization: Bearer cncamp-token" -k

引导tokenn

kubeadm token list

下面我们以webhook的插件方式增加认证方式

package main

import (
	"context"
	"encoding/json"
	"log"
	"net/http"

	"github.com/google/go-github/github"
	"golang.org/x/oauth2"
	authentication "k8s.io/api/authentication/v1beta1"
)

func main() {
	http.HandleFunc("/authenticate", func(w http.ResponseWriter, r *http.Request) {
		decoder := json.NewDecoder(r.Body)
		var tr authentication.TokenReview
		err := decoder.Decode(&tr)
		if err != nil {
			log.Println("[Error]", err.Error())
			w.WriteHeader(http.StatusBadRequest)
			json.NewEncoder(w).Encode(map[string]interface{}{
				"apiVersion": "authentication.k8s.io/v1beta1",
				"kind":       "TokenReview",
				"status": authentication.TokenReviewStatus{
					Authenticated: false,
				},
			})
			return
		}
		log.Print("receving request")

		ts := oauth2.StaticTokenSource(
			&oauth2.Token{AccessToken: tr.Spec.Token},
		)

		tc := oauth2.NewClient(context.Background(), ts)
		client := github.NewClient(tc)

		user, _, err := client.Users.Get(context.Background(), "")

		if err != nil {
			log.Println("[Error]", err.Error())
			w.WriteHeader(http.StatusUnauthorized)
			json.NewEncoder(w).Encode(map[string]interface{}{
				"apiVersion": "authentication.k8s.io/v1beta1",
				"kind":       "TokenReview",
				"status": authentication.TokenReviewStatus{
					Authenticated: false,
				},
			})
			return
		}

		log.Printf("[Success] login as %s ", *user.Login)
		w.WriteHeader(http.StatusOK)

		trs := authentication.TokenReviewStatus{
			Authenticated: true,
			User: authentication.UserInfo{
				Username: *user.Login,
				UID:      *user.Login,
			},
		}
		json.NewEncoder(w).Encode(map[string]interface{}{
			"apiVersion": "authentication.k8s.io/v1beta1",
			"kind":       "TokenReview",
			"status":     trs,
		})
	})
	log.Println(http.ListenAndServe(":3000", nil))
}

mkdir -p /etc/config
[root@k8s-master1 ~]# cat /etc/config/config.json 
{
  "kind": "Config",
  "apiVersion": "v1",
  "preferences": {},
  "clusters": [
    {
      "name": "github-authn",
      "cluster": {
        "server": "http://192.168.150.24:3000/authenticate"
      }
    }
  ],
  "users": [
    {
      "name": "authn-apiserver",
      "user": {
        "token": "secret"
      }
    }
  ],
  "contexts": [
    {
      "name": "webhook",
      "context": {
        "cluster": "github-authn",
        "user": "authn-apiserver"
      }
    }
  ],
  "current-context": "webhook"
}

cp webhook-config.json /etc/config

修改apiserver的yaml,加上

- --authentication-token-webhook-config=/etc/config/config.json

token是从github上申请的

vi ~/.kube/config

- name: mfanjie
  user:
    token: <mytoken>

apiserver通用的授权方式就是RBAC

Role:角色,它其实是一组规则,定义了一组对 Kubernetes API 对象的操作权限。
Subject:被作用者,既可以是“人”,也可以是“机器”,也可以是你在 Kubernetes 里定义的“用户”。
RoleBinding:定义了“被作用者”和“角色”的绑定关系。

下面我们定义一个role

[root@k8s-master1 ~]# cat role.yaml 
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: myname
  name: sasa-role
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get","watch","list"]

创建service account


apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: mynamespace
  name: example-sa

创建rolebinding

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: example-rolebinding
  namespace: myname
subjects:
- kind: ServiceAccount
  name: example-sa
  namespace: myname
roleRef:
  kind: Role
  name: sasa-role
  apiGroup: rbac.authorization.k8s.io

创建pod使用这个serviceaccount

apiVersion: v1
kind: Pod
metadata: 
  namespace: myname
  name: my-pod
spec: 
  containers: 
  - name: nginx
    image: nginx:latest
    imagePullPolicy: IfNotPresent
  serviceAccountName: example-sa

在这里插入图片描述在这里插入图片描述实际上,一个 ServiceAccount,在 Kubernetes 里对应的“用户”的名字是

system:serviceaccount:<namespace>:<serviceaccount>

用户组的名字

system:serviceaccount:<namespace>

比如,现在我们可以在 RoleBinding 里定义如下的 subjects

subject:
- kind: group
  name: system:serviceaccount:myname
  apiGroup: rbac.authorization.k8s.io

这就意味着这个 Role 的权限规则,作用于 mynamespace 里的所有 ServiceAccount。这就用到了“用户组”的概念。
而下面这个例子:

subject:
- kind: group
  name: system:serviceaccount
  apiGroup: rbac.authorization.k8s.io

就意味着这个 Role 的权限规则,作用于整个系统里的所有 ServiceAccount。

除此之外,Kubernetes 还提供了四个预先定义好的 ClusterRole 来供用户直接使用:
cluster-admin;
admin;
edit;
view
通过它们的名字,你应该能大致猜出它们都定义了哪些权限。比如,这个名叫 view 的 ClusterRole,就规定了被作用者只有 Kubernetes API 的只读权限

在cluster创建时,创建自定义的role,比如namespace-creator
Namespace-creator role定义用户可操作的对象和对应的读写操作。
创建自定义的namespace admission controller
• 当namespace创建请求被处理时,获取当前用户信息并annotate到namespace
创建RBAC controller
• Watch namespace的创建事件
• 获取当前namespace的创建者信息
• 在当前namespace创建rolebinding对象,并将namespace-creator 角色和用户绑定

当多个团队、多个用户共享使用K8s集群时,会出现不均匀资源使用,默认情况下先到先得,这时可以通过ResourceQuota来对命名空间资源使用总量做限制,从而解决这个问题。

使用流程:k8s管理员为每个命名空间创建一个或多个ResourceQuota对象,定义资源使用总量,K8s会跟踪命名空间资源使用情况,当超过定义的资源配额会返回拒绝。

apiVersion: v1
kind: ResourceQuota
metadata:
  name: my-cheap-namespace
spec:
  hard:
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "16"
    limits.memory: 16Gi
准入控制插件
AlwaysAdmit: 接受所有请求。
AlwaysPullImages: 总是拉取最新镜像。在多租户场景下非常有用。
DenyEscalatingExec: 禁止特权容器的exec和attach操作。
ImagePolicyWebhook: 通过webhook决定image策略,需要同时配置--admission-controlconfig-file
ServiceAccount:自动创建默认ServiceAccount,并确保Pod引用的ServiceAccount已经存在
SecurityContextDeny:拒绝包含非法SecurityContext配置的容器
ResourceQuota:限制Pod的请求不会超过配额,需要在namespace中创建一个ResourceQuota对象
LimitRanger:为Pod设置默认资源请求和限制,需要在namespace中创建一个LimitRange对象
InitialResources:根据镜像的历史使用记录,为容器设置默认资源请求和限制
NamespaceLifecycle:确保处于termination状态的namespace不再接收新的对象创建请求,并拒绝请求不存在的namespace
DefaultStorageClass:为PVC设置默认StorageClass
DefaultTolerationSeconds:设置Pod的默认forgiveness toleration为5分钟
PodSecurityPolicy:使用Pod Security Policies时必须开启
NodeRestriction:限制kubelet仅可访问node、endpoint、pod、service以及secret、
configmap、PV和PVC等相关的资源

admission插件代码如下

/*
Copyright (c) 2019 StackRox Inc.

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 main

import (
	"errors"
	"fmt"
	"log"
	"net/http"
	"path/filepath"

	admission "k8s.io/api/admission/v1beta1"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	tlsDir      = `/run/secrets/tls`
	tlsCertFile = `tls.crt`
	tlsKeyFile  = `tls.key`
)

var (
	podResource = metav1.GroupVersionResource{Version: "v1", Resource: "pods"}
)

// applySecurityDefaults implements the logic of our example admission controller webhook. For every pod that is created
// (outside of Kubernetes namespaces), it first checks if `runAsNonRoot` is set. If it is not, it is set to a default
// value of `false`. Furthermore, if `runAsUser` is not set (and `runAsNonRoot` was not initially set), it defaults
// `runAsUser` to a value of 1234.
//
// To demonstrate how requests can be rejected, this webhook further validates that the `runAsNonRoot` setting does
// not conflict with the `runAsUser` setting - i.e., if the former is set to `true`, the latter must not be `0`.
// Note that we combine both the setting of defaults and the check for potential conflicts in one webhook; ideally,
// the latter would be performed in a validating webhook admission controller.
func applySecurityDefaults(req *admission.AdmissionRequest) ([]patchOperation, error) {
	// This handler should only get called on Pod objects as per the MutatingWebhookConfiguration in the YAML file.
	// However, if (for whatever reason) this gets invoked on an object of a different kind, issue a log message but
	// let the object request pass through otherwise.
	if req.Resource != podResource {
		log.Printf("expect resource to be %s", podResource)
		return nil, nil
	}

	// Parse the Pod object.
	raw := req.Object.Raw
	pod := corev1.Pod{}
	if _, _, err := universalDeserializer.Decode(raw, nil, &pod); err != nil {
		return nil, fmt.Errorf("could not deserialize pod object: %v", err)
	}

	// Retrieve the `runAsNonRoot` and `runAsUser` values.
	var runAsNonRoot *bool
	var runAsUser *int64
	if pod.Spec.SecurityContext != nil {
		runAsNonRoot = pod.Spec.SecurityContext.RunAsNonRoot
		runAsUser = pod.Spec.SecurityContext.RunAsUser
	}

	// Create patch operations to apply sensible defaults, if those options are not set explicitly.
	var patches []patchOperation
	if runAsNonRoot == nil {
		patches = append(patches, patchOperation{
			Op:   "add",
			Path: "/spec/securityContext/runAsNonRoot",
			// The value must not be true if runAsUser is set to 0, as otherwise we would create a conflicting
			// configuration ourselves.
			Value: runAsUser == nil || *runAsUser != 0,
		})

		if runAsUser == nil {
			patches = append(patches, patchOperation{
				Op:    "add",
				Path:  "/spec/securityContext/runAsUser",
				Value: 1234,
			})
		}
	} else if *runAsNonRoot == true && (runAsUser != nil && *runAsUser == 0) {
		// Make sure that the settings are not contradictory, and fail the object creation if they are.
		return nil, errors.New("runAsNonRoot specified, but runAsUser set to 0 (the root user)")
	}

	return patches, nil
}

func main() {
	certPath := filepath.Join(tlsDir, tlsCertFile)
	keyPath := filepath.Join(tlsDir, tlsKeyFile)

	mux := http.NewServeMux()
	mux.Handle("/mutate", admitFuncHandler(applySecurityDefaults))
	server := &http.Server{
		// We listen on port 8443 such that we do not need root privileges or extra capabilities for this server.
		// The Service object will take care of mapping this port to the HTTPS port 443.
		Addr:    ":8443",
		Handler: mux,
	}
	log.Fatal(server.ListenAndServeTLS(certPath, keyPath))
}

/*
Copyright (c) 2019 StackRox Inc.

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 main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"

	admission "k8s.io/api/admission/v1beta1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/serializer"
)

const (
	jsonContentType = `application/json`
)

var (
	universalDeserializer = serializer.NewCodecFactory(runtime.NewScheme()).UniversalDeserializer()
)

// patchOperation is an operation of a JSON patch, see https://tools.ietf.org/html/rfc6902 .
type patchOperation struct {
	Op    string      `json:"op"`
	Path  string      `json:"path"`
	Value interface{} `json:"value,omitempty"`
}

// admitFunc is a callback for admission controller logic. Given an AdmissionRequest, it returns the sequence of patch
// operations to be applied in case of success, or the error that will be shown when the operation is rejected.
type admitFunc func(*admission.AdmissionRequest) ([]patchOperation, error)

// isKubeNamespace checks if the given namespace is a Kubernetes-owned namespace.
func isKubeNamespace(ns string) bool {
	return ns == metav1.NamespacePublic || ns == metav1.NamespaceSystem
}

// doServeAdmitFunc parses the HTTP request for an admission controller webhook, and -- in case of a well-formed
// request -- delegates the admission control logic to the given admitFunc. The response body is then returned as raw
// bytes.
func doServeAdmitFunc(w http.ResponseWriter, r *http.Request, admit admitFunc) ([]byte, error) {
	// Step 1: Request validation. Only handle POST requests with a body and json content type.

	if r.Method != http.MethodPost {
		w.WriteHeader(http.StatusMethodNotAllowed)
		return nil, fmt.Errorf("invalid method %s, only POST requests are allowed", r.Method)
	}

	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return nil, fmt.Errorf("could not read request body: %v", err)
	}

	if contentType := r.Header.Get("Content-Type"); contentType != jsonContentType {
		w.WriteHeader(http.StatusBadRequest)
		return nil, fmt.Errorf("unsupported content type %s, only %s is supported", contentType, jsonContentType)
	}

	// Step 2: Parse the AdmissionReview request.

	var admissionReviewReq admission.AdmissionReview

	if _, _, err := universalDeserializer.Decode(body, nil, &admissionReviewReq); err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return nil, fmt.Errorf("could not deserialize request: %v", err)
	} else if admissionReviewReq.Request == nil {
		w.WriteHeader(http.StatusBadRequest)
		return nil, errors.New("malformed admission review: request is nil")
	}

	// Step 3: Construct the AdmissionReview response.

	admissionReviewResponse := admission.AdmissionReview{
		// Since the admission webhook now supports multiple API versions, we need
		// to explicitly include the API version in the response.
		// This API version needs to match the version from the request exactly, otherwise
		// the API server will be unable to process the response.
		// Note: a v1beta1 AdmissionReview is JSON-compatible with the v1 version, that's why
		// we do not need to differentiate during unmarshaling or in the actual logic.
		TypeMeta: metav1.TypeMeta{
			Kind: "AdmissionReview",
			APIVersion: "admission.k8s.io/v1",
		},
		Response: &admission.AdmissionResponse{
			UID: admissionReviewReq.Request.UID,
		},
	}

	var patchOps []patchOperation
	// Apply the admit() function only for non-Kubernetes namespaces. For objects in Kubernetes namespaces, return
	// an empty set of patch operations.
	if !isKubeNamespace(admissionReviewReq.Request.Namespace) {
		patchOps, err = admit(admissionReviewReq.Request)
	}

	if err != nil {
		// If the handler returned an error, incorporate the error message into the response and deny the object
		// creation.
		admissionReviewResponse.Response.Allowed = false
		admissionReviewResponse.Response.Result = &metav1.Status{
			Message: err.Error(),
		}
	} else {
		// Otherwise, encode the patch operations to JSON and return a positive response.
		patchBytes, err := json.Marshal(patchOps)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return nil, fmt.Errorf("could not marshal JSON patch: %v", err)
		}
		admissionReviewResponse.Response.Allowed = true
		admissionReviewResponse.Response.Patch = patchBytes

		// Announce that we are returning a JSON patch (note: this is the only
		// patch type currently supported, but we have to explicitly announce
		// it nonetheless).
		admissionReviewResponse.Response.PatchType = new(admission.PatchType)
		*admissionReviewResponse.Response.PatchType = admission.PatchTypeJSONPatch
	}

	// Return the AdmissionReview with a response as JSON.
	bytes, err := json.Marshal(&admissionReviewResponse)
	if err != nil {
		return nil, fmt.Errorf("marshaling response: %v", err)
	}
	return bytes, nil
}

// serveAdmitFunc is a wrapper around doServeAdmitFunc that adds error handling and logging.
func serveAdmitFunc(w http.ResponseWriter, r *http.Request, admit admitFunc) {
	log.Print("Handling webhook request ...")

	var writeErr error
	if bytes, err := doServeAdmitFunc(w, r, admit); err != nil {
		log.Printf("Error handling webhook request: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		_, writeErr = w.Write([]byte(err.Error()))
	} else {
		log.Print("Webhook request handled successfully")
		_, writeErr = w.Write(bytes)
	}

	if writeErr != nil {
		log.Printf("Could not write response: %v", writeErr)
	}
}

// admitFuncHandler takes an admitFunc and wraps it into a http.Handler by means of calling serveAdmitFunc.
func admitFuncHandler(admit admitFunc) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		serveAdmitFunc(w, r, admit)
	})
}

deployment,service,MutatingWebhookConfiguration,定义如下

apiVersion: apps/v1
kind: Deployment
metadata:
  name: webhook-server
  namespace: webhook-demo
  labels:
    app: webhook-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: webhook-server
  template:
    metadata:
      labels:
        app: webhook-server
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 1234
      containers:
      - name: server
        image: cncamp/admission-controller-webhook-demo:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 8443
          name: webhook-api
        volumeMounts:
        - name: webhook-tls-certs
          mountPath: /run/secrets/tls
          readOnly: true
      volumes:
      - name: webhook-tls-certs
        secret:
          secretName: webhook-server-tls
---
apiVersion: v1
kind: Service
metadata:
  name: webhook-server
  namespace: webhook-demo
spec:
  selector:
    app: webhook-server
  ports:
    - port: 443
      targetPort: webhook-api
---
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: demo-webhook
webhooks:
  - name: webhook-server.webhook-demo.svc
    sideEffects: None
    admissionReviewVersions: ["v1", "v1beta1"]
    clientConfig:
      service:
        name: webhook-server
        namespace: webhook-demo
        path: "/mutate"
      caBundle: ${CA_PEM_B64}
    rules:
      - operations: [ "CREATE" ]
        apiGroups: [""]
        apiVersions: ["v1"]
        resources: ["pods"]

dockerfile如下,就是cncamp/admission-controller-webhook-demo镜像

FROM scratch

COPY ./webhook-server /
ENTRYPOINT ["/webhook-server"]

运行脚本如下

set -euo pipefail

basedir="$(dirname "$0")/deployment"
keydir="$(mktemp -d)"

# Generate keys into a temporary directory.
echo "Generating TLS keys ..."
"${basedir}/generate-keys.sh" "$keydir"

# Create the `webhook-demo` namespace. This cannot be part of the YAML file as we first need to create the TLS secret,
# which would fail otherwise.
echo "Creating Kubernetes objects ..."
kubectl create namespace webhook-demo

# Create the TLS secret for the generated keys.
kubectl -n webhook-demo create secret tls webhook-server-tls \
    --cert "${keydir}/webhook-server-tls.crt" \
    --key "${keydir}/webhook-server-tls.key"

# Read the PEM-encoded CA certificate, base64 encode it, and replace the `${CA_PEM_B64}` placeholder in the YAML
# template with it. Then, create the Kubernetes resources.
ca_pem_b64="$(openssl base64 -A <"${keydir}/ca.crt")"
sed -e 's@${CA_PEM_B64}@'"$ca_pem_b64"'@g' <"${basedir}/deployment.yaml.template" \
    | kubectl create -f -

# Delete the key directory to prevent abuse (DO NOT USE THESE KEYS ANYWHERE ELSE).
rm -rf "$keydir"

echo "The webhook server has been deployed and configured!"

密钥生成脚本如下

: ${1?'missing key directory'}

key_dir="$1"

chmod 0700 "$key_dir"
cd "$key_dir"

cat >server.conf <<EOF
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
prompt = no
[req_distinguished_name]
CN = webhook-server.webhook-demo.svc
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth, serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = webhook-server.webhook-demo.svc
EOF


# Generate the CA cert and private key
openssl req -nodes -new -x509 -keyout ca.key -out ca.crt -subj "/CN=Admission Controller Webhook Demo CA"
# Generate the private key for the webhook server
openssl genrsa -out webhook-server-tls.key 2048
# Generate a Certificate Signing Request (CSR) for the private key, and sign it with the private key of the CA.
openssl req -new -key webhook-server-tls.key -subj "/CN=webhook-server.webhook-demo.svc" -config server.conf \
    | openssl x509 -req -CA ca.crt -CAkey ca.key -CAcreateserial -out webhook-server-tls.crt -extensions v3_req -extfile server.conf

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值