Path to pluralsight course : https://app.pluralsight.com/library/courses/lfcs-linux-user-group-management/table-of-contents
To look at the local user account DB, open up passwd
[centos@server1 ~]$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:999:998:User for polkitd:/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
postfix:x:89:89::/var/spool/postfix:/sbin/nologin
chrony:x:998:996::/var/lib/chrony:/sbin/nologin
centos:x:1000:1000:centos:/home/centos:/bin/bash
tss:x:59:59:Account used by the trousers package to sandbox the tcsd daemon:/dev/null:/sbin/nologin
unbound:x:997:994:Unbound DNS resolver:/etc/unbound:/sbin/nologin
geoclue:x:996:993:User for geoclue:/var/lib/geoclue:/sbin/nologin
usbmuxd:x:113:113:usbmuxd user:/:/sbin/nologin
openvpn:x:995:992:OpenVPN:/etc/openvpn:/sbin/nologin
abrt:x:173:173::/etc/abrt:/sbin/nologin
setroubleshoot:x:994:990::/var/lib/setroubleshoot:/sbin/nologin
lightdm:x:993:989::/var/lib/lightdm:/sbin/nologin
nm-openconnect:x:992:988:NetworkManager user for OpenConnect:/:/sbin/nologin
nm-openvpn:x:991:986:Default user for running openvpn spawned by NetworkManager:/:/sbin/nologin
vboxadd:x:990:1::/var/run/vboxadd:/bin/false
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
puppet:x:52:52:Puppet:/var/lib/puppet:/sbin/nologin
getent is a utility that can be used to get entries from a number of important text files in a linux system called databases. Examples of this are passwd, services, networks, hosts, group
The name services switch mechanism is the mechanism that is used by getent to search in mulitple locations for users. If we look in the nssswitch.conf file we can see the places where passwd will check (files for local file system, and sss for system security services subsystem which covers Active directory)
[centos@server1 ~]$ grep passwd /etc/nsswitch.conf
#passwd: db files nisplus nis
passwd: files sss
The cat /etc/passwd
command only shows you local users. If you are integrated with an AD domain, you can
use getent, which will return the local users as well as the users in the domain.
In our case there are only local users so the output is the same as the command above
[centos@server1 ~]$ getent passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
...
To get an individual entry from a database, in this case getting a user called
centos from the passwd
database
[centos@server1 ~]$ getent passwd centos
centos:x:1000:1000:centos:/home/centos:/bin/bash
To get a list of groups, can be used for local groups and domain-based groups
[centos@server1 ~]$ getent group
root:x:0:
bin:x:1:
daemon:x:2:
sys:x:3:
adm:x:4:
tty:x:5:
disk:x:6:
lp:x:7:
mem:x:8:
kmem:x:9:
wheel:x:10:centos
...
To use getent to get a list of networks
[centos@server1 ~]$ getent networks
default 0.0.0.0
loopback 127.0.0.0
link-local 169.254.0.0
To get a list of services/port names. This is basically querying the /etc/services file If we are integrated with LDAP, we can use this command to get a list of services on the LDAP servers
[centos@server1 ~]$ getent services
tcpmux 1/tcp
tcpmux 1/udp
rje 5/tcp
rje 5/udp
echo 7/tcp
echo 7/udp
discard 9/tcp sink null
discard 9/udp sink null
systat 11/tcp users
systat 11/udp users
daytime 13/tcp
daytime 13/udp
...
There is a login shell and a non-login shell. When we ssh to a machine or login on
the console itself, that starts up a new login shell. If we also use a su -
or su -l
that will initiate a new login shell.
The login shell is going to first look for the /etc/profile
login script, and thats
going to include it's extension directory /etc/profile.d
.
When the profile login script runs it wll look for the ~/.bash_profile
file and that
exists by default on a CentOS7 system. If the .bash_profile
doesn't exist we can default to
looking for the ~/.profile
file.
The .bash_profile
script is going to call the ~/.bashrc
and the first thing that that is going
to do is check the /etc/bashrc
script to see if it exists and execute it.
So when we get a login shell, we get everything, our full environment is set.
If we use a non-login shell (su
by itself), or we just run the bash
command
then we only run the ~/.bashrc
which in turn runs /etc/bashrc
, but we miss out on
the whole profile structure in this case
As explained above, in your /etc
directory there is a profile script that get's
executed along with any scrips in the /etc/profile.d directory. If you wanted a
script to be executed at login time, you don't need to edit the profile
script,
you can just drop the new script into the /etc/profile.d directory. As you install
new software on your system you will need additional scripts being added to this
directory.
centos@server1 etc]$ ls profile*
profile
profile.d:
256term.csh 256term.sh bash_completion.sh colorgrep.csh colorgrep.sh colorls.csh colorls.sh csh.local flatpak.sh lang.csh lang.sh less.csh less.sh sh.local vim.csh vim.sh vte.sh which2.csh which2.sh
You can also change your bash behavior by editing the /etc/bashrc
file. For example
you can change the value of the PS1 variable (which controls the bash prompt), and
have it display the full current working directory path rather than just the current
directory you are in.
The /etc/skel
directory is the default directory template that is used to create
home directories for new users. Modifying it will not affect existing users.
The contents of this directory becomes the contents of the home directory for new
users
[root@server1 /var/tmp]# cd /etc/skel/
[root@server1 /etc/skel]# ls -al
total 28
drwxr-xr-x. 3 root root 92 Mar 2 11:41 .
drwxr-xr-x. 121 root root 8192 Mar 21 14:06 ..
-rw-r--r--. 1 root root 18 Apr 1 2020 .bash_logout
-rw-r--r--. 1 root root 193 Apr 1 2020 .bash_profile
-rw-r--r--. 1 root root 231 Apr 1 2020 .bashrc
drwxr-xr-x. 4 root root 39 Feb 27 16:13 .mozilla
-rw-r--r--. 1 root root 658 Apr 7 2020 .zshrc
Useful side note! In a bash script the .
and source
commands mean the same thing -
they both are used to execute a script.
To get info for the current user. This shows the user id and the group id, which in this case are the same, but they don't always have to be the same. CentOS and Redhat based systems use private groups so each user account is going to be a member of their own group. The idea around private groups is that it keeps your user data private unless you choose to share it out.
This command will also show us the groups
info, which show us which groups the user is a
member of, and these groups can be used as an indicator as to what resources can be accessed.
So when you are accessing resources you can access resources that have access permissions to
either of these groups.
The selinux context of the user is also shown.
[centos@server1 ~]$ id
uid=1000(centos) gid=1000(centos) groups=1000(centos),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
To get info for another user
[centos@server1 ~]$ id root
uid=0(root) gid=0(root) groups=0(root)
To see the primary group id - ig -g
To see the primary group id - ig -G
To see the group names - id -gn
You can have local accounts or domain-based accounts.
To create a local user (you need elevated privileges and you will be prompted for your password). The -m flag is to create a home directory, but this is default behaviour, so it can be left out and the home directory will still be created.
[centos@server1 ~]$ sudo useradd -m user1
[sudo] password for centos:
passwd
is where local users are stored, to verify that our new user has been added
We can see that the password field is an x
which means it is stored in the shadow
file.
The comments field is blank in this case ::
[centos@server1 ~]$ tail -n 1 /etc/passwd
user1:x:1001:1001::/home/user1:/bin/bash
The contents of the new user's home directory will have been derived from the /etc/skel
directory template
[centos@server1 ~]$ sudo ls -al /home/user1
[sudo] password for centos:
total 16
drwx------. 3 user1 user1 92 Mar 21 16:40 .
drwxr-xr-x. 4 root root 33 Mar 21 16:40 ..
-rw-r--r--. 1 user1 user1 18 Apr 1 2020 .bash_logout
-rw-r--r--. 1 user1 user1 199 Mar 21 14:18 .bash_profile
-rw-r--r--. 1 user1 user1 236 Mar 21 14:16 .bashrc
drwxr-xr-x. 4 user1 user1 39 Feb 27 16:13 .mozilla
-rw-r--r--. 1 user1 user1 658 Apr 7 2020 .zshrc
To create a new user with no private group to be created (-N
), with a certain primary group
(-g users
) and a certain secondary group (-G adm
). We verify that the user's home
directory has also been created.
[centos@server1 ~]$ sudo useradd -N user2 -g users -G adm
[centos@server1 ~]$ !t
tail -n 1 /etc/passwd
user2:x:1002:100::/home/user2:/bin/bash
[centos@server1 ~]$ ls /home/
centos user1 user2
To create a user with a custom shell (the ordinary bourne shell)
[centos@server1 ~]$ sudo useradd user3 -G adm -s /bin/sh
[centos@server1 ~]$ !t
tail -n 1 /etc/passwd
user3:x:1003:1003::/home/user3:/bin/sh
On a Ubuntu/Debian based system there is a utility called /sbin/adduser
where you can
also specify the user's password during user creation. But on a CentOS7/RHEL system
this is symlinked to the useradd
command
[centos@server1 ~]$ ls -l /sbin/adduser
lrwxrwxrwx. 1 root root 7 Feb 27 15:42 /sbin/adduser -> useradd
When you create a new user you don't specify their passwords at creation time.
To set a user's password. You will need to enter your own password and then enter the user's password and retype to confirm it
[centos@server1 ~]$ sudo passwd user1
[sudo] password for centos:
Changing password for user user1.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.
User passwords are stored in the /etc/shadow file. They are encrypted using a SHA 512 algorithm
[centos@server1 ~]$ sudo grep user1 /etc/shadow
user1:$6$4IN6vMEf$Lod7W.r2XLZecVudYmKw.FCoD.B4odS9NcmM/El8CScfkaXmfrLKCwGR01W/0T6iE2xNrr4Wd1RNuT36op2x5/:18707:0:99999:7:::
To see an example of shadow data for users and their passwords. user2 and user3
have invalid passwords, denoted by the !!
in the second field. The third field
is the date the password was changed (in number of days since 1st Jan 1970). The
0 is the number of days the user has to keep the password before changing, it's not
set in this case. The 99999 is the how often in days the password needs to be changed,
you get a warning 7 days before it expires.
[centos@server1 ~]$ sudo grep user. /etc/shadow
user1:$6$4IN6vMEf$Lod7W.r2XLZecVudYmKw.FCoD.B4odS9NcmM/El8CScfkaXmfrLKCwGR01W/0T6iE2xNrr4Wd1RNuT36op2x5/:18707:0:99999:7:::
user2:!!:18707:0:99999:7:::
user3:!!:18707:0:99999:7:::
To change the password for a particular user. In this example we are passing the
name of the user and it's new password to the chpasswd
command (which requires
elevated privileges to run)
centos@server1 ~]$ echo 'user2:Password1' | sudo chpasswd
[sudo] password for centos:
[centos@server1 ~]$ sudo grep user. /etc/shadow
user1:$6$4IN6vMEf$Lod7W.r2XLZecVudYmKw.FCoD.B4odS9NcmM/El8CScfkaXmfrLKCwGR01W/0T6iE2xNrr4Wd1RNuT36op2x5/:18707:0:99999:7:::
user2:$6$aj.y/yeVvQWj6Un$bZaJATQtkdIxSNi.pVV9oeCwyKNh0S/Cr7p.kS.eb5afkEz7cfW1jipb5hHUQ0ZlfL4ppKLpB6R64J8OvHP4e.:18707:0:99999:7:::
user3:!!:18707:0:99999:7:::
A different way to change the password. But this is specific to RHEL/CentOS based systems, so better to learn the above approach which can also be used on Ubuntu systems
[centos@server1 ~]$ echo Password1 | sudo passwd user3 --stdin
Changing password for user user3.
passwd: all authentication tokens updated successfully.
[centos@server1 ~]$ sudo grep user. /etc/shadow
user1:$6$4IN6vMEf$Lod7W.r2XLZecVudYmKw.FCoD.B4odS9NcmM/El8CScfkaXmfrLKCwGR01W/0T6iE2xNrr4Wd1RNuT36op2x5/:18707:0:99999:7:::
user2:$6$aj.y/yeVvQWj6Un$bZaJATQtkdIxSNi.pVV9oeCwyKNh0S/Cr7p.kS.eb5afkEz7cfW1jipb5hHUQ0ZlfL4ppKLpB6R64J8OvHP4e.:18707:0:99999:7:::
user3:$6$j75ocy9g$A2eplLGmiSkn1/6UaZUbYPdkHIepKz5ZQiyUNjWvENBv4db/slf5xle0zQwmQTRkLqsLg3PRrMMnSdyJpla1s.:18707:0:99999:7:::
The shadow
data shown above shows the age data for each user and their passwords,
albeit in a not too human friendly way. Using the chage
command will display this
information in a human readable format. If you query a user other than your own you
need elevated privileges. This info comes from the shadow file.
[centos@server1 ~]$ sudo chage -l centos
Last password change : never
Password expires : never
Password inactive : never
Account expires : never
Minimum number of days between password change : 0
Maximum number of days between password change : 99999
Number of days of warning before password expires : 7
Password data is normally kept in the shadow file for security reasons. If you
want your passwords to be stored in the /etc/passwd file, you can run pwunconv
.
However doing it this way you lose the password aging info from the shadow file.
centos@server1 ~]$ sudo pwunconv
[centos@server1 ~]$ grep user1 /etc/passwd
user1:$6$4IN6vMEf$Lod7W.r2XLZecVudYmKw.FCoD.B4odS9NcmM/El8CScfkaXmfrLKCwGR01W/0T6iE2xNrr4Wd1RNuT36op2x5/:1001:1001::/home/user1:/bin/bash
To put the password data back into shadow
run
[centos@server1 ~]$ sudo pwconv
[centos@server1 ~]$ grep user1 /etc/passwd
user1:x:1001:1001::/home/user1:/bin/bash
To see what is possible with the chage
command
[centos@server1 ~]$ chage --help
Usage: chage [options] LOGIN
Options:
-d, --lastday LAST_DAY set date of last password change to LAST_DAY
-E, --expiredate EXPIRE_DATE set account expiration date to EXPIRE_DATE
-h, --help display this help message and exit
-I, --inactive INACTIVE set password inactive after expiration
to INACTIVE
-l, --list show account aging information
-m, --mindays MIN_DAYS set minimum number of days before password
change to MIN_DAYS
-M, --maxdays MAX_DAYS set maximum number of days before password
change to MAX_DAYS
-R, --root CHROOT_DIR directory to chroot into
-W, --warndays WARN_DAYS set expiration warning days to WARN_DAYS
We will use the above knowledge to force a user password change every 40 days
[centos@server1 ~]$ sudo chage -M 40 user1
[centos@server1 ~]$ chage -l user1
chage: Permission denied.
[centos@server1 ~]$ sudo chage -l user1
Last password change : Mar 21, 2021
Password expires : Apr 30, 2021
Password inactive : never
Account expires : never
Minimum number of days between password change : 0
Maximum number of days between password change : 40
Number of days of warning before password expires : 7
To lock a password for a user. This command really just pre-pends the !!
in front
of the password rendering it invalid. Requires elevated privileges
[centos@server1 ~]$ sudo passwd -l user1
Locking password for user user1.
passwd: Success
[centos@server1 ~]$ sudo grep user1 /etc/shadow
user1:!!$6$4IN6vMEf$Lod7W.r2XLZecVudYmKw.FCoD.B4odS9NcmM/El8CScfkaXmfrLKCwGR01W/0T6iE2xNrr4Wd1RNuT36op2x5/:18707:0:40:7:::
To unlock the password for a user. You can see the password is no longer invalid. Requires elevated privileges
[centos@server1 ~]$ sudo passwd -u user1
Unlocking password for user user1.
passwd: Success
[centos@server1 ~]$ sudo grep user1 /etc/shadow
user1:$6$4IN6vMEf$Lod7W.r2XLZecVudYmKw.FCoD.B4odS9NcmM/El8CScfkaXmfrLKCwGR01W/0T6iE2xNrr4Wd1RNuT36op2x5/:18707:0:40:7:::
We can use default info to be applied when we create new users. The default information is pulled from a couple of locations
The first file is the login.defs
file. This contains things like password aging
default values, id ranges for users and groups, home directory creation default value,
the umask
value, the password encryption algorithm, etc.
[centos@server1 ~]$ cat /etc/login.defs
#
# Please note that the parameters in this configuration file control the
# behavior of the tools from the shadow-utils component. None of these
# tools uses the PAM mechanism, and the utilities that use PAM (such as the
# passwd command) should therefore be configured elsewhere. Refer to
# /etc/pam.d/system-auth for more information.
#
# *REQUIRED*
# Directory where mailboxes reside, _or_ name of file, relative to the
# home directory. If you _do_ define both, MAIL_DIR takes precedence.
# QMAIL_DIR is for Qmail
#
#QMAIL_DIR Maildir
MAIL_DIR /var/spool/mail
#MAIL_FILE .mail
# Password aging controls:
#
# PASS_MAX_DAYS Maximum number of days a password may be used.
# PASS_MIN_DAYS Minimum number of days allowed between password changes.
# PASS_MIN_LEN Minimum acceptable password length.
# PASS_WARN_AGE Number of days warning given before a password expires.
#
PASS_MAX_DAYS 99999
PASS_MIN_DAYS 0
PASS_MIN_LEN 5
PASS_WARN_AGE 7
#
# Min/max values for automatic uid selection in useradd
#
UID_MIN 1000
UID_MAX 60000
# System accounts
SYS_UID_MIN 201
SYS_UID_MAX 999
#
# Min/max values for automatic gid selection in groupadd
#
GID_MIN 1000
GID_MAX 60000
# System accounts
SYS_GID_MIN 201
SYS_GID_MAX 999
#
# If defined, this command is run when removing a user.
# It should remove any at/cron/print jobs etc. owned by
# the user to be removed (passed as the first argument).
#
#USERDEL_CMD /usr/sbin/userdel_local
#
# If useradd should create home directories for users by default
# On RH systems, we do. This option is overridden with the -m flag on
# useradd command line.
#
CREATE_HOME yes
# The permission mask is initialized to this value. If not specified,
# the permission mask will be initialized to 022.
UMASK 077
# This enables userdel to remove user groups if no members exist.
#
USERGROUPS_ENAB yes
# Use SHA512 to encrypt password.
ENCRYPT_METHOD SHA512
To see additional default info given to users on creation
[centos@server1 ~]$ useradd -D
GROUP=100
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/bin/bash
SKEL=/etc/skel
CREATE_MAIL_SPOOL=yes
To change one of the defaults we can add an extra switch to the command, in this example use a different default shell
[centos@server1 ~]$ sudo useradd -Ds /bin/sh
[sudo] password for centos:
[centos@server1 ~]$ useradd -D
GROUP=100
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/sbin/nologin
SKEL=/etc/skel
CREATE_MAIL_SPOOL=no
The above information is held in a file and you can edit this file directly as well
[centos@server1 ~]$ sudo cat /etc/default/useradd
# useradd defaults file
GROUP=100
HOME=/home
INACTIVE=-1
EXPIRE=
SHELL=/bin/sh
SKEL=/etc/skel
CREATE_MAIL_SPOOL=yes
In the /etc/passwd
there is a field that we can use to store the user's full name.
The field is the comments field but it is commonly used to store the user's full name
To modify a user;
[centos@server1 ~]$ sudo usermod -c "User One" user1
[centos@server1 ~]$ grep user1 /etc/passwd
user1:x:1001:1001:User One:/home/user1:/bin/bash
Other things we can modify for a user is the default shell. To see a list of
available shells use chsh
[centos@server1 ~]$ chsh -l
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
/bin/zsh
We can use chsh to modify the shell of a user. If it's for another user we need elevated privileges
[centos@server1 ~]$ chsh -s /bin/sh centos
Changing shell for centos.
Password:
Shell changed.
[centos@server1 ~]$ grep centos /etc/passwd
centos:x:1000:1000:centos:/home/centos:/bin/sh
But it's better to use the usermod
command
[centos@server1 ~]$ sudo usermod -s /bin/bash centos
[sudo] password for centos:
[centos@server1 ~]$ grep centos /etc/passwd
centos:x:1000:1000:centos:/home/centos:/bin/bash
To delete a user. Using -r
will do a thorough deletion, including removing the
users home directory, mail spool files and cron jobs.
[centos@server1 ~]$ sudo userdel -r user2
[centos@server1 ~]$ ls /home
centos user1 user3
To leave the home directory in place when deleting a user, omit -r
. We can also
see that we can't resolve the user name from the uid anymore, because the user is
deleted.
[centos@server1 ~]$ sudo userdel user3
[centos@server1 ~]$ ls /home
centos user1 user3
[centos@server1 ~]$ ls -l /home
total 4
drwx------. 14 centos centos 4096 Mar 12 10:47 centos
drwx------. 3 user1 user1 92 Mar 21 16:40 user1
drwx------. 3 1003 1003 92 Mar 21 16:52 user3
To remove any residual files owned by the user that was deleted, we can delete them using a find and the uid
[centos@server1 ~]$ sudo find /home -uid 1003 -delete
[centos@server1 ~]$ ls /home
centos user1
CentOS7 has several flat files or "databases" that contain things like user info,
group info, services etc. The group database is located in /etc/group
You can grep the group for a group name or a user name. Below we are searching for a username, and it shows which groups the user belongs to. The second group is the user's private group that gets created when the user is created.
[centos@server1 ~]$ grep centos /etc/group
wheel:x:10:centos
centos:x:1000:centos
This info is confirmed when we run id
[centos@server1 ~]$ id
uid=1000(centos) gid=1000(centos) groups=1000(centos),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
To change your primary group id in the current session. Then if we create a new file we can see that the group ownership is the wheel group
[centos@server1 ~]$ newgrp wheel
[centos@server1 ~]$ id
uid=1000(centos) gid=10(wheel) groups=10(wheel),1000(centos) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[centos@server1 ~]$ touch g1
[centos@server1 ~]$ ls -l g1
-rw-r--r--. 1 centos wheel 0 Mar 22 21:22 g1
If we then exit out of hte current session, the primary group id will revert to it's normal setting
centos@server1 ~]$ exit
exit
[centos@server1 ~]$ touch g2
[centos@server1 ~]$ ls -l g2
-rw-rw-r--. 1 centos centos 0 Mar 22 21:24 g2
To create a new group, use groupadd
. When we grep the group database we see the
new group. The x
in the second field denotes that the group password is held in the
gshadow
file
[centos@server1 ~]$ sudo groupadd sales
[sudo] password for centos:
[centos@server1 ~]$ grep sales /etc/group
sales:x:1002:
When we look at the gshadow file we can see an entry for our group, and it has an
invalid password. Elevated privileges are required to look at the gshadow
file
[centos@server1 ~]$ sudo grep sales /etc/gshadow
sales:!::
groupadd
, groupmod
and groupdel
are the commands used to add, edit and delete a group
To add a user to another secondary group, "sales" use this. You need to include the existing secondary group if you want to keep it. If you were to just list the sales group the user would be removed from the wheel group. This is the method you would use if you wanted to do it from a user perspective
centos@server1 ~]$ sudo usermod -G sales,wheel centos
[sudo] password for centos:
Sorry, try again.
[sudo] password for centos:
[centos@server1 ~]$ id -Gn centos
centos wheel sales
[centos@server1 ~]$ id -G centos
1000 10 1002
If you wanted to do it from a group perspective, ie. add a load of users to a group, then use something like this;
[centos@server1 ~]$ sudo gpasswd -a centos sales
Adding user centos to group sales
To add in a list of members at the same time
[centos@server1 ~]$ sudo gpasswd -M centos,root,user1 sales
[centos@server1 ~]$ grep sales /etc/group
sales:x:1002:centos,root,user1
If you run id
for your own user, you will not see the group settings updated, you need to start a new session before you will see that
[centos@server1 ~]$ id
uid=1000(centos) gid=1000(centos) groups=1000(centos),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[centos@server1 ~]$ su -l centos
Last login: Wed Mar 24 20:40:09 GMT 2021 from 192.168.99.1 on pts/0
[centos@server1 ~]$ id
uid=1000(centos) gid=1000(centos) groups=1000(centos),10(wheel),1002(sales) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[centos@server1 ~]$ id -Gn
centos wheel sales
For this we are going to use the files associated with the httpd service, /var/www/html
When you install httpd initially it is owned by the root group, but the "others" section
will have read and execute privileges
[centos@server1 ~]$ ls -ld /var/www/html
drwxr-xr-x. 2 root root 6 Nov 16 16:19 /var/www/html
We should recursively change the group ownership of the /var/www
directory to the apache group
[centos@server1 ~]$ sudo chgrp -R apache /var/www
[centos@server1 ~]$ ls -ld /var/www
drwxr-xr-x. 4 root apache 33 Mar 14 21:36 /var/www
However we should remove the "others" privileges, they are no longer needed
[centos@server1 ~]$ sudo chmod -R o= /var/www
[centos@server1 ~]$ ls -ld /var/www
drwxr-x---. 4 root apache 33 Mar 14 21:36 /var/www
In the html directory we want to set the SGID bit, which will change the group x
to an s
to denote the sticky bit has been set. So this means that when any new file
is created in the html directory, its going to be group owned by the directory's owner.
[centos@server1 ~]$ sudo -i
[root@server1 ~]# cd /var/www/html
[root@server1 /var/www/html]# ls -ld
drwxr-x---. 2 root apache 24 Mar 24 21:11 .
[root@server1 /var/www/html]# chmod g+s .
[root@server1 /var/www/html]# ls -ld
drwxr-s---. 2 root apache 24 Mar 24 21:11 .
We set umask 027
to keep privileges away from the others group
[root@server1 /var/www/html]# umask 027
If we, as the root user, now create a new file under the /var/www/html
dir, instead of it
being owned by the root group, it will belong to the apache group because the sticky bit has
been set.
[root@server1 /var/www/html]# vi test.html
[root@server1 /var/www/html]# ls -l
total 8
-rw-r-----. 1 root apache 9 Mar 24 21:11 index.html
-rw-r-----. 1 root apache 15 Mar 24 21:26 test.html
[root@server1 /var/www/html]# id
uid=0(root) gid=0(root) groups=0(root),1002(sales) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
Group passwords are stored in the /etc/gshadow
file.
You need to enter the password of a group when you want to change the primary group
associated with a user. But first the group must have a valid password. You can check that by
grepping the /etc/group
directory
[centos@server1 ~]$ grep adm /etc/group
adm:!:4:
To set the password for a group. You need to enter your own password and then the groups password
[centos@server1 ~]$ sudo gpasswd adm
[sudo] password for centos:
Changing the password for group adm
New Password:
Re-enter new password:
Then if you check the /etc/group
file again you will see that the password is now in the gshadow
file
[centos@server1 ~]$ grep adm /etc/group
adm:x:4:
Then to change the primary group for your user, the group now has a valid password that can be entered
[centos@server1 ~]$ newgrp adm
Password:
[centos@server1 ~]$ id -gn
adm
[centos@server1 ~]$ id -Gn
adm wheel centos sales
Side note: Group passwords actually lessens security. If you have a group's password you can change your primary group ownership and possibly gain more access than you currently have
PAM
are pluggable authentication modules. When you login it accesses the PAM
and that will give you access to a session (assuming you have the permissions to
do so).
User logins can come from SSH, through the server console or through a GUI.
PAM modules can tie in to our login program and provide methods for Authentication (which can be password based, biometric based etc). There could be limits applied such as when we're allowed to login, how many times, etc. We can create our home directory during the login process and that's going to be affected by a PAM module.
When you have gained access to our session PAM can continue to monitor things such as our access to resources, such as how many processes we're allowed to run, how much memory can be used, how many files we can open. This can be continually monitored by PAM modules.
Password policy is another thing that can be monitored by PAM, it can ensure that we are applying complexity rules etc.
PAM-related files and config are located in the following three locations;
/etc/security/
- Contains config files for the PAM modules/etc/pam.d/
- Contains config files for the authentication programs and programs that can use PAM./lib64/security/
To get a list of the programs that can be tied into PAM, and their PAM configuration files
[centos@server1 ~]$ ls -al /etc/pam.d/
total 160
drwxr-xr-x. 2 root root 4096 Mar 2 11:39 .
drwxr-xr-x. 122 root root 8192 Mar 25 21:25 ..
-rw-r--r--. 1 root root 272 Oct 30 2018 atd
-rw-r--r--. 1 root root 192 Feb 2 16:31 chfn
-rw-r--r--. 1 root root 192 Feb 2 16:31 chsh
-rw-r--r--. 1 root root 232 Apr 1 2020 config-util
-rw-r--r--. 1 root root 287 Aug 9 2019 crond
lrwxrwxrwx. 1 root root 19 Feb 27 15:45 fingerprint-auth -> fingerprint-auth-ac
-rw-r--r--. 1 root root 702 Feb 27 16:12 fingerprint-auth-ac
-rw-r--r--. 1 root root 963 Nov 28 2017 lightdm
-rw-r--r--. 1 root root 698 Nov 28 2017 lightdm-autologin
-rw-r--r--. 1 root root 409 Nov 28 2017 lightdm-greeter
-rw-r--r--. 1 root root 97 Oct 1 17:29 liveinst
-rw-r--r--. 1 root root 796 Feb 2 16:31 login
-rw-r--r--. 1 root root 413 Feb 5 2017 mate-screensaver
-rw-r--r--. 1 root root 121 Nov 3 2018 mate-system-log
-rw-r--r--. 1 root root 154 Apr 1 2020 other
-rw-r--r--. 1 root root 188 Apr 1 2020 passwd
lrwxrwxrwx. 1 root root 16 Feb 27 15:45 password-auth -> password-auth-ac
-rw-r--r--. 1 root root 1033 Feb 27 16:12 password-auth-ac
-rw-r--r--. 1 root root 510 Aug 6 2020 pluto
-rw-r--r--. 1 root root 155 Apr 1 2020 polkit-1
lrwxrwxrwx. 1 root root 12 Feb 27 15:45 postlogin -> postlogin-ac
-rw-r--r--. 1 root root 330 Feb 27 15:45 postlogin-ac
-rw-r--r--. 1 root root 144 Feb 27 2020 ppp
-rw-r--r--. 1 root root 681 Feb 2 16:31 remote
-rw-r--r--. 1 root root 143 Feb 2 16:31 runuser
-rw-r--r--. 1 root root 138 Feb 2 16:31 runuser-l
-rw-r--r--. 1 root root 36 Sep 30 14:19 screen
lrwxrwxrwx. 1 root root 17 Feb 27 15:45 smartcard-auth -> smartcard-auth-ac
-rw-r--r--. 1 root root 752 Feb 27 16:12 smartcard-auth-ac
lrwxrwxrwx. 1 root root 25 Feb 27 15:42 smtp -> /etc/alternatives/mta-pam
-rw-r--r--. 1 root root 76 Apr 1 2020 smtp.postfix
-rw-r--r--. 1 root root 904 Aug 9 2019 sshd
-rw-r--r--. 1 root root 540 Feb 2 16:31 su
-rw-r--r--. 1 root root 200 Jan 26 21:56 sudo
-rw-r--r--. 1 root root 178 Jan 26 21:56 sudo-i
-rw-r--r--. 1 root root 137 Feb 2 16:31 su-l
lrwxrwxrwx. 1 root root 14 Feb 27 15:45 system-auth -> system-auth-ac
-rw-r--r--. 1 root root 1031 Feb 27 16:12 system-auth-ac
-rw-r--r--. 1 root root 97 Aug 3 2017 system-config-language
-rw-r--r--. 1 root root 129 Feb 2 16:34 systemd-user
-rw-r--r--. 1 root root 84 Oct 30 2018 vlock
-rw-r--r--. 1 root root 163 Feb 24 21:10 xserver
The shared libraries that PAM uses are all located at /lib64/security/
We can configure some of the PAM modules by accessing the following files
[centos@server1 ~]$ ls -al /etc/security/
total 68
drwxr-xr-x. 6 root root 4096 Mar 9 21:24 .
drwxr-xr-x. 122 root root 8192 Mar 25 21:25 ..
-rw-r--r--. 1 root root 4564 Apr 1 2020 access.conf
-rw-r--r--. 1 root root 82 Apr 1 2020 chroot.conf
drwxr-xr-x. 2 root root 109 Feb 27 16:13 console.apps
-rw-r--r--. 1 root root 604 Apr 1 2020 console.handlers
-rw-r--r--. 1 root root 939 Apr 1 2020 console.perms
drwxr-xr-x. 2 root root 6 Apr 1 2020 console.perms.d
-rw-r--r--. 1 root root 3635 Apr 1 2020 group.conf
-rw-r--r--. 1 root root 2442 Mar 9 21:20 limits.conf
drwxr-xr-x. 2 root root 27 Feb 27 15:42 limits.d
-rw-r--r--. 1 root root 1440 Apr 1 2020 namespace.conf
drwxr-xr-x. 2 root root 6 Apr 1 2020 namespace.d
-rwxr-xr-x. 1 root root 1019 Apr 1 2020 namespace.init
-rw-------. 1 root root 0 Apr 1 2020 opasswd
-rw-r--r--. 1 root root 2972 Apr 1 2020 pam_env.conf
-rw-r--r--. 1 root root 1718 Dec 6 2011 pwquality.conf
-rw-r--r--. 1 root root 419 Apr 1 2020 sepermit.conf
-rw-r--r--. 1 root root 2179 Apr 1 2020 time.conf
The default behaviour when creating users using the useradd command is for the user's
home directory to be created. However if you are batch creating hundreds of users, you
may not want to create all the home directories at user creation time, so you can turn off
the home directory creation by editing the /etc/login.defs
file and change the
CREATE_HOME
flag to no.
[centos@server1 ~]$ sudo vi /etc/login.defs
#
# If useradd should create home directories for users by default
# On RH systems, we do. This option is overridden with the -m flag on
# useradd command line.
#
CREATE_HOME no
Then if we create a user we will see that his home directory has not been created, but if we grep the passwd file we can see what his home directory will be called when it's created. We can also see the user's entry in the shadow file but it's showing as an invalid password because it hasn't been set yet
[centos@server1 ~]$ sudo useradd bob
[centos@server1 ~]$ ls /home
centos user1
[centos@server1 ~]$ grep bob /etc/passwd
bob:x:1002:1003::/home/bob:/bin/bash
[centos@server1 ~]$ sudo grep bob /etc/shadow
bob:!!:18711:0:99999:7:::
To set a password for the user bob, we just used the passwd
command
[centos@server1 ~]$ sudo passwd bob
Changing password for user bob.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.
Now the new user has a password so should be able to log. But the user doesn't
have a home directory yet. We can use PAM to automate the creation of home directories
First we check to see if we have the correct module and service installed, the oddjob
service
[centos@server1 ~]$ rpm -qa | grep oddjob
oddjob-mkhomedir-0.31.5-4.el7.x86_64
oddjob-0.31.5-4.el7.x86_64
Looks like we have the oddjob-mkhomedir module, and the service. The mkhomedir mechanism
is the preferred mechanism for creating home directories in Red Hat distros. To use it,
we need to enable and start the oddjob
service.
[centos@server1 ~]$ systemctl enable oddjobd
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-unit-files ===
Authentication is required to manage system service or unit files.
Authenticating as: centos
Password:
==== AUTHENTICATION COMPLETE ===
Created symlink from /etc/systemd/system/multi-user.target.wants/oddjobd.service to /usr/lib/systemd/system/oddjobd.service.
==== AUTHENTICATING FOR org.freedesktop.systemd1.reload-daemon ===
Authentication is required to reload the systemd state.
Authenticating as: centos
Password:
==== AUTHENTICATION COMPLETE ===
[centos@server1 ~]$ systemctl start oddjobd
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ===
Authentication is required to manage system services or units.
Authenticating as: centos
Password:
==== AUTHENTICATION COMPLETE ===
[centos@server1 ~]$ sudo systemctl status oddjobd
● oddjobd.service - privileged operations for unprivileged applications
Loaded: loaded (/usr/lib/systemd/system/oddjobd.service; enabled; vendor preset: disabled)
Active: active (running) since Thu 2021-03-25 21:52:07 GMT; 15s ago
Main PID: 2013 (oddjobd)
CGroup: /system.slice/oddjobd.service
└─2013 /usr/sbin/oddjobd -n -p /var/run/oddjobd.pid -t 300
Mar 25 21:52:07 server1.example.com systemd[1]: Started privileged operations for unprivileged applications.
To enable the automatic home dir creation across all the PAM modules.
[centos@server1 ~]$ sudo authconfig --enablemkhomedir --update
Let's gain root privileges and look at the contents of the /etc/pam.d directory,
grepping for mkhomedir. There are references to the pam_oddjob_mkhomedir
are in
a lot of PAM modules. Using the authconfig
command enables it for us in all the modules.
Without that we'd have to manually configure all these modules.
[centos@server1 ~]$ sudo -i
[root@server1 ~]# cd /etc/pam.d/
[root@server1 /etc/pam.d]# grep mkhomedir *
fingerprint-auth:session optional pam_oddjob_mkhomedir.so umask=0077
fingerprint-auth-ac:session optional pam_oddjob_mkhomedir.so umask=0077
password-auth:session optional pam_oddjob_mkhomedir.so umask=0077
password-auth-ac:session optional pam_oddjob_mkhomedir.so umask=0077
smartcard-auth:session optional pam_oddjob_mkhomedir.so umask=0077
smartcard-auth-ac:session optional pam_oddjob_mkhomedir.so umask=0077
system-auth:session optional pam_oddjob_mkhomedir.so umask=0077
system-auth-ac:session optional pam_oddjob_mkhomedir.so umask=0077
Having done that, if we now log in as the new user bob, we can see that his home dir is created at login time.
[root@server1 /etc/pam.d]# su - bob
Creating home directory for bob.
[bob@server1 ~]$ pwd
/home/bob
[bob@server1 ~]$ ls -al
total 16
drwx------. 3 bob bob 92 Mar 25 21:54 .
drwxr-xr-x. 5 root root 44 Mar 25 21:54 ..
-rw-------. 1 bob bob 18 Mar 25 21:54 .bash_logout
-rw-------. 1 bob bob 199 Mar 25 21:54 .bash_profile
-rw-------. 1 bob bob 236 Mar 25 21:54 .bashrc
drwx------. 4 bob bob 39 Mar 25 21:54 .mozilla
-rw-------. 1 bob bob 658 Mar 25 21:54 .zshrc
PAM modules can be used to control the quality/complexity of user passwords
The pam.d directory holds configuration files for programs that can use PAM modules.
There is a shared file that is used by many programs, system-auth
To look at the PAM configuration that controls password changing
[centos@server1 ~]$ cat /etc/pam.d/system-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
auth required pam_faildelay.so delay=2000000
auth sufficient pam_unix.so nullok try_first_pass
auth requisite pam_succeed_if.so uid >= 1000 quiet_success
auth required pam_deny.so
account required pam_unix.so
account sufficient pam_localuser.so
account sufficient pam_succeed_if.so uid < 1000 quiet
account required pam_permit.so
password requisite pam_pwquality.so try_first_pass local_users_only retry=3 authtok_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 optional pam_systemd.so
session optional pam_oddjob_mkhomedir.so umask=0077
session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session required pam_unix.so
The settings in this file relevant to passwords are all of password
event. So
password changes fall under password
type events.
password requisite pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=
requisite
means it has to pass success for anything else to run. Then we have the name of
the module pam_pwquality.so
. These shared libraries are stored in /lib64/security
.
This is followed by some options for the module such as try_first_pass
etc. The
pam_pwquality.so
is where we are setting our password quality, and the configuration for this
is outlined below.
To look at password quality config file , look at the /etc/security/pwquality.conf
file.
It contains policies for things such as max and min number of characters and lots of other
constraints that can be placed on password changes.
[centos@server1 ~]$ less /etc/security/pwquality.conf
Configuration for systemwide password quality limits
# Defaults:
#
# Number of characters in the new password that must not be present in the
# old password.
# difok = 5
#
# Minimum acceptable size for the new password (plus one if
# credits are not disabled which is the default). (See pam_cracklib manual.)
# Cannot be set to lower value than 6.
# minlen = 9
#
# The maximum credit for having digits in the new password. If less than 0
# it is the minimum number of digits in the new password.
# dcredit = 1
#
# The maximum credit for having uppercase characters in the new password.
# If less than 0 it is the minimum number of uppercase characters in the new
# password.
# ucredit = 1
#
# The maximum credit for having lowercase characters in the new password.
# If less than 0 it is the minimum number of lowercase characters in the new
# password.
# lcredit = 1
#
# The maximum credit for having other characters in the new password.
# If less than 0 it is the minimum number of other characters in the new
# password.
# ocredit = 1
...
There is a handy program that gives your password a score based on the policies set
When you type the command pwscore
you then enter a password and it gives it a
quality rating
[centos@server1 ~]$ pwscore
Password1
Password quality check failed:
The password fails the dictionary check - it is based on a dictionary word
[centos@server1 ~]$ pwscore
Aorangi&1922
78
It even detects if your password is based on a dictionary word, even if you are using symbols that resemble letters;
[centos@server1 ~]$ pwscore
P@$$w0rd1
Password quality check failed:
The password fails the dictionary check - it is based on a dictionary word
pwscore
values run from 0 to 100, where 0 is a very weak password and 100 is a very
strong one
It is quite likely that you do not want to allow your users unfettered access to all of the resources on your linux system, as it is most likely shared with other users and services. We can use PAM to control the level of access that users have through to the resources.
To see the restrictions that are in place use the ulimit
command.
We can actually change some of these settings ourselves and don't need PAM to do so
[centos@server1 ~]$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 3832
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 3832
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
To view a particular limit use the switch that you can identify from the above
output. For example, to view the max user processes (-u)
;
[centos@server1 ~]$ ulimit -u
3832
To change a particular value, you use the flag of the limit you want to change, with the new value, such as;
[centos@server1 ~]$ ulimit -u 10
[centos@server1 ~]$ ulimit -u
10
To demonstrate this new limit we can create a script that will call itself and therefore
create a new process each time it does it. The $0
is what calls the current script.
However after 10 concurrent processes are reached the limit should kick in and prevent
a new process from being created.
[centos@server1 ~]$ vi test.sh
#!/bin/bash
echo "test test"
$0
Then when we make it executable and run it we get the following;
[centos@server1 ~]$ chmod +x test.sh
[centos@server1 ~]$ ./test.sh
test test
test test
test test
test test
test test
test test
test test
test test
./test.sh: fork: retry: No child processes
./test.sh: fork: retry: No child processes
./test.sh: fork: retry: No child processes
./test.sh: fork: retry: No child processes
The above describes how you could impose a limit on your own user. However the more
likely scenario would be an admin imposing limits across users and groups in the organization.
The settings for this is controlled by the /etc/security/limits.conf
file.
This limits policy file is laid out as follows;
#<domain> <type> <item> <value>
domain
can be a user name, or a group name (with "@group" syntax)
type
can be soft
or hard
. soft
is the current effective limit. hard
is the absolute
ceiling that cannot be exceeded. An admin can adjust the soft limit but it can never exceed
the hard limit.
item
- describes where the limits can be implemented on. The list of items are as follows;
#<item> can be one of the following:
# - core - limits the core file size (KB)
# - data - max data size (KB)
# - fsize - maximum filesize (KB)
# - memlock - max locked-in-memory address space (KB)
# - nofile - max number of open file descriptors
# - rss - max resident set size (KB)
# - stack - max stack size (KB)
# - cpu - max CPU time (MIN)
# - nproc - max number of processes
# - as - address space limit (KB)
# - maxlogins - max number of logins for this user
# - maxsyslogins - max number of logins on the system
# - priority - the priority to run user process with
# - locks - max number of file locks the user can hold
# - sigpending - max number of pending signals
# - msgqueue - max memory used by POSIX message queues (bytes)
# - nice - max nice priority allowed to raise to values: [-20, 19]
# - rtprio - max realtime priority
So for example if you wanted to set a limit for all users to have 4 concurrent logins;
#<domain> <type> <item> <value>
* - maxlogins 4
To make some limits for the users group for number of processes. This is setting a soft limit (the effective limit) of 50 and a hard limit of 75, which cannot be exceeded.
#<domain> <type> <item> <value>
@users soft nproc 50
@users hard nproc 75
We can in effect put a virtual "lock" on our servers, ensuring users can only log in at certain times of day using PAM.
To impose restrictions around ssh access we need to add a new restriction policy
to the /etc/pam.d/sshd
file. Add a new entry to the account section of that file,
above the other account entries
[centos@server1 /etc/pam.d]$ sudo vi sshd
...
account required pam_time.so
account required pam_nologin.so
account include password-auth
...
The new restriction is an account
restriction, it is required
which means we need to
pass but can continue. That's why we need to put this restriction in first above the others.
pam_time.so
is the library file that is going to be checked when this restriction is
enacted. That module is located in the /lib64/security
The configuration for this module is located in /etc/security/time.conf
To create a new login time restriction in this file, edit it and add in the shown line at the bottom of the file.
[centos@server1 /etc/pam.d]$ sudo vi /etc/security/time.conf
*;*;centos|bob;Wk0800-1800
- The first
*
is for services, we have specified all services although we could have specifiedsshd
. - The second
*
is for terminals, again we have specified all terminals. - The third section is a user or list of users, in this case we have specified a pipe-delimited list of 2 users, "centos" and "bob"
- The last section is the actual time window those users are allowed to log in under. In
this case it is week days (
Wk
) between the hours of 0800 and 1800.
If one of those users attempts to login outside of those hours then they will be blocked.
First of all we need to check if our hostname has been set
centos@server1 ~]$ hostname
server1.example.com
Then we need to gain root privileges
[centos@server1 ~]$ su -
Password:
Last login: Wed Mar 24 20:53:01 GMT 2021 on pts/0
Then we need to add an entry for our machine and hostname into our /etc/hosts
file.
We can confirm our ip address by running ip a s
. We can confirm that our hosts
file has the entry by pinging the hostname FQDN.
[root@server1 ~]# echo "192.168.99.107 server1.example.com" >> /etc/hosts
[root@server1 ~]# ping server1.example.com
PING server1.example.com (192.168.99.107) 56(84) bytes of data.
64 bytes from server1.example.com (192.168.99.107): icmp_seq=1 ttl=64 time=0.064 ms
64 bytes from server1.example.com (192.168.99.107): icmp_seq=2 ttl=64 time=0.055 ms
We can check to make sure no ldap server is listening
[root@server1 ~]# netstat -ltn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN
tcp6 0 0 :::22 :::* LISTEN
tcp6 0 0 ::1:25 :::* LISTEN
Next we need to add a firewall rule to allow ldap traffic through. The firewall settings need to be reloaded for it to take effect in the current session (otherwise we need to create a new session)
[root@server1 ~]# firewall-cmd --permanent --add-service=ldap
success
[root@server1 ~]# firewall-cmd --reload
success
Finally we install the various ldap packages and tools using yum
[root@server1 ~]# yum install -y openldap openldap-clients openldap-servers migrationtools.noarch
To get a DB Config example in place;
[root@server1 ~]# cp /usr/share/openldap-servers/DB_CONFIG.example /var/lib/ldap/DB_CONFIG
[root@server1 ~]# ls -l /var/lib/ldap/
total 4
-rw-r--r--. 1 root root 845 Mar 27 21:22 DB_CONFIG
Next we run the slaptest
command to generate the db files (it looks like a failure
but it actually generates the necessary files we need). We need to change the ownership
to the ldap
user and ldap
group as well
[root@server1 ~]# slaptest
605fa268 hdb_db_open: database "dc=my-domain,dc=com": db_open(/var/lib/ldap/id2entry.bdb) failed: No such file or directory (2).
605fa268 backend_startup_one (type=hdb, suffix="dc=my-domain,dc=com"): bi_db_open failed! (2)
slap_startup failed (test would succeed using the -u switch)
[root@server1 ~]# chown ldap.ldap /var/lib/ldap/*
[root@server1 ~]# ls -l /var/lib/ldap/
total 18968
-rw-r--r--. 1 ldap ldap 2048 Mar 27 21:23 alock
-rw-------. 1 ldap ldap 2326528 Mar 27 21:23 __db.001
-rw-------. 1 ldap ldap 17448960 Mar 27 21:23 __db.002
-rw-------. 1 ldap ldap 1884160 Mar 27 21:23 __db.003
-rw-r--r--. 1 ldap ldap 845 Mar 27 21:22 DB_CONFIG
Next we enable and start the OpenLDAP service
[root@server1 ~]# systemctl start slapd
[root@server1 ~]# systemctl enable slapd
Created symlink from /etc/systemd/system/multi-user.target.wants/slapd.service to /usr/lib/systemd/system/slapd.service.
[root@server1 ~]# systemctl status slapd
● slapd.service - OpenLDAP Server Daemon
Loaded: loaded (/usr/lib/systemd/system/slapd.service; enabled; vendor preset: disabled)
Active: active (running) since Sat 2021-03-27 21:27:36 GMT; 11s ago
Docs: man:slapd
man:slapd-config
man:slapd-hdb
man:slapd-mdb
file:///usr/share/doc/openldap-servers/guide.html
Main PID: 5331 (slapd)
CGroup: /system.slice/slapd.service
└─5331 /usr/sbin/slapd -u ldap -h ldapi:/// ldap:///
We check the ports being listened on to see if ldap is running using netstat
[root@server1 ~]# netstat -ltn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:389 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN
tcp6 0 0 :::389 :::* LISTEN
tcp6 0 0 :::22 :::* LISTEN
tcp6 0 0 ::1:25 :::* LISTEN
[root@server1 ~]# netstat -lt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:ldap 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:ssh 0.0.0.0:* LISTEN
tcp 0 0 localhost:smtp 0.0.0.0:* LISTEN
tcp6 0 0 [::]:ldap [::]:* LISTEN
tcp6 0 0 [::]:ssh [::]:* LISTEN
tcp6 0 0 localhost:smtp [::]:* LISTEN
To configure the ldap schema files. The schema defines what can be created in our directory service.
[root@server1 /etc/openldap/schema]# ldapadd -Y EXTERNAL -H ldapi:/// -D "cn=config" -f cosine.ldif
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0
adding new entry "cn=cosine,cn=schema,cn=config"
[root@server1 /etc/openldap/schema]# ldapadd -Y EXTERNAL -H ldapi:/// -D "cn=config" -f nis.ldif
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0
adding new entry "cn=nis,cn=schema,cn=config"
To create an encrypted password for our directory administrator, passing in the secret
(-s
) and removing the trailing carraige return (-n
) and sending it through to the
rootpwd file, which is just a temporary file that we need to store this password.
[root@server1 ~]# slappasswd -s Password1 -n > rootpwd
[root@server1 ~]# cat rootpwd
{SSHA}t2GXHIRTx7bRxiuf5ucCxs35fTA1YTAP
We are now going to import this password into another configuration file. The contents of this file are provided as part of the course notes, we are just inserting the SSHA of the password created above.
[root@server1 ~]# vi config.ldif
dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcSuffix
olcSuffix: dc=example,dc=com
dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcRootDN
olcRootDN: cn=Manager,dc=example,dc=com
dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcRootPW
#Password is Password1 or add your own
olcRootPW: {SSHA}t2GXHIRTx7bRxiuf5ucCxs35fTA1YTAP
dn: cn=config
changetype: modify
replace: olcLogLevel
olcLogLevel: 0
dn: olcDatabase={1}monitor,cn=config
changetype: modify
replace: olcAccess
olcAccess: {0}to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" read by dn.base="cn=Manager,dc=example,dc=com" read by * none
Finally, we can import this config file using the ldapmodify
command. This step
completes the configuration of the ldap server
[root@server1 ~]# ldapmodify -Y EXTERNAL -H ldapi:/// -D "cn=config" -f config.ldif
SASL/EXTERNAL authentication started
SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth
SASL SSF: 0
modifying entry "olcDatabase={2}hdb,cn=config"
modifying entry "olcDatabase={2}hdb,cn=config"
modifying entry "olcDatabase={2}hdb,cn=config"
modifying entry "cn=config"
modifying entry "olcDatabase={1}monitor,cn=config"
The previous step was about configuring the LDAP server. Now we need to create the hierarchal structure, which is going to look after our directory tree. We are now going to create the top-level containers that are going to organize our directory entries.
[root@server1 ~]# vi structure.ldif
dn: dc=example,dc=com
dc: example
objectClass: top
objectClass: domain
dn: ou=people,dc=example,dc=com
ou: people
objectClass: top
objectClass: organizationalUnit
dn: ou=group,dc=example,dc=com
ou: group
objectClass: top
objectClass: organizationalUnit
Next we are going to import that ldif file using ldapadd command. We are going
to authenticate as the admin user we created in the config.ldif
file (cn=Manager),
so we need to enter that password that we created for that user. This will create
the upper level containers
[root@server1 ~]# ldapadd -x -W -D "cn=Manager,dc=example,dc=com" -f structure.ldif
Enter LDAP Password:
adding new entry "dc=example,dc=com"
adding new entry "ou=people,dc=example,dc=com"
adding new entry "ou=group,dc=example,dc=com"
If we want to check if we have the entries in correctly we can do an ldapsearch
[root@server1 ~]# ldapsearch -x -W -D "cn=Manager,dc=example,dc=com" -b "dc=example,dc=com" -s sub "(objectclass=organizationalUnit)"
Enter LDAP Password:
# extended LDIF
#
# LDAPv3
# base <dc=example,dc=com> with scope subtree
# filter: (objectclass=organizationalUnit)
# requesting: ALL
#
# people, example.com
dn: ou=people,dc=example,dc=com
ou: people
objectClass: top
objectClass: organizationalUnit
# group, example.com
dn: ou=group,dc=example,dc=com
ou: group
objectClass: top
objectClass: organizationalUnit
# search result
search: 2
result: 0 Success
# numResponses: 3
# numEntries: 2
Now that we have our top level structures of our directory in place we can proceed with creating groups and users. We are going to create the groups first so that they are in place and it is easy for us to add new users to those groups
We need to create a group.ldif file with the following contents (obtained from course files)
[root@server1 ~]# vi group.ldif
dn: cn=ldapusers,ou=group,dc=example,dc=com
objectClass: posixGroup
cn: ldapusers
gidNumber: 4000
In the above config we are creating a group called ldapusers
(cn=ldapusers
).
It is being created in the group
container (ou=group
) within domain dc=example
.
The object class is posixGroup and this time because everything does inherit from "top"
it is unneccessary to add that in, it's implied.
The common name (cn
) is ldapusers.
We have to have a gidNumber
. We are starting with 4000 as the starting group id number
just to be pretty clear and distinct from the other group ids that we created on our local system.
To create the group specified above, we run this command, entering the password of the
ldap admin user (cn=Manager
)
[root@server1 ~]# ldapadd -x -W -D "cn=Manager,dc=example,dc=com" -f group.ldif
Enter LDAP Password:
adding new entry "cn=ldapusers,ou=group,dc=example,dc=com"
Having created our group now we can go ahead and create our users. Users have a little bit more properties than groups. We can use migration tools to held add in new users on our system into LDAP, but based on the structure of existing users and they will add in the structure that we need, but we just have to make sure that is set correctly with the configuration file.
First, edit the /etc/share/migrationtools/migratio_common.ph
file and change DEFAULT_MAIL_DOMAIN
and DEFAULT_BASE
to the following values;
# Default DNS domain
$DEFAULT_MAIL_DOMAIN = "example.com";
# Default base
$DEFAULT_BASE = "dc=example,dc=com";
Next we are going to grab an existing user, centos
from our /etc/passwd
and just
copy the details of that user into a new file ~/passwd
[root@server1 ~]# grep centos /etc/passwd > passwd
[root@server1 ~]# cat passwd
centos:x:1000:1000:centos:/home/centos:/bin/bash
Then we can create a user.ldif template file based off the centos user using the migrate_passwd.pl perl script
[root@server1 ~]# /usr/share/migrationtools/migrate_passwd.pl passwd user.ldif
Now that we have a user.ldif template we can use that to add in new users. We just
need to edit the users.ldif file and replace the references to the centos user to the new
user. Fields such as dn
, uid
, cn
, uidNumber
, gidNumber
, homeDirectory
and gecos
need to be amended to reflect the new user.
Then we can use the ldapadd command to add the user based of the user.ldif template.
[root@server1 ~]# ldapadd -x -W -D "cn=Manager,dc=example,dc=com" -f user.ldif
Enter LDAP Password:
adding new entry "uid=fred,ou=People,dc=example,dc=com"
We have our directory structure up and running. Now that we our LDAP server set up we can move on and demonstrate how to set up authentication against an OpenLDAP server.
We are going to use a separate server to the one that we installed the LDAP Server on. Log on to server2
First we are going to append an entry into our /etc/hosts file for the server where the LDAP Server is installed, which in our case is server1.example.com. Ping it by it's hostname to ensure we can hit it.
[root@server2 ~]# echo "192.168.99.107 server1.example.com" >> /etc/hosts
[root@server2 ~]# ping server1.example.com
PING server1.example.com (192.168.99.107) 56(84) bytes of data.
64 bytes from server1.example.com (192.168.99.107): icmp_seq=1 ttl=64 time=0.984 ms
64 bytes from server1.example.com (192.168.99.107): icmp_seq=2 ttl=64 time=0.637 ms
Next we are going to install oddjob-mkhomedir to ensure our home directories get created at login time. When it is installed we enable and start the oddjobd service
[root@server2 ~]# yum install oddjob-mkhomedir
root@server2 ~]# systemctl start oddjobd
[root@server2 ~]# systemctl enable oddjobd
Created symlink from /etc/systemd/system/multi-user.target.wants/oddjobd.service to /usr/lib/systemd/system/oddjobd.service.
[root@server2 ~]# systemctl status oddjobd
● oddjobd.service - privileged operations for unprivileged applications
Loaded: loaded (/usr/lib/systemd/system/oddjobd.service; enabled; vendor preset: disabled)
Active: active (running) since Sun 2021-03-28 12:23:54 IST; 12s ago
Main PID: 1502 (oddjobd)
CGroup: /system.slice/oddjobd.service
└─1502 /usr/sbin/oddjobd -n -p /var/run/oddjobd.pid -t 300
Then we are going to install some openldap client tools
[root@server2 ~]# yum install openldap-clients nss-pam-ldapd
Once installed we need to configure the client. There are two ways
METHOD 1 : authconfig-tui
This is a terminal menu-system tool that allows you to configure your ldap client.
On the first screen you need to enable "Use LDAP" and "Use LDAP Authentication" in
addition to what is already enabled on that screen.
On the second screen, don't enable "Use TLS" but change the Server to "ldap://server1.example.com"
Keep the Base DN to what it is "dc=example,dc=com". Click on Ok to save the settings and
exit the authconfig-tui
tool
[root@server2 ~]# authconfig-tui
METHOD 2 : authconfig
This is a command line tool that does the same as the tool above, but without the
nice menu system
[root@server2 ~]# authconfig --enableldap --ldapserver=server1.example.com --ldapbasedn="dc=example,dc=com" --enablemkhomedir --update
If we grep the /etc/nsswitch.conf
file we will see that passwd
(the user DB) has been
enabled for ldap
[root@server2 ~]# grep passwd /etc/nsswitch.conf
#passwd: db files nisplus nis
passwd: files sss ldap
If we look at passwd using the getent
tool, we'll see the user we created via ldap at
the bottom of the list
[root@server2 ~]# getent passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
...
...
fred:x:4000:4000:fred bloggs:/home/fred:/bin/bash
Finally, if we login as this new user, fred, we will see that it's home directory
will be created and if we run the id
command we will see that it is part of the
ldapusers group we created earlier.
[root@server2 ~]# su - fred
Creating home directory for fred.
[fred@server2 ~]$ id
uid=4000(fred) gid=4000(ldapusers) groups=4000(ldapusers) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
We've set up the LDAP client on server2, but not on server1 (which is where the LDAP
server is installed). This means that we'll be able to easily list our ldap users and
groups on server2 using the standard posix tools such as getent
, but not on server1.
On the server that the LDAP client is installed, you can run getent passwd
and you'll be able to see users that are in the LDAP database. The same thing with
getent group
, you'll be able to see the LDAP groups that have been creatd.
If you can see these users in the /etc/passwd
file on a server, it means that those
users can login to those servers.
This capability has been setup through the name service switch file. If you grep the /etc/nsswitch.conf file for "ldap" you can see what system entities have been enabled for ldap
[root@server2 ~]# grep ldap /etc/nsswitch.conf
passwd: files sss ldap
shadow: files sss ldap
group: files sss ldap
netgroup: files sss ldap
automount: files ldap
If we don't want ldap enabled for some of these we can turn it off by editing the nsswitch.conf file and removing ldap for the relevant entities. You might want to do this because the more entities that have information stored in ldap, the more lookups will be made against ldap.
IF you were to login to server1 and run getent passwd
or getent group
, you will
not see the ldap users or groups on that machine, only your local users and groups.
This is because it has not been set up as an LDAP client. As such, LDAP users
will not be able to login to server1. This is best practice behaviour as standard users
should not be able to login to domain controllers.
If you want to query the LDAP directory from a machine with the LDAP client, you don't actually need to login to the LDAP server, you can just run a query.
To initiate an ldapsearch. We can specify the base that we want to search (-b
)
which in our case is "dc=example,dc=com". The response from this command includes a lot of
metadata
[root@server2 ~]# ldapsearch -x -H ldap://server1.example.com -b dc=example,dc=com
# extended LDIF
#
# LDAPv3
# base <dc=example,dc=com> with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#
# example.com
dn: dc=example,dc=com
dc: example
objectClass: top
objectClass: domain
# people, example.com
dn: ou=people,dc=example,dc=com
ou: people
objectClass: top
objectClass: organizationalUnit
# group, example.com
dn: ou=group,dc=example,dc=com
ou: group
objectClass: top
objectClass: organizationalUnit
...
...
# search result
search: 2
result: 0 Success
# numResponses: 6
# numEntries: 5
By adding the -LLL
flag to our command we will just get back the data from our
search, minus the metadata.
[root@server2 ~]# ldapsearch -x -LLL -H ldap://server1.example.com -b dc=example,dc=com
dn: dc=example,dc=com
dc: example
objectClass: top
objectClass: domain
dn: ou=people,dc=example,dc=com
ou: people
objectClass: top
objectClass: organizationalUnit
dn: ou=group,dc=example,dc=com
ou: group
objectClass: top
objectClass: organizationalUnit
dn: cn=ldapusers,ou=group,dc=example,dc=com
objectClass: posixGroup
cn: ldapusers
gidNumber: 4000
dn: uid=fred,ou=people,dc=example,dc=com
uid: fred
cn: fred
objectClass: account
objectClass: posixAccount
objectClass: top
objectClass: shadowAccount
userPassword:: e2NyeXB0fSQ2JDFNTi5lNEkvSC81dzZDUWckUDllN29RdWI5dm0vNnBTQWFvZU9
rbDUxTUMyWnNVbU5pbkNrTmoyMXI2OU1pRGQuUWJpeDhhM0FEdjQuUGxZcGRrVXp1TzBPQVlGT1Jv
LkFteHIzVjE=
shadowLastChange: 18707
shadowMin: 0
shadowMax: 99999
shadowWarning: 7
loginShell: /bin/bash
uidNumber: 4000
gidNumber: 4000
homeDirectory: /home/fred
gecos: fred bloggs
If you just want to return users for your search (identified by objectclass=account)
[root@server2 ~]# ldapsearch -x -LLL -H ldap://server1.example.com -b dc=example,dc=com "(objectclass=account)"
dn: uid=fred,ou=people,dc=example,dc=com
uid: fred
cn: fred
objectClass: account
objectClass: posixAccount
objectClass: top
objectClass: shadowAccount
userPassword:: e2NyeXB0fSQ2JDFNTi5lNEkvSC81dzZDUWckUDllN29RdWI5dm0vNnBTQWFvZU9
rbDUxTUMyWnNVbU5pbkNrTmoyMXI2OU1pRGQuUWJpeDhhM0FEdjQuUGxZcGRrVXp1TzBPQVlGT1Jv
LkFteHIzVjE=
shadowLastChange: 18707
shadowMin: 0
shadowMax: 99999
shadowWarning: 7
loginShell: /bin/bash
uidNumber: 4000
gidNumber: 4000
homeDirectory: /home/fred
dn: uid=sally,ou=people,dc=example,dc=com
gecos: fred bloggs
You can combine filter conditions using the &
. You can also use |
to
create an OR condition
root@server2 ~]# ldapsearch -x -LLL -H ldap://server1.example.com -b dc=example,dc=com "(&(objectclass=account)(uid=fred))"
dn: uid=fred,ou=people,dc=example,dc=com
uid: fred
cn: fred
objectClass: account
objectClass: posixAccount
objectClass: top
objectClass: shadowAccount
userPassword:: e2NyeXB0fSQ2JDFNTi5lNEkvSC81dzZDUWckUDllN29RdWI5dm0vNnBTQWFvZU9
rbDUxTUMyWnNVbU5pbkNrTmoyMXI2OU1pRGQuUWJpeDhhM0FEdjQuUGxZcGRrVXp1TzBPQVlGT1Jv
LkFteHIzVjE=
shadowLastChange: 18707
shadowMin: 0
shadowMax: 99999
shadowWarning: 7
loginShell: /bin/bash
uidNumber: 4000
gidNumber: 4000
homeDirectory: /home/fred
gecos: fred bloggs
[root@server2 ~]# ldapsearch -x -LLL -H ldap://server1.example.com -b dc=example,dc=com "(&(objectclass=account)(uid=fred))" uidNumber uid
dn: uid=fred,ou=people,dc=example,dc=com
uid: fred
uidNumber: 4000
If you just want certain attributes to be returned for your search, you can list the attributes you want
[root@server2 ~]# ldapsearch -x -LLL -H ldap://server1.example.com -b dc=example,dc=com "(&(objectclass=account)(uid=fred))" uidNumber uid
dn: uid=fred,ou=people,dc=example,dc=com
uid: fred
uidNumber: 4000
Finally, if you want to use the output of a search as a template to create new users, redirect the
search results to an ldif
file, then edit that ldif file and put in your new user's details,
and then add the user to LDAP using ldapadd
.
[root@server2 ~]# ldapsearch -x -LLL -H ldap://server1.example.com -b dc=example,dc=com "(&(objectclass=account)(uid=fred))" > newuser.ldif
[root@server2 ~]# vi !$
vi newuser.ldif
[root@server2 ~]# ldapadd -x -W -D cn=Manager,dc=example,dc=com -f newuser.ldif
Enter LDAP Password:
adding new entry "uid=sally,ou=people,dc=example,dc=com"
If you run getent passwd
you can see your new user. You can now login as the
new user
[root@server2 ~]# getent passwd
root:x:0:0:root:/root:/bin/bash
...
...
fred:x:4000:4000:fred bloggs:/home/fred:/bin/bash
sally:x:4001:4000:sally bloggs:/home/sally:/bin/bash
Ensuring that Kerberos servers and clients are in time sync is important and to that end we will install NTP on both server1 and server2. We will configure server1 to get time from an external time source and then we configure server2 to collect time from the server1 machine.
[root@server1 ~]# yum install -y ntp
Once installed we can configure it's config file. We want to ensure that machines on the
same network can access our this server for NTP pursoses. The NTP config is located at
/etc/ntp.comf
. We want uncomment the restrict directive for the local network and change
the subnet mask. This will allow other machines to query the service and we can set up
peer synchronization.
[root@server1 ~]# vi /etc/ntp.conf
...
# Hosts on local network are less restricted.
restrict 192.168.99.0 mask 255.255.255.0 nomodify notrap
Then we can enable and start the ntpd
service
[root@server1 ~]# systemctl enable ntpd
Created symlink from /etc/systemd/system/multi-user.target.wants/ntpd.service to /usr/lib/systemd/system/ntpd.service.
[root@server1 ~]# systemctl start ntpd
[root@server1 ~]# systemctl status ntpd
● ntpd.service - Network Time Service
Loaded: loaded (/usr/lib/systemd/system/ntpd.service; enabled; vendor preset: disabled)
Active: active (running) since Sun 2021-03-28 15:39:01 IST; 4s ago
Process: 5292 ExecStart=/usr/sbin/ntpd -u ntp:ntp $OPTIONS (code=exited, status=0/SUCCESS)
Main PID: 5293 (ntpd)
CGroup: /system.slice/ntpd.service
└─5293 /usr/sbin/ntpd -u ntp:ntp -g
Mar 28 15:39:01 server1.example.com ntpd[5293]: Listen normally on 2 lo 127.0.0.1 UDP 123
...
We can run an ntpq query to ensure we're up and running. The server with the * is the server we're currently synchronizing with.
[root@server1 ~]# ntpq -p
remote refid st t when poll reach delay offset jitter
==============================================================================
+stratum2-3.ntp. 129.70.137.82 2 u 2 64 1 44.346 0.545 6.999
*ec2-52-17-30-11 89.101.218.6 2 u 1 64 1 15.390 -3.863 1.088
+chris.magnet.ie 88.81.100.130 2 u - 64 1 14.422 -0.832 7.032
Then we need to add a rule to our firewall to allow NTP traffic in
[root@server1 ~]# firewall-cmd --add-service=ntp --permanent
success
[root@server1 ~]# firewall-cmd --reload
success
Then we need to add an entry to our /etc/hosts
file for server2, so our hosts
file should have the following
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.99.107 server1 server1.example.com
192.168.99.105 server2 server2.example.com
On server2, we need to do some very similar setup, other than the fact that the NTP server we are syncing up with is our server1 machine.
First we install ntp
[root@server2 ~]# yum install -y ntp
Then we change the ntp configuration, and change the server section to that it
points to our server1 machine as the prefered machine (denoted by prefer
), and
that we have left just one external ntp server as a backup if server1 is unavailable
for any reason. iburst
just means to synchronize quickly.
[root@server2 ~]# vi /etc/ntp.conf
...
# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (http://www.pool.ntp.org/join.html).
server server1.example.com iburst prefer
server 3.centos.pool.ntp.org iburst
...
Then we enable and start the ntp service on server2
[root@server2 ~]# systemctl enable ntpd
Created symlink from /etc/systemd/system/multi-user.target.wants/ntpd.service to /usr/lib/systemd/system/ntpd.service.
[root@server2 ~]# systemctl start ntpd
[root@server2 ~]# systemctl status ntpd
● ntpd.service - Network Time Service
Loaded: loaded (/usr/lib/systemd/system/ntpd.service; enabled; vendor preset: disabled)
Active: active (running) since Sun 2021-03-28 15:44:02 IST; 5s ago
Process: 4284 ExecStart=/usr/sbin/ntpd -u ntp:ntp $OPTIONS (code=exited, status=0/SUCCESS)
Main PID: 4285 (ntpd)
CGroup: /system.slice/ntpd.service
└─4285 /usr/sbin/ntpd -u ntp:ntp -g
...
We don't need to make any changes to the firewall settings, there will be no incoming ntp traffic to this machine.
Finally if we do an ntp query, we can see that the server we are synced to for NTP purposes is our server1 machine
[root@server2 ~]# ntpq -p
remote refid st t when poll reach delay offset jitter
==============================================================================
*server1.example 52.17.30.119 3 u 10 64 1 0.487 11.327 0.067
ec2-52-17-231-7 193.120.142.71 2 u 9 64 1 15.347 -1.422 16.045
This involves installing the kadmin server and the KDC on server1
The first thing we need to do is install a Random Number Generator for KDC
[root@server1 ~]# yum install -y rng-tools.x86_64
Then we need to start the rngd service. Before we do that, we need to edit the rngd.service file and make sure it uses the /dev/urandom device.
[root@server1 ~]# vi /usr/lib/systemd/system/rngd.service
[Service]
ExecStart=/sbin/rngd -f -r /dev/urandom
Then we can start and enable the rngd service
[root@server1 /usr/lib/systemd/system]# systemctl start rngd
[root@server1 /usr/lib/systemd/system]# systemctl status rngd
● rngd.service - Hardware RNG Entropy Gatherer Daemon
Loaded: loaded (/usr/lib/systemd/system/rngd.service; enabled; vendor preset: enabled)
Active: active (running) since Wed 2021-03-31 21:05:11 IST; 11s ago
Main PID: 1916 (rngd)
CGroup: /system.slice/rngd.service
└─1916 /sbin/rngd -f -r /dev/urandom
...
[root@server1 /usr/lib/systemd/system]# systemctl enable rngd
Now we can do our kerberos installation, installing the kerberos server, client and PAM modules
[root@server1 /usr/lib/systemd/system]# yum install -y krb5-server krb5-workstation.x86_64 pam_krb5
The Kerberos server config is located in /var/kerberos/krb5kdc directory, and if we cat the two files in that dir, we can see that they are set up to use EXAMPLE.COM as the realm. This is fortunate for us because example.com is the domain we are using, so we don't need to make any changes here.
[root@server1 /var/kerberos/krb5kdc]# cat kadm5.acl
*/admin@EXAMPLE.COM *
[root@server1 /var/kerberos/krb5kdc]# cat kdc.conf
[kdcdefaults]
kdc_ports = 88
kdc_tcp_ports = 88
[realms]
EXAMPLE.COM = {
#master_key_type = aes256-cts
acl_file = /var/kerberos/krb5kdc/kadm5.acl
dict_file = /usr/share/dict/words
admin_keytab = /var/kerberos/krb5kdc/kadm5.keytab
supported_enctypes = aes256-cts:normal aes128-cts:normal des3-hmac-sha1:normal arcfour-hmac:normal camellia256-cts:normal camellia128-cts:normal des-hmac-sha1:normal des-cbc-md5:normal des-cbc-crc:normal
}
The Kerberos client config is located in /etc/krb5.conf
# Configuration snippets may be placed in this directory as well
includedir /etc/krb5.conf.d/
[logging]
default = FILE:/var/log/krb5libs.log
kdc = FILE:/var/log/krb5kdc.log
admin_server = FILE:/var/log/kadmind.log
[libdefaults]
dns_lookup_realm = false
ticket_lifetime = 24h
renew_lifetime = 7d
forwardable = true
rdns = false
pkinit_anchors = FILE:/etc/pki/tls/certs/ca-bundle.crt
# default_realm = EXAMPLE.COM
default_ccache_name = KEYRING:persistent:%{uid}
[realms]
# EXAMPLE.COM = {
# kdc = kerberos.example.com
# admin_server = kerberos.example.com
# }
[domain_realm]
# .example.com = EXAMPLE.COM
# example.com = EXAMPLE.COM
We need to edit this file and uncomment the default_realm, the realms block and the domain_realm block. And we need to change the kdc and admin_server to point at server1.example.com. The file should look like the following.
# Configuration snippets may be placed in this directory as well
includedir /etc/krb5.conf.d/
[logging]
default = FILE:/var/log/krb5libs.log
kdc = FILE:/var/log/krb5kdc.log
admin_server = FILE:/var/log/kadmind.log
[libdefaults]
dns_lookup_realm = false
ticket_lifetime = 24h
renew_lifetime = 7d
forwardable = true
rdns = false
pkinit_anchors = FILE:/etc/pki/tls/certs/ca-bundle.crt
default_realm = EXAMPLE.COM
default_ccache_name = KEYRING:persistent:%{uid}
[realms]
EXAMPLE.COM = {
kdc = server1.example.com
admin_server = server1.example.com
}
[domain_realm]
.example.com = EXAMPLE.COM
example.com = EXAMPLE.COM
Now we need to initialize our KDC databases for our realm EXAMPLE.COM. The DB master key password is "aoraki"
[root@server1 /var/kerberos/krb5kdc]# kdb5_util create -s -r EXAMPLE.COM
Loading random data
Initializing database '/var/kerberos/krb5kdc/principal' for realm 'EXAMPLE.COM',
master key name 'K/M@EXAMPLE.COM'
You will be prompted for the database Master Password.
It is important that you NOT FORGET this password.
Enter KDC database master key:
Re-enter KDC database master key to verify:
If we look at the contents of our current dir we can see the new db files that have been created
[root@server1 /var/kerberos/krb5kdc]# ls
kadm5.acl kdc.conf principal principal.kadm5 principal.kadm5.lock principal.ok
Finally we need to start the kadmin and KDC services
root@server1 /var/kerberos/krb5kdc]# systemctl start krb5kdc kadmin
[root@server1 /var/kerberos/krb5kdc]# systemctl enable krb5kdc kadmin
Created symlink from /etc/systemd/system/multi-user.target.wants/krb5kdc.service to /usr/lib/systemd/system/krb5kdc.service.
Created symlink from /etc/systemd/system/multi-user.target.wants/kadmin.service to /usr/lib/systemd/system/kadmin.service.
[root@server1 /var/kerberos/krb5kdc]# systemctl status krb5kdc kadmin
● krb5kdc.service - Kerberos 5 KDC
Loaded: loaded (/usr/lib/systemd/system/krb5kdc.service; enabled; vendor preset: disabled)
Active: active (running) since Wed 2021-03-31 21:23:02 IST; 13s ago
Main PID: 2306 (krb5kdc)
CGroup: /system.slice/krb5kdc.service
└─2306 /usr/sbin/krb5kdc -P /var/run/krb5kdc.pid
Mar 31 21:23:02 server1.example.com systemd[1]: Starting Kerberos 5 KDC...
Mar 31 21:23:02 server1.example.com systemd[1]: Can't open PID file /var/run/krb5kdc.pid (yet?) after start: No such file or directory
Mar 31 21:23:02 server1.example.com systemd[1]: Started Kerberos 5 KDC.
● kadmin.service - Kerberos 5 Password-changing and Administration
Loaded: loaded (/usr/lib/systemd/system/kadmin.service; enabled; vendor preset: disabled)
Active: active (running) since Wed 2021-03-31 21:23:02 IST; 13s ago
Main PID: 2307 (kadmind)
CGroup: /system.slice/kadmin.service
└─2307 /usr/sbin/kadmind -P /var/run/kadmind.pid
Mar 31 21:23:02 server1.example.com systemd[1]: Starting Kerberos 5 Password-changing and Administration...
Mar 31 21:23:02 server1.example.com systemd[1]: Can't open PID file /var/run/kadmind.pid (yet?) after start: No such file or directory
Mar 31 21:23:02 server1.example.com systemd[1]: Started Kerberos 5 Password-changing and Administration.
We now need to add principals. We need to first add some firewall rules we can connect through to our server (ensuring to reload the firewall service at the end)
[root@server1 /var/kerberos/krb5kdc]# firewall-cmd --add-service=kpasswd --permanent
success
[root@server1 /var/kerberos/krb5kdc]# firewall-cmd --add-service=kerberos --permanent
success
[root@server1 /var/kerberos/krb5kdc]# firewall-cmd --add-port=749/tcp --permanent
success
[root@server1 /var/kerberos/krb5kdc]# firewall-cmd --reload
success
We are now going to do a bit of management so that we can get up and running with the local machine. We are going to use kadmin.local to do this (which just authenticates as the root user). This gives us the kadmin.local prompt.
[root@server1 ~]# kadmin.local
Authenticating as principal root/admin@EXAMPLE.COM with password.
To list kerberos principals, run the listprincs cmd
kadmin.local: listprincs
K/M@EXAMPLE.COM
kadmin/admin@EXAMPLE.COM
kadmin/changepw@EXAMPLE.COM
kadmin/server1@EXAMPLE.COM
kiprop/server1@EXAMPLE.COM
krbtgt/EXAMPLE.COM@EXAMPLE.COM
To add a new principal, in our case the root user. You need to add a password for the user which can be a separate password to the normal root user (and probably should be). This new principal will be able to connect locally and remotely to the administration service.
kadmin.local: addprinc root/admin
WARNING: no policy specified for root/admin@EXAMPLE.COM; defaulting to no policy
Enter password for principal "root/admin@EXAMPLE.COM":
Re-enter password for principal "root/admin@EXAMPLE.COM":
Principal "root/admin@EXAMPLE.COM" created.
If we want to create a principal for the centos user, but not make him an admin
kadmin.local: addprinc centos
WARNING: no policy specified for centos@EXAMPLE.COM; defaulting to no policy
Enter password for principal "centos@EXAMPLE.COM":
Re-enter password for principal "centos@EXAMPLE.COM":
Principal "centos@EXAMPLE.COM" created.
We also need to create a principal for any hosts that are using the service. So we'll create one for server1.example.com. We'll generate a random key for authentication purposes
kadmin.local: addprinc -randkey host/server1.example.com
WARNING: no policy specified for host/server1.example.com@EXAMPLE.COM; defaulting to no policy
Principal "host/server1.example.com@EXAMPLE.COM" created.
If we now list our principals again we'll see the new principals we added
kadmin.local: listprincs
K/M@EXAMPLE.COM
centos@EXAMPLE.COM
host/server1.example.com@EXAMPLE.COM
kadmin/admin@EXAMPLE.COM
kadmin/changepw@EXAMPLE.COM
kadmin/server1@EXAMPLE.COM
kiprop/server1@EXAMPLE.COM
krbtgt/EXAMPLE.COM@EXAMPLE.COM
root/admin@EXAMPLE.COM
We need to create a copy of the keytab file for our host, server1. We can do this using ktadd
kadmin.local: ktadd host/server1.example.com
Entry for principal host/server1.example.com with kvno 2, encryption type aes256-cts-hmac-sha1-96 added to keytab FILE:/etc/krb5.keytab.
Entry for principal host/server1.example.com with kvno 2, encryption type aes128-cts-hmac-sha1-96 added to keytab FILE:/etc/krb5.keytab.
Entry for principal host/server1.example.com with kvno 2, encryption type des3-cbc-sha1 added to keytab FILE:/etc/krb5.keytab.
Entry for principal host/server1.example.com with kvno 2, encryption type arcfour-hmac added to keytab FILE:/etc/krb5.keytab.
Entry for principal host/server1.example.com with kvno 2, encryption type camellia256-cts-cmac added to keytab FILE:/etc/krb5.keytab.
Entry for principal host/server1.example.com with kvno 2, encryption type camellia128-cts-cmac added to keytab FILE:/etc/krb5.keytab.
Entry for principal host/server1.example.com with kvno 2, encryption type des-hmac-sha1 added to keytab FILE:/etc/krb5.keytab.
Entry for principal host/server1.example.com with kvno 2, encryption type des-cbc-md5 added to keytab FILE:/etc/krb5.keytab.
Now that our kerberos server is up and running we now need to look at "kerberizing" our services. The real benefit of using kerberos across all your servers is that you can authenticate just once to obtain a kerberos token, and then use that token to log into all the servers that support kerberos.
Having set up our principals we are able to user server1 as a kerberos-based host.
We will kerberize the ssh client first, make the following changes to the ssh_config
file
vi /etc/ssh/ssh_config
Uncomment out the following settings and change their values to yes
GSSAPIAuthentication yes
GSSAPIDelegateCredentials yes
Reload sshd and then enable kerberos for ssh
[root@server1 ~]# systemctl reload sshd
[root@server1 ~]# authconfig --enablekrb5 --update
To test that you can use kerberos to ssh into a box, you first need to log in as a non-root user
[root@server1 ~]# su -l centos
Last login: Thu Apr 8 21:44:08 IST 2021 from server1 on pts/1
Then if you try run the klist command you can see that the centos user does not have any kerberos tokens.
[centos@server1 ~]$ klist
klist: Credentials cache keyring 'persistent:1000:1000' not found
To obtain a kerberos token that can be presented on login run the kinit command and enter the password specified when the kerberos principals were set up
[centos@server1 ~]$ kinit
Password for centos@EXAMPLE.COM:
If you run klist again you will now see a token as been created. It can be used for the next 24 hours for authentication
[centos@server1 ~]$ klist
Ticket cache: KEYRING:persistent:1000:1000
Default principal: centos@EXAMPLE.COM
Valid starting Expires Service principal
29/04/21 21:54:12 30/04/21 21:54:12 krbtgt/EXAMPLE.COM@EXAMPLE.COM
You can then ssh to server1 and will not be prompted for a password
[centos@server1 ~]$ ssh server1.example.com
Last login: Thu Apr 29 21:54:00 2021
You can destroy your token if you don't want to keep it around any more
[centos@server1 ~]$ kdestroy
[centos@server1 ~]$ klist
klist: Credentials cache keyring 'persistent:1000:1000' not found
You can configure additional hosts to accept kerberos authentication.
You need to edit the /etc/hosts file
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.99.107 server1.example.com
192.168.99.105 server2.example.com
You need to install the kerberos utilities. You don't need to install the kerberos server on any additional hosts, just the kerberos client
[root@server2 ~]# yum install -y krb5-workstation pam_krb5
Copy the krb5 conf file from server1 machine. This sets up the realms and domain names
[root@server2 ~]# scp centos@server1.example.com:/etc/krb5.conf /etc
We can log in to the remote kerberos system using kadmin
. This will connect to the
the "remote" kerberos server (On the kerberos service you use kadmin.local
. You will
be prompted for the root user kerberos principal
[root@server2 ~]# kadmin
Authenticating as principal root/admin@EXAMPLE.COM with password.
Password for root/admin@EXAMPLE.COM:
Once in kadmin, you can run commands against the remote kerberos system, such as listprincs
kadmin: listprincs
K/M@EXAMPLE.COM
centos@EXAMPLE.COM
host/server1.example.com@EXAMPLE.COM
kadmin/admin@EXAMPLE.COM
kadmin/changepw@EXAMPLE.COM
kadmin/server1@EXAMPLE.COM
kiprop/server1@EXAMPLE.COM
krbtgt/EXAMPLE.COM@EXAMPLE.COM
root/admin@EXAMPLE.COM
We need to add in a host for server2. Afterwards if you run listprincs
again
you will see the new host added
kadmin: addprinc -randkey host/server2.example.com
WARNING: no policy specified for host/server2.example.com@EXAMPLE.COM; defaulting to no policy
Principal "host/server2.example.com@EXAMPLE.COM" created.
kadmin: listprincs
K/M@EXAMPLE.COM
centos@EXAMPLE.COM
host/server1.example.com@EXAMPLE.COM
host/server2.example.com@EXAMPLE.COM
kadmin/admin@EXAMPLE.COM
kadmin/changepw@EXAMPLE.COM
kadmin/server1@EXAMPLE.COM
kiprop/server1@EXAMPLE.COM
krbtgt/EXAMPLE.COM@EXAMPLE.COM
root/admin@EXAMPLE.COM
We now need to add the ktab file, which will add the key pairs through to the local file
kadmin: ktadd host/server2.example.com
Entry for principal host/server2.example.com with kvno 2, encryption type aes256-cts-hmac-sha1-96 added to keytab FILE:/etc/krb5.keytab.
Entry for principal host/server2.example.com with kvno 2, encryption type aes128-cts-hmac-sha1-96 added to keytab FILE:/etc/krb5.keytab.
Entry for principal host/server2.example.com with kvno 2, encryption type des3-cbc-sha1 added to keytab FILE:/etc/krb5.keytab.
Entry for principal host/server2.example.com with kvno 2, encryption type arcfour-hmac added to keytab FILE:/etc/krb5.keytab.
Entry for principal host/server2.example.com with kvno 2, encryption type camellia256-cts-cmac added to keytab FILE:/etc/krb5.keytab.
Entry for principal host/server2.example.com with kvno 2, encryption type camellia128-cts-cmac added to keytab FILE:/etc/krb5.keytab.
Entry for principal host/server2.example.com with kvno 2, encryption type des-hmac-sha1 added to keytab FILE:/etc/krb5.keytab.
Entry for principal host/server2.example.com with kvno 2, encryption type des-cbc-md5 added to keytab FILE:/etc/krb5.keytab.
We will configure the ssh client to enable kerberos authentication, make the following changes to the ssh_config
file
vi /etc/ssh/ssh_config
Uncomment out the following settings and change their values to yes
GSSAPIAuthentication yes
GSSAPIDelegateCredentials yes
Reload sshd and then enable kerberos for ssh
[root@server2 ~]# systemctl reload sshd
[root@server2 ~]# authconfig --enablekrb5 --update
Exit out of root and check if you have any kerberos tokens
[root@server2 ~]# exit
logout
[centos@server2 ~]$ klist
klist: Credentials cache keyring 'persistent:1000:1000' not found
We can generate a kerberos keyring using kinit
[centos@server2 ~]$ kinit
Password for centos@EXAMPLE.COM:
[centos@server2 ~]$ klist
Ticket cache: KEYRING:persistent:1000:1000
Default principal: centos@EXAMPLE.COM
Valid starting Expires Service principal
04/05/21 21:12:27 05/05/21 21:12:27 krbtgt/EXAMPLE.COM@EXAMPLE.COM
You can then log into your kerberos enabled servers without having to provide a password or use a ssk public/private key pair
[centos@server2 ~]$ ssh server1.example.com
Last login: Tue May 4 20:54:25 2021 from 192.168.99.1
[centos@server2 ~]$ ssh server2.example.com
Last login: Tue May 4 20:54:25 2021 from 192.168.99.1