ArchieTiger
ArchieTiger

Reputation: 2243

subprocess.Popen for "ssh sudo -u user -i" is failing with "sudo: sorry, you must have a tty to run sudo"

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

Upvotes: 3

Views: 1318

Answers (3)

Martin Prikryl
Martin Prikryl

Reputation: 202222

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.

Upvotes: 5

GGets
GGets

Reputation: 416

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.

Upvotes: 0

bbaassssiiee
bbaassssiiee

Reputation: 6782

Inserting this line in /etc/sudoers on the remote server will resolve the issue:

Defaults    !requiretty

Upvotes: 0

Related Questions