Per-user rc.local files a bad idea? - linux

I was thinking about running commands at system start as a regular user. Currently, to do so, I use a syntax like this:
su -c 'command with some arguments' user
But I was thinking that it might be beneficial to have a per-user rc.local file at ~/.rc.local that would automatically get run as the user at startup. The code I came up for it is like this:
awk -F":" '{ if ($3 >= 1000) print $1, $6 }' /etc/passwd | while read u h
do
[ -x "$h/.rc.local" ] && su -c "cd $h; ./.rc.local" $u
done
This would be added to /etc/rc.local. It searches the home directory of every non-system user (i.e., a uid >= 1000), and if the file exists, and is executable, it cd's to the users home directory and executes that script as the user.
To me, this seems to eliminate any source of security risk, since it would be executing the script as the user, rather than root, but I've recently been reminded of my lack of security knowledge, so I present this question: is this a bad idea?
Right now, I'd only be running this on my home computer, and my home computer only has one non-system user. A few years down the road, my daughter my get her own account on the computer, but besides that, it's not like complete strangers will have access to this in any way.
So, is there any potential security hole that I'm overlooking? Also, is there a more elegant way to write that command?

This overlaps with the #reboot capability of per-user crontabs.

Related

Shell directory tests [ -d ] and permissions

I have a simple shell script that looks like this :
#!/bin/bash
AMAVIS_VIRUSMAILS="/var/lib/amavis/virusmails"
if [ -d "${AMAVIS_VIRUSMAILS}" ]; then
echo "ok"
fi
/var/lib/amavis and its sub-directories are, of course, owned by the amavis user. Which means that I get :
$ ls /var/lib/amavis/virusmails
ls: cannot access /var/lib/amavis/virusmails: Permission denied
When I run the ls command as another user without root or sudo privileges.
However. If I run the above shell script, it doesn't print "ok". Which, in other words, means the shell script is telling me the directory does not exist. Which is of course not true, it does exist, but I just don't have the permissions to access it.
I get the same result with #!/bin/sh
I therefore have two questions :
Is this the as expected and "as designed" behavior of the -d test ?
How should I best work around the problem so as to avoid false negatives ?
If it makes any difference, this is Ubuntu 14.04LTS, 3.13.0-53-generic. Which means I'm running GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu), or dash(sh) version 0.5.7-4ubuntu1
Which, in other words, means the shell script is telling me the directory does not exist. Which is of course not true, it does exist, but I just don't have the permissions to access it.
Yes. You can tell if do not have access => it does not exists for you (this is not fully proper, as you i.e. won't be able to create such directory, but that's another case anyway and you should be dealing with it in multiuser environment anyway)
Never mind, I just resorted to checking
$EUID -ne 0

How to run a script automatically when the user logs out in Linux?

I need to implement a feature that monitors which user logs in or out of the Linux desktop. When a user logs in or out, a script needs to be run automatically to notify a daemon process which user logged in or out.
I searched in Google and found a script under /etc/profile.d will be run automatically after the user logs in.
But I didn't find a common solution that will run a script automatically when the user logs out. It looks the solution is different for different linux distribution. Such as:
For Ubuntu, I need to modify the file /etc/lightdm/lightdm.conf
I need to support multiple Linux distributions, including: CentOS, Ubuntu, Redhat, and so on. If I use different solutions for different Linux distributions, my code will be very complicated.
I would like find a common solution for different Linux distributions. Can you please give some clues?
In bash, the ~/.bash_logout file will be executed when exiting shell.
So place in it script you want to execute
simply find out WHO's logged in and record when you first see them, and when you not longer see them. then read the "crontab" manual page and install a process that keeps track of this
the basic command: who | awk '{ print $1 }' | sort -u
set -- /tmp/whoseloggedin /tmp/whoWASloggedin
saving the data. ... | tee $1
comm -23 $1 $2 | sed "s/^/$(date) /" >> /tmp/justloggedIN
comm -13 $1 $2 | sed 's/^/$(date) /" >> /tnp/justloggedOFF
mv $1 $2
sleep for a second or two, and repeat.
you might store the data in a more reliable place than "/tmp/"

su without password within a script

I'm sure this question has been answered before, but I can't find an answer that I like. I would like to write a shell script that executes a very specific script as another user (I want anyone to be able to start postgres as the postgres user). The script will have 710 perms so it will be executable by a certain group but not readable or writable by that group.
Now, I'm pretty sure there's no way to use 'su' without an interactive password prompt. There are lots of good reasons for that and I don't need to be convinced of the merit of those reasons (I'm told that someone savvier than me could grab the password off the processes list which is bad).
Question is, more generally how would I accomplish what I want to do without abusing unix security paradigms? Is there a way to allow user to execute a very specific process as another user?
This sort of situation is exactly what sudo was designed for.
You can create an executable (not a shell script) that launches the script that should run as the postgres user. Change the owner of the executable to the postgres user, and set the setuid bit.
See Best practice to run Linux service as a different user to address Celada's concern.
Well, you could use a simple script to access programmatically to an user using sudo and then execute all code you want.
Here is a simple script:
if [ "$#" -ne 2 ]; then
echo "Usage: "
echo " suprompt <user> <password>"
else
echo $2 | sudo -sS su $1
sudo su $1
fi
This script uses two arguments. The first one is the user you want to be, and the second arg is the password.
It works automatically.
You can change the final statement and do: sudo su $1 -c <command>
I hope this will work for you.

Fastest Way to Determine User Permissions in /etc/sudoer

Users will be remotely accessing ***nix based machines via SSH and I need to determine the fastest way to check if the username that they are currently using has NOPASSWD access in the /etc/sudoers file.
Possible options:
grep for the username in /etc/sudoers, parse command prompt output to determine if it has NOPASSWD, if not, remove the line then append the new permissions
Just append a permission string to the file regardless (bad idea).
Attempt to sudo into a protected file and see if it prompts me for a password.
I'm hoping for something easier, but my google-fu hasn't come up with any answers.
If sudo -v succeeds, the user has been authorized to use sudo; if it fails, then the user has not been authorized to use sudo.
# su user -c 'setsid sudo -v </dev/null'; echo $?
[sudo] password for user:
1
# su root -c 'setsid sudo -v </dev/null'; echo $?
0
Without setsid, sudo will try to ask for the password interactively even if stdin/stdout/stderr have all been redirected. If you don't have a controlling terminal, this isn't needed, but you will probably need something other than su to change user permissions, like fork+setreuid.
If you indeed need "the fastest way", I guess you're building a webserver that would handle many concurrent requests.
This raises another problem - the concurrency issue. Generally, many process reading and writing to the same important file is a recipe for a catastrophe.
Build a small independent process to handle the task. It should have a minimal interface that will receive requests from the clients, and updates for the the /etc/sudoer file. Something like has_NOPASSWD_access() and set_NOPASSWD_access(). It should read the file only when it needs to be written, so you'll greatly reduce the I/O time required to serve a request.
Pros -
Fast : No I/O needed for just reading the file, because it is stored in the buffer since the initial read
Thread safe: Only one server writes and reads the sudoer file
Single choice principle - only this process handles the sudoer file
Elegant (I hope) :-)
Cons -
- List them in the comments, and I'll add.

How to restrict SSH users to a predefined set of commands after login?

This is a idea for a security. Our employees shall have access to some commands on a linux server but not all. They shall e.g. have the possibility to access a log file (less logfile) or start different commands (shutdown.sh / run.sh).
Background information:
All employees access the server with the same user name: Our product runs with "normal" user permissions, no "installation" is needed. Just unzip it in your user dir and run it. We manage several servers where our application is "installed". On every machine there is a user johndoe. Our employees sometimes need access to the application on command line to access and check log files or to restart the application by hand. Only some people shall have full command line access.
We are using ppk authentication on the server.
It would be great if employee1 can only access the logfile and employee2 can also do X etc...
Solution:
As a solution I'll use the command option as stated in the accepted answer. I'll make my own little shell script that will be the only file that can be executed for some employees. The script will offer several commands that can be executed, but no others. I'll use the following parameters in authorized_keys from as stated here:
command="/bin/myscript.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty
ssh-dss AAAAB3....o9M9qz4xqGCqGXoJw= user#host
This is enough security for us. Thanks, community!
You can also restrict keys to permissible commands (in the authorized_keys file).
I.e. the user would not log in via ssh and then have a restricted set of commands but rather would only be allowed to execute those commands via ssh (e.g. "ssh somehost bin/showlogfile")
ssh follows the rsh tradition by using the user's shell program from the password file to execute commands.
This means that we can solve this without involving ssh configuration in any way.
If you don't want the user to be able to have shell access, then simply replace that user's shell with a script. If you look in /etc/passwd you will see that there is a field which assigns a shell command interpreter to each user. The script is used as the shell both for their interactive login ssh user#host as well as for commands ssh user#host command arg ....
Here is an example. I created a user foo whose shell is a script. The script prints the message my arguments are: followed by its arguments (each on a separate line and in angle brackets) and terminates. In the log in case, there are no arguments. Here is what happens:
webserver:~# ssh foo#localhost
foo#localhost's password:
Linux webserver [ snip ]
[ snip ]
my arguments are:
Connection to localhost closed.
If the user tries to run a command, it looks like this:
webserver:~# ssh foo#localhost cat /etc/passwd
foo#localhost's password:
my arguments are:
<-c>
<cat /etc/passwd>
Our "shell" receives a -c style invocation, with the entire command as one argument, just the same way that /bin/sh would receive it.
So as you can see, what we can do now is develop the script further so that it recognizes the case when it has been invoked with a -c argument, and then parses the string (say by pattern matching). Those strings which are allowed can be passed to the real shell by recursively invoking /bin/bash -c <string>. The reject case can print an error message and terminate (including the case when -c is missing).
You have to be careful how you write this. I recommend writing only positive matches which allow only very specific things, and disallow everything else.
Note: if you are root, you can still log into this account by overriding the shell in the su command, like this su -s /bin/bash foo. (Substitute shell of choice.) Non-root cannot do this.
Here is an example script: restrict the user into only using ssh for git access to repositories under /git.
#!/bin/sh
if [ $# -ne 2 ] || [ "$1" != "-c" ] ; then
printf "interactive login not permitted\n"
exit 1
fi
set -- $2
if [ $# != 2 ] ; then
printf "wrong number of arguments\n"
exit 1
fi
case "$1" in
( git-upload-pack | git-receive-pack )
;; # continue execution
( * )
printf "command not allowed\n"
exit 1
;;
esac
# Canonicalize the path name: we don't want escape out of
# git via ../ path components.
gitpath=$(readlink -f "$2") # GNU Coreutils specific
case "$gitpath" in
( /git/* )
;; # continue execution
( * )
printf "access denied outside of /git\n"
exit 1
;;
esac
if ! [ -e "$gitpath" ] ; then
printf "that git repo doesn't exist\n"
exit 1
fi
"$1" "$gitpath"
Of course, we are trusting that these Git programs git-upload-pack and git-receive-pack don't have holes or escape hatches that will give users access to the system.
That is inherent in this kind of restriction scheme. The user is authenticated to execute code in a certain security domain, and we are kludging in a restriction to limit that domain to a subdomain. For instance if you allow a user to run the vim command on a specific file to edit it, the user can just get a shell with :!sh[Enter].
What you are looking for is called Restricted Shell. Bash provides such a mode in which users can only execute commands present in their home directories (and they cannot move to other directories), which might be good enough for you.
I've found this thread to be very illustrative, if a bit dated.
Why don't you write your own login-shell? It would be quite simple to use Bash for this, but you can use any language.
Example in Bash
Use your favorite editor to create the file /root/rbash.sh (this can be any name or path, but should be chown root:root and chmod 700):
#!/bin/bash
commands=("man" "pwd" "ls" "whoami")
timestamp(){ date +'%Y-%m-%s %H:%M:%S'; }
log(){ echo -e "$(timestamp)\t$1\t$(whoami)\t$2" > /var/log/rbash.log; }
trycmd()
{
# Provide an option to exit the shell
if [[ "$ln" == "exit" ]] || [[ "$ln" == "q" ]]
then
exit
# You can do exact string matching for some alias:
elif [[ "$ln" == "help" ]]
then
echo "Type exit or q to quit."
echo "Commands you can use:"
echo " help"
echo " echo"
echo "${commands[#]}" | tr ' ' '\n' | awk '{print " " $0}'
# You can use custom regular expression matching:
elif [[ "$ln" =~ ^echo\ .*$ ]]
then
ln="${ln:5}"
echo "$ln" # Beware, these double quotes are important to prevent malicious injection
# For example, optionally you can log this command
log COMMAND "echo $ln"
# Or you could even check an array of commands:
else
ok=false
for cmd in "${commands[#]}"
do
if [[ "$cmd" == "$ln" ]]
then
ok=true
fi
done
if $ok
then
$ln
else
log DENIED "$cmd"
fi
fi
}
# Optionally show a friendly welcome-message with instructions since it is a custom shell
echo "$(timestamp) Welcome, $(whoami). Type 'help' for information."
# Optionally log the login
log LOGIN "$#"
# Optionally log the logout
trap "trap=\"\";log LOGOUT;exit" EXIT
# Optionally check for '-c custom_command' arguments passed directly to shell
# Then you can also use ssh user#host custom_command, which will execute /root/rbash.sh
if [[ "$1" == "-c" ]]
then
shift
trycmd "$#"
else
while echo -n "> " && read ln
do
trycmd "$ln"
done
fi
All you have to do is set this executable as your login shell. For example, edit your /etc/passwd file, and replace your current login shell of that user /bin/bash with /root/rbash.sh.
This is just a simple example, but you can make it as advanced as you want, the idea is there. Be careful to not lock yourself out by changing login shell of your own and only user. And always test weird symbols and commands to see if it is actually secure.
You can test it with: su -s /root/rbash.sh.
Beware, make sure to match the whole command, and be careful with wildcards! Better exclude Bash-symbols such as ;, &, &&, ||, $, and backticks to be sure.
Depending on the freedom you give the user, it won't get much safer than this. I've found that often I only needed to make a user that has access to only a few relevant commands, and in that case this is really the better solution.
However, do you wish to give more freedom, a jail and permissions might be more appropriate. Mistakes are easily made, and only noticed when it's already too late.
You should acquire `rssh', the restricted shell
You can follow the restriction guides mentioned above, they're all rather self-explanatory, and simple to follow. Understand the terms `chroot jail', and how to effectively implement sshd/terminal configurations, and so on.
Being as most of your users access your terminals via sshd, you should also probably look into sshd_conifg, the SSH daemon configuration file, to apply certain restrictions via SSH. Be careful, however. Understand properly what you try to implement, for the ramifications of incorrect configurations are probably rather dire.
GNU Rush may be the most flexible and secure way to accomplish this:
GNU Rush is a Restricted User Shell, designed for sites that provide limited remote access to their resources, such as svn or git repositories, scp, or the like. Using a sophisticated configuration file, GNU Rush gives you complete control over the command lines that users execute, as well as over the usage of system resources, such as virtual memory, CPU time, etc.
You might want to look at setting up a jail.
[Disclosure: I wrote sshdo which is described below]
If you want the login to be interactive then setting up a restricted shell is probably the right answer. But if there is an actual set of commands that you want to allow (and nothing else) and it's ok for these commands to be executed individually via ssh (e.g. ssh user#host cmd arg blah blah), then a generic command whitelisting control for ssh might be what you need. This is useful when the commands are scripted somehow at the client end and doesn't require the user to actually type in the ssh command.
There's a program called sshdo for doing this. It controls which commands may be executed via incoming ssh connections. It's available for download at:
http://raf.org/sshdo/ (read manual pages here)
https://github.com/raforg/sshdo/
It has a training mode to allow all commands that are attempted, and a --learn option to produce the configuration needed to allow learned commands permanently. Then training mode can be turned off and any other commands will not be executed.
It also has an --unlearn option to stop allowing commands that are no longer in use so as to maintain strict least privilege as requirements change over time.
It is very fussy about what it allows. It won't allow a command with any arguments. Only complete shell commands can be allowed.
But it does support simple patterns to represent similar commands that vary only in the digits that appear on the command line (e.g. sequence numbers or date/time stamps).
It's like a firewall or whitelisting control for ssh commands.
And it supports different commands being allowed for different users.
Another way of looking at this is using POSIX ACLs, it needs to be supported by your file system, however you can have fine-grained tuning of all commands in linux the same way you have the same control on Windows (just without the nicer UI). link
Another thing to look into is PolicyKit.
You'll have to do quite a bit of googling to get everything working as this is definitely not a strength of Linux at the moment.

Resources