Monday, January 14, 2019

Flexible Server Part IV: Base build of guest VM


Part IV looks at how to build up a guest VM with a base configuration that is going to be common to most of my VMs including providing access to lots of temporary storage via NFS and enabling LDAP authentication so I don't have to deal with local passwords on every VM. You may find other services are common for your purposes so add these to the mix during your build. Finally, I clone the image so we can build more copies later without going through the manual steps each time.

Updates, common packages and basic configuration

So we start by installing a few packages and potentially running some configuration scripts. Accessing the VMs via ssh is important so I'll add that along with the NFS client.  I also want to make sure the timezone is set correctly as this may not have been established during the unattended image installation process.


?
01
02
03
04
05
06
07
08
09
10
11
12
13
# make sure the image has the latest updates
root@goat-lin001:~> apt-get update
root@goat-lin001:~> apt-get dist-upgrade
# add whatever common packages you want in the base build
root@goat-lin001:~> apt-get install ssh
root@goat-lin001:~> apt-get install nfs-client
# if required, fix your timezone settings, try one of the following:
root@goat-lin001:~> dpkg-reconfigure tzdata
# or this with the applicable timezone substituted
root@goat-lin001:~> ln -sf /usr/share/zoneinfo/America/Los_Angeles /etc/localtime
# make sure the image has the latest updates
root@goat-lin001:~> apt-get update
root@goat-lin001:~> apt-get dist-upgrade

# add whatever common packages you want in the base build
root@goat-lin001:~> apt-get install ssh
root@goat-lin001:~> apt-get install nfs-client

# if required, fix your timezone settings, try one of the following:
root@goat-lin001:~> dpkg-reconfigure tzdata

# or this with the applicable timezone substituted
root@goat-lin001:~> ln -sf /usr/share/zoneinfo/America/Los_Angeles /etc/localtime
Add the configuration for the NFS mount per your local configuration and test the mount comes up and all should be well on the next reboot.
?
01
02
03
04
05
06
07
08
09
10
root@goat-lin001:~> vi /etc/fstab
# /etc/fstab: static file system information.
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
proc            /proc           proc    defaults        0       0
devpts          /dev/pts        devpts  rw,noexec,nosuid,gid=5,mode=620 0  0
/dev/xvda1 / ext4 noatime,nodiratime,errors=remount-ro 0 1
192.168.1.2:/volume2/Scratch /mnt/scratch nfs auto,noatime,nolock,bg,nfsvers=4,intr,tcp,actimeo=1800 0 0
root@goat-lin001:~> mount /mnt/scratch
root@goat-lin001:~&gt; vi /etc/fstab
# /etc/fstab: static file system information.
#
# &lt;file system&gt; &lt;mount point&gt;   &lt;type&gt;  &lt;options&gt;       &lt;dump&gt;  &lt;pass&gt;
proc            /proc           proc    defaults        0       0
devpts          /dev/pts        devpts  rw,noexec,nosuid,gid=5,mode=620 0  0
/dev/xvda1 / ext4 noatime,nodiratime,errors=remount-ro 0 1
192.168.1.2:/volume2/Scratch /mnt/scratch nfs auto,noatime,nolock,bg,nfsvers=4,intr,tcp,actimeo=1800 0 0

root@goat-lin001:~&gt; mount /mnt/scratch

LDAP server configuration

An LDAP service provides a way for the guest VMs OS to authenticate users against a centralised database, and also to check for group membership amongst other things. The main purpose here is to avoid using local password databases across our VMs which would be difficult to maintain over time. I won't cover how to set up users and groups in the LDAP server since this will depend on what you have available in your setup and there are generally decent tutorials that will help you accomplish this. I will instead focus on configuring the guest VM.

I have created a couple of groups on the LDAP server:
  • unixusers: membership required to log in to a VM Guest
  • sudo: membership required for ability for specific users to elevate privileges via sudo cmd

Note that you may not want some of your accounts to be able to log in remotely or to elevate privileges (such as service accounts you may set up to run an application). So the above is a fairly simple way to implement these restrictions.
You will need the following packages installed, with an optional package for testing ldap connectivity and set-up with the ldapsearch utility (it's really quite useful):
?
1
2
3
4
root@goat-lin001:~> apt-get install ldap-auth-client nscd
# For ldapsearch util (testing):
root@goat-lin001:~> apt-get install ldap-utils
root@goat-lin001:~&gt; apt-get install ldap-auth-client nscd

# For ldapsearch util (testing):
root@goat-lin001:~&gt; apt-get install ldap-utils

A starting point for LDAP configuration on Ubuntu can be found here, but I found a range of other useful resources that may help you extend my simple configuration or diagnose problems. The package installer will ask for a number of inputs that will handle most of the configuration. An overview of this is provided here.
Configure the LDAP profile for NSS by running:
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
root@goat-lin001:~> sudo auth-client-config -t nss -p lac_ldap
root@goat-lin001:~> cat /etc/nsswitch.conf
# Example configuration of GNU Name Service Switch functionality.
# If you have the `glibc-doc-reference' and `info' packages installed, try:
# `info libc "Name Service Switch"' for information about this file.
# pre_auth-client-config # passwd:         compat systemd
passwd: files ldap
# pre_auth-client-config # group:          compat systemd
group: files ldap
# pre_auth-client-config # shadow:         compat
shadow: files ldap
gshadow:        files
hosts:          files dns
networks:       files
protocols:      db files
services:       db files
ethers:         db files
rpc:            db files
# pre_auth-client-config # netgroup:       nis
netgroup: nis
root@goat-lin001:~&gt; sudo auth-client-config -t nss -p lac_ldap

root@goat-lin001:~&gt; cat /etc/nsswitch.conf
# Example configuration of GNU Name Service Switch functionality.
# If you have the `glibc-doc-reference' and `info' packages installed, try:
# `info libc "Name Service Switch"' for information about this file.

# pre_auth-client-config # passwd:         compat systemd
passwd: files ldap
# pre_auth-client-config # group:          compat systemd
group: files ldap
# pre_auth-client-config # shadow:         compat
shadow: files ldap
gshadow:        files

hosts:          files dns
networks:       files

protocols:      db files
services:       db files
ethers:         db files
rpc:            db files

# pre_auth-client-config # netgroup:       nis
netgroup: nis
To enable home directory creation on login:
?
1
2
3
4
5
6
7
root@goat-lin001:~> vi /usr/share/pam-configs/my_mkhomedir
Name: activate mkhomedir
Default: yes
Priority: 900
Session-Type: Additional
Session:
        required                        pam_mkhomedir.so umask=0022 skel=/etc/skel
root@goat-lin001:~&gt; vi /usr/share/pam-configs/my_mkhomedir
Name: activate mkhomedir
Default: yes
Priority: 900
Session-Type: Additional
Session:
        required                        pam_mkhomedir.so umask=0022 skel=/etc/skel
In this case /etc/skel would contain the full contents of the user's home directory.
You may want the default shell for users to be something other than /bin/sh, so fix that by editing /etc/default/useradd and changing: SHELL=/bin/sh to whatever suits your taste. You can use chsh or edit /etc/passwd to change the shell if it's already been set. Note that this works for local users, but not LDAP users, and we'll look at how to deal with that a bit later.
?
1
2
3
4
5
6
7
root@goat-lin001:~> vi /usr/share/pam-configs/my_groups
Name: activate /etc/security/group.conf
Default: yes
Priority: 900
Auth-Type: Primary
Auth:
        required                        pam_group.so use_first_pass
root@goat-lin001:~&gt; vi /usr/share/pam-configs/my_groups
Name: activate /etc/security/group.conf
Default: yes
Priority: 900
Auth-Type: Primary
Auth:
        required                        pam_group.so use_first_pass
These changes now need to be activated. You may see many more options in the PAM configuration screen than those shown, there is generally one profile to confirm enablement per module you enable.
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
root@goat-lin001:~# pam-auth-update
Package configuration
 ┌───────────────────────────┤ PAM configuration ├───────────────────────────┐
 │ Pluggable Authentication Modules (PAM) determine how authentication,      │
 │ authorization, and password changing are handled on the system, as well   │
 │ as allowing configuration of additional actions to take when starting     │
 │ user sessions.                                                            │
 │                                                                           │
 │ Some PAM module packages provide profiles that can be used to             │
 │ automatically adjust the behavior of all PAM-using applications on the    │
 │ system.  Please indicate which of these behaviors you wish to enable.     │
 │                                                                           │
 │ PAM profiles to enable:                                                   │
 │                                                                           │
 │    [*] activate mkhomedir                                                 │
 │    [*] activate /etc/security/group.conf                                  │
 │    [*] Unix authentication                                                │
 │                                                                           │
 │                                                                           │
 │                    <Ok>                        <Cancel>                   │
 │                                                                           │
 └───────────────────────────────────────────────────────────────────────────┘
root@goat-lin001:~# /etc/init.d/nscd restart
Restarting nscd (via systemctl): nscd.service.
root@goat-lin001:~# pam-auth-update
Package configuration

 ┌───────────────────────────┤ PAM configuration ├───────────────────────────┐
 │ Pluggable Authentication Modules (PAM) determine how authentication,      │
 │ authorization, and password changing are handled on the system, as well   │
 │ as allowing configuration of additional actions to take when starting     │
 │ user sessions.                                                            │
 │                                                                           │
 │ Some PAM module packages provide profiles that can be used to             │
 │ automatically adjust the behavior of all PAM-using applications on the    │
 │ system.  Please indicate which of these behaviors you wish to enable.     │
 │                                                                           │
 │ PAM profiles to enable:                                                   │
 │                                                                           │
 │    [*] activate mkhomedir                                                 │
 │    [*] activate /etc/security/group.conf                                  │
 │    [*] Unix authentication                                                │
 │                                                                           │
 │                                                                           │
 │                    &lt;Ok&gt;                        &lt;Cancel&gt;                   │
 │                                                                           │
 └───────────────────────────────────────────────────────────────────────────┘


root@goat-lin001:~# /etc/init.d/nscd restart
Restarting nscd (via systemctl): nscd.service.


There are other configuration options for PAM which you can read about here.

If you find difficulties with the LDAP configuration you can edit /etc/ldap.conf or use dpkg-reconfigure to re-run the installation script, or run it directly /usr/sbin/pam-auth-update.


You may need to add a password to access your LDAP server:
?
1
2
3
4
5
root@goat-lin001:~> ls -al /etc/ldap.secret
-rw-r--r-- 1 root root 5 Apr 21 01:06 /etc/ldap.secret
root@goat-lin001:~> chmod 600 /etc/ldap.secret
root@goat-lin001:~> ls -al /etc/ldap.secret
-rw------- 1 root root 5 Apr 21 01:06 /etc/ldap.secret
root@goat-lin001:~&gt; ls -al /etc/ldap.secret
-rw-r--r-- 1 root root 5 Apr 21 01:06 /etc/ldap.secret
root@goat-lin001:~&gt; chmod 600 /etc/ldap.secret
root@goat-lin001:~&gt; ls -al /etc/ldap.secret
-rw------- 1 root root 5 Apr 21 01:06 /etc/ldap.secret

Add the following as the last line of /etc/security/access.conf

  • -:ALL EXCEPT root (unixusers):ALL
Recall that we created unixusers as a group in the LDAP server.

Add the highlighted line to /etc/pam.d/common-account to get PAM to use /etc/security/access.conf config above.

?
1
2
account required                        pam_permit.so
account required                        pam_access.so
account required                        pam_permit.so
account required                        pam_access.so

LDAP troubleshooting 

I found that a fair bit of digging and trial and error was required to get my LDAP configuration up and running with the various tweaks I was interested in having working. Some of the documentation is unclear or incomplete, and there are some interesting side-effects of switching to LDAP. I've included a few insights I got below in the hope that they may assist you in troubleshooting your own configuration.

Testing LDAP queries with ldapsearch

Sometimes you may not be sure that the LDAP configuration or data are correct. One way to get direct feedback on this is to use the ldapsearch utility.
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
root@goat-lin010:/etc/pam.d> ldapsearch -x -h ldap.webber.net -D "uid=root,cn=users,dc=ldap,dc=webber,dc=net" -w none -b "dc=ldap,dc=webber,dc=net" -s sub "(uid=gsw)"
# extended LDIF
#
# LDAPv3
# base <dc=ldap,dc=webber,dc=net> with scope subtree
# filter: (uid=gsw)
# requesting: ALL
#
# gsw, users, ldap.webber.net
dn: uid=gsw,cn=users,dc=ldap,dc=webber,dc=net
objectClass: top
objectClass: posixAccount
objectClass: shadowAccount
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: apple-user
objectClass: sambaSamAccount
objectClass: sambaIdmapEntry
objectClass: extensibleObject
cn: gsw
uid: gsw
uidNumber: 1000001
gidNumber: 1000001
loginShell: /bin/sh
homeDirectory: /home/gsw
shadowLastChange: 16921
shadowMin: 0
shadowMax: 99999
shadowWarning: 7
shadowExpire: -1
shadowInactive: 0
shadowFlag: 0
sn: gsw
authAuthority: ;basic;
sambaSID: S-1-5-21-13ABC813-33DEF77G36-21ZCR119X5-1092
sambaNTPassword: 283****C149
sambaLMPassword: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
sambaPasswordHistory: 00000000000000000000000000000000000000000000000000000000
 00000000
sambaPwdLastSet: 1461995430
sambaAcctFlags: [U          ]
displayName: gsw
userPassword:: e0NSWVBU***************************hhLldFbl*****ahFYXov
memberOf: cn=administrators,cn=groups,dc=ldap,dc=webber,dc=net
memberOf: cn=users,cn=groups,dc=ldap,dc=webber,dc=net
memberOf: cn=unixusers,cn=groups,dc=ldap,dc=webber,dc=net
gecos: This is a description
# search result
search: 2
result: 0 Success
# numResponses: 2
# numEntries: 1
# numResponses: 1
root@goat-lin010:/etc/pam.d&gt; ldapsearch -x -h ldap.webber.net -D "uid=root,cn=users,dc=ldap,dc=webber,dc=net" -w none -b "dc=ldap,dc=webber,dc=net" -s sub "(uid=gsw)"

# extended LDIF
#
# LDAPv3
# base &lt;dc=ldap,dc=webber,dc=net&gt; with scope subtree
# filter: (uid=gsw)
# requesting: ALL
#

# gsw, users, ldap.webber.net
dn: uid=gsw,cn=users,dc=ldap,dc=webber,dc=net
objectClass: top
objectClass: posixAccount
objectClass: shadowAccount
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: apple-user
objectClass: sambaSamAccount
objectClass: sambaIdmapEntry
objectClass: extensibleObject
cn: gsw
uid: gsw
uidNumber: 1000001
gidNumber: 1000001
loginShell: /bin/sh
homeDirectory: /home/gsw
shadowLastChange: 16921
shadowMin: 0
shadowMax: 99999
shadowWarning: 7
shadowExpire: -1
shadowInactive: 0
shadowFlag: 0
sn: gsw
authAuthority: ;basic;
sambaSID: S-1-5-21-13ABC813-33DEF77G36-21ZCR119X5-1092
sambaNTPassword: 283****C149
sambaLMPassword: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
sambaPasswordHistory: 00000000000000000000000000000000000000000000000000000000
 00000000
sambaPwdLastSet: 1461995430
sambaAcctFlags: [U          ]
displayName: gsw
userPassword:: e0NSWVBU***************************hhLldFbl*****ahFYXov
memberOf: cn=administrators,cn=groups,dc=ldap,dc=webber,dc=net
memberOf: cn=users,cn=groups,dc=ldap,dc=webber,dc=net
memberOf: cn=unixusers,cn=groups,dc=ldap,dc=webber,dc=net
gecos: This is a description

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

# numResponses: 1

Testing user and group membership

You can see whether your user is getting looked up via LDAP. You are looking for Ids that match those set up in LDAP rather than your local system - in the example above the entry uidNumber: 1000001 matches what the id command returns for the user gsw.


?
1
2
root@goat-lin010:/etc/pam.d# id gsw
uid=1000001(gsw) gid=1000001(users) groups=1000001(users),1000002(administrators)
root@goat-lin010:/etc/pam.d# id gsw
uid=1000001(gsw) gid=1000001(users) groups=1000001(users),1000002(administrators)

Persist local user configuration

You may notice that setting user shells and other config locally doesn't work once you've enabled LDAP. Many of these settings are now controlled by LDAP configuration. In the following example, an LDIF file is created to change the login shell of a user, which is applied with the ldapmodify command.

?
01
02
03
04
05
06
07
08
09
10
11
root@goat-lin001:~> cat test1.shellchange.ldif
dn: uid=test1,cn=users,dc=ldap,dc=webber,dc=net
changetype: modify
replace: loginShell
loginShell: /bin/bash
root@goat-lin001:~> ldapmodify -f test1.shellchange.ldif -h ldap.webber.net -D "uid=root,cn=users,dc=ldap,dc=webber,dc=net" -W
Enter LDAP Password:
modifying entry "uid=test1,cn=users,dc=ldap,dc=webber,dc=net"
root@goat-lin001:~> ldapsearch -x -h ldap.webber.net -D "uid=root,cn=users,dc=ldap,dc=webber,dc=net" -w none -b "dc=ldap,dc=webber,dc=net" -s sub "(uid=test1)" | grep loginShell
loginShell: /bin/bash
root@goat-lin001:~&gt; cat test1.shellchange.ldif
dn: uid=test1,cn=users,dc=ldap,dc=webber,dc=net
changetype: modify
replace: loginShell
loginShell: /bin/bash
root@goat-lin001:~&gt; ldapmodify -f test1.shellchange.ldif -h ldap.webber.net -D "uid=root,cn=users,dc=ldap,dc=webber,dc=net" -W
Enter LDAP Password:
modifying entry "uid=test1,cn=users,dc=ldap,dc=webber,dc=net"

root@goat-lin001:~&gt; ldapsearch -x -h ldap.webber.net -D "uid=root,cn=users,dc=ldap,dc=webber,dc=net" -w none -b "dc=ldap,dc=webber,dc=net" -s sub "(uid=test1)" | grep loginShell
loginShell: /bin/bash

There seemed to be some flakeyness in my setup which meant this didn't always work, so an alternative for lab environments might just be to add the following to /etc/ldap.conf:

  • nss_override_attribute_value loginShell /bin/bash

Help! id -a and getent show different data for the same users

Another issue I encountered was that running id -a and getent groups would show different groups. It seems that some of the data is looked up and cached and needs to be invalidated. This is normally done in /etc/init.d/nscd which calls nscd --invalidate=<element> at shutdown but if you're testing your configuration out and making live updates to both the LDAP server and your local configuration you may find that you need to force a new lookup to see the latest data. The following example shows how adding a user to a group in the LDAP server isn't immediately reflected on the local LDAP client.
?
1
2
3
4
5
root@goat-lin001:~> id gsw
uid=1000001(gsw) gid=1000001(users) groups=1000001(users)
root@goat-lin001:~> nscd --invalidate=group
root@goat-lin001:~> id gsw
uid=1000001(gsw) gid=1000001(users) groups=1000001(users),1000002(administrators),1000003(unixusers),1000004(sudo)
root@goat-lin001:~&gt; id gsw
uid=1000001(gsw) gid=1000001(users) groups=1000001(users)
root@goat-lin001:~&gt; nscd --invalidate=group
root@goat-lin001:~&gt; id gsw
uid=1000001(gsw) gid=1000001(users) groups=1000001(users),1000002(administrators),1000003(unixusers),1000004(sudo)


No comments:

Post a Comment