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

Michael H. Warfield mhw at WittsEnd.com
Thu Dec 26 23:08:57 UTC 2013


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.
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
+"
-- 
1.8.3.1


-- 
Michael H. Warfield (AI4NB) | (770) 978-7061 |  mhw at WittsEnd.com
   /\/\|=mhw=|\/\/          | (678) 463-0932 |  http://www.wittsend.com/mhw/
   NIC whois: MHW9          | An optimist believes we live in the best of all
 PGP Key: 0x674627FF        | possible worlds.  A pessimist is sure of it!

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 482 bytes
Desc: This is a digitally signed message part
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20131226/18b8455c/attachment-0001.pgp>


More information about the lxc-devel mailing list