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

Make guest username, uid, home directory configurable #2827

Merged
merged 1 commit into from
Nov 2, 2024
Merged
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
3 changes: 3 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ linters-settings:
- name: context-keys-type
- name: deep-exit
- name: dot-imports
arguments:
- allowedPackages:
- github.com/lima-vm/lima/pkg/must
- name: empty-block
- name: error-naming
- name: error-return
Expand Down
21 changes: 8 additions & 13 deletions cmd/limactl/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"strings"

"github.com/coreos/go-semver/semver"
"github.com/lima-vm/lima/pkg/osutil"
"github.com/lima-vm/lima/pkg/sshutil"
"github.com/lima-vm/lima/pkg/store"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -48,11 +47,7 @@ func copyAction(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
u, err := osutil.LimaUser(false)
if err != nil {
return err
}
instDirs := make(map[string]string)
instances := make(map[string]*store.Instance)
scpFlags := []string{}
scpArgs := []string{}
debug, err := cmd.Flags().GetBool("debug")
Expand Down Expand Up @@ -85,28 +80,28 @@ func copyAction(cmd *cobra.Command, args []string) error {
}
if legacySSH {
scpFlags = append(scpFlags, "-P", fmt.Sprintf("%d", inst.SSHLocalPort))
scpArgs = append(scpArgs, fmt.Sprintf("%[email protected]:%s", u.Username, path[1]))
scpArgs = append(scpArgs, fmt.Sprintf("%[email protected]:%s", *inst.Config.User.Name, path[1]))
} else {
scpArgs = append(scpArgs, fmt.Sprintf("scp://%[email protected]:%d/%s", u.Username, inst.SSHLocalPort, path[1]))
scpArgs = append(scpArgs, fmt.Sprintf("scp://%[email protected]:%d/%s", *inst.Config.User.Name, inst.SSHLocalPort, path[1]))
}
instDirs[instName] = inst.Dir
instances[instName] = inst
default:
return fmt.Errorf("path %q contains multiple colons", arg)
}
}
if legacySSH && len(instDirs) > 1 {
if legacySSH && len(instances) > 1 {
return fmt.Errorf("more than one (instance) host is involved in this command, this is only supported for openSSH v8.0 or higher")
}
scpFlags = append(scpFlags, "-3", "--")
scpArgs = append(scpFlags, scpArgs...)

var sshOpts []string
if len(instDirs) == 1 {
if len(instances) == 1 {
// Only one (instance) host is involved; we can use the instance-specific
// arguments such as ControlPath. This is preferred as we can multiplex
// sessions without re-authenticating (MaxSessions permitting).
for _, instDir := range instDirs {
sshOpts, err = sshutil.SSHOpts(instDir, false, false, false, false)
for _, inst := range instances {
sshOpts, err = sshutil.SSHOpts(inst.Dir, *inst.Config.User.Name, false, false, false, false)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/limactl/edit.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func editAction(cmd *cobra.Command, args []string) error {
logrus.Info("Aborting, no changes made to the instance")
return nil
}
y, err := limayaml.Load(yBytes, filePath)
y, err := limayaml.LoadWithWarnings(yBytes, filePath)
if err != nil {
return err
}
Expand Down
1 change: 1 addition & 0 deletions cmd/limactl/shell.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ func shellAction(cmd *cobra.Command, args []string) error {

sshOpts, err := sshutil.SSHOpts(
inst.Dir,
*inst.Config.User.Name,
*inst.Config.SSH.LoadDotSSHPubKeys,
*inst.Config.SSH.ForwardAgent,
*inst.Config.SSH.ForwardX11,
Expand Down
1 change: 1 addition & 0 deletions cmd/limactl/show-ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ func showSSHAction(cmd *cobra.Command, args []string) error {
filepath.Join(inst.Dir, filenames.SSHConfig), inst.Hostname)
opts, err := sshutil.SSHOpts(
inst.Dir,
*inst.Config.User.Name,
*inst.Config.SSH.LoadDotSSHPubKeys,
*inst.Config.SSH.ForwardAgent,
*inst.Config.SSH.ForwardX11,
Expand Down
1 change: 1 addition & 0 deletions cmd/limactl/tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ func tunnelAction(cmd *cobra.Command, args []string) error {

sshOpts, err := sshutil.SSHOpts(
inst.Dir,
*inst.Config.User.Name,
*inst.Config.SSH.LoadDotSSHPubKeys,
*inst.Config.SSH.ForwardAgent,
*inst.Config.SSH.ForwardX11,
Expand Down
7 changes: 7 additions & 0 deletions hack/test-templates.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ declare -A CHECKS=(
["mount-path-with-spaces"]=""
["provision-ansible"]=""
["param-env-variables"]=""
["set-user"]=""
)

case "$NAME" in
Expand All @@ -63,6 +64,7 @@ case "$NAME" in
CHECKS["mount-path-with-spaces"]="1"
CHECKS["provision-ansible"]="1"
CHECKS["param-env-variables"]="1"
CHECKS["set-user"]="1"
;;
"docker")
CONTAINER_ENGINE="docker"
Expand Down Expand Up @@ -172,6 +174,11 @@ if [[ -n ${CHECKS["param-env-variables"]} ]]; then
limactl shell "$NAME" test -e /tmp/param-user
fi

if [[ -n ${CHECKS["set-user"]} ]]; then
INFO 'Testing that user settings can be provided by lima.yaml'
limactl shell "$NAME" grep "^john:x:4711:4711:John Doe:/home/john-john" /etc/passwd
fi

INFO "Testing proxy settings are imported"
got=$(limactl shell "$NAME" env | grep FTP_PROXY)
# Expected: FTP_PROXY is set in addition to ftp_proxy, localhost is replaced
Expand Down
6 changes: 6 additions & 0 deletions hack/test-templates/test-misc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,9 @@ probes:
# $ limactl disk create data --size 10G
additionalDisks:
- "data"

user:
name: john
comment: John Doe
home: "/home/{{.User}}-{{.User}}"
uid: 4711
2 changes: 1 addition & 1 deletion pkg/cidata/cidata.TEMPLATE.d/boot/02-wsl2-setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[ "$LIMA_CIDATA_VMTYPE" = "wsl2" ] || exit 0

# create user
sudo useradd -u "${LIMA_CIDATA_UID}" "${LIMA_CIDATA_USER}" -c "${LIMA_CIDATA_GECOS}" -d "${LIMA_CIDATA_HOME}"
sudo useradd -u "${LIMA_CIDATA_UID}" "${LIMA_CIDATA_USER}" -c "${LIMA_CIDATA_COMMENT}" -d "${LIMA_CIDATA_HOME}"
sudo mkdir "${LIMA_CIDATA_HOME}"/.ssh/
sudo cp "${LIMA_CIDATA_MNT}"/ssh_authorized_keys "${LIMA_CIDATA_HOME}"/.ssh/authorized_keys
sudo chown "${LIMA_CIDATA_USER}" "${LIMA_CIDATA_HOME}"/.ssh/authorized_keys
Expand Down
2 changes: 1 addition & 1 deletion pkg/cidata/cidata.TEMPLATE.d/lima.env
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ LIMA_CIDATA_DEBUG={{ .Debug }}
LIMA_CIDATA_NAME={{ .Name }}
LIMA_CIDATA_USER={{ .User }}
LIMA_CIDATA_UID={{ .UID }}
LIMA_CIDATA_GECOS={{ .GECOS }}
LIMA_CIDATA_COMMENT={{ .Comment }}
LIMA_CIDATA_HOME={{ .Home}}
LIMA_CIDATA_HOSTHOME_MOUNTPOINT={{ .HostHomeMountPoint }}
LIMA_CIDATA_MOUNTS={{ len .Mounts }}
Expand Down
4 changes: 2 additions & 2 deletions pkg/cidata/cidata.TEMPLATE.d/user-data
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ timezone: {{.TimeZone}}
users:
- name: "{{.User}}"
uid: "{{.UID}}"
{{- if .GECOS }}
gecos: {{ printf "%q" .GECOS }}
{{- if .Comment }}
gecos: {{ printf "%q" .Comment }}
{{- end }}
homedir: "{{.Home}}"
shell: /bin/bash
Expand Down
18 changes: 5 additions & 13 deletions pkg/cidata/cidata.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"path"
"path/filepath"
"slices"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -117,23 +116,15 @@ func templateArgs(bootScripts bool, instDir, name string, instConfig *limayaml.L
if err := limayaml.Validate(instConfig, false); err != nil {
return nil, err
}
u, err := osutil.LimaUser(true)
if err != nil {
return nil, err
}
uid, err := strconv.Atoi(u.Uid)
if err != nil {
return nil, err
}
args := TemplateArgs{
Debug: debugutil.Debug,
BootScripts: bootScripts,
Name: name,
Hostname: identifierutil.HostnameFromInstName(name), // TODO: support customization
User: u.Username,
UID: uid,
GECOS: u.Name,
Home: fmt.Sprintf("/home/%s.linux", u.Username),
User: *instConfig.User.Name,
Comment: *instConfig.User.Comment,
Home: *instConfig.User.Home,
UID: *instConfig.User.UID,
GuestInstallPrefix: *instConfig.GuestInstallPrefix,
UpgradePackages: *instConfig.UpgradePackages,
Containerd: Containerd{System: *instConfig.Containerd.System, User: *instConfig.Containerd.User},
Expand All @@ -151,6 +142,7 @@ func templateArgs(bootScripts bool, instDir, name string, instConfig *limayaml.L

firstUsernetIndex := limayaml.FirstUsernetIndex(instConfig)
var subnet net.IP
var err error

if firstUsernetIndex != -1 {
usernetName := instConfig.Networks[firstUsernetIndex].Lima
Expand Down
10 changes: 4 additions & 6 deletions pkg/cidata/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"github.com/lima-vm/lima/pkg/iso9660util"

"github.com/containerd/containerd/identifiers"
"github.com/lima-vm/lima/pkg/osutil"
"github.com/lima-vm/lima/pkg/textutil"
)

Expand Down Expand Up @@ -59,9 +58,9 @@ type TemplateArgs struct {
Hostname string // instance hostname
IID string // instance id
User string // user name
GECOS string // user information
Comment string // user information
Home string // home directory
UID int
UID uint32
SSHPubKeys []string
Mounts []Mount
MountType string
Expand Down Expand Up @@ -97,9 +96,8 @@ func ValidateTemplateArgs(args *TemplateArgs) error {
if err := identifiers.Validate(args.Name); err != nil {
return err
}
if !osutil.ValidateUsername(args.User) {
return errors.New("field User must be valid linux username")
}
// args.User is intentionally not validated here; the user can override with any name they want
// limayaml.FillDefault will validate the default (local) username, but not an explicit setting
if args.User == "root" {
return errors.New("field User must not be \"root\"")
}
Expand Down
20 changes: 10 additions & 10 deletions pkg/cidata/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ var defaultRemoveDefaults = false

func TestConfig(t *testing.T) {
args := &TemplateArgs{
Name: "default",
User: "foo",
UID: 501,
GECOS: "Foo",
Home: "/home/foo.linux",
Name: "default",
User: "foo",
UID: 501,
Comment: "Foo",
Home: "/home/foo.linux",
SSHPubKeys: []string{
"ssh-rsa dummy [email protected]",
},
Expand All @@ -30,11 +30,11 @@ func TestConfig(t *testing.T) {

func TestConfigCACerts(t *testing.T) {
args := &TemplateArgs{
Name: "default",
User: "foo",
UID: 501,
GECOS: "Foo",
Home: "/home/foo.linux",
Name: "default",
User: "foo",
UID: 501,
jandubois marked this conversation as resolved.
Show resolved Hide resolved
Comment: "Foo",
Home: "/home/foo.linux",
SSHPubKeys: []string{
"ssh-rsa dummy [email protected]",
},
Expand Down
5 changes: 3 additions & 2 deletions pkg/hostagent/hostagent.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ func New(instName string, stdout io.Writer, signalCh chan os.Signal, opts ...Opt

sshOpts, err := sshutil.SSHOpts(
inst.Dir,
*inst.Config.User.Name,
*inst.Config.SSH.LoadDotSSHPubKeys,
*inst.Config.SSH.ForwardAgent,
*inst.Config.SSH.ForwardX11,
Expand Down Expand Up @@ -182,13 +183,13 @@ func New(instName string, stdout io.Writer, signalCh chan os.Signal, opts ...Opt
// Block ports 22 and sshLocalPort on all IPs
for _, port := range []int{sshGuestPort, sshLocalPort} {
rule := limayaml.PortForward{GuestIP: net.IPv4zero, GuestPort: port, Ignore: true}
limayaml.FillPortForwardDefaults(&rule, inst.Dir, inst.Param)
limayaml.FillPortForwardDefaults(&rule, inst.Dir, inst.Config.User, inst.Param)
rules = append(rules, rule)
}
rules = append(rules, inst.Config.PortForwards...)
// Default forwards for all non-privileged ports from "127.0.0.1" and "::1"
rule := limayaml.PortForward{}
limayaml.FillPortForwardDefaults(&rule, inst.Dir, inst.Param)
limayaml.FillPortForwardDefaults(&rule, inst.Dir, inst.Config.User, inst.Param)
rules = append(rules, rule)

limaDriver := driverutil.CreateTargetDriverInstance(&driver.BaseDriver{
Expand Down
2 changes: 1 addition & 1 deletion pkg/instance/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func Create(ctx context.Context, instName string, instConfig []byte, saveBrokenY
}
// limayaml.Load() needs to pass the store file path to limayaml.FillDefault() to calculate default MAC addresses
filePath := filepath.Join(instDir, filenames.LimaYAML)
loadedInstConfig, err := limayaml.Load(instConfig, filePath)
loadedInstConfig, err := limayaml.LoadWithWarnings(instConfig, filePath)
if err != nil {
return nil, err
}
Expand Down
Loading
Loading