I want to ssh into a remote server, change user then execute a script. I'm using subprocess
to do this but it appears sudo -u userB -i
is not changing user.
HOST = 'remote_server'
USER = 'userA'
CMD = ' whoami; sudo -u userB -i; whoami'
ssh = subprocess.Popen(['ssh', '{}@{}'.format(USER, HOST),CMD],
shell=False,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
result = ssh.stdout.readlines()
if not result:
err = ssh.stderr.readlines()
print('ERROR: {}'.format(err))
else:
print "success"
print(result)
$ success
$ ['userA\n', 'userA\n']
When I replaced with CMD ='sudo -u userB -i && whoami'
, I got this error:
ERROR: ['sudo: sorry, you must have a tty to run sudo\n']
On the terminal, I'm able to do passwordless ssh, sudo -u userB -i && whoami
sudo
on the target system is configured to require a terminal emulation. That is usually done to actually prevent the kind of automation you are trying to implement. Talk to your server administrator, before trying to bypass that restriction. Either the restriction is not needed, then the admin can turn it off for you. Or there is a reason for the restriction and the Administrator won't be happy that you are trying to bypass it.
The restriction can be turned off by removing requiretty
option from sudoers
file (the option is off by default).
To make ssh
use the terminal emulation for the command provided on its command-line, you need to use -t
switch. But for -t
to actually have any effect, you need to run ssh
from a terminal in the first place. As subprocess.Popen
is not a terminal, you will get:
Pseudo-terminal will not be allocated because stdin is not a terminal
To bypass that, you have to use double -t
switch: -tt
.
To @SuryaSekharMondal (who offered bounty on this question): You are using Paramiko, not ssh
. What I wrote above is not relevant to you.
Inserting this line in /etc/sudoers
on the remote server will resolve the issue:
Defaults !requiretty
I would approach this problem with a small C program and give it the suid permission, i.e.
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
// #include <stdio.h>
#include <string.h>
int main(int argc,char *argv[]){
setuid(0);//su
char *cmd="/usr/sbin/hddtemp";
char *arg[]={(char *)"hddtemp",(char *)"--numeric"};
#define array_size(x) (sizeof(x)/sizeof(*x))
int size=array_size(arg);
size_t i,n=size+argc;
char **args=malloc((n)*sizeof(char*));
if(args==NULL)return 255;
for(i=0;i<size;i++){
args[i]=malloc(strlen(arg[i])+1);
if(args[i]==NULL)return 255;
strcpy(args[i],arg[i]);
}
for(i=0;i<argc-1;i++){
args[size+i]=malloc(strlen(argv[i+1])+1);
if(args[size+i]==NULL)return 255;
strcpy(args[size+i],argv[i+1]);
}
// printf ("total args %2zu\n",i);
// for(i=0;i<n;i++)printf ("args[%2zu] : %s\n",i,args[i]);
execv(cmd,args);
return 1;
}
First, compile with:
gcc -Wall program.c -o program
Then, chown and chmod to
chown 0:userB program
chmod 4750 program
And, if you don't have the need to mix hardcoded arguments with passed ones you could just use the following:
#include <unistd.h>
int main(int argc,char *argv[]){
setuid(1000); //user id of userB
char *cmd="/usr/bin/whoami";
execv(cmd,argv);
return 1; // indicate error if execv() didn't succeed.
}
In general, every admin will advise you against adding exceptions to /etc/suders and there's a good reason why.