mirror of
https://github.com/kairos-io/kairos-agent.git
synced 2025-04-27 19:28:59 +00:00
Check for user+admin validity before actions (#575)
This commit is contained in:
parent
a3aadbbaa9
commit
97d25b8993
@ -29,9 +29,7 @@ import (
|
||||
"github.com/kairos-io/kairos-sdk/utils"
|
||||
qr "github.com/mudler/go-nodepair/qrcode"
|
||||
"github.com/mudler/go-pluggable"
|
||||
yip "github.com/mudler/yip/pkg/schema"
|
||||
"github.com/pterm/pterm"
|
||||
"github.com/twpayne/go-vfs/v4"
|
||||
)
|
||||
|
||||
func displayInfo(agentConfig *Config) {
|
||||
@ -217,27 +215,9 @@ func RunInstall(c *config.Config) error {
|
||||
utils.SetEnv(c.Env)
|
||||
utils.SetEnv(c.Install.Env)
|
||||
|
||||
// If nousers is enabled we do not check for the validity of the users and such
|
||||
// At this point, the config should be fully parsed and the yip stages ready
|
||||
if !c.Install.NoUsers {
|
||||
found := false
|
||||
cc, _ := c.Config.String()
|
||||
yamlConfig, err := yip.Load(cc, vfs.OSFS, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, stage := range yamlConfig.Stages {
|
||||
for _, x := range stage {
|
||||
if len(x.Users) > 0 {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if !found {
|
||||
return fmt.Errorf("No users found in any stage\nWe require at least one user or the install option 'install.nousers: true' to be set in the config in order to allow installing a system with no users.")
|
||||
}
|
||||
err := c.CheckForUsers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// UKI path. Check if we are on UKI AND if we are running off a cd, otherwise it makes no sense to run the install
|
||||
|
@ -38,6 +38,10 @@ func reset(reboot, unattended, resetOem bool, dir ...string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = cfg.CheckForUsers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Load the installation Config from the cloud-config data
|
||||
resetSpec, err := config.ReadResetSpecFromConfig(cfg)
|
||||
if err != nil {
|
||||
@ -65,6 +69,10 @@ func resetUki(reboot, unattended, resetOem bool, dir ...string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = cfg.CheckForUsers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Load the installation Config from the cloud-config data
|
||||
resetSpec, err := config.ReadUkiResetSpecFromConfig(cfg)
|
||||
if err != nil {
|
||||
|
@ -11,7 +11,6 @@ import (
|
||||
"github.com/kairos-io/kairos-agent/v2/internal/bus"
|
||||
"github.com/kairos-io/kairos-agent/v2/pkg/action"
|
||||
config "github.com/kairos-io/kairos-agent/v2/pkg/config"
|
||||
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
|
||||
"github.com/kairos-io/kairos-agent/v2/pkg/uki"
|
||||
internalutils "github.com/kairos-io/kairos-agent/v2/pkg/utils"
|
||||
events "github.com/kairos-io/kairos-sdk/bus"
|
||||
@ -64,6 +63,7 @@ func ListNewerReleases(includePrereleases bool) ([]string, error) {
|
||||
return tagList.FullImages()
|
||||
}
|
||||
|
||||
// TODO: Check where force and preReleases is being used? They dont seem to be used anywhere?
|
||||
func Upgrade(
|
||||
source string, force, strictValidations bool, dirs []string, upgradeEntry string, preReleases bool) error {
|
||||
bus.Manager.Initialize()
|
||||
@ -71,16 +71,27 @@ func Upgrade(
|
||||
if internalutils.UkiBootMode() == internalutils.UkiHDD {
|
||||
return upgradeUki(source, dirs, upgradeEntry, strictValidations)
|
||||
} else {
|
||||
return upgrade(source, force, strictValidations, dirs, upgradeEntry, preReleases)
|
||||
return upgrade(source, dirs, upgradeEntry, strictValidations)
|
||||
}
|
||||
}
|
||||
|
||||
func upgrade(source string, force, strictValidations bool, dirs []string, upgradeEntry string, preReleases bool) error {
|
||||
upgradeSpec, c, err := generateUpgradeSpec(source, force, strictValidations, dirs, upgradeEntry, preReleases)
|
||||
func upgrade(sourceImageURL string, dirs []string, upgradeEntry string, strictValidations bool) error {
|
||||
c, err := getConfig(sourceImageURL, dirs, upgradeEntry, strictValidations)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
utils.SetEnv(c.Env)
|
||||
|
||||
err = c.CheckForUsers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load the upgrade Config from the system
|
||||
upgradeSpec, err := config.ReadUpgradeSpecFromConfig(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = upgradeSpec.Sanitize()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -96,6 +107,55 @@ func upgrade(source string, force, strictValidations bool, dirs []string, upgrad
|
||||
return hook.Run(*c, upgradeSpec, hook.AfterUpgrade...)
|
||||
}
|
||||
|
||||
func upgradeUki(sourceImageURL string, dirs []string, upgradeEntry string, strictValidations bool) error {
|
||||
c, err := getConfig(sourceImageURL, dirs, upgradeEntry, strictValidations)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
utils.SetEnv(c.Env)
|
||||
|
||||
err = c.CheckForUsers()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Load the upgrade Config from the system
|
||||
upgradeSpec, err := config.ReadUkiUpgradeSpecFromConfig(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = upgradeSpec.Sanitize()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
upgradeAction := uki.NewUpgradeAction(c, upgradeSpec)
|
||||
|
||||
err = upgradeAction.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return hook.Run(*c, upgradeSpec, hook.AfterUpgrade...)
|
||||
}
|
||||
|
||||
func getConfig(sourceImageURL string, dirs []string, upgradeEntry string, strictValidations bool) (*config.Config, error) {
|
||||
cliConf, err := generateUpgradeConfForCLIArgs(sourceImageURL, upgradeEntry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, err := config.Scan(collector.Directories(dirs...),
|
||||
collector.Readers(strings.NewReader(cliConf)),
|
||||
collector.StrictValidation(strictValidations))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c, err
|
||||
|
||||
}
|
||||
|
||||
func allReleases() (versioneer.TagList, error) {
|
||||
artifact, err := versioneer.NewArtifactFromOSRelease()
|
||||
if err != nil {
|
||||
@ -155,30 +215,6 @@ func generateUpgradeConfForCLIArgs(source, upgradeEntry string) (string, error)
|
||||
return string(d), err
|
||||
}
|
||||
|
||||
func generateUpgradeSpec(sourceImageURL string, force, strictValidations bool, dirs []string, upgradeEntry string, preReleases bool) (*v1.UpgradeSpec, *config.Config, error) {
|
||||
cliConf, err := generateUpgradeConfForCLIArgs(sourceImageURL, upgradeEntry)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
c, err := config.Scan(collector.Directories(dirs...),
|
||||
collector.Readers(strings.NewReader(cliConf)),
|
||||
collector.StrictValidation(strictValidations))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
utils.SetEnv(c.Env)
|
||||
|
||||
// Load the upgrade Config from the system
|
||||
upgradeSpec, err := config.ReadUpgradeSpecFromConfig(c)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return upgradeSpec, c, nil
|
||||
}
|
||||
|
||||
func getReleasesFromProvider(includePrereleases bool) ([]string, error) {
|
||||
var result []string
|
||||
bus.Manager.Response(events.EventAvailableReleases, func(p *pluggable.Plugin, r *pluggable.EventResponse) {
|
||||
@ -199,42 +235,6 @@ func getReleasesFromProvider(includePrereleases bool) ([]string, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func upgradeUki(source string, dirs []string, upgradeEntry string, strictValidations bool) error {
|
||||
cliConf, err := generateUpgradeConfForCLIArgs(source, upgradeEntry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c, err := config.Scan(collector.Directories(dirs...),
|
||||
collector.Readers(strings.NewReader(cliConf)),
|
||||
collector.StrictValidation(strictValidations))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
utils.SetEnv(c.Env)
|
||||
|
||||
// Load the upgrade Config from the system
|
||||
upgradeSpec, err := config.ReadUkiUpgradeSpecFromConfig(c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = upgradeSpec.Sanitize()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
upgradeAction := uki.NewUpgradeAction(c, upgradeSpec)
|
||||
|
||||
err = upgradeAction.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return hook.Run(*c, upgradeSpec, hook.AfterUpgrade...)
|
||||
}
|
||||
|
||||
// ExtraConfigUpgrade is the struct that holds the upgrade options that come from flags and events
|
||||
type ExtraConfigUpgrade struct {
|
||||
Upgrade struct {
|
||||
|
@ -189,6 +189,54 @@ func (c Config) LoadInstallState() (*v1.InstallState, error) {
|
||||
return installState, nil
|
||||
}
|
||||
|
||||
func contains(s []string, str string) bool {
|
||||
for _, v := range s {
|
||||
if v == str {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// CheckForUsers will check the config for any users and validate that at least we have 1 admin.
|
||||
// Since Kairos 3.3.x we don't ship a default user with the system, so before a system with no specific users
|
||||
// was relying in our default cloud-configs which created a kairos user ALWAYS (with SUDO!)
|
||||
// But now we don't ship it anymore. So a user upgrading from 3.2.x to 3.3.x that created no users, will end up with a blocked
|
||||
// system.
|
||||
// So we need to see if they are setting a user in their config and if not refuse to continue
|
||||
func (c Config) CheckForUsers() (err error) {
|
||||
// If nousers is enabled we do not check for the validity of the users and such
|
||||
// At this point, the config should be fully parsed and the yip stages ready
|
||||
if !c.Install.NoUsers {
|
||||
anyAdmin := false
|
||||
cc, _ := c.Config.String()
|
||||
yamlConfig, err := yip.Load(cc, vfs.OSFS, nil, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, stage := range yamlConfig.Stages {
|
||||
for _, x := range stage {
|
||||
if len(x.Users) > 0 {
|
||||
for _, user := range x.Users {
|
||||
if contains(user.Groups, "admin") || user.PrimaryGroup == "admin" {
|
||||
anyAdmin = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if !anyAdmin {
|
||||
return fmt.Errorf("No users found in any stage that are part of the 'admin' group.\n" +
|
||||
"In Kairos 3.3.x we no longer ship a default hardcoded user with the system configs and require users to provide their own user." +
|
||||
"Please provide at least 1 user that is part of the 'admin' group(for sudo) with your cloud configs." +
|
||||
"If you still want to continue without creating any users in the system, set 'install.nousers: true' to be in the config in order to allow a system with no users.")
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Sanitize checks the consistency of the struct, returns error
|
||||
// if unsolvable inconsistencies are found
|
||||
func (c *Config) Sanitize() error {
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
pkgConfig "github.com/kairos-io/kairos-agent/v2/pkg/config"
|
||||
"github.com/kairos-io/kairos-agent/v2/pkg/constants"
|
||||
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
|
||||
fsutils "github.com/kairos-io/kairos-agent/v2/pkg/utils/fs"
|
||||
@ -265,4 +266,40 @@ var _ = Describe("Schema", func() {
|
||||
Expect(err).Should(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Validate users in config", func() {
|
||||
It("Validates a existing user in the system", func() {
|
||||
cc := `#cloud-config
|
||||
stages:
|
||||
initramfs:
|
||||
- name: "Set user and password"
|
||||
users:
|
||||
kairos:
|
||||
passwd: "kairos"
|
||||
groups:
|
||||
- "admin"
|
||||
`
|
||||
config, err := pkgConfig.Scan(collector.Readers(strings.NewReader(cc)), collector.NoLogs)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(config.CheckForUsers()).ToNot(HaveOccurred())
|
||||
})
|
||||
It("Fails if there is no user", func() {
|
||||
config, err := pkgConfig.Scan()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(config.CheckForUsers()).To(HaveOccurred())
|
||||
})
|
||||
It("Fails if there is user but its not admin", func() {
|
||||
cc := `#cloud-config
|
||||
stages:
|
||||
initramfs:
|
||||
- name: "Set user and password"
|
||||
users:
|
||||
kairos:
|
||||
passwd: "kairos"
|
||||
`
|
||||
config, err := pkgConfig.Scan(collector.Readers(strings.NewReader(cc)), collector.NoLogs)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(config.CheckForUsers()).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user