diff --git a/config/v3_0/schema/ignition.json b/config/v3_0/schema/ignition.json index 22345708fe..f6b6fbee66 100644 --- a/config/v3_0/schema/ignition.json +++ b/config/v3_0/schema/ignition.json @@ -496,6 +496,9 @@ }, "shell": { "type": ["string", "null"] + }, + "shouldExist": { + "type": ["boolean", "null"] } }, "required": [ @@ -516,6 +519,9 @@ }, "system": { "type": ["boolean", "null"] + }, + "shouldExist": { + "type": ["boolean", "null"] } }, "required": [ diff --git a/config/v3_0/types/schema.go b/config/v3_0/types/schema.go index c8999275b3..e328be6a53 100644 --- a/config/v3_0/types/schema.go +++ b/config/v3_0/types/schema.go @@ -132,6 +132,7 @@ type PasswdGroup struct { Name string `json:"name"` PasswordHash *string `json:"passwordHash,omitempty"` System *bool `json:"system,omitempty"` + ShouldExist *bool `json:"shouldExist,omitempty"` } type PasswdUser struct { @@ -145,6 +146,7 @@ type PasswdUser struct { PasswordHash *string `json:"passwordHash,omitempty"` PrimaryGroup *string `json:"primaryGroup,omitempty"` SSHAuthorizedKeys []SSHAuthorizedKey `json:"sshAuthorizedKeys,omitempty"` + ShouldExist *bool `json:"shouldExist,omitempty"` Shell *string `json:"shell,omitempty"` System *bool `json:"system,omitempty"` UID *int `json:"uid,omitempty"` diff --git a/config/v3_1/schema/ignition.json b/config/v3_1/schema/ignition.json index 495b040f81..557bc66a73 100644 --- a/config/v3_1/schema/ignition.json +++ b/config/v3_1/schema/ignition.json @@ -514,6 +514,9 @@ }, "shell": { "type": ["string", "null"] + }, + "shouldExist": { + "type": ["boolean", "null"] } }, "required": [ @@ -534,6 +537,9 @@ }, "system": { "type": ["boolean", "null"] + }, + "shouldExist": { + "type": ["boolean", "null"] } }, "required": [ diff --git a/config/v3_1/types/schema.go b/config/v3_1/types/schema.go index 94781514f2..ab657e51e7 100644 --- a/config/v3_1/types/schema.go +++ b/config/v3_1/types/schema.go @@ -129,6 +129,7 @@ type PasswdGroup struct { Name string `json:"name"` PasswordHash *string `json:"passwordHash,omitempty"` System *bool `json:"system,omitempty"` + ShouldExist *bool `json:"shouldExist,omitempty"` } type PasswdUser struct { @@ -142,6 +143,7 @@ type PasswdUser struct { PasswordHash *string `json:"passwordHash,omitempty"` PrimaryGroup *string `json:"primaryGroup,omitempty"` SSHAuthorizedKeys []SSHAuthorizedKey `json:"sshAuthorizedKeys,omitempty"` + ShouldExist *bool `json:"shouldExist,omitempty"` Shell *string `json:"shell,omitempty"` System *bool `json:"system,omitempty"` UID *int `json:"uid,omitempty"` diff --git a/config/v3_2_experimental/schema/ignition.json b/config/v3_2_experimental/schema/ignition.json index 495b040f81..557bc66a73 100644 --- a/config/v3_2_experimental/schema/ignition.json +++ b/config/v3_2_experimental/schema/ignition.json @@ -514,6 +514,9 @@ }, "shell": { "type": ["string", "null"] + }, + "shouldExist": { + "type": ["boolean", "null"] } }, "required": [ @@ -534,6 +537,9 @@ }, "system": { "type": ["boolean", "null"] + }, + "shouldExist": { + "type": ["boolean", "null"] } }, "required": [ diff --git a/config/v3_2_experimental/types/schema.go b/config/v3_2_experimental/types/schema.go index fe2d3fdb45..97edab17bd 100644 --- a/config/v3_2_experimental/types/schema.go +++ b/config/v3_2_experimental/types/schema.go @@ -128,6 +128,7 @@ type PasswdGroup struct { Gid *int `json:"gid,omitempty"` Name string `json:"name"` PasswordHash *string `json:"passwordHash,omitempty"` + ShouldExist *bool `json:"shouldExist,omitempty"` System *bool `json:"system,omitempty"` } @@ -142,6 +143,7 @@ type PasswdUser struct { PasswordHash *string `json:"passwordHash,omitempty"` PrimaryGroup *string `json:"primaryGroup,omitempty"` SSHAuthorizedKeys []SSHAuthorizedKey `json:"sshAuthorizedKeys,omitempty"` + ShouldExist *bool `json:"shouldExist,omitempty"` Shell *string `json:"shell,omitempty"` System *bool `json:"system,omitempty"` UID *int `json:"uid,omitempty"` diff --git a/internal/distro/distro.go b/internal/distro/distro.go index 9c21a1506b..acca66fc50 100644 --- a/internal/distro/distro.go +++ b/internal/distro/distro.go @@ -34,6 +34,7 @@ var ( // Helper programs groupaddCmd = "groupadd" + groupdelCmd = "groupdel" mdadmCmd = "mdadm" mountCmd = "mount" sgdiskCmd = "sgdisk" @@ -41,6 +42,7 @@ var ( udevadmCmd = "udevadm" usermodCmd = "usermod" useraddCmd = "useradd" + userdelCmd = "userdel" setfilesCmd = "setfiles" wipefsCmd = "wipefs" @@ -74,6 +76,7 @@ func KernelCmdlinePath() string { return kernelCmdlinePath } func SystemConfigDir() string { return fromEnv("SYSTEM_CONFIG_DIR", systemConfigDir) } func GroupaddCmd() string { return groupaddCmd } +func GroupdelCmd() string { return groupdelCmd } func MdadmCmd() string { return mdadmCmd } func MountCmd() string { return mountCmd } func SgdiskCmd() string { return sgdiskCmd } @@ -81,6 +84,7 @@ func ModprobeCmd() string { return modprobeCmd } func UdevadmCmd() string { return udevadmCmd } func UsermodCmd() string { return usermodCmd } func UseraddCmd() string { return useraddCmd } +func UserdelCmd() string { return userdelCmd } func SetfilesCmd() string { return setfilesCmd } func WipefsCmd() string { return wipefsCmd } diff --git a/internal/exec/stages/files/passwd.go b/internal/exec/stages/files/passwd.go index 3ef4b00092..de0cfdfeaf 100644 --- a/internal/exec/stages/files/passwd.go +++ b/internal/exec/stages/files/passwd.go @@ -100,9 +100,12 @@ func (s stage) createUsers(config types.Config) error { defer s.Logger.PopPrefix() for _, u := range config.Passwd.Users { - if err := s.EnsureUser(u); err != nil { + shouldExist := u.ShouldExist == nil || *u.ShouldExist + if err := s.EnsureUser(u, shouldExist); err != nil { return fmt.Errorf("failed to create user %q: %v", u.Name, err) + } else if !shouldExist && err == nil { + continue } if err := s.SetPasswordHash(u); err != nil { @@ -128,9 +131,12 @@ func (s stage) createGroups(config types.Config) error { defer s.Logger.PopPrefix() for _, g := range config.Passwd.Groups { - if err := s.CreateGroup(g); err != nil { + shouldExist := g.ShouldExist == nil || *g.ShouldExist + if err := s.CreateGroup(g, shouldExist); err != nil { return fmt.Errorf("failed to create group %q: %v", g.Name, err) + } else if !shouldExist && err == nil { + continue } } diff --git a/internal/exec/util/passwd.go b/internal/exec/util/passwd.go index 30b03ac4bd..0d6b4ddf02 100644 --- a/internal/exec/util/passwd.go +++ b/internal/exec/util/passwd.go @@ -49,14 +49,37 @@ func appendIfStringSet(args []string, arg string, str *string) []string { return args } +// DeleteUser deletes a user from the OS +func (u Util) DeleteUser(c types.PasswdUser) error { + args := []string{"--remove", "--force", c.Name} + _, err := u.LogCmd(exec.Command(distro.UserdelCmd(), args...), + "deleting user %q", c.Name) + return err +} + +// DeleteGroup deletes a group from the OS +func (u Util) DeleteGroup(c types.PasswdGroup) error { + args := []string{"--force", c.Name} + _, err := u.LogCmd(exec.Command(distro.GroupdelCmd(), args...), + "deleting group %q", c.Name) + return err +} + // EnsureUser ensures that the user exists as described. If the user does not // yet exist, they will be created, otherwise the existing user will be // modified. -func (u Util) EnsureUser(c types.PasswdUser) error { +func (u Util) EnsureUser(c types.PasswdUser, shouldExist bool) error { exists, err := u.CheckIfUserExists(c) if err != nil { return err } + if !shouldExist && exists { + if err := u.DeleteUser(c); err != nil { + return fmt.Errorf("failed to delete user %q: %v", + c.Name, err) + } + return nil + } args := []string{"--root", u.DestDir} var cmd string @@ -245,7 +268,14 @@ func (u Util) SetPasswordHash(c types.PasswdUser) error { } // CreateGroup creates the group as described. -func (u Util) CreateGroup(g types.PasswdGroup) error { +func (u Util) CreateGroup(g types.PasswdGroup, shouldExist bool) error { + if !shouldExist { + if err := u.DeleteGroup(g); err != nil { + return fmt.Errorf("failed to delete group %q: %v", + g.Name, err) + } + return nil + } args := []string{"--root", u.DestDir} if g.Gid != nil {