Run scripts when session starts/ends
From BononWiki
Contents |
Different methods
I've written this simple script to run scripts when a session starts or when it ends.
Normally when a session starts, the only script/program started it the login, or the xsession/xinit script in case of a Xsession. The programs started have their own means to run scripts, like bash "starts" (sources) scripts found in /etc/profile.d (as user), and with KDM has the ability to run scripts via the Xstartup script (as root).
I needed a way to start scripts for every environment, so I had a problem here. Till I found out that ConsoleKit has the ability to run a script when a session starts, ends or it's state changes. In the ChangeLog of the source of ConsoleKit:
commit a90ecd3544f03c16f6c5a97aafb1c0b33b58101c
Author: David Zeuthen <davidz@redhat.com>
Date: Thu Oct 4 20:39:09 2007 -0400
add a way to synchronously run programs on session
add/remove/activity_change
This patch adds support for running programs when
1. A session is added
2. A session is removed
3. The activity of a session changes
Executables with the suffix .ck in the directories
$sysconfdir/ConsoleKit/run-session.d
(typically /etc/ConsoleKit/run-session.d)
and
$libdir/ConsoleKit/run-session.d
(typically /usr/lib/ConsoleKit/run-session.d)
will be run on each event. The former directory is meant to be used
for the system administrator and the latter is meant to be used by
programs.
Only when all programs in these directories have run, ConsoleKit will
resume event processing and e.g. broadcast the event on the system
message bus. Hence, this new mechanism can be used to safely
(e.g. without race conditions) perform operations on certain resources
before programs in the desktop session are told they may use them. The
obvious example here is managing ACL's on /dev such that certain
device nodes are only available to users in local and active sessions.
The environment of the program launched is the environment that the
ConsoleKit daemon was launched with and also the following variables
(variables tagged with [*] may not be set).
CK_SESSION_ID
CK_SESSION_TYPE
CK_SESSION_SEAT_ID
CK_SESSION_USER_UID
CK_SESSION_DISPLAY_DEVICE [*]
CK_SESSION_X11_DISPLAY_DEVICE [*]
CK_SESSION_X11_DISPLAY [*]
CK_SESSION_REMOTE_HOST_NAME [*]
CK_SESSION_IS_ACTIVE
CK_SESSION_IS_LOCAL
corresponding to the properties of a Session object in question. Each
program is passed exactly one parameter that can assume one of the
following values:
- session_active_changed: is_active changed
- session_added: the session was added
- session_removed: the session was removed
As a safety hatch, there is a timeout of 15 seconds for each program;
if it hasn't exited within 15 seconds, the daemon will send it a
SIGTERM signal, and move on to the next program. The daemon is still
responsive when the program is running - this is to ensure that the
program itself can call into the org.freedesktop.ConsoleKit service.
Apart from ConsoleKit there are other sollutions which are able to do the same:
pam-script
With pam-script it's possible to run a script in every phase of PAM, like auth and session. In the auth phase the password is set in a environment var. This script is not part of the normal Linux PAM installation.
Previous this module has been available at:
Since september 2011 I've taken over maintenance, and now it's available at:
Get it with git:
git clone git://gitorious.org/pam_script/pam_script.git pam_script cd pam_script
the pammodule will standard look in /etc/security for the scripts:
onauth - when in auth phase onsessionopen - when opening a session onsessionclose -when closing a session
These standards can be overridden when using the parameter onauth="path to auth script", onsessionopen="path to onsessionopen script" and onsessionclose="path to onsessionclose script" resp.
This looks like:
session required pam_script.so or
auth required pam_script.so
Options are:
. seteuidon{open,close,auth}, call seteuid when running external script
. onsessionopen=%script%, default /etc/security/onsessionopen
. onsessionclose=%script%, default /etc/security/onsessionclose
. onauth=%script%, default /etc/security/onauth
. domount, remount the whole system, only used when doing a chroot
. dochroot, do a chroot, the directory to chroot to has to be set by the scripts in the file /var/run/pam-script-info-%pid%, which is set in the environment var PAM_SCRIPT_INFO. When the value CHROOTDIR=/%dirtochrootto% is not found, chroot (and all related actions like mount and unshare) are not performed.
. dounshare, clone the namespace (the mount namespace)
When specifying the seteuid option, or the other do* options like dochroot, without a value, is the same as specifying it like dochroot=1.
Some notes:
as you may have read, this module uses a static table when remounting the system. It should use a dynamic table, not every system uses the same system directories, and not every chroot uses/requires the same remounts. Better should be a map containing what directories to remount.
pam-exec
Almost the same as pam-script is pam-exec:
pam_exec - call an external command
The command will have to be supplied as a parameter.
Installation
Get the source via git:
git clone git://gitorious.org/ck-runsession/ck-runsession.git ck-runsession cd ck-runsession
There is a setup script:
./setup.sh
It will offer a choice, what method to use.
Standard it will install the following directories:
/etc/session.d/added/
/removed/
/scripts/
/session-scripts.conf
When ConsoleKit is chosen, it installs a simple script in /etc/ConsoleKit/run-session.d, with pam_script it installs onsessionopen and onsessionclose in /etc/security.
A simple test script (ck-test.sh) is supplied. This creates the file /tmp/test-ck.log. Usefull to see it's working, but of course has to be removed in production environment.
Whatever method chosen, the effect is the same: when a session start's scripts in /etc/session.d/added and when it ends scripts in /etc/session.d/removed are launched.
HOW DOES IT WORK
a. the scripts check the existence of the directory
$HOME/.session
When not found, the script returns immediatly.
b. depending on what happens, a session start or an end, it looks at scripts it finds at (default) /etc/session.d/added resp /etc/session.d/removed. If configured to do so, it uses the at daemon to run the scripts, and additional the at extension script, you'll find here:
The reason to use the at daemon is to run the scripts using some control, without having to wait for them to finish.
c. the scripts are launched in the order a "ls" command finds them. On my computer the added directory looks like:
sbon [ /etc/session.d/added ]$ ls -al total 8 drwxr-xr-x 2 root root 4096 2011-03-01 10:55 . drwxr-xr-x 5 root root 4096 2010-01-20 11:52 .. lrwxrwxrwx 1 root root 39 2011-04-11 11:21 10W-determine-settings.sh -> /etc/mount.md5key/determine-settings.sh lrwxrwxrwx 1 root root 44 2011-04-11 11:21 11W-launch_session_automount.sh -> ../scripts/start/launch_session_automount.sh lrwxrwxrwx 1 root root 35 2011-04-11 11:21 15W-mount_workspace.sh -> ../scripts/start/mount_workspace.sh lrwxrwxrwx 1 root root 37 2011-04-11 11:21 20D-manage_workspace.sh -> /etc/mount.md5key/manage_workspace.sh lrwxrwxrwx 1 root root 58 2011-04-11 11:21 50-determine_local_devices.sh -> /etc/mount.md5key/scripts-local/determine_local_devices.sh lrwxrwxrwx 1 root root 52 2011-04-11 11:21 70-correct_workspace.sh -> /etc/mount.md5key/scripts-local/correct_workspace.sh
d. apart from the first number (used to get the launch order) an additional character is used here. This indicates the way it should be launched:
W: wait for it to finish via eval:
lrwxrwxrwx 1 root root 39 2011-04-11 11:21 10W-determine-settings.sh lrwxrwxrwx 1 root root 44 2011-04-11 11:21 11W-launch_session_automount.sh lrwxrwxrwx 1 root root 35 2011-04-11 11:21 15W-mount_workspace.sh
This makes the whole process wait for the script to finish. Some script require that. An example is the creation of an environment to chroot to like I'm using in the construction mount.md5key. The actual chroot is done later (via the pam module pam_chroot), but before it the can do so, the environment has to be ready, like remounting of the various directories.
D: launch it directly via at.
lrwxrwxrwx 1 root root 37 2011-04-11 11:21 20D-manage_workspace.sh
This launches the script without waiting, fully async.
Every other script (without extra character or not reckognized) is made part of a list, which is launched with the at extension script. The calling process - here run_scripts.ck - does not wait for the list of scripts to finish - they are launched as "one" - but the scripts themselves are launched in the same order as found in the directory.
lrwxrwxrwx 1 root root 58 2011-04-11 11:21 50-determine_local_devices.sh lrwxrwxrwx 1 root root 52 2011-04-11 11:21 70-correct_workspace.sh
When using ConsoleKit extra actions are taken:
a. the parameter indicates what has happened, a session is added, removed or changed.
b. the script looks only at the values "session_added" and "session_removed". Other values (like session_active_changed) are ignored.
c. the script maintains a simple tree in /tmp/ConsoleKit/{sessions,users} which reflects all the sessions and users logged in:
sbon [ /tmp/ConsoleKit/sessions/Session1 ]$ ls -al total 12 drwxr-xr-x 2 root root 100 2011-07-11 06:01 . drwxr-xr-x 4 root root 80 2011-07-11 06:01 .. -rw-r--r-- 1 root root 3 2011-07-11 06:01 display -rw-r--r-- 1 root root 9 2011-07-11 06:01 session -rw-r--r-- 1 root root 5 2011-07-11 06:01 unixuser
and
sbon [ /tmp/ConsoleKit/users/sbon ]$ ls -al total 0 drwxr-xr-x 2 root root 80 2011-07-11 06:01 . drwxr-xr-x 3 root root 60 2011-07-11 06:01 .. lrwxrwxrwx 1 root root 23 2011-07-11 06:01 Session1 -> ../../sessions/Session1 lrwxrwxrwx 1 root root 23 2011-07-11 06:01 Session2 -> ../../sessions/Session2
I did this cause there was no easy way to see in a moment what sessions there are, and what users.
Doing a chroot
Since the construction I've been working on - mount.md5key/fuse-workspace - needs a chroot when the session starts, it's logical to use the pam_chroot module:
It's a very handy module, which does a chroot to a certain directory, under certain conditions. Look at the webpage for more details, I'm using it as follows:
the pamchroot.so module checks the chroot.conf file in /etc/security what to do. chroot.conf looks like:
# /etc/security/chroot.conf # basic format: # username chroot_dir # foo /home/foo condition /directory/to/chroot/to
My chroot.conf looks like:
sbon [ /etc/security ]$ cat chroot.conf # /etc/security/chroot.conf # basic format: # username chroot_dir # foo /home/foo
@pamchroot /var/lib/workspace/%u/chroot
which means that if the user is part of the group pamchroot, it's doing a chroot to the directory
/var/lib/workspace/%u/chroot
where %u is replaced by the username.
In the condition regular expression are allowed. To make the module treat it as a regular_expression, the parameter use_regex is required. If using extended regular expressions, use the paramter use_ext_regex instead.
Group check (ie user is member of a group) are supported - like above - use the paramter use_groups.
Additional parameters I've used are notfound=success, and onerr=succeed. These make the module to continue, when the "dir to chroot to" cannot be determined, for example in the case of above the user is NOT member of the group pamchroot.
The pam files where pam_chroot.so is stacked look like:
session required pam_chroot.so use_ext_regex use_groups notfound=success onerr=succeed
MAKING USE of pam_succeed_if
. example:
session required pam_script.so runas=root session [default=1 success=ignore] pam_succeed_if.so user ingroup pamchroot session required pam_chroot.so use_ext_regex use_groups notfound=success onerr=succeed
and the chroot.conf:
# /etc/security/chroot.conf # basic format: # username chroot_dir # foo /home/foo
@mount-md5key /var/lib/workspace/%u/chroot
Explenation:
. membership of group pamchroot is required to make use of pam module pam_chroot.so. If not, the module is skipped. . when member of pamchroot, the chroot is done to /var/lib/workspace.. when member of mount-md5key. . (non)membership of groups can be set in one of the scripts launched earlier by pam_script.so.
NAMESPACES in stead of CHROOT
Recent linux systems support the changing of the namespace, which should be much better than a chroot. I'm looking at this if it's worth to use a namespace in stead of chroot.
There is already a PAM module for this, but it's working in an perculiar way I cannot understand directly.