[FrontPage] [TitleIndex] [WordIndex

This is a read-only archived version of wiki.centos.org

Transparent Encryption For the User's Home Folder

The following steps have been completed on a fresh CentOS v6.5 install to allow users on a desktop to encrypt their Home directory.

ref: http://www.howtoforge.com/encrypting_encfs_pam_script

Introduction

Many organisations are requesting users to encrypt their laptop to protect confidential information (customer names, internal contact details, etc.). On many Windows systems this is problematic and intrusive. This page is meant to help CentOS (and alike) users in configuring their systems to encrypt/decrypt their Home folder auto-magically using EncFS.

Install CentOS

The steps described in this page are based on an install of CentOS from the "Minimal" install CD. Any installation media should do.

Dummy User Account

When setting up the first user (during the installation process), I suggest you create a dummy account (e.g. "Administrator"). This will allow the configuration to be completed for the 'real' user.

Disk Setup

We are NOT setting up encryption using the installer method.

The recommendation is to select "All files in one partition" and accept the defaults.

When the installation is complete, reboot to complete the configuration and log in with the dummy user created during the installation. This user will be able to get root privileges for the rest of the process.

After installing CentOS always make sure your system is up to date so run the following:

yum update

Ensure /tmp is tmpfs

As we will be storing transient encoded information in the /tmp directory (the information is only stored for the duration of the login) it is prudent to ensure an attack by powering off the machine during login won't expose this information. It is easiest to set this up in single user mode. Therefore boot into single user mode by temporarily modifying the grub boot entry on boot.

  1. Press space when the grub boot loader laucnhes.
  2. Press e to edit the current boot stanza.

  3. Go down to the line starting with kernel.

  4. Press e to edit the line.

  5. Add a " -s" (without the quotes, so that's a [space][-][s]).
  6. Press [Enter].
  7. Press [b] to boot.

When booted into single user mode, delete all the files in /tmp and then add the following into the /etc/fstab file.

tmpfs         /tmp        tmpfs   defaults    0 0

Then save the file and reboot.

Install the required packages

pam_script(5)

pam-script.so is a pam module that implements session management. It optionally runs a session open script (/etc/security/onsessionopen), a session close script (/etc/security/onsessionclose) or an authentication script (/etc/security/onauth) if they exist. Alternatively any other script can be executed using the options onsessionopen=/path/to/script, onsessionclose=/path/to/script and onauth=/path/to/script.

<!> The pam_script utility comes from RepoForge, so you'll need to ensure that is installed from RepoForge.

encfs(1)

EncFS creates a virtual encrypted filesystem which stores encrypted data in the rootdir directory and makes the unencrypted data visible at the mountPoint directory. The user must supply a password which is used to (indirectly) encrypt both filenames and file contents.

<!> The encfs utility comes from EPEL, so you'll need to ensure that is installed from EPEL.

After setting up the repositories you can then install the packages.

# yum install pam_script fuse-encfs

Test encfs works

<!> Add the user to the 'fuse' group (logout and log back in again to pick up group memberships).

After installation you can test if it works:

$ mkdir -p ~/test/encrypted
$ mkdir -p ~/test/decrypted

$ encfs ~/test/encrypted ~/test/decrypted

$ mount (should show the just created mount)

$ echo "This is very secret." > ~/test/decrypted/testfile

$ cat ~/test/decrypted/testfile
This is very secret.

$ fusermount -u ~/test/decrypted
$ ls -al ~/test/decrypted

No file should exist

$ ls -al ~/test/encrypted
.  ..  .encfs6.xml  PoJa3a5ZO0iiZoTKmuNYZsLp

The filename is arbitrary, as it's hashed by encfs.

Remount the directory to view the contents of the file.

$ encfs ~/test/encrypted ~/test/decrypted
$ cat ~/test/decrypted/testfile
This is very secret.

The directory encrypted contains the encrypted files, and remains on the harddisk after unmounting and/or shutting down. The directory decrypted is the interface to it, and disappears when unmounted and/or shutting down.

The file testfile should appear in the decrypted directory, and in encrypted form in the encrypted directory. Name and content of the file are encrypted.

Now setup the users encrypted $HOME

The configuration will setup encfs for a new "real" user.

Now create your "real" user and ensure they're added to the fuse group.

# adduser <username>
# usermod -aG fuse <username>

Move their home directory/files out of the way:

# mkdir /var/backups/<username_home>
# mv /home/<username>/.* /var/backups/<username_home>/.
# rmdir /home/<username>
# mkdir -p /home/.encfs/<username> /home/<username>
# chown <username>.<username> /home/.encfs/<username> /home/<username>

To create the user's encrypted directory, we will use their login via the su utility, but without pulling in their environment (e.g. do NOT use the - switch to the su command).

Create the encrypted directory (If you encounter problems with permissions, see the Section on permissions):

# su <username>
$ encfs -v /home/.encfs/<username> /home/<username>

Accept the default options, or tinker with the encryption settings. Paranoia mode works, but won't support hard links. The author has used paranoia mode successfully.

{i} SET THE PASSWORD TO BE THE SAME AS YOUR LOGIN PASSWORD

Check that encrypted filesystem is mounted correctly:

$ mount

This should output something like the following:

encfs on /home/<username> type fuse.encfs (rw,nosuid,nodev,default_permissions)

Move the home directory/files back:

$ mv /var/backups/<username_home>/* /home/<username>/.

Create the scripts to autmount on login and unmount on logout

There is a single script /etc/security/onauth which is symlinked to from /etc/security/onsessionopen and /etc/security/onsessionclose. This was chosen to centralise setup and configuration.

$ cat /etc/security/onauth
#!/bin/sh
#
# What: onauth, onsessionopen, onsessionclose
# When: 5-Aug-2013
# Who:  Philip Jensen
# Why:  To capture login credentials to transparently fusermount an encrypted
#       home directory for a user.
#

# Setup vars
USER=$1
PASSWORD=$PAM_AUTHTOK
USER_FILE=/tmp/__u
PASSWORD_FILE=/tmp/__p
LOG_FILE=/var/log/pam_script.log

/bin/echo "------------------------------" >> ${LOG_FILE}
date >> ${LOG_FILE}
/bin/echo "Run as `whoami`" >> ${LOG_FILE}
#echo "Params passed to this script" >> ${LOG_FILE}
#echo "\$0 = $0" >> ${LOG_FILE}
#echo "\$1 = $1" >> ${LOG_FILE}
#echo "\$2 = $2" >> ${LOG_FILE}
#echo "\$3 = $3" >> ${LOG_FILE}
#echo "\$PAM_AUTHTOK = ${PAM_AUTHTOK}" >> ${LOG_FILE}
#echo "" >> ${LOG_FILE}

capture_credentials() {
        #
        umask 277
        /bin/echo "${USER}" | base64 > ${USER_FILE}
        /bin/echo "${PASSWORD}" | base64 > ${PASSWORD_FILE}
        exit 0
}

mount_encfs_home() {
        USER=`cat ${USER_FILE} | base64 -d`
        PASSWORD=`cat ${PASSWORD_FILE} | base64 -d`

        #echo ${PASSWORD} | su - ${USER} -c "/usr/bin/encfs -v -S /home/.encfs/${USER} /home/${USER} -- -o nonempty"  >> ${LOG_FILE} 2>&1
        echo ${PASSWORD} | su - ${USER} -c "/usr/bin/encfs -v -S /home/.encfs/${USER} /home/${USER} -- -o nonempty"  >> /dev/null 2>&1

        rm ${USER_FILE} ${PASSWORD_FILE}
}

umount_encfs_home() {
        echo "Unmounting encrypted home dir /home/.encfs/${USER} from /home/${USER}" >> ${LOG_FILE}
        
        # need to do a lazy unmount to wait until the filesystem is clean.
        #umount -l /home/${USER} >> ${LOG_FILE} 2>&1
        umount -l /home/${USER} >> /dev/null 2>&1
}

case "$0" in
        *onauth)
                echo "Capturing credentials" >> ${LOG_FILE}
                capture_credentials
                ;;
        *onsessionopen)
                echo "Trying to mount encfs home" >> ${LOG_FILE}
                mount_encfs_home
                ;;
        *onsessionclose)
                echo "Trying to un-mount encfs home" >> ${LOG_FILE}
                umount_encfs_home
                ;;
esac

echo "------------------------------" >> ${LOG_FILE}
exit 0

Make sure the script is executable.

# chmod +x /etc/security/onauth

Now create the symlinks

# ln -s /etc/security/onauth /etc/security/onsessionopen
# ln -s /etc/security/onauth /etc/security/onsessionclose

Setup PAM

As we're capturing the user's password to unlock the EncFS keys to mount and decrypt the encrypted filesystem, the changes are made to /etc/pam.d/password-auth

# cat /etc/pam.d/password-auth
#%PAM-1.0
# This file is auto-generated.
# User changes will be destroyed the next time authconfig is run.
auth        required      pam_env.so
# pam-script for EncFS mounting ......
auth        optional      pam_script.so expose=1 runas=root
auth        sufficient    pam_unix.so nullok try_first_pass
auth        requisite     pam_succeed_if.so uid >= 500 quiet
auth        required      pam_deny.so

account     required      pam_unix.so
account     sufficient    pam_localuser.so
account     sufficient    pam_succeed_if.so uid < 500 quiet
account     required      pam_permit.so

password    requisite     pam_cracklib.so try_first_pass retry=3 type=
password    sufficient    pam_unix.so sha512 shadow nullok try_first_pass use_authtok
password    required      pam_deny.so

session     optional      pam_keyinit.so revoke
session     required      pam_limits.so
session     [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
# pam-script for EncFS mounting ......
session     optional      pam_script.so runas=root
session     required      pam_unix.so

<!> This is automatically regenerated by authconfig, so the file attributes could be modified to make it immutable.

password-auth is typically a symlink to password-auth-ac, so set the immutable bit chattr +i password-auth-ac.

Check the attributes:-

# lsattr password-auth-ac
----i--------e- password-auth-ac

Fix ICEauthority issue

When X starts for the user it wants to write a file, but the file doesn't yet exist in a writable form as it's have a fuse mount laid over the top. The following resolves this issue.

See the last two lines of the file.

/etc/security/pam_env.conf

This change is required to remove the annoying message "Cannot update ICEauthority /home/(user)/.ICEauthority. The last line of this example contains the change.

# cat /etc/security/pam_env.conf 
#
# This is the configuration file for pam_env, a PAM module to load in 
# a configurable list of environment variables for a 
# 
# The original idea for this came from Andrew G. Morgan ...
#<quote>
#   Mmm. Perhaps you might like to write a pam_env module that reads a
#   default environment from a file? I can see that as REALLY
#   useful... Note it would be an "auth" module that returns PAM_IGNORE
#   for the auth part and sets the environment returning PAM_SUCCESS in
#   the setcred function...
#</quote>
#
# What I wanted was the REMOTEHOST variable set, purely for selfish
# reasons, and AGM didn't want it added to the SimpleApps login
# program (which is where I added the patch). So, my first concern is
# that variable, from there there are numerous others that might/would
# be useful to be set: NNTPSERVER, LESS, PATH, PAGER, MANPAGER .....
#
# Of course, these are a different kind of variable than REMOTEHOST in
# that they are things that are likely to be configured by
# administrators rather than set by logging in, how to treat them both
# in the same config file?
#
# Here is my idea: 
#
# Each line starts with the variable name, there are then two possible
# options for each variable DEFAULT and OVERRIDE. 
# DEFAULT allows and administrator to set the value of the
# variable  to some default value, if none is supplied then the empty
# string is assumed. The OVERRIDE option tells pam_env that it should
# enter in its value (overriding the default value) if there is one
# to use. OVERRIDE is not used, "" is assumed and no override will be
# done. 
#
# VARIABLE   [DEFAULT=[value]]  [OVERRIDE=[value]]
#
# (Possibly non-existent) environment variables may be used in values
# using the ${string} syntax and (possibly non-existent) PAM_ITEMs may
# be used in values using the @{string} syntax. Both the $ and @
# characters can be backslash escaped to be used as literal values
# values can be delimited with "", escaped " not supported.
# Note that many environment variables that you would like to use
# may not be set by the time the module is called.
# For example, HOME is used below several times, but 
# many PAM applications don't make it available by the time you need it.
#
#
# First, some special variables
#
# Set the REMOTEHOST variable for any hosts that are remote, default
# to "localhost" rather than not being set at all
#REMOTEHOST     DEFAULT=localhost OVERRIDE=@{PAM_RHOST}
#
# Set the DISPLAY variable if it seems reasonable 
#DISPLAY                DEFAULT=${REMOTEHOST}:0.0 OVERRIDE=${DISPLAY}
#
#
#  Now some simple variables
#
#PAGER          DEFAULT=less
#MANPAGER       DEFAULT=less
#LESS           DEFAULT="M q e h15 z23 b80"
#NNTPSERVER     DEFAULT=localhost
#PATH           DEFAULT=${HOME}/bin:/usr/local/bin:/bin\
#:/usr/bin:/usr/local/bin/X11:/usr/bin/X11
#
# silly examples of escaped variables, just to show how they work.
#
#DOLLAR         DEFAULT=\$
#DOLLARDOLLAR   DEFAULT=        OVERRIDE=\$${DOLLAR}
#DOLLARPLUS     DEFAULT=\${REMOTEHOST}${REMOTEHOST}
#ATSIGN         DEFAULT=""      OVERRIDE=\@
#
# set the ICEAUTHORITY file location to allow GNOME to start on encfs $HOME
ICEAUTHORITY DEFAULT=/tmp/.ICEauthority_@{PAM_USER}

Password Utility

There are some significant challenges in automating this process as the user must be able to change their own password, this requires the passwd utility to be used (as it's the only suid binary). However, it's possible to overcome this issue with the use of an expect script. Therefore install the expect package:

yum install expect

Then copy the following code to /usr/local/bin/passwd. The normal user's PATH is set to execute this passwd utility before the system one in /usr/bin.

#
# What:  /usr/local/bin/passwd
# When:  5/Aug/2013
# Who:   Philip Jensen (partially generated by autoexpect - refer expect-dev)
# Why:   To keep a user's password in sync with their encfs password
#        which needs to be unlocked when the user logs in.
#
#

set force_conservative 0  ;# set to 1 to force conservative mode even if
                          ;# script wasn't run conservatively originally
if {$force_conservative} {
        set send_slow {1 .1}
        proc send {ignore arg} {
                sleep .1
                exp_send -s -- $arg
        }
}

# Tell the user they aren't using the real passwd utility.
puts "###############################################################"
puts "#                                                             #"
puts "#     This 'passwd' utility overrides the original.           #"
puts "#                                                             #"
puts "#     It is used to change the user's UNIX password           #"
puts "#     as well as their encrypted file system password.        #"
puts "#                                                             #"
puts "#  The original password changing utility is /usr/bin/passwd  #"
puts "#                                                             #"
puts "###############################################################"

# Who is the user we are changing the password for. 
# (actually this is needed for encfsctl - see below)
set user [exec whoami]
puts "\nChanging password for $user.  If this is incorrect press Ctrl + C\r"

# Get the user's current password.
send_user "\nCurrent password: "
stty -echo
expect_user -re "(.*)\n"
set curr_password $expect_out(1,string)
stty echo

# Get their new password
send_user "\nNew password: "
stty -echo
expect_user -re "(.*)\n"
set new_password_one $expect_out(1,string)
stty echo
send_user "\nRe-enter new password: "
stty -echo
expect_user -re "(.*)\n"
set new_password_two $expect_out(1,string)
stty echo
puts "\n"

# Do the *NEW* passwords match?
if {$new_password_one!=$new_password_two} {
        puts "Passwords don't match, exiting!\n"
        exit 2
} else {
        set new_password "$new_password_one"
}

# Debug output
#puts "Changing password from: $curr_password\nto: $new_password\n"

# Hide output from the screen (operate in silent mode). 
# Of course this may not hide any potential output from the command line.
# Beware of background process watchers.
log_user 0

set timeout -1
spawn /bin/sh
match_max 100000

# begin changing passwords
puts "\nChanging user's password."

send -- "/usr/bin/passwd\r"
#expect -exact "/usr/bin/passwd\r
#Changing password for $user.\r
#(current) UNIX password: "
expect "UNIX password: "
send -- "$curr_password\r"
# we need to handle the user entering an incorrect current password.
expect {
        "Authentication token manipulation error" {puts "Current User password incorrect!" ; exit }
        "New password: " { send -- "$new_password\r" }
}

# we need to handle the user's proposed password being too simple.
expect {
        "BAD PASSWORD:" {puts "\nNew password doesn't meet password rules for complexity.\nExiting without changing passwords!" ; exit }
        "Retype new password: " { send -- "$new_password\r" }
}
#expect -exact "Retype new password: " { send -- "$new_password\r" }
expect -exact "\r
passwd: all authentication tokens updated successfully.\r"
send_user "User password changed successfully."

# If we get this far, changing the user's password succeeded,
# therefore we can proceed to change the encfs password.
puts "\nChanging encfs password."
expect -exact "\$ "
send -- "/usr/bin/encfsctl passwd /home/.encfs/$user\r"
expect -exact "\r
Enter current Encfs password\r
EncFS Password: "
send -- "$curr_password\r"

# what if the current encfs password didn't match the current user's password
# Of course now we have a very BIG problem as the transparent login won't work.
# But if they've used this utility, then they should be fine.  ;-)
expect {
        "Invalid password\r" { puts "\nCurrent encfs password incorrect.\nLogin and EncFS passwords not in sync.\nAutomatic mounting of encrypted home filesystem will fail!!\n\nUse /usr/bin/passwd and /usr/bin/encfsctl manually." ; exit }
        "New Encfs Password: " { send -- "$new_password\r" }
}
expect -exact "\r
Verify Encfs Password: "
send -- "$new_password\r"
expect "Volume Key successfully updated."
puts "Password change complete!"
send -- "exit\r"
expect eof

Make the file executable.

chmod +x /usr/local/bin/passwd

Now su to the user and test they can change their password.

su - <encrypted_home_user>
passwd

This should run the new password changing utility and change the user's password, and their encfs mount password.

Now you should be able to logout, and login (via GDM) as the user with an encrypted home.

An interesting side benefit of having a fuse mount, is that no other user will be able to view the contents of the encrypted directory - not event root!

Try it for yourself.


2023-09-11 07:23