[lxc-devel] [PATCH v2 3/3] Improve setting the default password in a new container

TAMUKI Shoichi tamuki at linet.gr.jp
Wed Oct 29 10:29:24 UTC 2014


Hello,

From: "Michael H. Warfield" <mhw at WittsEnd.com>
Subject: Re: [lxc-devel] [PATCH v2 3/3] Improve setting the default password in a new container
Date: Sat, 11 Oct 2014 13:24:10 -0400

> > > > The default password in a new container is now auto-generated using
> > > > phoneme rules and (good) random numbers.
> > > > 
> > > > Even if the default random password is set in a distribution-specific
> > > > template and you use the download template to pull a pre-built rootfs
> > > > image, you will get the same password every time unless the pre-built
> > > > rootfs image is updated.
> > > > 
> > > > So, the default random password in a new container is to be set after
> > > > container creation.  The user names whose passwords to be changed are
> > > > stored in *.chpasswd file which is located at /usr/share/lxc/config.
> > > > Each line of the file specifies a user name whose password is to be
> > > > changed.  If the target *.chpasswd file does not exist, no password is
> > > > changed in a new container.
> > > 
> > > This is obviously a festering problem and one that has already been
> > > addressed in the Fedora and CentOS templates in a different manner and
> > > additional patches have been submitted and under discussion.  Did you
> > > even bother to read the code in the Fedora and CentOS templates?
> > 
> > At first, I intended to use the code in the Fedora/CentOS templates,
> > but I became aware that the method was available only when using the
> > template with '-t' option to lxc-create.  It can not be used by non-
> > priv users.
> 
> That would then be handled by the download template and, iirc, it was
> Stephane's intention to have those containers start with "locked"
> accounts and require lxc-attach or something similar to set up.

I spent time to look into the Fedora template about the password
treatment.  In summary, the Fedora template works as following specs:

----------------------------------------------------------------------
root_password=[password]
If the "root_password" is non-blank, use it, else set a default.
This can be passed to the script as an environment variable and is
set by a shell conditional assignment.  Looks weird but it is what it is.

If the root password contains a ding ($) then try to expand it.
That will pick up things like ${name} and ${RANDOM}.
If the root password contains more than 3 consecutive X's, pass it as
a template to mktemp and take the result.

These are conditional assignments.  They can be overridden from the
pre-existing environment variables.

root_display_password=[yes|no]
If root_display_password=yes, display the temporary root password at
exit.  Default to no.

root_store_password=[yes|no]
If root_store_password=yes, store it in the configuration directory.
Default to yes.

root_prompt_password=[yes|no]
If root_prompt_password=yes, invoke "passwd" to force the user to change
the root password after the container is created.  Default to no.

root_expire_password=[yes|no]
If root_expire_password=yes, you will be prompted to change the root
password at the first login.  Default to yes.
----------------------------------------------------------------------

Currently, it seems not to take multi-user accounts into account, and
it does not support with locked accounts.  So, I tried rewriting the
code:

----------------------------------------------------------------------
#!/bin/bash
	:
	:
	:
# Some combinations of the tuning knobs below do not exactly make sense.
# but that's ok.
#
# If the "LXC_PASSWORD" is non-blank, use it, else set a default.
# This can be passed to the script as an environment variable and is
# set by a shell conditional assignment.  Looks weird but it is what it is.
#
# If the password is "PROMPT", invoke "passwd" to force the user to
# change the password after the container is created.
# Prompting for something interactive has potential for mayhem with
# users running under the API...  Don't default to "PROMPT".
# If the password contains one of 3 common "disabled" conventions, or
# a hash indicator for a real password hash, assume in encrypted form.
# If the password contains a ding ($) then try to expand it.
# That will pick up things like ${name} and ${RANDOM}.
# If the password contains more than 3 consecutive X's, pass it as
# a template to mktemp and take the result.
#
# If LXC_DISPLAY_PASSWORD = yes, display the temporary password at exit.
# If LXC_STORE_PASSWORD = yes, store it in the configuration directory
# If LXC_EXPIRE_PASSWORD = yes, you will be prompted to change the
# password at the first login.
#
# These are conditional assignments...  They can be overridden from the
# pre-existing environment variables...
#
# Make sure this is in single quotes to defer expansion to later!
#: ${LXC_PASSWORD='${uname[$i]^}-$name-$RANDOM'}
: ${LXC_PASSWORD='${uname[$i]^}-$name-XXXXXX'}
# Now, it doesn't make much sense to display, store, and force change
# together.  But, we gotta test, right???
: ${LXC_DISPLAY_PASSWORD=no}
: ${LXC_STORE_PASSWORD=yes}
# Expire password?  Default to yes, but can be overridden from
# the environment variable
: ${LXC_EXPIRE_PASSWORD=yes}
	:
	:
	:
configure_foo() {
	:
	:
	:
  # Let's do something better for the initial password.
  # It's not perfect but it will defeat common scanning brute force
  # attacks in the case where ssh is exposed.  It may also be set to
  # expired, forcing the user to change it at first login.
  for i in `seq 0 $((${#uname[@]} - 1))` ; do
    if [ "${passwd[$i]}" == "PROMPT" ] ; then
      cat <<- EOF | unexpand
	Invoking the passwd command in the container to set the ${uname[$i]} password...
	
	        chroot $rootfs passwd ${uname[$i]}
	
	EOF
      chroot $rootfs passwd ${uname[$i]}
    elif [ -n "$(expr "${passwd[$i]}" : '\(\!\{1,2\}\|\$\|*\)$')" ] ; then
      # Matches one of 3 common "disabled" conventions so treat them
      # as if they were "hashed" passwords to be copied literally
      # by chpasswd.  Result is an account with no valid password.
      echo "Disabling ${uname[$i]} password..."
      echo "${uname[$i]}:${passwd[$i]}" | chroot $rootfs chpasswd -e
    elif [ -n "$(expr "${passwd[$i]}" : '$\([1256]\|2a\)\$')" ] ; then
      # Matches on a hash indicator for a real password hash.
      echo -n "Setting ${uname[$i]} password hash to "
      [ "$LXC_DISPLAY_PASSWORD" == "yes" ] \
          && echo "'${passwd[$i]}'..." || echo "'********'..."
      echo "${uname[$i]}:${passwd[$i]}" | chroot $rootfs chpasswd -e
    else
      # Anything else, assume a clear text password template...
      echo -n "Setting ${uname[$i]} password to "
      [ "$LXC_DISPLAY_PASSWORD" == "yes" ] \
          && echo "'${passwd[$i]}'..." || echo "'********'..."
      echo "${uname[$i]}:${passwd[$i]}" | chroot $rootfs chpasswd
      [ "$LXC_DISPLAY_PASSWORD" == "yes" ] || continue
      cat <<- EOF
	The temporary password for ${uname[$i]} is: '${passwd[$i]}'
	
	You may want to note that password down before starting the container.
	
	EOF
    fi
  done
  if [ "$LXC_EXPIRE_PASSWORD" == "yes" ] ; then
    # Also set this password as expired to force the user to change it!
    for i in `seq 0 $((${#uname[@]} - 1))` ; do
      chroot $rootfs passwd -e ${uname[$i]}
    done
    cat <<- "EOF"
	The password is set up as "expired" and will require it to be changed
	at first login, which you should do as soon as possible.  If you lose the
	password or wish to change it without starting the container, you
	can change it from the host by running the following command (which will
	also reset the expired flag):
	
	EOF
    for i in `seq 0 $((${#uname[@]} - 1))` ; do
      echo $'\t'"chroot $rootfs passwd ${uname[$i]}"
    done
    echo
  fi
	:
	:
	:
}

copy_configuration() {
	:
	:
	:
  for i in `seq 0 $((${#uname[@]} - 1))` ; do
    echo "${uname[$i]}" >> $path/chpasswd
  done
  if [ "$LXC_STORE_PASSWORD" == "yes" ] ; then
    touch $path/tmp_pass
    chmod 600 $path/tmp_pass
    # Append the user and password to the file.
    # We might be ultimately adding more than one.
    for i in `seq 0 $((${#uname[@]} - 1))` ; do
      echo "Storing $(uname[$i]} password in '$path/tmp_pass'..."
      echo "${uname[$i]}:${passwd[i]}" >> $path/tmp_pass
    done
    cat <<- EOF | unexpand
	The temporary password is stored in:
	
	        '$path/tmp_pass'
	
	EOF
  fi
	:
	:
	:
}
	:
	:
	:
uname=root
#uname=(root alice bob $user)
for i in `seq 0 $((${#uname[@]} - 1))` ; do
  if [ -z "$LXC_PASSWORD" ] ; then
    passwd[$i]=${uname[$i]^}-$name-$RANDOM
  else
    if [ -n "$(expr "$LXC_PASSWORD" : '\(\!\{1,2\}\|\$\|*\)$')"
        -o -n "$(expr "$LXC_PASSWORD" : '$\([1256]\|2a\)\$')" ] ; then
      # If the password contains one of 3 common "disabled" conven-
      # tions, or a hash indicator for a real password hash, leave it
      # unchanged.
      passwd[$i]="$LXC_PASSWORD"
    else
      # In case it's got a ding in it, try and expand it!
      # In theory, this could also access and external program.
      passwd[$i]=`eval echo "$LXC_PASSWORD"`
    fi
    # If it has more than 3 consequtive X's in it, feed it
    # through mktemp as a template.
    if [ `expr "${passwd[$i]}" : '.*XXX$'` -ne 0 ] ; then
      passwd[$i]=`mktemp -u ${passwd[$i]}`
    fi
  fi
done
	:
	:
	:
----------------------------------------------------------------------

However, when using download template, the accounts should be locked
eventually.  Also, I think the password treatment should have the same
behavior between using the template with '-t' option to lxc-create and
using download template.  So, I think that creating passwords should
be done after the template runs.  That means, most of the processing
about creating passwords in the template will be moved to the point
after the template runs in lxc-create command:

----------------------------------------------------------------------
#!/bin/bash
	:
	:
	:
configure_foo() {
	:
	:
	:
  # set password
  for i in `seq 0 $((${#uname[@]} - 1))` ; do
    echo "Setting ${uname[$i]} password to '${uname[$i]}'..."
    echo "${uname[$i]}:${uname[$i]}" | chroot $rootfs chpasswd
    echo "Disabling ${uname[$i]} password..."
    chroot $rootfs passwd -l ${uname[$i]}
  done
	:
	:
	:
}

copy_configuration() {
	:
	:
	:
  for i in `seq 0 $((${#uname[@]} - 1))` ; do
    echo "${uname[$i]}" >> $path/chpasswd
  done
	:
	:
	:
}
	:
	:
	:
uname=root
#uname=(root alice bob $user)
	:
	:
	:
----------------------------------------------------------------------

Regards,
TAMUKI Shoichi


More information about the lxc-devel mailing list