diff --git a/pkg/agent/config/config.go b/pkg/agent/config/config.go index 550eff6bf0..ed1b4d6383 100644 --- a/pkg/agent/config/config.go +++ b/pkg/agent/config/config.go @@ -101,7 +101,7 @@ RETRY: } } -type HTTPRequester func(u string, client *http.Client, username, password string) ([]byte, error) +type HTTPRequester func(u string, client *http.Client, username, password, token string) ([]byte, error) func Request(path string, info *clientaccess.Info, requester HTTPRequester) ([]byte, error) { u, err := url.Parse(info.BaseURL) @@ -109,17 +109,19 @@ func Request(path string, info *clientaccess.Info, requester HTTPRequester) ([]b return nil, err } u.Path = path - return requester(u.String(), clientaccess.GetHTTPClient(info.CACerts), info.Username, info.Password) + return requester(u.String(), clientaccess.GetHTTPClient(info.CACerts, info.CertFile, info.KeyFile), info.Username, info.Password, info.Token()) } func getNodeNamedCrt(nodeName string, nodeIPs []net.IP, nodePasswordFile string) HTTPRequester { - return func(u string, client *http.Client, username, password string) ([]byte, error) { + return func(u string, client *http.Client, username, password, token string) ([]byte, error) { req, err := http.NewRequest(http.MethodGet, u, nil) if err != nil { return nil, err } - if username != "" { + if token != "" { + req.Header.Add("Authorization", "Bearer "+token) + } else if username != "" { req.SetBasicAuth(username, password) } @@ -320,8 +322,10 @@ func get(ctx context.Context, envInfo *cmds.Agent, proxy proxy.Proxy) (*config.N if envInfo.Debug { logrus.SetLevel(logrus.DebugLevel) } - - info, err := clientaccess.ParseAndValidateToken(proxy.SupervisorURL(), envInfo.Token) + clientKubeletCert := filepath.Join(envInfo.DataDir, "agent", "client-kubelet.crt") + clientKubeletKey := filepath.Join(envInfo.DataDir, "agent", "client-kubelet.key") + withCert := clientaccess.WithClientCertificate(clientKubeletCert, clientKubeletKey) + info, err := clientaccess.ParseAndValidateToken(proxy.SupervisorURL(), envInfo.Token, withCert) if err != nil { return nil, err } @@ -399,8 +403,6 @@ func get(ctx context.Context, envInfo *cmds.Agent, proxy proxy.Proxy) (*config.N return nil, err } - clientKubeletCert := filepath.Join(envInfo.DataDir, "agent", "client-kubelet.crt") - clientKubeletKey := filepath.Join(envInfo.DataDir, "agent", "client-kubelet.key") if err := getNodeNamedHostFile(clientKubeletCert, clientKubeletKey, nodeName, nodeIPs, newNodePasswordFile, info); err != nil { return nil, err } @@ -447,6 +449,8 @@ func get(ctx context.Context, envInfo *cmds.Agent, proxy proxy.Proxy) (*config.N nodeConfig.Images = filepath.Join(envInfo.DataDir, "agent", "images") nodeConfig.AgentConfig.NodeName = nodeName nodeConfig.AgentConfig.NodeConfigPath = nodeConfigPath + nodeConfig.AgentConfig.ClientKubeletCert = clientKubeletCert + nodeConfig.AgentConfig.ClientKubeletKey = clientKubeletKey nodeConfig.AgentConfig.ServingKubeletCert = servingKubeletCert nodeConfig.AgentConfig.ServingKubeletKey = servingKubeletKey nodeConfig.AgentConfig.ClusterDNS = controlConfig.ClusterDNS @@ -610,7 +614,8 @@ func get(ctx context.Context, envInfo *cmds.Agent, proxy proxy.Proxy) (*config.N // getAPIServers attempts to return a list of apiservers from the server. func getAPIServers(ctx context.Context, node *config.Node, proxy proxy.Proxy) ([]string, error) { - info, err := clientaccess.ParseAndValidateToken(proxy.SupervisorURL(), node.Token) + withCert := clientaccess.WithClientCertificate(node.AgentConfig.ClientKubeletCert, node.AgentConfig.ClientKubeletKey) + info, err := clientaccess.ParseAndValidateToken(proxy.SupervisorURL(), node.Token, withCert) if err != nil { return nil, err } @@ -627,7 +632,8 @@ func getAPIServers(ctx context.Context, node *config.Node, proxy proxy.Proxy) ([ // getKubeProxyDisabled attempts to return the DisableKubeProxy setting from the server configuration data. // It first checks the server readyz endpoint, to ensure that the configuration has stabilized before use. func getKubeProxyDisabled(ctx context.Context, node *config.Node, proxy proxy.Proxy) (bool, error) { - info, err := clientaccess.ParseAndValidateToken(proxy.SupervisorURL(), node.Token) + withCert := clientaccess.WithClientCertificate(node.AgentConfig.ClientKubeletCert, node.AgentConfig.ClientKubeletKey) + info, err := clientaccess.ParseAndValidateToken(proxy.SupervisorURL(), node.Token, withCert) if err != nil { return false, err } diff --git a/pkg/agent/run.go b/pkg/agent/run.go index 815a696e0d..6f0ee0fb60 100644 --- a/pkg/agent/run.go +++ b/pkg/agent/run.go @@ -266,6 +266,9 @@ func Run(ctx context.Context, cfg cmds.Agent) error { func createProxyAndValidateToken(ctx context.Context, cfg *cmds.Agent) (proxy.Proxy, error) { agentDir := filepath.Join(cfg.DataDir, "agent") + clientKubeletCert := filepath.Join(agentDir, "client-kubelet.crt") + clientKubeletKey := filepath.Join(agentDir, "client-kubelet.key") + if err := os.MkdirAll(agentDir, 0700); err != nil { return nil, err } @@ -276,8 +279,13 @@ func createProxyAndValidateToken(ctx context.Context, cfg *cmds.Agent) (proxy.Pr return nil, err } + options := []clientaccess.ValidationOption{ + clientaccess.WithUser("node"), + clientaccess.WithClientCertificate(clientKubeletCert, clientKubeletKey), + } + for { - newToken, err := clientaccess.ParseAndValidateTokenForUser(proxy.SupervisorURL(), cfg.Token, "node") + newToken, err := clientaccess.ParseAndValidateToken(proxy.SupervisorURL(), cfg.Token, options...) if err != nil { logrus.Error(err) select { diff --git a/pkg/cli/agent/agent.go b/pkg/cli/agent/agent.go index ecc010871f..13d2eb688b 100644 --- a/pkg/cli/agent/agent.go +++ b/pkg/cli/agent/agent.go @@ -1,8 +1,10 @@ package agent import ( + "crypto/tls" "fmt" "os" + "path/filepath" "runtime" "github.com/erikdubbelboer/gspt" @@ -49,7 +51,11 @@ func Run(ctx *cli.Context) error { logrus.Fatal("cluster-secret is deprecated. Use --token instead.") } - if cmds.AgentConfig.Token == "" { + clientKubeletCert := filepath.Join(cmds.AgentConfig.DataDir, "agent", "client-kubelet.crt") + clientKubeletKey := filepath.Join(cmds.AgentConfig.DataDir, "agent", "client-kubelet.key") + _, err := tls.LoadX509KeyPair(clientKubeletCert, clientKubeletKey) + + if err != nil && cmds.AgentConfig.Token == "" { return fmt.Errorf("--token is required") } diff --git a/pkg/cli/cert/cert.go b/pkg/cli/cert/cert.go index afbbe8a07a..b5e2d8a88d 100644 --- a/pkg/cli/cert/cert.go +++ b/pkg/cli/cert/cert.go @@ -285,7 +285,7 @@ func rotateCA(app *cli.Context, cfg *cmds.Server, sync *cmds.CertRotateCA) error return err } - info, err := clientaccess.ParseAndValidateTokenForUser(cmds.ServerConfig.ServerURL, serverConfig.ControlConfig.Token, "server") + info, err := clientaccess.ParseAndValidateToken(cmds.ServerConfig.ServerURL, serverConfig.ControlConfig.Token, clientaccess.WithUser("server")) if err != nil { return err } diff --git a/pkg/cli/secretsencrypt/secrets_encrypt.go b/pkg/cli/secretsencrypt/secrets_encrypt.go index 58e7aaa678..d1fa095300 100644 --- a/pkg/cli/secretsencrypt/secrets_encrypt.go +++ b/pkg/cli/secretsencrypt/secrets_encrypt.go @@ -38,7 +38,7 @@ func commandPrep(app *cli.Context, cfg *cmds.Server) (*clientaccess.Info, error) } cfg.Token = string(bytes.TrimRight(tokenByte, "\n")) } - return clientaccess.ParseAndValidateTokenForUser(cmds.ServerConfig.ServerURL, cfg.Token, "server") + return clientaccess.ParseAndValidateToken(cmds.ServerConfig.ServerURL, cfg.Token, clientaccess.WithUser("server")) } func wrapServerError(err error) error { diff --git a/pkg/clientaccess/token.go b/pkg/clientaccess/token.go index a6090deaa2..d9ab822b9c 100644 --- a/pkg/clientaccess/token.go +++ b/pkg/clientaccess/token.go @@ -14,6 +14,7 @@ import ( "strings" "time" + "github.com/k3s-io/k3s/pkg/kubeadm" "github.com/pkg/errors" certutil "github.com/rancher/dynamiclistener/cert" "github.com/sirupsen/logrus" @@ -21,7 +22,6 @@ import ( const ( tokenPrefix = "K10" - tokenFormat = "%s%s::%s:%s" caHashLength = sha256.Size * 2 defaultClientTimeout = 10 * time.Second @@ -43,44 +43,66 @@ var ( // Info contains fields that track parsed parts of a cluster join token type Info struct { - CACerts []byte `json:"cacerts,omitempty"` - BaseURL string `json:"baseurl,omitempty"` - Username string `json:"username,omitempty"` - Password string `json:"password,omitempty"` + *kubeadm.BootstrapTokenString + + CACerts []byte + BaseURL string + Username string + Password string + CertFile string + KeyFile string caHash string } -// String returns the token data, templated according to the token format +// ValidationOption is a callback to mutate the token prior to use +type ValidationOption func(*Info) + +// WithClientCertificate configures certs and keys to be used +// to authenticate the request. +func WithClientCertificate(certFile, keyFile string) ValidationOption { + return func(i *Info) { + i.CertFile = certFile + i.KeyFile = keyFile + } +} + +// WithUser overrides the username from the token with the provided value. +func WithUser(username string) ValidationOption { + return func(i *Info) { + i.Username = username + } +} + +// String returns the token data in K10 format func (i *Info) String() string { + creds := i.Username + ":" + i.Password + if i.BootstrapTokenString != nil { + creds = i.BootstrapTokenString.String() + } digest, _ := hashCA(i.CACerts) - return fmt.Sprintf(tokenFormat, tokenPrefix, digest, i.Username, i.Password) + return tokenPrefix + digest + "::" + creds +} + +// Token returns the bootstrap token string, if available. +func (i *Info) Token() string { + if i.BootstrapTokenString != nil { + return i.BootstrapTokenString.String() + } + return "" } // ParseAndValidateToken parses a token, downloads and validates the server's CA bundle, // and validates it according to the caHash from the token if set. -func ParseAndValidateToken(server string, token string) (*Info, error) { +func ParseAndValidateToken(server string, token string, options ...ValidationOption) (*Info, error) { info, err := parseToken(token) if err != nil { return nil, err } - if err := info.setAndValidateServer(server); err != nil { - return nil, err + for _, option := range options { + option(info) } - return info, nil -} - -// ParseAndValidateTokenForUser parses a token with user override, downloads and -// validates the server's CA bundle, and validates it according to the caHash from the token if set. -func ParseAndValidateTokenForUser(server, token, username string) (*Info, error) { - info, err := parseToken(token) - if err != nil { - return nil, err - } - - info.Username = username - if err := info.setAndValidateServer(server); err != nil { return nil, err } @@ -159,13 +181,21 @@ func parseToken(token string) (*Info, error) { return nil, errors.New("token must not be empty") } + // Turn bare password or bootstrap token into full K10 token with empty CA hash, + // for consistent parsing in the section below. if !strings.HasPrefix(token, tokenPrefix) { - token = fmt.Sprintf(tokenFormat, tokenPrefix, "", "", token) + _, err := kubeadm.NewBootstrapTokenString(token) + if err != nil { + token = tokenPrefix + ":::" + token + } else { + token = tokenPrefix + "::" + token + } } - // Strip off the prefix + // Strip off the prefix. token = token[len(tokenPrefix):] + // Split into CA hash and creds. parts := strings.SplitN(token, "::", 2) token = parts[0] if len(parts) > 1 { @@ -177,14 +207,20 @@ func parseToken(token string) (*Info, error) { token = parts[1] } - parts = strings.SplitN(token, ":", 2) - if len(parts) != 2 || len(parts[1]) == 0 { - return nil, errors.New("invalid token format") + // Try to parse creds as bootstrap token string; fall back to basic auth. + // If neither works, error. + bts, err := kubeadm.NewBootstrapTokenString(token) + if err != nil { + parts = strings.SplitN(token, ":", 2) + if len(parts) != 2 || len(parts[1]) == 0 { + return nil, errors.New("invalid token format") + } + info.Username = parts[0] + info.Password = parts[1] + } else { + info.BootstrapTokenString = bts } - info.Username = parts[0] - info.Password = parts[1] - return &info, nil } @@ -192,21 +228,30 @@ func parseToken(token string) (*Info, error) { // If the CA bundle is empty, it validates using the default http client using the OS CA bundle. // If the CA bundle is not empty but does not contain any valid certs, it validates using // an empty CA bundle (which will always fail). -func GetHTTPClient(cacerts []byte) *http.Client { +// If valid cert+key paths can be loaded from the provided paths, they are used for client cert auth. +func GetHTTPClient(cacerts []byte, certFile, keyFile string) *http.Client { if len(cacerts) == 0 { return defaultClient } - pool := x509.NewCertPool() - pool.AppendCertsFromPEM(cacerts) + tlsConfig := &tls.Config{ + RootCAs: x509.NewCertPool(), + } + + tlsConfig.RootCAs.AppendCertsFromPEM(cacerts) + + // Try to load certs from the provided cert and key. We ignore errors, + // as it is OK if the paths were empty or the files don't currently exist. + cert, err := tls.LoadX509KeyPair(certFile, keyFile) + if err == nil { + tlsConfig.Certificates = []tls.Certificate{cert} + } return &http.Client{ Timeout: defaultClientTimeout, Transport: &http.Transport{ DisableKeepAlives: true, - TLSClientConfig: &tls.Config{ - RootCAs: pool, - }, + TLSClientConfig: tlsConfig, }, } } @@ -223,7 +268,7 @@ func (i *Info) Get(path string) ([]byte, error) { } p.Scheme = u.Scheme p.Host = u.Host - return get(p.String(), GetHTTPClient(i.CACerts), i.Username, i.Password) + return get(p.String(), GetHTTPClient(i.CACerts, i.CertFile, i.KeyFile), i.Username, i.Password, i.Token()) } // Put makes a request to a subpath of info's BaseURL @@ -238,7 +283,7 @@ func (i *Info) Put(path string, body []byte) error { } p.Scheme = u.Scheme p.Host = u.Host - return put(p.String(), body, GetHTTPClient(i.CACerts), i.Username, i.Password) + return put(p.String(), body, GetHTTPClient(i.CACerts, i.CertFile, i.KeyFile), i.Username, i.Password, i.Token()) } // setServer sets the BaseURL and CACerts fields of the Info by connecting to the server @@ -296,13 +341,13 @@ func getCACerts(u url.URL) ([]byte, error) { // This first request is expected to fail. If the server has // a cert that can be validated using the default CA bundle, return // success with no CA certs. - _, err := get(url, defaultClient, "", "") + _, err := get(url, defaultClient, "", "", "") if err == nil { return nil, nil } // Download the CA bundle using a client that does not validate certs. - cacerts, err := get(url, insecureClient, "", "") + cacerts, err := get(url, insecureClient, "", "", "") if err != nil { return nil, errors.Wrap(err, "failed to get CA certs") } @@ -310,7 +355,7 @@ func getCACerts(u url.URL) ([]byte, error) { // Request the CA bundle again, validating that the CA bundle can be loaded // and used to validate the server certificate. This should only fail if we somehow // get an empty CA bundle. or if the dynamiclistener cert is incorrectly signed. - _, err = get(url, GetHTTPClient(cacerts), "", "") + _, err = get(url, GetHTTPClient(cacerts, "", ""), "", "", "") if err != nil { return nil, errors.Wrap(err, "CA cert validation failed") } @@ -320,13 +365,15 @@ func getCACerts(u url.URL) ([]byte, error) { // get makes a request to a url using a provided client, username, and password, // returning the response body. -func get(u string, client *http.Client, username, password string) ([]byte, error) { +func get(u string, client *http.Client, username, password, token string) ([]byte, error) { req, err := http.NewRequest(http.MethodGet, u, nil) if err != nil { return nil, err } - if username != "" { + if token != "" { + req.Header.Add("Authorization", "Bearer "+token) + } else if username != "" { req.SetBasicAuth(username, password) } @@ -345,13 +392,15 @@ func get(u string, client *http.Client, username, password string) ([]byte, erro // put makes a request to a url using a provided client, username, and password // only an error is returned -func put(u string, body []byte, client *http.Client, username, password string) error { +func put(u string, body []byte, client *http.Client, username, password, token string) error { req, err := http.NewRequest(http.MethodPut, u, bytes.NewBuffer(body)) if err != nil { return err } - if username != "" { + if token != "" { + req.Header.Add("Authorization", "Bearer "+token) + } else if username != "" { req.SetBasicAuth(username, password) } diff --git a/pkg/clientaccess/token_test.go b/pkg/clientaccess/token_test.go index 76c458797e..c538de164c 100644 --- a/pkg/clientaccess/token_test.go +++ b/pkg/clientaccess/token_test.go @@ -21,6 +21,7 @@ import ( var ( defaultUsername = "server" defaultPassword = "token" + defaultToken = "abcdef.0123456789abcdef" ) // Test_UnitTrustedCA confirms that tokens are validated when the server uses a cert (self-signed or otherwise) @@ -62,7 +63,7 @@ func Test_UnitTrustedCA(t *testing.T) { assert.Equal(testCase.expected, info.Username, testCase.token) } - info, err = ParseAndValidateTokenForUser(server.URL, testCase.token, "agent") + info, err = ParseAndValidateToken(server.URL, testCase.token, WithUser("agent")) if assert.NoError(err, testCase) { assert.Nil(info.CACerts, testCase) assert.Equal("agent", info.Username, testCase) @@ -108,7 +109,7 @@ func Test_UnitUntrustedCA(t *testing.T) { assert.Equal(testCase.expected, info.Username, testCase) } - info, err = ParseAndValidateTokenForUser(server.URL, testCase.token, "agent") + info, err = ParseAndValidateToken(server.URL, testCase.token, WithUser("agent")) if assert.NoError(err, testCase) { assert.Equal(testInfo.CACerts, info.CACerts, testCase) assert.Equal("agent", info.Username, testCase) @@ -132,11 +133,41 @@ func Test_UnitInvalidServers(t *testing.T) { _, err := ParseAndValidateToken(testCase.server, testCase.token) assert.EqualError(err, testCase.expected, testCase) - _, err = ParseAndValidateTokenForUser(testCase.server, testCase.token, defaultUsername) + _, err = ParseAndValidateToken(testCase.server, testCase.token, WithUser(defaultUsername)) assert.EqualError(err, testCase.expected, testCase) } } +// Test_UnitValidTokens tests that valid tokens can be parsed, and give the expected result +func Test_UnitValidTokens(t *testing.T) { + assert := assert.New(t) + server := newTLSServer(t, defaultUsername, defaultPassword, false) + defer server.Close() + digest, _ := hashCA(getServerCA(server)) + + testCases := []struct { + server string + token string + expectUsername string + expectPassword string + expectToken string + }{ + {server.URL, defaultPassword, "", defaultPassword, ""}, + {server.URL, defaultToken, "", "", defaultToken}, + {server.URL, "K10" + digest + ":::" + defaultPassword, "", defaultPassword, ""}, + {server.URL, "K10" + digest + "::" + defaultUsername + ":" + defaultPassword, defaultUsername, defaultPassword, ""}, + {server.URL, "K10" + digest + "::" + defaultToken, "", "", defaultToken}, + } + + for _, testCase := range testCases { + info, err := ParseAndValidateToken(testCase.server, testCase.token) + assert.NoError(err) + assert.Equal(testCase.expectUsername, info.Username, testCase) + assert.Equal(testCase.expectPassword, info.Password, testCase) + assert.Equal(testCase.expectToken, info.Token(), testCase) + } +} + // Test_UnitInvalidTokens tests that tokens which are empty, invalid, or incorrect are properly rejected func Test_UnitInvalidTokens(t *testing.T) { assert := assert.New(t) @@ -164,7 +195,7 @@ func Test_UnitInvalidTokens(t *testing.T) { assert.EqualError(err, testCase.expected, testCase) assert.Nil(info, testCase) - info, err = ParseAndValidateTokenForUser(testCase.server, testCase.token, defaultUsername) + info, err = ParseAndValidateToken(testCase.server, testCase.token, WithUser(defaultUsername)) assert.EqualError(err, testCase.expected, testCase) assert.Nil(info, testCase) } @@ -199,7 +230,7 @@ func Test_UnitInvalidCredentials(t *testing.T) { assert.Empty(res, testCase) } - info, err = ParseAndValidateTokenForUser(server.URL, testCase, defaultUsername) + info, err = ParseAndValidateToken(server.URL, testCase, WithUser(defaultUsername)) assert.NoError(err, testCase) if assert.NotNil(info) { res, err := info.Get("/v1-k3s/server-bootstrap") @@ -219,7 +250,7 @@ func Test_UnitWrongCert(t *testing.T) { assert.Error(err) assert.Nil(info) - info, err = ParseAndValidateTokenForUser(server.URL, defaultPassword, defaultUsername) + info, err = ParseAndValidateToken(server.URL, defaultPassword, WithUser(defaultUsername)) assert.Error(err) assert.Nil(info) } @@ -244,7 +275,7 @@ func Test_UnitConnectionFailures(t *testing.T) { assert.WithinDuration(time.Now(), startTime, testDuration, testCase) startTime = time.Now() - info, err = ParseAndValidateTokenForUser(testCase.server, testCase.token, defaultUsername) + info, err = ParseAndValidateToken(testCase.server, testCase.token, WithUser(defaultUsername)) assert.Error(err, testCase) assert.Nil(info, testCase) assert.WithinDuration(startTime, time.Now(), testDuration, testCase) @@ -295,7 +326,7 @@ func Test_UnitParseAndGet(t *testing.T) { } for _, testCase := range testCases { - info, err := ParseAndValidateTokenForUser(server.URL+testCase.extraBasePre, defaultPassword, defaultUsername) + info, err := ParseAndValidateToken(server.URL+testCase.extraBasePre, defaultPassword, WithUser(defaultUsername)) // Check for expected error when parsing server + token if testCase.parseFail { assert.Error(err, testCase) diff --git a/pkg/cluster/bootstrap.go b/pkg/cluster/bootstrap.go index 79c436603c..69d5155f1e 100644 --- a/pkg/cluster/bootstrap.go +++ b/pkg/cluster/bootstrap.go @@ -94,7 +94,7 @@ func (c *Cluster) shouldBootstrapLoad(ctx context.Context) (bool, bool, error) { // etcd is promoted from learner. Odds are we won't need this info, and we don't want to fail startup // due to failure to retrieve it as this will break cold cluster restart, so we ignore any errors. if c.config.JoinURL != "" && c.config.Token != "" { - c.clientAccessInfo, _ = clientaccess.ParseAndValidateTokenForUser(c.config.JoinURL, c.config.Token, "server") + c.clientAccessInfo, _ = clientaccess.ParseAndValidateToken(c.config.JoinURL, c.config.Token, clientaccess.WithUser("server")) } return false, true, nil } else if c.config.JoinURL == "" { @@ -109,7 +109,7 @@ func (c *Cluster) shouldBootstrapLoad(ctx context.Context) (bool, bool, error) { // Fail if the token isn't syntactically valid, or if the CA hash on the remote server doesn't match // the hash in the token. The password isn't actually checked until later when actually bootstrapping. - info, err := clientaccess.ParseAndValidateTokenForUser(c.config.JoinURL, c.config.Token, "server") + info, err := clientaccess.ParseAndValidateToken(c.config.JoinURL, c.config.Token, clientaccess.WithUser("server")) if err != nil { return false, false, err } @@ -453,7 +453,7 @@ func (c *Cluster) compareConfig() error { if token == "" { token = c.config.Token } - agentClientAccessInfo, err := clientaccess.ParseAndValidateTokenForUser(c.config.JoinURL, token, "node") + agentClientAccessInfo, err := clientaccess.ParseAndValidateToken(c.config.JoinURL, token, clientaccess.WithUser("node")) if err != nil { return err } diff --git a/pkg/daemons/config/types.go b/pkg/daemons/config/types.go index 2b9047e902..3b577bce1d 100644 --- a/pkg/daemons/config/types.go +++ b/pkg/daemons/config/types.go @@ -76,6 +76,8 @@ type Agent struct { PodManifests string NodeName string NodeConfigPath string + ClientKubeletCert string + ClientKubeletKey string ServingKubeletCert string ServingKubeletKey string ServiceCIDR *net.IPNet