kubeconfig.go 8.3 KB
Newer Older
J
jeff 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
/*

 Copyright 2019 The KubeSphere Authors.

 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 kubeconfig

import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/base64"
	"encoding/pem"
H
hongming 已提交
29
	"fmt"
J
jeff 已提交
30
	"io/ioutil"
H
hongming 已提交
31
	"kubesphere.io/kubesphere/pkg/simple/client/k8s"
J
jeff 已提交
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
	"math/big"
	rd "math/rand"
	"time"

	"github.com/golang/glog"
	"gopkg.in/yaml.v2"
	metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	"k8s.io/apimachinery/pkg/api/errors"

	"k8s.io/api/core/v1"

	"kubesphere.io/kubesphere/pkg/constants"
)

const (
	caPath           = "/etc/kubernetes/pki/ca.crt"
	keyPath          = "/etc/kubernetes/pki/ca.key"
	clusterName      = "kubernetes"
	kubectlConfigKey = "config"
	defaultNamespace = "default"
)

type clusterInfo struct {
	CertificateAuthorityData string `yaml:"certificate-authority-data"`
	Server                   string `yaml:"server"`
}

type cluster struct {
	Cluster clusterInfo `yaml:"cluster"`
	Name    string      `yaml:"name"`
}

type contextInfo struct {
	Cluster   string `yaml:"cluster"`
	User      string `yaml:"user"`
	NameSpace string `yaml:"namespace"`
}

type contextObject struct {
	Context contextInfo `yaml:"context"`
	Name    string      `yaml:"name"`
}

type userInfo struct {
	CaData  string `yaml:"client-certificate-data"`
	KeyData string `yaml:"client-key-data"`
}

type user struct {
	Name string   `yaml:"name"`
	User userInfo `yaml:"user"`
}

type kubeConfig struct {
	ApiVersion     string            `yaml:"apiVersion"`
	Clusters       []cluster         `yaml:"clusters"`
	Contexts       []contextObject   `yaml:"contexts"`
	CurrentContext string            `yaml:"current-context"`
	Kind           string            `yaml:"kind"`
	Preferences    map[string]string `yaml:"preferences"`
	Users          []user            `yaml:"users"`
}

type CertInformation struct {
	Country            []string
	Organization       []string
	OrganizationalUnit []string
	EmailAddress       []string
	Province           []string
	Locality           []string
	CommonName         string
	CrtName, KeyName   string
	IsCA               bool
	Names              []pkix.AttributeTypeAndValue
}

func createCRT(RootCa *x509.Certificate, RootKey *rsa.PrivateKey, info CertInformation) ([]byte, []byte, error) {
	var cert, key bytes.Buffer
	Crt := newCertificate(info)
	Key, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		glog.Error(err)
		return nil, nil, err
	}

	var buf []byte

	buf, err = x509.CreateCertificate(rand.Reader, Crt, RootCa, &Key.PublicKey, RootKey)

	if err != nil {
		glog.Error(err)
		return nil, nil, err
	}
	pem.Encode(&cert, &pem.Block{Type: "CERTIFICATE", Bytes: buf})

	if err != nil {
		glog.Error(err)
		return nil, nil, err
	}

	buf = x509.MarshalPKCS1PrivateKey(Key)
	pem.Encode(&key, &pem.Block{Type: "PRIVATE KEY", Bytes: buf})

	return cert.Bytes(), key.Bytes(), nil
}

func Parse(crtPath, keyPath string) (rootcertificate *x509.Certificate, rootPrivateKey *rsa.PrivateKey, err error) {
	rootcertificate, err = parseCrt(crtPath)
	if err != nil {
		glog.Error(err)
		return nil, nil, err
	}
	rootPrivateKey, err = parseKey(keyPath)
	return rootcertificate, rootPrivateKey, nil
}

func parseCrt(path string) (*x509.Certificate, error) {
	buf, err := ioutil.ReadFile(path)
	if err != nil {
		glog.Error(err)
		return nil, err
	}
	p := &pem.Block{}
	p, buf = pem.Decode(buf)
	return x509.ParseCertificate(p.Bytes)
}

func parseKey(path string) (*rsa.PrivateKey, error) {
	buf, err := ioutil.ReadFile(path)
	if err != nil {
		glog.Error(err)
		return nil, err
	}
	p, buf := pem.Decode(buf)
	return x509.ParsePKCS1PrivateKey(p.Bytes)
}

func newCertificate(info CertInformation) *x509.Certificate {
	rd.Seed(time.Now().UnixNano())
	return &x509.Certificate{
		SerialNumber: big.NewInt(rd.Int63()),
		Subject: pkix.Name{
			Country:            info.Country,
			Organization:       info.Organization,
			OrganizationalUnit: info.OrganizationalUnit,
			Province:           info.Province,
			CommonName:         info.CommonName,
			Locality:           info.Locality,
			ExtraNames:         info.Names,
		},
		NotBefore:             time.Now(),
		NotAfter:              time.Now().AddDate(20, 0, 0),
		BasicConstraintsValid: true,
		IsCA:                  info.IsCA,
		ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
		KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		EmailAddresses:        info.EmailAddress,
	}
}

func generateCaAndKey(user, caPath, keyPath string) (string, string, error) {
	crtInfo := CertInformation{CommonName: user, IsCA: false}

	crt, pri, err := Parse(caPath, keyPath)
	if err != nil {
		glog.Error(err)
		return "", "", err
	}
	cert, key, err := createCRT(crt, pri, crtInfo)
	if err != nil {
		glog.Error(err)
		return "", "", err
	}

	base64Cert := base64.StdEncoding.EncodeToString(cert)
	base64Key := base64.StdEncoding.EncodeToString(key)
	return base64Cert, base64Key, nil
}

H
hongming 已提交
212
func createKubeConfig(username string) (string, error) {
J
jeff 已提交
213 214 215 216 217 218 219
	tmpKubeConfig := kubeConfig{ApiVersion: "v1", Kind: "Config"}
	serverCa, err := ioutil.ReadFile(caPath)
	if err != nil {
		glog.Errorln(err)
		return "", err
	}
	base64ServerCa := base64.StdEncoding.EncodeToString(serverCa)
H
hongming 已提交
220
	tmpClusterInfo := clusterInfo{CertificateAuthorityData: base64ServerCa, Server: k8s.MasterURL}
J
jeff 已提交
221 222 223
	tmpCluster := cluster{Cluster: tmpClusterInfo, Name: clusterName}
	tmpKubeConfig.Clusters = append(tmpKubeConfig.Clusters, tmpCluster)

H
hongming 已提交
224 225
	contextName := username + "@" + clusterName
	tmpContext := contextObject{Context: contextInfo{User: username, Cluster: clusterName, NameSpace: defaultNamespace}, Name: contextName}
J
jeff 已提交
226 227
	tmpKubeConfig.Contexts = append(tmpKubeConfig.Contexts, tmpContext)

H
hongming 已提交
228
	cert, key, err := generateCaAndKey(username, caPath, keyPath)
J
jeff 已提交
229 230 231 232 233

	if err != nil {
		return "", err
	}

H
hongming 已提交
234
	tmpUser := user{User: userInfo{CaData: cert, KeyData: key}, Name: username}
J
jeff 已提交
235 236 237 238 239 240 241 242 243 244 245
	tmpKubeConfig.Users = append(tmpKubeConfig.Users, tmpUser)
	tmpKubeConfig.CurrentContext = contextName

	config, err := yaml.Marshal(tmpKubeConfig)
	if err != nil {
		return "", err
	}

	return string(config), nil
}

H
hongming 已提交
246
func CreateKubeConfig(username string) error {
H
hongming 已提交
247
	k8sClient := k8s.Client()
H
hongming 已提交
248 249
	configName := fmt.Sprintf("kubeconfig-%s", username)
	_, err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(configName, metaV1.GetOptions{})
J
jeff 已提交
250 251

	if errors.IsNotFound(err) {
H
hongming 已提交
252
		config, err := createKubeConfig(username)
J
jeff 已提交
253 254 255 256 257 258
		if err != nil {
			glog.Errorln(err)
			return err
		}

		data := map[string]string{"config": string(config)}
H
hongming 已提交
259
		configMap := v1.ConfigMap{TypeMeta: metaV1.TypeMeta{Kind: "Configmap", APIVersion: "v1"}, ObjectMeta: metaV1.ObjectMeta{Name: configName}, Data: data}
J
jeff 已提交
260 261
		_, err = k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Create(&configMap)
		if err != nil && !errors.IsAlreadyExists(err) {
H
hongming 已提交
262
			glog.Errorf("create username %s's kubeConfig failed, reason: %v", username, err)
J
jeff 已提交
263 264 265 266 267 268 269 270
			return err
		}
	}

	return nil

}

H
hongming 已提交
271
func GetKubeConfig(username string) (string, error) {
H
hongming 已提交
272
	k8sClient := k8s.Client()
H
hongming 已提交
273 274
	configName := fmt.Sprintf("kubeconfig-%s", username)
	configMap, err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(configName, metaV1.GetOptions{})
J
jeff 已提交
275
	if err != nil {
H
hongming 已提交
276
		glog.Errorf("cannot get username %s's kubeConfig, reason: %v", username, err)
J
jeff 已提交
277 278 279 280 281
		return "", err
	}
	return configMap.Data[kubectlConfigKey], nil
}

H
hongming 已提交
282
func DelKubeConfig(username string) error {
H
hongming 已提交
283
	k8sClient := k8s.Client()
H
hongming 已提交
284 285
	configName := fmt.Sprintf("kubeconfig-%s", username)
	_, err := k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Get(configName, metaV1.GetOptions{})
J
jeff 已提交
286 287 288 289 290
	if errors.IsNotFound(err) {
		return nil
	}

	deletePolicy := metaV1.DeletePropagationBackground
H
hongming 已提交
291
	err = k8sClient.CoreV1().ConfigMaps(constants.KubeSphereControlNamespace).Delete(configName, &metaV1.DeleteOptions{PropagationPolicy: &deletePolicy})
J
jeff 已提交
292
	if err != nil {
H
hongming 已提交
293
		glog.Errorf("delete username %s's kubeConfig failed, reason: %v", username, err)
J
jeff 已提交
294 295 296 297
		return err
	}
	return nil
}