Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

c/snap-bootstrap: import users from hybrid system into recovery system #14521

Draft
wants to merge 13 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 76 additions & 16 deletions cmd/snap-bootstrap/cmd_initramfs_mounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -465,12 +465,12 @@ func copyNetworkConfig(src, dst string) error {
// have been what was broken so we don't want to break
// network configuration for recover mode as well, but for
// now this is fine
"system-data/etc/netplan/*",
"etc/netplan/*",
// etc/machine-id is part of what systemd-networkd uses to generate a
// DHCP clientid (the other part being the interface name), so to have
// the same IP addresses across run mode and recover mode, we need to
// also copy the machine-id across
"system-data/etc/machine-id",
"etc/machine-id",
} {
if err := copyFromGlobHelper(src, dst, globEx); err != nil {
return err
Expand All @@ -490,7 +490,7 @@ func copyUbuntuDataMisc(src, dst string) error {
// mode currently, unclear how/when we could do this, but recover mode
// isn't meant to be long lasting and as such it's probably not a big
// problem to "lose" the time spent in recover mode
"system-data/var/lib/systemd/timesync/clock",
"var/lib/systemd/timesync/clock",
} {
if err := copyFromGlobHelper(src, dst, globEx); err != nil {
return err
Expand All @@ -507,28 +507,68 @@ func copyUbuntuDataMisc(src, dst string) error {
//
// to the target directory. This is used to copy the authentication
// data from a real uc20 ubuntu-data partition into a ephemeral one.
func copyUbuntuDataAuth(src, dst string) error {
func copyUbuntuDataAuth(srcUbuntuData, destUbuntuData string) error {
for _, globEx := range []string{
"system-data/var/lib/extrausers/*",
"system-data/etc/ssh/*",
// so that users have proper perms, i.e. console-conf added users are
// sudoers
"system-data/etc/sudoers.d/*",
"user-data/*/.ssh/*",
// this ensures we get proper authentication to snapd from "snap"
// commands in recover mode
"user-data/*/.snap/auth.json",
// this ensures we also get non-ssh enabled accounts copied
"user-data/*/.profile",
// so that users have proper perms, i.e. console-conf added users are
// sudoers
"system-data/etc/sudoers.d/*",
} {
if err := copyFromGlobHelper(src, dst, globEx); err != nil {
if err := copyFromGlobHelper(srcUbuntuData, destUbuntuData, globEx); err != nil {
return err
}
}

// ensure the user state is transferred as well
srcState := filepath.Join(srcUbuntuData, "system-data/var/lib/snapd/state.json")
dstState := filepath.Join(destUbuntuData, "system-data/var/lib/snapd/state.json")
err := state.CopyState(srcState, dstState, []string{"auth.users", "auth.macaroon-key", "auth.last-id"})
if err != nil && !errors.Is(err, state.ErrNoState) {
return fmt.Errorf("cannot copy user state: %v", err)
}

return nil
}

func copyHybridUbuntuDataAuth(srcUbuntuData, destUbuntuData string) error {
for _, globEx := range []string{
"etc/ssh/*",
"etc/sudoers.d/*",
"root/.ssh/*",
} {
if err := copyFromGlobHelper(
srcUbuntuData,
filepath.Join(destUbuntuData, "system-data"),
globEx,
); err != nil {
return err
}
}

for _, globEx := range []string{
"*/.ssh/*",
"*/.snap/auth.json",
"*/.profile",
} {
if err := copyFromGlobHelper(
filepath.Join(srcUbuntuData, "home"),
filepath.Join(destUbuntuData, "user-data"),
globEx,
); err != nil {
return err
}
}

// ensure the user state is transferred as well
srcState := filepath.Join(src, "system-data/var/lib/snapd/state.json")
dstState := filepath.Join(dst, "system-data/var/lib/snapd/state.json")
srcState := filepath.Join(srcUbuntuData, "var/lib/snapd/state.json")
dstState := filepath.Join(destUbuntuData, "system-data/var/lib/snapd/state.json")
err := state.CopyState(srcState, dstState, []string{"auth.users", "auth.macaroon-key", "auth.last-id"})
if err != nil && !errors.Is(err, state.ErrNoState) {
return fmt.Errorf("cannot copy user state: %v", err)
Expand Down Expand Up @@ -1415,16 +1455,36 @@ func generateMountsModeRecover(mst *initramfsMountsState) error {
// ubuntu-data, and as such we can proceed with copying files from there
// onto the tmpfs
// Proceed only if we trust ubuntu-data to be paired with ubuntu-save
hybrid := model.Classic() && model.KernelSnap() != nil

if machine.trustData() {
// TODO: erroring here should fallback to copySafeDefaultData and
// proceed on with degraded mode anyways
if err := copyUbuntuDataAuth(boot.InitramfsHostUbuntuDataDir, boot.InitramfsDataDir); err != nil {
return err
hostSystemData := boot.InitramfsHostWritableDir(model)
recoverySystemData := boot.InitramfsWritableDir(model, false)
if hybrid {
if err := importHybridUserData(
hostSystemData,
filepath.Join(boot.InitramfsRunMntDir, "base"),
); err != nil {
return err
}

if err := copyHybridUbuntuDataAuth(boot.InitramfsHostUbuntuDataDir, boot.InitramfsDataDir); err != nil {
return err
}
} else {
// TODO: erroring here should fallback to copySafeDefaultData and
// proceed on with degraded mode anyways
if err := copyUbuntuDataAuth(
boot.InitramfsHostUbuntuDataDir,
boot.InitramfsDataDir,
); err != nil {
return err
}
}
if err := copyNetworkConfig(boot.InitramfsHostUbuntuDataDir, boot.InitramfsDataDir); err != nil {
if err := copyNetworkConfig(hostSystemData, recoverySystemData); err != nil {
return err
}
if err := copyUbuntuDataMisc(boot.InitramfsHostUbuntuDataDir, boot.InitramfsDataDir); err != nil {
if err := copyUbuntuDataMisc(hostSystemData, recoverySystemData); err != nil {
return err
}
} else {
Expand Down
Loading
Loading