package server import ( "context" "encoding/json" "fmt" "io" "net/http" "path/filepath" "github.com/k3s-io/k3s/pkg/cluster" "github.com/k3s-io/k3s/pkg/daemons/config" "github.com/k3s-io/k3s/pkg/passwd" "github.com/k3s-io/k3s/pkg/util" "github.com/k3s-io/k3s/pkg/version" "github.com/sirupsen/logrus" ) type TokenRotateRequest struct { NewToken *string `json:"newToken,omitempty"` } func getServerTokenRequest(req *http.Request) (TokenRotateRequest, error) { b, err := io.ReadAll(req.Body) if err != nil { return TokenRotateRequest{}, err } result := TokenRotateRequest{} err = json.Unmarshal(b, &result) return result, err } func tokenRequestHandler(ctx context.Context, server *config.Control) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { if req.Method != http.MethodPut { util.SendError(fmt.Errorf("method not allowed"), resp, req, http.StatusMethodNotAllowed) return } var err error sTokenReq, err := getServerTokenRequest(req) logrus.Debug("Received token request") if err != nil { util.SendError(err, resp, req, http.StatusBadRequest) return } if err = tokenRotate(ctx, server, *sTokenReq.NewToken); err != nil { util.SendErrorWithID(err, "token", resp, req, http.StatusInternalServerError) return } resp.WriteHeader(http.StatusOK) }) } func tokenRotate(ctx context.Context, server *config.Control, newToken string) error { passwd, err := passwd.Read(server.Runtime.PasswdFile) if err != nil { return err } if err != nil { return err } oldToken, found := passwd.Pass("server") if !found { return fmt.Errorf("server token not found") } if newToken == "" { newToken, err = util.Random(16) if err != nil { return err } } if err := passwd.EnsureUser("server", version.Program+":server", newToken); err != nil { return err } // If the agent token is the same a server, we need to change both if agentToken, found := passwd.Pass("node"); found && agentToken == oldToken && server.AgentToken == "" { if err := passwd.EnsureUser("node", version.Program+":agent", newToken); err != nil { return err } } if err := passwd.Write(server.Runtime.PasswdFile); err != nil { return err } serverTokenFile := filepath.Join(server.DataDir, "token") if err := writeToken("server:"+newToken, serverTokenFile, server.Runtime.ServerCA); err != nil { return err } if err := cluster.RotateBootstrapToken(ctx, server, oldToken); err != nil { return err } server.Token = newToken return cluster.Save(ctx, server, true) }