|
GNU Rush |
Restricted User Shell |
Sergey Poznyakoff |
| GNU Rush – a restricted user shell. (split by section): | ![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
? |
The rule statement configures a GNU Rush rule. This is a
block statement, which means that all statements located between
it and the next rule statement (or end of file, whichever
occurs first) modify the definition of the rule.
The syntax of the rule statement is:
The tag argument is optional. If it is given, it supplies a
tag for the rule, i.e. a (presumably unique) identifier, which
is used to label this rule. Rush uses this tag in its diagnostic
messages. For rules without explicit tag, Rush supplies a
default tag, which is constructed by concatenating ‘#’ character
and the ordinal number of rule in the configuration file, in decimal
notation. Rule numbering begins from ‘1’.
The following sub-sections describe statements that can be used
within a rule.
These statements define conditions that are used to match the rule with the request. A rule may contain any number of conditions. All conditions are tested in order of their appearance in the rule and are tied together using boolean shortcut ‘and’ evaluation: if any of them yields false, the rest is not evaluated and control is transferred to the subsequent rule.
True, if the current command line matches regular expression regex.
For example:
command ^scp (-v )?-t /incoming/(alpha|ftp) |
By default, POSIX extended regular expressions are used.
This, however can be changed using regex statement (see section Regex).
True, if nth word from the command line matches
regular expression regexp. Notice, that square brackets form
part of the statement syntax. A special value ‘$’ can be used
instead of n to denote the last word. Unless changed by
previous regex statement (see section Regex), POSIX
extended regular expressions are used.
The command line is split into words using the same rules as used in
/bin/sh.
For example, the condition below yields true if the last argument is an absolute path name:
match[$] ^/.* |
Compare the number of command line arguments to num. The comparison operator is given by op, which can be one of the following: ‘=’ (or ‘==’), ‘!=’, ‘<’, ‘<=’, ‘>’, ‘>=’.
For example, the following condition matches if the number of arguments is less than 3:
argc < 3 |
Compare current UID to num. The comparison operator is given by op, which can be one of the following: ‘=’ (‘==’), ‘!=’, ‘<’, ‘<=’, ‘>’, ‘>=’.
Compare current GID to num. The comparison operator is given by op, which can be one of the following: ‘=’ (‘==’), ‘!=’, ‘<’, ‘<=’, ‘>’, ‘>=’.
Argument is a whitespace-separated list of user names. This condition yields true, if the user name matches one of the listed names. String comparisons are case-sensitive.
Argument is a whitespace-separated list of group names. This condition yields true, if the the name of any group the user is a member of matches one of listed names. String comparisons are case-sensitive.
For example, to match users from groups ‘admin’ and ‘root’:
group admin root |
Each condition allows for a negated form, by placing an exclamation sign between the condition keyword and expression. For example:
command ^scpTrue, if the command line begins with ‘scp’.
command ! ^scpTrue if the command line does not begin with ‘scp’.
Special actions that allow to rewrite command line are called transformations. GNU Rush supports five kinds of transformations: ‘set’, ‘transform’, ‘delete’, ‘map’. All of them operate on command line split into words. Additionally, ‘set’ and ‘transform’ can operate on the entire command line.
Rush performs word splitting using the same rules as sh.
Transformation actions refer to words by their index. Three
kinds of indexes are supported. First of all, a word may be referred
to by its ordinal number in the command line. The command name itself
has index ‘0’. For example, given the command line:
/bin/scp -t /upload |
one gets:
| Index | Value |
|---|---|
| 0 | /bin/scp |
| 1 | -t |
| 2 | /upload |
Negative indexes can also be used. They refer to words in the backward order, as illustrated in the following table:
| Index | Value |
|---|---|
| -1 | /upload |
| -2 | -t |
| -3 | /bin/scp |
Finally, the last word may be referred to as ‘$’, and the
command name itself as ‘^’. There is a subtle difference between
‘0’ and ‘^’. The notation ‘^’ refers to the name of
the program that rush will execute at the end of the
matching rule, whereas the notation ‘0’ refers to the 0th
argument that will be passed to that program (‘argv[0]’). Most
of the time the two values coincide. Unless the rule modifies ‘^’,
‘0’th word will be used as the program name. There exist some
cases when you need to explicitly set ‘^’. See section Interactive Access,
for a possible use of this feature.
Some of the transformations implement patterns. A pattern is a string which may contain meta-variables. Before using, these meta-variables are expanded using the following rules:
The ‘set’ transformation allows to replace entire command line, or any of its arguments, with the given value.
Command line is replaced with the expansion of pattern (see patterns). E.g.:
set /bin/echo "Command forbidden: ${command}"
|
As another example, the following rule uses transform to
ensure that ‘/usr/bin/cvs’ binary is used:
rule cvs command ^cvs server set[0] /usr/bin/cvs |
In versions of GNU Rush prior to 1.6, it was common to use the
transform action for this purpose.
Replace nth argument with the expansion of pattern. Notice, that square brackets are part of the statement syntax.
See indexing, for a description of n. See patterns, for a description of pattern. E.g.:
set[0] /bin/echo |
The ‘delete’ action deletes the given word, or range of words, from the command line. It has two forms.
Delete nth word. See indexing, for a detailed description of n and its syntax. However, see the note below.
Delete words from n to m, inclusive. For example, the following action removes all arguments, except the command name:
delete 1 $ |
Neither form can be used to delete the command name, i.e. ‘[0]’ or ‘[^]’.
The transform action modifies the value of the command line,
or any particular word of it, according to a sed s-expression.
S-expressions are described in more detail below (see s-expression).
This action has several forms.
Apply expression expr to entire command line. For example, the action below adds a ‘-t’ option after the command name:
transform s,^[^ ]+,& -t, |
Expand pattern as described in patterns, apply expr to the expansion, and replace the command line with the result.
Apply expression expr to nth word from the command line. Notice, that square brackets are part of the statement syntax. See indexing, for a detailed description of n and its syntax.
Apply expression expr to the expanded pattern and assign the result to nth word. See patterns, for a description of patterns and their expansion. See indexing, for a detailed description of n and its syntax.
The example below replaces the 0th argument with the base name of the command, prefixed by a dash:
transform[0] ${^} s,.*/,-
|
For instance, if the command name is ‘/bin/bash’, ‘argv[0]’ will become ‘-bash’.
The transformation expression, expr, is a sed-like
replace expression of the form:
s/regexp/replace/[flags] |
where regexp is a regular expression, replace is a replacement for each part of the input that matches regexp. Both regexp and replace are described in detail in The "s" Command: (sed)The "s" Command section `The `s' Command' in GNU sed.
As in sed, you can give several replace expressions,
separated by a semicolon.
Supported flags are:
Apply the replacement to all matches to the regexp, not just the first.
Use case-insensitive matching
regexp is an extended regular expression (see Extended regular expressions: (sed)Extended regexps section `Extended regular expressions' in GNU sed).
Only replace the numberth match of the regexp.
Note: the POSIX standard does not specify what should happen
when you mix the ‘g’ and number modifiers. Rush
follows the GNU sed implementation in this regard, so
the interaction is defined to be: ignore matches before the
numberth, and then match and replace all matches from the
numberth on.
Any delimiter can be used in lieue of ‘/’, the only requirement being that it be used consistently throughout the expression. For example, the following two expressions are equivalent:
transform s/one/two/ transform s,one,two, |
Changing delimiters is often useful when the regex contains
slashes. For instance, it is more convenient to write s,/,-, than
s/\//-/.
For example, the following rule uses transform to ensure that
‘/usr/bin/cvs’ binary is used:
rule cvs command ^cvs server transform[0] s|.*|/usr/bin/cvs| |
The same effect can be achieved with a set statement, as shown in
set-command.
As a more complex example, consider the following rule:
rule svn command ^svnserve -t transform s|-r *[^ ]*||;s|^svnserve |/usr/bin/svnserve -r /svnroot | |
This transform expression first removes all occurrences of ‘-r’ option and its arguments from the command line, and then adds its own ‘-r’ option and replaces ‘svnserve’ with the full program file name.
The ‘map’ statement uses file lookup to find a new value for the command line word.
Arguments are:
Argument index, as in indexing.
Name of the map file. It must begin with ‘/’ or ‘~/’. Before using, the file permissions and ownership are checked using the procedure described in security checks.
A string containing allowed field delimiters.
The value of the lookup key. Before using, it is expanded as described in patterns.
Number of the key field in file. Fields are numbered starting from 1.
Number of the value field.
If supplied, this value is used as a replacement value, when the key was not found in file.
The map file consists of records, separated by newline characters (in other words, a record occupies one line). Each record consists of fields, separated by delimiters, given in delim argument. If delim contains a space character, then fields may be delimited by any amount of whitespace characters (spaces and/or tabulations). Otherwise, exactly one delimiter delimits fields.
Fields are numbered starting from 1.
The map action works as follows:
For example, suppose that the file ‘/etc/passwd.rush’ has the same syntax as the system ‘passwd’ file (see Password File: (passwd(5))passwd section `passwd' in passwd(5) man page). Then, the following statement will replace ‘argv[0]’ with the value of ‘shell’ field, using the current user name as a key:
map[0] /etc/passwd.rush : ${user} 1 7
|
See also Interactive Access, for another example of using this statement.
System actions provide an interface to the operating system.
Set the umask. The mask must be an octal value not greater than ‘0777’. The default umask is ‘022’.
Change the root directory to that specified in dir. This directory will be used for file names beginning with ‘/’. A tilde (‘~’) in the beginning of dir is replaced with the user's home directory.
The directory dir must be properly set up to execute the
commands. For example, the following rule defines execution of
sftp-server in an environment, chrooted to the user's home
directory:
rule sftp command ^.*/sftp-server set[0] bin/sftp-server chroot ~ |
For this to work, each user's home must contain the directory ‘bin’ with a copy of ‘sftp-server’ in it, as well as all directories and files that are needed for executing it, in particular ‘lib’.
Change to the directory dir. The argument is subject to
tilde-expansion (see chroot, above). If both chdir and
chroot are specified, then chroot is executed first.
Impose limits on system resources, as defined by res. The
argument consists of commands, optionally separated by any
amount of whitespace. A command is a single command letter followed
by a number, that specifies the limit. The command letters are
case-insensitive and coincide with those used by the shell ulimit
utility:
| Command | The limit it sets |
|---|---|
| A | max address space (KB) |
| C | max core file size (KB) |
| D | max data size (KB) |
| F | maximum file size (KB) |
| M | max locked-in-memory address space (KB) |
| N | max number of open files |
| R | max resident set size (KB) |
| S | max stack size (KB) |
| T | max CPU time (MIN) |
| U | max number of processes |
| L | max number of logins for this user (see below) |
| P | process priority -20..20 (negative = high priority) |
For example:
limits T10 R20 U16 P20 |
If some limit cannot be set, execution of the rule aborts. In
particular, ‘L’ limit can be regarded as a condition, rather than
action. The setting limit Ln succeeds only if no
more than n rush instances are simultaneously running for
the same user. This can be used to limit the number of simultaneously
open sessions.
The use of ‘L’ resource automatically enables forked mode. See section Accounting and Forked Mode, for more information about it.
The env action allows to modify the environment in which the
program will be executed.
Modify the environment.
Its argument is a whitespace-delimited list of specifiers. The following specifiers are understood:
Clear the environment. This is understood only when used as a first word in args.
Unset the environment variable name.
Unset the environment variable name only if its value is val.
Retain the environment variable name.
Define environment variable name to have given value.
Retain variable name and append value to its existing
value. If no such variable is present in the environment, it is
created and value is assigned to it. However, if value
begins with a punctuation character, this character is removed from it
before the assignment. This is convenient for using this construct with
environment variables like PATH, e.g.:
PATH+=:/sbin |
In this example, if PATH exists, ‘:/sbin’ will be appended
to it. Otherwise, it will be created and ‘/sbin’ will be
assigned to it.
Retain variable name and prepend value to its existing value. If no such variable is present in the environment, it is created and value is assigned to it. However, if value ends with a punctuation character, this character is removed from it before assignment.
A fall-through rule is a special rule that does not execute the
requested command. When a matching fall-through rule is encountered,
rush evaluates it and continues scanning its configuration
for the next matching rule. Any transformation and env
actions found in a fall-through rule take effect immediately, which
means that subsequent rules will see modified command line and
environment. Execution of any other actions found in the fall-through rule
is delayed until a usual rule is found.
A fall-through rule is declared using the following statement:
Declare a fall-through rule.
Usually this statement is placed as the last statement in a rule, e.g.:
rule default umask 002 env - HOME USERNAME PATH fall-through |
Fall-through rules provide a way to set default values for subsequent rules. For example, any rules that follow the ‘default’ rule shown above, will inherit the umask and environment set there.
One can also use fall-through rules to “normalize” command lines. For example, consider this rule:
rule default transform[0] s|.*/||; fall-through |
It will remove all path components from the first command line argument. As a result, all subsequent rules may expect a bare binary name as the first argument.
Yet another common use for such rules is to enable accounting (see the next subsection), or set resource limits for the rest of rules:
rule default limit l1 fall-through |
GNU Rush is able to operate in two modes, which we call default and
forked. When operating in the default mode, the process image of
rush itself is overwritten by the command being executed.
Thus, when it comes to launching the requested command,
the running instance of rush ceases to exist.
There is also another operation mode, which we call forked
mode. When running in this mode, rush executes the
requested command in a subprocess, and remains in memory supervising
its execution. Once the command terminates, rush exits.
One advantage of the forked mode is that it allows to run
accounting, i.e. to note who is doing what and to keep a
history of invocations. The accounting, in turn, can be used to limit
simultaneous executions of commands (logins, in
GNU Rush terminology), as requested by ‘L’ command to limit
statement (see L limit).
The forked mode is enabled on a per-rule basis, for rules that
contain either ‘L’ command in the limit statement, or
‘acct on’ command:
Turn accounting mode on or off, depending on bool. The argument can be one of the following: ‘yes’, ‘on’, ‘t’, ‘true’, or ‘1’, to enable accounting, and ‘no’, ‘off’, ‘nil’, ‘false’, ‘0’, to disable it.
Notice, that there is no need in explicit acct on command, if
you use limit L.
The notion ‘rule contains’, used above, means that either the rule in question contains that statement, or inherits it from one of the above fall-through rules (see section Fall-through). In fact, in most cases the accounting should affect all rules, therefore we suggest to enable it in a fall-through rule at the beginning of the configuration file, e.g.:
rule default acct on fall-through |
If the need be, you can disable it for some of the subsequent rules by
placing acct off in it. Notice, that this will disable
accounting only, the forked mode will remain in action. To disable it
as well and enforce default mode for a given rule, use fork off
statement:
Enable or disable forked mode. This statement is mainly designed as a way of disabling the forked mode for a given rule.
Once the accounting enabled, you can view the list of currently
logged in users using rushwho command (see section The rushwho utility.) and
view the history of last logins using rushlast command
(see section The rushlast utility.).
Rush can be configured to send a notification over
INET or UNIX sockets, after completing user
request. It is done using the post-socket statement:
Notify URL about completing the user request. This statement implies forked mode (see section Accounting and Forked Mode).
Allowed formats for url are:
Connect to remote host hostname using TCP/IP. Hostname is the host name or IP address of the remote machine. Optional port specifies the port number to connect to. It can be either a decimal port number or a service name from ‘/etc/services’. If port is absent, ‘tcpmux’ (port 1) is assumed.
Connect to a UNIX socket filename.
For example:
rule default post-socket inet://localhost |
The GNU Rush notification protocol is based on TCPMUX (RFC 1078).
After establishing connection, rush sends the rule tag
followed by a CRLF pair. The rule tag acts as a service name. The
remote party replies with a single character indicating positive
(‘+’) or negative (‘-’) acknowledgment, immediately followed
by an optional message of explanation, terminated with a CRLF.
If positive acknowledgment is received, rush sends a
single line, consisting of the user name and the executed command
line, separated by a single space character. The line is terminated
with a CRLF.
After sending this line, rush closes the connection.
The post-process notification feature can be used to schedule execution of some actions after certain rules.
See section Notification, for an example of how to use this feature.
An exit rule does not execute any commands. Instead, it writes the supplied error message to the specified file descriptor and exits immediately. An exit rule is defined using the following statement:
Write textual message message to a file descriptor, given by the optional argument fd. If fd is absent, ‘2’ (standard error) is used.
The message argument is subject to backslash interpretation (see backslash-interpretation).
For example (note the use of line continuation character):
exit \
\r\nYou are not allowed to execute that command.\r\n\
\r\nIf you think this is wrong, ask <foo@bar.com> for assistance.\r\n
|
If message begins with a ‘@’ sign, the remaining characters are treated as the name of a predefined error message (see section Error Messages). The corresponding message text is retrieved and used instead of message. For example:
exit @nologin-message |
If the characters after ‘@’ do not correspond to any
predefefined error message name, an error of type ‘config-error’
is signalled and rush exits.
If you need to begin your exit message with a ‘@’ sign, duplicate it, as in this example:
exit @@Special error message |
This example will produce ‘@Special error message’.
Exit actions are useful for writing trap rules, i.e. the rules that are intended to trap incorrect or prohibited command lines and to return customized reply messages in such cases. Consider the following rule:
rule git command ^git-.+ match[1] ^/sources/[^ ]+\.git$ transform s|.*|/usr/bin/git-shell -c "&"| |
It allows to use only those Git repositories that are located under ‘/sources’ directory(6). If a user tries to access a repository outside this root, he will be returned a default error message, saying ‘You are not permitted to execute this command’ (see section usage-error). You can, however, provide a more convenient message in this case. To do so, place the following after the ‘git’ rule:
rule git-trap command ^git-.+ exit fatal: Use of this repository is prohibited. |
This rule will trap all git invocations that do not match the ‘git’ rule.
GNU Rush is internationalized, which means that it is able to
produce log and diagnostic messages in any language, if a
corresponding translation file is provided. This file is called a
localization or domain file. To find an appropriate
localization file, rush uses the following parameters:
Locale name is a string that describes the language, territory and optionally, the character set to use. It consists of the language (ISO 639) and country (ISO 3166) codes, separated by an underscore character, e.g. ‘en_US’ or ‘pl_PL’. If a character set is specified, its name follows the country code and is separated from it by a ‘@’ character.
There are two special locale names: ‘C’ or ‘POSIX’ mean to
use the default POSIX locale, and ‘""’ (an empty
string), means to use the value of the environment variable
LC_ALL as the locale name.
Directory where localization files are located. If not specified, a predefined set of directories is searched for the matching file.
Text domain defines the base name of the localization file.
Given these parameters, the name of the full pathname of the localization file is defined as:
locale_dir/locale/LC_MESSAGES/domain.mo |
GNU Rush produces three kinds of messages:
These are diagnostics messages that GNU Rush produces to its log output (syslog, unless in test mode).
Messages sent to the remote party when rush is not able to
execute the request (see section Error Messages).
These are messages sent to the remote party by exit rules
(see section Exit rule).
These messages use different domain names (and may use different locale directories). The diagnostics and error messages use textual domain ‘rush’. The corresponding locale directory is defined at compile time and defaults to ‘prefix/share/locale’, where prefix stands for the installation prefix, which is ‘/usr/local’, by default.
GNU Rush is shipped with several localization files, which are installed by default. As of version 1.6, these files cover the following locales:
This is default domain, it is always supported and does not require any special handling. It is roughly equivalent to ‘en_US’ (English).
Polish messages.
Ukrainian messages.
If the localization you need is not in this list, visit http://translationproject.org/domain/rush.html. If it is not there either, consider writing it (see (gettext)Translators section `Translators' in GNU gettext utilities, for a detailed instructions on how to do that).
Exit messages use custom domain files. It is responsibility of the system administrator to provide and install such files.
The following configuration directives control localization. They
are available for use in rule statements:
Sets the locale name. To specify empty locale, use ‘""’ as
name (recall that empty locale name means use the value of the
environment variable LC_ALL as locale name).
Sets the name of the locale directory.
Sets the textual domain name.
The following configuration fragment illustrates their use:
rule l10n locale pl_PL text-domain rush-config fall-through |
Different users may have different localization preferences. See per-user l10n, for a description of how to implement this.
You need to write a localization file for your configuration script if it implements exit rules (see section Exit rule) and changes user locale (see section locale).
Preparing localization consists of three stages: extracting exit messages and forming a PO file, editing this file, compiling and installing it. The discussion below describes these stages in detail.
A PO (Portable Object) file is a plain text file, containing original messages and their translations for a particular language. See The Format of PO Files: (gettext)PO Files section `PO Files' in GNU gettext utilities, for a description of its format.
The script rush-po.awk serves for extracting messages from
the configuration file and forming a PO file. This script
is installed in the ‘prefix/share/rush’
directory(7), where prefix stands for your
installation prefix.
Supposing the package is installed in the default prefix, the following command will create a PO file from your configuration file:
awk -f /usr/local/share/rush/rush-po.awk \
/usr/local/etc/rush.rc > myconf.po
|
Please note, that rush-po.awk does not handle
include directives (see section Include). If your configuration
file uses these directives, you need to specify each include file in
the rush-po.awk command line, e.g.:
awk -f /usr/local/share/rush/rush-po.awk \
/usr/local/etc/rush.rc /home/*/.rush > myconf.po
|
Open the created PO file with your favorite editor and supply
message translations after msgstr keywords. Although you can
use any editor, capable of handling plain text files, we recommend to
use GNU Emacs, which provides a special po-mode. See PO Files and PO Mode Basics: (gettext)Basics section `Basics' in GNU gettext utilities, for guidelines
on editing PO files and using the po-mode.
When ready, the PO file needs be compiled into a
MO (Message Object) file, which is directly readable
by rush. This is done using msgfmt utility from
GNU gettext:
msgfmt -o myconf.mo myconf.po |
See (gettext)msgfmt Invocation section `msgfmt Invocation' in GNU gettext utilities, for a
detailed description of the msgfmt utility.
After creating the MO file, copy it into appropriate directory. It is important that the installed MO file uses the naming scheme described in localization file naming.
See section git, for a better way to handle Git accesses.
To be precise, it is installed in
‘dataroot/rush’, where dataroot is a directory for
read-only architecture-independent data, which is set by the
‘--datarootdir’ option to configure. Unless that
option is used, this directory defaults to
‘prefix/share/rush’
| GNU Rush – a restricted user shell. (split by section): | ![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
? |
Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.