Fabric: Masalah dengan variabel lingkungan & PATH di server jauh (bukan sumber .bashrc)

Dibuat pada 9 Okt 2016  ·  22Komentar  ·  Sumber: fabric/fabric

Hai,

Saya mencoba memuat file .bashrc (yaitu source /home/ubuntu/.bashrc) saat menjalankan perintah fab's run untuk menambahkan beberapa variabel lingkungan dan memperluas variabel path:

run('sumber /home/ubuntu/.bashrc && echo $PATH')

Ini hanya menunjukkan kepada saya:

[[email protected]] keluar: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin: /bin:/usr/games:/usr/local/games

alih-alih daftar jalur yang lebih panjang yang saya lihat ketika saya masuk ke server jauh secara manual.

Bagaimana cara agar fab mengimpor file .bashrc dengan benar di direktori home jarak jauh saya?

Terima kasih!

Komentar yang paling membantu

Ini adalah hal yang bash, bukan hal yang luar biasa.

File apa yang bash putuskan untuk sumber saat startup dapat menjadi rumit http://blog.flowblok.id.au/2013-02/shell-startup-scripts.html dan debian/ubuntu (dan sebagian besar distro) memiliki beberapa penyesuaian di /etc/profile dan secara default ~/.bashrc .

Penggunaan shell fab default adalah /bin/bash -l -c , dan -l menjadikannya shell "login". Tanpa kustomisasi debian/ubuntu, shell "login" bash dimungkinkan untuk sumber ~/.bash_profile tetapi tidak ~/.bashrc .

Tetapi di ubuntu 16.04, tampaknya sumber .bashrc secara default bahkan untuk login-Shell. Tetapi baris yang ditambahkan di bagian bawah default .bashrc tidak diproses, karena akan keluar di dekat bagian atas jika terdeteksi berjalan secara non-interaktif.

Di sini saya telah menambahkan dua baris ke pengguna default ubuntu-16.04 .bashrc

# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples

export VAR1=val1

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

export VAR2=val2

# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth
...

Inilah fabfile yang saya uji:

from fabric.api import run, env, task

<strong i="23">@task</strong>
def get_myvars():
    run("echo VAR1=$VAR1 VAR2=$VAR2")

Hasil:

$ fab -H testpy05.ec2.st-av.net get_myvars
[testpy05.ec2.st-av.net] Executing task 'get_myvars'
[testpy05.ec2.st-av.net] run: echo VAR1=$VAR1 VAR2=$VAR2
[testpy05.ec2.st-av.net] out: VAR1=val1 VAR2=
[testpy05.ec2.st-av.net] out: 
...

Semua 22 komentar

Ini adalah hal yang bash, bukan hal yang luar biasa.

File apa yang bash putuskan untuk sumber saat startup dapat menjadi rumit http://blog.flowblok.id.au/2013-02/shell-startup-scripts.html dan debian/ubuntu (dan sebagian besar distro) memiliki beberapa penyesuaian di /etc/profile dan secara default ~/.bashrc .

Penggunaan shell fab default adalah /bin/bash -l -c , dan -l menjadikannya shell "login". Tanpa kustomisasi debian/ubuntu, shell "login" bash dimungkinkan untuk sumber ~/.bash_profile tetapi tidak ~/.bashrc .

Tetapi di ubuntu 16.04, tampaknya sumber .bashrc secara default bahkan untuk login-Shell. Tetapi baris yang ditambahkan di bagian bawah default .bashrc tidak diproses, karena akan keluar di dekat bagian atas jika terdeteksi berjalan secara non-interaktif.

Di sini saya telah menambahkan dua baris ke pengguna default ubuntu-16.04 .bashrc

# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples

export VAR1=val1

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

export VAR2=val2

# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth
...

Inilah fabfile yang saya uji:

from fabric.api import run, env, task

<strong i="23">@task</strong>
def get_myvars():
    run("echo VAR1=$VAR1 VAR2=$VAR2")

Hasil:

$ fab -H testpy05.ec2.st-av.net get_myvars
[testpy05.ec2.st-av.net] Executing task 'get_myvars'
[testpy05.ec2.st-av.net] run: echo VAR1=$VAR1 VAR2=$VAR2
[testpy05.ec2.st-av.net] out: VAR1=val1 VAR2=
[testpy05.ec2.st-av.net] out: 
...

Sekilas terdengar sah, terima kasih @ploxiln!

Terima kasih atas jawaban terperinci!

Ini tampaknya berbeda dari perilaku ssh standar dan sulit untuk di-debug. Ketika saya secara manual ssh semuanya bekerja dengan baik, sedangkan fabric memiliki nilai PATH yang berbeda karena tidak menjalankan .bash_profile di OSX.

@bitprophet ini mungkin bug OSX??

@code-tree Apakah Anda menggunakan Fabric 1 atau 2? 1.x menggunakan pembungkus shell eksplisit yang akan berbeda dari klien OpenSSH standar; versi 2 tidak melakukan pembungkus apa pun dan seharusnya berperilaku lebih seperti klien OpenSSH dalam hal apa yang dijalankan sshd atas nama Anda.

Versi 1 memiliki beberapa opsi nilai konfigurasi env untuk mengubah pembungkus shell itu, jadi Anda mungkin ingin mencoba menambahkan flag ke dalamnya, seperti -i which IIRC adalah bash untuk "jalankan mode interaktif dan sumber beberapa file tambahan".

Itu aneh karena saya menggunakan versi 2.2.1 . Saat ini saya harus melakukan hal berikut agar aplikasi saya berfungsi:
c.run('bash -l -c "python3 ./configure.py"')
Seperti ketika saya menginstal Python 3 ia menambahkan dirinya ke PATH melalui .bash_profile , yang tidak dijalankan oleh Fabric. Saya juga belum membuat modifikasi apa pun pada konfigurasi sshd bawaan standar.

Itu aneh kalau begitu, saya harus melihat apakah saya bisa meniru. Baru saja melakukan penelusuran kewarasan cepat untuk membuktikan apa yang saya maksud re: Fabric 2 tidak melakukan sesuatu yang 'khusus' re: sshd driven execution:

@code-tree juga, jika Anda bisa, silakan kirim beberapa detail lebih lanjut tentang apa yang Anda jalankan & detail lingkungan lainnya (OS/versi klien dan server, dll.).

Tentu, saya menjalankan versi OSX lama sebagai server. Jika Anda dapat mengonfirmasi semua berfungsi dengan baik pada MacOS yang lebih baru, maka oh well. Tetapi jika tidak maka mungkin ada masalah dengan MacOS sebagai server pada umumnya?

  • Klien: Ubuntu 16.04, Python 3.6.6, OpenSSH 7.2p2, Bash 4.3.48, Fabric 2.2.1
  • Server: OSX 10.11, Python 3.6.6, OpenSSH 6.9p1, Bash 3.2.57

Catatan tambahan, saya ingin tahu apakah ini benar-benar kasus salah diagnosis:

fabric memiliki nilai PATH yang berbeda karena tidak menjalankan .bash_profile di OSX

Bisakah ini menjadi contoh #1744, di mana Fabric 2 berbeda dari ssh dalam hal shunting lokal env vars ke pipa? (Tergantung pada env vars yang tepat yang dimaksud dan apakah mereka mungkin serupa secara lokal dan jarak jauh; @code-tree dapat dengan mudah menyangkal teori ini dengan memeriksa ulang nilai-nilai yang tepat dalam permainan: bash_profile lokal dan jarak jauh mereka ... 😁)


Tapi saya akan memecahkan masalah ini secara langsung, supaya saya tahu saya tidak gila tentang: pemahaman saya tentang apa yang dilakukan sshd dalam hal shell dan file sumber.

Saya juga bertanya-tanya apakah ini terkait dengan skenario/intelijen dari #1816, yang juga tentang shell dan file startup. Ini _mungkin_ tidak relevan secara langsung karena pernyataan di sini tentang ssh berperilaku satu arah dan Fabric berperilaku dengan cara lain (dibandingkan dengan "bagaimana sshd memanggil shell secara normal"), tetapi tetap layak untuk ditautkan.

Menemukan bahwa mencoba tersandung pada sekelompok penyelaman kode sshd yang dalam yang saya ingat lakukan dalam beberapa bulan terakhir, terkait dengan sesuatu di sepanjang baris ini. Saya tidak dapat menemukannya sekarang meskipun itu menjengkelkan. EDIT: ah saya pikir itu adalah masalah Paramiko yang tidak terkait (tidak menautkan karena tidak ada gunanya membingungkan) tentang menjalankan perintah sebelum otentikasi. OKE.

Baiklah, jadi apa yang sebenarnya terjadi ketika kita meminta perintah exec?

  • SSH-the-protocol ingin Anda membuat permintaan saluran shell (yang memuat program shell login yang ditentukan pengguna dan tidak mengambil command ) atau saluran exec permintaan (yang hanya mengatakan "mengeksekusi string perintah yang diberikan, yang mungkin berisi jalur" dan tidak menentukan lebih lanjut)
  • Paramiko melakukan yang terakhir, seperti yang kami temukan di atas.
  • Jadi apa yang sebenarnya dilakukan OpenSSH itu sendiri? Saya akan melihat checkout lokal saya dari openssh-portable (di 775f8a23f2353f5869003c57a213d14b28e0736e )

Yah, pertama saya akan bermain-main sedikit. Berjalan melawan wadah Debian 8.10 sewenang-wenang (yang menjalankan OpenSSH 6.7!) Saya melihat ini di log tingkat DEBUG3 setelah melakukan ssh localhost whoami :

debug1: server_input_channel_req: channel 0 request exec reply 1
debug1: session_by_channel: session 0 channel 0
debug1: session_input_channel_req: session 0 req exec
Starting session: command for root from 172.17.0.1 port 42598

Melakukan fab -H localhost -- whoami :

debug1: server_input_channel_req: channel 0 request exec reply 1
debug1: session_by_channel: session 0 channel 0
debug1: session_input_channel_req: session 0 req exec
Starting session: command for root from 172.17.0.1 port 42610

Oke, mereka berdua melakukan exec ...itu yang diharapkan. Adakah yang berbeda di pohon proses? seharusnya tidak, dan...tidak ada. keduanya terlihat seperti ini:

sshd,1 -D -e
  └─sshd,1535
      └─pstree,1537 -alpU

Jadi, pasti tidak ada cangkang yang dimainkan? Apa yang dia lakukan? Bisakah saya menggunakan hal-hal kecil seperti && ?

Saya bisa! misalnya whoami && id berfungsi dengan baik. Jadi itu agak aneh mengingat pemahaman saya tentang kontrol proses Unix; sepertinya tidak mungkin sshd benar-benar melakukan exec(string) , tetapi jika menggunakan Shell untuk menafsirkan, tidakkah kita akan melihatnya di pstree ?

Saya pikir sudah waktunya untuk benar-benar menggali ke openssh-portable dan melihat apa itu.


Juga, ya, saya _melakukan_ bagian dari penelusuran ini (tetapi untuk alasan/fokus yang berbeda) di masa lalu, seperti pada, Des 2016: https://github.com/paramiko/paramiko/pull/398#issuecomment -264281759. Sudah benar-benar lupa...


Alasan mengapa saya tidak melihat shell di pstree adalah karena penggunaan execve , yang melakukan penggantian proses induk alih-alih melahirkan anak. Senang mendengarnya. (EDIT: salah, itu hanya menggantikan proc anak sshd dengan Shell; Shell itu sendiri mungkin juga melakukan panggilan exec gaya replace-me; lihat komentar di bawah.)


Pokoknya! Ini masih berarti klien Fabric dan OpenSSH melakukan hal yang sama persis dalam hal eksekusi shell; tidak ada yang mereka lakukan yang akan memodifikasi bagian-bagian dari basis kode OpenSSH ini. Itu harus selalu seperti bash -c "python3 ./configure.py" , dengan nilai aktual bash tergantung pada pengguna level macOS @code-tree dan shell yang dikonfigurasi.

Membuat saya bertanya-tanya apakah firasat saya tentang transmisi env var menjadi pembeda akurat, karena itulah satu-satunya hal lain yang dapat saya pikirkan tentang: bagaimana kedua lingkungan eksekusi dapat berbeda. FWIW dalam pengujian saya yang berfokus pada CLI, env melaporkan hal yang sama pada kedua sistem, dan terlepas dari masalah terkait env lainnya, perilaku _default_ (karena kebijakan keamanan SSH) harus selalu bahwa tidak ada "kebocoran" env lokal sisi lain.

Ya, saya cukup yakin sshd selalu menjalankan string perintah di shell pengguna (dikonfigurasi di /etc/passwd). Namun itu menambahkan string perintah dengan "exec" jika dapat mengurai string perintah dan menentukan bahwa itu adalah satu perintah.

$ ssh testdeploy02.ec2.st-av.net pstree -a
...
  |-sshd -D
  |   `-sshd 
  |       `-sshd  
  |           `-pstree -a
$ ssh testdeploy02.ec2.st-av.net 'pstree -a && sleep 1'
...
  |-sshd -D
  |   `-sshd 
  |       `-sshd  
  |           `-bash -c pstree -a && sleep 1
  |               `-pstree -a
$ ssh testdeploy02.ec2.st-av.net 'VAR=-a sh -c "exec pstree \$VAR"'
...
  |-sshd -D
  |   `-sshd 
  |       `-sshd  
  |           `-pstree -a

Adapun variabel lingkungan, ini sebagian besar dikendalikan oleh klien ssh dan konfigurasi server sshd:

$ grep Env /etc/ssh/*_config
/etc/ssh/ssh_config:    SendEnv LANG LC_*
/etc/ssh/sshd_config:AcceptEnv LANG LC_*

Jadi klien ssh secara eksplisit mengirim beberapa variabel (di luar string perintah itu sendiri) dan mereka difilter oleh sshd. Tetapi tampaknya ada beberapa pengecualian:

$ ssh testdeploy02.ec2.st-av.net env | grep TERM
$ ssh -t testdeploy02.ec2.st-av.net env | grep TERM
TERM=xterm-256color

Sebenarnya saya pikir perilaku "exec otomatis untuk mengganti Shell dengan perintah tunggal" adalah fitur bash:

$ dash -c "pstree -a"
...
  ├─sshd -D
  │   └─sshd 
  │       └─sshd  
  │           └─bash
  │               └─dash -c pstree -a
  │                   └─pstree -a
$ bash -c "pstree -a"
  ├─sshd -D
  │   └─sshd 
  │       └─sshd  
  │           └─bash
  │               └─pstree -a

EDIT: Untuk kelengkapan:

$ bash -c "pstree -a && sleep 1"
  ├─sshd -D
  │   └─sshd 
  │       └─sshd  
  │           └─bash
  │               └─bash -c pstree -a && sleep 1
  │                   └─pstree -a

Ah ya, saya salah, execve hanya berarti proc anak sshd yang diganti, jadi apa yang terjadi setelah itu terserah Shell itu sendiri dan apa yang dilakukannya untuk -c xxx . Tes saya ada di zsh, fwiw.

Juga @ploxiln ya masalahnya dengan env vars merujuk tiket lain yang ditautkan di atas, meskipun masih harus dilihat apakah @code-tree benar-benar mengalami gejala yang berbeda dengan itu.

Maaf, saya pikir ini alarm palsu. Saya tidak menyadari bahwa ada perbedaan antara menjalankan perintah melalui ssh inline dan mengeksekusi setelah masuk melalui ssh. Ketika saya melakukan ssh host 'echo $PATH' itu juga belum memperbarui PATH di mana echo $PATH setelah masuk berfungsi dengan baik.

Saya masih tidak mengerti mengapa ssh melakukan ini (sepertinya saya memang harus melakukan ssh host 'bash -l -c "python3 ./configure.py"' ). Bagaimanapun, aman untuk mengatakan ini bukan masalah Fabric. Maaf bila membingungkan.

@code-tree ini terkait dengan apa yang disebutkan sebelumnya - shell memiliki beberapa mode berbeda yang mereka operasikan, sering disebut 'login' dan 'interaktif' (dan biasanya itu berada di atas mode dasar yang dianggap tidak keduanya) dan mode mana shell sedang dijalankan, mengubah kumpulan file rc mana yang dimuat.

Poin penting di sini adalah bahwa bash -c xxx tidak dianggap "interaktif" dan dengan demikian melewatkan pemuatan file tertentu - seperti .bash_profile - tetapi hanya menjalankan bash tanpa -c biasanya interaktif, dan memuat file profil.

Seperti yang Anda lihat di atas - sshd selalu menjalankan bash -c <command send down the pipe> ketika Anda memintanya untuk menjalankan satu perintah (seperti yang dilakukan Fabric, atau seperti yang dilakukan ssh host command ), dan dengan demikian, selalu dalam mode non-interaktif. (Kecuali jika Anda melakukan apa yang Fabric 1 lakukan dan menjalankan shell Anda sendiri, bersarang, dengan -l ...tetapi kemudian Anda menjalankan bash -c "bash -l -c \"oh god escaping is hard help\"" dan saya akan pergi memiliki beberapa sekarang.

Lagi pula, tidak semuanya hilang, saya perlu menyegarkan ingatan saya tentang bagian ini setiap beberapa tahun rupanya jadi uh ... sekarang saya segar

Ini adalah hal yang bash, bukan hal yang luar biasa.

File apa yang bash putuskan untuk sumber saat startup dapat menjadi rumit http://blog.flowblok.id.au/2013-02/shell-startup-scripts.html dan debian/ubuntu (dan sebagian besar distro) memiliki beberapa penyesuaian di /etc/profile dan secara default ~/.bashrc .

Penggunaan shell fab default adalah /bin/bash -l -c , dan -l menjadikannya shell "login". Tanpa kustomisasi debian/ubuntu, shell "login" bash dimungkinkan untuk sumber ~/.bash_profile tetapi tidak ~/.bashrc .

Tetapi di ubuntu 16.04, tampaknya sumber .bashrc secara default bahkan untuk login-Shell. Tetapi baris yang ditambahkan di bagian bawah default .bashrc tidak diproses, karena akan keluar di dekat bagian atas jika terdeteksi berjalan secara non-interaktif.

Di sini saya telah menambahkan dua baris ke pengguna default ubuntu-16.04 .bashrc

# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples

export VAR1=val1

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

export VAR2=val2

# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth
...

Inilah fabfile yang saya uji:

from fabric.api import run, env, task

<strong i="24">@task</strong>
def get_myvars():
    run("echo VAR1=$VAR1 VAR2=$VAR2")

Hasil:

$ fab -H testpy05.ec2.st-av.net get_myvars
[testpy05.ec2.st-av.net] Executing task 'get_myvars'
[testpy05.ec2.st-av.net] run: echo VAR1=$VAR1 VAR2=$VAR2
[testpy05.ec2.st-av.net] out: VAR1=val1 VAR2=
[testpy05.ec2.st-av.net] out: 
...

Kau telah menyelamatkan hidupku. Ini adalah jawaban sempurna yang saya temukan :) Terima kasih banyak!

Apakah halaman ini membantu?
0 / 5 - 0 peringkat