Bagaimana cara menggunakan su untuk mengeksekusi sisa skrip bash sebagai pengguna itu?

126

Saya telah menulis skrip yang mengambil, sebagai argumen, string yang merupakan rangkaian nama pengguna dan proyek. Skrip seharusnya mengalihkan (su) ke nama pengguna, cd ke direktori tertentu berdasarkan string proyek.

Saya pada dasarnya ingin melakukan:

su $USERNAME;  
cd /home/$USERNAME/$PROJECT;  
svn update;  

Masalahnya adalah begitu saya melakukan su ... itu hanya menunggu di sana. Yang masuk akal karena aliran eksekusi telah beralih ke pengguna. Setelah saya keluar, maka hal-hal lainnya akan dijalankan tetapi tidak berfungsi seperti yang diinginkan.

Saya menambahkan su ke perintah svn tetapi perintah itu gagal (yaitu tidak memperbarui svn di direktori yang diinginkan).

Bagaimana cara menulis skrip yang memungkinkan pengguna untuk mengganti pengguna dan menjalankan svn (antara lain)?

Avery
sumber

Jawaban:

86

Triknya adalah dengan menggunakan perintah "sudo" bukan "su"

Anda mungkin perlu menambahkan ini

username1 ALL=(username2) NOPASSWD: /path/to/svn

ke file / etc / sudoers Anda

dan ubah skrip Anda menjadi:

sudo -u username2 -H sh -c "cd /home/$USERNAME/$PROJECT; svn update" 

Di mana namapengguna2 adalah pengguna yang Anda inginkan untuk menjalankan perintah SVN dan namapengguna1 adalah pengguna yang menjalankan skrip.

Jika Anda membutuhkan banyak pengguna untuk menjalankan skrip ini, gunakan a %groupnamesebagai ganti username1

Kimvais
sumber
Saya memiliki masalah serupa, tetapi saya ingin menjalankan chshuntuk pengguna lain. Masalah saya tercantum di sini di stackoverflow.com/q/15307289/80353 Bagaimana cara menyesuaikan jawaban Anda dalam situasi saya?
Kim Stacks
Saya melakukan ini - tetapi masih meminta saya untuk memasukkan kata sandi.
Hippyjim
@ Hippyjim Anda yakin Anda mendapatkan nama pengguna yang benar?
Kimvais
1
Ya - ternyata saya juga perlu mengizinkan penggunaan / bin / bash juga.
Hippyjim
3
Apakah Anda menggunakan sudoatau sumemiliki kepentingan sekunder, meskipun sudojauh lebih aman dan nyaman.
tripleee
105

Jauh lebih sederhana: gunakan sudountuk menjalankan shell dan gunakan heredoc untuk memberinya perintah.

#!/usr/bin/env bash
whoami
sudo -i -u someuser bash << EOF
echo "In"
whoami
EOF
echo "Out"
whoami

(jawaban awalnya di SuperUser )

Dan Dascalescu
sumber
5
Jawaban ini bekerja paling baik. Saya merekomendasikan untuk menggunakan opsi -iuntuk mendapatkan someuserlingkungan yang diharapkan.
not2savvy
2
sudomungkin nyaman tetapi tidak ada gunanya jika tidak di luar kotak (seperti di AIX). su -c 'commands'adalah jawaban yang benar.
Rumit
1
Solusi epik!
3bdalla
Bagaimana membuat variabel tersedia dalam lingkup herdoc?
dotslashlu
pada AIX itu melempar kesalahan ini ksh: sh: 0403-006 Izin eksekusi ditolak.
AhmedRana
54

Gunakan skrip seperti berikut untuk menjalankan sisa atau sebagian skrip di bawah pengguna lain:

#!/bin/sh

id

exec sudo -u transmission /bin/sh - << eof

id

eof
Walder
sumber
11
Anda mungkin ingin menggunakan "sudo -i -u ..." untuk memastikan bahwa $ HOME disetel dengan benar.
Bryan Larsen
Maaf untuk pertanyaan noob, tapi apa id disini?
Nitin Jadhav
1
@NitinJadhav, dia menggunakannya di sini hanya untuk menunjukkan ID pengguna saat ini, ID root adalah 0, jadi id pertama akan menunjukkan beberapa nomor, tetapi yang kedua pasti akan menunjukkan 0 (karena yang kedua dieksekusi di dalam blok yang dijalankan oleh root). Anda dapat pengguna whoamialih-alih idyang akan mengembalikan nama alih-alih id
Mohammed Noureldin
@MohammedNoureldin Terima kasih!
Nitin Jadhav
sudomungkin nyaman tetapi tidak ada gunanya jika tidak di luar kotak (seperti di AIX). su -c 'commands'adalah jawaban yang benar.
Rumit
52

Anda perlu menjalankan semua perintah pengguna yang berbeda sebagai skrip mereka sendiri. Jika hanya satu, atau beberapa perintah, maka inline seharusnya berfungsi. Jika banyak perintah maka mungkin yang terbaik adalah memindahkannya ke file mereka sendiri.

su -c "cd /home/$USERNAME/$PROJECT ; svn update" -m "$USERNAME" 
Douglas Leeder
sumber
4
Ini satu-satunya jawaban yang benar. Sudo tidak diperlukan untuk ini.
helvete
1
Anda mungkin juga perlu menyediakan shell su -s /bin/bash.
mixel
solusi terbaik untuk pengguna root
rfinz
Jawaban terbaik bagi mereka yang tidak menginstal sudo secara default (Saya melihat Anda AIX)
Tricky
46

Berikut adalah pendekatan lain, yang lebih nyaman dalam kasus saya (saya hanya ingin melepaskan hak akses root dan melakukan sisa skrip saya dari pengguna terbatas): Anda dapat membuat skrip restart sendiri dari pengguna yang benar. Misalkan ini dijalankan sebagai root pada awalnya. Maka akan terlihat seperti ini:

#!/bin/bash
if [ $UID -eq 0 ]; then
  user=$1
  dir=$2
  shift 2     # if you need some other parameters
  cd "$dir"
  exec su "$user" "$0" -- "$@"
  # nothing will be executed beyond that line,
  # because exec replaces running process with the new one
fi

echo "This will be run from user $UID"
...
MarSoft
sumber
6
Saya bertanya-tanya mengapa ini tidak dinilai lebih tinggi. Ini adalah memecahkan pertanyaan asli dengan sebaik-baiknya sambil menjaga semuanya tetap bash.
SirVer
Atau runuser -u $user -- "$@", seperti yang dinyatakan dalam su (1)
cghislai
1
exec su "$user" "$0" -- "$@"
macieksk
1
Terima kasih @macieksk, tangkapan bagus. Akan diperbarui. Ini --sangat berguna.
MarSoft
1
Pendekatan yang disajikan adalah yang terbaik dari semuanya dan lengkap. Semua ditulis di dalam skrip tunggal, dan semua menggunakan bash. Secara efektif, skrip ini dijalankan dua kali. Pada pengujian skrip pertama itu adalah root, kemudian siapkan lingkungan, dan ubah pengguna dengan su. Tetapi lakukan dengan perintah exec, maka hanya ada satu contoh skrip. Dengan loop kedua, frase if / fi dihilangkan, kemudian script langsung melakukan aksi yang sudah disiapkan. Dalam contoh ini adalah echo. Semua pendekatan lain menyelesaikan masalah hanya sebagian. Tentu saja skrip ini dapat dijalankan di bawah sh bukan bash, tetapi kita harus menguji variabel khusus $ HOME, bukan $ UID
Znik
7

Gunakan sudosebagai gantinya

EDIT : Sebagai Douglas menunjukkan, Anda tidak dapat menggunakan cddi sudokarena bukan merupakan eksternal perintah. Anda harus menjalankan perintah di subkulit untuk membuatnya cdbekerja.

sudo -u $USERNAME -H sh -c "cd ~/$PROJECT; svn update"

sudo -u $USERNAME -H cd ~/$PROJECT
sudo -u $USERNAME svn update

Anda mungkin diminta untuk memasukkan kata sandi pengguna itu, tetapi hanya sekali.

iamamac
sumber
Itu tidak akan berhasil - cd akan hilang setelah sudo pertama selesai dijalankan.
Douglas Leeder
Sebenarnya, Anda bahkan tidak dapat memanggil cd secara langsung karena ini bukan perintah eksternal .
iamamac
sudomungkin nyaman tetapi tidak ada gunanya jika tidak di luar kotak (seperti di AIX). su -c 'commands'adalah jawaban yang benar.
Rumit
6

Tidak mungkin mengubah pengguna dalam skrip shell. Solusi menggunakan sudo yang dijelaskan dalam jawaban lain mungkin adalah pilihan terbaik Anda.

Jika Anda cukup marah untuk menjalankan skrip perl sebagai root, Anda dapat melakukannya dengan $< $( $> $) variabel yang menyimpan uid / gid nyata / efektif, misalnya:

#!/usr/bin/perl -w
$user = shift;
if (!$<) {
    $> = getpwnam $user;
    $) = getgrnam $user;
} else {
    die 'must be root to change uid';
}
system('whoami');
P-Nuts
sumber
-1 sebagai Ini IS mungkin dengan sudo untuk memperoleh hak-hak pengguna lain sementara.
Kimvais
4
Ini tidak mungkin dalam arti bahwa pengguna yang menjalankan skrip shell itu sendiri tidak dapat diubah (seperti yang ditanyakan oleh pertanyaan asli). Memanggil proses lain dengan sudo tidak mengubah siapa skrip itu sendiri dijalankan.
P-Nuts
2

Ini berhasil untuk saya

Saya memisahkan "penyediaan" saya dari "permulaan" saya.

 # Configure everything else ready to run 
  config.vm.provision :shell, path: "provision.sh"
  config.vm.provision :shell, path: "start_env.sh", run: "always"

lalu di start_env.sh saya

#!/usr/bin/env bash

echo "Starting Server Env"
#java -jar /usr/lib/node_modules/selenium-server-standalone-jar/jar/selenium-server-standalone-2.40.0.jar  &
#(cd /vagrant_projects/myproj && sudo -u vagrant -H sh -c "nohup npm install 0<&- &>/dev/null &;bower install 0<&- &>/dev/null &")
cd /vagrant_projects/myproj
nohup grunt connect:server:keepalive 0<&- &>/dev/null &
nohup apimocker -c /vagrant_projects/myproj/mock_api_data/config.json 0<&- &>/dev/null &
httpete
sumber
-2

Terinspirasi oleh ide dari @ MarSoft tetapi saya mengubah baris seperti berikut:

USERNAME='desireduser'
COMMAND=$0
COMMANDARGS="$(printf " %q" "${@}")"
if [ $(whoami) != "$USERNAME" ]; then
  exec sudo -E su $USERNAME -c "/usr/bin/bash -l $COMMAND $COMMANDARGS"
  exit
fi

Saya telah terbiasa sudomengizinkan eksekusi tanpa sandi dari skrip. Jika Anda ingin memasukkan kata sandi untuk pengguna, hapus sudo. Jika Anda tidak membutuhkan variabel lingkungan, hapus -Edari sudo.

The /usr/bin/bash -lMemastikan, bahwa profile.dskrip dieksekusi untuk lingkungan diinisialisasi.

Trendfischer
sumber
sudomungkin nyaman tetapi tidak ada gunanya jika tidak di luar kotak (seperti di AIX). su -c 'commands'adalah jawaban yang benar.
Rumit
@Tricky Mungkin membaca jawaban lengkapnya, itu sudah menyarankan penghapusan sudo. Faktanya, sudo tidak sesederhana itu, dalam banyak kasus Anda memerlukan bahkan sudo -Edan entri konfigurasi di sudoers.d untuk memungkinkan eksekusi tanpa tty !requiretty. Tetapi ada banyak kasus, di mana sudo diperlukan untuk skrip yang disebut otomatis, di mana dialog kata sandi dapat mengganggu. Jadi saya tidak akan menghapusnya dari solusi standar.
Trendfischer
Kode yang dimaksud benar-benar bermasalah - ini akan merusak nama skrip atau argumen dengan spasi; perlu diingat bahwa meletakkan "$@"di dalam string berarti argumen setelah yang pertama ditambahkan ke string terpisah, tidak disertakan dalam -cargumen. Jika Anda ingin membuatnya aman, Anda mungkin printf -v arg_q '%q ' "$0" "$@"dan kemudian menggunakansu "$USERNAME" -c "/usr/bin/bash -l $arg_q"
Charles Duffy
@CharlesDuffy Anda benar! Saya menyederhanakan skrip produksi saya terlalu banyak untuk jawaban ini :-( Hanya menambahkan COMMANDARGS=$@penyelesaian masalah dengan -c. Argumen dengan spasi bukanlah masalah sebelumnya, tetapi saya menerapkan masukan Anda yang baik. Saya hanya perlu melakukan beberapa eksperimen untuk membuatnya berfungsi. Saya Saya telah mengedit pertanyaan dan semoga saya tidak menempelkan bug lain. Terima kasih atas komentar Anda, memalukan tetapi perlu.
Trendfischer