mirror of
https://github.com/kairos-io/kairos-agent.git
synced 2025-07-04 11:56:18 +00:00
Allow installing with no users (#574)
This commit is contained in:
parent
e8bb8cf4ff
commit
a3aadbbaa9
@ -29,7 +29,9 @@ import (
|
|||||||
"github.com/kairos-io/kairos-sdk/utils"
|
"github.com/kairos-io/kairos-sdk/utils"
|
||||||
qr "github.com/mudler/go-nodepair/qrcode"
|
qr "github.com/mudler/go-nodepair/qrcode"
|
||||||
"github.com/mudler/go-pluggable"
|
"github.com/mudler/go-pluggable"
|
||||||
|
yip "github.com/mudler/yip/pkg/schema"
|
||||||
"github.com/pterm/pterm"
|
"github.com/pterm/pterm"
|
||||||
|
"github.com/twpayne/go-vfs/v4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func displayInfo(agentConfig *Config) {
|
func displayInfo(agentConfig *Config) {
|
||||||
@ -215,6 +217,29 @@ func RunInstall(c *config.Config) error {
|
|||||||
utils.SetEnv(c.Env)
|
utils.SetEnv(c.Env)
|
||||||
utils.SetEnv(c.Install.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.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 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
|
// 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
|
||||||
// From the installed system
|
// From the installed system
|
||||||
if internalutils.IsUkiWithFs(c.Fs) {
|
if internalutils.IsUkiWithFs(c.Fs) {
|
||||||
|
@ -152,12 +152,20 @@ func InteractiveInstall(debug, spawnShell bool, sourceImgURL string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
userName, err := prompt("User to setup", "kairos", canBeEmpty, true, false)
|
createUser, err := prompt("Do you want to create any users? If not, system will not be accesible via terminal or ssh", "y", yesNo, true, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
userPassword, err := prompt("Password", "", canBeEmpty, true, true)
|
var userName, userPassword, sshKeys, makeAdmin string
|
||||||
|
|
||||||
|
if isYes(createUser) {
|
||||||
|
userName, err = prompt("User to setup", "kairos", canBeEmpty, true, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
userPassword, err = prompt("Password", "", canBeEmpty, true, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -166,17 +174,23 @@ func InteractiveInstall(debug, spawnShell bool, sourceImgURL string) error {
|
|||||||
userPassword = "!"
|
userPassword = "!"
|
||||||
}
|
}
|
||||||
|
|
||||||
users, err := prompt("SSH access (rsakey, github/gitlab supported, comma-separated)", "github:someuser,github:someuser2", canBeEmpty, true, false)
|
sshKeys, err = prompt("SSH access (rsakey, github/gitlab supported, comma-separated)", "github:someuser,github:someuser2", canBeEmpty, true, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
makeAdmin, err = prompt("Make the user an admin (with sudo permissions)?", "y", yesNo, true, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup the users if we selected the default values as they are not valid users
|
// Cleanup the users if we selected the default values as they are not valid users
|
||||||
if users == "github:someuser,github:someuser2" {
|
if sshKeys == "github:someuser,github:someuser2" {
|
||||||
users = ""
|
sshKeys = ""
|
||||||
|
}
|
||||||
|
if sshKeys != "" {
|
||||||
|
sshUsers = strings.Split(sshKeys, ",")
|
||||||
}
|
}
|
||||||
if users != "" {
|
|
||||||
sshUsers = strings.Split(users, ",")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prompt the user by prompts defined by the provider
|
// Prompt the user by prompts defined by the provider
|
||||||
@ -216,41 +230,49 @@ func InteractiveInstall(debug, spawnShell bool, sourceImgURL string) error {
|
|||||||
return InteractiveInstall(debug, spawnShell, sourceImgURL)
|
return InteractiveInstall(debug, spawnShell, sourceImgURL)
|
||||||
}
|
}
|
||||||
|
|
||||||
usersToSet := map[string]schema.User{}
|
|
||||||
|
|
||||||
stage := config.NetworkStage.String()
|
|
||||||
|
|
||||||
if userName != "" {
|
|
||||||
user := schema.User{
|
|
||||||
Name: userName,
|
|
||||||
PasswordHash: userPassword,
|
|
||||||
Groups: []string{"admin"},
|
|
||||||
SSHAuthorizedKeys: sshUsers,
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we got no ssh keys, we don't need network, do the user as soon as possible
|
|
||||||
if len(sshUsers) == 0 {
|
|
||||||
stage = config.InitramfsStage.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
usersToSet = map[string]schema.User{
|
|
||||||
userName: user,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cloudConfig := schema.YipConfig{Name: "Config generated by the installer",
|
|
||||||
Stages: map[string][]schema.Stage{stage: {
|
|
||||||
{
|
|
||||||
Users: usersToSet,
|
|
||||||
},
|
|
||||||
}}}
|
|
||||||
|
|
||||||
// This is temporal to generate a valid cc file, no need to properly initialize everything
|
// This is temporal to generate a valid cc file, no need to properly initialize everything
|
||||||
cc := &config.Config{
|
cc := &config.Config{
|
||||||
Install: &config.Install{
|
Install: &config.Install{
|
||||||
Device: device,
|
Device: device,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var cloudConfig schema.YipConfig
|
||||||
|
|
||||||
|
// Only add the user stage if we have any users
|
||||||
|
if userName != "" {
|
||||||
|
var isAdmin []string
|
||||||
|
|
||||||
|
if isYes(makeAdmin) {
|
||||||
|
isAdmin = append(isAdmin, "admin")
|
||||||
|
}
|
||||||
|
|
||||||
|
user := schema.User{
|
||||||
|
Name: userName,
|
||||||
|
PasswordHash: userPassword,
|
||||||
|
Groups: isAdmin,
|
||||||
|
SSHAuthorizedKeys: sshUsers,
|
||||||
|
}
|
||||||
|
|
||||||
|
stage := config.NetworkStage.String()
|
||||||
|
// If we got no ssh keys, we don't need network, do the user as soon as possible
|
||||||
|
if len(sshUsers) == 0 {
|
||||||
|
stage = config.InitramfsStage.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
cloudConfig = schema.YipConfig{Name: "Config generated by the installer",
|
||||||
|
Stages: map[string][]schema.Stage{stage: {
|
||||||
|
{
|
||||||
|
Users: map[string]schema.User{
|
||||||
|
userName: user,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}}
|
||||||
|
} else {
|
||||||
|
// If no users, we need to set this option to skip the user validation and confirm that we want a system with no users.
|
||||||
|
cc.Install.NoUsers = true
|
||||||
|
}
|
||||||
|
|
||||||
// Merge all yamls into one
|
// Merge all yamls into one
|
||||||
dat, err := config.MergeYAML(cloudConfig, cc, result)
|
dat, err := config.MergeYAML(cloudConfig, cc, result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -55,6 +55,7 @@ type Install struct {
|
|||||||
ExtraPartitions sdkTypes.PartitionList `yaml:"extra-partitions,omitempty" mapstructure:"extra-partitions"`
|
ExtraPartitions sdkTypes.PartitionList `yaml:"extra-partitions,omitempty" mapstructure:"extra-partitions"`
|
||||||
ExtraDirsRootfs []string `yaml:"extra-dirs-rootfs,omitempty" mapstructure:"extra-dirs-rootfs"`
|
ExtraDirsRootfs []string `yaml:"extra-dirs-rootfs,omitempty" mapstructure:"extra-dirs-rootfs"`
|
||||||
Force bool `yaml:"force,omitempty" mapstructure:"force"`
|
Force bool `yaml:"force,omitempty" mapstructure:"force"`
|
||||||
|
NoUsers bool `yaml:"nousers,omitempty" mapstructure:"nousers"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfig(opts ...GenericOptions) *Config {
|
func NewConfig(opts ...GenericOptions) *Config {
|
||||||
|
@ -93,7 +93,7 @@ func structFieldsContainedInOtherStruct(left, right interface{}) {
|
|||||||
leftFieldName := leftTypes.Field(i).Name
|
leftFieldName := leftTypes.Field(i).Name
|
||||||
if leftTypes.Field(i).IsExported() {
|
if leftTypes.Field(i).IsExported() {
|
||||||
It(fmt.Sprintf("Checks that the new schema contians the field %s", leftFieldName), func() {
|
It(fmt.Sprintf("Checks that the new schema contians the field %s", leftFieldName), func() {
|
||||||
if leftFieldName == "Source" {
|
if leftFieldName == "Source" || leftFieldName == "NoUsers" {
|
||||||
Skip("Schema not updated yet")
|
Skip("Schema not updated yet")
|
||||||
}
|
}
|
||||||
Expect(
|
Expect(
|
||||||
|
Loading…
Reference in New Issue
Block a user