CIFS Client Setup

From trapsink.com
Jump to: navigation, search


Overview

Common Internet File System (CIFS) is the Windows analog to Network File System (NFS); to quote Microsoft:

The Common Internet File System (CIFS) is the standard way that computer users share files across corporate intranets and the Internet. An enhanced version of the Microsoft open, cross-platform Server Message Block (SMB) protocol, CIFS is a native file-sharing protocol in Windows 2000. CIFS defines a series of commands used to pass information between networked computers.

Linux users are used to the Samba project and it's suite of utilities which provide the client side connection to CIFS shares on the Windows server platform, allowing a Linux server to mount the remote Windows share for use.


Conventions Used

Setting up a Windows server is not covered herein; this document is using these conventions:

  • Release: Windows 2012 R2 Server
  • Domain: CIFSGROUP
  • Server: CIFSSERVER, 192.168.5.5
  • CIFS User: cifsuser / p@ssw0rd
  • Test User: testuser / p@ssw0rd, used for testing additional ACLs
  • CIFS Share: c:\cifsdata\ as 'cifsdata', full ownership by user 'cifsuser'

The above values are used in all examples below. A dummy file server-file.txt has been created in c:\cifsdata\ and the cifsuser been given ownership; this will allow for initial testing.


Software Installation

The filesystem kernel module cifs.ko is already provided by the kernel on all distros, however the userspace utilities need to be installed. This primarily consists of mount.cifs, cifs.idmap, setcifsacl and getcifsacl. While not absolutely required, installing the smbclient utility is also recommended for debugging and troubleshooting.

Install the required packages:

# RHEL5 / CentOS5
# Note: RHEL/CentOS 5 do not provide setcifsacl/getcifsacl
yum install samba3x-client

# RHEL6 / CentOS6 / RHEL7 / CentOS7
yum install cifs-utils samba-client

# Debian / Ubuntu
apt-get update; apt-get install cifs-utils smbclient

# Arch
pacman -Sy; pacman -S cifs-utils smbclient

# openSUSE
zypper install cifs-utils samba-client

Select distros need a special service enabled to mount network filesystems at boot:

# RHEL5 / CentOS5 / RHEL6 / CentOS6
chkconfig netfs on

# Debian 7 - ignore the name, it works for CIFS too
insserv mountnfs.sh

Distros using systemd and upstart require no special service to be enabled. Some distros (such as Debian) may automatically start and enable the Winbind daemon which is not being used, disable it:

insserv -r winbind
service winbind stop


Share Testing

Before setting up the CIFS mount with the kernel, use smbclient to test that the share can be listed:

# smbclient -W CIFSGROUP -U CIFSSERVER\\cifsuser -L //192.168.5.5
Enter CIFSSERVER\cifsuser's password: 
Domain=[CIFSSERVER] OS=[Windows Server 2012 R2 Standard 9600] Server=[Windows Server 2012 R2 Standard 6.3]

	Sharename       Type      Comment
	---------       ----      -------
	ADMIN$          Disk      Remote Admin
	C$              Disk      Default share
	cifsdata        Disk      
	IPC$            IPC       Remote IPC

If this is working correctly, completely connect to the share and ensure the server-file.txt can be seen:

# smbclient -W CIFSGROUP -U CIFSSERVER\\cifsuser //192.168.5.5/cifsdata
Enter CIFSSERVER\cifsuser's password: 
Domain=[CIFSSERVER] OS=[Windows Server 2012 R2 Standard 9600] Server=[Windows Server 2012 R2 Standard 6.3]
smb: \> ls
  .                                   D        0  Sun Sep 21 14:54:42 2014
  ..                                  D        0  Sun Sep 21 14:54:42 2014
  server-file.txt                     A        0  Sun Sep 21 14:54:25 2014

		40957 blocks of size 1048576. 23832 blocks available
smb: \> exit


Client Configuration

First, create a file to hold the user and password values - the domain can also be specified, however experience has shown that the domain setting in this file doesn't always work depending on which release of the software is present. We will specify the domain in the /etc/fstab config instead.

/etc/cifspw
user=cifsuser
password=p@ssw0rd

Secure the file against prying eyes:

chmod 0600 /etc/cifspw


Next, add an entry to /etc/fstab for the mount - CIFS and POSIX permissions/attributes differ greatly, so notice that we are going to set a handful of values that maps the remote CIFS share to Linux friendly values.

Remember that the user permissions actually used are those on the remote server used to mount the share (cifsuser in these examples); these uid/gid mappings are for the Linux side only to provide POSIX friendly uid/gid usage as a client. Regardless of what is specified here, the remote end will always use the mount user from /etc/cifspw.

  • uid: user to map
  • gid: group to map
  • domain: remote CIFS domain
  • credentials: name of the file with user/pass
  • iocharset: which character set to use locally
  • file_mode: default file permissions
  • dir_mode: default directory permissions
  • _netdev: on startup ensure the network is up first
  • soft: if the CIFS share goes away, don't hang Linux

The user nobody exists on all distros, however on Debian/Ubuntu the group is nogroup whereas on all other distros the group is nobody. These are being used as a generic mapping example - they can be any local Linux user/group needed to accomplish the mission, for example apache or www-data if configuring a webserver that needs to write to the share.

/etc/fstab
# every distro except Debian/Ubuntu
//192.168.5.5/cifsdata  /data  cifs  uid=nobody,gid=nobody,domain=CIFSGROUP,credentials=/etc/cifspw,iocharset=utf8,file_mode=0644,dir_mode=0755,_netdev,soft  0 0

# Debian/Ubuntu only
//192.168.5.5/cifsdata  /data  cifs  uid=nobody,gid=nogroup,domain=CIFSGROUP,credentials=/etc/cifspw,iocharset=utf8,file_mode=0644,dir_mode=0755,_netdev,soft  0 0


Mount the share and test that basic create and delete privileges are working as expected, while examining the permissions and uid/gid mappings:

mkdir /data
mount /data
touch /data/test-file
ls -l /data/
 
  -rw-r--r-- 0 nobody nobody 0 Sep 21 14:54 server-file.txt
  -rw-r--r-- 1 nobody nobody 0 Sep 21 14:59 test-file


ACL Testing

The setcifsacl and getcifsacl userspace tools allow setting/getting the ACLs present on the remote end. If the client is connected to the domain via Winbind then the remote user name can be used and the cifs.idmap infrastructure will handle the details.

However, for these examples we're not connected via Winbind, so we need to obtain the Security Identifier (SID) of the user on the domain. By far the easiest way to accomplish this is by downloading PSUtils and using psgetsid.exe from PowerShell.

Retrieve the SID of testuser:

PS C:\PSTools> .\PsGetsid.exe testuser
SID for CIFSSERVER\testuser:
S-1-5-21-762712803-3572108623-4099884218-1003

From the client, attempt to set an ACL for testuser and check it; you can also use the standard Windows properties Security tab to verify it worked as intended:

setcifsacl -a "ACL:S-1-5-21-762712803-3572108623-4099884218-1003:ALLOWED/I/FULL" /data/test-file

getcifsacl /data/test-file

  REVISION:0x1
  CONTROL:0x8004
  OWNER:S-1-5-21-762712803-3572108623-4099884218-1001
  GROUP:S-1-5-21-762712803-3572108623-4099884218-513
  ACL:S-1-5-21-762712803-3572108623-4099884218-500:ALLOWED/I/FULL
  ACL:S-1-5-32-544:ALLOWED/I/FULL
  ACL:S-1-5-21-762712803-3572108623-4099884218-1001:ALLOWED/I/FULL
  ACL:S-1-5-18:ALLOWED/I/FULL
  ACL:S-1-5-21-762712803-3572108623-4099884218-1003:ALLOWED/I/FULL

If the Linux client is connected with Winbind, the use of the remote domain\username is possible, see the setcifsacl(1) man page for further information.


Kerberos Authentication

If a Windows Active Directory domain is available - or you can create one - Kerberos (KRB5) authentication with a randomized password can be used in place of the credentials file (/etc/cifspw) for additional security. First, we need to redefine a convention:

  • Domain: CIFSDOMAIN (NetBIOS name), cifsdomain.local (AD name)

Note that after this section is complete the usage of smbclient is different - since we will use a random password in the Kerberos principal, Kerberos authentication with smbclient must also be used:

smbclient -k -U cifsuser@CIFSDOMAIN.LOCAL //cifsserver/cifsdata

The IP address cannot be used, it has to be the name of the CIFS server as shown and described below, as the Kerberos infrastructure will now be using names, not IPs. The smbclient command can be used while the share is mounted, the two methods work together without issue.

Linux Packages

Two additional packages are required along with the ones installed previously; they may or may not already be installed. Some distros may ask for KRB5 configuration post-install, just accept the defaults - we'll overwrite them later.

# RHEL / CentOS
yum install keyutils krb5-workstation

# Debian / Ubuntu
apt-get update; apt-get install keyutils krb5-user

# Arch
pacman -Sy; pacman -S keyutils krb5

# openSUSE
zypper install keyutils krb5-client

Domain Controller

If the server is already part of a domain, skip this step and use the existing domain. There is no going back from this section, back up your server first as required.

If an AD domain is not present, the server will need to have the Active Directory Domain Services (AD DS) services installed and the server itself promoted to Domain Controller. This is required to create the Key Distribution Center (KDC) on the domain, responsible for supplying session tickets and temporary session keys.

Assuming Windows 2012 R2:

  1. Open Server Manager, go to Local Server on the left, then scroll all the way to the bottom Roles and Features
  2. Click the drop-down Tasks on the right side of the Roles and Features block, choose Add Roles and Features
  3. Under Installation Type in the Wizard choose the Role-based option then Next
  4. Under Server Selection choose our server, CIFSSERVER then Next
  5. Under Server Roles choose Active Directory Domain Services, accept the popup, then Next
  6. Under Features and AD DS accept the defaults then Next and start the install

Almost done, leave that dialog there even though it says you can close it and wait. Now that the bits are installed, you need to Promote this server to a Domain Controller -- the option is listed in blue text on the finish screen from the above steps, click it.

  1. On the initial screen choose the last option Add a Forest to start fresh
  2. For Root domain name enter the domain (cifsdomain.local herein), then Next
  3. The next screen asks for a Functional level and DNS, accept the defaults
  4. Enter a password of your choosing for Directory Services Restore Mode, then Next
  5. You will most likely get an error about DNS Delegation, click Next
  6. The NetBIOS name (CIFSDOMAIN herein) should automatically fill in; if not, enter it then Next
  7. Accept all other defaults for Paths, click Next to review and Next to being the Prerequisites check
  8. More warnings about DNS Delegation show up, ignore them
  9. Click Install and go get coffee, this takes awhile

The server will reboot automatically when finished. Give it some time, even after the reboot it's doing things that cause the login to take quite awhile.

Test the Domain

Identical to the above, first test the Domain for basic functionality -- this will ensure that something has not gone wrong if you had to promote this server to be a Domain Controller. Simply swap the old CIFSGROUP for CIFSDOMAIN in the smbclient command:

smbclient -W CIFSDOMAIN -U CIFSDOMAIN\\cifsuser //192.168.5.5/cifsdata

Enter CIFSDOMAIN\cifsuser's password: 
Domain=[CIFSDOMAIN] OS=[Windows Server 2012 R2 Standard 9600] Server=[Windows Server 2012 R2 Standard 6.3]
smb: \> exit

If this is no longer working, correct it before continuing.

Generate the Keytab

On the Windows server, the KRB5 keytab file needs to be generated to map the user to the principal at the same time. Open an Administrator PowerShell prompt, change to the shared directory and create it like so:

PS C:\Users\Administrator> cd C:\cifsdata
PS C:\cifsdata> ktpass.exe /princ cifsuser@CIFSDOMAIN.LOCAL /ptype KRB5_NT_PRINCIPAL /out krb5.keytab +rndPass /crypto AES256-SHA1 /mapuser CIFSDOMAIN\cifsuser

If this works successfully, a message should look like:

Targeting domain controller: CIFSSERVER.cifsdomain.local
Using legacy password setting method
Failed to set property 'servicePrincipalName' to 'cifsuser' on Dn 'CN=cifsuser,CN=Users,DC=cifsdomain,DC=local': 0x13.
WARNING: Unable to set SPN mapping data.
If cifsuser already has an SPN mapping installed for cifsuser, this is no cause for concern.
Key created.
Output keytab to krb5.keytab:
Keytab version: 0x502
keysize 75 cifsuser@CIFSDOMAIN.LOCAL ptype 1 (KRB5_NT_PRINCIPAL) vno 2 etype 0x12 (AES256-SHA1) keylength 32 (0x3053927eb10407491db5a4cd05849cca3ac96f7ce1bad32269e174efa50439f9)

Over on the Linux side, use smbclient to connect and download the file; this keytab file can be used on multiple clients, so make sure a backup copy is stored in a secure, non-public location.

SECURITY ALERT: Do not leave this file laying around on the public share! This file should be protected and accessible to only the Windows Administrator or Linux root users.

# smbclient -W CIFSDOMAIN -U CIFSDOMAIN\\cifsuser //192.168.5.5/cifsdata
smb: \> get krb5.keytab
smb: \> rm krb5.keytab

Move it into /etc/ and secure it from prying eyes, then test that you can read the principal:

mv krb5.keytab /etc/krb5.keytab
chown root:root /etc/krb5.keytab
chmod 0600 /etc/krb5.keytab

# klist -ke
Keytab name: FILE:/etc/krb5.keytab
KVNO Principal
---- --------------------------------------------------------------------------
   2 cifsuser@CIFSDOMAIN.LOCAL (aes256-cts-hmac-sha1-96) 

On RHEL5/CentOS5 use /usr/kerberos/bin/klist as it's not in $PATH until you log out and back in again.

Client KRB5 Setup

The client needs a few files configured to utilize a transparent ticket via upcall methods to the server. Some of these may already be present, or possibly configured in different files as there are several ways to do it. Debian and Ubuntu for instance configure these in /etc/request-key.conf by default, whereas RHEL/CentOS and openSUSE place them in separate files in /etc/request-key.d/:

/etc/request-key.d/cifs.idmap.conf
create  cifs.idmap    * * /usr/sbin/cifs.idmap %k
/etc/request-key.d/cifs.spnego.conf
create  cifs.spnego    * * /usr/sbin/cifs.upcall %k
/etc/request-key.d/dns_resolver.conf
create  dns_resolver   * * /usr/sbin/cifs.upcall %k

Next, we need to tell the client Kerberos libraries how to contact the upstream KRB5 infrastructure via the normal configuration in /etc/krb5.conf - this is only a basic template, adjust if other settings are already present:

/etc/krb5.conf
[logging]
 default = FILE:/var/log/krb5libs.log
 kdc = FILE:/var/log/krb5kdc.log
 admin_server = FILE:/var/log/kadmind.log

[libdefaults]
 default_realm = CIFSDOMAIN.LOCAL
 dns_lookup_realm = false
 dns_lookup_kdc = false
 ticket_lifetime = 24h
 renew_lifetime = 7d
 forwardable = true

[realms]
 CIFSDOMAIN.LOCAL = {
  kdc = cifsserver.cifsdomain.local
  admin_server = cifsserver.cifsdomain.local
  default_domain = cifsdomain.local
 }

[domain_realm]
 .cifsdomain.local = CIFSDOMAIN.LOCAL
 cifsdomain.local = CIFSDOMAIN.LOCAL

Finally, add an entry to /etc/hosts to map the names to IP of the server and domain:

/etc/hosts
192.168.5.5 cifsserver cifsserver.cifsdomain.local cifsdomain.local

Be careful using DNS (/etc/resolv.conf) instead - it's possible the Windows server - which is now a DNS server - will return public IPs for a DNS query. We are ensuring the client always uses the private network IP by using a local /etc/hosts configuration.

Connect and Test

Using the identical style from the non-KRB5 mount, the options are changed slightly to indicate the security krb5i in /etc/fstab:

# every distro except Debian/Ubuntu
//cifsserver/cifsdata  /data  cifs  user=cifsuser,sec=krb5i,uid=nobody,gid=nobody,iocharset=utf8,file_mode=0644,dir_mode=0755,_netdev,soft  0 0

# Debian/Ubuntu only
//cifsserver/cifsdata  /data  cifs  user=cifsuser,sec=krb5i,uid=nobody,gid=nogroup,iocharset=utf8,file_mode=0644,dir_mode=0755,_netdev,soft  0 0

Connect to the share, test it out:

mount /data
touch /data/test-krb5.txt
ls -l /data/test-krb5.txt 
rm -f /data/test-krb5.txt

At this point everything should be working as expected as with a non-Kerberos mount. The smbclient command must be adjusted to use Kerberos as well, no password prompt should now occur:

smbclient -k -U cifsuser@CIFSDOMAIN.LOCAL //cifsserver/cifsdata

See the debugging section below if something is now working to satisfaction.


CIFS Debugging

Various parts of the setup - particularly Kerberos - may not work as expected and require a bit of debugging. Fortunately a nice interface is present in the kernel module to activate on the fly.

First, enable the debug mode of the cifs module after it's been loaded into the kernel (which creates this interface). The default is 0 (no debugging), set it to 9 for max verbosity:

echo 9 > /proc/fs/cifs/cifsFYI

Next add an output destination for the debug info; for example, if using rsyslog on RHEL / CentOS 6:

echo '*.debug  /var/log/cifs-debug.log' >> /etc/rsyslog.conf
touch /var/log/cifs-debug.log
service rsyslog restart

Exact configuration will depend on the distribution and client setup already in place, as the logger in use may be syslog, rsyslog, syslog-ng, systemd-journald, etc. Don't forget to disable the debugging interface when complete:

echo 0 > /proc/fs/cifs/cifsFYI

...otherwise performance will suffer as everything is being logged to a very high degree.


Packaging Bugs

Some distributions may have slightly broken packages of cifs-utils for which the symlink of idmap is missing. I've reported these two bugs on Ubuntu 14 and Arch, Red Hat already had a bug report and it was fixed:


In general, the fix is very easy until these packages are updated or if you cannot use the latest packages:

# Ubuntu 14, cifs-utils 2:6.0-1ubuntu2
mkdir /etc/cifs-utils
ln -s /usr/lib/x86_64-linux-gnu/cifs-utils/idmapwb.so /etc/cifs-utils/idmap-plugin

# Arch, cifs-utils 6.3-2
mkdir /etc/cifs-utils
ln -s /usr/lib/cifs-utils/idmapwb.so /etc/cifs-utils/idmap-plugin

For versions of cifs-utils less than 6.2 an error will occur if it's missing:

ERROR: unable to initialize idmapping plugin: /etc/cifs-utils/idmap-plugin: cannot open shared object file: No such file or directory

For cifs-utils 6.2 and above, it's a warning instead:

WARNING: unable to initialize idmapping plugin. Only "raw" SID strings will be accepted: /etc/cifs-utils/idmap-plugin: cannot open shared object file: No such file or directory

All the other distros have this symlink present and work as expected.


Troubleshooting

RHEL5 Kerberos Fails

When configuring Kerberos authentication, an error may present like so when the mount command is issued:

mount error(126): Required key not available
Refer to the mount.cifs(8) manual page (e.g. man mount.cifs)

Enabling debugging reveals lines such as these in the log:

  kernel:  CIFS VFS: Send error in SessSetup = -126
  kernel:  fs/cifs/connect.c: CIFS VFS: leaving cifs_mount (xid = 2) rc = -126
  kernel:  CIFS VFS: cifs_mount failed w/return code = -126

Please refer to upstream bugzilla #574750 for full information; in a nutshell there's an issue with how the KRB5 credentials are obtained and used with the older code (RHEL5 doesn't use upstream cifs-utils). Adjust the /etc/fstab mount line to use uid=0 per that bug report - be warned though this means only the root user can actually write to the share when it's a system level mount.

Kerberos Upcall Fails

When configuring Kerberos authentication, an error may present like so when the mount command is issued:

mount error(38): Function not implemented
Refer to the mount.cifs(8) manual page (e.g. man mount.cifs)

Enabling debugging reveals lines such as these in the log:

  kernel: fs/cifs/sess.c: sess setup type 5
  kernel: CIFS VFS: Kerberos negotiated but upcall support disabled!
  kernel: CIFS VFS: Send error in SessSetup = -38
  kernel: CIFS VFS: cifs_mount failed w/return code = -38

This indicates that CONFIG_CIFS_UPCALL is disabled in the kernel; it can be checked like so:

gzip -dc /proc/config.gz | grep CONFIG_CIFS_UPCALL

This feature is enabled on all major upstream kernels, however some providers (such as custom cloud images) may have their own kernel. This feature must be enabled for Kerberos to work with CIFS.


Additional Reading