Skip to content

Commit

Permalink
Make guest username, uid, home directory configurable
Browse files Browse the repository at this point in the history
Also disallows the "admin" username by default (because it is a builtin
user in Ubuntu), but can be overridden by setting it explicitly in lima.yaml.

Signed-off-by: Jan Dubois <[email protected]>
  • Loading branch information
jandubois committed Nov 1, 2024
1 parent 9fe06aa commit 1e8aaeb
Show file tree
Hide file tree
Showing 27 changed files with 336 additions and 194 deletions.
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
12 changes: 4 additions & 8 deletions pkg/cidata/cidata.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,7 @@ 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)
uid, err := strconv.Atoi(*instConfig.User.UID)
if err != nil {
return nil, err
}
Expand All @@ -130,10 +126,10 @@ func templateArgs(bootScripts bool, instDir, name string, instConfig *limayaml.L
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),
Comment: *instConfig.User.Comment,
User: *instConfig.User.Name,
Home: *instConfig.User.Home,
GuestInstallPrefix: *instConfig.GuestInstallPrefix,
UpgradePackages: *instConfig.UpgradePackages,
Containerd: Containerd{System: *instConfig.Containerd.System, User: *instConfig.Containerd.User},
Expand Down
8 changes: 3 additions & 5 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,7 +58,7 @@ 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
SSHPubKeys []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,
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

0 comments on commit 1e8aaeb

Please sign in to comment.