[lxc-devel] [PATCH] CentOS and Fedora Templates: Harden root passwords and add static MAC network addresses.

Marian Marinov mm at yuhu.biz
Mon Jan 13 16:13:00 UTC 2014


On 12/27/2013 01:08 AM, Michael H. Warfield wrote:
> CentOS and Fedora Templates: Harden root passwords and add static MAC network addresses.
>
> 1) Add logic to root password setting.  Root password is now set to
> 	"Root-${name}-${RANDOM} to defeat common brute force scans.
> 2) Enhance exit messages to explain root password and password changing.
> 3) Add random generated hwaddr (MAC) entires for any network interfaces
> 	in default config copied to container.

I'm using both the fedora and centos templates and having a static MAC is a problem.

I have replaced static MAC with $(gen_mac) shell function.

function gen_mac() {
         mac_vars=(0 1 2 3 4 5 6 7 8 9 a b c d e f)
         mac_base='52:53:54:'
         ret=''
         for i in {1..6}; do
                 n=$RANDOM
                 let 'n %= 16'
                 ret="${ret}${mac_vars[$n]}"
                 if [ $i -eq 2 ] || [ $i -eq 4 ]; then
                         ret="${ret}:"
                 fi
         done
         echo "${mac_base}${ret}"
}

There is also another variant of this function:
function gen_mac() {
  	echo "52:53:54:$(openssl rand -hex 3| sed 's/\(..\)/\1:/g; s/.$//')"
}


> 4) Add shell variable expansion of default config.
> 5) Cross patch templates to bring them more into coherence with each other.
>
> Signed-off-by: Michael H. Warfield <mhw at WittsEnd.com>
> ---
>   templates/lxc-centos.in | 114 +++++++++++++++++++++++++++++++++++++-----------
>   templates/lxc-fedora.in |  99 +++++++++++++++++++++++++++++++++++++----
>   2 files changed, 180 insertions(+), 33 deletions(-)
>
> diff --git a/templates/lxc-centos.in b/templates/lxc-centos.in
> index 7d47715..5bb5349 100644
> --- a/templates/lxc-centos.in
> +++ b/templates/lxc-centos.in
> @@ -33,6 +33,7 @@ default_path=@LXCPATH@
>   # We really need something better here!
>   root_password=root
>
> +# These are only going into comments in the resulting config...
>   lxc_network_type=veth
>   lxc_network_link=lxcbr0
>
> @@ -256,8 +257,10 @@ EOF
>       mknod -m 600 ${dev_path}/initctl p
>       mknod -m 666 ${dev_path}/ptmx c 5 2
>
> -    echo "setting root passwd to $root_password"
> +    echo "Setting root passwd to '$root_password'"
>       echo "root:$root_password" | chroot $rootfs_path chpasswd
> +    # Also set this password as expired to force the user to change it!
> +    chroot $rootfs_path passwd -e root
>
>       # This will need to be enhanced for CentOS 7 when systemd
>       # comes into play...   /\/\|=mhw=|\/\/
> @@ -374,6 +377,7 @@ copy_centos()
>       # i prefer rsync (no reason really)
>       mkdir -p $rootfs_path
>       rsync -a $cache/rootfs/ $rootfs_path/
> +    echo
>       return 0
>   }
>
> @@ -428,28 +432,71 @@ install_centos()
>       return $?
>   }
>
> -copy_configuration()
> +create_hwaddr()
>   {
> +    echo $(dd if=/dev/urandom bs=8 count=1 2>/dev/null | md5sum
> +        | sed -e 's/\(..\)\(..\)\(..\)\(..\)\(..\).*/fe:\1:\2:\3:\4:\5/')
> +}
>
> +copy_configuration()
> +{
>       mkdir -p $config_path
> +
> +    grep -q "^lxc.rootfs" $config_path/config 2>/dev/null || echo "
> +lxc.rootfs = $rootfs_path
> +" >> $config_path/config
> +
> +    # The following code is to create static MAC addresses for each
> +    # interface in the container.  This code will work for multiple
> +    # interfaces in the default config.
> +    mv $config_path/config $config_path/config.def
> +    while read LINE
> +    do
> +        # This should catch variable expansions from the default config...
> +        if expr "${LINE}" : '.*\$' > /dev/null 2>&1
> +        then
> +		LINE=$(eval "echo \"${LINE}\"")
> +        fi
> +
> +        # There is a tab and a space in the regex bracket below!
> +        # Seems that \s doesn't work in brackets.
> +        KEY=$(expr $LINE : '\s*\([^	 ]*\)\s*=')
> +
> +        if [[ "${KEY}" != "lxc.network.hwaddr" ]]
> +        then
> +            echo ${LINE} >> $config_path/config
> +
> +            if [[ "${KEY}" == "lxc.network.link" ]]
> +            then
> +                echo "lxc.network.hwaddr = $(create_hwaddr)" >> $config_path/config
> +            fi
> +        fi
> +    done < $config_path/config.def
> +
> +    rm -f $config_path/config.def
> +
>       cat <<EOF >> $config_path/config
>   lxc.utsname = $utsname
>   lxc.tty = 4
>   lxc.pts = 1024
> -lxc.rootfs = $rootfs_path
> -lxc.mount  = $config_path/fstab
> +lxc.mount = $config_path/fstab
>   lxc.cap.drop = sys_module mac_admin mac_override sys_time
>
>   lxc.autodev = $auto_dev
>
> +# When using LXC with apparmor, uncomment the next line to run unconfined:
> +#lxc.aa_profile = unconfined
> +
>   # example simple networking setup, uncomment to enable
>   #lxc.network.type = $lxc_network_type
>   #lxc.network.flags = up
>   #lxc.network.link = $lxc_network_link
>   #lxc.network.name = eth0
> -# additional example for veth network type, static MAC address,
> -# and persistent veth device name on host side
> +# Additional example for veth network type
> +#    static MAC address,
>   #lxc.network.hwaddr = 00:16:3e:77:52:20
> +#    persistent veth device name on host side
> +#        Note: This may potentially collide with other containers of same name!
>   #lxc.network.veth.pair = v-$name-e0
>
>   #cgroups
> @@ -460,8 +507,6 @@ lxc.cgroup.devices.allow = c 1:5 rwm
>   # consoles
>   lxc.cgroup.devices.allow = c 5:1 rwm
>   lxc.cgroup.devices.allow = c 5:0 rwm
> -lxc.cgroup.devices.allow = c 4:0 rwm
> -lxc.cgroup.devices.allow = c 4:1 rwm
>   # /dev/{,u}random
>   lxc.cgroup.devices.allow = c 1:9 rwm
>   lxc.cgroup.devices.allow = c 1:8 rwm
> @@ -473,13 +518,12 @@ EOF
>
>       cat <<EOF > $config_path/fstab
>   proc            proc         proc    nodev,noexec,nosuid 0 0
> -devpts          dev/pts      devpts defaults 0 0
>   sysfs           sys          sysfs defaults  0 0
>   EOF
>
>       if [ $? -ne 0 ]; then
> -    echo "Failed to add configuration"
> -    return 1
> +        echo "Failed to add configuration"
> +        return 1
>       fi
>
>       return 0
> @@ -489,22 +533,21 @@ clean()
>   {
>
>       if [ ! -e $cache ]; then
> -    exit 0
> +        exit 0
>       fi
>
>       # lock, so we won't purge while someone is creating a repository
>       (
> -    flock -x 200
> -    if [ $? != 0 ]; then
> -        echo "Cache repository is busy."
> -        exit 1
> -    fi
> -
> -    echo -n "Purging the download cache for centos-$release..."
> -    rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1
> -    exit 0
> +        flock -x 200
> +        if [ $? != 0 ]; then
> +            echo "Cache repository is busy."
> +            exit 1
> +        fi
>
> -    ) 200>/var/lock/subsys/lxc-centos
> +        echo -n "Purging the download cache for centos-$release..."
> +        rm --preserve-root --one-file-system -rf $cache && echo "Done." || exit 1
> +        exit 0
> +    ) 200>@LOCALSTATEDIR@/lock/subsys/lxc-centos
>   }
>
>   usage()
> @@ -554,6 +597,12 @@ if [ ! -z "$clean" -a -z "$path" ]; then
>       exit 0
>   fi
>
> +# Let's do something better for the initial root password.
> +# It's not perfect but it will defeat common scanning brute force
> +# attacks in the case where ssh is exposed.  It will also be set to
> +# expired, forcing the user to change it at first login.
> +root_password=Root-${name}-${RANDOM}
> +
>   if [ -z "${utsname}" ]; then
>       utsname=${name}
>   fi
> @@ -572,7 +621,7 @@ fi
>   #    utsname and hostname = Container_Name.Domain_Name
>
>   if [ $(expr "$utsname" : '.*\..*\.') = 0 ]; then
> -    if [ -n "$(dnsdomainname)" ]; then
> +    if [[ "$(dnsdomainname)" != "" && "$(dnsdomainname)" != "localdomain" ]]; then
>           utsname=${utsname}.$(dnsdomainname)
>       fi
>   fi
> @@ -666,5 +715,20 @@ if [ ! -z $clean ]; then
>       clean || exit 1
>       exit 0
>   fi
> -echo "container rootfs and config created, default root password is '$root_password'"
> -echo "edit the config file to check/enable networking setup"
> +echo "
> +Container rootfs and config have been created.
> +Edit the config file to check/enable networking setup.
> +"
> +
> +echo "The temporary password for root is: '$root_password'
> +
> +You may want to note that password down before starting the container.
> +
> +The password set up as "expired" and will require it to be changed it at
> +first login, which you should do as soon as possible.  If you lose the
> +root 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):
> +
> +        chroot ${rootfs_path} passwd
> +"
> diff --git a/templates/lxc-fedora.in b/templates/lxc-fedora.in
> index 5f66ff1..b0c214a 100644
> --- a/templates/lxc-fedora.in
> +++ b/templates/lxc-fedora.in
> @@ -33,6 +33,10 @@ default_path=@LXCPATH@
>   # We really need something better here!
>   root_password=root
>
> +# These are only going into comments in the resulting config...
> +lxc_network_type=veth
> +lxc_network_link=lxcbr0
> +
>   # is this fedora?
>   # Alow for weird remixes like the Raspberry Pi
>   #
> @@ -194,8 +198,10 @@ EOF
>       mknod -m 600 ${dev_path}/initctl p
>       mknod -m 666 ${dev_path}/ptmx c 5 2
>
> -    echo "setting root passwd to $root_password"
> +    echo "Setting root passwd to '$root_password'"
>       echo "root:$root_password" | chroot $rootfs_path chpasswd
> +    # Also set this password as expired to force the user to change it!
> +    chroot $rootfs_path passwd -e root
>
>       # specifying this in the initial packages doesn't always work.
>       # Even though it should have...
> @@ -243,7 +249,7 @@ configure_fedora_init()
>
>   configure_fedora_systemd()
>   {
> -    unlink ${rootfs_path}/etc/systemd/system/default.target
> +    rm -f ${rootfs_path}/etc/systemd/system/default.target
>       touch ${rootfs_path}/etc/fstab
>       chroot ${rootfs_path} ln -s /dev/null /etc/systemd/system/udev.service
>       chroot ${rootfs_path} ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target
> @@ -837,6 +843,7 @@ copy_fedora()
>       # i prefer rsync (no reason really)
>       mkdir -p $rootfs_path
>       rsync -Ha $cache/rootfs/ $rootfs_path/
> +    echo
>       return 0
>   }
>
> @@ -891,11 +898,53 @@ install_fedora()
>       return $?
>   }
>
> -copy_configuration()
> +# Generate a random hardware (MAC) address composed of FE followed by
> +# 5 random bytes...
> +create_hwaddr()
>   {
> +    echo $(dd if=/dev/urandom bs=8 count=1 2>/dev/null | md5sum |
> +        sed -e 's/\(..\)\(..\)\(..\)\(..\)\(..\).*/fe:\1:\2:\3:\4:\5/')
> +}
>
> +copy_configuration()
> +{
>       mkdir -p $config_path
> -    grep -q "^lxc.rootfs" $config_path/config 2>/dev/null || echo "lxc.rootfs = $rootfs_path" >> $config_path/config
> +
> +    grep -q "^lxc.rootfs" $config_path/config 2>/dev/null || echo "
> +lxc.rootfs = $rootfs_path
> +" >> $config_path/config
> +
> +    # The following code is to create static MAC addresses for each
> +    # interface in the container.  This code will work for multiple
> +    # interfaces in the default config.  It will also strip any
> +    # hwaddr stanzas out of the default config since we can not share
> +    # MAC addresses between containers.
> +    mv $config_path/config $config_path/config.def
> +    while read LINE
> +    do
> +        # This should catch variable expansions from the default config...
> +        if expr "${LINE}" : '.*\$' > /dev/null 2>&1
> +        then
> +		LINE=$(eval "echo \"${LINE}\"")
> +        fi
> +
> +        # There is a tab and a space in the regex bracket below!
> +        # Seems that \s doesn't work in brackets.
> +        KEY=$(expr "${LINE}" : '\s*\([^	 ]*\)\s*=')
> +
> +        if [[ "${KEY}" != "lxc.network.hwaddr" ]]
> +        then
> +            echo "${LINE}" >> $config_path/config
> +
> +            if [[ "${KEY}" == "lxc.network.link" ]]
> +            then
> +                echo "lxc.network.hwaddr = $(create_hwaddr)" >> $config_path/config
> +            fi
> +        fi
> +    done < $config_path/config.def
> +
> +    rm -f $config_path/config.def
> +
>       cat <<EOF >> $config_path/config
>   lxc.utsname = $utsname
>   lxc.tty = 4
> @@ -908,6 +957,18 @@ lxc.autodev = $auto_dev
>   # When using LXC with apparmor, uncomment the next line to run unconfined:
>   #lxc.aa_profile = unconfined
>
> +# example simple networking setup, uncomment to enable
> +#lxc.network.type = $lxc_network_type
> +#lxc.network.flags = up
> +#lxc.network.link = $lxc_network_link
> +#lxc.network.name = eth0
> +# Additional example for veth network type
> +#    static MAC address,
> +#lxc.network.hwaddr = 00:16:3e:77:52:20
> +#    persistent veth device name on host side
> +#        Note: This may potentially collide with other containers of same name!
> +#lxc.network.veth.pair = v-$name-e0
> +
>   #cgroups
>   lxc.cgroup.devices.deny = a
>   # /dev/null and zero
> @@ -929,6 +990,7 @@ EOF
>   proc            proc         proc    nodev,noexec,nosuid 0 0
>   sysfs           sys          sysfs defaults  0 0
>   EOF
> +
>       if [ $? -ne 0 ]; then
>           echo "Failed to add configuration"
>           return 1
> @@ -1006,6 +1068,12 @@ if [ ! -z "$clean" -a -z "$path" ]; then
>       exit 0
>   fi
>
> +# Let's do something better for the initial root password.
> +# It's not perfect but it will defeat common scanning brute force
> +# attacks in the case where ssh is exposed.  It will also be set to
> +# expired, forcing the user to change it at first login.
> +root_password=Root-${name}-${RANDOM}
> +
>   if [ -z "${utsname}" ]; then
>       utsname=${name}
>   fi
> @@ -1024,7 +1092,7 @@ fi
>   #    utsname and hostname = Container_Name.Domain_Name
>
>   if [ $(expr "$utsname" : '.*\..*\.') = 0 ]; then
> -    if [ -n "$(dnsdomainname)" ]; then
> +    if [[ "$(dnsdomainname)" != "" && "$(dnsdomainname)" != "localdomain" ]]; then
>           utsname=${utsname}.$(dnsdomainname)
>       fi
>   fi
> @@ -1128,12 +1196,14 @@ if [ ! -z $clean ]; then
>       clean || exit 1
>       exit 0
>   fi
> -echo "container rootfs and config created"
> +echo "
> +Container rootfs and config have been created.
> +Edit the config file to check/enable networking setup.
> +"
>
>   if [[ -d ${cache_base}/bootstrap ]]
>   then
> -    echo "
> -You have successfully built a Fedora container and cache.  This cache may
> +    echo "You have successfully built a Fedora container and cache.  This cache may
>   be used to create future containers of various revisions.  The directory
>   ${cache_base}/bootstrap contains a bootstrap
>   which may no longer needed and can be removed.
> @@ -1147,3 +1217,16 @@ This is only used in the creation of the bootstrap run-time-environment
>   and may be removed.
>   "
>   fi
> +
> +echo "The temporary password for root is: '$root_password'
> +
> +You may want to note that password down before starting the container.
> +
> +The password set up as "expired" and will require it to be changed it at
> +first login, which you should do as soon as possible.  If you lose the
> +root 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):
> +
> +        chroot ${rootfs_path} passwd
> +"
>
>
>
> _______________________________________________
> lxc-devel mailing list
> lxc-devel at lists.linuxcontainers.org
> http://lists.linuxcontainers.org/listinfo/lxc-devel
>



More information about the lxc-devel mailing list