From f4b61ec97b1bd38aa6201509e71abea269ca5451 Mon Sep 17 00:00:00 2001 From: Gerard Braad Date: Wed, 31 May 2023 15:42:33 +0800 Subject: [PATCH] Set emergency login for core user with random password This PR allow user to set the emergency login for core user with a random generated password. It is helpful when user lost the ssh access and use VMM to debug the issue which require password for `core` user. Generated password is stored in `~/.crc/machine/crc/passwd` file which user can consume in case of debugging. When user unset the `enable-emergency-login` setting then password is locked for `core` user. ``` $ crc config set enable-emergency-login true $ crc config view - enable-emergency-login : true [...] $ crc start [...] INFO CRC VM is running INFO Emergency login password for core user is stored to /home/prkumar/.crc/machines/crc/passwd INFO Updating authorized keys... [...] ``` --- cmd/crc/cmd/start.go | 2 ++ pkg/crc/api/handlers.go | 1 + pkg/crc/config/settings.go | 3 +++ pkg/crc/constants/constants.go | 1 + pkg/crc/machine/start.go | 35 ++++++++++++++++++++++++++++++++++ pkg/crc/machine/types/types.go | 3 +++ 6 files changed, 45 insertions(+) diff --git a/cmd/crc/cmd/start.go b/cmd/crc/cmd/start.go index 70da5ba2e7..87c8044745 100644 --- a/cmd/crc/cmd/start.go +++ b/cmd/crc/cmd/start.go @@ -79,6 +79,8 @@ func runStart(ctx context.Context) (*types.StartResult, error) { IngressHTTPPort: config.Get(crcConfig.IngressHTTPPort).AsUInt(), IngressHTTPSPort: config.Get(crcConfig.IngressHTTPSPort).AsUInt(), EnableSharedDirs: config.Get(crcConfig.EnableSharedDirs).AsBool(), + + EmergencyLogin: config.Get(crcConfig.EmergencyLogin).AsBool(), } client := newMachine() diff --git a/pkg/crc/api/handlers.go b/pkg/crc/api/handlers.go index 5d00432f8a..c0ef5d8a10 100644 --- a/pkg/crc/api/handlers.go +++ b/pkg/crc/api/handlers.go @@ -130,6 +130,7 @@ func getStartConfig(cfg crcConfig.Storage, args client.StartConfig) types.StartC IngressHTTPSPort: cfg.Get(crcConfig.IngressHTTPSPort).AsUInt(), Preset: crcConfig.GetPreset(cfg), EnableSharedDirs: cfg.Get(crcConfig.EnableSharedDirs).AsBool(), + EmergencyLogin: cfg.Get(crcConfig.EmergencyLogin).AsBool(), } } diff --git a/pkg/crc/config/settings.go b/pkg/crc/config/settings.go index 5258760c59..503d9f97ed 100644 --- a/pkg/crc/config/settings.go +++ b/pkg/crc/config/settings.go @@ -34,6 +34,7 @@ const ( SharedDirPassword = "shared-dir-password" // #nosec G101 IngressHTTPPort = "ingress-http-port" IngressHTTPSPort = "ingress-https-port" + EmergencyLogin = "enable-emergency-login" ) func RegisterSettings(cfg *Config) { @@ -88,6 +89,8 @@ func RegisterSettings(cfg *Config) { "Disable update check (true/false, default: false)") cfg.AddSetting(ExperimentalFeatures, false, ValidateBool, SuccessfullyApplied, "Enable experimental features (true/false, default: false)") + cfg.AddSetting(EmergencyLogin, false, ValidateBool, SuccessfullyApplied, + "Enable emergency login for 'core' user. Password is randomly generated. (true/false, default: false)") // Shared directories configs if runtime.GOOS == "windows" { diff --git a/pkg/crc/constants/constants.go b/pkg/crc/constants/constants.go index 4c55631443..1c1112793c 100644 --- a/pkg/crc/constants/constants.go +++ b/pkg/crc/constants/constants.go @@ -110,6 +110,7 @@ var ( MachineInstanceDir = filepath.Join(MachineBaseDir, "machines") DaemonSocketPath = filepath.Join(CrcBaseDir, "crc.sock") KubeconfigFilePath = filepath.Join(MachineInstanceDir, DefaultName, "kubeconfig") + PasswdFilePath = filepath.Join(MachineInstanceDir, DefaultName, "passwd") ) func GetDefaultBundlePath(preset crcpreset.Preset) string { diff --git a/pkg/crc/machine/start.go b/pkg/crc/machine/start.go index f781a5cbb9..1e45831582 100644 --- a/pkg/crc/machine/start.go +++ b/pkg/crc/machine/start.go @@ -6,6 +6,7 @@ import ( "crypto/x509" "encoding/json" "fmt" + "math/rand" "os" "path/filepath" "strconv" @@ -32,6 +33,7 @@ import ( "github.com/crc-org/crc/pkg/crc/telemetry" crctls "github.com/crc-org/crc/pkg/crc/tls" "github.com/crc-org/crc/pkg/libmachine/host" + crcos "github.com/crc-org/crc/pkg/os" "github.com/crc-org/machine/libmachine/drivers" libmachinestate "github.com/crc-org/machine/libmachine/state" "github.com/docker/go-units" @@ -417,6 +419,16 @@ func (client *client) Start(ctx context.Context, startConfig types.StartConfig) } logging.Info("CRC VM is running") + if startConfig.EmergencyLogin { + if err := enableEmergencyLogin(sshRunner); err != nil { + return nil, errors.Wrap(err, "Error enabling emergency login") + } + } else { + if err := disableEmergencyLogin(sshRunner); err != nil { + return nil, errors.Wrap(err, "Error deleting the password for core user") + } + } + // Post VM start immediately update SSH key and copy kubeconfig to instance // dir and VM if err := updateSSHKeyPair(sshRunner); err != nil { @@ -784,6 +796,29 @@ func addNameServerToInstance(sshRunner *crcssh.Runner, ns string) error { return nil } +func enableEmergencyLogin(sshRunner *crcssh.Runner) error { + if crcos.FileExists(constants.PasswdFilePath) { + return nil + } + charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + b := make([]byte, 8) + for i := range b { + b[i] = charset[rand.Intn(len(charset))] //nolint + } + if err := os.WriteFile(constants.PasswdFilePath, b, 0600); err != nil { + return err + } + logging.Infof("Emergency login password for core user is stored to %s", constants.PasswdFilePath) + _, _, err := sshRunner.Run(fmt.Sprintf("sudo passwd core --unlock && echo %s | sudo passwd core --stdin", b)) + return err +} + +func disableEmergencyLogin(sshRunner *crcssh.Runner) error { + defer os.Remove(constants.PasswdFilePath) + _, _, err := sshRunner.RunPrivileged("disable core user password", "passwd", "--lock", "core") + return err +} + func updateSSHKeyPair(sshRunner *crcssh.Runner) error { // Read generated public key publicKey, err := os.ReadFile(constants.GetPublicKeyPath()) diff --git a/pkg/crc/machine/types/types.go b/pkg/crc/machine/types/types.go index e7c358643c..8373f5439f 100644 --- a/pkg/crc/machine/types/types.go +++ b/pkg/crc/machine/types/types.go @@ -37,6 +37,9 @@ type StartConfig struct { // Ports to access openshift routes IngressHTTPPort uint IngressHTTPSPort uint + + // Enable emergency login + EmergencyLogin bool } type ClusterConfig struct {