Executando uma função Bash Script com Sudo

22

Eu tenho um script que faz várias coisas diferentes, a maioria das quais não requer privilégios especiais. No entanto, uma seção específica, que eu conti em uma função, precisa de privilégios de root.

Não desejo exigir que o script inteiro seja executado como root e quero poder chamar essa função, com privilégios de root, de dentro do script. Solicitar uma senha, se necessário, não é um problema, pois, na maioria das vezes, é interativo. No entanto, quando tento usar sudo functionx, recebo:

sudo: functionx: command not found

Como eu esperava, exportnão fez diferença. Eu gostaria de poder executar a função diretamente no script, em vez de quebrá-la e executá-la como um script separado por vários motivos.

Existe alguma maneira de tornar minha função "visível" para o sudo sem extraí-la, encontrar o diretório apropriado e executá-la como um script independente?

A função tem aproximadamente uma página e contém várias seqüências de caracteres, algumas com aspas duplas e outras com aspas simples. Também depende de uma função de menu definida em outra parte do script principal.

Eu só esperaria que alguém com sudo QUALQUER capaz de executar a função, pois uma das coisas que faz é alterar as senhas.

BryKKan
fonte
O fato de haver várias funções envolvidas torna ainda mais complicado e propenso a falhas. Agora você precisa encontrar todas essas dependências (e todas as dependências delas, se houver ... até os níveis mais profundos), incluindo outras funções que a função de menu possa chamar e declaretambém elas.
11486
Concordo, e talvez seja necessário apenas morder a bala e quebrá-la (e fazer o possível para determinar com precisão o caminho a partir do qual ela foi executada, além de esperar que o usuário final mantenha os arquivos juntos) se não houver alternativas melhores.
BryKKan

Respostas:

19

Admito que não há uma maneira simples e intuitiva de fazer isso, e isso é um pouco hackey. Mas você pode fazer assim:

function hello()
{
    echo "Hello!"
}

# Test that it works.
hello

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

Ou, mais simplesmente:

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

Funciona para mim:

$ 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!

Basicamente, declare -fretornará o conteúdo da função, que você passará para a bash -clinha.

Se você deseja exportar todas as funções da instância externa do bash, altere FUNC=$(declare -f hello)para FUNC=$(declare -f).

Editar

Para abordar os comentários sobre a citação, consulte este exemplo:

$ 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.
Vai
fonte
11
Isso funciona apenas por acidente, porque echo "Hello!"é efetivamente o mesmo que echo Hello!(ou seja, aspas duplas não fazem diferença para esse comando de eco específico). Em muitas / na maioria das outras circunstâncias, as aspas duplas na função provavelmente quebrarão o bash -ccomando.
cas
11
Isso responde à pergunta original; portanto, se eu não conseguir uma solução melhor, eu a aceitarei. No entanto, ele interrompe minha função específica (consulte minha edição), pois depende de funções definidas em outras partes do script.
BryKKan
11
Eu fiz alguns testes no início desta tarde (usando bash -xce não apenas bash -c) e parece bashser inteligente o suficiente para re-citar as coisas nessa situação, mesmo na medida de substituir aspas duplas por aspas simples e mudar 'para, '\''se necessário. Eu tenho certeza que haverá alguns casos que ele não pode resolver, mas definitivamente funciona para casos pelo menos simples e moderadamente complexos - por exemplo, tryfunction hello() { filename="this is a 'filename' with single quotes and spaces" ; echo "$filename" ; } ; FUNC=$(declare -f hello) ; bash -xc "$FUNC ; hello"
cas
4
O @cas declare -fimprime a definição da função de uma maneira que pode ser analisada novamente pelo bash, assim bash -c "$(declare -f)" como funciona corretamente (assumindo que o shell externo também seja do bash). O exemplo que você postou mostra-lo funcionando corretamente - onde as citações foram alteradas é no rastreamento , porque festança imprime traços em sintaxe shell, por exemplo, tentarbash -xc 'echo "hello world"'
Gilles 'SO parada sendo mal'
3
Excelente resposta. I implementado sua solução - gostaria de observar que você pode importar o próprio script de dentro do script, desde que você ninho lo dentro de uma condicional que verifica se contra sudo yourFunctionsendo encontrado (caso contrário, você recebe um erro de segmentação da recursão)
GrayedFox
5

O "problema" é que sudolimpa o ambiente (exceto algumas variáveis ​​permitidas) e define algumas variáveis ​​com valores seguros predefinidos para proteger contra riscos de segurança. em outras palavras, isso não é realmente um problema. É uma característica.

Por exemplo, se você definiu PATH="/path/to/myevildirectory:$PATH"e sudonão definiu PATH para um valor predefinido, qualquer script que não especificasse o nome completo do caminho para TODOS os comandos executados (ou seja, a maioria dos scripts) procuraria /path/to/myevildirectoryantes de qualquer outro diretório. Coloque comandos como lsou grepou outras ferramentas comuns lá e você poderá facilmente fazer o que quiser no sistema.

A maneira mais fácil / melhor é reescrever a função como um script e salvá-la em algum lugar no caminho (ou especificar o caminho completo para o script na sudolinha de comando - o que você precisará fazer de qualquer maneira, a menos que sudoesteja configurado para permitir que você para executar QUALQUER comando como root) e torná-lo executável comchmod +x /path/to/scriptname.sh

Reescrever uma função shell como um script é tão simples como apenas salvar os comandos dentro da definição da função para um arquivo (sem a function ..., {e }linhas).

cas
fonte
Isso não responde à pergunta de forma alguma. Ele especificamente quer evitar colocá-lo em um script.
Will
11
sudo -EEvita também limpar o meio ambiente.
Will
Eu entendo até certo ponto por que isso está acontecendo. Eu esperava que houvesse algum meio de substituir temporariamente esse comportamento. Em algum outro lugar, uma opção -E foi mencionada, embora não funcionasse nesse caso. Infelizmente, embora eu aprecie a explicação de como torná-lo um script independente, isso especificamente não responde à pergunta, porque eu queria um meio de evitar isso. Não tenho controle sobre onde o usuário final coloca o script e gostaria de evitar diretórios codificados e a música e a dança de tentar determinar com precisão de onde o script principal foi executado.
BryKKan
não importa se é isso que o OP pediu ou não. Se o que ele quer ou não funciona ou apenas pode ser feito fazendo algo extremamente inseguro, é necessário que lhes digam isso e uma alternativa - mesmo que a alternativa seja algo que eles declararam explicitamente que não querem (porque às vezes é a única ou a melhor maneira de fazê-lo com segurança). Seria irresponsável dizer a alguém como dar um tiro no próprio pé sem avisar sobre as prováveis ​​consequências de apontar uma arma para os pés e apertar o gatilho.
11556
11
@cas Isso é verdade. Isso não pode ser feito com segurança. É uma resposta aceitável em algumas circunstâncias. Veja minha última edição. Gostaria de saber se a sua opinião sobre as implicações de segurança é a mesma, considerando isso.
BryKKan
3

Eu escrevi minha própria Sudofunção bash para fazer isso, funciona para chamar funções e aliases:

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
}
SebMa
fonte
2

Você pode combinar funções e aliases

Exemplo:

function hello_fn() {
    echo "Hello!" 
}

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

então sudo hellofunciona

hbt
fonte
1

Aqui está uma variação da resposta de Will . Envolve um catprocesso adicional , mas oferece o conforto do heredoc. Em poucas palavras, é assim:

f () 
{
    echo ok;
}

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

Se você quiser mais informações, tente o seguinte:

#!/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

A saída é:

here's the menu
a difficult thing to pass

Aqui temos a menufunção correspondente à da pergunta, que é "definida em outra parte do script principal". Se "em outro lugar" significa que sua definição já foi lida nesta fase quando a função exigente sudoestá sendo executada, a situação é análoga. Mas pode não ter sido lido ainda. Pode haver outra função que ainda acionará sua definição. Nesse caso declare -f menu, deve ser substituído por algo mais sofisticado ou o script inteiro corrigido de forma que a menufunção já esteja declarada.

Tomasz
fonte
Muito interessante. Vou ter que experimentar em algum momento. E sim, a menufunção teria sido declarada antes deste ponto, como fé chamada em um menu.
precisa saber é o seguinte
0

Supondo que seu script seja (a) independente ou (b) possa obter seus componentes com base em sua localização (em vez de lembrar onde está o diretório inicial), você pode fazer algo assim:

  • use o $0nome do caminho para o script, usando o sudocomando, e passe uma opção que o script verificará para chamar a atualização da senha. Contanto que você encontre o script no caminho (em vez de apenas executar ./myscript), deverá obter um nome de caminho absoluto $0.
  • desde que sudoexecuta o script, ele tem acesso às funções necessárias no script.
  • na parte superior do script (em vez disso, após as declarações de função), o script verificaria o seu uide perceberia que ele foi executado como usuário raiz e, como ele possui a opção definida para solicitar a atualização da senha, vá e faça naquela.

Os scripts podem se repetir por vários motivos: a alteração de privilégios é um deles.

Thomas Dickey
fonte