Menjalankan Fungsi Bash Script dengan Sudo

22

Saya memiliki skrip yang melakukan sejumlah hal yang berbeda, yang sebagian besar tidak memerlukan hak khusus. Namun, satu bagian spesifik, yang saya isi di dalam suatu fungsi, membutuhkan hak akses root.

Saya tidak ingin mengharuskan seluruh skrip dijalankan sebagai root, dan saya ingin dapat memanggil fungsi ini, dengan hak akses root, dari dalam skrip. Meminta kata sandi jika perlu bukanlah masalah karena sebagian besar bersifat interaktif. Namun, ketika saya mencoba menggunakan sudo functionx, saya mendapatkan:

sudo: functionx: command not found

Seperti yang saya harapkan, exporttidak ada bedanya. Saya ingin dapat menjalankan fungsi secara langsung dalam skrip alih-alih memecahnya dan menjalankannya sebagai skrip terpisah karena sejumlah alasan.

Apakah ada cara agar fungsi saya "terlihat" oleh sudo tanpa mengekstraknya, menemukan direktori yang sesuai, dan kemudian menjalankannya sebagai skrip yang berdiri sendiri?

Fungsinya sekitar satu halaman sendiri dan berisi banyak string, beberapa kutip ganda dan beberapa kutip tunggal. Ini juga tergantung pada fungsi menu yang didefinisikan di tempat lain dalam skrip utama.

Saya hanya berharap seseorang dengan sudo ANY untuk dapat menjalankan fungsi, karena salah satu hal yang dilakukannya adalah mengubah kata sandi.

BryKKan
sumber
Fakta bahwa ada beberapa fungsi yang terlibat membuatnya semakin rumit dan rentan terhadap kegagalan. Anda sekarang harus menemukan semua dependensi seperti itu (dan semua dependensinya juga, jika ada ... hingga level berapa dalam) termasuk fungsi lain yang mungkin dipanggil oleh fungsi menu dan declaremereka juga.
cas
Setuju, dan saya mungkin harus menggigit peluru dan memecahnya (dan melakukan yang terbaik untuk secara akurat menentukan jalan dari mana ia dijalankan, ditambah harapan pengguna akhir menyimpan file bersama) jika tidak ada alternatif yang lebih baik.
BryKKan

Jawaban:

19

Saya akan mengakui bahwa tidak ada cara sederhana dan intuitif untuk melakukan ini, dan ini sedikit hackey. Tapi, Anda bisa melakukannya seperti ini:

function hello()
{
    echo "Hello!"
}

# Test that it works.
hello

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

Atau lebih sederhana:

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

Ini bekerja untuk saya:

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

Pada dasarnya, declare -fakan mengembalikan konten fungsi, yang kemudian Anda sampaikan ke bash -cinline.

Jika Anda ingin mengekspor semua fungsi dari instance luar bash, ubah FUNC=$(declare -f hello)ke FUNC=$(declare -f).

Edit

Untuk membahas komentar tentang mengutip, lihat contoh ini:

$ 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.
Akan
sumber
1
Ini hanya berfungsi secara kebetulan, karena echo "Hello!"secara efektif sama dengan echo Hello!(yaitu tanda kutip ganda tidak membuat perbedaan untuk perintah gema khusus ini). Dalam banyak / sebagian besar keadaan lain, tanda kutip ganda dalam fungsi cenderung melanggar bash -cperintah.
cas
1
Ini menjawab pertanyaan awal, jadi jika saya tidak mendapatkan solusi yang lebih baik saya akan menerimanya. Namun, itu merusak fungsi khusus saya (lihat edit saya) karena itu tergantung pada fungsi yang didefinisikan di tempat lain dalam skrip.
BryKKan
1
saya melakukan beberapa pengujian awal sore ini (menggunakan bash -xcbukan hanya bash -c) dan sepertinya bashcukup pintar untuk re-quote hal dalam situasi ini, bahkan sampai penggantian tanda kutip ganda dengan tanda kutip tunggal dan mengubah 'untuk '\''jika perlu. Saya yakin akan ada beberapa kasus yang tidak bisa ditangani, tetapi pasti berfungsi untuk setidaknya kasus sederhana dan cukup kompleks - misalnya cobafunction 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 -fprint definisi fungsi dengan cara yang dapat re-diurai oleh bash, sehingga bash -c "$(declare -f)" tidak bekerja dengan benar (dengan asumsi bahwa kulit luar juga bash). Contoh yang Anda poskan menunjukkannya berfungsi dengan benar - di mana tanda kutip diubah ada dalam jejak , karena bash mencetak jejak dalam sintaksis shell, mis. Cobabash -xc 'echo "hello world"'
Gilles 'SO- stop being evil'
3
Jawaban yang sangat bagus. Saya menerapkan solusi Anda - Saya akan mencatat bahwa Anda dapat mengimpor skrip itu sendiri dari dalam skrip, asalkan Anda membuatnya dalam kondisi yang memeriksa jika sudo yourFunctiontidak ditemukan (jika tidak Anda mendapatkan kesalahan segmentasi dari rekursi)
GrayedFox
5

"Masalahnya" adalah yang sudomembersihkan lingkungan (kecuali beberapa variabel yang diizinkan) dan menetapkan beberapa variabel ke nilai aman yang telah ditentukan sebelumnya untuk melindungi dari risiko keamanan. dengan kata lain, ini sebenarnya bukan masalah. Itu fitur.

Misalnya, jika Anda menetapkan PATH="/path/to/myevildirectory:$PATH"dan sudotidak menetapkan PATH ke nilai yang ditentukan sebelumnya, maka skrip apa pun yang tidak menentukan pathname lengkap untuk SEMUA perintah yang dijalankannya (yaitu sebagian besar skrip) akan mencari /path/to/myevildirectorysebelum direktori lain. Masukkan perintah seperti lsatau grepatau alat umum lainnya di sana dan Anda dapat dengan mudah melakukan apa pun yang Anda suka di sistem.

Cara termudah / terbaik adalah menulis ulang fungsi sebagai skrip dan menyimpannya di suatu tempat di path (atau menentukan path lengkap ke skrip pada sudobaris perintah - yang perlu Anda lakukan kecuali jika sudodikonfigurasi untuk memungkinkan Anda untuk menjalankan perintah APA SAJA sebagai root), dan membuatnya dapat dijalankan denganchmod +x /path/to/scriptname.sh

Menulis ulang fungsi shell sebagai skrip sesederhana hanya menyimpan perintah di dalam definisi fungsi ke file (tanpa function ..., {dan }baris).

cas
sumber
Ini tidak menjawab pertanyaan dengan cara apa pun. Dia secara khusus ingin menghindari memasukkannya ke dalam naskah.
Will
1
Juga sudo -Ehindari membersihkan lingkungan.
Will
Saya mengerti sampai batas tertentu mengapa itu terjadi. Saya berharap ada beberapa cara untuk sementara meniadakan perilaku ini. Di tempat lain opsi -E disebutkan, meskipun itu tidak berhasil dalam kasus ini. Sayangnya, sementara saya menghargai penjelasan tentang cara membuatnya menjadi skrip mandiri, itu secara khusus tidak menjawab pertanyaan, karena saya ingin cara untuk menghindarinya. Saya tidak memiliki kendali atas di mana pengguna akhir menempatkan skrip dan saya ingin menghindari direktori hard-coded dan lagu serta tarian mencoba untuk secara akurat menentukan di mana skrip utama dijalankan.
BryKKan
tidak masalah apakah itu yang diminta OP atau tidak. Jika apa yang dia inginkan tidak akan berhasil atau hanya dapat dibuat untuk bekerja dengan melakukan sesuatu yang sangat tidak aman maka mereka perlu diberitahu itu dan diberi alternatif - bahkan jika alternatifnya adalah sesuatu yang secara eksplisit mereka katakan tidak mereka inginkan (karena terkadang itulah satu-satunya atau cara terbaik untuk melakukannya dengan aman). Tidak bertanggung jawab untuk memberi tahu seseorang cara menembak diri sendiri di kaki tanpa memberi mereka peringatan tentang konsekuensi yang mungkin timbul dari menodongkan pistol ke kaki mereka dan menarik pelatuknya.
cas
1
@cas Itu benar. Itu tidak dapat dilakukan dengan aman adalah jawaban yang dapat diterima dalam beberapa keadaan. Lihat hasil edit terakhir saya. Saya ingin tahu jika pendapat Anda tentang implikasi keamanan sama dengan itu.
BryKKan
3

Saya telah menulis Sudofungsi bash saya sendiri untuk melakukan itu, berfungsi untuk memanggil fungsi dan 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
}
SebMa
sumber
2

Anda dapat menggabungkan fungsi dan alias

Contoh:

function hello_fn() {
    echo "Hello!" 
}

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

lalu sudo hellobekerja

hbt
sumber
1

Berikut variasi jawaban Will . Ini melibatkan catproses tambahan , tetapi menawarkan kenyamanan heredoc. Singkatnya bunyinya seperti ini:

f () 
{
    echo ok;
}

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

Jika Anda ingin lebih banyak makanan untuk dipikirkan, coba ini:

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

Outputnya adalah:

here's the menu
a difficult thing to pass

Di sini kita punya menufungsi yang sesuai dengan yang ada di pertanyaan, yang "didefinisikan di tempat lain dalam skrip utama". Jika "di tempat lain" berarti definisinya telah dibaca pada tahap ini ketika fungsi sudoyang diminta dijalankan, maka situasinya analog. Tapi itu mungkin belum dibaca. Mungkin ada fungsi lain yang belum memicu definisinya. Dalam hal declare -f menuini harus diganti dengan sesuatu yang lebih canggih, atau seluruh skrip dikoreksi sedemikian rupa sehingga menufungsinya sudah dinyatakan.

Tomasz
sumber
Sangat menarik. Saya harus mencobanya di beberapa titik. Dan ya, menufungsi akan dideklarasikan sebelum titik ini, seperti fyang dipanggil dari menu.
BryKKan
0

Dengan asumsi bahwa skrip Anda (a) mandiri atau (b) dapat mengambil komponennya berdasarkan lokasinya (daripada mengingat di mana direktori home Anda), Anda dapat melakukan sesuatu seperti ini:

  • gunakan $0pathname untuk skrip, gunakan itu dalam sudoperintah, dan berikan opsi yang skrip akan periksa, untuk memanggil pembaruan kata sandi. Selama Anda mengandalkan menemukan skrip di path (bukan hanya menjalankan ./myscript), Anda harus mendapatkan pathname absolut di $0.
  • karena sudomenjalankan skrip, ia memiliki akses ke fungsi yang diperlukan dalam skrip.
  • di bagian atas skrip (lebih tepatnya, melewati deklarasi fungsi), skrip akan memeriksa uiddan menyadari bahwa skrip tersebut dijalankan sebagai pengguna root , dan melihat bahwa ia memiliki opsi yang ditetapkan untuk memberi tahu itu untuk memperbarui kata sandi, pergi dan lakukan bahwa.

Naskah dapat muncul kembali dengan sendirinya karena berbagai alasan: mengubah hak istimewa adalah salah satunya.

Thomas Dickey
sumber