From 6b434dd55f228cfb1c62c1df4b9412294040364a Mon Sep 17 00:00:00 2001 From: Robert Fairley Date: Wed, 6 Mar 2019 13:29:19 -0500 Subject: [PATCH 1/3] internal/*: drop merging authorized_keys.d into authorized_keys This drops the authorized_keys_d code, having Ignition directly write SSH keys to a keyfile. By default, Ignition writes the fragment to .ssh/authorized_keys.d/ignition. Setting the flag distro.writeAuthorizedKeysFragment to "false" (through the environment variable IGNITION_WRITE_AUTHORIZED_KEYS_FRAGMENT) causes Ignition to write the SSH keys to .ssh/authorized_keys. Distributions that do not read fragments from .ssh/authorized_keys.d can then instead read from .ssh/authorized_keys. Fixes: #716 --- doc/configuration-v3_0-experimental.md | 2 +- .../{authorized_keys_d => }/as_user/as_user.c | 0 .../as_user/as_user.go | 0 .../{authorized_keys_d => }/as_user/as_user.h | 0 .../authorized_keys_d/authorized_keys_d.go | 369 ------------------ internal/distro/distro.go | 8 + internal/exec/util/passwd.go | 40 +- 7 files changed, 36 insertions(+), 383 deletions(-) rename internal/{authorized_keys_d => }/as_user/as_user.c (100%) rename internal/{authorized_keys_d => }/as_user/as_user.go (100%) rename internal/{authorized_keys_d => }/as_user/as_user.h (100%) delete mode 100644 internal/authorized_keys_d/authorized_keys_d.go diff --git a/doc/configuration-v3_0-experimental.md b/doc/configuration-v3_0-experimental.md index 7f35ab041..199b699b0 100644 --- a/doc/configuration-v3_0-experimental.md +++ b/doc/configuration-v3_0-experimental.md @@ -104,7 +104,7 @@ The Ignition configuration is a JSON document conforming to the following specif * **_users_** (list of objects): the list of accounts that shall exist. * **name** (string): the username for the account. * **_passwordHash_** (string): the encrypted password for the account. - * **_sshAuthorizedKeys_** (list of strings): a list of SSH keys to be added to the user's authorized_keys. + * **_sshAuthorizedKeys_** (list of strings): a list of SSH keys to be added as an SSH key fragment at `.ssh/authorized_keys.d/ignition` in the user's home directory. * **_uid_** (integer): the user ID of the account. * **_gecos_** (string): the GECOS field of the account. * **_homeDir_** (string): the home directory of the account. diff --git a/internal/authorized_keys_d/as_user/as_user.c b/internal/as_user/as_user.c similarity index 100% rename from internal/authorized_keys_d/as_user/as_user.c rename to internal/as_user/as_user.c diff --git a/internal/authorized_keys_d/as_user/as_user.go b/internal/as_user/as_user.go similarity index 100% rename from internal/authorized_keys_d/as_user/as_user.go rename to internal/as_user/as_user.go diff --git a/internal/authorized_keys_d/as_user/as_user.h b/internal/as_user/as_user.h similarity index 100% rename from internal/authorized_keys_d/as_user/as_user.h rename to internal/as_user/as_user.h diff --git a/internal/authorized_keys_d/authorized_keys_d.go b/internal/authorized_keys_d/authorized_keys_d.go deleted file mode 100644 index a5e3e7f2b..000000000 --- a/internal/authorized_keys_d/authorized_keys_d.go +++ /dev/null @@ -1,369 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// +build linux - -// authorized_keys_d manages a user's ~/.ssh/authorized_keys.d and can produce -// a ~/.ssh/authorized_keys file from the authorized_keys.d contents. -package authorized_keys_d - -import ( - "fmt" - "io/ioutil" - "os" - "os/user" - "path/filepath" - "sort" - "strings" - "syscall" - - "github.com/coreos/ignition/internal/authorized_keys_d/as_user" -) - -const ( - AuthorizedKeysFile = "authorized_keys" - AuthorizedKeysDir = "authorized_keys.d" - PreservedKeysName = "orig_authorized_keys" - SSHDir = ".ssh" - - lockFile = ".authorized_keys.d.lock" // In "~/". - stageFile = ".authorized_keys.d.stage_file" // In "~/.ssh". - stageDir = ".authorized_keys.d.stage_dir" // In "~/.ssh". -) - -// SSHAuthorizedKeysDir represents an opened user's authorized_keys.d. -type SSHAuthorizedKeysDir struct { - path string // Path to authorized_keys.d directory. - user *user.User // User of the directory. - lock *os.File // Lock file for serializing Open()-Close(). -} - -// SSHAuthorizedKey represents an opened user's authorized_keys.d/ entry. -type SSHAuthorizedKey struct { - Name string // Name given to the key. - Disabled bool // Disabled state of the key. - Path string // Path to the file backing the key. - origin *SSHAuthorizedKeysDir // Originating dir for this key. -} - -// sshDirPath returns the path to the .ssh dir for the user. -func sshDirPath(u *user.User) string { - return filepath.Join(u.HomeDir, SSHDir) -} - -// authKeysFilePath returns the path to the authorized_keys file for the user. -func authKeysFilePath(u *user.User) string { - return filepath.Join(sshDirPath(u), AuthorizedKeysFile) -} - -// authKeysDirPath returns the path to the authorized_keys.d for the user. -func authKeysDirPath(u *user.User) string { - return filepath.Join(sshDirPath(u), AuthorizedKeysDir) -} - -// lockFilePath returns the path to the lock file for the user. -func lockFilePath(u *user.User) string { - return filepath.Join(u.HomeDir, lockFile) -} - -// stageDirPath returns the path to the staging directory for the user. -func stageDirPath(u *user.User) string { - return filepath.Join(sshDirPath(u), stageDir) -} - -// stageFilePath returns the path to the staging file for the user. -func stageFilePath(u *user.User) string { - return filepath.Join(sshDirPath(u), stageFile) -} - -// opendir opens the authorized keys directory. -func opendir(dir string) (*SSHAuthorizedKeysDir, error) { - fi, err := os.Stat(dir) - if err != nil { - return nil, err - } - if !fi.IsDir() { - return nil, fmt.Errorf("%q is not a directory", dir) - } - return &SSHAuthorizedKeysDir{path: dir}, nil -} - -// acquireLock locks the lock file for the given user's authorized_keys.d. -// A lock file is created if it doesn't already exist. -// The locking is currently a simple coarse-grained mutex held for the -// Open()-Close() duration, implemented using a lock file in the user's ~/. -func acquireLock(u *user.User) (*os.File, error) { - f, err := as_user.OpenFile(u, lockFilePath(u), - syscall.O_CREAT|syscall.O_RDONLY, 0600) - if err != nil { - return nil, err - } - if err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX); err != nil { - f.Close() - return nil, err - } - return f, nil -} - -// createAuthorizedKeysDir creates an authorized keys directory for the user. -// If the user has an authorized_keys file, it is migrated. -func createAuthorizedKeysDir(u *user.User) (*SSHAuthorizedKeysDir, error) { - td := stageDirPath(u) - if err := as_user.MkdirAll(u, td, 0700); err != nil { - return nil, err - } - defer os.RemoveAll(td) - - akd, err := opendir(td) - if err != nil { - return nil, err - } - akd.user = u - - akfb, err := ioutil.ReadFile(authKeysFilePath(u)) - if err != nil && !os.IsNotExist(err) { - return nil, err - } else if err == nil { - err = akd.Add(PreservedKeysName, akfb, false, false) - if err != nil { - return nil, err - } - } - if err = akd.rename(authKeysDirPath(u)); err != nil { - return nil, err - } - return akd, err -} - -// Open opens the authorized keys directory for the supplied user. -// If create is false, Open will fail if no directory exists yet. -// If create is true, Open will create the directory if it doesn't exist, -// preserving the authorized_keys file in the process. -// After a successful open, Close should be called when finished to unlock -// the directory. -func Open(usr *user.User, create bool) (*SSHAuthorizedKeysDir, error) { - l, err := acquireLock(usr) - if err != nil { - return nil, err - } - defer func() { - if err != nil { - l.Close() - } - }() - - akd, err := opendir(authKeysDirPath(usr)) - if err != nil && (!create || !os.IsNotExist(err)) { - return nil, err - } else if os.IsNotExist(err) { - akd, err = createAuthorizedKeysDir(usr) - if err != nil { - return nil, err - } - } - - akd.lock = l - akd.user = usr - return akd, nil -} - -// Close closes the authorized keys directory. -func (akd *SSHAuthorizedKeysDir) Close() error { - return akd.lock.Close() -} - -// rename renames the authorized_keys dir to the supplied path. -func (akd *SSHAuthorizedKeysDir) rename(to string) error { - err := as_user.Rename(akd.user, akd.path, to) - if err != nil { - return err - } - akd.path = to - return nil -} - -// keyPath returns the path to the named key. -func (akd *SSHAuthorizedKeysDir) keyPath(n string) string { - return filepath.Join(akd.path, n) -} - -// WalkKeys iterates across all keys in akd, calling f for each key. -// Iterating stops on error, and the error is propagated out. -func (akd *SSHAuthorizedKeysDir) WalkKeys(f func(*SSHAuthorizedKey) error) error { - d, err := os.Open(akd.path) - if err != nil { - return err - } - - names, err := d.Readdirnames(0) - if err != nil { - return err - } - - sort.Strings(names) - for _, n := range names { - ak, err := akd.Open(n) - if err != nil { - return err - } - if err := f(ak); err != nil { - return err - } - } - - return nil -} - -// Open opens the key at name. -func (akd *SSHAuthorizedKeysDir) Open(name string) (*SSHAuthorizedKey, error) { - p := akd.keyPath(name) - fi, err := os.Stat(p) - if err != nil { - return nil, err - } - ak := &SSHAuthorizedKey{ - Name: name, - Disabled: (fi.Size() == 0), - Path: p, - origin: akd, - } - return ak, nil -} - -// Remove removes the key at name. -func (akd *SSHAuthorizedKeysDir) Remove(name string) error { - ak, err := akd.Open(name) - if err != nil { - return err - } - return ak.Remove() -} - -// Disable disables the key at name. -func (akd *SSHAuthorizedKeysDir) Disable(name string) error { - ak, err := akd.Open(name) - if err != nil { - return err - } - return ak.Disable() -} - -// Add adds the supplied key at name. -// replace enables replacing keys already existing at name. -// force enables adding keys to a disabled name, enabling it in the process. -// Names starting wtih ".", and anything containing "/" are disallowed. -func (akd *SSHAuthorizedKeysDir) Add(name string, keys []byte, replace, force bool) error { - if strings.HasPrefix(name, ".") || strings.Contains(name, "/") { - return fmt.Errorf(`illegal name`) - } - - p := akd.keyPath(name) - fi, err := os.Stat(p) - if err == nil { - if fi.Size() > 0 && !replace { - return fmt.Errorf("key %q already exists", name) - } else if fi.Size() == 0 && !force { - return fmt.Errorf("key %q disabled", name) - } - } else if !os.IsNotExist(err) { - return err - } - ak := &SSHAuthorizedKey{Path: p, origin: akd} - return ak.Replace(keys) -} - -// KeysFilePath returns the backing authorized_keys file path for this -// SSHAuthorizedKeysDir. This is the file written to by Sync(). -func (akd *SSHAuthorizedKeysDir) KeysFilePath() string { - return authKeysFilePath(akd.user) -} - -// KeysDirPath returns the authorized_keys.d directory path for this -// SSHAuthorizedKeysDir. This is the directory containing the discrete key -// files. -func (akd *SSHAuthorizedKeysDir) KeysDirPath() string { - return authKeysDirPath(akd.user) -} - -// Sync synchronizes the user's ~/.ssh/authorized_keys file with the -// current authorized_keys.d directory state. -func (akd *SSHAuthorizedKeysDir) Sync() error { - sp := stageFilePath(akd.user) - sf, err := as_user.OpenFile(akd.user, sp, - syscall.O_CREAT|syscall.O_TRUNC|syscall.O_WRONLY, 0600) - if err != nil { - return err - } - defer func() { - if err != nil { - sf.Close() - os.Remove(sp) - } - }() - - if err := akd.WalkKeys(func(k *SSHAuthorizedKey) error { - if !k.Disabled { - kb, err := ioutil.ReadFile(k.Path) - if err != nil { - return err - } - kb = append(kb, '\n') - if _, err := sf.Write(kb); err != nil { - return err - } - } - return nil - }); err != nil { - return err - } - - if err := sf.Close(); err != nil { - return err - } - - err = as_user.Rename(akd.user, sp, authKeysFilePath(akd.user)) - if err != nil { - return err - } - - return nil -} - -// Remove removes the opened key. -func (ak *SSHAuthorizedKey) Remove() error { - return os.Remove(ak.Path) -} - -// Disable disables the opened key. -func (ak *SSHAuthorizedKey) Disable() error { - return os.Truncate(ak.Path, 0) -} - -// Replace replaces the opened key with the supplied data. -func (ak *SSHAuthorizedKey) Replace(keys []byte) error { - sp := stageFilePath(ak.origin.user) - sf, err := as_user.OpenFile(ak.origin.user, sp, - syscall.O_WRONLY|syscall.O_CREAT|syscall.O_TRUNC, 0600) - if err != nil { - return err - } - defer os.Remove(sp) - if _, err = sf.Write(keys); err != nil { - return err - } - if err := sf.Close(); err != nil { - return err - } - return as_user.Rename(ak.origin.user, sp, ak.Path) -} diff --git a/internal/distro/distro.go b/internal/distro/distro.go index 99f3b0e84..2f5eb5b56 100644 --- a/internal/distro/distro.go +++ b/internal/distro/distro.go @@ -57,6 +57,11 @@ var ( // Flags selinuxRelabel = "false" blackboxTesting = "false" + // writeAuthorizedKeysFragment indicates whether to write SSH keys + // specified in the Ignition config as a fragment to + // ".ssh/authorized_keys.d/ignition" ("true"), or to + // ".ssh/authorized_keys" ("false"). + writeAuthorizedKeysFragment = "true" ) func DiskByIDDir() string { return diskByIDDir } @@ -85,6 +90,9 @@ func XfsMkfsCmd() string { return xfsMkfsCmd } func SelinuxRelabel() bool { return bakedStringToBool(selinuxRelabel) } func BlackboxTesting() bool { return bakedStringToBool(blackboxTesting) } +func WriteAuthorizedKeysFragment() bool { + return bakedStringToBool(fromEnv("WRITE_AUTHORIZED_KEYS_FRAGMENT", writeAuthorizedKeysFragment)) +} func fromEnv(nameSuffix, defaultValue string) string { value := os.Getenv("IGNITION_" + nameSuffix) diff --git a/internal/exec/util/passwd.go b/internal/exec/util/passwd.go index 6837e4448..b5e7a44e2 100644 --- a/internal/exec/util/passwd.go +++ b/internal/exec/util/passwd.go @@ -17,11 +17,13 @@ package util import ( "fmt" "os/exec" + "os/user" + "path/filepath" "strconv" "strings" "syscall" - keys "github.com/coreos/ignition/internal/authorized_keys_d" + "github.com/coreos/ignition/internal/as_user" "github.com/coreos/ignition/internal/config/types" "github.com/coreos/ignition/internal/distro" "github.com/coreos/ignition/internal/log" @@ -160,6 +162,26 @@ func translateV2_1PasswdUserGroupSliceToStringSlice(groups []types.Group) []stri return newGroups } +// writeAuthKeysFile writes the content in keys to the path fp for the user, +// creating any directories in fp as needed. +func writeAuthKeysFile(u *user.User, fp string, keys []byte) error { + if err := as_user.MkdirAll(u, filepath.Dir(fp), 0700); err != nil { + return err + } + + f, err := as_user.OpenFile(u, fp, syscall.O_WRONLY|syscall.O_CREAT|syscall.O_TRUNC, 0600) + if err != nil { + return err + } + if _, err = f.Write(keys); err != nil { + return err + } + if err := f.Close(); err != nil { + return err + } + return nil +} + // AuthorizeSSHKeys adds the provided SSH public keys to the user's authorized keys. func (u Util) AuthorizeSSHKeys(c types.PasswdUser) error { if len(c.SSHAuthorizedKeys) == 0 { @@ -172,12 +194,6 @@ func (u Util) AuthorizeSSHKeys(c types.PasswdUser) error { return fmt.Errorf("unable to lookup user %q", c.Name) } - akd, err := keys.Open(usr, true) - if err != nil { - return err - } - defer akd.Close() - // TODO(vc): introduce key names to config? // TODO(vc): validate c.SSHAuthorizedKeys well-formedness. ks := strings.Join(translateV2_1SSHAuthorizedKeySliceToStringSlice(c.SSHAuthorizedKeys), "\n") @@ -189,12 +205,10 @@ func (u Util) AuthorizeSSHKeys(c types.PasswdUser) error { ks = ks + "\n" } - if err := akd.Add("ignition", []byte(ks), true, true); err != nil { - return err - } - - if err := akd.Sync(); err != nil { - return err + if distro.WriteAuthorizedKeysFragment() { + writeAuthKeysFile(usr, filepath.Join(usr.HomeDir, ".ssh", "authorized_keys.d", "ignition"), []byte(ks)) + } else { + writeAuthKeysFile(usr, filepath.Join(usr.HomeDir, ".ssh", "authorized_keys"), []byte(ks)) } return nil From f167d039f09772510e75f3f511ad3d4fc6758dc7 Mon Sep 17 00:00:00 2001 From: Robert Fairley Date: Tue, 5 Mar 2019 11:10:54 -0500 Subject: [PATCH 2/3] tests/*: expose env vars through Test.Env This allows individual tests to configure environment variables by setting the Env member of types.Test. The TestIgnitionBlackBox and TestIgnitionBlackBoxNegative functions may then append additional environment variables during setup that apply to all tests. Preparatory commit for adding a blackbox test in #751. --- tests/blackbox_test.go | 5 ++--- tests/types/types.go | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/blackbox_test.go b/tests/blackbox_test.go index 937c15fe4..6926497c7 100644 --- a/tests/blackbox_test.go +++ b/tests/blackbox_test.go @@ -272,9 +272,8 @@ func outer(t *testing.T, test types.Test, negativeTests bool) error { } // Ignition - appendEnv := []string{ - "IGNITION_SYSTEM_CONFIG_DIR=" + systemConfigDir, - } + appendEnv := test.Env + appendEnv = append(appendEnv, "IGNITION_SYSTEM_CONFIG_DIR="+systemConfigDir) if !negativeTests { if err := runIgnition(t, ctx, "disks", "", tmpDirectory, appendEnv); err != nil { diff --git a/tests/types/types.go b/tests/types/types.go index 9d9bfacb2..4dafaae5a 100644 --- a/tests/types/types.go +++ b/tests/types/types.go @@ -96,6 +96,7 @@ type Test struct { Out []Disk // Expected disk state after running Ignition MntDevices []MntDevice SystemDirFiles []File + Env []string // Environment variables for Ignition Config string ConfigMinVersion string ConfigVersion string From f89ffe2adc04a8f7ca2c7f8dcc3b89f734e2e4f1 Mon Sep 17 00:00:00 2001 From: Robert Fairley Date: Wed, 6 Mar 2019 13:33:12 -0500 Subject: [PATCH 3/3] tests/positive/passwd: add test UseAuthorizedKeysFile Add a blackbox test to verify that ~/.ssh/authorized_keys is written when IGNITION_WRITE_AUTHORIZED_KEYS_FRAGMENT=false. Also updates the existing test AddPasswdUsers to use the environment variable IGNITION_WRITE_AUTHORIZED_KEYS_FRAGMENT=true so that the test will pass consistently if the binary is built with a non-default writeAuthorizedKeysFragment flag. --- tests/positive/passwd/users.go | 81 +++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/tests/positive/passwd/users.go b/tests/positive/passwd/users.go index 0b4abf07a..22d2362fa 100644 --- a/tests/positive/passwd/users.go +++ b/tests/positive/passwd/users.go @@ -21,12 +21,14 @@ import ( func init() { register.Register(register.PositiveTest, AddPasswdUsers()) + register.Register(register.PositiveTest, UseAuthorizedKeysFile()) } func AddPasswdUsers() types.Test { name := "Adding users" in := types.GetBaseDisk() out := types.GetBaseDisk() + env := []string{"IGNITION_WRITE_AUTHORIZED_KEYS_FRAGMENT=true"} config := `{ "ignition": { "version": "$version" @@ -140,6 +142,82 @@ ENCRYPT_METHOD SHA512 }, Contents: "root:*::root\nusers:*::\nsudo:*::\nwheel:*::root,core\nsudo:*::\ndocker:*::core\nsystemd-coredump:!!::\nfleet:!!::core\nrkt-admin:!!::\nrkt:!!::core\ncore:*::\ntest:!::\njenkins:!::\n", }, + { + Node: types.Node{ + Name: "ignition", + Directory: "home/test/.ssh/authorized_keys.d", + User: 1000, + Group: 1000, + }, + Contents: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBRZPFJNOvQRfokigTtl0IBi71LHZrFOk4EJ3Zowtk/bX5uIVai0Cd4+hqlocYL10idgtFBH28skeKfsmHwgS9XwOvP+g+kqAl7yCz8JEzIUzl1fxNZDToi0jA3B5MwXkpt+IWfnabwi2cRZhlzrz9rO+eExu5s3NfaRmmmCYrjCJIRPKSCrW8U0n9fVSbX4PDdMXVmH7r+t8MtR8523vCbakFR/Y0YIqkPVdfuUXHh9rDCdH4B7mt7nYX2LWQXGUvmI13mgQoy04ifkaR3ImuOMp3Y1J1gm6clO74IMCq/sK9+XJhbxMPPHUoUJ2EwbaG7Dbh3iqz47e9oVki4gIH stephenlowrie@localhost.localdomain\n", + }, + }) + + return types.Test{ + Name: name, + In: in, + Out: out, + Env: env, + Config: config, + ConfigMinVersion: configMinVersion, + } +} + +// UseAuthorizedKeysFile verifies that ~/.ssh/authorized_keys is written +// when IGNITION_WRITE_AUTHORIZED_KEYS_FRAGMENT=false. +func UseAuthorizedKeysFile() types.Test { + name := "Use authorized_keys file" + in := types.GetBaseDisk() + out := types.GetBaseDisk() + env := []string{"IGNITION_WRITE_AUTHORIZED_KEYS_FRAGMENT=false"} + config := `{ + "ignition": { + "version": "$version" + }, + "passwd": { + "users": [{ + "name": "test", + "create": {}, + "passwordHash": "zJW/EKqqIk44o", + "sshAuthorizedKeys": [ + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBRZPFJNOvQRfokigTtl0IBi71LHZrFOk4EJ3Zowtk/bX5uIVai0Cd4+hqlocYL10idgtFBH28skeKfsmHwgS9XwOvP+g+kqAl7yCz8JEzIUzl1fxNZDToi0jA3B5MwXkpt+IWfnabwi2cRZhlzrz9rO+eExu5s3NfaRmmmCYrjCJIRPKSCrW8U0n9fVSbX4PDdMXVmH7r+t8MtR8523vCbakFR/Y0YIqkPVdfuUXHh9rDCdH4B7mt7nYX2LWQXGUvmI13mgQoy04ifkaR3ImuOMp3Y1J1gm6clO74IMCq/sK9+XJhbxMPPHUoUJ2EwbaG7Dbh3iqz47e9oVki4gIH stephenlowrie@localhost.localdomain" + ] + } + ] + } + }` + configMinVersion := "3.0.0-experimental" + in[0].Partitions.AddFiles("ROOT", []types.File{ + { + Node: types.Node{ + Name: "passwd", + Directory: "etc", + }, + Contents: "root:x:0:0:root:/root:/bin/bash\ncore:x:500:500:CoreOS Admin:/home/core:/bin/bash\nsystemd-coredump:x:998:998:systemd Core Dumper:/:/sbin/nologin\nfleet:x:253:253::/:/sbin/nologin\n", + }, + { + Node: types.Node{ + Name: "shadow", + Directory: "etc", + }, + Contents: "root:*:15887:0:::::\ncore:*:15887:0:::::\nsystemd-coredump:!!:17301::::::\nfleet:!!:17301::::::\n", + }, + { + Node: types.Node{ + Name: "group", + Directory: "etc", + }, + Contents: "root:x:0:root\nwheel:x:10:root,core\nsudo:x:150:\ndocker:x:233:core\nsystemd-coredump:x:998:\nfleet:x:253:core\ncore:x:500:\nrkt-admin:x:999:\nrkt:x:251:core\n", + }, + { + Node: types.Node{ + Name: "gshadow", + Directory: "etc", + }, + Contents: "root:*::root\nusers:*::\nsudo:*::\nwheel:*::root,core\nsudo:*::\ndocker:*::core\nsystemd-coredump:!!::\nfleet:!!::core\nrkt-admin:!!::\nrkt:!!::core\ncore:*::\n", + }, + }) + out[0].Partitions.AddFiles("ROOT", []types.File{ { Node: types.Node{ Name: "authorized_keys", @@ -147,7 +225,7 @@ ENCRYPT_METHOD SHA512 User: 1000, Group: 1000, }, - Contents: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBRZPFJNOvQRfokigTtl0IBi71LHZrFOk4EJ3Zowtk/bX5uIVai0Cd4+hqlocYL10idgtFBH28skeKfsmHwgS9XwOvP+g+kqAl7yCz8JEzIUzl1fxNZDToi0jA3B5MwXkpt+IWfnabwi2cRZhlzrz9rO+eExu5s3NfaRmmmCYrjCJIRPKSCrW8U0n9fVSbX4PDdMXVmH7r+t8MtR8523vCbakFR/Y0YIqkPVdfuUXHh9rDCdH4B7mt7nYX2LWQXGUvmI13mgQoy04ifkaR3ImuOMp3Y1J1gm6clO74IMCq/sK9+XJhbxMPPHUoUJ2EwbaG7Dbh3iqz47e9oVki4gIH stephenlowrie@localhost.localdomain\n\n", + Contents: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBRZPFJNOvQRfokigTtl0IBi71LHZrFOk4EJ3Zowtk/bX5uIVai0Cd4+hqlocYL10idgtFBH28skeKfsmHwgS9XwOvP+g+kqAl7yCz8JEzIUzl1fxNZDToi0jA3B5MwXkpt+IWfnabwi2cRZhlzrz9rO+eExu5s3NfaRmmmCYrjCJIRPKSCrW8U0n9fVSbX4PDdMXVmH7r+t8MtR8523vCbakFR/Y0YIqkPVdfuUXHh9rDCdH4B7mt7nYX2LWQXGUvmI13mgQoy04ifkaR3ImuOMp3Y1J1gm6clO74IMCq/sK9+XJhbxMPPHUoUJ2EwbaG7Dbh3iqz47e9oVki4gIH stephenlowrie@localhost.localdomain\n", }, }) @@ -155,6 +233,7 @@ ENCRYPT_METHOD SHA512 Name: name, In: in, Out: out, + Env: env, Config: config, ConfigMinVersion: configMinVersion, }