Esecuzione di una funzione Bash Script con Sudo


22

Ho una sceneggiatura che fa diverse cose, molte delle quali non richiedono alcun privilegio speciale. Tuttavia, una sezione specifica, che ho contenuto all'interno di una funzione, necessita dei privilegi di root.

Non desidero richiedere che l'intero script venga eseguito come root e voglio poter chiamare questa funzione, con i privilegi di root, dall'interno dello script. Chiedere una password, se necessario, non è un problema poiché è comunque per lo più interattivo. Tuttavia, quando provo ad usare sudo functionx, ottengo:

sudo: functionx: command not found

Come mi aspettavo, exportnon ha fatto differenza. Mi piacerebbe essere in grado di eseguire la funzione direttamente nello script anziché scomporla ed eseguirla come script separato per una serie di motivi.

Esiste un modo per rendere "visibile" la mia funzione di sudo senza estrarla, trovare la directory appropriata e quindi eseguirla come script autonomo?

La funzione riguarda una pagina lunga e contiene più stringhe, alcune tra virgolette doppie e altre tra virgolette singole. Dipende anche da una funzione di menu definita altrove nello script principale.

Mi aspetterei solo che qualcuno con sudo ANY sia in grado di eseguire la funzione, poiché una delle cose che fa è cambiare le password.


Il fatto che ci siano diverse funzioni coinvolte lo rende ancora più complicato e incline al fallimento. Ora devi trovare tutte queste dipendenze (e anche tutte le loro dipendenze, se ce ne sono ... fino a livelli molto profondi) comprese tutte le altre funzioni che la funzione di menu potrebbe chiamare e declareanche loro.
Cas

D'accordo, e potrei dover solo mordere il proiettile e romperlo (e fare del mio meglio per determinare con precisione il percorso da cui è stato eseguito, oltre a sperare che l'utente finale mantenga i file insieme) se non ci sono alternative migliori.
BryKKan,

Risposte:


19

Devo ammettere che non esiste un modo semplice e intuitivo per farlo, e questo è un po 'complicato. Ma puoi farlo in questo modo:

function hello()
{
    echo "Hello!"
}

# Test that it works.
hello

FUNC=$(declare -f hello)
sudo bash -c "$FUNC; hello"

O più semplicemente:

sudo bash -c "$(declare -f hello); hello"

Per me funziona:

$ bash --version
GNU bash, version 4.3.42(1)-release (x86_64-apple-darwin14.5.0)
$ hello
Hello!
$
$ FUNC=$(declare -f hello)
$ sudo bash -c "$FUNC; hello"
Hello!

Fondamentalmente, declare -frestituirà il contenuto della funzione, che passerà quindi in bash -clinea.

Se si desidera esportare tutte le funzioni dall'istanza esterna di bash, passare FUNC=$(declare -f hello)a FUNC=$(declare -f).

modificare

Per rispondere ai commenti sull'offerta, vedi questo esempio:

$ hello()
> {
> echo "This 'is a' test."
> }
$ declare -f hello
hello ()
{
    echo "This 'is a' test."
}
$ FUNC=$(declare -f hello)
$ sudo bash -c "$FUNC; hello"
Password:
This 'is a' test.

1
Funziona solo per caso, perché echo "Hello!"è effettivamente lo stesso di echo Hello!(le doppie virgolette non fanno differenza per questo particolare comando echo). In molte / molte altre circostanze, è probabile che le doppie virgolette nella funzione interrompano il bash -ccomando.
Cas

1
Questo risponde alla domanda originale, quindi se non trovo una soluzione migliore la accetterò. Tuttavia, interrompe la mia funzione particolare (vedi la mia modifica) poiché dipende dalle funzioni definite altrove nello script.
BryKKan,

1
Ho fatto qualche test all'inizio di questo pomeriggio (usando bash -xce non solo bash -c) e sembra che bashè abbastanza intelligente per le cose ri-citazione in questa situazione, fino al punto di sostituire virgolette con le virgolette singole e cambiare 'a '\''se necessario. Sono sicuro che ci saranno alcuni casi che non può gestire, ma sicuramente funziona per casi almeno semplici e moderatamente complessi - ad esempio, provafunction hello() { filename="this is a 'filename' with single quotes and spaces" ; echo "$filename" ; } ; FUNC=$(declare -f hello) ; bash -xc "$FUNC ; hello"
cas

4
@cas declare -fstampa la definizione della funzione in un modo che può essere ri-analizzato da bash, così bash -c "$(declare -f)" fa funzionare correttamente (supponendo che il guscio esterno è anche bash). L'esempio che hai pubblicato mostra che funziona correttamente - dove le virgolette sono state modificate è nella traccia , perché bash stampa le tracce nella sintassi della shell, ad esempio provabash -xc 'echo "hello world"'
SO di Gilles- smetti di essere malvagio '

3
Risposta eccellente. Ho implementato la tua soluzione - Vorrei notare che puoi importare lo script stesso all'interno dello script, a condizione che lo annidi in un condizionale che controlla se non sudo yourFunctionviene trovato (altrimenti si ottiene un errore di segmentazione dalla ricorsione)
GrayedFox

5

Il "problema" è che sudocancella l'ambiente (ad eccezione di una manciata di variabili consentite) e imposta alcune variabili su valori di sicurezza predefiniti al fine di proteggere dai rischi per la sicurezza. in altre parole, questo non è in realtà un problema. È una caratteristica.

Ad esempio, se si imposta PATH="/path/to/myevildirectory:$PATH"e sudonon si imposta PATH su un valore predefinito, qualsiasi script che non specifica il percorso completo su TUTTI i comandi che esegue (ovvero la maggior parte degli script) cercherà /path/to/myevildirectoryprima di qualsiasi altra directory. Inserisci comandi come lso grepo altri strumenti comuni e puoi facilmente fare quello che vuoi sul sistema.

Il modo più semplice / migliore è riscrivere la funzione come script e salvarla da qualche parte nel percorso (o specificare il percorso completo dello script dalla sudoriga di comando - cosa che dovrete comunque fare a meno che non sudosia configurato per consentirvi per eseguire QUALSIASI comando come root) e renderlo eseguibile conchmod +x /path/to/scriptname.sh

Riscrittura di una funzione di shell come script è così semplice come salvare i comandi all'interno della definizione di funzione in un file (senza function ..., {e }linee).


Questo non risponde in alcun modo alla domanda. Vuole specificamente evitare di metterlo in una sceneggiatura.
Will

1
sudo -EEvita anche di ripulire l'ambiente.
Will

Capisco in parte perché sta accadendo. Speravo ci fossero dei mezzi per scavalcare temporaneamente questo comportamento. Da qualche altra parte è stata menzionata un'opzione -E, anche se in questo caso non ha funzionato. Sfortunatamente, mentre apprezzo la spiegazione di come renderlo uno script autonomo, ciò non risponde specificamente alla domanda, perché volevo un modo per evitarlo. Non ho alcun controllo sul luogo in cui l'utente finale posiziona la sceneggiatura e vorrei evitare sia le directory hardcoded sia la canzone e la danza di cercare di determinare con precisione da dove è stata eseguita la sceneggiatura principale.
BryKKan,

non importa se questo è ciò che l'OP ha richiesto o meno. Se ciò che vuole o non funziona o può essere fatto funzionare solo facendo qualcosa di estremamente insicuro, allora devono essere informati e fornire loro un'alternativa, anche se l'alternativa è qualcosa che hanno dichiarato esplicitamente di non volere (perché a volte è l'unico o il modo migliore per farlo in sicurezza). Sarebbe irresponsabile dire a qualcuno come spararsi al piede senza avvisarli delle probabili conseguenze di puntare una pistola ai piedi e premere il grilletto.
Cas

1
@cas Questo è vero. Non può essere fatto in modo sicuro è una risposta accettabile in alcune circostanze. Vedi la mia ultima modifica però. Sarei curioso di sapere se la tua opinione sulle implicazioni di sicurezza è la stessa dato questo.
BryKKan,

3

Ho scritto la mia Sudofunzione bash per farlo, funziona per chiamare funzioni e alias:

function Sudo {
        local firstArg=$1
        if [ $(type -t $firstArg) = function ]
        then
                shift && command sudo bash -c "$(declare -f $firstArg);$firstArg $*"
        elif [ $(type -t $firstArg) = alias ]
        then
                alias sudo='\sudo '
                eval "sudo $@"
        else
                command sudo "$@"
        fi
}

2

È possibile combinare funzioni e alias

Esempio:

function hello_fn() {
    echo "Hello!" 
}

alias hello='bash -c "$(declare -f hello_fn); hello_fn"' 
alias sudo='sudo '

quindi sudo hellofunziona


1

Ecco una variazione sulla risposta di Will . Implica un ulteriore catprocesso, ma offre il comfort dell'eredità. In breve, va così:

f () 
{
    echo ok;
}

cat <<EOS | sudo bash
$(declare -f f)
f
EOS

Se vuoi più cibo per pensare, prova questo:

#!/bin/bash

f () 
{ 
    x="a b"; 
    menu "$x"; 
    y="difficult thing"; 
    echo "a $y to parse"; 
}

menu () 
{
    [ "$1" == "a b" ] && 
    echo "here's the menu"; 
}

cat <<EOS | sudo bash
$(declare -f f)
$(declare -f menu)
f
EOS

L'output è:

here's the menu
a difficult thing to pass

Qui abbiamo la menufunzione corrispondente a quella nella domanda, che è "definita altrove nello script principale". Se "altrove" significa che la sua definizione è già stata letta in questa fase quando sudoviene eseguita la funzione impegnativa , la situazione è analoga. Ma potrebbe non essere stato ancora letto. Potrebbe esserci un'altra funzione che attiverà ancora la sua definizione. In questo caso declare -f menudeve essere sostituito con qualcosa di più sofisticato, o l'intero script corretto in un modo che la menufunzione è già dichiarata.


Molto interessante. Dovrò provarlo ad un certo punto. E sì, la menufunzione sarebbe stata dichiarata prima di questo punto, come fviene invocata da un menu.
BryKKan,

0

Supponendo che il tuo script sia (a) autonomo o (b) possa generare i suoi componenti in base alla sua posizione (piuttosto che ricordare dove si trova la tua home directory), potresti fare qualcosa del genere:

  • usa il $0percorso per lo script, usando quello nel sudocomando, e passa un'opzione che lo script controllerà, per chiamare l'aggiornamento della password. Fintanto che fai affidamento sul trovare lo script nel percorso (piuttosto che semplicemente eseguirlo ./myscript), dovresti ottenere un percorso assoluto in $0.
  • poiché sudoesegue lo script, ha accesso alle funzioni necessarie nello script.
  • nella parte superiore dello script (piuttosto, oltre le dichiarazioni delle funzioni), lo script verificherà uide realizzerà che è stato eseguito come utente root e vedendo che ha l'opzione impostata per dirgli di aggiornare la password, andare e fare quello.

Gli script possono ricorrere su se stessi per vari motivi: cambiare i privilegi è uno di questi.

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.