Why this linux command can affect the environment variables? - linux

When I changed my current user to admin using
sudo su admin
I found that the environment variable changed too. What I intend to do is to change my user to admin with the env not changed.
Then I found a command as follows:
sudo bash -c "su - admin"
This command does indeed what I want, but I googled about bash -c, with no clue to why this command can do that for me. Could anyone give me a clear explanation? Thanks a lot.

first you should read the sudo manpage and set theses options in the /etc/sudoers file or you can do it interactively (see second below).
default sudoers file may not preserve the existing $USER environment unless you set the config options to do so. You'll want to read up on env_reset because depending on your OS distribution the sudo config will be different in most cases.
I dont mean to be terse but I am on a mobile device..
I do not recommend using sudo su .. for anything. whomever is sharing sudo su with the public is a newb, and you can accomplish the same cleaner with just sudo.
with your example whats happining is you are starting a subshell owned by the original user ("not admin") . you are starting the subshell with -c "string" sudo has the equivelant of the shell's -c using -s which either reads the shell from the arg passed to -s or the shell defined in the passwd file.
second you should use:
$ sudo -u admin -E -s
much cleaner right ? :)
-u sets the user, obviously
-s we just explained
-E preserves the orig user env
see for yourself just
$ echo $HOME # should show the original users /home/orig_user
$ env
your original env is preserved with none of that sudo su ugliness.
if you were interested in simulating a users login without preserving the env..
$ sudo -u user -i
or for root:
Might require -E depending on distro sudoers file
$ sudo -s
or
$ sudo -i
-i simulates the login and uses the users env.
hopefully this helps and someone will kindly format it to be more readable since im on my mobile.

bash with -c argument defines below.
-c string
If the -c option is present, then commands are read from string. If there are arguments after the string, they are assigned to the positional parameters, starting with $0.
Thanks & Regards,
Alok

Related

Running aliases via sudo in bash or ksh

So here is my weirdness from work.
We have user accounts and service accounts and sudo access to the service accounts from the user accounts. Most, if not all, user accounts are running bash, whilst the service accounts use a misxture of bash or ksh.
When I am logged in as a user(bash) and using sudo to a service account(ksh) and run the following:
sudo -u svc_user -i alias_name
The alias runs as expected.
When I am logged in as a user(bash) and using sudo to a service account(bash) and run the following:
sudo -u svc_user -i alias_name
I get the error:
-bash: alias_name: command not found
Strangely, even when both accounts are bash, I can see the alias:
sudo -u svc_user -i alias
alias alias_name='func_name'
sudo -u svc_user -i which alias_name
alias alias_name='func_name'
Is there a reason why this works with ksh as the service account shell and not in bash?
Please let me know if any further details are required?
EDIT:- I just want to be clear, the alias I want to run belongs to the service account and not the original user who is calling sudo
Because bash only parse the first word in your command line to check if there is an alias in current environment.
You can try sudo alias_name, maybe also cannot find the alias. But you can try to add alias sudo='sudo ' to tell bash to parse next command for alias.
However, for your current case, it maybe actually run command bash -c xxxx in your srv_account. So it is not an easy things to resolve.

Use source with script that contains sudo command

I have a script that copies files between servers. I am using the lsof command to make sure that the file is not being written to before being moved. The user running the script and the user writing to the file are different, so I need to sudo to the file owner. Here is the relevant line in the sudoers file :
userA ALL=(userB:userB) NOPASSWD: ALL
In the main script (ran as userA), I have tried calling sudo then the subscript containing the lsof command:
sudo su - userB -c 'source ./getOpenFiles.sh'
getOpenFiles.sh has this one line:
#!/bin/bash
lsofResult=$(/usr/sbin/lsof "${sourcePath}")
I have also tried calling the subscript:
source ./getOpenFiles.sh
Then have the first line of the subscript be the sudo:
#!/bin/bash
sudo su - banjobs
lsofResult=$(/usr/sbin/lsof "${sourcePath}")`.
Neither solution is working.
What you actually want is something more like:
lsofResult=$(sudo -u banjobs lsof "${sourcePath}")
Let's go over why the other approaches didn't work one-at-a-time:
Running source under sudo su -c
sudo su - userB -c 'source ./getOpenFiles.sh'
...uses sudo to run su, which runs sh -c 'source ./getOpenFiles.sh'. This doesn't work for several independent reasons:
sh -c 'source ./getOpenFiles.sh' relies on the source keyword being available in /bin/sh, but this is a bash extension.
Even if your /bin/sh is provided by bash, this still defeats the purpose of using source: By starting a new copy of /bin/sh and sourcing your script into that, you're defining the variable in the new shell, not in the original shell that started sudo su.
Running sudo su - banjobs, followed by lsofResult=$(/usr/sbin/lsof "${sourcePath}")
...means that lsofResult=$(...) doesn't run until after sudo su - banjobs exits. If sudo su - banjobs has exited, then the current user isn't banjobs any more, so the sudo command has no effect whatsoever on the lsof.
Demonstrating, in detail, how to test this (for folks who don't have a banoff or userB account on their system):
# place relevant contents in sourceme.bash
# switching from lsof to fuser for this test since OS X lsof does not accept a directory
# as an argument.
cat >sourceme.bash <<'EOF'
lsofResult=$(sudo -u root fuser "${sourcePath}" 2>&1)
EOF
# set sourcePath in the outer shell
sourcePath=$PWD
source sourceme.bash
declare -p lsofResult
...yields, on my system, output akin to the following:
declare -- lsofResult="/Users/chaduffy/tmp: 17165c 17686c 17687c 17688c 17689c 17690c"
...showing that the code in question did in fact work as described.

sudo: command not found

I am trying to execute screen as another user using sudo.
I'm using the command:
echo 'userpassword' | /usr/bin/sudo -u 'myuser' -S '/usr/bin/screen -ls'
Any help found on the internet says that the sudo clears the environment variables (like PATH). So I decided to use the full path to the applications but I'm still getting the command not found error.
Error:
sudo: /usr/bin/screen -ls: command not found
Sudo is installed on the system.
Screen is installed on the system.
For sudo, I have tried the -E and -H flag but it doesn't help.
I tried to set PATH variable using something like this:
... | /usr/bin/sudo -u 'myuser' -S 'env PATH=$PATH; /usr/bin/screen -ls'
Supposedly the $PATH was suppose to expand before the command executes but I was getting other errors...
Can someone provide a command that will let me execute commands as another user and explain what each part of the command does so I can understand it?
Thanks.
Try,
export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:$PATH
Probably you replaced the path variable while trying to set a new path variable.
Going forward, do 'echo $PATH' before adding a new path variable.
There doesn's seem to be any need to encapsulate command in quotes, without them it even works.
echo 'userpassword' | /usr/bin/sudo -u 'myuser' -S screen -ls

Use sudo without password INSIDE a script

For some reason I need, as user, to run without sudo a script script.sh which needs root privileges to work.
I saw as the only solution to put sudo INSIDE script.sh. Let's take an example :
script.sh :
#!/bin/sh
sudo apt-get update
Of course, if I execute this script, I get a prompt asking me for a password. Then I added to my sudoers file (at the end to override everything else) :
user ALL=(ALL:ALL) NOPASSWD:/path/to/script.sh
By the way, I also tried the line :
user ALL=(ALL) NOPASSWD:/path/to/script.sh
(I think I didn't fully understand the difference)
But this doesn't solve my problem if I don't use sudo to execute this script :
# ./script.sh
[sudo] password for user:
# sudo ./script.sh
Starts updating...
Well, so I say to myself "Ok, that means that if I have a file refered in sudoers as I did, it will work without prompt only if I call him with sudo, what is not what I want".
So, ok, I create another script script2.sh as following :
script2.sh
#!/bin/sh
sudo /path/to/script.sh
In fact it works. But I am not truly satisfied of this solution, particularly by the fact that I have to use 2 scripts for every command.
This post is then for helping people having this problem and searching for the same solution (I didn't find a good post on it), and perhaps have better solutions coming from you guys.
Feel free to share your ideas !
EDIT 1 :
I want to insist on the fact that this "apt-get update" was just an example FAR from whhat my script actually is. My script has a lot of commands (with some cd to root-access-only config files), and the solution can't be "Well, just do it directly with apt-get".
The principle of an example is to help the understanding, not to be excuse to simplify the answer of the general problem.
From my blog: IDMRockstar.com:
The kicker is that sometimes, I need to run commands as root. Here's the quick and dirty way I accomplish that without divulging the passwords:
#! /bin/bash
read -s -p "Enter Password for sudo: " sudoPW
echo $sudoPW | sudo -S yum update
This way the user is prompted for the password (and hidden from terminal) and then passed into commands as needed, so I'm not running the entire script as root =)
If you have a better, way, I'd love to hear it! I'm not a shell scripting expert by any means.
Cheers!
.: Adam
If you want to run sudo /usr/bin/apt-get update without a password, you need to have the sudoers entry:
user ALL=(ALL:ALL) NOPASSWD:/usr/bin/apt-get update
For the larger issue of the script as a whole, there are two possible approaches:
Approach 1
For each command in the script that needs sudo, create a line in sudoers specifically for that command. In this case, the script can be called normally:
./script1.sh
Approach 2
Place a line in sudoers for the script as a whole. When this is done, the individual commands do not need sudo. However, sudo must be used to start the script as in:
sudo ./script.sh
If your password isn't something you want to be very secure about, (maybe some testing server in the company etc.) you can elevate to sudo in the script via echo like:
echo YourPasswordHere | sudo -S Command
The prompt still prints the "enter password" text to output though. So don't expect it to be neat.
See this Askubuntu post
As you noted, the file that must appear in the sudoers configuration is the one that is launched by sudo, and not the one that runs sudo.
That being said, what we often do, is having something like
user ALL=(ALL:ALL) NOPASSWD:/path/to/script.sh
in the sudo configuration, where script.sh has all the commands that the script has to do.
Then we define either a Bash function or an alias so that script.sh is actually
sudo /path/to/script.sh
The only issue is if some commands must not be run as root, you need to insert some su - user -c "command" commands in the script.
In new /etc/sudoers.d/apt-get file, put single line:
user ALL=(ALL:ALL) NOPASSWD:/usr/bin/apt-get update
Fully qualified path to executable is required here.
Then use following in your script:
sudo apt-get update
Here, fully specified name is not required. Sudo uses PATH environment variable for executable resolution.
While changing and checking sudoers configuration, be sure to keep another root session open for error recovery.
I suggest you look at the sudo environment variables - specifically you can use (and check for) $SUDO_USER. Call your script with sudo (1 entry in sudoers), then do user stuff as SUDO_USER and root stuff as root.
As mentioned by Basilevs you need to add your user to the sudoers file in order to avoid that sudo commands in the script get stuck awaiting the password.
On Ubuntu 16, there is a simpler way: just add the user to the sudo group, like this:
sudo usermod -aG sudo *username*
From then on, it should work like a charm.
Note:
This works only on the condition that the following line is in file /etc/sudoers:
%sudo ALL=NOPASSWD: ALL
(such line gives passwordless sudo privileges at group level, in this case to the sudo group)
(if this line is not present and you want to add it, make sure you use visudo)
Simply, in order to execute commands as root you must use su (even sudo uses su)
As long as you execute sudo ./script2.sh successfully just instead :
sudo su
"#" //commands as root here
"#" exit
//commands as use here
you can make it a shell function with the name sudo, but no other better way i think,however it's the case with scripts inti,rc android ..etc
must be tidy ;)
however this requires you to put NOPASSWD: su wich is totaly secure indeed
any way here just lacks POISX permissions principle which is filtering so dont enable something to all users or vice versa
simply, call sudo as much as you want with no additional thing then:
chown root script.sh
chmod 0755 script.sh
chgrp sudo script.sh
"make root owner of .sh"
"make it read only and exec for others"
"and put it in sudo group"
of course under sudo
that's it

How do you give su the current user environment variables

I have a variable that is set through .bashrc.
In ~/.bashrc:
PROJ_HOME=~/Projects/stable
From a bash shell, I'd like to do something like this:
$ su -l kenneth -c 'echo $PROJ_HOME'
However, when I do this, the expected /home/kenneth/Projects/stable is not printed out.
Any ideas on how I can do this?
Have you tried the option su -m ?
-m, --preserve-environment
do not reset environment variables
For example: su -m kenneth -c 'echo $PROJ_HOME'
You need to export the variable. You may not need to use the -m option to su to preserve the environment.
export PROJ_HOME=~/Projects/stable
Try with su -m -l kenneth -c 'echo $PROJ_HOME'. -m should preserve the environment.
EDIT
Reading your question one more time, I think I might understood it reversed.
You might also try this: su -l kenneth -c '. /home/kenneth/.bashrc; echo $PROJ_HOME'.
There are multiple steps here which you need to understand.
PROJ_HOME=~/Projects/stable
creates a variable in the current shell with the expanded value of the path. In other words, if you are logged in as user luser, the variable will contain something like /home/luser/Projects/stable.
If the intent is for su to get the value /home/kenneth/Projects/stable you either need to evaluate this expression as that user, or rewrite it to contain the expected value for kenneth before running sudo.
In the first instance, if the assignment is in a file /etc/project.rc you can simply
su -l kenneth -c '. /etc/project.rc; echo "$PROJECT_HOME"`
In the second case, maybe try something like
PROJECT_HOME=~kenneth/Projects/stable su -m -l kenneth -c 'echo "$PROJECT_HOME"'
though of course that unattractively hardcodes the value for Kenneth (and your shell might not actually have the facility to expand ~kenneth to the home directory of kenneth, in which case maybe use getent etc).
I also fixed this issue and I fixed by exporting env variable into profile. Below is my sample code:
echo export runner_token=$(echo $resp_json | jq -r '.token') >> /etc/profile
su -p - ubuntu -c '$HOME/actions-runner/config.sh --url https://github.com/${gh_repo_user}/${gh_repo_name} --token "$runner_token" --name MAC-AWS-RUNNER --labels ${gh_runner_labels}'
Use single quotes around the command:
$ su -l kenneth -c 'echo $PROJ_PATH'
Double quotes interprets the value of $PROJ_PATH as seen by root (empty string), then executes the command "echo (empty string)" as the user kenneth.
Single quotes will pass 'echo $PROJ_PATH' as the command, and the value of $PROJ_PATH in kenneth's environment is what will be echoed.

Resources