Skip to content

Commit

Permalink
Update kubeadmin password for each fresh start
Browse files Browse the repository at this point in the history
This patch will create a random password and then update the cluster
secret for htpasswd. It also add `~/.crc/machine/crc/kubeadmin-password`
file which contains created random password so that all commands able to
refers it.

fixes: #2048
  • Loading branch information
praveenkumar authored and guillaumerose committed Mar 16, 2021
1 parent 5566ef7 commit 716d38e
Show file tree
Hide file tree
Showing 27 changed files with 481 additions and 9 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ require (
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
github.com/zalando/go-keyring v0.1.1
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb // indirect
golang.org/x/oauth2 v0.0.0-20201203001011-0b49973bad19 // indirect
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -966,8 +966,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c h1:9HhBz5L/UjnK9XLtiZhYAdue5BVKep3PMmS2LuPDt8k=
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
Expand Down
94 changes: 94 additions & 0 deletions pkg/crc/cluster/kubeadminPassword.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package cluster

import (
"crypto/rand"
"encoding/base64"
"fmt"
"math/big"

"github.com/code-ready/crc/pkg/crc/machine/bundle"
"github.com/code-ready/crc/pkg/crc/oc"
"golang.org/x/crypto/bcrypt"
)

// UpdateKubeAdminUserPassword does following
// - Create and put updated kubeadmin password to ~/.crc/machine/crc/kubeadmin-password
// - Update the htpasswd secret
func UpdateKubeAdminUserPassword(kubeAdminPassword string, ocConfig oc.Config, bundle *bundle.CrcBundleInfo) error {
// In case of cluster started from stopped state.
if kubeAdminPassword == "" {
return nil
}
hashDeveloperPasswd, err := hashBcrypt("developer")
if err != nil {
return err
}

hashKubeAdminPasswd, err := hashBcrypt(kubeAdminPassword)
if err != nil {
return err
}
base64Data := getBase64(hashDeveloperPasswd, hashKubeAdminPasswd)

cmdArgs := []string{"patch", "secret", "htpass-secret", "-p",
fmt.Sprintf(`'{"data":{"htpasswd":"%s"}}'`, base64Data),
"-n", "openshift-config", "--type", "merge"}
_, stderr, err := ocConfig.RunOcCommandPrivate(cmdArgs...)
if err != nil {
return fmt.Errorf("Failed to update kubeadmin password %v: %s", err, stderr)
}
if err := bundle.UpdateKubeadminPassword(kubeAdminPassword); err != nil {
return err
}
return nil
}

// generateRandomPasswordHash generates a hash of a random ASCII password
// 5char-5char-5char-5char
// Copied from openshift/installer https://github.com/openshift/installer/blob/master/pkg/asset/password/password.go
func GenerateRandomPasswordHash(length int) (string, error) {
const (
lowerLetters = "abcdefghijkmnopqrstuvwxyz"
upperLetters = "ABCDEFGHIJKLMNPQRSTUVWXYZ"
digits = "23456789"
all = lowerLetters + upperLetters + digits
)
var password string
for i := 0; i < length; i++ {
n, err := rand.Int(rand.Reader, big.NewInt(int64(len(all))))
if err != nil {
return "", err
}
newchar := string(all[n.Int64()])
if password == "" {
password = newchar
}
if i < length-1 {
n, err = rand.Int(rand.Reader, big.NewInt(int64(len(password)+1)))
if err != nil {
return "", err
}
j := n.Int64()
password = password[0:j] + newchar + password[j:]
}
}
pw := []rune(password)
for _, replace := range []int{5, 11, 17} {
pw[replace] = '-'
}

return string(pw), nil
}

func hashBcrypt(password string) (hash string, err error) {
passwordBytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return
}
return string(passwordBytes), nil
}

func getBase64(developerUserPassword, adminUserPassword string) string {
s := fmt.Sprintf("developer:%s\nkubeadmin:%s", developerUserPassword, adminUserPassword)
return base64.StdEncoding.EncodeToString([]byte(s))
}
16 changes: 15 additions & 1 deletion pkg/crc/machine/bundle/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,27 @@ func (bundle *CrcBundleInfo) GetInitramfsPath() string {
}

func (bundle *CrcBundleInfo) GetKubeadminPassword() (string, error) {
rawData, err := ioutil.ReadFile(bundle.resolvePath(bundle.ClusterInfo.KubeadminPasswordFile))
// First check if ~/.crc/machine/crc/kubeadmin-password file exist and read password from there
kubeAdminPasswordFile := filepath.Join(constants.MachineInstanceDir, constants.DefaultName, bundle.ClusterInfo.KubeadminPasswordFile)
if _, err := os.Stat(kubeAdminPasswordFile); err != nil {
kubeAdminPasswordFile = bundle.resolvePath(bundle.ClusterInfo.KubeadminPasswordFile)
}
rawData, err := ioutil.ReadFile(kubeAdminPasswordFile)
if err != nil {
return "", err
}
return strings.TrimSpace(string(rawData)), nil
}

func (bundle *CrcBundleInfo) UpdateKubeadminPassword(kubeadminPassword string) error {
kubeAdminPasswordFile := filepath.Join(constants.MachineInstanceDir, constants.DefaultName, bundle.ClusterInfo.KubeadminPasswordFile)
err := ioutil.WriteFile(kubeAdminPasswordFile, []byte(kubeadminPassword), 0600)
if err != nil {
return err
}
return nil
}

func (bundle *CrcBundleInfo) GetBundleBuildTime() (time.Time, error) {
return time.Parse(time.RFC3339, strings.TrimSpace(bundle.BuildInfo.BuildTime))
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/crc/machine/console.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func (client *client) GetConsoleURL() (*ConsoleResult, error) {
return nil, errors.Wrap(err, "Error loading bundle metadata")
}

clusterConfig, err := getClusterConfig(crcBundleMetadata)
clusterConfig, err := getClusterConfig(crcBundleMetadata, "")
if err != nil {
return nil, errors.Wrap(err, "Error loading cluster configuration")
}
Expand Down
5 changes: 4 additions & 1 deletion pkg/crc/machine/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ import (
"github.com/code-ready/machine/libmachine/drivers"
)

func getClusterConfig(bundleInfo *bundle.CrcBundleInfo) (*ClusterConfig, error) {
func getClusterConfig(bundleInfo *bundle.CrcBundleInfo, generatedKubeadminPassword string) (*ClusterConfig, error) {
kubeadminPassword, err := bundleInfo.GetKubeadminPassword()
if err != nil {
return nil, fmt.Errorf("Error reading kubeadmin password from bundle %v", err)
}
if generatedKubeadminPassword != "" {
kubeadminPassword = generatedKubeadminPassword
}
proxyConfig, err := getProxyConfig(bundleInfo.ClusterInfo.BaseDomain)
if err != nil {
return nil, err
Expand Down
14 changes: 12 additions & 2 deletions pkg/crc/machine/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ func (client *client) Start(ctx context.Context, startConfig StartConfig) (*Star

// Pre-VM start
var host *host.Host
var kubeAdminUserPassword string
exists, err := client.Exists()
if err != nil {
return nil, errors.Wrap(err, "Cannot determine if VM exists")
Expand All @@ -131,6 +132,11 @@ func (client *client) Start(ctx context.Context, startConfig StartConfig) (*Star
NetworkMode: client.networkMode(),
}

kubeAdminUserPassword, err = cluster.GenerateRandomPasswordHash(23)
if err != nil {
return nil, errors.Wrap(err, "Cannot generate the kubeadmin user password")
}

crcBundleMetadata, err = getCrcBundleInfo(startConfig.BundlePath)
if err != nil {
return nil, errors.Wrap(err, "Error getting bundle metadata")
Expand Down Expand Up @@ -180,7 +186,7 @@ func (client *client) Start(ctx context.Context, startConfig StartConfig) (*Star
}
if vmState == state.Running {
logging.Infof("A CodeReady Containers VM for OpenShift %s is already running", crcBundleMetadata.GetOpenshiftVersion())
clusterConfig, err := getClusterConfig(crcBundleMetadata)
clusterConfig, err := getClusterConfig(crcBundleMetadata, kubeAdminUserPassword)
if err != nil {
return nil, errors.Wrap(err, "Cannot create cluster configuration")
}
Expand Down Expand Up @@ -222,7 +228,7 @@ func (client *client) Start(ctx context.Context, startConfig StartConfig) (*Star
}
}

clusterConfig, err := getClusterConfig(crcBundleMetadata)
clusterConfig, err := getClusterConfig(crcBundleMetadata, kubeAdminUserPassword)
if err != nil {
return nil, errors.Wrap(err, "Cannot create cluster configuration")
}
Expand Down Expand Up @@ -366,6 +372,10 @@ func (client *client) Start(ctx context.Context, startConfig StartConfig) (*Star
return nil, errors.Wrap(err, "Failed to update cluster pull secret")
}

if err := cluster.UpdateKubeAdminUserPassword(kubeAdminUserPassword, ocConfig, crcBundleMetadata); err != nil {
return nil, errors.Wrap(err, "Failed to update kubeadmin user password")
}

if err := cluster.EnsureClusterIDIsNotEmpty(ocConfig); err != nil {
return nil, errors.Wrap(err, "Failed to update cluster ID")
}
Expand Down
35 changes: 35 additions & 0 deletions vendor/golang.org/x/crypto/bcrypt/base64.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 716d38e

Please sign in to comment.