papyri/md/cifs_client_setup.md
2024-03-20 11:40:22 -05:00

546 lines
22 KiB
Markdown

# CIFS Client Setup
## Contents
- [Overview](#overview)
- [Conventions Used](#conventions-used)
- [Software Installation](#software-installation)
- [Share Testing](#share-testing)
- [Client Configuration](#client-configuration)
- [ACL Testing](#acl-testing)
- [Kerberos Authentication](#kerberos-authentication)
- [Linux Packages](#linux-packages)
- [Domain Controller](#domain-controller)
- [Test the Domain](#test-the-domain)
- [Generate the Keytab](#generate-the-keytab)
- [Client KRB5 Setup](#client-krb5-setup)
- [Connect and Test](#connect-and-test)
- [CIFS Debugging](#cifs-debugging)
- [Packaging Bugs](#packaging-bugs)
- [Troubleshooting](#troubleshooting)
- [RHEL5 Kerberos Fails](#rhel5-kerberos-fails)
- [Kerberos Upcall Fails](#kerberos-upcall-fails)
- [Additional Reading](#additional-reading)
## Overview
Common Internet File System (**CIFS**) is the Windows analog to Network File System (**NFS**); to [quote Microsoft](http://technet.microsoft.com/en-us/library/cc939973.aspx):
> 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](http://www.samba.org/) 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`.
Options:
- **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](active_directory_with_winbind.md) 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](http://en.wikipedia.org/wiki/Security_Identifier) (**SID**) of the user on the domain. By far the easiest way to accomplish this is by [downloading PSUtils](http://technet.microsoft.com/en-us/sysinternals/bb897417.aspx) 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 tosatisfaction.
## 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:
- <https://bugs.launchpad.net/ubuntu/+source/cifs-utils/+bug/1372120>
- <https://bugs.archlinux.org/task/42052>
- <https://bugzilla.redhat.com/show_bug.cgi?id=985067>
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](https://bugzilla.redhat.com/show_bug.cgi?id=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
- <http://en.wikipedia.org/wiki/Server_Message_Block>
- <https://wiki.samba.org/index.php/LinuxCIFS_utils>
- <http://en.wikipedia.org/wiki/Security_Identifier>
- <http://technet.microsoft.com/en-us/library/cc733924%28v=ws.10%29.aspx>
- <https://access.redhat.com/solutions/262553>