diff --git a/config/config_test.go b/config/config_test.go index efcdd18..5d27e54 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -164,7 +164,7 @@ func TestConfigCompile(t *testing.T) { func TestCloudConfigUnknownKeys(t *testing.T) { contents := ` -coreos: +coreos: etcd: discovery: "https://discovery.etcd.io/827c73219eeb2fa5530027c37bf18877" coreos_unknown: @@ -227,7 +227,7 @@ func TestCloudConfigEmpty(t *testing.T) { // Assert that the parsing of a cloud config file "generally works" func TestCloudConfig(t *testing.T) { contents := ` -coreos: +coreos: etcd: discovery: "https://discovery.etcd.io/827c73219eeb2fa5530027c37bf18877" update: @@ -236,14 +236,14 @@ coreos: - name: 50-eth0.network runtime: yes content: '[Match] - + Name=eth47 - - + + [Network] - + Address=10.209.171.177/19 - + ' oem: id: rackspace @@ -367,6 +367,7 @@ users: gecos: arbitrary comment homedir: /home/place no_create_home: yes + lock_passwd: false primary_group: things groups: - ping diff --git a/config/user.go b/config/user.go index eb04bc1..474f465 100644 --- a/config/user.go +++ b/config/user.go @@ -24,6 +24,7 @@ type User struct { GECOS string `yaml:"gecos"` Homedir string `yaml:"homedir"` NoCreateHome bool `yaml:"no_create_home"` + LockPasswd bool `yaml:"lock_passwd"` PrimaryGroup string `yaml:"primary_group"` Groups []string `yaml:"groups"` NoUserGroup bool `yaml:"no_user_group"` diff --git a/initialize/config.go b/initialize/config.go index 94a47fa..6be892d 100644 --- a/initialize/config.go +++ b/initialize/config.go @@ -73,6 +73,11 @@ func Apply(cfg config.CloudConfig, ifaces []network.InterfaceGenerator, env *Env } } + if err = system.LockUnlockUser(&user); err != nil { + log.Printf("Failed lock/unlock user '%s': %v", user.Name, err) + return err + } + if len(user.SSHAuthorizedKeys) > 0 { log.Printf("Authorizing %d SSH keys for user '%s'", len(user.SSHAuthorizedKeys), user.Name) if err := system.AuthorizeSSHKeys(user.Name, env.SSHKeyName(), user.SSHAuthorizedKeys); err != nil { diff --git a/system/user.go b/system/user_linux.go similarity index 68% rename from system/user.go rename to system/user_linux.go index 3f973c3..f63a340 100644 --- a/system/user.go +++ b/system/user_linux.go @@ -18,15 +18,13 @@ import ( "fmt" "log" "os/exec" - "os/user" "strings" "github.com/coreos/coreos-cloudinit/config" ) func UserExists(u *config.User) bool { - _, err := user.Lookup(u.Name) - return err == nil + return exec.Command("getent", "passwd", u.Name).Run() == nil } func CreateUser(u *config.User) error { @@ -81,12 +79,46 @@ func CreateUser(u *config.User) error { output, err := exec.Command("useradd", args...).CombinedOutput() if err != nil { log.Printf("Command 'useradd %s' failed: %v\n%s", strings.Join(args, " "), err, output) + return err + } + + return nil +} + +func IsLockedUser(u *config.User) bool { + output, err := exec.Command("getent", "shadow", u.Name).CombinedOutput() + if err == nil { + fields := strings.Split(string(output), ":") + if len(fields[1]) > 1 && fields[1][0] == '!' { + return true + } + } + return false +} + +func LockUnlockUser(u *config.User) error { + args := []string{} + + if u.LockPasswd { + args = append(args, "-l") + } else { + if !IsLockedUser(u) { + return nil + } + args = append(args, "-u") + } + + args = append(args, u.Name) + + output, err := exec.Command("passwd", args...).CombinedOutput() + if err != nil { + log.Printf("Command 'passwd %s' failed: %v\n%s", strings.Join(args, " "), err, output) } return err } func SetUserPassword(user, hash string) error { - cmd := exec.Command("/usr/sbin/chpasswd", "-e") + cmd := exec.Command("chpasswd", "-e") stdin, err := cmd.StdinPipe() if err != nil { @@ -112,3 +144,12 @@ func SetUserPassword(user, hash string) error { return nil } + +func UserHome(name string) (string, error) { + output, err := exec.Command("getent", "passwd", name).CombinedOutput() + if err != nil { + return "", err + } + passwd := strings.Split(string(output), ":") + return passwd[5], nil +}