64

What's a good way of running a shell script as a different user. I'm using Debian etch, and I know which user I want to impersonate.

If I was doing it manually, I would do:

su postgres ./backup_db.sh /tmp/test exit 

Since I want to automate the process, I need a way to run backup_db.sh as postgres (inheriting the environment, etc)

Thanks!

9 Answers 9

97

To run your script as another user as one command, run:

/bin/su -c "/path/to/backup_db.sh /tmp/test" - postgres Breaking it down: /bin/su : switch user -c "/path/to..." : command to run - : option to su, make it a login session (source profile for the user) postgres : user to become 

I recommend always using full paths in scripts like this - you can't always guarantee that you'll be in the right directory when you su (maybe someone changed the homedir on you, who knows). I also always use the full path to su (/bin/su) because I'm paranoid. It's possible someone can edit your path and cause you to use a compromised version of su.

6
  • It will always require me type in password ? How can get round it ? Commented Mar 29, 2012 at 7:15
  • 3
    It will always require you to type in a password if you are running the command as a non-root user. If you want to avoid the password, you can configure sudo to allow that. HOWEVER - configuring sudo to allow a user to run su allows them to become any user. I would suggest creating a script for your command, setting the script permissions to 700 and owned by root, then configuring sudo to allow a user to run that single script. Commented May 22, 2012 at 15:39
  • I believe - while this answer might work for the OP - it is not entirely correct. To my knowledge, using both - (or --login) together with --command, -c doesn't actually start a login session, because -c always forces a non-login shell. Commented May 1, 2013 at 13:46
  • 1
    Note: for portability, the - postgress should appear at the end of the command. From the man page: When - is used, it must be specified before any username. For portability it is recommended to use it as last option, before any username. The other forms (-l and --login) do not have this restriction. Commented May 2, 2017 at 14:45
  • will this work on www-data user ? Commented Aug 30, 2021 at 21:56
32

If the target user to run has nologin shelll defined, then you can use the -s option to specify the shell:

/bin/su -s /bin/bash -c '/path/to/your/script' testuser 

See the following question: run script as user who has nologin shell

10

To automate this on a schedule you could put it in the user's crontab. Cron jobs won't get the full environment though, but it it might be better to put all the env variables you need in the script itself anyways.

To edit the user's crontab:

sudo crontab -u postgres -e 
1
  • Could you please explain more? Commented May 26, 2016 at 15:23
4

This should be an informative read -- setuid on shell scripts

If you run su with a "- username" argument sequence, it will make a login shell for the user to give the same environment as the user. Usually, used to quickly execute your script with your home environment from a different login.

1
  • This can be useful, if you need to perform a series of actions. But bear in mind that most system service accounts shouldn't have valid home paths and shells. Commented Jul 23, 2009 at 10:59
3

Try the su manpage:

su -c script_run_as_postgres.sh - postgres

Alernately, you could use sudo to allow you to run just that command as postgres without a password. It takes some setup in your /etc/sudoers, though.

1
  • +1 for mentioning sudo. sudo is NOT all about root access! You can use sudo to run thing under other accounts and can configure it to not ask for your password when you do. Commented Sep 5, 2024 at 7:30
2

The "su -c ... " method posted by others is a good one. For automation, you could add the script to the crontab of the user you need it to execute as.

2

If the user already has an entry to sudo and you don't know superuser's password then you can try following: This one restarts the postgres initialized at /data/my-db/pgsql/9.6/data

sudo su - postgres -c "/usr/pgsql-9.6/bin/pg_ctl -D /data/my-db/pgsql/9.6/data -l /var/log/pgsql.log restart" 
1
  • sudo su requires a "side-step" through /root/ access to then execute su - and all the rest of it. sudo -u username avoids this, running the command under the stated user without going anywhere near root. Commented Sep 5, 2024 at 7:35
1

You can also use:

sudo -u postgres script_run_as_postgres.sh

1
  • If you need a login shell, you can run this command with -i Commented Jan 22, 2021 at 8:17
1

For sites that may have special PAM setups that make this difficult, you can bypass su/sudo entirely with systemd and a little more typing:

  1. Define a oneshot service that executes your script. If your script is only a few commands, you can use one or more ExecStart with commands instead of executing an external file that contains those same commands.

/home/myuser2/myscript.service

[Unit] Description=My Script [Service] Type=oneshot User=myuser2 ExecStart=/full/path/to/my/script.sh [Install] WantedBy=multi-user.target 
  1. Define a Polkit rule that lets another unprivileged user start the service

/etc/polkit-1/rules.d/my-script.rules

polkit.addRule(function(action, subject) { if (action.id == "org.freedesktop.systemd1.manage-units" && subject.user == "myuser1") { if (action.lookup("unit") == "myscript.service") { var verb = action.lookup("verb"); if (verb == "start" || verb == "stop" || verb == "restart") { return polkit.Result.YES; } } } }); 
  1. Enable the service:
systemctl enable /home/myuser2/myscript.service 

Now myuser1 can do systemctl start myscript.service which will ask systemd to execute the script (as myuser2). The service can be debugged as usual with systemctl status and journalctl.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.