For the purpose of this article, I give you an existing context.

Imagine you have one server share.yourdomain.com that is an NFS server, and that shares a directory that we have named /heapdump

Now imagine you have ten other linux servers with one or several tomcat instances.

On each of these servers, each tomcat instance is exectuded with its own linux user. For instance, you’ll have on each server /home/user1/<tomcat_home>, /home/user2/<tomcat_home>and /home/user3/<tomcat_home>

Difference between SGID on a file and SGID on a directory

SGID, which stands for Set Group ID, is another special permission that can be applied to executable files and directories.

SGID on a file

When an executable file has the SGID permission enabled, it allows users who execute the file to temporarily assume the group ownership of the file.

SGID on a directory

when a file is created in a directory with the SGID, then this file will be assigned the directory group by default.

In addition, if another directory is created in the directory with SGID rights, this sub-directory will also have these rights.

Configuration of the shared directory on the nfs server, apply SGID on the shared directory

First we define a new group called tomcat with group id 2001 (ensure to choose a free group id).

And we will create this group on all the other servers, with the same gid 2001.

Also we set the SGID permission on the shared directory. When SGID permission is set on a directory, then every children files or children directories will inherit owner, group, and permissions or the parent directory.

groupadd -g 2001 tomcat
chmod g+s /heapdump

Here after we set the SGID permission, we can see it in the permissions column: drwxrws— (the ‘s’).

Now we must authorize the other servers to access this directory with read and write permissions. For instance, if all the machines are in the private network /heapdump 10.0.3.0/24:

Edit the /etc/exports file and add the following line:

/heapdump 10.0.3.0/24(rw,sync,no_subtree_check)

Configuration of the mount point on the nfs clients

Create the same linux group, especially with the same group id 2001. And add all users that should use your mount point inside this group.

And then create your mount point.

groupadd -g 2001 tomcat
usermod -aG tomcat user1
usermod -aG tomcat user2
usermod -aG tomcat user3
mkdir /mnt/heapdump

Now edit the /etc/fstab file in order the mount point is set at linux boot, add the following line:

10.0.3.189:/heapdump /mnt/heapdump nfs rw,nofail 0 0

Now execute:

systemctl daemon-reload
mount -a
[root@yourhost ~]# ls -l /mnt/
total 4
drwxrws---. 3 root tomcat 4096 19 juil. 16:15 heapdump

As you can the SGID is represented by a ‘s’ in the permissions column.

Now ensure that the users user1, user2, and user3 can read and write inside /mnt/heapdump.
And verify that when you create a directory or a file with user1 inside /mnt/heapdump the permissions are well inherited.

[user1@yourhost ~]$ ls -l /mnt/heapdump/
total 16
drwx------. 2 root root 16384 17 juil. 11:14 lost+found
[user1@yourhost ~]$ mkdir /mnt/heapdump/test
[user1@yourhost ~]$ ls -l /mnt/heapdump/
total 20
drwx------. 2 root   root   16384 17 juil. 11:14 lost+found
drwxrwsr-x. 2 user1  tomcat  4096 19 juil. 17:08 test
[user1@yourhost ~]$ touch /mnt/heapdump/testfile
[user1@yourhost ~]$ ls -l /mnt/heapdump/
total 20
drwx------. 2 root   root   16384 17 juil. 11:14 lost+found
drwxrwsr-x. 2 user1  tomcat  4096 19 juil. 17:08 test
-rw-rw-r--. 1 user1  tomcat     0 19 juil. 17:08 testfile
[user1@yourhost ~]$

Ansible example

Here some simple ansible tasks to automate the deployment on the NFS shared directory.

Some variables are used:

  • client_nfs_path_heapdump : /mnt/heapdump
  • tomcat_group_name : tomcat
  • tomcat_group_gid : 2001
  • host_nfs : 10.0.3.80
  • tomcat_user : user1

We can imagine that tomcat_user can also be a list of user (item list) and then you would be able to iterate on several user value in order to add each user in the tomcat_group_name.

Of course you can generalize this example with more general variable name. These names have been chosen just to illustrate the previous explaination.

- name: "umount {{ client_nfs_path_heapdump }} in case it is mounted but doesn't have the good permissions"
  shell: umount {{ client_nfs_path_heapdump }}
  ignore_errors: true
  # We ignore errors in case {{ client_nfs_path_heapdump }} is not mounted. Then unmount command will throw an error

- name: "Ensure folder {{ client_nfs_path_heapdump }} exists, and has mode 2770 (2 is for SGID)"
  ansible.builtin.file:
    path: '{{ client_nfs_path_heapdump }}'
    state: directory

- name: "Ensure group 'tomcat' exists with correct gid"
  ansible.builtin.group:
    name: '{{tomcat_group_name}}'
    state: present
    gid: '{{tomcat_group_gid}}'

- name: "Ensure user {{tomcat_user}} is member of {{tomcat_group_name}} group"
  shell: usermod -aG '{{tomcat_group_name}}' '{{tomcat_user}}'

- name: "Ensure {{ host_nfs }}:{{ host_nfs_path_heapdump }} is mounted on {{ client_nfs_path_heapdump }}"
  ansible.posix.mount:
    src: '{{ host_nfs }}:{{ host_nfs_path_heapdump }}'
    path: '{{ client_nfs_path_heapdump }}'
    opts: rw,nofail
    state: mounted
    fstype: nfs

As you can see there is no need to specify inside ansible the SGID permission, because it has only to be done on the shared directory present on the NFS server.