Merge pull request #995 from ibuildthecloud/master

Refactor tokens, bootstrap, and cli args
pull/1012/head
Erik Wilson 2019-11-04 14:24:00 -07:00 committed by GitHub
commit 5d342a3051
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
69 changed files with 2274 additions and 2593 deletions

2
go.mod
View File

@ -97,7 +97,7 @@ require (
github.com/opencontainers/runc v1.0.0-rc2.0.20190611121236-6cc515888830
github.com/pkg/errors v0.8.1
github.com/rakelkar/gonetsh v0.0.0-20190719023240-501daadcadf8 // indirect
github.com/rancher/dynamiclistener v0.1.1-0.20191010011134-8a2488bc860a
github.com/rancher/dynamiclistener v0.1.1-0.20191031022009-6224794ef3cb
github.com/rancher/helm-controller v0.2.2
github.com/rancher/kine v0.0.0-00010101000000-000000000000
github.com/rancher/remotedialer v0.2.0

10
go.sum
View File

@ -416,9 +416,8 @@ github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgo
github.com/knative/build v0.6.0/go.mod h1:/sU74ZQkwlYA5FwYDJhYTy61i/Kn+5eWfln2jDbw3Qo=
github.com/knative/pkg v0.0.0-20190514205332-5e4512dcb2ca/go.mod h1:7Ijfhw7rfB+H9VtosIsDYvZQ+qYTz7auK3fHW/5z4ww=
github.com/knative/serving v0.6.1/go.mod h1:ljvMfwQy2qanaM/8xnBSK4Mz3Vv2NawC2fo5kFRJS1A=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@ -563,8 +562,8 @@ github.com/rancher/cri v1.3.0-k3s.2 h1:k2XFyD+ZdsGvNfugdvqD38KSMANT3JmTFULFM2CtI
github.com/rancher/cri v1.3.0-k3s.2/go.mod h1:Ht5T1dIKzm+4NExmb7wDVG6qR+j0xeXIjjhCv1d9geY=
github.com/rancher/cri-tools v1.16.1-k3s.1 h1:iporgQ46noE6dtLzq6fWcIO2qjyPZy2m42d2P+UnGJg=
github.com/rancher/cri-tools v1.16.1-k3s.1/go.mod h1:TEKhKv2EJIZp+p9jnEy4C63g8CosJzsI4kyKKkHag+8=
github.com/rancher/dynamiclistener v0.1.1-0.20191010011134-8a2488bc860a h1:1bUYAv5U/Ky4YJ9o8gWxX+vNcjpIL3JWNBao70OlkFE=
github.com/rancher/dynamiclistener v0.1.1-0.20191010011134-8a2488bc860a/go.mod h1:8hbGf35mB7ormKEFqsAgjgeI5rLbj5N764jG41dNhps=
github.com/rancher/dynamiclistener v0.1.1-0.20191031022009-6224794ef3cb h1:bMoA9UHr1QNTWVrf0fSJCba6YDU1xmt2jmeohpiugKg=
github.com/rancher/dynamiclistener v0.1.1-0.20191031022009-6224794ef3cb/go.mod h1:fs/dxyNcB3YT6W9fVz4bDGfhmSQS17QQup6BIcGF++s=
github.com/rancher/flannel v0.11.0-k3s.1 h1:mIwnfWDafjzQgFkZeJ1AkFrrAT3EdBaA1giE0eLJKo8=
github.com/rancher/flannel v0.11.0-k3s.1/go.mod h1:Hn4ZV+eq0LhLZP63xZnxdGwXEoRSxs5sxELxu27M3UA=
github.com/rancher/helm-controller v0.2.2 h1:MUqisy53/Ay1EYOF2uTCYBbGpgtZLNKKrI01BdxIbQo=
@ -682,7 +681,6 @@ github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRci
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stripe/safesql v0.2.0/go.mod h1:q7b2n0JmzM1mVGfcYpanfVb2j23cXZeWFxcILPn3JV4=
github.com/syndtr/gocapability v0.0.0-20160928074757-e7cb7fa329f4/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8 h1:zLV6q4e8Jv9EHjNg/iHfzwDkCve6Ua5jCygptrtXHvI=
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
@ -741,7 +739,6 @@ golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -808,7 +805,6 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190730183949-1393eb018365/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3 h1:7TYNF4UdlohbFwpNH04CoPMp1cHUZgO1Ebq5r2hIjfo=

View File

@ -10,3 +10,53 @@ subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: kube-apiserver
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: system:k3s-controller
rules:
- apiGroups:
- ""
resources:
- nodes
verbs:
- get
- apiGroups:
- ""
resources:
- namespaces
verbs:
- list
- watch
- apiGroups:
- "networking.k8s.io"
resources:
- networkpolicies
verbs:
- list
- watch
- apiGroups:
- ""
resources:
- endpoints
- pods
verbs:
- list
- get
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: system:k3s-controller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:k3s-controller
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: system:k3s-controller

View File

@ -6,7 +6,6 @@ import (
cryptorand "crypto/rand"
"crypto/tls"
"encoding/hex"
"encoding/pem"
"fmt"
"io/ioutil"
sysnet "net"
@ -27,7 +26,6 @@ import (
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/apimachinery/pkg/util/net"
"k8s.io/client-go/util/cert"
"k8s.io/kubernetes/pkg/kubelet/apis/deviceplugin/v1beta1"
)
@ -183,17 +181,6 @@ func getHostnameAndIP(info cmds.Agent) (string, string, error) {
return name, ip, nil
}
func writeKubeConfig(envInfo *cmds.Agent, info clientaccess.Info, tlsCert *tls.Certificate) (string, error) {
os.MkdirAll(envInfo.DataDir, 0700)
kubeConfigPath := filepath.Join(envInfo.DataDir, "kubeconfig.yaml")
info.CACerts = pem.EncodeToMemory(&pem.Block{
Type: cert.CertificateBlockType,
Bytes: tlsCert.Certificate[1],
})
return kubeConfigPath, info.WriteKubeConfig(kubeConfigPath)
}
func isValidResolvConf(resolvConfFile string) bool {
file, err := os.Open(resolvConfFile)
if err != nil {
@ -293,11 +280,6 @@ func get(envInfo *cmds.Agent) (*config.Node, error) {
return nil, err
}
kubeconfigNode, err := writeKubeConfig(envInfo, *info, servingCert)
if err != nil {
return nil, err
}
clientKubeletCert := filepath.Join(envInfo.DataDir, "client-kubelet.crt")
if err := getNodeNamedHostFile(clientKubeletCert, nodeName, nodePasswordFile, info); err != nil {
return nil, err
@ -328,6 +310,21 @@ func get(envInfo *cmds.Agent) (*config.Node, error) {
return nil, err
}
clientK3sControllerCert := filepath.Join(envInfo.DataDir, "client-k3s-controller.crt")
if err := getHostFile(clientK3sControllerCert, info); err != nil {
return nil, err
}
clientK3sControllerKey := filepath.Join(envInfo.DataDir, "client-k3s-controller.key")
if err := getHostFile(clientK3sControllerKey, info); err != nil {
return nil, err
}
kubeconfigK3sController := filepath.Join(envInfo.DataDir, "k3scontroller.kubeconfig")
if err := control.KubeConfig(kubeconfigK3sController, info.URL, serverCAFile, clientK3sControllerCert, clientK3sControllerKey); err != nil {
return nil, err
}
nodeConfig := &config.Node{
Docker: envInfo.Docker,
ContainerRuntimeEndpoint: envInfo.ContainerRuntimeEndpoint,
@ -345,9 +342,9 @@ func get(envInfo *cmds.Agent) (*config.Node, error) {
nodeConfig.AgentConfig.ResolvConf = locateOrGenerateResolvConf(envInfo)
nodeConfig.AgentConfig.ClientCA = clientCAFile
nodeConfig.AgentConfig.ListenAddress = "0.0.0.0"
nodeConfig.AgentConfig.KubeConfigNode = kubeconfigNode
nodeConfig.AgentConfig.KubeConfigKubelet = kubeconfigKubelet
nodeConfig.AgentConfig.KubeConfigKubeProxy = kubeconfigKubeproxy
nodeConfig.AgentConfig.KubeConfigK3sController = kubeconfigK3sController
if envInfo.Rootless {
nodeConfig.AgentConfig.RootDir = filepath.Join(envInfo.DataDir, "kubelet")
}

View File

@ -13,8 +13,7 @@ import (
"github.com/rancher/k3s/pkg/daemons/config"
"github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
v1 "k8s.io/client-go/kubernetes/typed/core/v1"
)
const (
@ -74,21 +73,11 @@ func Prepare(ctx context.Context, nodeConfig *config.Node) error {
return createFlannelConf(nodeConfig)
}
func Run(ctx context.Context, nodeConfig *config.Node) error {
func Run(ctx context.Context, nodeConfig *config.Node, nodes v1.NodeInterface) error {
nodeName := nodeConfig.AgentConfig.NodeName
restConfig, err := clientcmd.BuildConfigFromFlags("", nodeConfig.AgentConfig.KubeConfigNode)
if err != nil {
return err
}
client, err := kubernetes.NewForConfig(restConfig)
if err != nil {
return err
}
for {
node, err := client.CoreV1().Nodes().Get(nodeName, metav1.GetOptions{})
node, err := nodes.Get(nodeName, metav1.GetOptions{})
if err == nil && node.Spec.PodCIDR != "" {
break
}
@ -101,11 +90,11 @@ func Run(ctx context.Context, nodeConfig *config.Node) error {
}
go func() {
err := flannel(ctx, nodeConfig.FlannelIface, nodeConfig.FlannelConf, nodeConfig.AgentConfig.KubeConfigNode)
err := flannel(ctx, nodeConfig.FlannelIface, nodeConfig.FlannelConf, nodeConfig.AgentConfig.KubeConfigKubelet)
logrus.Fatalf("flannel exited: %v", err)
}()
return err
return nil
}
func createCNIConf(dir string) error {

View File

@ -10,7 +10,7 @@ import (
)
func Run(ctx context.Context, nodeConfig *config.Node) error {
restConfig, err := clientcmd.BuildConfigFromFlags("", nodeConfig.AgentConfig.KubeConfigNode)
restConfig, err := clientcmd.BuildConfigFromFlags("", nodeConfig.AgentConfig.KubeConfigK3sController)
if err != nil {
return err
}

View File

@ -9,6 +9,7 @@ import (
"strings"
"time"
systemd "github.com/coreos/go-systemd/daemon"
"github.com/rancher/k3s/pkg/agent/config"
"github.com/rancher/k3s/pkg/agent/containerd"
"github.com/rancher/k3s/pkg/agent/flannel"
@ -21,10 +22,11 @@ import (
"github.com/rancher/k3s/pkg/daemons/agent"
daemonconfig "github.com/rancher/k3s/pkg/daemons/config"
"github.com/rancher/k3s/pkg/rootless"
"github.com/rancher/wrangler-api/pkg/generated/controllers/core"
corev1 "github.com/rancher/wrangler-api/pkg/generated/controllers/core/v1"
"github.com/rancher/wrangler/pkg/start"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
v1 "k8s.io/client-go/kubernetes/typed/core/v1"
"k8s.io/client-go/tools/clientcmd"
)
@ -64,14 +66,18 @@ func run(ctx context.Context, cfg cmds.Agent, lb *loadbalancer.LoadBalancer) err
return err
}
coreClient, err := coreClient(nodeConfig.AgentConfig.KubeConfigKubelet)
if err != nil {
return err
}
if !nodeConfig.NoFlannel {
if err := flannel.Run(ctx, nodeConfig); err != nil {
if err := flannel.Run(ctx, nodeConfig, coreClient.CoreV1().Nodes()); err != nil {
return err
}
}
if !nodeConfig.AgentConfig.DisableCCM {
if err := syncAddressesLabels(ctx, &nodeConfig.AgentConfig); err != nil {
if err := syncAddressesLabels(ctx, &nodeConfig.AgentConfig, coreClient.CoreV1().Nodes()); err != nil {
return err
}
}
@ -86,6 +92,15 @@ func run(ctx context.Context, cfg cmds.Agent, lb *loadbalancer.LoadBalancer) err
return ctx.Err()
}
func coreClient(cfg string) (kubernetes.Interface, error) {
restConfig, err := clientcmd.BuildConfigFromFlags("", cfg)
if err != nil {
return nil, err
}
return kubernetes.NewForConfig(restConfig)
}
func Run(ctx context.Context, cfg cmds.Agent) error {
if err := validate(); err != nil {
return err
@ -100,10 +115,6 @@ func Run(ctx context.Context, cfg cmds.Agent) error {
cfg.DataDir = filepath.Join(cfg.DataDir, "agent")
os.MkdirAll(cfg.DataDir, 0700)
if cfg.ClusterSecret != "" {
cfg.Token = "K10node:" + cfg.ClusterSecret
}
lb, err := loadbalancer.Setup(ctx, cfg)
if err != nil {
return err
@ -113,7 +124,7 @@ func Run(ctx context.Context, cfg cmds.Agent) error {
}
for {
tmpFile, err := clientaccess.AgentAccessInfoToTempKubeConfig("", cfg.ServerURL, cfg.Token)
newToken, err := clientaccess.NormalizeAndValidateTokenForUser(cfg.ServerURL, cfg.Token, "node")
if err != nil {
logrus.Error(err)
select {
@ -123,10 +134,11 @@ func Run(ctx context.Context, cfg cmds.Agent) error {
}
continue
}
os.Remove(tmpFile)
cfg.Token = newToken
break
}
systemd.SdNotify(true, "READY=1\n")
return run(ctx, cfg, lb)
}
@ -149,73 +161,51 @@ func validate() error {
return nil
}
func syncAddressesLabels(ctx context.Context, agentConfig *daemonconfig.Agent) error {
func syncAddressesLabels(ctx context.Context, agentConfig *daemonconfig.Agent, nodes v1.NodeInterface) error {
for {
nodeController, nodeCache, err := startNodeController(ctx, agentConfig)
node, err := nodes.Get(agentConfig.NodeName, metav1.GetOptions{})
if err != nil {
logrus.Infof("Waiting for kubelet to be ready on node %s: %v", agentConfig.NodeName, err)
time.Sleep(1 * time.Second)
continue
}
nodeCached, err := nodeCache.Get(agentConfig.NodeName)
if err != nil {
logrus.Infof("Waiting for kubelet to be ready on node %s: %v", agentConfig.NodeName, err)
time.Sleep(1 * time.Second)
continue
}
node := nodeCached.DeepCopy()
updated := updateLabelMap(ctx, agentConfig, node.Labels)
if updated {
_, err = nodeController.Update(node)
if err == nil {
logrus.Infof("addresses labels has been set succesfully on node: %s", agentConfig.NodeName)
break
newLabels, update := updateLabelMap(agentConfig, node.Labels)
if update {
node.Labels = newLabels
if _, err := nodes.Update(node); err != nil {
logrus.Infof("Failed to update node %s: %v", agentConfig.NodeName, err)
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(time.Second):
continue
}
}
logrus.Infof("Failed to update node %s: %v", agentConfig.NodeName, err)
time.Sleep(1 * time.Second)
continue
logrus.Infof("addresses labels has been set successfully on node: %s", agentConfig.NodeName)
} else {
logrus.Infof("addresses labels has already been set successfully on node: %s", agentConfig.NodeName)
}
logrus.Infof("addresses labels has already been set succesfully on node: %s", agentConfig.NodeName)
return nil
break
}
return nil
}
func startNodeController(ctx context.Context, agentConfig *daemonconfig.Agent) (corev1.NodeController, corev1.NodeCache, error) {
restConfig, err := clientcmd.BuildConfigFromFlags("", agentConfig.KubeConfigKubelet)
if err != nil {
return nil, nil, err
}
coreFactory := core.NewFactoryFromConfigOrDie(restConfig)
nodeController := coreFactory.Core().V1().Node()
nodeCache := nodeController.Cache()
if err := start.All(ctx, 1, coreFactory); err != nil {
return nil, nil, err
func updateLabelMap(agentConfig *daemonconfig.Agent, nodeLabels map[string]string) (map[string]string, bool) {
result := map[string]string{}
for k, v := range nodeLabels {
result[k] = v
}
return nodeController, nodeCache, nil
}
result[InternalIPLabel] = agentConfig.NodeIP
result[HostnameLabel] = agentConfig.NodeName
if agentConfig.NodeExternalIP == "" {
delete(result, ExternalIPLabel)
} else {
result[ExternalIPLabel] = agentConfig.NodeExternalIP
}
func updateLabelMap(ctx context.Context, agentConfig *daemonconfig.Agent, nodeLabels map[string]string) bool {
if nodeLabels == nil {
nodeLabels = make(map[string]string)
}
updated := false
if internalIPLabel, ok := nodeLabels[InternalIPLabel]; !ok || internalIPLabel != agentConfig.NodeIP {
nodeLabels[InternalIPLabel] = agentConfig.NodeIP
updated = true
}
if hostnameLabel, ok := nodeLabels[HostnameLabel]; !ok || hostnameLabel != agentConfig.NodeName {
nodeLabels[HostnameLabel] = agentConfig.NodeName
updated = true
}
nodeExternalIP := agentConfig.NodeExternalIP
if externalIPLabel := nodeLabels[ExternalIPLabel]; externalIPLabel != nodeExternalIP && nodeExternalIP != "" {
nodeLabels[ExternalIPLabel] = nodeExternalIP
updated = true
} else if nodeExternalIP == "" && externalIPLabel != "" {
delete(nodeLabels, ExternalIPLabel)
updated = true
}
return updated
return result, !equality.Semantic.DeepEqual(nodeLabels, result)
}

View File

@ -3,11 +3,8 @@ package tunnel
import (
"context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"fmt"
"net"
"net/http"
"reflect"
"sync"
"time"
@ -21,8 +18,8 @@ import (
"k8s.io/apimachinery/pkg/fields"
watchtypes "k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/transport"
)
var (
@ -54,12 +51,7 @@ func getAddresses(endpoint *v1.Endpoints) []string {
}
func Setup(ctx context.Context, config *config.Node, onChange func([]string)) error {
restConfig, err := clientcmd.BuildConfigFromFlags("", config.AgentConfig.KubeConfigNode)
if err != nil {
return err
}
transportConfig, err := restConfig.TransportConfig()
restConfig, err := clientcmd.BuildConfigFromFlags("", config.AgentConfig.KubeConfigK3sController)
if err != nil {
return err
}
@ -69,6 +61,16 @@ func Setup(ctx context.Context, config *config.Node, onChange func([]string)) er
return err
}
nodeRestConfig, err := clientcmd.BuildConfigFromFlags("", config.AgentConfig.KubeConfigKubelet)
if err != nil {
return err
}
tlsConfig, err := rest.TLSConfigFor(nodeRestConfig)
if err != nil {
return err
}
addresses := []string{config.ServerAddress}
endpoint, _ := client.CoreV1().Endpoints("default").Get("kubernetes", metav1.GetOptions{})
@ -84,7 +86,7 @@ func Setup(ctx context.Context, config *config.Node, onChange func([]string)) er
wg := &sync.WaitGroup{}
for _, address := range addresses {
if _, ok := disconnect[address]; !ok {
disconnect[address] = connect(ctx, wg, address, config, transportConfig)
disconnect[address] = connect(ctx, wg, address, tlsConfig)
}
}
@ -132,7 +134,7 @@ func Setup(ctx context.Context, config *config.Node, onChange func([]string)) er
for _, address := range addresses {
validEndpoint[address] = true
if _, ok := disconnect[address]; !ok {
disconnect[address] = connect(ctx, nil, address, config, transportConfig)
disconnect[address] = connect(ctx, nil, address, tlsConfig)
}
}
@ -164,25 +166,10 @@ func Setup(ctx context.Context, config *config.Node, onChange func([]string)) er
return nil
}
func connect(rootCtx context.Context, waitGroup *sync.WaitGroup, address string, config *config.Node, transportConfig *transport.Config) context.CancelFunc {
func connect(rootCtx context.Context, waitGroup *sync.WaitGroup, address string, tlsConfig *tls.Config) context.CancelFunc {
wsURL := fmt.Sprintf("wss://%s/v1-k3s/connect", address)
headers := map[string][]string{
"X-K3s-NodeName": {config.AgentConfig.NodeName},
}
ws := &websocket.Dialer{}
if len(config.CACerts) > 0 {
pool := x509.NewCertPool()
pool.AppendCertsFromPEM(config.CACerts)
ws.TLSClientConfig = &tls.Config{
RootCAs: pool,
}
}
if transportConfig.Username != "" {
auth := transportConfig.Username + ":" + transportConfig.Password
auth = base64.StdEncoding.EncodeToString([]byte(auth))
headers["Authorization"] = []string{"Basic " + auth}
ws := &websocket.Dialer{
TLSClientConfig: tlsConfig,
}
once := sync.Once{}
@ -194,7 +181,7 @@ func connect(rootCtx context.Context, waitGroup *sync.WaitGroup, address string,
go func() {
for {
remotedialer.ClientConnect(ctx, wsURL, http.Header(headers), ws, func(proto, address string) bool {
remotedialer.ClientConnect(ctx, wsURL, nil, ws, func(proto, address string) bool {
host, port, err := net.SplitHostPort(address)
return err == nil && proto == "tcp" && ports[port] && host == "127.0.0.1"
}, func(_ context.Context) error {

View File

@ -1,7 +1,6 @@
package v1
import (
"github.com/rancher/dynamiclistener"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
)
@ -9,16 +8,6 @@ import (
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type ListenerConfig struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Status dynamiclistener.ListenerStatus `json:"status,omitempty"`
}
// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type Addon struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

View File

@ -122,63 +122,3 @@ func (in *AddonStatus) DeepCopy() *AddonStatus {
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ListenerConfig) DeepCopyInto(out *ListenerConfig) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Status.DeepCopyInto(&out.Status)
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ListenerConfig.
func (in *ListenerConfig) DeepCopy() *ListenerConfig {
if in == nil {
return nil
}
out := new(ListenerConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ListenerConfig) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ListenerConfigList) DeepCopyInto(out *ListenerConfigList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ListenerConfig, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ListenerConfigList.
func (in *ListenerConfigList) DeepCopy() *ListenerConfigList {
if in == nil {
return nil
}
out := new(ListenerConfigList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ListenerConfigList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}

View File

@ -26,23 +26,6 @@ import (
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ListenerConfigList is a list of ListenerConfig resources
type ListenerConfigList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []ListenerConfig `json:"items"`
}
func NewListenerConfig(namespace, name string, obj ListenerConfig) *ListenerConfig {
obj.APIVersion, obj.Kind = SchemeGroupVersion.WithKind("ListenerConfig").ToAPIVersionAndKind()
obj.Name = name
obj.Namespace = namespace
return &obj
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// AddonList is a list of Addon resources
type AddonList struct {
metav1.TypeMeta `json:",inline"`

View File

@ -28,8 +28,7 @@ import (
)
var (
AddonResourceName = "addons"
ListenerConfigResourceName = "listenerconfigs"
AddonResourceName = "addons"
)
// SchemeGroupVersion is group version used to register these objects
@ -55,8 +54,6 @@ func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(SchemeGroupVersion,
&Addon{},
&AddonList{},
&ListenerConfig{},
&ListenerConfigList{},
)
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
return nil

View File

@ -0,0 +1,80 @@
package bootstrap
import (
"encoding/json"
"io"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"github.com/pkg/errors"
"github.com/rancher/k3s/pkg/daemons/config"
)
func Handler(bootstrap *config.ControlRuntimeBootstrap) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
rw.Header().Set("Content-Type", "application/json")
Write(rw, bootstrap)
})
}
func Write(w io.Writer, bootstrap *config.ControlRuntimeBootstrap) error {
paths, err := objToMap(bootstrap)
if err != nil {
return nil
}
dataMap := map[string][]byte{}
for pathKey, path := range paths {
if path == "" {
continue
}
data, err := ioutil.ReadFile(path)
if err != nil {
return errors.Wrapf(err, "failed to read %s", path)
}
dataMap[pathKey] = data
}
return json.NewEncoder(w).Encode(dataMap)
}
func Read(r io.Reader, bootstrap *config.ControlRuntimeBootstrap) error {
paths, err := objToMap(bootstrap)
if err != nil {
return err
}
files := map[string][]byte{}
if err := json.NewDecoder(r).Decode(&files); err != nil {
return err
}
for pathKey, data := range files {
path, ok := paths[pathKey]
if !ok {
continue
}
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
return errors.Wrapf(err, "failed to mkdir %s", filepath.Dir(path))
}
if err := ioutil.WriteFile(path, data, 0600); err != nil {
return errors.Wrapf(err, "failed to write to %s", path)
}
}
return nil
}
func objToMap(obj interface{}) (map[string]string, error) {
bytes, err := json.Marshal(obj)
if err != nil {
return nil, err
}
data := map[string]string{}
return data, json.Unmarshal(bytes, &data)
}

View File

@ -3,39 +3,18 @@ package agent
import (
"context"
"fmt"
"io/ioutil"
"os"
"strings"
"time"
systemd "github.com/coreos/go-systemd/daemon"
"github.com/rancher/k3s/pkg/agent"
"github.com/rancher/k3s/pkg/cli/cmds"
"github.com/rancher/k3s/pkg/datadir"
"github.com/rancher/k3s/pkg/netutil"
"github.com/rancher/k3s/pkg/token"
"github.com/rancher/wrangler/pkg/signals"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
func readToken(path string) (string, error) {
if path == "" {
return "", nil
}
for {
tokenBytes, err := ioutil.ReadFile(path)
if err == nil {
return strings.TrimSpace(string(tokenBytes)), nil
} else if os.IsNotExist(err) {
logrus.Infof("Waiting for %s to be available\n", path)
time.Sleep(2 * time.Second)
} else {
return "", err
}
}
}
func Run(ctx *cli.Context) error {
if err := cmds.InitLogging(); err != nil {
return err
@ -45,14 +24,14 @@ func Run(ctx *cli.Context) error {
}
if cmds.AgentConfig.TokenFile != "" {
token, err := readToken(cmds.AgentConfig.TokenFile)
token, err := token.ReadFile(cmds.AgentConfig.TokenFile)
if err != nil {
return err
}
cmds.AgentConfig.Token = token
}
if cmds.AgentConfig.Token == "" && cmds.AgentConfig.ClusterSecret == "" {
if cmds.AgentConfig.Token == "" {
return fmt.Errorf("--token is required")
}
@ -76,7 +55,6 @@ func Run(ctx *cli.Context) error {
cfg.DataDir = dataDir
contextCtx := signals.SetupSignalHandler(context.Background())
systemd.SdNotify(true, "READY=1\n")
return agent.Run(contextCtx, cfg)
}

View File

@ -17,7 +17,6 @@ type Agent struct {
NodeIP string
NodeExternalIP string
NodeName string
ClusterSecret string
PauseImage string
Docker bool
ContainerRuntimeEndpoint string
@ -44,82 +43,82 @@ var (
AgentConfig Agent
NodeIPFlag = cli.StringFlag{
Name: "node-ip,i",
Usage: "(agent) IP address to advertise for node",
Usage: "(agent/networking) IP address to advertise for node",
Destination: &AgentConfig.NodeIP,
}
NodeExternalIPFlag = cli.StringFlag{
Name: "node-external-ip",
Usage: "(agent) External IP address to advertise for node",
Usage: "(agent/networking) External IP address to advertise for node",
Destination: &AgentConfig.NodeExternalIP,
}
NodeNameFlag = cli.StringFlag{
Name: "node-name",
Usage: "(agent) Node name",
Usage: "(agent/node) Node name",
EnvVar: "K3S_NODE_NAME",
Destination: &AgentConfig.NodeName,
}
DockerFlag = cli.BoolFlag{
Name: "docker",
Usage: "(agent) Use docker instead of containerd",
Usage: "(agent/runtime) Use docker instead of containerd",
Destination: &AgentConfig.Docker,
}
CRIEndpointFlag = cli.StringFlag{
Name: "container-runtime-endpoint",
Usage: "(agent/runtime) Disable embedded containerd and use alternative CRI implementation",
Destination: &AgentConfig.ContainerRuntimeEndpoint,
}
PrivateRegistryFlag = cli.StringFlag{
Name: "private-registry",
Usage: "(agent/runtime) Private registry configuration file",
Destination: &AgentConfig.PrivateRegistry,
Value: "/etc/rancher/k3s/registries.yaml",
}
PauseImageFlag = cli.StringFlag{
Name: "pause-image",
Usage: "(agent/runtime) Customized pause image for containerd sandbox",
Destination: &AgentConfig.PauseImage,
}
FlannelFlag = cli.BoolFlag{
Name: "no-flannel",
Usage: "(agent) Disable embedded flannel",
Usage: "(deprecated) use --flannel-backend=none",
Destination: &AgentConfig.NoFlannel,
}
FlannelIfaceFlag = cli.StringFlag{
Name: "flannel-iface",
Usage: "(agent) Override default flannel interface",
Usage: "(agent/networking) Override default flannel interface",
Destination: &AgentConfig.FlannelIface,
}
FlannelConfFlag = cli.StringFlag{
Name: "flannel-conf",
Usage: "(agent) (experimental) Override default flannel config file",
Usage: "(agent/networking) Override default flannel config file",
Destination: &AgentConfig.FlannelConf,
}
CRIEndpointFlag = cli.StringFlag{
Name: "container-runtime-endpoint",
Usage: "(agent) Disable embedded containerd and use alternative CRI implementation",
Destination: &AgentConfig.ContainerRuntimeEndpoint,
}
PauseImageFlag = cli.StringFlag{
Name: "pause-image",
Usage: "(agent) Customized pause image for containerd sandbox",
Destination: &AgentConfig.PauseImage,
}
ResolvConfFlag = cli.StringFlag{
Name: "resolv-conf",
Usage: "(agent) Kubelet resolv.conf file",
Usage: "(agent/networking) Kubelet resolv.conf file",
EnvVar: "K3S_RESOLV_CONF",
Destination: &AgentConfig.ResolvConf,
}
ExtraKubeletArgs = cli.StringSliceFlag{
Name: "kubelet-arg",
Usage: "(agent) Customized flag for kubelet process",
Usage: "(agent/flags) Customized flag for kubelet process",
Value: &AgentConfig.ExtraKubeletArgs,
}
ExtraKubeProxyArgs = cli.StringSliceFlag{
Name: "kube-proxy-arg",
Usage: "(agent) Customized flag for kube-proxy process",
Usage: "(agent/flags) Customized flag for kube-proxy process",
Value: &AgentConfig.ExtraKubeProxyArgs,
}
NodeTaints = cli.StringSliceFlag{
Name: "node-taint",
Usage: "(agent) Registering kubelet with set of taints",
Usage: "(agent/node) Registering kubelet with set of taints",
Value: &AgentConfig.Taints,
}
NodeLabels = cli.StringSliceFlag{
Name: "node-label",
Usage: "(agent) Registering kubelet with set of labels",
Usage: "(agent/node) Registering kubelet with set of labels",
Value: &AgentConfig.Labels,
}
PrivateRegistryFlag = cli.StringFlag{
Name: "private-registry",
Usage: "(agent) Private registry configuration file",
Destination: &AgentConfig.PrivateRegistry,
Value: "/etc/rancher/k3s/registries.yaml",
}
)
func NewAgentCommand(action func(ctx *cli.Context) error) cli.Command {
@ -135,54 +134,57 @@ func NewAgentCommand(action func(ctx *cli.Context) error) cli.Command {
AlsoLogToStderr,
cli.StringFlag{
Name: "token,t",
Usage: "Token to use for authentication",
Usage: "(cluster) Token to use for authentication",
EnvVar: "K3S_TOKEN",
Destination: &AgentConfig.Token,
},
cli.StringFlag{
Name: "token-file",
Usage: "Token file to use for authentication",
Usage: "(cluster) Token file to use for authentication",
EnvVar: "K3S_TOKEN_FILE",
Destination: &AgentConfig.TokenFile,
},
cli.StringFlag{
Name: "server,s",
Usage: "Server to connect to",
Usage: "(cluster) Server to connect to",
EnvVar: "K3S_URL",
Destination: &AgentConfig.ServerURL,
},
cli.StringFlag{
Name: "data-dir,d",
Usage: "Folder to hold state",
Usage: "(agent/data) Folder to hold state",
Destination: &AgentConfig.DataDir,
Value: "/var/lib/rancher/k3s",
},
cli.StringFlag{
Name: "cluster-secret",
Usage: "Shared secret used to bootstrap a cluster",
Destination: &AgentConfig.ClusterSecret,
EnvVar: "K3S_CLUSTER_SECRET",
},
NodeNameFlag,
NodeLabels,
NodeTaints,
DockerFlag,
CRIEndpointFlag,
PauseImageFlag,
PrivateRegistryFlag,
NodeIPFlag,
NodeExternalIPFlag,
ResolvConfFlag,
FlannelIfaceFlag,
FlannelConfFlag,
ExtraKubeletArgs,
ExtraKubeProxyArgs,
cli.BoolFlag{
Name: "rootless",
Usage: "(experimental) Run rootless",
Destination: &AgentConfig.Rootless,
},
DockerFlag,
// Deprecated/hidden below
FlannelFlag,
FlannelIfaceFlag,
FlannelConfFlag,
NodeNameFlag,
NodeIPFlag,
CRIEndpointFlag,
PauseImageFlag,
ResolvConfFlag,
ExtraKubeletArgs,
ExtraKubeProxyArgs,
NodeLabels,
NodeTaints,
PrivateRegistryFlag,
NodeExternalIPFlag,
cli.StringFlag{
Name: "cluster-secret",
Usage: "(deprecated) use --token",
Destination: &AgentConfig.Token,
EnvVar: "K3S_CLUSTER_SECRET",
},
},
}
}

View File

@ -25,22 +25,22 @@ var (
VLevel = cli.IntFlag{
Name: "v",
Usage: "Number for the log level verbosity",
Usage: "(logging) Number for the log level verbosity",
Destination: &LogConfig.VLevel,
}
VModule = cli.StringFlag{
Name: "vmodule",
Usage: "Comma-separated list of pattern=N settings for file-filtered logging",
Usage: "(logging) Comma-separated list of pattern=N settings for file-filtered logging",
Destination: &LogConfig.VModule,
}
LogFile = cli.StringFlag{
Name: "log,l",
Usage: "Log to file",
Usage: "(logging) Log to file",
Destination: &LogConfig.LogFile,
}
AlsoLogToStderr = cli.BoolFlag{
Name: "alsologtostderr",
Usage: "Log to standard error as well as file (if set)",
Usage: "(logging) Log to standard error as well as file (if set)",
Destination: &LogConfig.AlsoLogToStderr,
}
)

View File

@ -0,0 +1,7 @@
// +build !dqlite
package cmds
const (
hideDqlite = true
)

View File

@ -9,12 +9,14 @@ import (
type Server struct {
ClusterCIDR string
ClusterSecret string
AgentToken string
AgentTokenFile string
Token string
TokenFile string
ServiceCIDR string
ClusterDNS string
ClusterDomain string
HTTPSPort int
HTTPPort int
DataDir string
DisableAgent bool
KubeConfigOutput string
@ -26,7 +28,6 @@ type Server struct {
ExtraControllerArgs cli.StringSlice
ExtraCloudControllerArgs cli.StringSlice
Rootless bool
StoreBootstrap bool
StorageEndpoint string
StorageCAFile string
StorageCertFile string
@ -34,10 +35,13 @@ type Server struct {
AdvertiseIP string
AdvertisePort int
DisableScheduler bool
ServerURL string
FlannelBackend string
DefaultLocalStoragePath string
DisableCCM bool
DisableNPC bool
ClusterInit bool
ClusterReset bool
}
var ServerConfig Server
@ -55,190 +59,236 @@ func NewServerCommand(action func(*cli.Context) error) cli.Command {
AlsoLogToStderr,
cli.StringFlag{
Name: "bind-address",
Usage: "k3s bind address (default: localhost)",
Usage: "(listener) k3s bind address (default: 0.0.0.0)",
Destination: &ServerConfig.BindAddress,
},
cli.IntFlag{
Name: "https-listen-port",
Usage: "HTTPS listen port",
Usage: "(listener) HTTPS listen port",
Value: 6443,
Destination: &ServerConfig.HTTPSPort,
},
cli.StringFlag{
Name: "advertise-address",
Usage: "(listener) IP address that apiserver uses to advertise to members of the cluster (default: node-external-ip/node-ip)",
Destination: &ServerConfig.AdvertiseIP,
},
cli.IntFlag{
Name: "http-listen-port",
Usage: "HTTP listen port (for /healthz, HTTPS redirect, and port for TLS terminating LB)",
Value: 0,
Destination: &ServerConfig.HTTPPort,
Name: "advertise-port",
Usage: "(listener) Port that apiserver uses to advertise to members of the cluster (default: listen-port)",
Destination: &ServerConfig.AdvertisePort,
},
cli.StringSliceFlag{
Name: "tls-san",
Usage: "(listener) Add additional hostname or IP as a Subject Alternative Name in the TLS cert",
Value: &ServerConfig.TLSSan,
},
cli.StringFlag{
Name: "data-dir,d",
Usage: "Folder to hold state default /var/lib/rancher/k3s or ${HOME}/.rancher/k3s if not root",
Usage: "(data) Folder to hold state default /var/lib/rancher/k3s or ${HOME}/.rancher/k3s if not root",
Destination: &ServerConfig.DataDir,
},
cli.StringFlag{
Name: "cluster-cidr",
Usage: "(networking) Network CIDR to use for pod IPs",
Destination: &ServerConfig.ClusterCIDR,
Value: "10.42.0.0/16",
},
cli.StringFlag{
Name: "service-cidr",
Usage: "(networking) Network CIDR to use for services IPs",
Destination: &ServerConfig.ServiceCIDR,
Value: "10.43.0.0/16",
},
cli.StringFlag{
Name: "cluster-dns",
Usage: "(networking) Cluster IP for coredns service. Should be in your service-cidr range (default: 10.43.0.10)",
Destination: &ServerConfig.ClusterDNS,
Value: "",
},
cli.StringFlag{
Name: "cluster-domain",
Usage: "(networking) Cluster Domain",
Destination: &ServerConfig.ClusterDomain,
Value: "cluster.local",
},
cli.StringFlag{
Name: "flannel-backend",
Usage: fmt.Sprintf("(networking) One of '%s', '%s', '%s', or '%s'", config.FlannelBackendNone, config.FlannelBackendVXLAN, config.FlannelBackendIPSEC, config.FlannelBackendWireguard),
Destination: &ServerConfig.FlannelBackend,
Value: config.FlannelBackendVXLAN,
},
cli.StringFlag{
Name: "token,t",
Usage: "(cluster) Shared secret used to join a server or agent to a cluster",
Destination: &ServerConfig.Token,
EnvVar: "K3S_CLUSTER_SECRET,K3S_TOKEN",
},
cli.StringFlag{
Name: "token-file",
Usage: "(cluster) File containing the cluster-secret/token",
Destination: &ServerConfig.TokenFile,
EnvVar: "K3S_TOKEN_FILE",
},
cli.StringFlag{
Name: "agent-token",
Usage: "(cluster) Shared secret used to join agents to the cluster, but not agents",
Destination: &ServerConfig.AgentToken,
EnvVar: "K3S_AGENT_TOKEN",
},
cli.StringFlag{
Name: "agent-token-file",
Usage: "(cluster) File containing the agent secret",
Destination: &ServerConfig.AgentTokenFile,
EnvVar: "K3S_AGENT_TOKEN_FILE",
},
cli.StringFlag{
Name: "server,s",
Usage: "(cluster) Server to connect to, used to join a cluster",
EnvVar: "K3S_URL",
Destination: &ServerConfig.ServerURL,
},
cli.BoolFlag{
Name: "new-cluster",
Hidden: hideDqlite,
Usage: "(cluster) Initialize new cluster master",
EnvVar: "K3S_CLUSTER_INIT",
Destination: &ServerConfig.ClusterInit,
},
cli.BoolFlag{
Name: "reset-cluster",
Hidden: hideDqlite,
Usage: "(cluster) Forget all peers and become a single cluster new cluster master",
EnvVar: "K3S_CLUSTER_RESET",
Destination: &ServerConfig.ClusterReset,
},
cli.StringFlag{
Name: "write-kubeconfig,o",
Usage: "(client) Write kubeconfig for admin client to this file",
Destination: &ServerConfig.KubeConfigOutput,
EnvVar: "K3S_KUBECONFIG_OUTPUT",
},
cli.StringFlag{
Name: "write-kubeconfig-mode",
Usage: "(client) Write kubeconfig with this mode",
Destination: &ServerConfig.KubeConfigMode,
EnvVar: "K3S_KUBECONFIG_MODE",
},
cli.StringSliceFlag{
Name: "kube-apiserver-arg",
Usage: "(flags) Customized flag for kube-apiserver process",
Value: &ServerConfig.ExtraAPIArgs,
},
cli.StringSliceFlag{
Name: "kube-scheduler-arg",
Usage: "(flags) Customized flag for kube-scheduler process",
Value: &ServerConfig.ExtraSchedulerArgs,
},
cli.StringSliceFlag{
Name: "kube-controller-manager-arg",
Usage: "(flags) Customized flag for kube-controller-manager process",
Value: &ServerConfig.ExtraControllerArgs,
},
cli.StringSliceFlag{
Name: "kube-cloud-controller-manager-arg",
Usage: "(flags) Customized flag for kube-cloud-controller-manager process",
Value: &ServerConfig.ExtraCloudControllerArgs,
},
cli.StringFlag{
Name: "storage-endpoint",
Usage: "(db) Specify etcd, Mysql, Postgres, or Sqlite (default) data source name",
Destination: &ServerConfig.StorageEndpoint,
EnvVar: "K3S_STORAGE_ENDPOINT",
},
cli.StringFlag{
Name: "storage-cafile",
Usage: "(db) SSL Certificate Authority file used to secure storage backend communication",
Destination: &ServerConfig.StorageCAFile,
EnvVar: "K3S_STORAGE_CAFILE",
},
cli.StringFlag{
Name: "storage-certfile",
Usage: "(db) SSL certification file used to secure storage backend communication",
Destination: &ServerConfig.StorageCertFile,
EnvVar: "K3S_STORAGE_CERTFILE",
},
cli.StringFlag{
Name: "storage-keyfile",
Usage: "(db) SSL key file used to secure storage backend communication",
Destination: &ServerConfig.StorageKeyFile,
EnvVar: "K3S_STORAGE_KEYFILE",
},
cli.StringFlag{
Name: "default-local-storage-path",
Usage: "(storage) Default local storage path for local provisioner storage class",
Destination: &ServerConfig.DefaultLocalStoragePath,
},
cli.StringSliceFlag{
Name: "no-deploy",
Usage: "(components) Do not deploy packaged components (valid items: coredns, servicelb, traefik, local-storage)",
},
cli.BoolFlag{
Name: "disable-scheduler",
Usage: "(components) Disable Kubernetes default scheduler",
Destination: &ServerConfig.DisableScheduler,
},
cli.BoolFlag{
Name: "disable-cloud-controller",
Usage: "(components) Disable k3s default cloud controller manager",
Destination: &ServerConfig.DisableCCM,
},
cli.BoolFlag{
Name: "disable-network-policy",
Usage: "(components) Disable k3s default network policy controller",
Destination: &ServerConfig.DisableNPC,
},
NodeNameFlag,
NodeLabels,
NodeTaints,
DockerFlag,
CRIEndpointFlag,
PauseImageFlag,
PrivateRegistryFlag,
NodeIPFlag,
NodeExternalIPFlag,
ResolvConfFlag,
FlannelIfaceFlag,
FlannelConfFlag,
ExtraKubeletArgs,
ExtraKubeProxyArgs,
cli.BoolFlag{
Name: "rootless",
Usage: "(experimental) Run rootless",
Destination: &ServerConfig.Rootless,
},
// Hidden/Deprecated flags below
FlannelFlag,
cli.StringFlag{
Name: "cluster-secret",
Usage: "(deprecated) use --token",
Destination: &ServerConfig.Token,
EnvVar: "K3S_CLUSTER_SECRET",
},
cli.BoolFlag{
Name: "disable-agent",
Usage: "Do not run a local agent and register a local kubelet",
Hidden: true,
Destination: &ServerConfig.DisableAgent,
},
cli.StringFlag{
Name: "cluster-cidr",
Usage: "Network CIDR to use for pod IPs",
Destination: &ServerConfig.ClusterCIDR,
Value: "10.42.0.0/16",
},
cli.StringFlag{
Name: "cluster-secret",
Usage: "Shared secret used to bootstrap a cluster",
Destination: &ServerConfig.ClusterSecret,
EnvVar: "K3S_CLUSTER_SECRET",
},
cli.StringFlag{
Name: "service-cidr",
Usage: "Network CIDR to use for services IPs",
Destination: &ServerConfig.ServiceCIDR,
Value: "10.43.0.0/16",
},
cli.StringFlag{
Name: "cluster-dns",
Usage: "Cluster IP for coredns service. Should be in your service-cidr range",
Destination: &ServerConfig.ClusterDNS,
Value: "",
},
cli.StringFlag{
Name: "cluster-domain",
Usage: "Cluster Domain",
Destination: &ServerConfig.ClusterDomain,
Value: "cluster.local",
cli.StringSliceFlag{
Hidden: true,
Name: "kube-controller-arg",
Usage: "(flags) Customized flag for kube-controller-manager process",
Value: &ServerConfig.ExtraControllerArgs,
},
cli.StringSliceFlag{
Name: "no-deploy",
Usage: "Do not deploy packaged components (valid items: coredns, servicelb, traefik)",
Hidden: true,
Name: "kube-cloud-controller-arg",
Usage: "(flags) Customized flag for kube-cloud-controller-manager process",
Value: &ServerConfig.ExtraCloudControllerArgs,
},
cli.StringFlag{
Name: "write-kubeconfig,o",
Usage: "Write kubeconfig for admin client to this file",
Destination: &ServerConfig.KubeConfigOutput,
EnvVar: "K3S_KUBECONFIG_OUTPUT",
},
cli.StringFlag{
Name: "write-kubeconfig-mode",
Usage: "Write kubeconfig with this mode",
Destination: &ServerConfig.KubeConfigMode,
EnvVar: "K3S_KUBECONFIG_MODE",
},
cli.StringSliceFlag{
Name: "tls-san",
Usage: "Add additional hostname or IP as a Subject Alternative Name in the TLS cert",
Value: &ServerConfig.TLSSan,
},
cli.StringSliceFlag{
Name: "kube-apiserver-arg",
Usage: "Customized flag for kube-apiserver process",
Value: &ServerConfig.ExtraAPIArgs,
},
cli.StringSliceFlag{
Name: "kube-scheduler-arg",
Usage: "Customized flag for kube-scheduler process",
Value: &ServerConfig.ExtraSchedulerArgs,
},
cli.StringSliceFlag{
Name: "kube-controller-arg",
Usage: "Customized flag for kube-controller-manager process",
Value: &ServerConfig.ExtraControllerArgs,
},
cli.StringSliceFlag{
Name: "kube-cloud-controller-arg",
Usage: "Customized flag for kube-cloud-controller-manager process",
Value: &ServerConfig.ExtraCloudControllerArgs,
},
cli.BoolFlag{
Name: "rootless",
Usage: "(experimental) Run rootless",
Destination: &ServerConfig.Rootless,
},
cli.BoolFlag{
Name: "bootstrap-save",
Usage: "(experimental) Save bootstrap information in the storage endpoint",
Hidden: true,
Destination: &ServerConfig.StoreBootstrap,
},
cli.StringFlag{
Name: "storage-endpoint",
Usage: "Specify etcd, Mysql, Postgres, or Sqlite (default) data source name",
Destination: &ServerConfig.StorageEndpoint,
EnvVar: "K3S_STORAGE_ENDPOINT",
},
cli.StringFlag{
Name: "storage-cafile",
Usage: "SSL Certificate Authority file used to secure storage backend communication",
Destination: &ServerConfig.StorageCAFile,
EnvVar: "K3S_STORAGE_CAFILE",
},
cli.StringFlag{
Name: "storage-certfile",
Usage: "SSL certification file used to secure storage backend communication",
Destination: &ServerConfig.StorageCertFile,
EnvVar: "K3S_STORAGE_CERTFILE",
},
cli.StringFlag{
Name: "storage-keyfile",
Usage: "SSL key file used to secure storage backend communication",
Destination: &ServerConfig.StorageKeyFile,
EnvVar: "K3S_STORAGE_KEYFILE",
},
cli.StringFlag{
Name: "advertise-address",
Usage: "IP address that apiserver uses to advertise to members of the cluster",
Destination: &ServerConfig.AdvertiseIP,
},
cli.IntFlag{
Name: "advertise-port",
Usage: "Port that apiserver uses to advertise to members of the cluster",
Value: 0,
Destination: &ServerConfig.AdvertisePort,
},
cli.BoolFlag{
Name: "disable-scheduler",
Usage: "Disable Kubernetes default scheduler",
Destination: &ServerConfig.DisableScheduler,
},
cli.BoolFlag{
Name: "disable-cloud-controller",
Usage: "Disable k3s default cloud controller manager",
Destination: &ServerConfig.DisableCCM,
},
cli.BoolFlag{
Name: "disable-network-policy",
Usage: "Disable k3s default network policy controller",
Destination: &ServerConfig.DisableNPC,
},
cli.StringFlag{
Name: "flannel-backend",
Usage: fmt.Sprintf("(experimental) One of '%s', '%s', '%s', or '%s'", config.FlannelBackendNone, config.FlannelBackendVXLAN, config.FlannelBackendIPSEC, config.FlannelBackendWireguard),
Destination: &ServerConfig.FlannelBackend,
Value: config.FlannelBackendVXLAN,
},
cli.StringFlag{
Name: "default-local-storage-path",
Usage: "Default local storage path for local provisioner storage class",
Destination: &ServerConfig.DefaultLocalStoragePath,
},
NodeIPFlag,
NodeNameFlag,
DockerFlag,
FlannelFlag,
FlannelIfaceFlag,
FlannelConfFlag,
CRIEndpointFlag,
PauseImageFlag,
ResolvConfFlag,
ExtraKubeletArgs,
ExtraKubeProxyArgs,
NodeLabels,
NodeTaints,
PrivateRegistryFlag,
NodeExternalIPFlag,
},
}
}

View File

@ -16,6 +16,7 @@ import (
"github.com/rancher/k3s/pkg/netutil"
"github.com/rancher/k3s/pkg/rootless"
"github.com/rancher/k3s/pkg/server"
"github.com/rancher/k3s/pkg/token"
"github.com/rancher/wrangler/pkg/signals"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
@ -56,23 +57,28 @@ func run(app *cli.Context, cfg *cmds.Server) error {
serverConfig := server.Config{}
serverConfig.DisableAgent = cfg.DisableAgent
serverConfig.ControlConfig.ClusterSecret = cfg.ClusterSecret
serverConfig.ControlConfig.Token = cfg.Token
serverConfig.ControlConfig.AgentToken = cfg.AgentToken
serverConfig.ControlConfig.JoinURL = cfg.ServerURL
if cfg.AgentTokenFile != "" {
serverConfig.ControlConfig.AgentToken, err = token.ReadFile(cfg.AgentTokenFile)
if err != nil {
return err
}
}
if cfg.TokenFile != "" {
serverConfig.ControlConfig.Token, err = token.ReadFile(cfg.TokenFile)
if err != nil {
return err
}
}
serverConfig.ControlConfig.DataDir = cfg.DataDir
serverConfig.ControlConfig.KubeConfigOutput = cfg.KubeConfigOutput
serverConfig.ControlConfig.KubeConfigMode = cfg.KubeConfigMode
serverConfig.ControlConfig.NoScheduler = cfg.DisableScheduler
serverConfig.Rootless = cfg.Rootless
serverConfig.TLSConfig.HTTPSPort = cfg.HTTPSPort
serverConfig.TLSConfig.HTTPPort = cfg.HTTPPort
for _, san := range knownIPs(cfg.TLSSan) {
addr := net2.ParseIP(san)
if addr != nil {
serverConfig.TLSConfig.KnownIPs = append(serverConfig.TLSConfig.KnownIPs, san)
} else {
serverConfig.TLSConfig.Domains = append(serverConfig.TLSConfig.Domains, san)
}
}
serverConfig.TLSConfig.BindAddress = cfg.BindAddress
serverConfig.ControlConfig.SANs = knownIPs(cfg.TLSSan)
serverConfig.ControlConfig.BindAddress = cfg.BindAddress
serverConfig.ControlConfig.HTTPSPort = cfg.HTTPSPort
serverConfig.ControlConfig.ExtraAPIArgs = cfg.ExtraAPIArgs
serverConfig.ControlConfig.ExtraControllerArgs = cfg.ExtraControllerArgs
@ -84,21 +90,25 @@ func run(app *cli.Context, cfg *cmds.Server) error {
serverConfig.ControlConfig.Storage.KeyFile = cfg.StorageKeyFile
serverConfig.ControlConfig.AdvertiseIP = cfg.AdvertiseIP
serverConfig.ControlConfig.AdvertisePort = cfg.AdvertisePort
serverConfig.ControlConfig.BootstrapReadOnly = !cfg.StoreBootstrap
serverConfig.ControlConfig.FlannelBackend = cfg.FlannelBackend
serverConfig.ControlConfig.ExtraCloudControllerArgs = cfg.ExtraCloudControllerArgs
serverConfig.ControlConfig.DisableCCM = cfg.DisableCCM
serverConfig.ControlConfig.DisableNPC = cfg.DisableNPC
serverConfig.ControlConfig.ClusterInit = cfg.ClusterInit
serverConfig.ControlConfig.ClusterReset = cfg.ClusterReset
if cmds.AgentConfig.FlannelIface != "" && cmds.AgentConfig.NodeIP == "" {
cmds.AgentConfig.NodeIP = netutil.GetIPFromInterface(cmds.AgentConfig.FlannelIface)
}
if serverConfig.ControlConfig.AdvertiseIP == "" && cmds.AgentConfig.NodeExternalIP != "" {
serverConfig.ControlConfig.AdvertiseIP = cmds.AgentConfig.NodeExternalIP
}
if serverConfig.ControlConfig.AdvertiseIP == "" && cmds.AgentConfig.NodeIP != "" {
serverConfig.ControlConfig.AdvertiseIP = cmds.AgentConfig.NodeIP
}
if serverConfig.ControlConfig.AdvertiseIP != "" {
serverConfig.TLSConfig.KnownIPs = append(serverConfig.TLSConfig.KnownIPs, serverConfig.ControlConfig.AdvertiseIP)
serverConfig.ControlConfig.SANs = append(serverConfig.ControlConfig.SANs, serverConfig.ControlConfig.AdvertiseIP)
}
_, serverConfig.ControlConfig.ClusterIPRange, err = net2.ParseCIDR(cfg.ClusterCIDR)
@ -114,7 +124,7 @@ func run(app *cli.Context, cfg *cmds.Server) error {
if err != nil {
return err
}
serverConfig.TLSConfig.KnownIPs = append(serverConfig.TLSConfig.KnownIPs, apiServerServiceIP.String())
serverConfig.ControlConfig.SANs = append(serverConfig.ControlConfig.SANs, apiServerServiceIP.String())
// If cluster-dns CLI arg is not set, we set ClusterDNS address to be ServiceCIDR network + 10,
// i.e. when you set service-cidr to 192.168.0.0/16 and don't provide cluster-dns, it will be set to 192.168.0.10
@ -160,8 +170,7 @@ func run(app *cli.Context, cfg *cmds.Server) error {
os.Unsetenv("NOTIFY_SOCKET")
ctx := signals.SetupSignalHandler(context.Background())
certs, err := server.StartServer(ctx, &serverConfig)
if err != nil {
if err := server.StartServer(ctx, &serverConfig); err != nil {
return err
}
@ -175,12 +184,17 @@ func run(app *cli.Context, cfg *cmds.Server) error {
<-ctx.Done()
return nil
}
ip := serverConfig.TLSConfig.BindAddress
ip := serverConfig.ControlConfig.BindAddress
if ip == "" {
ip = "127.0.0.1"
}
url := fmt.Sprintf("https://%s:%d", ip, serverConfig.TLSConfig.HTTPSPort)
token := server.FormatToken(serverConfig.ControlConfig.Runtime.NodeToken, certs)
url := fmt.Sprintf("https://%s:%d", ip, serverConfig.ControlConfig.HTTPSPort)
token, err := server.FormatToken(serverConfig.ControlConfig.Runtime.AgentToken, serverConfig.ControlConfig.Runtime.ServerCA)
if err != nil {
return err
}
agentConfig := cmds.AgentConfig
agentConfig.Debug = app.GlobalBool("bool")

View File

@ -9,7 +9,6 @@ import (
"io/ioutil"
"net/http"
"net/url"
"os"
"strings"
"github.com/pkg/errors"
@ -35,21 +34,6 @@ type clientToken struct {
password string
}
func AgentAccessInfoToTempKubeConfig(tempDir, server, token string) (string, error) {
f, err := ioutil.TempFile(tempDir, "tmp-")
if err != nil {
return "", err
}
if err := f.Close(); err != nil {
return "", err
}
err = accessInfoToKubeConfig(f.Name(), server, token)
if err != nil {
os.Remove(f.Name())
}
return f.Name(), err
}
func AgentAccessInfoToKubeConfig(destFile, server, token string) error {
return accessInfoToKubeConfig(destFile, server, token)
}
@ -62,6 +46,10 @@ type Info struct {
Token string `json:"token,omitempty"`
}
func (i *Info) ToToken() string {
return fmt.Sprintf("K10%s::%s:%s", hashCA(i.CACerts), i.username, i.password)
}
func (i *Info) WriteKubeConfig(destFile string) error {
return clientcmd.WriteToFile(*i.KubeConfig(), destFile)
}
@ -98,6 +86,22 @@ func (i *Info) KubeConfig() *clientcmdapi.Config {
return config
}
func NormalizeAndValidateTokenForUser(server, token, user string) (string, error) {
if !strings.HasPrefix(token, "K10") {
token = "K10::" + user + ":" + token
}
info, err := ParseAndValidateToken(server, token)
if err != nil {
return "", err
}
if info.username != user {
info.username = user
}
return info.ToToken(), nil
}
func ParseAndValidateToken(server, token string) (*Info, error) {
url, err := url.Parse(server)
if err != nil {
@ -132,13 +136,17 @@ func ParseAndValidateToken(server, token string) (*Info, error) {
return nil, err
}
return &Info{
i := &Info{
URL: url.String(),
CACerts: cacerts,
username: parsedToken.username,
password: parsedToken.password,
Token: token,
}, nil
}
// normalize token
i.Token = i.ToToken()
return i, nil
}
func accessInfoToKubeConfig(destFile, server, token string) error {
@ -164,11 +172,15 @@ func validateCACerts(cacerts []byte, hash string) (bool, string, string) {
return true, "", ""
}
digest := sha256.Sum256([]byte(cacerts))
newHash := hex.EncodeToString(digest[:])
newHash := hashCA(cacerts)
return hash == newHash, hash, newHash
}
func hashCA(cacerts []byte) string {
digest := sha256.Sum256(cacerts)
return hex.EncodeToString(digest[:])
}
func ParseUsernamePassword(token string) (string, string, bool) {
parsed, err := parseToken(token)
if err != nil {

48
pkg/cluster/cluster.go Normal file
View File

@ -0,0 +1,48 @@
package cluster
import (
"context"
"github.com/rancher/k3s/pkg/clientaccess"
"github.com/rancher/k3s/pkg/daemons/config"
)
type Cluster struct {
token string
clientAccessInfo *clientaccess.Info
config *config.Control
runtime *config.ControlRuntime
db interface{}
}
func (c *Cluster) Start(ctx context.Context) error {
join, err := c.shouldJoin()
if err != nil {
return err
}
if join {
if err := c.join(); err != nil {
return err
}
}
if err := c.startClusterAndHTTPS(ctx); err != nil {
return err
}
if join {
if err := c.postJoin(ctx); err != nil {
return err
}
}
return c.joined()
}
func New(config *config.Control) *Cluster {
return &Cluster{
config: config,
runtime: config.Runtime,
}
}

81
pkg/cluster/https.go Normal file
View File

@ -0,0 +1,81 @@
package cluster
import (
"context"
"crypto/tls"
"net"
"net/http"
"path/filepath"
"github.com/rancher/dynamiclistener"
"github.com/rancher/dynamiclistener/factory"
"github.com/rancher/dynamiclistener/storage/file"
"github.com/rancher/dynamiclistener/storage/kubernetes"
"github.com/rancher/dynamiclistener/storage/memory"
"github.com/rancher/k3s/pkg/daemons/config"
"github.com/rancher/wrangler-api/pkg/generated/controllers/core"
"github.com/sirupsen/logrus"
)
func (c *Cluster) newListener(ctx context.Context) (net.Listener, http.Handler, error) {
tcp, err := dynamiclistener.NewTCPListener(c.config.BindAddress, c.config.HTTPSPort)
if err != nil {
return nil, nil, err
}
cert, key, err := factory.LoadCerts(c.runtime.ServerCA, c.runtime.ServerCAKey)
if err != nil {
return nil, nil, err
}
storage := tlsStorage(ctx, c.config.DataDir, c.runtime)
return dynamiclistener.NewListener(tcp, storage, cert, key, dynamiclistener.Config{
CN: "k3s",
Organization: []string{"k3s"},
TLSConfig: tls.Config{
ClientAuth: tls.RequestClientCert,
},
SANs: c.config.SANs,
})
}
func (c *Cluster) startClusterAndHTTPS(ctx context.Context) error {
l, handler, err := c.newListener(ctx)
if err != nil {
return err
}
handler, err = c.getHandler(handler)
if err != nil {
return err
}
l, handler, err = c.initClusterDB(ctx, l, handler)
if err != nil {
return err
}
server := http.Server{
Handler: handler,
}
go func() {
err := server.Serve(l)
logrus.Fatalf("server stopped: %v", err)
}()
go func() {
<-ctx.Done()
server.Shutdown(context.Background())
}()
return nil
}
func tlsStorage(ctx context.Context, dataDir string, runtime *config.ControlRuntime) dynamiclistener.TLSStorage {
fileStorage := file.New(filepath.Join(dataDir, "tls/dynamic-cert.json"))
cache := memory.NewBacked(fileStorage)
return kubernetes.New(ctx, func() *core.Factory {
return runtime.Core
}, "kube-system", "k3s-serving", cache)
}

74
pkg/cluster/join.go Normal file
View File

@ -0,0 +1,74 @@
package cluster
import (
"bytes"
"fmt"
"os"
"path/filepath"
"github.com/rancher/k3s/pkg/bootstrap"
"github.com/rancher/k3s/pkg/clientaccess"
"github.com/sirupsen/logrus"
)
func (c *Cluster) shouldJoin() (bool, error) {
if c.config.JoinURL == "" {
return false, nil
}
stamp := filepath.Join(c.config.DataDir, "db/joined")
if _, err := os.Stat(stamp); err == nil {
logrus.Info("Already joined to cluster, not rejoining")
return false, nil
}
if c.config.Token == "" {
return false, fmt.Errorf("K3S_TOKEN is required to join a cluster")
}
return true, nil
}
func (c *Cluster) joined() error {
if err := os.MkdirAll(filepath.Dir(c.joinStamp()), 0700); err != nil {
return err
}
if _, err := os.Stat(c.joinStamp()); err == nil {
return nil
}
f, err := os.Create(c.joinStamp())
if err != nil {
return err
}
return f.Close()
}
func (c *Cluster) join() error {
c.runtime.Cluster.Join = true
token, err := clientaccess.NormalizeAndValidateTokenForUser(c.config.JoinURL, c.config.Token, "server")
if err != nil {
return err
}
c.token = token
info, err := clientaccess.ParseAndValidateToken(c.config.JoinURL, token)
if err != nil {
return err
}
c.clientAccessInfo = info
content, err := clientaccess.Get("/v1-k3s/server-bootstrap", info)
if err != nil {
return err
}
return bootstrap.Read(bytes.NewBuffer(content), &c.runtime.ControlRuntimeBootstrap)
}
func (c *Cluster) joinStamp() string {
return filepath.Join(c.config.DataDir, "db/joined")
}

17
pkg/cluster/nocluster.go Normal file
View File

@ -0,0 +1,17 @@
// +build !dqlite
package cluster
import (
"context"
"net"
"net/http"
)
func (c *Cluster) initClusterDB(ctx context.Context, l net.Listener, handler http.Handler) (net.Listener, http.Handler, error) {
return l, handler, nil
}
func (c *Cluster) postJoin(ctx context.Context) error {
return nil
}

25
pkg/cluster/router.go Normal file
View File

@ -0,0 +1,25 @@
package cluster
import (
"net/http"
)
func (c *Cluster) getHandler(handler http.Handler) (http.Handler, error) {
next := c.router()
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
handler.ServeHTTP(rw, req)
next.ServeHTTP(rw, req)
}), nil
}
func (c *Cluster) router() http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
if c.runtime.Handler == nil {
http.Error(rw, "starting", http.StatusServiceUnavailable)
return
}
c.runtime.Handler.ServeHTTP(rw, req)
})
}

View File

@ -70,7 +70,6 @@ func main() {
Groups: map[string]args.Group{
"k3s.cattle.io": {
Types: []interface{}{
v1.ListenerConfig{},
v1.Addon{},
},
GenerateTypes: true,

View File

@ -9,7 +9,7 @@ import (
"strings"
"github.com/rancher/kine/pkg/endpoint"
"github.com/rancher/wrangler-api/pkg/generated/controllers/core"
"k8s.io/apiserver/pkg/authentication/authenticator"
)
@ -47,40 +47,36 @@ type Containerd struct {
}
type Agent struct {
NodeName string
ClientKubeletCert string
ClientKubeletKey string
ClientKubeProxyCert string
ClientKubeProxyKey string
ServingKubeletCert string
ServingKubeletKey string
ClusterCIDR net.IPNet
ClusterDNS net.IP
ClusterDomain string
ResolvConf string
RootDir string
KubeConfigNode string
KubeConfigKubelet string
KubeConfigKubeProxy string
NodeIP string
NodeExternalIP string
RuntimeSocket string
ListenAddress string
ClientCA string
CNIBinDir string
CNIConfDir string
ExtraKubeletArgs []string
ExtraKubeProxyArgs []string
PauseImage string
CNIPlugin bool
NodeTaints []string
NodeLabels []string
IPSECPSK string
StrongSwanDir string
PrivateRegistry string
DisableCCM bool
DisableNPC bool
Rootless bool
NodeName string
ServingKubeletCert string
ServingKubeletKey string
ClusterCIDR net.IPNet
ClusterDNS net.IP
ClusterDomain string
ResolvConf string
RootDir string
KubeConfigKubelet string
KubeConfigKubeProxy string
KubeConfigK3sController string
NodeIP string
NodeExternalIP string
RuntimeSocket string
ListenAddress string
ClientCA string
CNIBinDir string
CNIConfDir string
ExtraKubeletArgs []string
ExtraKubeProxyArgs []string
PauseImage string
CNIPlugin bool
NodeTaints []string
NodeLabels []string
IPSECPSK string
StrongSwanDir string
PrivateRegistry string
DisableCCM bool
DisableNPC bool
Rootless bool
}
type Control struct {
@ -88,7 +84,8 @@ type Control struct {
AdvertiseIP string
ListenPort int
HTTPSPort int
ClusterSecret string
AgentToken string
Token string
ClusterIPRange *net.IPNet
ServiceIPRange *net.IPNet
ClusterDNS net.IP
@ -98,19 +95,24 @@ type Control struct {
KubeConfigMode string
DataDir string
Skips []string
BootstrapReadOnly bool
Storage endpoint.Config
NoScheduler bool
ExtraAPIArgs []string
ExtraControllerArgs []string
ExtraSchedulerAPIArgs []string
ExtraCloudControllerArgs []string
ExtraSchedulerAPIArgs []string
NoLeaderElect bool
JoinURL string
FlannelBackend string
IPSECPSK string
DefaultLocalStoragePath string
DisableCCM bool
DisableNPC bool
ClusterInit bool
ClusterReset bool
BindAddress string
SANs []string
Runtime *ControlRuntime `json:"-"`
}
@ -124,9 +126,6 @@ type ControlRuntimeBootstrap struct {
PasswdFile string
RequestHeaderCA string
RequestHeaderCAKey string
ClientKubeletKey string
ClientKubeProxyKey string
ServingKubeletKey string
IPSECKey string
}
@ -145,8 +144,10 @@ type ControlRuntime struct {
ServingKubeAPICert string
ServingKubeAPIKey string
ServingKubeletKey string
ClientToken string
NodeToken string
ServerToken string
AgentToken string
Handler http.Handler
Tunnel http.Handler
Authenticator authenticator.Request
@ -161,8 +162,19 @@ type ControlRuntime struct {
ClientSchedulerCert string
ClientSchedulerKey string
ClientKubeProxyCert string
ClientKubeProxyKey string
ClientKubeletKey string
ClientCloudControllerCert string
ClientCloudControllerKey string
ClientK3sControllerCert string
ClientK3sControllerKey string
Cluster ClusterConfig
Core *core.Factory
}
type ClusterConfig struct {
Join bool
}
type ArgString []string

View File

@ -1,100 +0,0 @@
package control
import (
"context"
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"github.com/pkg/errors"
"github.com/rancher/k3s/pkg/daemons/config"
"github.com/rancher/kine/pkg/client"
"github.com/sirupsen/logrus"
)
const (
k3sRuntimeEtcdPath = "/k3s/runtime"
)
// fetchBootstrapData copies the bootstrap data (certs, keys, passwords)
// from etcd to individual files specified by cfg.Runtime.
func fetchBootstrapData(ctx context.Context, cfg *config.Control, c client.Client) error {
logrus.Info("Fetching bootstrap data from etcd")
gr, err := c.Get(ctx, k3sRuntimeEtcdPath)
if err != nil {
return err
}
if gr.Modified == 0 {
return nil
}
paths, err := objToMap(&cfg.Runtime.ControlRuntimeBootstrap)
if err != nil {
return err
}
files := map[string][]byte{}
if err := json.Unmarshal(gr.Data, &files); err != nil {
return err
}
for pathKey, data := range files {
path, ok := paths[pathKey]
if !ok {
continue
}
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
return errors.Wrapf(err, "failed to mkdir %s", filepath.Dir(path))
}
if err := ioutil.WriteFile(path, data, 0700); err != nil {
return errors.Wrapf(err, "failed to write to %s", path)
}
}
return nil
}
// storeBootstrapData copies the bootstrap data in the opposite direction to
// fetchBootstrapData.
func storeBootstrapData(ctx context.Context, cfg *config.Control, client client.Client) error {
if cfg.BootstrapReadOnly {
return nil
}
paths, err := objToMap(&cfg.Runtime.ControlRuntimeBootstrap)
if err != nil {
return nil
}
dataMap := map[string][]byte{}
for pathKey, path := range paths {
if path == "" {
continue
}
data, err := ioutil.ReadFile(path)
if err != nil {
return errors.Wrapf(err, "failed to read %s", path)
}
dataMap[pathKey] = data
}
bytes, err := json.Marshal(dataMap)
if err != nil {
return err
}
return client.Put(ctx, k3sRuntimeEtcdPath, bytes)
}
func objToMap(obj interface{}) (map[string]string, error) {
bytes, err := json.Marshal(obj)
if err != nil {
return nil, err
}
data := map[string]string{}
return data, json.Unmarshal(bytes, &data)
}

View File

@ -3,13 +3,9 @@ package control
import (
"context"
"crypto"
cryptorand "crypto/rand"
"crypto/x509"
"encoding/csv"
"encoding/hex"
"fmt"
"html/template"
"io"
"io/ioutil"
"math/rand"
"net"
@ -21,11 +17,14 @@ import (
"strings"
"time"
certutil "github.com/rancher/dynamiclistener/cert"
// registering k3s cloud provider
_ "github.com/rancher/k3s/pkg/cloudprovider"
certutil "github.com/rancher/dynamiclistener/cert"
"github.com/rancher/k3s/pkg/cluster"
"github.com/rancher/k3s/pkg/daemons/config"
"github.com/rancher/kine/pkg/client"
"github.com/rancher/k3s/pkg/passwd"
"github.com/rancher/k3s/pkg/token"
"github.com/rancher/kine/pkg/endpoint"
"github.com/rancher/wrangler-api/pkg/generated/controllers/rbac"
"github.com/sirupsen/logrus"
@ -121,6 +120,10 @@ func controllerManager(cfg *config.Control, runtime *config.ControlRuntime) {
"cluster-signing-cert-file": runtime.ServerCA,
"cluster-signing-key-file": runtime.ServerCAKey,
}
offset := cfg.HTTPSPort - 6443
if offset > 0 {
argsMap["port"] = strconv.Itoa(10252 + offset)
}
if cfg.NoLeaderElect {
argsMap["leader-elect"] = "false"
}
@ -143,6 +146,10 @@ func scheduler(cfg *config.Control, runtime *config.ControlRuntime) {
"bind-address": "127.0.0.1",
"secure-port": "0",
}
offset := cfg.HTTPSPort - 6443
if offset > 0 {
argsMap["port"] = strconv.Itoa(10251 + offset)
}
if cfg.NoLeaderElect {
argsMap["leader-elect"] = "false"
}
@ -291,6 +298,8 @@ func prepare(ctx context.Context, config *config.Control, runtime *config.Contro
runtime.ClientKubeAPIKey = path.Join(config.DataDir, "tls", "client-kube-apiserver.key")
runtime.ClientKubeProxyCert = path.Join(config.DataDir, "tls", "client-kube-proxy.crt")
runtime.ClientKubeProxyKey = path.Join(config.DataDir, "tls", "client-kube-proxy.key")
runtime.ClientK3sControllerCert = path.Join(config.DataDir, "tls", "client-k3s-controller.crt")
runtime.ClientK3sControllerKey = path.Join(config.DataDir, "tls", "client-k3s-controller.key")
runtime.ServingKubeAPICert = path.Join(config.DataDir, "tls", "serving-kube-apiserver.crt")
runtime.ServingKubeAPIKey = path.Join(config.DataDir, "tls", "serving-kube-apiserver.key")
@ -301,20 +310,14 @@ func prepare(ctx context.Context, config *config.Control, runtime *config.Contro
runtime.ClientAuthProxyCert = path.Join(config.DataDir, "tls", "client-auth-proxy.crt")
runtime.ClientAuthProxyKey = path.Join(config.DataDir, "tls", "client-auth-proxy.key")
etcdClient, err := prepareStorageBackend(ctx, config)
if err != nil {
return err
}
defer etcdClient.Close()
if err := fetchBootstrapData(ctx, config, etcdClient); err != nil {
return err
}
if err := genCerts(config, runtime); err != nil {
return err
}
if err := cluster.New(config).Start(ctx); err != nil {
return err
}
if err := genServiceAccount(runtime); err != nil {
return err
}
@ -327,124 +330,44 @@ func prepare(ctx context.Context, config *config.Control, runtime *config.Contro
return err
}
if err := storeBootstrapData(ctx, config, etcdClient); err != nil {
if err := prepareStorageBackend(ctx, config); err != nil {
return err
}
return readTokens(runtime)
}
func prepareStorageBackend(ctx context.Context, config *config.Control) (client.Client, error) {
func prepareStorageBackend(ctx context.Context, config *config.Control) error {
etcdConfig, err := endpoint.Listen(ctx, config.Storage)
if err != nil {
return nil, err
return err
}
config.Storage.Config = etcdConfig.TLSConfig
config.Storage.Endpoint = strings.Join(etcdConfig.Endpoints, ",")
config.NoLeaderElect = !etcdConfig.LeaderElect
return client.New(etcdConfig)
}
func readTokenFile(passwdFile string) (map[string]string, error) {
f, err := os.Open(passwdFile)
if err != nil {
return nil, err
}
defer f.Close()
reader := csv.NewReader(f)
reader.FieldsPerRecord = -1
tokens := map[string]string{}
for {
record, err := reader.Read()
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
if len(record) < 2 {
continue
}
tokens[record[1]] = record[0]
}
return tokens, nil
return nil
}
func readTokens(runtime *config.ControlRuntime) error {
tokens, err := readTokenFile(runtime.PasswdFile)
tokens, err := passwd.Read(runtime.PasswdFile)
if err != nil {
return err
}
if nodeToken, ok := tokens["node"]; ok {
runtime.NodeToken = "node:" + nodeToken
if nodeToken, ok := tokens.Pass("node"); ok {
runtime.AgentToken = "node:" + nodeToken
}
if clientToken, ok := tokens["admin"]; ok {
if serverToken, ok := tokens.Pass("server"); ok {
runtime.AgentToken = "server:" + serverToken
}
if clientToken, ok := tokens.Pass("admin"); ok {
runtime.ClientToken = "admin:" + clientToken
}
return nil
}
func ensureNodeToken(config *config.Control, runtime *config.ControlRuntime) error {
if config.ClusterSecret == "" {
return nil
}
f, err := os.Open(runtime.PasswdFile)
if err != nil {
return err
}
defer f.Close()
records := [][]string{}
reader := csv.NewReader(f)
for {
record, err := reader.Read()
if err == io.EOF {
break
}
if err != nil {
return err
}
if len(record) < 3 {
return fmt.Errorf("password file '%s' must have at least 3 columns (password, user name, user uid), found %d", runtime.PasswdFile, len(record))
}
if record[1] == "node" {
if record[0] == config.ClusterSecret {
return nil
}
record[0] = config.ClusterSecret
}
records = append(records, record)
}
f.Close()
return WritePasswords(runtime.PasswdFile, records)
}
func WritePasswords(passwdFile string, records [][]string) error {
out, err := os.Create(passwdFile + ".tmp")
if err != nil {
return err
}
defer out.Close()
if err := out.Chmod(0600); err != nil {
return err
}
if err := csv.NewWriter(out).WriteAll(records); err != nil {
return err
}
return os.Rename(passwdFile+".tmp", passwdFile)
}
func genEncryptedNetworkInfo(controlConfig *config.Control, runtime *config.ControlRuntime) error {
if s, err := os.Stat(runtime.IPSECKey); err == nil && s.Size() > 0 {
psk, err := ioutil.ReadFile(runtime.IPSECKey)
@ -455,7 +378,7 @@ func genEncryptedNetworkInfo(controlConfig *config.Control, runtime *config.Cont
return nil
}
psk, err := getToken(ipsecTokenSize)
psk, err := token.Random(ipsecTokenSize)
if err != nil {
return err
}
@ -468,42 +391,71 @@ func genEncryptedNetworkInfo(controlConfig *config.Control, runtime *config.Cont
return nil
}
func genUsers(config *config.Control, runtime *config.ControlRuntime) error {
if s, err := os.Stat(runtime.PasswdFile); err == nil && s.Size() > 0 {
return ensureNodeToken(config, runtime)
func migratePassword(p *passwd.Passwd) error {
server, _ := p.Pass("server")
node, _ := p.Pass("node")
if server == "" && node != "" {
return p.EnsureUser("server", "k3s:server", node)
}
adminToken, err := getToken(userTokenSize)
if err != nil {
return err
}
systemToken, err := getToken(userTokenSize)
if err != nil {
return err
}
nodeToken, err := getToken(userTokenSize)
if err != nil {
return err
}
if config.ClusterSecret != "" {
nodeToken = config.ClusterSecret
}
return WritePasswords(runtime.PasswdFile, [][]string{
{adminToken, "admin", "admin", "system:masters"},
{systemToken, "system", "system", "system:masters"},
{nodeToken, "node", "node", "system:masters"},
})
return nil
}
func getToken(size int) (string, error) {
token := make([]byte, size, size)
_, err := cryptorand.Read(token)
if err != nil {
return "", err
func getServerPass(passwd *passwd.Passwd, config *config.Control) (string, error) {
var (
err error
)
serverPass := config.Token
if serverPass == "" {
serverPass, _ = passwd.Pass("server")
}
return hex.EncodeToString(token), err
if serverPass == "" {
serverPass, err = token.Random(16)
if err != nil {
return "", err
}
}
return serverPass, nil
}
func getNodePass(config *config.Control, serverPass string) string {
if config.AgentToken == "" {
return serverPass
}
return config.AgentToken
}
func genUsers(config *config.Control, runtime *config.ControlRuntime) error {
passwd, err := passwd.Read(runtime.PasswdFile)
if err != nil {
return err
}
if err := migratePassword(passwd); err != nil {
return err
}
serverPass, err := getServerPass(passwd, config)
if err != nil {
return err
}
nodePass := getNodePass(config, serverPass)
if err := passwd.EnsureUser("admin", "system:masters", ""); err != nil {
return err
}
if err := passwd.EnsureUser("node", "k3s:agent", nodePass); err != nil {
return err
}
if err := passwd.EnsureUser("server", "k3s:server", serverPass); err != nil {
return err
}
return passwd.Write(runtime.PasswdFile)
}
func genCerts(config *config.Control, runtime *config.ControlRuntime) error {
@ -578,7 +530,10 @@ func genClientCerts(config *config.Control, runtime *config.ControlRuntime) erro
}
}
if _, err = factory("system:kube-proxy", []string{"system:nodes"}, runtime.ClientKubeProxyCert, runtime.ClientKubeProxyKey); err != nil {
if _, err = factory("system:kube-proxy", nil, runtime.ClientKubeProxyCert, runtime.ClientKubeProxyKey); err != nil {
return err
}
if _, err = factory("system:k3s-controller", nil, runtime.ClientK3sControllerCert, runtime.ClientK3sControllerKey); err != nil {
return err
}

View File

@ -4,6 +4,7 @@ import (
"context"
"net"
"net/http"
"strings"
"time"
"github.com/rancher/remotedialer"
@ -37,14 +38,9 @@ func authorizer(req *http.Request) (clientKey string, authed bool, err error) {
return "", false, nil
}
if user.GetName() != "node" {
return "", false, nil
if strings.HasPrefix(user.GetName(), "system:node:") {
return strings.TrimPrefix(user.GetName(), "system:node:"), true, nil
}
nodeName := req.Header.Get("X-K3s-NodeName")
if nodeName == "" {
return "", false, nil
}
return nodeName, true, nil
return "", false, nil
}

View File

@ -2,6 +2,7 @@ package datadir
import (
"os"
"path/filepath"
"github.com/pkg/errors"
"github.com/rancher/wrangler/pkg/resolvehome"
@ -32,5 +33,5 @@ func LocalHome(dataDir string, forceLocal bool) (string, error) {
return "", errors.Wrapf(err, "resolving %s", dataDir)
}
return dataDir, nil
return filepath.Abs(dataDir)
}

View File

@ -141,7 +141,7 @@ func localStorageYaml() (*asset, error) {
return a, nil
}
var _rolebindingsYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\xcf\xbd\x0a\xc2\x40\x10\x04\xe0\xfe\x9e\xe2\x5e\xe0\x22\x76\x72\xa5\x16\xf6\x01\xed\x37\xb9\x55\xd7\xdc\x1f\xbb\x7b\x01\x7d\x7a\x09\x48\x1a\x51\xb0\x1c\x18\xe6\x63\xa0\xd2\x19\x59\xa8\x64\x6f\x79\x80\xb1\x83\xa6\xb7\xc2\xf4\x04\xa5\x92\xbb\x69\x27\x1d\x95\xcd\xbc\x35\x13\xe5\xe0\xed\x21\x36\x51\xe4\xbe\x44\xdc\x53\x0e\x94\xaf\x26\xa1\x42\x00\x05\x6f\xac\xcd\x90\xd0\xdb\xa9\x0d\xe8\xa0\x92\x20\xcf\xc8\x6e\x89\x11\xd5\x41\x48\x94\x0d\x97\x88\x3d\x5e\x96\x36\x54\x3a\x72\x69\xf5\x87\x6c\xac\xfd\x80\x57\x47\x1e\xa2\x98\xfc\xba\x5f\xe9\x6d\x48\x1b\xee\x38\xaa\x78\xe3\xfe\x42\x4e\x82\xfc\xe5\x85\x79\x05\x00\x00\xff\xff\x54\xf2\x55\xe2\x29\x01\x00\x00")
var _rolebindingsYaml = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xac\x92\x31\x6f\xe3\x30\x0c\x85\x77\xfd\x0a\x21\xbb\x72\x38\xdc\x72\xf0\xd8\x0e\xdd\x03\xb4\x3b\x6d\xb3\x09\x6b\x59\x14\x48\x2a\x41\xfb\xeb\x0b\xa7\x6e\x82\xa4\x76\x90\xb4\xdd\x24\x41\x7c\x1f\x1f\xf9\x20\xd3\x13\x8a\x12\xa7\xca\x4b\x0d\xcd\x12\x8a\x6d\x58\xe8\x0d\x8c\x38\x2d\xbb\xff\xba\x24\xfe\xb3\xfd\xeb\x3a\x4a\x6d\xe5\xef\x63\x51\x43\x59\x71\xc4\x3b\x4a\x2d\xa5\xb5\xeb\xd1\xa0\x05\x83\xca\x79\x9f\xa0\xc7\xca\x77\xa5\xc6\x00\x99\x14\x65\x8b\x12\x86\x6b\x44\x0b\xd0\xf6\x94\x9c\x70\xc4\x15\x3e\x0f\xbf\x21\xd3\x83\x70\xc9\x17\xc8\xce\xfb\x2f\xe0\x03\x47\x5f\xd5\xb0\xaf\x0e\xfa\x99\x46\x86\x96\xfa\x05\x1b\xd3\xca\x85\x9b\x20\x8f\x8a\x32\xe3\xc2\xb9\x10\x82\xfb\xfe\xb4\x26\xc6\xf4\xd9\xfe\x3f\x0d\x0d\x27\x13\x8e\x11\xc5\x49\x89\x78\xd2\xb8\x0e\x15\xc1\x2f\x16\xce\x7b\x41\xe5\x22\x0d\x8e\x6f\x89\x5b\x54\xe7\xfd\x16\xa5\x1e\x9f\xd6\x68\x57\xd6\x42\x8f\x9a\xa1\x39\x17\x88\xa4\xb6\x3f\xec\xc0\x9a\xcd\x84\x56\x42\xdb\xb1\x74\x94\xd6\xa3\xdf\x29\xf1\x8f\x3f\x99\x23\x35\x74\x33\x61\x42\x10\x53\x9b\x99\x92\xe9\xfe\x96\xb9\x9d\xd3\x1c\xfc\x1f\xb5\x7f\xb8\xb4\xf9\x88\xcf\xec\xee\xf7\xb3\x7d\x0a\x38\x06\x7b\xf0\x78\x1d\xe3\x2c\xdc\x97\x01\xef\x01\x00\x00\xff\xff\x46\xd3\x6d\x9d\x0f\x04\x00\x00")
func rolebindingsYamlBytes() ([]byte, error) {
return bindataRead(

View File

@ -32,10 +32,6 @@ func (c *FakeK3sV1) Addons(namespace string) v1.AddonInterface {
return &FakeAddons{c, namespace}
}
func (c *FakeK3sV1) ListenerConfigs(namespace string) v1.ListenerConfigInterface {
return &FakeListenerConfigs{c, namespace}
}
// RESTClient returns a RESTClient that is used to communicate
// with API server by this client implementation.
func (c *FakeK3sV1) RESTClient() rest.Interface {

View File

@ -1,140 +0,0 @@
/*
Copyright The Kubernetes 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.
*/
// Code generated by main. DO NOT EDIT.
package fake
import (
k3scattleiov1 "github.com/rancher/k3s/pkg/apis/k3s.cattle.io/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
labels "k8s.io/apimachinery/pkg/labels"
schema "k8s.io/apimachinery/pkg/runtime/schema"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
testing "k8s.io/client-go/testing"
)
// FakeListenerConfigs implements ListenerConfigInterface
type FakeListenerConfigs struct {
Fake *FakeK3sV1
ns string
}
var listenerconfigsResource = schema.GroupVersionResource{Group: "k3s.cattle.io", Version: "v1", Resource: "listenerconfigs"}
var listenerconfigsKind = schema.GroupVersionKind{Group: "k3s.cattle.io", Version: "v1", Kind: "ListenerConfig"}
// Get takes name of the listenerConfig, and returns the corresponding listenerConfig object, and an error if there is any.
func (c *FakeListenerConfigs) Get(name string, options v1.GetOptions) (result *k3scattleiov1.ListenerConfig, err error) {
obj, err := c.Fake.
Invokes(testing.NewGetAction(listenerconfigsResource, c.ns, name), &k3scattleiov1.ListenerConfig{})
if obj == nil {
return nil, err
}
return obj.(*k3scattleiov1.ListenerConfig), err
}
// List takes label and field selectors, and returns the list of ListenerConfigs that match those selectors.
func (c *FakeListenerConfigs) List(opts v1.ListOptions) (result *k3scattleiov1.ListenerConfigList, err error) {
obj, err := c.Fake.
Invokes(testing.NewListAction(listenerconfigsResource, listenerconfigsKind, c.ns, opts), &k3scattleiov1.ListenerConfigList{})
if obj == nil {
return nil, err
}
label, _, _ := testing.ExtractFromListOptions(opts)
if label == nil {
label = labels.Everything()
}
list := &k3scattleiov1.ListenerConfigList{ListMeta: obj.(*k3scattleiov1.ListenerConfigList).ListMeta}
for _, item := range obj.(*k3scattleiov1.ListenerConfigList).Items {
if label.Matches(labels.Set(item.Labels)) {
list.Items = append(list.Items, item)
}
}
return list, err
}
// Watch returns a watch.Interface that watches the requested listenerConfigs.
func (c *FakeListenerConfigs) Watch(opts v1.ListOptions) (watch.Interface, error) {
return c.Fake.
InvokesWatch(testing.NewWatchAction(listenerconfigsResource, c.ns, opts))
}
// Create takes the representation of a listenerConfig and creates it. Returns the server's representation of the listenerConfig, and an error, if there is any.
func (c *FakeListenerConfigs) Create(listenerConfig *k3scattleiov1.ListenerConfig) (result *k3scattleiov1.ListenerConfig, err error) {
obj, err := c.Fake.
Invokes(testing.NewCreateAction(listenerconfigsResource, c.ns, listenerConfig), &k3scattleiov1.ListenerConfig{})
if obj == nil {
return nil, err
}
return obj.(*k3scattleiov1.ListenerConfig), err
}
// Update takes the representation of a listenerConfig and updates it. Returns the server's representation of the listenerConfig, and an error, if there is any.
func (c *FakeListenerConfigs) Update(listenerConfig *k3scattleiov1.ListenerConfig) (result *k3scattleiov1.ListenerConfig, err error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateAction(listenerconfigsResource, c.ns, listenerConfig), &k3scattleiov1.ListenerConfig{})
if obj == nil {
return nil, err
}
return obj.(*k3scattleiov1.ListenerConfig), err
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *FakeListenerConfigs) UpdateStatus(listenerConfig *k3scattleiov1.ListenerConfig) (*k3scattleiov1.ListenerConfig, error) {
obj, err := c.Fake.
Invokes(testing.NewUpdateSubresourceAction(listenerconfigsResource, "status", c.ns, listenerConfig), &k3scattleiov1.ListenerConfig{})
if obj == nil {
return nil, err
}
return obj.(*k3scattleiov1.ListenerConfig), err
}
// Delete takes name of the listenerConfig and deletes it. Returns an error if one occurs.
func (c *FakeListenerConfigs) Delete(name string, options *v1.DeleteOptions) error {
_, err := c.Fake.
Invokes(testing.NewDeleteAction(listenerconfigsResource, c.ns, name), &k3scattleiov1.ListenerConfig{})
return err
}
// DeleteCollection deletes a collection of objects.
func (c *FakeListenerConfigs) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
action := testing.NewDeleteCollectionAction(listenerconfigsResource, c.ns, listOptions)
_, err := c.Fake.Invokes(action, &k3scattleiov1.ListenerConfigList{})
return err
}
// Patch applies the patch and returns the patched listenerConfig.
func (c *FakeListenerConfigs) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *k3scattleiov1.ListenerConfig, err error) {
obj, err := c.Fake.
Invokes(testing.NewPatchSubresourceAction(listenerconfigsResource, c.ns, name, pt, data, subresources...), &k3scattleiov1.ListenerConfig{})
if obj == nil {
return nil, err
}
return obj.(*k3scattleiov1.ListenerConfig), err
}

View File

@ -19,5 +19,3 @@ limitations under the License.
package v1
type AddonExpansion interface{}
type ListenerConfigExpansion interface{}

View File

@ -27,7 +27,6 @@ import (
type K3sV1Interface interface {
RESTClient() rest.Interface
AddonsGetter
ListenerConfigsGetter
}
// K3sV1Client is used to interact with features provided by the k3s.cattle.io group.
@ -39,10 +38,6 @@ func (c *K3sV1Client) Addons(namespace string) AddonInterface {
return newAddons(c, namespace)
}
func (c *K3sV1Client) ListenerConfigs(namespace string) ListenerConfigInterface {
return newListenerConfigs(c, namespace)
}
// NewForConfig creates a new K3sV1Client for the given config.
func NewForConfig(c *rest.Config) (*K3sV1Client, error) {
config := *c

View File

@ -1,191 +0,0 @@
/*
Copyright The Kubernetes 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.
*/
// Code generated by main. DO NOT EDIT.
package v1
import (
"time"
v1 "github.com/rancher/k3s/pkg/apis/k3s.cattle.io/v1"
scheme "github.com/rancher/k3s/pkg/generated/clientset/versioned/scheme"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
types "k8s.io/apimachinery/pkg/types"
watch "k8s.io/apimachinery/pkg/watch"
rest "k8s.io/client-go/rest"
)
// ListenerConfigsGetter has a method to return a ListenerConfigInterface.
// A group's client should implement this interface.
type ListenerConfigsGetter interface {
ListenerConfigs(namespace string) ListenerConfigInterface
}
// ListenerConfigInterface has methods to work with ListenerConfig resources.
type ListenerConfigInterface interface {
Create(*v1.ListenerConfig) (*v1.ListenerConfig, error)
Update(*v1.ListenerConfig) (*v1.ListenerConfig, error)
UpdateStatus(*v1.ListenerConfig) (*v1.ListenerConfig, error)
Delete(name string, options *metav1.DeleteOptions) error
DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error
Get(name string, options metav1.GetOptions) (*v1.ListenerConfig, error)
List(opts metav1.ListOptions) (*v1.ListenerConfigList, error)
Watch(opts metav1.ListOptions) (watch.Interface, error)
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.ListenerConfig, err error)
ListenerConfigExpansion
}
// listenerConfigs implements ListenerConfigInterface
type listenerConfigs struct {
client rest.Interface
ns string
}
// newListenerConfigs returns a ListenerConfigs
func newListenerConfigs(c *K3sV1Client, namespace string) *listenerConfigs {
return &listenerConfigs{
client: c.RESTClient(),
ns: namespace,
}
}
// Get takes name of the listenerConfig, and returns the corresponding listenerConfig object, and an error if there is any.
func (c *listenerConfigs) Get(name string, options metav1.GetOptions) (result *v1.ListenerConfig, err error) {
result = &v1.ListenerConfig{}
err = c.client.Get().
Namespace(c.ns).
Resource("listenerconfigs").
Name(name).
VersionedParams(&options, scheme.ParameterCodec).
Do().
Into(result)
return
}
// List takes label and field selectors, and returns the list of ListenerConfigs that match those selectors.
func (c *listenerConfigs) List(opts metav1.ListOptions) (result *v1.ListenerConfigList, err error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
result = &v1.ListenerConfigList{}
err = c.client.Get().
Namespace(c.ns).
Resource("listenerconfigs").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Do().
Into(result)
return
}
// Watch returns a watch.Interface that watches the requested listenerConfigs.
func (c *listenerConfigs) Watch(opts metav1.ListOptions) (watch.Interface, error) {
var timeout time.Duration
if opts.TimeoutSeconds != nil {
timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
}
opts.Watch = true
return c.client.Get().
Namespace(c.ns).
Resource("listenerconfigs").
VersionedParams(&opts, scheme.ParameterCodec).
Timeout(timeout).
Watch()
}
// Create takes the representation of a listenerConfig and creates it. Returns the server's representation of the listenerConfig, and an error, if there is any.
func (c *listenerConfigs) Create(listenerConfig *v1.ListenerConfig) (result *v1.ListenerConfig, err error) {
result = &v1.ListenerConfig{}
err = c.client.Post().
Namespace(c.ns).
Resource("listenerconfigs").
Body(listenerConfig).
Do().
Into(result)
return
}
// Update takes the representation of a listenerConfig and updates it. Returns the server's representation of the listenerConfig, and an error, if there is any.
func (c *listenerConfigs) Update(listenerConfig *v1.ListenerConfig) (result *v1.ListenerConfig, err error) {
result = &v1.ListenerConfig{}
err = c.client.Put().
Namespace(c.ns).
Resource("listenerconfigs").
Name(listenerConfig.Name).
Body(listenerConfig).
Do().
Into(result)
return
}
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *listenerConfigs) UpdateStatus(listenerConfig *v1.ListenerConfig) (result *v1.ListenerConfig, err error) {
result = &v1.ListenerConfig{}
err = c.client.Put().
Namespace(c.ns).
Resource("listenerconfigs").
Name(listenerConfig.Name).
SubResource("status").
Body(listenerConfig).
Do().
Into(result)
return
}
// Delete takes name of the listenerConfig and deletes it. Returns an error if one occurs.
func (c *listenerConfigs) Delete(name string, options *metav1.DeleteOptions) error {
return c.client.Delete().
Namespace(c.ns).
Resource("listenerconfigs").
Name(name).
Body(options).
Do().
Error()
}
// DeleteCollection deletes a collection of objects.
func (c *listenerConfigs) DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error {
var timeout time.Duration
if listOptions.TimeoutSeconds != nil {
timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second
}
return c.client.Delete().
Namespace(c.ns).
Resource("listenerconfigs").
VersionedParams(&listOptions, scheme.ParameterCodec).
Timeout(timeout).
Body(options).
Do().
Error()
}
// Patch applies the patch and returns the patched listenerConfig.
func (c *listenerConfigs) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.ListenerConfig, err error) {
result = &v1.ListenerConfig{}
err = c.client.Patch(pt).
Namespace(c.ns).
Resource("listenerconfigs").
SubResource(subresources...).
Name(name).
Body(data).
Do().
Into(result)
return
}

View File

@ -27,7 +27,6 @@ import (
type Interface interface {
Addon() AddonController
ListenerConfig() ListenerConfigController
}
func New(controllerManager *generic.ControllerManager, client clientset.K3sV1Interface,
@ -48,6 +47,3 @@ type version struct {
func (c *version) Addon() AddonController {
return NewAddonController(v1.SchemeGroupVersion.WithKind("Addon"), c.controllerManager, c.client, c.informers.Addons())
}
func (c *version) ListenerConfig() ListenerConfigController {
return NewListenerConfigController(v1.SchemeGroupVersion.WithKind("ListenerConfig"), c.controllerManager, c.client, c.informers.ListenerConfigs())
}

View File

@ -1,242 +0,0 @@
/*
Copyright The Kubernetes 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.
*/
// Code generated by main. DO NOT EDIT.
package v1
import (
"context"
v1 "github.com/rancher/k3s/pkg/apis/k3s.cattle.io/v1"
clientset "github.com/rancher/k3s/pkg/generated/clientset/versioned/typed/k3s.cattle.io/v1"
informers "github.com/rancher/k3s/pkg/generated/informers/externalversions/k3s.cattle.io/v1"
listers "github.com/rancher/k3s/pkg/generated/listers/k3s.cattle.io/v1"
"github.com/rancher/wrangler/pkg/generic"
"k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/tools/cache"
)
type ListenerConfigHandler func(string, *v1.ListenerConfig) (*v1.ListenerConfig, error)
type ListenerConfigController interface {
ListenerConfigClient
OnChange(ctx context.Context, name string, sync ListenerConfigHandler)
OnRemove(ctx context.Context, name string, sync ListenerConfigHandler)
Enqueue(namespace, name string)
Cache() ListenerConfigCache
Informer() cache.SharedIndexInformer
GroupVersionKind() schema.GroupVersionKind
AddGenericHandler(ctx context.Context, name string, handler generic.Handler)
AddGenericRemoveHandler(ctx context.Context, name string, handler generic.Handler)
Updater() generic.Updater
}
type ListenerConfigClient interface {
Create(*v1.ListenerConfig) (*v1.ListenerConfig, error)
Update(*v1.ListenerConfig) (*v1.ListenerConfig, error)
UpdateStatus(*v1.ListenerConfig) (*v1.ListenerConfig, error)
Delete(namespace, name string, options *metav1.DeleteOptions) error
Get(namespace, name string, options metav1.GetOptions) (*v1.ListenerConfig, error)
List(namespace string, opts metav1.ListOptions) (*v1.ListenerConfigList, error)
Watch(namespace string, opts metav1.ListOptions) (watch.Interface, error)
Patch(namespace, name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.ListenerConfig, err error)
}
type ListenerConfigCache interface {
Get(namespace, name string) (*v1.ListenerConfig, error)
List(namespace string, selector labels.Selector) ([]*v1.ListenerConfig, error)
AddIndexer(indexName string, indexer ListenerConfigIndexer)
GetByIndex(indexName, key string) ([]*v1.ListenerConfig, error)
}
type ListenerConfigIndexer func(obj *v1.ListenerConfig) ([]string, error)
type listenerConfigController struct {
controllerManager *generic.ControllerManager
clientGetter clientset.ListenerConfigsGetter
informer informers.ListenerConfigInformer
gvk schema.GroupVersionKind
}
func NewListenerConfigController(gvk schema.GroupVersionKind, controllerManager *generic.ControllerManager, clientGetter clientset.ListenerConfigsGetter, informer informers.ListenerConfigInformer) ListenerConfigController {
return &listenerConfigController{
controllerManager: controllerManager,
clientGetter: clientGetter,
informer: informer,
gvk: gvk,
}
}
func FromListenerConfigHandlerToHandler(sync ListenerConfigHandler) generic.Handler {
return func(key string, obj runtime.Object) (ret runtime.Object, err error) {
var v *v1.ListenerConfig
if obj == nil {
v, err = sync(key, nil)
} else {
v, err = sync(key, obj.(*v1.ListenerConfig))
}
if v == nil {
return nil, err
}
return v, err
}
}
func (c *listenerConfigController) Updater() generic.Updater {
return func(obj runtime.Object) (runtime.Object, error) {
newObj, err := c.Update(obj.(*v1.ListenerConfig))
if newObj == nil {
return nil, err
}
return newObj, err
}
}
func UpdateListenerConfigOnChange(updater generic.Updater, handler ListenerConfigHandler) ListenerConfigHandler {
return func(key string, obj *v1.ListenerConfig) (*v1.ListenerConfig, error) {
if obj == nil {
return handler(key, nil)
}
copyObj := obj.DeepCopy()
newObj, err := handler(key, copyObj)
if newObj != nil {
copyObj = newObj
}
if obj.ResourceVersion == copyObj.ResourceVersion && !equality.Semantic.DeepEqual(obj, copyObj) {
newObj, err := updater(copyObj)
if newObj != nil && err == nil {
copyObj = newObj.(*v1.ListenerConfig)
}
}
return copyObj, err
}
}
func (c *listenerConfigController) AddGenericHandler(ctx context.Context, name string, handler generic.Handler) {
c.controllerManager.AddHandler(ctx, c.gvk, c.informer.Informer(), name, handler)
}
func (c *listenerConfigController) AddGenericRemoveHandler(ctx context.Context, name string, handler generic.Handler) {
removeHandler := generic.NewRemoveHandler(name, c.Updater(), handler)
c.controllerManager.AddHandler(ctx, c.gvk, c.informer.Informer(), name, removeHandler)
}
func (c *listenerConfigController) OnChange(ctx context.Context, name string, sync ListenerConfigHandler) {
c.AddGenericHandler(ctx, name, FromListenerConfigHandlerToHandler(sync))
}
func (c *listenerConfigController) OnRemove(ctx context.Context, name string, sync ListenerConfigHandler) {
removeHandler := generic.NewRemoveHandler(name, c.Updater(), FromListenerConfigHandlerToHandler(sync))
c.AddGenericHandler(ctx, name, removeHandler)
}
func (c *listenerConfigController) Enqueue(namespace, name string) {
c.controllerManager.Enqueue(c.gvk, c.informer.Informer(), namespace, name)
}
func (c *listenerConfigController) Informer() cache.SharedIndexInformer {
return c.informer.Informer()
}
func (c *listenerConfigController) GroupVersionKind() schema.GroupVersionKind {
return c.gvk
}
func (c *listenerConfigController) Cache() ListenerConfigCache {
return &listenerConfigCache{
lister: c.informer.Lister(),
indexer: c.informer.Informer().GetIndexer(),
}
}
func (c *listenerConfigController) Create(obj *v1.ListenerConfig) (*v1.ListenerConfig, error) {
return c.clientGetter.ListenerConfigs(obj.Namespace).Create(obj)
}
func (c *listenerConfigController) Update(obj *v1.ListenerConfig) (*v1.ListenerConfig, error) {
return c.clientGetter.ListenerConfigs(obj.Namespace).Update(obj)
}
func (c *listenerConfigController) UpdateStatus(obj *v1.ListenerConfig) (*v1.ListenerConfig, error) {
return c.clientGetter.ListenerConfigs(obj.Namespace).UpdateStatus(obj)
}
func (c *listenerConfigController) Delete(namespace, name string, options *metav1.DeleteOptions) error {
return c.clientGetter.ListenerConfigs(namespace).Delete(name, options)
}
func (c *listenerConfigController) Get(namespace, name string, options metav1.GetOptions) (*v1.ListenerConfig, error) {
return c.clientGetter.ListenerConfigs(namespace).Get(name, options)
}
func (c *listenerConfigController) List(namespace string, opts metav1.ListOptions) (*v1.ListenerConfigList, error) {
return c.clientGetter.ListenerConfigs(namespace).List(opts)
}
func (c *listenerConfigController) Watch(namespace string, opts metav1.ListOptions) (watch.Interface, error) {
return c.clientGetter.ListenerConfigs(namespace).Watch(opts)
}
func (c *listenerConfigController) Patch(namespace, name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.ListenerConfig, err error) {
return c.clientGetter.ListenerConfigs(namespace).Patch(name, pt, data, subresources...)
}
type listenerConfigCache struct {
lister listers.ListenerConfigLister
indexer cache.Indexer
}
func (c *listenerConfigCache) Get(namespace, name string) (*v1.ListenerConfig, error) {
return c.lister.ListenerConfigs(namespace).Get(name)
}
func (c *listenerConfigCache) List(namespace string, selector labels.Selector) ([]*v1.ListenerConfig, error) {
return c.lister.ListenerConfigs(namespace).List(selector)
}
func (c *listenerConfigCache) AddIndexer(indexName string, indexer ListenerConfigIndexer) {
utilruntime.Must(c.indexer.AddIndexers(map[string]cache.IndexFunc{
indexName: func(obj interface{}) (strings []string, e error) {
return indexer(obj.(*v1.ListenerConfig))
},
}))
}
func (c *listenerConfigCache) GetByIndex(indexName, key string) (result []*v1.ListenerConfig, err error) {
objs, err := c.indexer.ByIndex(indexName, key)
if err != nil {
return nil, err
}
for _, obj := range objs {
result = append(result, obj.(*v1.ListenerConfig))
}
return result, nil
}

View File

@ -55,8 +55,6 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
// Group=k3s.cattle.io, Version=v1
case v1.SchemeGroupVersion.WithResource("addons"):
return &genericInformer{resource: resource.GroupResource(), informer: f.K3s().V1().Addons().Informer()}, nil
case v1.SchemeGroupVersion.WithResource("listenerconfigs"):
return &genericInformer{resource: resource.GroupResource(), informer: f.K3s().V1().ListenerConfigs().Informer()}, nil
}

View File

@ -26,8 +26,6 @@ import (
type Interface interface {
// Addons returns a AddonInformer.
Addons() AddonInformer
// ListenerConfigs returns a ListenerConfigInformer.
ListenerConfigs() ListenerConfigInformer
}
type version struct {
@ -45,8 +43,3 @@ func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakList
func (v *version) Addons() AddonInformer {
return &addonInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}
// ListenerConfigs returns a ListenerConfigInformer.
func (v *version) ListenerConfigs() ListenerConfigInformer {
return &listenerConfigInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
}

View File

@ -1,89 +0,0 @@
/*
Copyright The Kubernetes 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.
*/
// Code generated by main. DO NOT EDIT.
package v1
import (
time "time"
k3scattleiov1 "github.com/rancher/k3s/pkg/apis/k3s.cattle.io/v1"
versioned "github.com/rancher/k3s/pkg/generated/clientset/versioned"
internalinterfaces "github.com/rancher/k3s/pkg/generated/informers/externalversions/internalinterfaces"
v1 "github.com/rancher/k3s/pkg/generated/listers/k3s.cattle.io/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
watch "k8s.io/apimachinery/pkg/watch"
cache "k8s.io/client-go/tools/cache"
)
// ListenerConfigInformer provides access to a shared informer and lister for
// ListenerConfigs.
type ListenerConfigInformer interface {
Informer() cache.SharedIndexInformer
Lister() v1.ListenerConfigLister
}
type listenerConfigInformer struct {
factory internalinterfaces.SharedInformerFactory
tweakListOptions internalinterfaces.TweakListOptionsFunc
namespace string
}
// NewListenerConfigInformer constructs a new informer for ListenerConfig type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewListenerConfigInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
return NewFilteredListenerConfigInformer(client, namespace, resyncPeriod, indexers, nil)
}
// NewFilteredListenerConfigInformer constructs a new informer for ListenerConfig type.
// Always prefer using an informer factory to get a shared informer instead of getting an independent
// one. This reduces memory footprint and number of connections to the server.
func NewFilteredListenerConfigInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
return cache.NewSharedIndexInformer(
&cache.ListWatch{
ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.K3sV1().ListenerConfigs(namespace).List(options)
},
WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
if tweakListOptions != nil {
tweakListOptions(&options)
}
return client.K3sV1().ListenerConfigs(namespace).Watch(options)
},
},
&k3scattleiov1.ListenerConfig{},
resyncPeriod,
indexers,
)
}
func (f *listenerConfigInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
return NewFilteredListenerConfigInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
}
func (f *listenerConfigInformer) Informer() cache.SharedIndexInformer {
return f.factory.InformerFor(&k3scattleiov1.ListenerConfig{}, f.defaultInformer)
}
func (f *listenerConfigInformer) Lister() v1.ListenerConfigLister {
return v1.NewListenerConfigLister(f.Informer().GetIndexer())
}

View File

@ -25,11 +25,3 @@ type AddonListerExpansion interface{}
// AddonNamespaceListerExpansion allows custom methods to be added to
// AddonNamespaceLister.
type AddonNamespaceListerExpansion interface{}
// ListenerConfigListerExpansion allows custom methods to be added to
// ListenerConfigLister.
type ListenerConfigListerExpansion interface{}
// ListenerConfigNamespaceListerExpansion allows custom methods to be added to
// ListenerConfigNamespaceLister.
type ListenerConfigNamespaceListerExpansion interface{}

View File

@ -1,94 +0,0 @@
/*
Copyright The Kubernetes 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.
*/
// Code generated by main. DO NOT EDIT.
package v1
import (
v1 "github.com/rancher/k3s/pkg/apis/k3s.cattle.io/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/client-go/tools/cache"
)
// ListenerConfigLister helps list ListenerConfigs.
type ListenerConfigLister interface {
// List lists all ListenerConfigs in the indexer.
List(selector labels.Selector) (ret []*v1.ListenerConfig, err error)
// ListenerConfigs returns an object that can list and get ListenerConfigs.
ListenerConfigs(namespace string) ListenerConfigNamespaceLister
ListenerConfigListerExpansion
}
// listenerConfigLister implements the ListenerConfigLister interface.
type listenerConfigLister struct {
indexer cache.Indexer
}
// NewListenerConfigLister returns a new ListenerConfigLister.
func NewListenerConfigLister(indexer cache.Indexer) ListenerConfigLister {
return &listenerConfigLister{indexer: indexer}
}
// List lists all ListenerConfigs in the indexer.
func (s *listenerConfigLister) List(selector labels.Selector) (ret []*v1.ListenerConfig, err error) {
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
ret = append(ret, m.(*v1.ListenerConfig))
})
return ret, err
}
// ListenerConfigs returns an object that can list and get ListenerConfigs.
func (s *listenerConfigLister) ListenerConfigs(namespace string) ListenerConfigNamespaceLister {
return listenerConfigNamespaceLister{indexer: s.indexer, namespace: namespace}
}
// ListenerConfigNamespaceLister helps list and get ListenerConfigs.
type ListenerConfigNamespaceLister interface {
// List lists all ListenerConfigs in the indexer for a given namespace.
List(selector labels.Selector) (ret []*v1.ListenerConfig, err error)
// Get retrieves the ListenerConfig from the indexer for a given namespace and name.
Get(name string) (*v1.ListenerConfig, error)
ListenerConfigNamespaceListerExpansion
}
// listenerConfigNamespaceLister implements the ListenerConfigNamespaceLister
// interface.
type listenerConfigNamespaceLister struct {
indexer cache.Indexer
namespace string
}
// List lists all ListenerConfigs in the indexer for a given namespace.
func (s listenerConfigNamespaceLister) List(selector labels.Selector) (ret []*v1.ListenerConfig, err error) {
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
ret = append(ret, m.(*v1.ListenerConfig))
})
return ret, err
}
// Get retrieves the ListenerConfig from the indexer for a given namespace and name.
func (s listenerConfigNamespaceLister) Get(name string) (*v1.ListenerConfig, error) {
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
if err != nil {
return nil, err
}
if !exists {
return nil, errors.NewNotFound(v1.Resource("listenerconfig"), name)
}
return obj.(*v1.ListenerConfig), nil
}

151
pkg/passwd/passwd.go Normal file
View File

@ -0,0 +1,151 @@
package passwd
import (
"encoding/csv"
"fmt"
"io"
"os"
"strings"
"github.com/rancher/k3s/pkg/token"
)
type entry struct {
pass string
role string
}
type Passwd struct {
changed bool
names map[string]entry
}
func Read(file string) (*Passwd, error) {
result := &Passwd{
names: map[string]entry{},
}
f, err := os.Open(file)
if err != nil {
if os.IsNotExist(err) {
return result, nil
}
return nil, err
}
defer f.Close()
reader := csv.NewReader(f)
reader.FieldsPerRecord = -1
for {
record, err := reader.Read()
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
if len(record) < 3 {
return nil, fmt.Errorf("password file '%s' must have at least 3 columns (password, user name, user uid), found %d", file, len(record))
}
e := entry{
pass: record[0],
}
if len(record) > 3 {
e.role = record[3]
}
result.names[record[1]] = e
}
return result, nil
}
func (p *Passwd) Check(name, pass string) (matches bool, exists bool) {
e, ok := p.names[name]
if !ok {
return false, false
}
return e.pass == pass, true
}
func (p *Passwd) EnsureUser(name, role, passwd string) error {
tokenPrefix := "::" + name + ":"
idx := strings.Index(passwd, tokenPrefix)
if idx > 0 && strings.HasPrefix(passwd, "K10") {
passwd = passwd[idx+len(tokenPrefix):]
}
if e, ok := p.names[name]; ok {
if passwd != "" && e.pass != passwd {
p.changed = true
e.pass = passwd
}
if e.role != role {
p.changed = true
e.role = role
}
p.names[name] = e
return nil
}
if passwd == "" {
token, err := token.Random(16)
if err != nil {
return err
}
passwd = token
}
p.changed = true
p.names[name] = entry{
pass: passwd,
role: role,
}
return nil
}
func (p *Passwd) Pass(name string) (string, bool) {
e, ok := p.names[name]
if !ok {
return "", false
}
return e.pass, true
}
func (p *Passwd) Write(passwdFile string) error {
if !p.changed {
return nil
}
var records [][]string
for name, e := range p.names {
records = append(records, []string{
e.pass,
name,
name,
e.role,
})
}
return writePasswords(passwdFile, records)
}
func writePasswords(passwdFile string, records [][]string) error {
out, err := os.Create(passwdFile + ".tmp")
if err != nil {
return err
}
defer out.Close()
if err := out.Chmod(0600); err != nil {
return err
}
if err := csv.NewWriter(out).WriteAll(records); err != nil {
return err
}
return os.Rename(passwdFile+".tmp", passwdFile)
}

View File

@ -9,9 +9,21 @@ import (
"k8s.io/apiserver/pkg/endpoints/request"
)
func doAuth(serverConfig *config.Control, next http.Handler, rw http.ResponseWriter, req *http.Request) {
func hasRole(mustRoles []string, roles []string) bool {
for _, check := range roles {
for _, role := range mustRoles {
if role == check {
return true
}
}
}
return false
}
func doAuth(roles []string, serverConfig *config.Control, next http.Handler, rw http.ResponseWriter, req *http.Request) {
if serverConfig == nil || serverConfig.Runtime.Authenticator == nil {
next.ServeHTTP(rw, req)
logrus.Errorf("authenticate not initialized")
rw.WriteHeader(http.StatusUnauthorized)
return
}
@ -22,7 +34,7 @@ func doAuth(serverConfig *config.Control, next http.Handler, rw http.ResponseWri
return
}
if !ok || resp.User.GetName() != "node" {
if !ok || !hasRole(roles, resp.User.GetGroups()) {
rw.WriteHeader(http.StatusUnauthorized)
return
}
@ -32,10 +44,10 @@ func doAuth(serverConfig *config.Control, next http.Handler, rw http.ResponseWri
next.ServeHTTP(rw, req)
}
func authMiddleware(serverConfig *config.Control) mux.MiddlewareFunc {
func authMiddleware(serverConfig *config.Control, roles ...string) mux.MiddlewareFunc {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
doAuth(serverConfig, next, rw, req)
doAuth(roles, serverConfig, next, rw, req)
})
}
}

View File

@ -62,7 +62,6 @@ func crds(ctx context.Context, config *rest.Config) error {
}
factory.BatchCreateCRDs(ctx, crd.NamespacedTypes(
"ListenerConfig.k3s.cattle.io/v1",
"Addon.k3s.cattle.io/v1",
"HelmChart.helm.cattle.io/v1")...)

View File

@ -3,70 +3,68 @@ package server
import (
"crypto"
"crypto/x509"
"encoding/csv"
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/gorilla/mux"
certutil "github.com/rancher/dynamiclistener/cert"
"github.com/rancher/k3s/pkg/bootstrap"
"github.com/rancher/k3s/pkg/daemons/config"
"github.com/rancher/k3s/pkg/daemons/control"
"github.com/rancher/k3s/pkg/passwd"
"github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/util/json"
)
const (
jsonMediaType = "application/json"
binaryMediaType = "application/octet-stream"
pbMediaType = "application/com.github.proto-openapi.spec.v2@v1.0+protobuf"
openapiPrefix = "openapi."
staticURL = "/static/"
staticURL = "/static/"
)
type CACertsGetter func() (string, error)
func router(serverConfig *config.Control, tunnel http.Handler, cacertsGetter CACertsGetter) http.Handler {
func router(serverConfig *config.Control, tunnel http.Handler, ca []byte) http.Handler {
authed := mux.NewRouter()
authed.Use(authMiddleware(serverConfig))
authed.Use(authMiddleware(serverConfig, "k3s:agent"))
authed.NotFoundHandler = serverConfig.Runtime.Handler
authed.Path("/v1-k3s/connect").Handler(tunnel)
authed.Path("/v1-k3s/serving-kubelet.crt").Handler(servingKubeletCert(serverConfig))
authed.Path("/v1-k3s/serving-kubelet.key").Handler(fileHandler(serverConfig.Runtime.ServingKubeletKey))
authed.Path("/v1-k3s/client-kubelet.crt").Handler(clientKubeletCert(serverConfig))
authed.Path("/v1-k3s/client-kubelet.key").Handler(fileHandler(serverConfig.Runtime.ClientKubeletKey))
authed.Path("/v1-k3s/client-kube-proxy.crt").Handler(fileHandler(serverConfig.Runtime.ClientKubeProxyCert))
authed.Path("/v1-k3s/client-kube-proxy.key").Handler(fileHandler(serverConfig.Runtime.ClientKubeProxyKey))
authed.Path("/v1-k3s/client-k3s-controller.crt").Handler(fileHandler(serverConfig.Runtime.ClientK3sControllerCert))
authed.Path("/v1-k3s/client-k3s-controller.key").Handler(fileHandler(serverConfig.Runtime.ClientK3sControllerKey))
authed.Path("/v1-k3s/client-ca.crt").Handler(fileHandler(serverConfig.Runtime.ClientCA))
authed.Path("/v1-k3s/server-ca.crt").Handler(fileHandler(serverConfig.Runtime.ServerCA))
authed.Path("/v1-k3s/config").Handler(configHandler(serverConfig))
nodeAuthed := mux.NewRouter()
nodeAuthed.Use(authMiddleware(serverConfig, "system:nodes"))
nodeAuthed.Path("/v1-k3s/connect").Handler(tunnel)
nodeAuthed.NotFoundHandler = authed
serverAuthed := mux.NewRouter()
serverAuthed.Use(authMiddleware(serverConfig, "k3s:server"))
serverAuthed.NotFoundHandler = nodeAuthed
serverAuthed.Path("/v1-k3s/server-bootstrap").Handler(bootstrap.Handler(&serverConfig.Runtime.ControlRuntimeBootstrap))
staticDir := filepath.Join(serverConfig.DataDir, "static")
router := mux.NewRouter()
router.NotFoundHandler = authed
router.NotFoundHandler = serverAuthed
router.PathPrefix(staticURL).Handler(serveStatic(staticURL, staticDir))
router.Path("/cacerts").Handler(cacerts(cacertsGetter))
router.Path("/cacerts").Handler(cacerts(ca))
router.Path("/ping").Handler(ping())
return router
}
func cacerts(getter CACertsGetter) http.Handler {
func cacerts(ca []byte) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
content, err := getter()
if err != nil {
resp.WriteHeader(http.StatusInternalServerError)
resp.Write([]byte(err.Error()))
}
resp.Header().Set("content-type", "text/plain")
resp.Write([]byte(content))
resp.Write(ca)
})
}
@ -242,38 +240,19 @@ func sendError(err error, resp http.ResponseWriter, status ...int) {
resp.Write([]byte(err.Error()))
}
func ensureNodePassword(passwdFile, nodeName, passwd string) error {
records := [][]string{}
if _, err := os.Stat(passwdFile); !os.IsNotExist(err) {
f, err := os.Open(passwdFile)
if err != nil {
return err
}
defer f.Close()
reader := csv.NewReader(f)
for {
record, err := reader.Read()
if err == io.EOF {
break
}
if err != nil {
return err
}
if len(record) < 2 {
return fmt.Errorf("password file '%s' must have at least 2 columns (password, nodeName), found %d", passwdFile, len(record))
}
if record[1] == nodeName {
if record[0] == passwd {
return nil
}
return fmt.Errorf("Node password validation failed for '%s', using passwd file '%s'", nodeName, passwdFile)
}
records = append(records, record)
}
f.Close()
func ensureNodePassword(passwdFile, nodeName, pass string) error {
passwd, err := passwd.Read(passwdFile)
if err != nil {
return err
}
records = append(records, []string{passwd, nodeName})
return control.WritePasswords(passwdFile, records)
match, exists := passwd.Check(nodeName, pass)
if exists {
if !match {
return fmt.Errorf("Node password validation failed for '%s', using passwd file '%s'", nodeName, passwdFile)
}
return nil
}
// If user doesn't exist we save this password for future validation
passwd.EnsureUser(nodeName, "", pass)
return passwd.Write(passwdFile)
}

View File

@ -14,7 +14,6 @@ import (
"time"
"github.com/pkg/errors"
"github.com/rancher/dynamiclistener"
"github.com/rancher/helm-controller/pkg/helm"
"github.com/rancher/k3s/pkg/clientaccess"
"github.com/rancher/k3s/pkg/daemons/config"
@ -25,10 +24,11 @@ import (
"github.com/rancher/k3s/pkg/rootlessports"
"github.com/rancher/k3s/pkg/servicelb"
"github.com/rancher/k3s/pkg/static"
"github.com/rancher/k3s/pkg/tls"
v1 "github.com/rancher/wrangler-api/pkg/generated/controllers/core/v1"
"github.com/rancher/wrangler/pkg/leader"
"github.com/rancher/wrangler/pkg/resolvehome"
"github.com/sirupsen/logrus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/net"
)
@ -39,79 +39,67 @@ func resolveDataDir(dataDir string) (string, error) {
return filepath.Join(dataDir, "server"), err
}
func StartServer(ctx context.Context, config *Config) (string, error) {
func StartServer(ctx context.Context, config *Config) error {
if err := setupDataDirAndChdir(&config.ControlConfig); err != nil {
return "", err
return err
}
if err := setNoProxyEnv(&config.ControlConfig); err != nil {
return "", err
return err
}
if err := control.Server(ctx, &config.ControlConfig); err != nil {
return "", errors.Wrap(err, "starting kubernetes")
return errors.Wrap(err, "starting kubernetes")
}
certs, err := startWrangler(ctx, config)
if err != nil {
return "", errors.Wrap(err, "starting tls server")
if err := startWrangler(ctx, config); err != nil {
return errors.Wrap(err, "starting tls server")
}
ip := net2.ParseIP(config.TLSConfig.BindAddress)
ip := net2.ParseIP(config.ControlConfig.BindAddress)
if ip == nil {
ip, err = net.ChooseHostInterface()
if err != nil {
hostIP, err := net.ChooseHostInterface()
if err == nil {
ip = hostIP
} else {
ip = net2.ParseIP("127.0.0.1")
}
}
printTokens(certs, ip.String(), &config.TLSConfig, &config.ControlConfig)
writeKubeConfig(certs, &config.TLSConfig, config)
if err := printTokens(ip.String(), &config.ControlConfig); err != nil {
return err
}
return certs, nil
return writeKubeConfig(config.ControlConfig.Runtime.ServerCA, config)
}
func startWrangler(ctx context.Context, config *Config) (string, error) {
func startWrangler(ctx context.Context, config *Config) error {
var (
err error
tlsConfig = &config.TLSConfig
controlConfig = &config.ControlConfig
)
caBytes, err := ioutil.ReadFile(controlConfig.Runtime.ServerCA)
ca, err := ioutil.ReadFile(config.ControlConfig.Runtime.ServerCA)
if err != nil {
return "", err
}
caKeyBytes, err := ioutil.ReadFile(controlConfig.Runtime.ServerCAKey)
if err != nil {
return "", err
return err
}
certs := string(caBytes)
tlsConfig.CACerts = certs
tlsConfig.CAKey = string(caKeyBytes)
tlsConfig.Handler = router(controlConfig, controlConfig.Runtime.Tunnel, func() (string, error) {
return certs, nil
})
controlConfig.Runtime.Handler = router(controlConfig, controlConfig.Runtime.Tunnel, ca)
sc, err := newContext(ctx, controlConfig.Runtime.KubeConfigAdmin)
if err != nil {
return "", err
return err
}
if err := stageFiles(ctx, sc, controlConfig); err != nil {
return "", err
return err
}
_, err = tls.NewServer(ctx, sc.K3s.K3s().V1().ListenerConfig(), *tlsConfig)
if err != nil {
return "", err
if err := sc.Start(ctx); err != nil {
return err
}
if err := startNodeCache(ctx, sc); err != nil {
return "", err
}
controlConfig.Runtime.Core = sc.Core
start := func(ctx context.Context) {
if err := masterControllers(ctx, sc, config); err != nil {
@ -122,7 +110,7 @@ func startWrangler(ctx context.Context, config *Config) (string, error) {
}
}
if !config.DisableAgent {
go setMasterRoleLabel(ctx, sc)
go setMasterRoleLabel(ctx, sc.Core.Core().V1().Node())
}
if controlConfig.NoLeaderElect {
go func() {
@ -134,7 +122,7 @@ func startWrangler(ctx context.Context, config *Config) (string, error) {
go leader.RunOrDie(ctx, "", "k3s", sc.K8s, start)
}
return certs, nil
return nil
}
func masterControllers(ctx context.Context, sc *Context, config *Config) error {
@ -162,7 +150,7 @@ func masterControllers(ctx context.Context, sc *Context, config *Config) error {
}
if !config.DisableServiceLB && config.Rootless {
return rootlessports.Register(ctx, sc.Core.Core().V1().Service(), config.TLSConfig.HTTPSPort)
return rootlessports.Register(ctx, sc.Core.Core().V1().Service(), config.ControlConfig.HTTPSPort)
}
return nil
@ -203,7 +191,7 @@ func HomeKubeConfig(write, rootless bool) (string, error) {
return resolvehome.Resolve(datadir.HomeConfig)
}
func printTokens(certs, advertiseIP string, tlsConfig *dynamiclistener.UserConfig, config *config.Control) {
func printTokens(advertiseIP string, config *config.Control) error {
var (
nodeFile string
)
@ -212,26 +200,43 @@ func printTokens(certs, advertiseIP string, tlsConfig *dynamiclistener.UserConfi
advertiseIP = "127.0.0.1"
}
if len(config.Runtime.NodeToken) > 0 {
p := filepath.Join(config.DataDir, "node-token")
if err := writeToken(config.Runtime.NodeToken, p, certs); err == nil {
if len(config.Runtime.AgentToken) > 0 {
p := filepath.Join(config.DataDir, "token")
if err := writeToken(config.Runtime.AgentToken, p, config.Runtime.ServerCA); err == nil {
logrus.Infof("Node token is available at %s", p)
nodeFile = p
}
// backwards compatibility
np := filepath.Join(config.DataDir, "node-token")
if !isSymlink(np) {
if err := os.RemoveAll(np); err != nil {
return err
}
if err := os.Symlink(p, np); err != nil {
return err
}
}
}
if len(nodeFile) > 0 {
printToken(tlsConfig.HTTPSPort, advertiseIP, "To join node to cluster:", "agent")
printToken(config.HTTPSPort, advertiseIP, "To join node to cluster:", "agent")
}
return nil
}
func writeKubeConfig(certs string, tlsConfig *dynamiclistener.UserConfig, config *Config) {
clientToken := FormatToken(config.ControlConfig.Runtime.ClientToken, certs)
ip := tlsConfig.BindAddress
func writeKubeConfig(certs string, config *Config) error {
clientToken, err := FormatToken(config.ControlConfig.Runtime.ClientToken, certs)
if err != nil {
return err
}
ip := config.ControlConfig.BindAddress
if ip == "" {
ip = "127.0.0.1"
}
url := fmt.Sprintf("https://%s:%d", ip, tlsConfig.HTTPSPort)
url := fmt.Sprintf("https://%s:%d", ip, config.ControlConfig.HTTPSPort)
kubeConfig, err := HomeKubeConfig(true, config.Rootless)
def := true
if err != nil {
@ -274,6 +279,8 @@ func writeKubeConfig(certs string, tlsConfig *dynamiclistener.UserConfig, config
if def {
logrus.Infof("Run: %s kubectl", filepath.Base(os.Args[0]))
}
return nil
}
func setupDataDirAndChdir(config *config.Control) error {
@ -312,18 +319,22 @@ func printToken(httpsPort int, advertiseIP, prefix, cmd string) {
logrus.Infof("%s k3s %s -s https://%s:%d -t ${NODE_TOKEN}", prefix, cmd, ip, httpsPort)
}
func FormatToken(token string, certs string) string {
func FormatToken(token string, certFile string) (string, error) {
if len(token) == 0 {
return token
return token, nil
}
prefix := "K10"
if len(certs) > 0 {
digest := sha256.Sum256([]byte(certs))
if len(certFile) > 0 {
bytes, err := ioutil.ReadFile(certFile)
if err != nil {
return "", nil
}
digest := sha256.Sum256(bytes)
prefix = "K10" + hex.EncodeToString(digest[:]) + "::"
}
return prefix + token
return prefix + token, nil
}
func writeToken(token, file, certs string) error {
@ -331,7 +342,10 @@ func writeToken(token, file, certs string) error {
return nil
}
token = FormatToken(token, certs)
token, err := FormatToken(token, certs)
if err != nil {
return err
}
return ioutil.WriteFile(file, []byte(token+"\n"), 0600)
}
@ -364,26 +378,23 @@ func isSymlink(config string) bool {
return false
}
func setMasterRoleLabel(ctx context.Context, sc *Context) error {
func setMasterRoleLabel(ctx context.Context, nodes v1.NodeClient) error {
for {
nodeName := os.Getenv("NODE_NAME")
nodeController := sc.Core.Core().V1().Node()
nodeCache := nodeController.Cache()
nodeCached, err := nodeCache.Get(nodeName)
node, err := nodes.Get(nodeName, metav1.GetOptions{})
if err != nil {
logrus.Infof("Waiting for master node %s startup: %v", nodeName, err)
time.Sleep(1 * time.Second)
continue
}
if v, ok := nodeCached.Labels[MasterRoleLabelKey]; ok && v == "true" {
if v, ok := node.Labels[MasterRoleLabelKey]; ok && v == "true" {
break
}
node := nodeCached.DeepCopy()
if node.Labels == nil {
node.Labels = make(map[string]string)
}
node.Labels[MasterRoleLabelKey] = "true"
_, err = nodeController.Update(node)
_, err = nodes.Update(node)
if err == nil {
logrus.Infof("master role label has been set succesfully on node: %s", nodeName)
break
@ -396,9 +407,3 @@ func setMasterRoleLabel(ctx context.Context, sc *Context) error {
}
return nil
}
func startNodeCache(ctx context.Context, sc *Context) error {
sc.Core.Core().V1().Node().Cache()
return sc.Start(ctx)
}

View File

@ -1,14 +1,12 @@
package server
import (
"github.com/rancher/dynamiclistener"
"github.com/rancher/k3s/pkg/daemons/config"
)
type Config struct {
DisableAgent bool
DisableServiceLB bool
TLSConfig dynamiclistener.UserConfig
ControlConfig config.Control
Rootless bool
}

39
pkg/token/read.go Normal file
View File

@ -0,0 +1,39 @@
package token
import (
cryptorand "crypto/rand"
"encoding/hex"
"io/ioutil"
"os"
"strings"
"time"
"github.com/sirupsen/logrus"
)
func Random(size int) (string, error) {
token := make([]byte, size, size)
_, err := cryptorand.Read(token)
if err != nil {
return "", err
}
return hex.EncodeToString(token), err
}
func ReadFile(path string) (string, error) {
if path == "" {
return "", nil
}
for {
tokenBytes, err := ioutil.ReadFile(path)
if err == nil {
return strings.TrimSpace(string(tokenBytes)), nil
} else if os.IsNotExist(err) {
logrus.Infof("Waiting for %s to be available\n", path)
time.Sleep(2 * time.Second)
} else {
return "", err
}
}
}

View File

@ -26,7 +26,6 @@ The tool is sponsored by the [marvin + konsorten GmbH](http://www.konsorten.de).
We thank all the authors who provided code to this library:
* Felix Kollmann
* Nicolas Perraut
## License

View File

@ -1,11 +0,0 @@
// +build linux darwin
package sequences
import (
"fmt"
)
func EnableVirtualTerminalProcessing(stream uintptr, enable bool) error {
return fmt.Errorf("windows only package")
}

View File

@ -0,0 +1,80 @@
package factory
import (
"crypto/ecdsa"
"crypto/x509"
"io/ioutil"
"os"
)
func GenCA() (*x509.Certificate, *ecdsa.PrivateKey, error) {
caKey, err := NewPrivateKey()
if err != nil {
return nil, nil, err
}
caCert, err := NewSelfSignedCACert(caKey, "dynamiclistener-ca", "dynamiclistener-org")
if err != nil {
return nil, nil, err
}
return caCert, caKey, nil
}
func LoadOrGenCA() (*x509.Certificate, *ecdsa.PrivateKey, error) {
cert, key, err := loadCA()
if err == nil {
return cert, key, nil
}
cert, key, err = GenCA()
if err != nil {
return nil, nil, err
}
certBytes, keyBytes, err := Marshal(cert, key)
if err != nil {
return nil, nil, err
}
if err := os.MkdirAll("./certs", 0700); err != nil {
return nil, nil, err
}
if err := ioutil.WriteFile("./certs/ca.pem", certBytes, 0600); err != nil {
return nil, nil, err
}
if err := ioutil.WriteFile("./certs/ca.key", keyBytes, 0600); err != nil {
return nil, nil, err
}
return cert, key, nil
}
func loadCA() (*x509.Certificate, *ecdsa.PrivateKey, error) {
return LoadCerts("./certs/ca.pem", "./certs/ca.key")
}
func LoadCerts(certFile, keyFile string) (*x509.Certificate, *ecdsa.PrivateKey, error) {
caPem, err := ioutil.ReadFile(certFile)
if err != nil {
return nil, nil, err
}
caKey, err := ioutil.ReadFile(keyFile)
if err != nil {
return nil, nil, err
}
key, err := ParseECPrivateKeyPEM(caKey)
if err != nil {
return nil, nil, err
}
cert, err := ParseCertPEM(caPem)
if err != nil {
return nil, nil, err
}
return cert, key, nil
}

View File

@ -0,0 +1,105 @@
package factory
import (
"crypto"
"crypto/ecdsa"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"math"
"math/big"
"net"
"time"
)
const (
ECPrivateKeyBlockType = "EC PRIVATE KEY"
CertificateBlockType = "CERTIFICATE"
)
func NewSelfSignedCACert(key crypto.Signer, cn string, org ...string) (*x509.Certificate, error) {
now := time.Now()
tmpl := x509.Certificate{
BasicConstraintsValid: true,
IsCA: true,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
NotAfter: now.Add(time.Hour * 24 * 365 * 10).UTC(),
NotBefore: now.UTC(),
SerialNumber: new(big.Int).SetInt64(0),
Subject: pkix.Name{
CommonName: cn,
Organization: org,
},
}
certDERBytes, err := x509.CreateCertificate(rand.Reader, &tmpl, &tmpl, key.Public(), key)
if err != nil {
return nil, err
}
return x509.ParseCertificate(certDERBytes)
}
func NewSignedCert(signer crypto.Signer, caCert *x509.Certificate, caKey crypto.Signer, cn string, orgs []string,
domains []string, ips []net.IP) (*x509.Certificate, error) {
serialNumber, err := rand.Int(rand.Reader, new(big.Int).SetInt64(math.MaxInt64))
if err != nil {
return nil, err
}
parent := x509.Certificate{
DNSNames: domains,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
IPAddresses: ips,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
NotAfter: time.Now().Add(time.Hour * 24 * 365).UTC(),
NotBefore: caCert.NotBefore,
SerialNumber: serialNumber,
Subject: pkix.Name{
CommonName: cn,
Organization: orgs,
},
}
cert, err := x509.CreateCertificate(rand.Reader, &parent, caCert, signer.Public(), caKey)
if err != nil {
return nil, err
}
return x509.ParseCertificate(cert)
}
func ParseECPrivateKeyPEM(keyData []byte) (*ecdsa.PrivateKey, error) {
var privateKeyPemBlock *pem.Block
for {
privateKeyPemBlock, keyData = pem.Decode(keyData)
if privateKeyPemBlock == nil {
break
}
if privateKeyPemBlock.Type == ECPrivateKeyBlockType {
return x509.ParseECPrivateKey(privateKeyPemBlock.Bytes)
}
}
return nil, fmt.Errorf("pem does not include a valid EC private key")
}
func ParseCertPEM(pemCerts []byte) (*x509.Certificate, error) {
var pemBlock *pem.Block
for {
pemBlock, pemCerts = pem.Decode(pemCerts)
if pemBlock == nil {
break
}
if pemBlock.Type == CertificateBlockType {
return x509.ParseCertificate(pemBlock.Bytes)
}
}
return nil, fmt.Errorf("pem does not include a valid x509 cert")
}

View File

@ -0,0 +1,164 @@
package factory
import (
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"net"
"sort"
"strings"
v1 "k8s.io/api/core/v1"
)
const (
cnPrefix = "listener.cattle.io/cn-"
static = "listener.cattle.io/static"
hashKey = "listener.cattle.io/hash"
)
type TLS struct {
CACert *x509.Certificate
CAKey crypto.Signer
CN string
Organization []string
}
func collectCNs(secret *v1.Secret) (domains []string, ips []net.IP, hash string, err error) {
var (
cns []string
digest = sha256.New()
)
for k, v := range secret.Annotations {
if strings.HasPrefix(k, cnPrefix) {
cns = append(cns, v)
}
}
sort.Strings(cns)
for _, v := range cns {
digest.Write([]byte(v))
ip := net.ParseIP(v)
if ip == nil {
domains = append(domains, v)
} else {
ips = append(ips, ip)
}
}
hash = hex.EncodeToString(digest.Sum(nil))
return
}
func (t *TLS) AddCN(secret *v1.Secret, cn ...string) (*v1.Secret, bool, error) {
var (
err error
)
if !NeedsUpdate(secret, cn...) {
return secret, false, nil
}
secret = populateCN(secret, cn...)
privateKey, err := getPrivateKey(secret)
if err != nil {
return nil, false, err
}
domains, ips, hash, err := collectCNs(secret)
if err != nil {
return nil, false, err
}
newCert, err := t.newCert(domains, ips, privateKey)
if err != nil {
return nil, false, err
}
certBytes, keyBytes, err := Marshal(newCert, privateKey)
if err != nil {
return nil, false, err
}
if secret.Data == nil {
secret.Data = map[string][]byte{}
}
secret.Data[v1.TLSCertKey] = certBytes
secret.Data[v1.TLSPrivateKeyKey] = keyBytes
secret.Annotations[hashKey] = hash
return secret, true, nil
}
func (t *TLS) newCert(domains []string, ips []net.IP, privateKey *ecdsa.PrivateKey) (*x509.Certificate, error) {
return NewSignedCert(privateKey, t.CACert, t.CAKey, t.CN, t.Organization, domains, ips)
}
func populateCN(secret *v1.Secret, cn ...string) *v1.Secret {
secret = secret.DeepCopy()
if secret.Annotations == nil {
secret.Annotations = map[string]string{}
}
for _, cn := range cn {
secret.Annotations[cnPrefix+cn] = cn
}
return secret
}
func NeedsUpdate(secret *v1.Secret, cn ...string) bool {
if secret.Annotations[static] == "true" {
return false
}
for _, cn := range cn {
if secret.Annotations[cnPrefix+cn] == "" {
return true
}
}
return false
}
func getPrivateKey(secret *v1.Secret) (*ecdsa.PrivateKey, error) {
keyBytes := secret.Data[v1.TLSPrivateKeyKey]
if len(keyBytes) == 0 {
return NewPrivateKey()
}
privateKey, err := ParseECPrivateKeyPEM(keyBytes)
if err == nil {
return privateKey, nil
}
return NewPrivateKey()
}
func Marshal(x509Cert *x509.Certificate, privateKey *ecdsa.PrivateKey) ([]byte, []byte, error) {
certBlock := pem.Block{
Type: CertificateBlockType,
Bytes: x509Cert.Raw,
}
keyBytes, err := x509.MarshalECPrivateKey(privateKey)
if err != nil {
return nil, nil, err
}
keyBlock := pem.Block{
Type: ECPrivateKeyBlockType,
Bytes: keyBytes,
}
return pem.EncodeToMemory(&certBlock), pem.EncodeToMemory(&keyBlock), nil
}
func NewPrivateKey() (*ecdsa.PrivateKey, error) {
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
}

View File

@ -3,16 +3,9 @@ module github.com/rancher/dynamiclistener
go 1.12
require (
github.com/hashicorp/golang-lru v0.5.1
github.com/kisielk/gotool v1.0.0 // indirect
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/rancher/wrangler v0.1.4
github.com/rancher/wrangler-api v0.2.0
github.com/sirupsen/logrus v1.4.1
github.com/stretchr/testify v1.3.0 // indirect
github.com/stripe/safesql v0.2.0 // indirect
golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c // indirect
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862 // indirect
golang.org/x/text v0.3.2 // indirect
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed // indirect
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect
k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b
k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d
)

View File

@ -1,47 +1,132 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-containerregistry v0.0.0-20190617215043-876b8855d23c/go.mod h1:yZAFP63pRshzrEYLXLGPmUt0Ay+2zdjmMN1loCnRLUk=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g=
github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/jetstack/cert-manager v0.7.2/go.mod h1:nbddmhjWxYGt04bxvwVGUSeLhZ2PCyNvd7MpXdq+yWY=
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/knative/build v0.6.0/go.mod h1:/sU74ZQkwlYA5FwYDJhYTy61i/Kn+5eWfln2jDbw3Qo=
github.com/knative/pkg v0.0.0-20190514205332-5e4512dcb2ca/go.mod h1:7Ijfhw7rfB+H9VtosIsDYvZQ+qYTz7auK3fHW/5z4ww=
github.com/knative/serving v0.6.1/go.mod h1:ljvMfwQy2qanaM/8xnBSK4Mz3Vv2NawC2fo5kFRJS1A=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
github.com/mattbaird/jsonpatch v0.0.0-20171005235357-81af80346b1a/go.mod h1:M1qoD/MqPgTZIk0EWKB38wE28ACRfVcn+cU08jyArI0=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rancher/wrangler v0.1.4 h1:bdzBw4H9JKQhXPBPNp4eHbmrkA24+VII865VLiVWcw8=
github.com/rancher/wrangler v0.1.4/go.mod h1:EYP7cqpg42YqElaCm+U9ieSrGQKAXxUH5xsr+XGpWyE=
github.com/rancher/wrangler-api v0.2.0 h1:VR7hLNnDrKykKLqthtwZ58pDDtUa9ijSNToPaJLEkWc=
github.com/rancher/wrangler-api v0.2.0/go.mod h1:zTPdNLZO07KvRaVOx6XQbKBSV55Fnn4s7nqmrMPJqd8=
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff h1:VARhShG49tiji6mdRNp7JTNDtJ0FhuprF93GBQ37xGU=
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stripe/safesql v0.2.0 h1:xiefmCDd8c35PVSGrL2FhBiaKxviXnGziBDOpOejeBE=
github.com/stripe/safesql v0.2.0/go.mod h1:q7b2n0JmzM1mVGfcYpanfVb2j23cXZeWFxcILPn3JV4=
github.com/tektoncd/pipeline v0.4.0/go.mod h1:IZzJdiX9EqEMuUcgdnElozdYYRh0/ZRC+NKMLj1K3Yw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284 h1:rlLehGeYg6jfoyz/eDqDU1iRXLKfR42nnNh57ytKEWo=
golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c h1:uOCk1iQW6Vc18bnC13MfzScl+wdKBmM9Y9kU7Z83/lw=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862 h1:rM0ROo5vb9AdYJi1110yjWGMej9ITfKddS89P3Fkhug=
golang.org/x/sys v0.0.0-20190509141414-a5b02f93d862/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I=
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo=
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b h1:aBGgKJUM9Hk/3AE8WaZIApnTxG35kbuQba2w+SXqezo=
k8s.io/api v0.0.0-20190409021203-6e4e0e4f393b/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA=
k8s.io/apiextensions-apiserver v0.0.0-20190409022649-727a075fdec8/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE=
k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d h1:Jmdtdt1ZnoGfWWIIik61Z7nKYgO3J+swQJtPYsP9wHA=
k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible h1:U5Bt+dab9K8qaUmXINrkXO135kA11/i5Kg1RUydgaMQ=
k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
k8s.io/code-generator v0.0.0-20190311093542-50b561225d70/go.mod h1:MYiN+ZJZ9HkETbgVZdWw2AsuAi9PZ4V80cwfuf2axe8=
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20190327210449-e17681d19d3a/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v0.3.0 h1:0VPpR+sizsiivjIfIAQH/rl8tan6jvWkS7lU+0di3lE=
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/kube-openapi v0.0.0-20190502190224-411b2483e503/go.mod h1:iU+ZGYsNlvU9XKUSso6SQfKTCCw7lFduMZy26Mgr2Fw=
k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5 h1:VBM/0P5TWxwk+Nw6Z+lAw3DKgO76g90ETOiA6rfLV1Y=
k8s.io/utils v0.0.0-20190506122338-8fab8cb257d5/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
sigs.k8s.io/structured-merge-diff v0.0.0-20190426204423-ea680f03cc65/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

198
vendor/github.com/rancher/dynamiclistener/listener.go generated vendored Normal file
View File

@ -0,0 +1,198 @@
package dynamiclistener
import (
"crypto"
"crypto/tls"
"crypto/x509"
"net"
"net/http"
"sync"
"github.com/rancher/dynamiclistener/factory"
"github.com/sirupsen/logrus"
v1 "k8s.io/api/core/v1"
)
type TLSStorage interface {
Get() (*v1.Secret, error)
Update(secret *v1.Secret) error
}
type Config struct {
CN string
Organization []string
TLSConfig tls.Config
SANs []string
}
func NewListener(l net.Listener, storage TLSStorage, caCert *x509.Certificate, caKey crypto.Signer, config Config) (net.Listener, http.Handler, error) {
if config.CN == "" {
config.CN = "dynamic"
}
if len(config.Organization) == 0 {
config.Organization = []string{"dynamic"}
}
dynamicListener := &listener{
factory: &factory.TLS{
CACert: caCert,
CAKey: caKey,
CN: config.CN,
Organization: config.Organization,
},
Listener: l,
storage: &nonNil{storage: storage},
sans: config.SANs,
tlsConfig: config.TLSConfig,
}
dynamicListener.tlsConfig.GetCertificate = dynamicListener.getCertificate
return tls.NewListener(dynamicListener, &dynamicListener.tlsConfig), dynamicListener.cacheHandler(), nil
}
type listener struct {
sync.RWMutex
net.Listener
factory *factory.TLS
storage TLSStorage
version string
tlsConfig tls.Config
cert *tls.Certificate
sans []string
}
func (l *listener) Accept() (net.Conn, error) {
conn, err := l.Listener.Accept()
if err != nil {
return conn, err
}
addr := conn.RemoteAddr()
if addr == nil {
return conn, nil
}
host, _, err := net.SplitHostPort(addr.String())
if err != nil {
logrus.Errorf("failed to parse network %s: %v", addr.Network(), err)
return conn, nil
}
return conn, l.updateCert(host)
}
func (l *listener) getCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
if hello.ServerName != "" {
if err := l.updateCert(hello.ServerName); err != nil {
return nil, err
}
}
return l.loadCert()
}
func (l *listener) updateCert(cn string) error {
l.RLock()
defer l.RUnlock()
secret, err := l.storage.Get()
if err != nil {
return err
}
if !factory.NeedsUpdate(secret, append(l.sans, cn)...) {
return nil
}
l.RUnlock()
l.Lock()
defer l.RLock()
defer l.Unlock()
secret, updated, err := l.factory.AddCN(secret, append(l.sans, cn)...)
if err != nil {
return err
}
if updated {
if err := l.storage.Update(secret); err != nil {
return err
}
// clear version to force cert reload
l.version = ""
}
return nil
}
func (l *listener) loadCert() (*tls.Certificate, error) {
l.RLock()
defer l.RUnlock()
secret, err := l.storage.Get()
if err != nil {
return nil, err
}
if l.cert != nil && l.version == secret.ResourceVersion {
return l.cert, nil
}
defer l.RLock()
l.RUnlock()
l.Lock()
defer l.Unlock()
secret, err = l.storage.Get()
if err != nil {
return nil, err
}
if l.cert != nil && l.version == secret.ResourceVersion {
return l.cert, nil
}
cert, err := tls.X509KeyPair(secret.Data[v1.TLSCertKey], secret.Data[v1.TLSPrivateKeyKey])
if err != nil {
return nil, err
}
l.cert = &cert
return l.cert, nil
}
func (l *listener) cacheHandler() http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
h, _, err := net.SplitHostPort(req.Host)
if err != nil {
h = req.Host
}
ip := net.ParseIP(h)
if len(ip) > 0 {
l.updateCert(h)
}
})
}
type nonNil struct {
sync.Mutex
storage TLSStorage
}
func (n *nonNil) Get() (*v1.Secret, error) {
n.Lock()
defer n.Unlock()
s, err := n.storage.Get()
if err != nil || s == nil {
return &v1.Secret{}, err
}
return s, nil
}
func (n *nonNil) Update(secret *v1.Secret) error {
n.Lock()
defer n.Unlock()
return n.storage.Update(secret)
}

View File

@ -1,50 +0,0 @@
package dynamiclistener
import (
"fmt"
"io/ioutil"
"path/filepath"
)
func ReadTLSConfig(userConfig *UserConfig) error {
var err error
path := userConfig.CertPath
userConfig.CACerts, err = readPEM(filepath.Join(path, "cacerts.pem"))
if err != nil {
return err
}
userConfig.Key, err = readPEM(filepath.Join(path, "key.pem"))
if err != nil {
return err
}
userConfig.Cert, err = readPEM(filepath.Join(path, "cert.pem"))
if err != nil {
return err
}
valid := false
if userConfig.Key != "" && userConfig.Cert != "" {
valid = true
} else if userConfig.Key == "" && userConfig.Cert == "" {
valid = true
}
if !valid {
return fmt.Errorf("invalid SSL configuration found, please set cert/key, cert/key/cacerts, cacerts only, or none")
}
return nil
}
func readPEM(path string) (string, error) {
content, err := ioutil.ReadFile(path)
if err != nil {
return "", nil
}
return string(content), nil
}

46
vendor/github.com/rancher/dynamiclistener/redirect.go generated vendored Normal file
View File

@ -0,0 +1,46 @@
package dynamiclistener
import (
"fmt"
"net"
"net/http"
"strconv"
"strings"
)
// Approach taken from letsencrypt, except manglePort is specific to us
func HTTPRedirect(next http.Handler) http.Handler {
return http.HandlerFunc(
func(rw http.ResponseWriter, r *http.Request) {
fmt.Println("!!!!!", r.URL.String(), r.Header)
if r.Header.Get("x-Forwarded-Proto") == "https" ||
r.Header.Get("x-Forwarded-Proto") == "wss" ||
strings.HasPrefix(r.URL.Path, "/ping") ||
strings.HasPrefix(r.URL.Path, "/health") {
next.ServeHTTP(rw, r)
return
}
if r.Method != "GET" && r.Method != "HEAD" {
http.Error(rw, "Use HTTPS", http.StatusBadRequest)
return
}
target := "https://" + manglePort(r.Host) + r.URL.RequestURI()
http.Redirect(rw, r, target, http.StatusFound)
})
}
func manglePort(hostport string) string {
host, port, err := net.SplitHostPort(hostport)
if err != nil {
return hostport
}
portInt, err := strconv.Atoi(port)
if err != nil {
return hostport
}
portInt = ((portInt / 1000) * 1000) + 443
return net.JoinHostPort(host, strconv.Itoa(portInt))
}

View File

@ -1,556 +0,0 @@
package dynamiclistener
import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
"log"
"net"
"net/http"
"reflect"
"strconv"
"strings"
"sync"
"time"
cert "github.com/rancher/dynamiclistener/cert"
"github.com/sirupsen/logrus"
)
type server struct {
sync.Mutex
userConfig UserConfig
listenConfigStorage ListenerConfigStorage
tlsCert *tls.Certificate
ips map[string]bool
domains map[string]bool
cn string
listeners []net.Listener
servers []*http.Server
activeCA *x509.Certificate
activeCAKey crypto.Signer
}
func NewServer(listenConfigStorage ListenerConfigStorage, config UserConfig) (ServerInterface, error) {
s := &server{
userConfig: config,
listenConfigStorage: listenConfigStorage,
cn: "cattle",
}
s.ips = map[string]bool{}
s.domains = map[string]bool{}
if err := s.userConfigure(); err != nil {
return nil, err
}
lc, err := listenConfigStorage.Get()
if err != nil {
return nil, err
}
return s, s.Update(lc)
}
func (s *server) CACert() (string, error) {
if s.userConfig.NoCACerts {
return "", nil
}
if s.userConfig.CACerts != "" {
return s.userConfig.CACerts, nil
}
return "", fmt.Errorf("ca cert not found")
}
func marshalPrivateKey(privateKey crypto.Signer) (string, []byte, error) {
var (
keyType string
bytes []byte
err error
)
if key, ok := privateKey.(*ecdsa.PrivateKey); ok {
keyType = cert.ECPrivateKeyBlockType
bytes, err = x509.MarshalECPrivateKey(key)
} else if key, ok := privateKey.(*rsa.PrivateKey); ok {
keyType = cert.RSAPrivateKeyBlockType
bytes = x509.MarshalPKCS1PrivateKey(key)
} else {
keyType = cert.PrivateKeyBlockType
bytes, err = x509.MarshalPKCS8PrivateKey(privateKey)
}
if err != nil {
logrus.Errorf("Unable to marshal private key: %v", err)
}
return keyType, bytes, err
}
func newPrivateKey() (crypto.Signer, error) {
caKeyBytes, err := cert.MakeEllipticPrivateKeyPEM()
if err != nil {
return nil, err
}
caKeyIFace, err := cert.ParsePrivateKeyPEM(caKeyBytes)
if err != nil {
return nil, err
}
return caKeyIFace.(crypto.Signer), nil
}
func (s *server) save() (_err error) {
defer func() {
if _err != nil {
logrus.Errorf("Saving cert error: %s", _err)
}
}()
certStr, err := certToString(s.tlsCert)
if err != nil {
return err
}
cfg, err := s.listenConfigStorage.Get()
if err != nil {
return err
}
cfg.GeneratedCerts = map[string]string{s.cn: certStr}
_, err = s.listenConfigStorage.Set(cfg)
return err
}
func (s *server) userConfigure() error {
if s.userConfig.HTTPSPort == 0 {
s.userConfig.HTTPSPort = 8443
}
for _, d := range s.userConfig.Domains {
s.domains[d] = true
}
for _, ip := range s.userConfig.KnownIPs {
if netIP := net.ParseIP(ip); netIP != nil {
s.ips[ip] = true
}
}
if bindAddress := net.ParseIP(s.userConfig.BindAddress); bindAddress != nil {
s.ips[s.userConfig.BindAddress] = true
}
if s.activeCA == nil && s.activeCAKey == nil {
if s.userConfig.CACerts != "" && s.userConfig.CAKey != "" {
ca, err := cert.ParseCertsPEM([]byte(s.userConfig.CACerts))
if err != nil {
return err
}
key, err := cert.ParsePrivateKeyPEM([]byte(s.userConfig.CAKey))
if err != nil {
return err
}
s.activeCA = ca[0]
s.activeCAKey = key.(crypto.Signer)
} else {
ca, key, err := genCA()
if err != nil {
return err
}
s.activeCA = ca
s.activeCAKey = key
}
}
return nil
}
func genCA() (*x509.Certificate, crypto.Signer, error) {
caKey, err := newPrivateKey()
if err != nil {
return nil, nil, err
}
caCert, err := cert.NewSelfSignedCACert(cert.Config{
CommonName: "k3s-ca",
Organization: []string{"k3s-org"},
}, caKey)
if err != nil {
return nil, nil, err
}
return caCert, caKey, nil
}
func (s *server) Update(status *ListenerStatus) (_err error) {
s.Lock()
defer func() {
s.Unlock()
if _err != nil {
logrus.Errorf("Update cert error: %s", _err)
}
if s.tlsCert == nil {
s.getCertificate(&tls.ClientHelloInfo{ServerName: "localhost"})
}
}()
certString := status.GeneratedCerts[s.cn]
tlsCert, err := stringToCert(certString)
if err != nil {
logrus.Errorf("Update cert unable to convert string to cert: %s", err)
s.tlsCert = nil
}
if tlsCert != nil {
s.tlsCert = tlsCert
for i, certBytes := range tlsCert.Certificate {
parsedCert, err := x509.ParseCertificate(certBytes)
if err != nil {
logrus.Errorf("Update cert %d parse error: %s", i, err)
s.tlsCert = nil
break
}
isExpired := cert.IsCertExpired(parsedCert)
if isExpired {
logrus.Infof("certificate is about to expire")
s.tlsCert = nil
break
}
ips := map[string]bool{}
for _, ip := range parsedCert.IPAddresses {
ips[ip.String()] = true
}
domains := map[string]bool{}
for _, domain := range parsedCert.DNSNames {
domains[domain] = true
}
if !(reflect.DeepEqual(ips, s.ips) && reflect.DeepEqual(domains, s.domains)) {
subset := true
for ip := range s.ips {
if !ips[ip] {
subset = false
break
}
}
if subset {
for domain := range s.domains {
if !domains[domain] {
subset = false
break
}
}
}
if !subset {
s.tlsCert = nil
}
for ip := range ips {
s.ips[ip] = true
}
for domain := range domains {
s.domains[domain] = true
}
}
}
}
return s.reload()
}
func (s *server) shutdown() error {
for _, listener := range s.listeners {
if err := listener.Close(); err != nil {
return err
}
}
s.listeners = nil
for _, server := range s.servers {
go server.Shutdown(context.Background())
}
s.servers = nil
return nil
}
func (s *server) reload() error {
if len(s.listeners) > 0 {
return nil
}
if err := s.shutdown(); err != nil {
return err
}
if err := s.serveHTTPS(); err != nil {
return err
}
return nil
}
func (s *server) getCertificate(hello *tls.ClientHelloInfo) (_servingCert *tls.Certificate, _err error) {
s.Lock()
changed := false
defer func() {
defer s.Unlock()
if _err != nil {
logrus.Errorf("Get certificate error: %s", _err)
return
}
if changed {
s.save()
}
}()
if hello.ServerName != "" && !s.domains[hello.ServerName] {
s.tlsCert = nil
s.domains[hello.ServerName] = true
}
if s.tlsCert != nil {
return s.tlsCert, nil
}
ips := []net.IP{}
for ipStr := range s.ips {
if ip := net.ParseIP(ipStr); ip != nil {
ips = append(ips, ip)
}
}
dnsNames := []string{}
for domain := range s.domains {
dnsNames = append(dnsNames, domain)
}
cfg := cert.Config{
CommonName: s.cn,
Organization: s.activeCA.Subject.Organization,
Usages: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
AltNames: cert.AltNames{
DNSNames: dnsNames,
IPs: ips,
},
}
key, err := newPrivateKey()
if err != nil {
return nil, err
}
cert, err := cert.NewSignedCert(cfg, key, s.activeCA, s.activeCAKey)
if err != nil {
return nil, err
}
tlsCert := &tls.Certificate{
Certificate: [][]byte{
cert.Raw,
},
PrivateKey: key,
}
changed = true
s.tlsCert = tlsCert
return tlsCert, nil
}
func (s *server) cacheHandler(handler http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
h, _, err := net.SplitHostPort(req.Host)
if err != nil {
h = req.Host
}
s.Lock()
if ip := net.ParseIP(h); ip != nil {
if !s.ips[h] {
s.ips[h] = true
s.tlsCert = nil
}
} else {
if !s.domains[h] {
s.domains[h] = true
s.tlsCert = nil
}
}
s.Unlock()
handler.ServeHTTP(resp, req)
})
}
func (s *server) serveHTTPS() error {
conf := &tls.Config{
ClientAuth: tls.RequestClientCert,
GetCertificate: s.getCertificate,
PreferServerCipherSuites: true,
}
listener, err := s.newListener(s.userConfig.BindAddress, s.userConfig.HTTPSPort, conf)
if err != nil {
return err
}
logger := logrus.StandardLogger()
server := &http.Server{
Handler: s.cacheHandler(s.Handler()),
ErrorLog: log.New(logger.WriterLevel(logrus.DebugLevel), "", log.LstdFlags),
}
s.servers = append(s.servers, server)
s.startServer(listener, server)
if s.userConfig.HTTPPort > 0 {
httpListener, err := s.newListener(s.userConfig.BindAddress, s.userConfig.HTTPPort, nil)
if err != nil {
return err
}
httpServer := &http.Server{
Handler: s.cacheHandler(httpRedirect(s.Handler())),
ErrorLog: log.New(logger.WriterLevel(logrus.DebugLevel), "", log.LstdFlags),
}
s.servers = append(s.servers, httpServer)
s.startServer(httpListener, httpServer)
}
return nil
}
// Approach taken from letsencrypt, except manglePort is specific to us
func httpRedirect(next http.Handler) http.Handler {
return http.HandlerFunc(
func(rw http.ResponseWriter, r *http.Request) {
if r.Header.Get("x-Forwarded-Proto") == "https" ||
strings.HasPrefix(r.URL.Path, "/ping") ||
strings.HasPrefix(r.URL.Path, "/health") {
next.ServeHTTP(rw, r)
return
}
if r.Method != "GET" && r.Method != "HEAD" {
http.Error(rw, "Use HTTPS", http.StatusBadRequest)
return
}
target := "https://" + manglePort(r.Host) + r.URL.RequestURI()
http.Redirect(rw, r, target, http.StatusFound)
})
}
func manglePort(hostport string) string {
host, port, err := net.SplitHostPort(hostport)
if err != nil {
return hostport
}
portInt, err := strconv.Atoi(port)
if err != nil {
return hostport
}
portInt = ((portInt / 1000) * 1000) + 443
return net.JoinHostPort(host, strconv.Itoa(portInt))
}
func (s *server) startServer(listener net.Listener, server *http.Server) {
go func() {
if err := server.Serve(listener); err != nil {
logrus.Errorf("server on %v returned err: %v", listener.Addr(), err)
}
}()
}
func (s *server) Handler() http.Handler {
return s.userConfig.Handler
}
func (s *server) newListener(ip string, port int, config *tls.Config) (net.Listener, error) {
addr := fmt.Sprintf("%s:%d", ip, port)
l, err := net.Listen("tcp", addr)
if err != nil {
return nil, err
}
l = tcpKeepAliveListener{l.(*net.TCPListener)}
if config != nil {
l = tls.NewListener(l, config)
}
s.listeners = append(s.listeners, l)
logrus.Info("Listening on ", addr)
return l, nil
}
func stringToCert(certString string) (*tls.Certificate, error) {
parts := strings.Split(certString, "#")
if len(parts) != 2 {
return nil, errors.New("Unable to split cert into two parts")
}
certPart, keyPart := parts[0], parts[1]
keyBytes, err := base64.StdEncoding.DecodeString(keyPart)
if err != nil {
return nil, err
}
key, err := cert.ParsePrivateKeyPEM(keyBytes)
if err != nil {
return nil, err
}
certBytes, err := base64.StdEncoding.DecodeString(certPart)
if err != nil {
return nil, err
}
return &tls.Certificate{
Certificate: [][]byte{certBytes},
PrivateKey: key,
}, nil
}
func certToString(cert *tls.Certificate) (string, error) {
keyType, keyBytes, err := marshalPrivateKey(cert.PrivateKey.(crypto.Signer))
if err != nil {
return "", err
}
privateKeyPemBlock := &pem.Block{
Type: keyType,
Bytes: keyBytes,
}
pemBytes := pem.EncodeToMemory(privateKeyPemBlock)
certString := base64.StdEncoding.EncodeToString(cert.Certificate[0])
keyString := base64.StdEncoding.EncodeToString(pemBytes)
return certString + "#" + keyString, nil
}
type tcpKeepAliveListener struct {
*net.TCPListener
}
func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
tc, err := ln.AcceptTCP()
if err != nil {
return
}
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod(3 * time.Minute)
return tc, nil
}

View File

@ -0,0 +1,42 @@
package file
import (
"encoding/json"
"github.com/rancher/dynamiclistener"
"k8s.io/api/core/v1"
"os"
)
func New(file string) dynamiclistener.TLSStorage {
return &storage{
file: file,
}
}
type storage struct {
file string
}
func (s *storage) Get() (*v1.Secret, error) {
f, err := os.Open(s.file)
if os.IsNotExist(err) {
return nil, nil
} else if err != nil {
return nil, err
}
defer f.Close()
secret := v1.Secret{}
return &secret, json.NewDecoder(f).Decode(&secret)
}
func (s *storage) Update(secret *v1.Secret) error {
f, err := os.Create(s.file)
if err != nil {
return err
}
defer f.Close()
return json.NewEncoder(f).Encode(secret)
}

View File

@ -0,0 +1,109 @@
package kubernetes
import (
"context"
"sync"
"time"
"github.com/rancher/dynamiclistener"
"github.com/rancher/wrangler-api/pkg/generated/controllers/core"
v1controller "github.com/rancher/wrangler-api/pkg/generated/controllers/core/v1"
"github.com/rancher/wrangler/pkg/start"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/equality"
)
type CoreGetter func() *core.Factory
func New(ctx context.Context, core CoreGetter, namespace, name string, backing dynamiclistener.TLSStorage) dynamiclistener.TLSStorage {
storage := &storage{
name: name,
namespace: namespace,
storage: backing,
ctx: ctx,
}
// lazy init
go func() {
for {
core := core()
if core != nil {
storage.init(core.Core().V1().Secret())
start.All(ctx, 5, core)
return
}
select {
case <-ctx.Done():
return
case <-time.After(time.Second):
}
}
}()
return storage
}
type storage struct {
sync.Mutex
namespace, name string
storage dynamiclistener.TLSStorage
secrets v1controller.SecretClient
ctx context.Context
}
func (s *storage) init(secrets v1controller.SecretController) {
s.Lock()
defer s.Unlock()
secrets.OnChange(s.ctx, "tls-storage", func(key string, secret *v1.Secret) (*v1.Secret, error) {
if secret == nil {
return nil, nil
}
if secret.Namespace == s.namespace && secret.Name == s.name {
if err := s.Update(secret); err != nil {
return nil, err
}
}
return secret, nil
})
s.secrets = secrets
}
func (s *storage) Get() (*v1.Secret, error) {
s.Lock()
defer s.Unlock()
return s.storage.Get()
}
func (s *storage) Update(secret *v1.Secret) (err error) {
s.Lock()
defer s.Unlock()
if s.secrets != nil {
if secret.UID == "" {
secret.Name = s.name
secret.Namespace = s.namespace
secret, err = s.secrets.Create(secret)
if err != nil {
return err
}
} else {
existingSecret, err := s.storage.Get()
if err != nil {
return err
}
if !equality.Semantic.DeepEqual(secret.Data, existingSecret.Data) {
secret, err = s.secrets.Update(secret)
if err != nil {
return err
}
}
}
}
return s.storage.Update(secret)
}

View File

@ -0,0 +1,42 @@
package memory
import (
"github.com/rancher/dynamiclistener"
v1 "k8s.io/api/core/v1"
)
func New() dynamiclistener.TLSStorage {
return &memory{}
}
func NewBacked(storage dynamiclistener.TLSStorage) dynamiclistener.TLSStorage {
return &memory{storage: storage}
}
type memory struct {
storage dynamiclistener.TLSStorage
secret *v1.Secret
}
func (m *memory) Get() (*v1.Secret, error) {
if m.secret == nil && m.storage != nil {
secret, err := m.storage.Get()
if err != nil {
return nil, err
}
m.secret = secret
}
return m.secret, nil
}
func (m *memory) Update(secret *v1.Secret) error {
if m.storage != nil {
if err := m.storage.Update(secret); err != nil {
return err
}
}
m.secret = secret
return nil
}

38
vendor/github.com/rancher/dynamiclistener/tcp.go generated vendored Normal file
View File

@ -0,0 +1,38 @@
package dynamiclistener
import (
"fmt"
"net"
"reflect"
"time"
)
func NewTCPListener(ip string, port int) (net.Listener, error) {
l, err := net.Listen("tcp", fmt.Sprintf("%s:%d", ip, port))
if err != nil {
return nil, err
}
tcpListener, ok := l.(*net.TCPListener)
if !ok {
return nil, fmt.Errorf("wrong listener type: %v", reflect.TypeOf(tcpListener))
}
return tcpKeepAliveListener{
TCPListener: tcpListener,
}, nil
}
type tcpKeepAliveListener struct {
*net.TCPListener
}
func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
tc, err := ln.AcceptTCP()
if err != nil {
return
}
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod(3 * time.Minute)
return tc, nil
}

View File

@ -1,63 +0,0 @@
package dynamiclistener
import (
"net/http"
)
type ListenerConfigStorage interface {
Set(*ListenerStatus) (*ListenerStatus, error)
Get() (*ListenerStatus, error)
}
type ServerInterface interface {
Update(status *ListenerStatus) error
CACert() (string, error)
}
type UserConfig struct {
// Required fields
Handler http.Handler
HTTPPort int
HTTPSPort int
CertPath string
// Optional fields
KnownIPs []string
Domains []string
Mode string
NoCACerts bool
CACerts string
CAKey string
Cert string
Key string
BindAddress string
}
type ListenerStatus struct {
Revision string `json:"revision,omitempty"`
CACert string `json:"caCert,omitempty"`
CAKey string `json:"caKey,omitempty"`
GeneratedCerts map[string]string `json:"generatedCerts" norman:"nocreate,noupdate"`
KnownIPs map[string]bool `json:"knownIps" norman:"nocreate,noupdate"`
}
func (l *ListenerStatus) DeepCopyInto(t *ListenerStatus) {
t.Revision = l.Revision
t.CACert = l.CACert
t.CAKey = l.CAKey
t.GeneratedCerts = copyMap(t.GeneratedCerts)
t.KnownIPs = map[string]bool{}
for k, v := range l.KnownIPs {
t.KnownIPs[k] = v
}
}
func copyMap(m map[string]string) map[string]string {
ret := map[string]string{}
for k, v := range m {
ret[k] = v
}
return ret
}

View File

@ -1,107 +0,0 @@
package client
import (
"context"
"fmt"
"time"
"github.com/coreos/etcd/clientv3"
"github.com/rancher/kine/pkg/endpoint"
)
type Value struct {
Data []byte
Modified int64
}
type Client interface {
Get(ctx context.Context, key string) (Value, error)
Put(ctx context.Context, key string, value []byte) error
Create(ctx context.Context, key string, value []byte) error
Update(ctx context.Context, key string, revision int64, value []byte) error
Close() error
}
type client struct {
c *clientv3.Client
}
func New(config endpoint.ETCDConfig) (Client, error) {
tlsConfig, err := config.TLSConfig.ClientConfig()
if err != nil {
return nil, err
}
c, err := clientv3.New(clientv3.Config{
Endpoints: config.Endpoints,
DialTimeout: 5 * time.Second,
TLS: tlsConfig,
})
if err != nil {
return nil, err
}
return &client{
c: c,
}, nil
}
func (c *client) Get(ctx context.Context, key string) (Value, error) {
resp, err := c.c.Get(ctx, key)
if err != nil {
return Value{}, err
}
if len(resp.Kvs) == 1 {
return Value{
Data: resp.Kvs[0].Value,
Modified: resp.Kvs[0].ModRevision,
}, nil
}
return Value{}, nil
}
func (c *client) Put(ctx context.Context, key string, value []byte) error {
val, err := c.Get(ctx, key)
if err != nil {
return err
}
if val.Modified == 0 {
return c.Create(ctx, key, value)
}
return c.Update(ctx, key, val.Modified, value)
}
func (c *client) Create(ctx context.Context, key string, value []byte) error {
resp, err := c.c.Txn(ctx).
If(clientv3.Compare(clientv3.ModRevision(key), "=", 0)).
Then(clientv3.OpPut(key, string(value))).
Commit()
if err != nil {
return err
}
if !resp.Succeeded {
return fmt.Errorf("key exists")
}
return nil
}
func (c *client) Update(ctx context.Context, key string, revision int64, value []byte) error {
resp, err := c.c.Txn(ctx).
If(clientv3.Compare(clientv3.ModRevision(key), "=", revision)).
Then(clientv3.OpPut(key, string(value))).
Else(clientv3.OpGet(key)).
Commit()
if err != nil {
return err
}
if !resp.Succeeded {
return fmt.Errorf("revision %d doesnt match", revision)
}
return nil
}
func (c *client) Close() error {
return c.c.Close()
}

79
vendor/modules.txt vendored
View File

@ -479,9 +479,9 @@ github.com/godbus/dbus
github.com/gogo/googleapis/google/rpc
# github.com/gogo/protobuf v1.3.0
github.com/gogo/protobuf/types
github.com/gogo/protobuf/gogoproto
github.com/gogo/protobuf/proto
github.com/gogo/protobuf/sortkeys
github.com/gogo/protobuf/gogoproto
github.com/gogo/protobuf/protoc-gen-gogo/descriptor
# github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef
github.com/golang/groupcache/lru
@ -617,7 +617,7 @@ github.com/json-iterator/go
github.com/juju/errors
# github.com/karrick/godirwalk v1.7.5
github.com/karrick/godirwalk
# github.com/konsorten/go-windows-terminal-sequences v1.0.2
# github.com/konsorten/go-windows-terminal-sequences v1.0.1
github.com/konsorten/go-windows-terminal-sequences
# github.com/kubernetes-sigs/cri-tools v0.0.0-00010101000000-000000000000 => github.com/rancher/cri-tools v1.16.1-k3s.1
github.com/kubernetes-sigs/cri-tools/cmd/crictl
@ -726,8 +726,12 @@ github.com/prometheus/procfs/internal/util
# github.com/rakelkar/gonetsh v0.0.0-20190719023240-501daadcadf8
github.com/rakelkar/gonetsh/netroute
github.com/rakelkar/gonetsh/netsh
# github.com/rancher/dynamiclistener v0.1.1-0.20191010011134-8a2488bc860a
# github.com/rancher/dynamiclistener v0.1.1-0.20191031022009-6224794ef3cb
github.com/rancher/dynamiclistener
github.com/rancher/dynamiclistener/factory
github.com/rancher/dynamiclistener/storage/file
github.com/rancher/dynamiclistener/storage/kubernetes
github.com/rancher/dynamiclistener/storage/memory
github.com/rancher/dynamiclistener/cert
# github.com/rancher/helm-controller v0.2.2
github.com/rancher/helm-controller/pkg/generated/controllers/helm.cattle.io
@ -745,7 +749,6 @@ github.com/rancher/helm-controller/pkg/generated/informers/externalversions/inte
github.com/rancher/helm-controller/pkg/apis/helm.cattle.io
# github.com/rancher/kine v0.0.0-00010101000000-000000000000 => github.com/ibuildthecloud/kine v0.1.1
github.com/rancher/kine/pkg/endpoint
github.com/rancher/kine/pkg/client
github.com/rancher/kine/pkg/drivers/mysql
github.com/rancher/kine/pkg/drivers/pgsql
github.com/rancher/kine/pkg/drivers/sqlite
@ -758,8 +761,8 @@ github.com/rancher/kine/pkg/broadcaster
# github.com/rancher/remotedialer v0.2.0
github.com/rancher/remotedialer
# github.com/rancher/wrangler v0.2.0
github.com/rancher/wrangler/pkg/start
github.com/rancher/wrangler/pkg/signals
github.com/rancher/wrangler/pkg/start
github.com/rancher/wrangler/pkg/controller-gen
github.com/rancher/wrangler/pkg/controller-gen/args
github.com/rancher/wrangler/pkg/cleanup
@ -1021,15 +1024,15 @@ google.golang.org/api/transport/http/internal/propagation
# google.golang.org/appengine v1.5.0
google.golang.org/appengine/cloudsql
google.golang.org/appengine/urlfetch
google.golang.org/appengine
google.golang.org/appengine/internal
google.golang.org/appengine/internal/urlfetch
google.golang.org/appengine
google.golang.org/appengine/internal/app_identity
google.golang.org/appengine/internal/modules
google.golang.org/appengine/internal/base
google.golang.org/appengine/internal/datastore
google.golang.org/appengine/internal/log
google.golang.org/appengine/internal/remote_api
google.golang.org/appengine/internal/app_identity
google.golang.org/appengine/internal/modules
# google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873
google.golang.org/genproto/googleapis/rpc/status
google.golang.org/genproto/googleapis/api/annotations
@ -1092,12 +1095,14 @@ k8s.io/api/core/v1
k8s.io/api/extensions/v1beta1
k8s.io/api/networking/v1
k8s.io/api/apps/v1
k8s.io/api/authentication/v1
k8s.io/api/autoscaling/v1
k8s.io/api/policy/v1beta1
k8s.io/api/apps/v1beta1
k8s.io/api/admissionregistration/v1
k8s.io/api/admissionregistration/v1beta1
k8s.io/api/apps/v1beta2
k8s.io/api/auditregistration/v1alpha1
k8s.io/api/autoscaling/v1
k8s.io/api/autoscaling/v2beta1
k8s.io/api/autoscaling/v2beta2
k8s.io/api/batch/v1
@ -1111,7 +1116,6 @@ k8s.io/api/events/v1beta1
k8s.io/api/networking/v1beta1
k8s.io/api/node/v1alpha1
k8s.io/api/node/v1beta1
k8s.io/api/policy/v1beta1
k8s.io/api/rbac/v1
k8s.io/api/rbac/v1alpha1
k8s.io/api/rbac/v1beta1
@ -1122,7 +1126,6 @@ k8s.io/api/settings/v1alpha1
k8s.io/api/storage/v1
k8s.io/api/storage/v1alpha1
k8s.io/api/storage/v1beta1
k8s.io/api/authentication/v1
k8s.io/api/authentication/v1beta1
k8s.io/api/authorization/v1
k8s.io/api/authorization/v1beta1
@ -1178,9 +1181,10 @@ k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions/apiextensio
k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/v1
k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/v1beta1
# k8s.io/apimachinery v0.0.0 => github.com/rancher/kubernetes/staging/src/k8s.io/apimachinery v1.16.2-k3s.1
k8s.io/apimachinery/pkg/api/equality
k8s.io/apimachinery/pkg/apis/meta/v1
k8s.io/apimachinery/pkg/util/json
k8s.io/apimachinery/pkg/util/net
k8s.io/apimachinery/pkg/apis/meta/v1
k8s.io/apimachinery/pkg/labels
k8s.io/apimachinery/pkg/util/intstr
k8s.io/apimachinery/pkg/fields
@ -1193,20 +1197,20 @@ k8s.io/apimachinery/pkg/apis/meta/v1/unstructured
k8s.io/apimachinery/pkg/util/yaml
k8s.io/apimachinery/pkg/runtime/serializer
k8s.io/apimachinery/pkg/util/runtime
k8s.io/apimachinery/pkg/api/equality
k8s.io/apimachinery/pkg/api/resource
k8s.io/apimachinery/pkg/conversion
k8s.io/apimachinery/pkg/selection
k8s.io/apimachinery/pkg/util/errors
k8s.io/apimachinery/pkg/util/validation
k8s.io/apimachinery/pkg/util/sets
k8s.io/apimachinery/pkg/util/wait
k8s.io/apimachinery/pkg/util/strategicpatch
k8s.io/apimachinery/pkg/api/resource
k8s.io/apimachinery/pkg/conversion
k8s.io/apimachinery/pkg/selection
k8s.io/apimachinery/pkg/api/meta
k8s.io/apimachinery/pkg/util/cache
k8s.io/apimachinery/pkg/util/clock
k8s.io/apimachinery/pkg/util/diff
k8s.io/apimachinery/pkg/util/naming
k8s.io/apimachinery/pkg/runtime/serializer/streaming
k8s.io/apimachinery/pkg/conversion/queryparams
k8s.io/apimachinery/pkg/api/validation/path
k8s.io/apimachinery/pkg/apis/meta/internalversion
@ -1215,7 +1219,6 @@ k8s.io/apimachinery/pkg/util/rand
k8s.io/apimachinery/pkg/util/jsonmergepatch
k8s.io/apimachinery/pkg/util/validation/field
k8s.io/apimachinery/pkg/version
k8s.io/apimachinery/pkg/runtime/serializer/streaming
k8s.io/apimachinery/pkg/runtime/serializer/json
k8s.io/apimachinery/pkg/runtime/serializer/protobuf
k8s.io/apimachinery/pkg/runtime/serializer/recognizer
@ -1224,9 +1227,9 @@ k8s.io/apimachinery/pkg/util/waitgroup
k8s.io/apimachinery/pkg/util/httpstream
k8s.io/apimachinery/pkg/util/remotecommand
k8s.io/apimachinery/pkg/util/httpstream/spdy
k8s.io/apimachinery/third_party/forked/golang/reflect
k8s.io/apimachinery/pkg/util/mergepatch
k8s.io/apimachinery/third_party/forked/golang/json
k8s.io/apimachinery/third_party/forked/golang/reflect
k8s.io/apimachinery/pkg/util/version
k8s.io/apimachinery/pkg/util/proxy
k8s.io/apimachinery/pkg/apis/meta/v1beta1
@ -1366,31 +1369,22 @@ k8s.io/cli-runtime/pkg/kustomize/k8sdeps/transformer/hash
k8s.io/cli-runtime/pkg/kustomize/k8sdeps/transformer/patch
k8s.io/cli-runtime/pkg/kustomize/k8sdeps/kv
# k8s.io/client-go v11.0.1-0.20190409021438-1a26190bd76a+incompatible => github.com/rancher/kubernetes/staging/src/k8s.io/client-go v1.16.2-k3s.1
k8s.io/client-go/tools/clientcmd
k8s.io/client-go/util/cert
k8s.io/client-go/kubernetes
k8s.io/client-go/kubernetes/typed/core/v1
k8s.io/client-go/tools/clientcmd
k8s.io/client-go/informers
k8s.io/client-go/listers/core/v1
k8s.io/client-go/tools/cache
k8s.io/client-go/transport
k8s.io/client-go/rest
k8s.io/client-go/tools/clientcmd/api
k8s.io/client-go/discovery
k8s.io/client-go/rest
k8s.io/client-go/util/flowcontrol
k8s.io/client-go/discovery/fake
k8s.io/client-go/testing
k8s.io/client-go/kubernetes/typed/apps/v1
k8s.io/client-go/kubernetes/typed/core/v1
k8s.io/client-go/tools/portforward
k8s.io/client-go/tools/remotecommand
k8s.io/client-go/transport/spdy
k8s.io/client-go/informers/core
k8s.io/client-go/kubernetes/scheme
k8s.io/client-go/informers/core/v1
k8s.io/client-go/tools/auth
k8s.io/client-go/tools/clientcmd/api/latest
k8s.io/client-go/util/homedir
k8s.io/client-go/util/keyutil
k8s.io/client-go/kubernetes/typed/admissionregistration/v1
k8s.io/client-go/kubernetes/typed/admissionregistration/v1beta1
k8s.io/client-go/kubernetes/typed/apps/v1beta1
@ -1427,6 +1421,11 @@ k8s.io/client-go/kubernetes/typed/settings/v1alpha1
k8s.io/client-go/kubernetes/typed/storage/v1
k8s.io/client-go/kubernetes/typed/storage/v1alpha1
k8s.io/client-go/kubernetes/typed/storage/v1beta1
k8s.io/client-go/kubernetes/scheme
k8s.io/client-go/tools/reference
k8s.io/client-go/tools/auth
k8s.io/client-go/tools/clientcmd/api/latest
k8s.io/client-go/util/homedir
k8s.io/client-go/informers/admissionregistration
k8s.io/client-go/informers/apps
k8s.io/client-go/informers/auditregistration
@ -1434,6 +1433,7 @@ k8s.io/client-go/informers/autoscaling
k8s.io/client-go/informers/batch
k8s.io/client-go/informers/certificates
k8s.io/client-go/informers/coordination
k8s.io/client-go/informers/core
k8s.io/client-go/informers/discovery
k8s.io/client-go/informers/events
k8s.io/client-go/informers/extensions
@ -1447,10 +1447,17 @@ k8s.io/client-go/informers/settings
k8s.io/client-go/informers/storage
k8s.io/client-go/tools/pager
k8s.io/client-go/util/retry
k8s.io/client-go/pkg/version
k8s.io/client-go/plugin/pkg/client/auth/exec
k8s.io/client-go/rest/watch
k8s.io/client-go/tools/metrics
k8s.io/client-go/transport
k8s.io/client-go/util/cert
k8s.io/client-go/informers/core/v1
k8s.io/client-go/tools/record
k8s.io/client-go/util/certificate
k8s.io/client-go/util/connrotation
k8s.io/client-go/tools/metrics
k8s.io/client-go/util/keyutil
k8s.io/client-go/tools/leaderelection
k8s.io/client-go/tools/leaderelection/resourcelock
k8s.io/client-go/discovery/cached
@ -1460,13 +1467,9 @@ k8s.io/client-go/metadata
k8s.io/client-go/metadata/metadatainformer
k8s.io/client-go/restmapper
k8s.io/client-go/scale
k8s.io/client-go/pkg/version
k8s.io/client-go/plugin/pkg/client/auth/exec
k8s.io/client-go/rest/watch
k8s.io/client-go/util/workqueue
k8s.io/client-go/informers/apps/v1
k8s.io/client-go/listers/apps/v1
k8s.io/client-go/tools/reference
k8s.io/client-go/util/exec
k8s.io/client-go/tools/clientcmd/api/v1
k8s.io/client-go/informers/admissionregistration/v1
@ -1501,6 +1504,9 @@ k8s.io/client-go/informers/settings/v1alpha1
k8s.io/client-go/informers/storage/v1
k8s.io/client-go/informers/storage/v1alpha1
k8s.io/client-go/informers/storage/v1beta1
k8s.io/client-go/pkg/apis/clientauthentication
k8s.io/client-go/pkg/apis/clientauthentication/v1alpha1
k8s.io/client-go/pkg/apis/clientauthentication/v1beta1
k8s.io/client-go/tools/record/util
k8s.io/client-go/util/certificate/csr
k8s.io/client-go/listers/storage/v1beta1
@ -1522,9 +1528,6 @@ k8s.io/client-go/listers/coordination/v1beta1
k8s.io/client-go/listers/autoscaling/v1
k8s.io/client-go/listers/storage/v1
k8s.io/client-go/tools/events
k8s.io/client-go/pkg/apis/clientauthentication
k8s.io/client-go/pkg/apis/clientauthentication/v1alpha1
k8s.io/client-go/pkg/apis/clientauthentication/v1beta1
k8s.io/client-go/discovery/cached/disk
k8s.io/client-go/util/jsonpath
k8s.io/client-go/listers/admissionregistration/v1