Perché non possiamo eseguire un elenco di comandi come utente diverso senza sudo?


8

Questa domanda è stata posta in modo diverso in altri forum. Ma non c'è stata una spiegazione decente per cui non puoi fare il seguito in bash.

#!/bin/bash
command1
SWITCH_USER_TO rag
command2
command3

Di solito, il modo suggerito è

#!/bin/bash
command1
sudo -u rag command2
sudo -u rag command3

ma perché non è possibile bashpassare a un altro utente ad un certo punto durante l'esecuzione dello script bash ed eseguire il resto dei comandi come un altro utente?


Questa domanda è stata posta anche su Superuser, all'indirizzo superuser.com/questions/93385/… , e questa risposta superuser.com/a/573882/76251 fornisce un metodo QUI DOC per cambiare gli utenti all'interno di uno script.
Tim Kennedy,

Risposte:


4

L'informazione è già nella mia altra risposta , ma è un po 'sepolta lì. Quindi ho pensato di aggiungerlo qui.

bashnon ha disposizioni per cambiare utente, ma lo zshfa.

In zsh, si modificano gli utenti assegnando valori a tali variabili:

  • $EUID: modifica l'ID utente effettivo (e nient'altro). Di solito, puoi cambiare l'euid tra il tuo userid reale e l'id utente set salvato (se chiamato da un eseguibile setuid) o cambiare qualsiasi cosa se l'euid è 0.
  • $UID: modifica l'ID utente effettivo, l'ID utente reale e l'ID utente impostato salvato sul nuovo valore. A meno che quel nuovo valore sia 0, non si può tornare indietro, poiché una volta che tutti e 3 sono stati impostati sullo stesso valore, non c'è modo di cambiarlo in nient'altro.
  • $EGIDe $GID: stessa cosa ma per ID di gruppo.
  • $USERNAME. È come usare sudoo su. Imposta il tuo euid, ruid, ssuid sull'uid di quell'utente. Imposta inoltre egid, rgid e ssgid e gruppi supplementari in base alle appartenenze ai gruppi definite nel database utenti. Ad esempio $UID, a meno che non sia impostato $USERNAMEsu root, non è possibile tornare indietro, ma ad esempio $UID, è possibile modificare l'utente solo per una subshell.

Se esegui questi script come "root":

#! /bin/zsh -
UID=0 # make sure all our uids are 0

id -u # run a command as root

EUID=1000

id -u # run a command as uid 1000 (but the real user id is still 0
      # so that command would be able to change its euid to that.
      # As for the gids, we only have those we had initially, so 
      # if started as "sudo the-script", only the groups root is a
      # member of.

EUID=0 # we're allowed to do that because our ruid is 0. We need to do
       # that because as a non-priviledged user, we can't set our euid
       # to anything else.

EUID=1001 # now we can change our euid since we're superuser again.

id -u # same as above

Ora, per cambiare utente come in sudoo su, possiamo farlo solo usando subshells, altrimenti potremmo farlo solo una volta:

#! /bin/zsh -

id -u # run as root

(
  USERNAME=rag
  # that's a subshell running as "rag"

  id # see all relevant group memberships are applied
)
# now back to the parent shell process running as root

(
  USERNAME=stephane
  # another subshell this time running as "stephane"

  id
)

8

Offerte del kernel man 2 setuide amici.

Ora funziona sul processo di chiamata. Ancora più importante, non puoi elevare i tuoi privilegi. Ecco perché sue il sudobit SETUID è impostato in modo che vengano eseguiti sempre con i privilegi più alti ( root) e rilasciati di conseguenza all'utente desiderato.

Questa combinazione significa che non puoi cambiare l'UID della shell eseguendo un altro programma per farlo (ecco perché non puoi aspettarti sudoo nessun altro programma per farlo) e non puoi (come shell) farlo da solo a meno che tu sei disposto a eseguire come root o lo stesso utente a cui desideri passare, il che non ha senso. Inoltre, una volta rilasciati i privilegi, non c'è modo di tornare indietro.


6

Bene, puoi sempre fare:

#! /bin/bash -
{ shopt -s expand_aliases;SWITCH_TO_USER(){ { _u=$*;_x="$(declare;alias
shopt -p;set +o);"'set -- "${_a[@]}";unset _x _a';set +x;} 2>/dev/null
exec sudo -u "$1" env "_x=$_x" bash -c 'eval "$_x" 2> /dev/null;. "$0"
' "$0";};alias skip=":||:<<'SWITCH_TO_USER $_u'"
alias SWITCH_TO_USER="{ eval '"'_a=("$@")'"';} 2>/dev/null;SWITCH_TO_USER"
${_u+:} alias skip=:;} 2>/dev/null
skip

echo test
a=foo
set a b

SWITCH_TO_USER root

echo "$a and $1 as $(id -un)"
set -x
foo() { echo "bar as $(id -un)"; }

SWITCH_TO_USER rag

foo
set +x

SWITCH_TO_USER root again

echo "hi again from $(id -un)"

(ʘ‿ʘ)

Ciò è iniziato inizialmente come uno scherzo poiché implementa ciò che è richiesto, sebbene probabilmente non esattamente come previsto, e non è praticamente utile. Ma mentre si è evoluto in qualcosa che funziona in una certa misura e coinvolge alcuni bei hack, ecco una piccola spiegazione:

Come ha detto Miroslav , se lasciamo da parte le funzionalità in stile Linux (che non aiuterebbero comunque qui), l'unico modo per un processo senza privilegi di cambiare uid è eseguendo un eseguibile setuid.

Tuttavia, una volta ottenuto il privilegio di superutente (eseguendo un eseguibile setuid il cui proprietario è root, ad esempio), è possibile cambiare l'ID utente effettivo avanti e indietro tra l'ID utente originale, 0 e qualsiasi altro id a meno che non si rinunci all'ID utente set salvato ( come cose come sudoo in sugenere fanno).

Per esempio:

$ sudo cp /usr/bin/env .
$ sudo chmod 4755 ./env

Ora ho un envcomando che mi permette di eseguire qualsiasi comando con un ID utente efficace e un ID utente impostato salvato pari a 0 (il mio ID utente reale è ancora 1000):

$ ./env id -u
0
$ ./env id -ru
1000
$ ./env -u PATH =perl -e '$>=1; system("id -u"); $>=0;$>=2; system("id -u");
   $>=0; $>=$<=3; system("id -ru; id -u"); $>=0;$<=$>=4; system("id -ru; id -u")'
1
2
3
3
4
4

perlha wrapper per setuid/ seteuid(quelli $>e $<variabili).

Anche zsh:

$ sudo zsh -c 'EUID=1; id -u; EUID=0; EUID=2; id -u'
1
2

Sebbene sopra quei idcomandi vengano chiamati con un vero ID utente e salvato un set userid di 0 (anche se se avessi usato il mio ./envinvece di sudoquello sarebbe stato solo il set salvato userid, mentre il vero ID utente sarebbe rimasto 1000), il che significa che se fossero comandi non attendibili, potrebbero comunque arrecare danno, quindi ti consigliamo di scriverlo come:

$ sudo zsh -c 'UID=1 id -u; UID=2 id -u'

(che è impostato su tutti gli uid (set effettivo, reale e salvato) solo per l'esecuzione di quei comandi.

bashnon ha alcun modo per modificare gli ID utente. Quindi, anche se avessi un eseguibile setuid con cui chiamare il tuo bashscript, questo non sarebbe d'aiuto.

Con bash, ti resta con l'esecuzione di un eseguibile setuid ogni volta che vuoi cambiare uid.

L'idea nello script sopra è su una chiamata a SWITCH_TO_USER, per eseguire una nuova istanza bash per eseguire il resto dello script.

SWITCH_TO_USER someuserè più o meno una funzione che esegue nuovamente lo script come un altro utente (usando sudo) ma salta l'inizio dello script fino a quando SWITCH_TO_USER someuser.

Dove diventa difficile è che vogliamo mantenere lo stato della bash corrente dopo aver avviato la nuova bash come utente diverso.

Analizziamolo:

{ shopt -s expand_aliases;

Avremo bisogno di alias. Uno dei trucchi di questo script è saltare la parte dello script fino a quando SWITCH_TO_USER someuser, con qualcosa del tipo:

:||: << 'SWITCH_TO_USER someuser'
part to skip
SWITCH_TO_USER

Tale modulo è simile a quello #if 0utilizzato in C, ovvero un modo per commentare completamente un codice.

:è una no-op che ritorna vera. Quindi : || :, il secondo :non viene mai eseguito. Tuttavia, viene analizzato. E << 'xxx'c'è una forma di documento qui dove (perché xxxè citato), non viene fatta alcuna espansione o interpretazione.

Avremmo potuto fare:

: << 'SWITCH_TO_USER someuser'
part to skip
SWITCH_TO_USER

Ma ciò avrebbe significato che il documento qui avrebbe dovuto essere scritto e trasmesso come standard :. :||:evita quello.

Ora, dove diventa confuso è che usiamo il fatto che bashespande gli alias molto presto nel suo processo di analisi. Essere skipalias della :||: << 'SWITCH_TO_USER someuther'parte del costrutto per commentare .

Andiamo avanti:

SWITCH_TO_USER(){ { _u=$*;_x="$(declare;alias
shopt -p;set +o);"'set -- "${_a[@]}";unset _x _a';set +x;} 2>/dev/null
exec sudo -u "$1" env "_x=$_x" bash -c 'eval "$_x" 2> /dev/null;. "$0"
' "$0";}

Ecco la definizione della funzione SWITCH_TO_USER . Vedremo di seguito che SWITCH_TO_USER alla fine sarà un alias racchiuso in quella funzione.

Tale funzione svolge la maggior parte della riesecuzione dello script. Alla fine vediamo che si ri-esegue (nello stesso processo a causa di exec) bashcon la _xvariabile nel suo ambiente (usiamo envqui perché di sudosolito igienizza il suo ambiente e non consente il passaggio di ambiti arbitrari attraverso l'altro). Ciò bashvaluta il contenuto di quella $_xvariabile come codice bash e genera lo script stesso.

_x è definito in precedenza come:

_x="$(declare;alias;shopt -p;set +o);"'set -- "${_a[@]}";unset _x _a'

Tutti i declare, alias, shopt -p set +ouscita costituiscono un dump dello stato interno del guscio. Cioè, scaricano la definizione di tutte le variabili, funzioni, alias e opzioni come codice shell pronto per essere valutato. Inoltre, aggiungiamo l'impostazione dei parametri posizionali ( $1, $2...) in base al valore $_adell'array (vedi sotto) e alcuni clean up in modo che l'enorme $_xvariabile non rimanga nell'ambiente per il resto della sceneggiatura.

Noterai che la prima parte fino a set +xè racchiusa in un gruppo di comandi il cui stderr viene reindirizzato a /dev/null( {...} 2> /dev/null). Questo perché, se ad un certo punto dello script set -x(o set -o xtrace) viene eseguito, non vogliamo che quel preambolo generi tracce perché vogliamo renderlo il meno invasivo possibile. Quindi eseguiamo un set +x(dopo esserci assicurati di scaricare xtracepreventivamente le opzioni (incluse )) in cui le tracce vengono inviate a / dev / null.

Lo eval "$_X"stderr viene anche reindirizzato su / dev / null per ragioni simili, ma anche per evitare errori di scrittura nel tentativo di variabili speciali di sola lettura.

Andiamo avanti con la sceneggiatura:

alias skip=":||:<<'SWITCH_TO_USER $_u'"

Questo è il nostro trucco descritto sopra. L'invocazione iniziale dello script verrà annullata (vedi sotto).

alias SWITCH_TO_USER="{ eval '"'_a=("$@")'"';} 2>/dev/null;SWITCH_TO_USER"

Ora il wrapper alias attorno a SWITCH_TO_USER. Il motivo principale è riuscire a passare i parametri posizionali ( $1, $2...) al nuovo bashche interpreterà il resto dello script. Non abbiamo potuto farlo nella SWITCH_TO_USER funzione perché all'interno della funzione, "$@"sono gli argomenti alle funzioni, non quelli degli script. Il reindirizzamento stderr su / dev / null è di nuovo per nascondere xtraces e evaldeve aggirare un bug bash. Quindi chiamiamo la SWITCH_TO_USER funzione .

${_u+:} alias skip=:

Quella parte annulla l' skipalias (lo sostituisce con il :comando no-op) a meno che non $_usia impostata la variabile.

skip

Questo è il nostro skipalias. Alla prima invocazione, sarà solo :(la no-op). Su subsequence re-invocazioni, sarà qualcosa di simile a: :||: << 'SWITCH_TO_USER root'.

echo test
a=foo
set a b

SWITCH_TO_USER root

Quindi qui, ad esempio, a quel punto, riprendiamo lo script come rootutente e lo script ripristinerà lo stato salvato, salterà su quella SWITCH_TO_USER rootlinea e proseguirà.

Ciò significa che deve essere scritto esattamente come stat, con SWITCH_TO_USERall'inizio della riga e con esattamente uno spazio tra gli argomenti.

La maggior parte dello stato, stdin, stdout e stderr saranno conservati, ma non gli altri descrittori di file perché in sudogenere li chiudono a meno che non siano esplicitamente configurati per non. Quindi per esempio:

exec 3> some-file
SWITCH_TO_USER bob
echo test >&3

in genere non funzionerà.

Nota anche che se lo fai:

SWITCH_TO_USER alice
SWITCH_TO_USER bob
SWITCH_TO_USER root

Funziona solo se hai il diritto di sudoas alicee aliceha il diritto di sudoas bobe bobas root.

Quindi, in pratica, questo non è davvero utile. Usare suinvece di sudo(o una sudoconfigurazione in cui sudoautentica l'utente target anziché il chiamante) potrebbe avere un po 'più senso, ma ciò significherebbe comunque che dovresti conoscere le password di tutti quei tipi.


puoi per favore spiegare cosa sta facendo la tua sceneggiatura?
straccio

@rag, l'hai chiesto tu ...
Stéphane Chazelas,

1
È la tua voce per il ioshcc? Avresti dovuto inserire solo una riga.
ott--

-2

Il comando "sudo -u ram sh" può essere usato per cambiare l'utente "ram", eseguendo il comando "sh" o shell. Il comando "esci" ti riporterà all'utente originale.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.