Skip to content

Commit 5a6e902

Browse files
guillaumerosepraveenkumar
authored andcommitted
Generate the kubeadmin password as soon as possible
The kubeadmin password is now generated at the same time as the disk image. The htpasswd file is updated in the cluster only if needed and not only when the kubeadmin-password file is created. This ensures when the VM is marked as existing to have everything in place in the machine directory. Still missing: the ssh key. Some users saw the error with crc console: Cannot create cluster configuration: Error reading kubeadmin password from bundle This change will avoid this error.
1 parent 42edbc4 commit 5a6e902

File tree

3 files changed

+140
-24
lines changed

3 files changed

+140
-24
lines changed

pkg/crc/cluster/kubeadmin_password.go

+96-24
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package cluster
22

33
import (
4+
"bufio"
5+
"bytes"
46
"crypto/rand"
57
"encoding/base64"
68
"fmt"
@@ -11,49 +13,63 @@ import (
1113
"github.com/code-ready/crc/pkg/crc/constants"
1214
"github.com/code-ready/crc/pkg/crc/logging"
1315
"github.com/code-ready/crc/pkg/crc/oc"
14-
crcos "github.com/code-ready/crc/pkg/os"
1516
"golang.org/x/crypto/bcrypt"
1617
)
1718

18-
// UpdateKubeAdminUserPassword does following
19-
// - Create and put updated kubeadmin password to ~/.crc/machine/crc/kubeadmin-password
20-
// - Update the htpasswd secret
21-
func UpdateKubeAdminUserPassword(ocConfig oc.Config, kubeAdminPassword string) error {
22-
var err error
19+
// GenerateKubeAdminUserPassword creates and put updated kubeadmin password to ~/.crc/machine/crc/kubeadmin-password
20+
func GenerateKubeAdminUserPassword() error {
21+
logging.Infof("Generating new password for the kubeadmin user")
2322
kubeAdminPasswordFile := constants.GetKubeAdminPasswordPath()
24-
if crcos.FileExists(kubeAdminPasswordFile) {
25-
logging.Debugf("kubeadmin password has already been updated")
26-
return nil
23+
kubeAdminPassword, err := GenerateRandomPasswordHash(23)
24+
if err != nil {
25+
return fmt.Errorf("Cannot generate the kubeadmin user password: %w", err)
2726
}
27+
return ioutil.WriteFile(kubeAdminPasswordFile, []byte(kubeAdminPassword), 0600)
28+
}
2829

29-
if kubeAdminPassword == "" {
30-
logging.Infof("Generating new password for the kubeadmin user")
31-
kubeAdminPassword, err = GenerateRandomPasswordHash(23)
32-
if err != nil {
33-
return fmt.Errorf("Cannot generate the kubeadmin user password: %w", err)
30+
// UpdateKubeAdminUserPassword updates the htpasswd secret
31+
func UpdateKubeAdminUserPassword(ocConfig oc.Config, newPassword string) error {
32+
if newPassword != "" {
33+
logging.Infof("Overriding password for kubeadmin user")
34+
if err := ioutil.WriteFile(constants.GetKubeAdminPasswordPath(), []byte(strings.TrimSpace(newPassword)), 0600); err != nil {
35+
return err
3436
}
3537
}
3638

37-
hashDeveloperPasswd, err := hashBcrypt("developer")
39+
kubeAdminPassword, err := GetKubeadminPassword()
3840
if err != nil {
39-
return err
41+
return fmt.Errorf("Cannot generate the kubeadmin user password: %w", err)
42+
}
43+
credentials := map[string]string{
44+
"developer": "developer",
45+
"kubeadmin": kubeAdminPassword,
4046
}
4147

42-
hashKubeAdminPasswd, err := hashBcrypt(kubeAdminPassword)
48+
given, _, err := ocConfig.RunOcCommandPrivate("get", "secret", "htpass-secret", "-n", "openshift-config", "-o", `jsonpath="{.data.htpasswd}"`)
49+
if err != nil {
50+
return err
51+
}
52+
ok, err := compareHtpasswd(given, credentials)
4353
if err != nil {
4454
return err
4555
}
46-
base64Data := getBase64(hashDeveloperPasswd, hashKubeAdminPasswd)
56+
if ok {
57+
return nil
58+
}
4759

60+
logging.Infof("Changing the password for the kubeadmin user")
61+
expected, err := getHtpasswd(credentials)
62+
if err != nil {
63+
return err
64+
}
4865
cmdArgs := []string{"patch", "secret", "htpass-secret", "-p",
49-
fmt.Sprintf(`'{"data":{"htpasswd":"%s"}}'`, base64Data),
66+
fmt.Sprintf(`'{"data":{"htpasswd":"%s"}}'`, expected),
5067
"-n", "openshift-config", "--type", "merge"}
5168
_, stderr, err := ocConfig.RunOcCommandPrivate(cmdArgs...)
5269
if err != nil {
5370
return fmt.Errorf("Failed to update kubeadmin password %v: %s", err, stderr)
5471
}
55-
56-
return ioutil.WriteFile(kubeAdminPasswordFile, []byte(kubeAdminPassword), 0600)
72+
return nil
5773
}
5874

5975
func GetKubeadminPassword() (string, error) {
@@ -110,7 +126,63 @@ func hashBcrypt(password string) (hash string, err error) {
110126
return string(passwordBytes), nil
111127
}
112128

113-
func getBase64(developerUserPassword, adminUserPassword string) string {
114-
s := fmt.Sprintf("developer:%s\nkubeadmin:%s", developerUserPassword, adminUserPassword)
115-
return base64.StdEncoding.EncodeToString([]byte(s))
129+
func getHtpasswd(credentials map[string]string) (string, error) {
130+
var ret []string
131+
for username, password := range credentials {
132+
hash, err := hashBcrypt(password)
133+
if err != nil {
134+
return "", err
135+
}
136+
ret = append(ret, fmt.Sprintf("%s:%s", username, hash))
137+
}
138+
return base64.StdEncoding.EncodeToString([]byte(strings.Join(ret, "\n"))), nil
139+
}
140+
141+
// source https://github.com/openshift/oauth-server/blob/04985077512fec241a5170074bf767c23592d7e7/pkg/authenticator/password/htpasswd/htpasswd.go
142+
func compareHtpasswd(given string, credentials map[string]string) (bool, error) {
143+
decoded, err := base64.StdEncoding.DecodeString(given)
144+
if err != nil {
145+
return false, err
146+
}
147+
scanner := bufio.NewScanner(bytes.NewReader(decoded))
148+
149+
found := 0
150+
for scanner.Scan() {
151+
line := scanner.Text()
152+
if len(line) == 0 {
153+
continue
154+
}
155+
parts := strings.SplitN(line, ":", 2)
156+
if len(parts) != 2 {
157+
continue
158+
}
159+
username := parts[0]
160+
password := parts[1]
161+
162+
if expectedPassword, ok := credentials[username]; ok {
163+
ok, err := testBCryptPassword(expectedPassword, password)
164+
if err != nil {
165+
return false, err
166+
}
167+
if !ok {
168+
return false, nil
169+
}
170+
found++
171+
}
172+
}
173+
if found != len(credentials) {
174+
return false, nil
175+
}
176+
return true, nil
177+
}
178+
179+
func testBCryptPassword(password, hash string) (bool, error) {
180+
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
181+
if err == bcrypt.ErrMismatchedHashAndPassword {
182+
return false, nil
183+
}
184+
if err != nil {
185+
return false, err
186+
}
187+
return true, nil
116188
}
+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package cluster
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func TestCompareHtpasswdWithOneUsername(t *testing.T) {
10+
htpasswd, err := getHtpasswd(map[string]string{"username": "password1"})
11+
assert.NoError(t, err)
12+
13+
ok, err := compareHtpasswd(htpasswd, map[string]string{"username": "password1"})
14+
assert.NoError(t, err)
15+
assert.True(t, ok)
16+
17+
ok, err = compareHtpasswd(htpasswd, map[string]string{"username": "password2"})
18+
assert.NoError(t, err)
19+
assert.False(t, ok)
20+
21+
ok, err = compareHtpasswd(htpasswd, map[string]string{"other": "password1"})
22+
assert.NoError(t, err)
23+
assert.False(t, ok)
24+
25+
ok, err = compareHtpasswd(htpasswd, map[string]string{"username1": "password1", "username2": "password2"})
26+
assert.NoError(t, err)
27+
assert.False(t, ok)
28+
}
29+
30+
func TestCompareHtpasswdWithTwoUsernames(t *testing.T) {
31+
htpasswd, err := getHtpasswd(map[string]string{"username1": "password1", "username2": "password2"})
32+
assert.NoError(t, err)
33+
34+
ok, err := compareHtpasswd(htpasswd, map[string]string{"username1": "password1", "username2": "password2"})
35+
assert.NoError(t, err)
36+
assert.True(t, ok)
37+
38+
ok, err = compareHtpasswd(htpasswd, map[string]string{"username1": "password1", "username2": "password3"})
39+
assert.NoError(t, err)
40+
assert.False(t, ok)
41+
}

pkg/crc/machine/start.go

+3
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,9 @@ func createHost(api libmachine.API, machineConfig config.MachineConfig) error {
524524
return fmt.Errorf("Error in driver during machine creation: %s", err)
525525
}
526526

527+
if err := cluster.GenerateKubeAdminUserPassword(); err != nil {
528+
return errors.Wrap(err, "Error generating new kubeadmin password")
529+
}
527530
if err := copyKubeconfig(machineConfig.Name, machineConfig); err != nil {
528531
return errors.Wrap(err, "Error copying kubeconfig file")
529532
}

0 commit comments

Comments
 (0)