Bash Script İşlevini Sudo ile Çalıştırma


22

Çoğu özel ayrıcalık gerektirmeyen bir dizi farklı şeyler yapan bir komut dosyası var. Ancak, bir işlev içinde bulunduğum belirli bir bölümün kök ayrıcalıklarına ihtiyacı vardır.

Tüm komut dosyasının kök olarak çalışmasını istemiyorum ve bu işlevi, komut dosyasından kök ayrıcalıklarıyla çağırmak istiyorum. Gerekirse bir parola istemek çoğunlukla etkileşimli olduğundan sorun değildir. Ancak, kullanmaya çalıştığımda sudo functionxşunu elde ederim:

sudo: functionx: command not found

Beklediğim gibi exportbir fark yaratmadı. Fonksiyonu, bir çok nedenden ötürü ayrı bir komut dosyası olarak yürütmek yerine, komut dosyasında doğrudan yürütmek istiyorum.

Benim işlevi çıkarmadan, uygun dizini bulmak ve sonra tek başına komut dosyası olarak yürütmeden sudo "görünür" yapmak için bir yolu var mı?

İşlev bir sayfanın kendisiyle ilgilidir ve bazıları çift tırnaklı, bazıları tek tırnaklı olmak üzere birden fazla dize içerir. Ayrıca ana komut dosyasında başka bir yerde tanımlanan bir menü işlevine de bağlıdır.

Sadece sudo HERHANGİ birisinin fonksiyonu çalıştırabilmesini beklerim, çünkü yaptığı şeylerden biri şifreleri değiştirmek.


İlgili birkaç fonksiyonun olması onu daha da karmaşık ve başarısızlığa eğilimli hale getirir. Artık menü işlevinin çağırabileceği diğer işlevler ve declarebunları da dahil olmak üzere tüm bu bağımlılıkları (ve varsa, tüm bağımlılıklarını da ... birçok derinliğe kadar) bulmak zorundasınız.
cas

Kabul ettim ve daha iyi alternatifler yoksa, mermiyi ısırıp kırmak zorundayım (ve çalıştığı yolu doğru bir şekilde belirlemek için elimden geleni yapmalıyım, ayrıca son kullanıcının dosyaları bir arada tutmasını umabilirim).
BryKKan

Yanıtlar:


19

Bunu yapmanın basit, sezgisel bir yolu olmadığını itiraf edeceğim ve bu biraz hokey. Ancak, bunu şu şekilde yapabilirsiniz:

function hello()
{
    echo "Hello!"
}

# Test that it works.
hello

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

Veya daha basit:

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

Benim için çalışıyor:

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

Temel olarak, declare -fdaha sonra bash -csatır içi olarak geçtiğiniz işlevin içeriğini döndürür .

Eğer Bash dış örneğinden tüm işlevleri vermek istiyorsanız, değiştirmek FUNC=$(declare -f hello)için FUNC=$(declare -f).

Düzenle

Alıntıyla ilgili yorumları ele almak için şu örneğe bakın:

$ 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
Bu sadece kazayla çalışır, çünkü echo "Hello!"etkili bir şekilde aynıdır echo Hello!(yani çift tırnaklar bu özel yankı komutu için fark yaratmaz). Diğer birçok / çoğu durumda, işlevdeki çift tırnaklar bash -ckomutu kırabilir .
cas

1
Bu orijinal soruyu cevaplıyor, bu yüzden daha iyi bir çözüm bulamazsam kabul edeceğim. Ancak, komut dosyasında başka bir yerde tanımlanan işlevlere bağlı olduğu için özel işlevimi bozuyor (düzenlememe bakın).
BryKKan

1
Bu öğleden önce bazı testler yaptım ( bash -xcsadece yerine kullanarak bash -c) ve bashbu durumda şeyleri yeniden alıntılamak için yeterince akıllı, hatta çift tırnakları tek tırnaklarla değiştirme 've '\''gerekirse değiştirme ölçüsünde gibi görünüyor . Eminim işleyemeyeceği bazı durumlar olacaktır, ancak kesinlikle en azından basit ve orta derecede karmaşık vakalar için çalışır - örneğinfunction 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 -fböylece yeniden ayrıştırıldı Bash ile olabilir bir şekilde işlev tanımında üzerinden baskılar, bash -c "$(declare -f)" yapar doğru çalışma (dış kabuk aynı zamanda darbe olduğu varsayılarak). Gönderdiğiniz örnek, düzgün çalıştığını gösterir - tırnakların değiştirildiği yer izdedir , çünkü bash kabuk sözdizimindeki izleri yazdırır, örneğin deneyinbash -xc 'echo "hello world"'
Gilles 'SO- kötü olmayı durdurun'

3
Mükemmel cevap. Çözümünüzü uyguladım - Komut dosyasını, bulunup sudo yourFunctionbulunmadığını kontrol eden bir koşul dahilinde iç içe yerleştirmeniz koşuluyla komut dosyasının içinden içe aktarabileceğinizi not ederim (aksi takdirde özyinelemeden bir bölümleme hatası alırsınız)
GrayedFox

5

"Sorun" sudoçevreyi temizler (izin verilen bir avuç değişken hariç) ve bazı değişkenleri güvenlik risklerine karşı korumak için önceden tanımlanmış güvenli değerlere ayarlar. diğer bir deyişle, bu aslında bir sorun değildir. Bu bir özellik.

Örneğin, PATH değerini önceden tanımlanmış bir değere ayarladıysanız PATH="/path/to/myevildirectory:$PATH"ve sudoayarlamadıysanız, çalıştırdığı TÜM komutlara (yani çoğu komut dosyası) tam yol adını belirtmeyen herhangi bir komut dosyası /path/to/myevildirectorybaşka bir dizinin önüne bakar . Gibi komutları koyun lsveya greporaya diğer ortak araçları ya ve kolayca sistemde gibi olan şeyleri yapabilirsiniz.

En kolay / en iyi yol, işlevi bir komut dosyası olarak yeniden yazmak ve yolun herhangi bir yerine kaydetmek (veya sudokomut satırında komut dosyasının tam yolunu belirtmektir - sudobuna izin vermek için yapılandırılmadığı sürece yine de yapmanız gerekir) HERHANGİ komutu root olarak çalıştırmak için) vechmod +x /path/to/scriptname.sh

Bir komut bir dosyaya işlev tanımı içindeki komutları tasarrufu gibi basit olduğu bir kabuk işlevi yeniden yazma (olmadan function ..., {ve }çizgiler).


Bu soruya hiçbir şekilde cevap vermiyor. Özellikle senaryoya koymaktan kaçınmak istiyor.
Will

1
Ayrıca sudo -Eçevreyi temizlemeyi de önler.
Will

Neden olduğunu bir dereceye kadar anlıyorum. Bu davranışı geçici olarak geçersiz kılmanın bazı yolları olduğunu umuyordum. Başka bir yerde bir -E seçeneğinden bahsedildi, ancak bu durumda işe yaramadı. Ne yazık ki, bunu bağımsız bir senaryo haline getirmenin açıklamasını takdir ederken, bu özellikle soruyu cevaplamıyor, çünkü bundan kaçınmanın bir yolunu istedim. Son kullanıcının komut dosyasını nereye yerleştirdiği üzerinde hiçbir kontrole sahip değilim ve hem sabit kodlu dizinlerden hem de ana komut dosyasının nereden çalıştırıldığını doğru bir şekilde belirlemeye çalışan şarkı ve danstan kaçınmak istiyorum.
BryKKan

OP'nin istediği şey bu olsun ya da olmasın önemli değil. İstediği şey işe yaramazsa veya sadece son derece güvensiz bir şey yaparak işe yaratılabilirse, o zaman söylenmeleri ve bir alternatif sunmaları gerekir - alternatif açıkça belirtmedikleri bir şey olsa bile (çünkü bazen güvenli bir şekilde yapmanın tek ya da en iyi yolu budur). Birisine ayaklarına silah doğrultmanın ve tetiği çekmenin olası sonuçları hakkında uyarı vermeden birisine kendilerini nasıl vurduğunu söylemek sorumsuz olacaktır.
cas

1
@cas Bu doğru. Güvenli bir şekilde yapılamaz bazı durumlarda kabul edilebilir bir cevaptır. Yine de son düzenlememi görün. Güvenlik sonuçları hakkındaki görüşünüzün aynı olup olmadığını merak ediyorum.
BryKKan

3

Bunu Sudoyapmak için kendi bash fonksiyonumu yazdım , fonksiyonları ve takma adları çağırmak için çalışıyor:

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

İşlevleri ve takma adları birleştirebilirsiniz

Misal:

function hello_fn() {
    echo "Hello!" 
}

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

sonra sudo helloçalışır


1

İşte Will'in cevabında bir varyasyon . Ek bir catsüreç içerir , ancak heredoc'un rahatlığını sunar. Özetle şöyle gider:

f () 
{
    echo ok;
}

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

Düşünce için daha fazla yemek istiyorsanız, şunu deneyin:

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

Çıktı:

here's the menu
a difficult thing to pass

Burada menu, "ana komut dosyasında başka bir yerde tanımlanmış olan" soruyla ilgili işleve sahibiz . "Başka bir yerde" tanımı, talep gerektiren işlev sudoyürütülürken bu aşamada tanımının zaten okunmuş olması anlamına gelirse, durum benzerdir. Ancak henüz okunmamış olabilir. Henüz tanımını tetikleyecek başka bir işlev olabilir. Bu durumda declare -f menu, daha karmaşık bir şeyle değiştirilmeli veya tüm komut dosyası, menuişlevin zaten bildirildiği şekilde düzeltilmelidir .


Çok ilginç. Bir noktada denemek zorunda kalacağım. Ve evet, menufonksiyon fbir menüden çağrıldığı gibi bu noktadan önce bildirilmiş olurdu .
BryKKan

0

Komut dosyanızın (a) bağımsız veya (b) bileşenlerini bulunduğu yere göre (ana dizininizin nerede olduğunu hatırlamak yerine) kaynaklayabileceğini varsayarsak, şöyle bir şey yapabilirsiniz:

  • $0komut dosyasında komut dosyasının yol adını kullanın ve sudoparola güncelleştirmesini çağırmak için komut dosyasının denetleyeceği bir seçeneği iletin. Komut dosyasını yolda çalıştırmak yerine (yalnızca çalıştırmak yerine ./myscript) güvendiğiniz sürece, mutlak bir yol adı almanız gerekir $0.
  • çünkü sudokomut dosyasını çalıştırır, sahip olduğu erişim Senaryoda ihtiyacı işlevlere.
  • betiğin en üstünde (daha ziyade, işlev bildirimlerini geçtikten sonra), komut dosyasını kontrol eder uidve kök kullanıcı olarak çalıştırıldığını fark eder ve parolayı güncellemesini, gitmesini ve yapmasını söyleme seçeneğinin olduğunu görür. söyledi.

Komut dosyaları çeşitli nedenlerle kendi başlarına yinelenebilir: ayrıcalıkların değiştirilmesi bunlardan biridir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.