Pertanyaan (TL; DR)
Ketika menetapkan port secara dinamis untuk penerusan jarak jauh ( -R
opsi alias ), bagaimana skrip pada mesin jarak jauh (misalnya bersumber dari .bashrc
) menentukan port mana yang dipilih oleh OpenSSH?
Latar Belakang
Saya menggunakan OpenSSH (di kedua ujungnya) untuk terhubung ke server pusat kami, yang saya bagikan dengan banyak pengguna lain. Untuk sesi jarak jauh saya (untuk saat ini) saya ingin meneruskan X, gelas dan pulseaudio.
Yang paling sepele adalah meneruskan X, menggunakan -X
opsi. Alamat X yang dialokasikan disimpan dalam variabel lingkungan DISPLAY
dan dari situ saya dapat menentukan port TCP yang sesuai, dalam banyak kasus, bagaimanapun juga. Tapi saya hampir tidak pernah perlu, karena kehormatan Xlib DISPLAY
.
Saya perlu mekanisme serupa untuk cangkir dan pulseaudio. Dasar-dasar untuk kedua layanan ada , masing-masing dalam bentuk variabel lingkungan CUPS_SERVER
dan PULSE_SERVER
. Berikut adalah contoh penggunaan:
ssh -X -R12345:localhost:631 -R54321:localhost:4713 datserver
export CUPS_SERVER=localhost:12345
lowriter #and I can print using my local printer
lpr -P default -o Duplex=DuplexNoTumble minutes.pdf #printing through the tunnel
lpr -H localhost:631 -P default -o Duplex=DuplexNoTumble minutes.pdf #printing remotely
mpg123 mp3s/van_halen/jump.mp3 #annoy co-workers
PULSE_SERVER=localhost:54321 mpg123 mp3s/van_halen/jump.mp3 #listen to music through the tunnel
Masalahnya adalah pengaturan CUPS_SERVER
dan PULSE_SERVER
benar.
Kami banyak menggunakan penerusan porta dan oleh karena itu saya memerlukan alokasi port dinamis. Alokasi port statis bukan merupakan opsi.
OpenSSH memiliki mekanisme untuk alokasi port dinamis pada server jarak jauh, dengan menetapkan 0
sebagai port-bind untuk penerusan jarak jauh ( -R
opsi). Dengan menggunakan perintah berikut, OpenSSH akan secara dinamis mengalokasikan port untuk cangkir dan penerusan pulsa.
ssh -X -R0:localhost:631 -R0:localhost:4713 datserver
Ketika saya menggunakan perintah itu, ssh
akan mencetak yang berikut ke STDERR
:
Allocated port 55710 for remote forward to 127.0.0.1:4713
Allocated port 41273 for remote forward to 127.0.0.1:631
Ada informasi yang saya inginkan! Pada akhirnya saya ingin menghasilkan:
export CUPS_SERVER=localhost:41273
export PULSE_SERVER=localhost:55710
Namun pesan "Alokasi port ..." dibuat di mesin lokal saya dan dikirim ke STDERR
, yang saya tidak dapat mengaksesnya di mesin jarak jauh. Anehnya, OpenSSH tampaknya tidak memiliki sarana untuk mengambil informasi tentang penerusan porta.
Bagaimana cara mengambil informasi itu untuk dimasukkan ke dalam skrip shell untuk secara memadai mengatur CUPS_SERVER
dan PULSE_SERVER
pada host jarak jauh?
Jalan buntu
Satu-satunya hal mudah yang dapat saya temukan adalah meningkatkan verbositas sshd
sampai informasi itu dapat dibaca dari log. Ini tidak layak karena informasi itu mengungkapkan lebih banyak informasi daripada yang dapat diakses oleh pengguna non-root.
Saya sedang berpikir tentang menambal OpenSSH untuk mendukung urutan pelarian tambahan yang mencetak representasi bagus dari struct internal permitted_opens
, tetapi bahkan jika itu yang saya inginkan, saya masih tidak dapat mengakses skrip mengakses urutan pelarian klien dari sisi server.
Pasti ada cara yang lebih baik
Pendekatan berikut ini tampaknya sangat tidak stabil dan terbatas pada satu sesi SSH per pengguna. Namun, saya membutuhkan setidaknya dua sesi bersamaan dan pengguna lain bahkan lebih. Tapi saya mencoba ...
Ketika bintang-bintang disejajarkan dengan benar, setelah mengorbankan satu atau dua ayam, saya dapat menyalahgunakan fakta yang sshd
tidak dimulai sebagai pengguna saya, tetapi menjatuhkan hak istimewa setelah login berhasil, untuk melakukan ini:
dapatkan daftar nomor port untuk semua soket mendengarkan milik pengguna saya
netstat -tlpen | grep ${UID} | sed -e 's/^.*:\([0-9]\+\) .*$/\1/'
dapatkan daftar nomor port untuk semua soket mendengarkan yang menjadi milik proses pengguna saya mulai
lsof -u ${UID} 2>/dev/null | grep LISTEN | sed -e 's/.*:\([0-9]\+\) (LISTEN).*$/\1/'
Semua port yang ada di set pertama, tetapi tidak di set kedua memiliki kemungkinan tinggi untuk menjadi port forwarding saya, dan memang mengurangi set hasil
41273
,55710
dan6010
; cangkir, nadi dan X, masing-masing.6010
diidentifikasi sebagai port X menggunakanDISPLAY
.41273
adalah port gelas, karenalpstat -h localhost:41273 -a
kembali0
.55710
adalah port pulsa, karenapactl -s localhost:55710 stat
kembali0
. (Bahkan mencetak nama host klien saya!)
(Untuk melakukan set substraksi I sort -u
dan menyimpan output dari baris perintah di atas dan gunakan comm
untuk melakukan substraksi.)
Pulseaudio memungkinkan saya mengidentifikasi klien dan, untuk semua maksud dan tujuan, ini dapat berfungsi sebagai jangkar untuk memisahkan sesi SSH yang perlu dipisahkan. Namun, saya belum menemukan cara untuk mengikat 41273
, 55710
dan 6010
ke sshd
proses yang sama . netstat
tidak akan mengungkapkan informasi itu kepada pengguna non-root. Saya hanya mendapatkan -
di PID/Program name
kolom tempat saya ingin membaca 2339/54
(dalam contoh khusus ini). Sangat dekat ...
sumber
netstat
tidak akan menunjukkan PID untuk proses yang tidak Anda miliki atau ruang kernel. MisalnyaJawaban:
Ambil dua (lihat histori untuk versi yang melakukan scp dari sisi server dan sedikit lebih sederhana), ini harus dilakukan. Intinya adalah ini:
Pertama, setup di sisi jarak jauh, Anda perlu mengaktifkan pengiriman variabel env dalam konfigurasi sshd :
Temukan baris dengan
AcceptEnv
dan tambahkanMY_PORT_FILE
padanya (atau tambahkan baris di bawahHost
bagian kanan jika belum ada). Bagi saya garis menjadi ini:Juga ingat untuk memulai kembali sshd agar ini berlaku.
Selain itu, agar skrip di bawah ini berfungsi, lakukan
mkdir ~/portfiles
di sisi yang jauh!Kemudian di sisi lokal, potongan skrip yang akan
Cuplikan skrip:
Kemudian cuplikan untuk sisi jarak jauh, cocok untuk .bashrc :
Catatan : Kode di atas tentu saja tidak diuji secara menyeluruh dan dapat mengandung semua jenis bug, kesalahan tempel-tempel, dll. Siapa pun yang menggunakannya dengan lebih baik juga memahaminya, gunakan dengan risiko Anda sendiri! Saya mengujinya hanya menggunakan koneksi localhost, dan itu berhasil untuk saya, dalam pengujian saya env. YMMV.
sumber
scp
dari sisi jauh ke sisi lokal, yang saya tidak bisa. Saya memiliki pendekatan yang serupa, tetapi saya akan membungkusnyassh
setelah membangun koneksi, kemudian mengirim file itu dari lokal ke remote melaluiscp
dan kemudian menarikssh
klien ke latar depan dan menjalankan skrip di sisi remote. Saya belum menemukan cara untuk skrip latar belakang dan latar depan proses lokal dan jarak jauh dengan baik. Membungkus dan mengintegrasikanssh
klien lokal dengan beberapa skrip jarak jauh seperti itu sepertinya bukan pendekatan yang baik.(while [ ... ] ; do sleep 1 ; done ; scp ... )&
. Kemudian tunggu di latar depan di server.bashrc
(dengan asumsi klien mengirim variabel env kanan) untuk file muncul. Saya akan memperbarui jawabannya nanti setelah beberapa pengujian (mungkin tidak ada waktu sampai besok).~/.ssh-${PID}-forwards
Cuplikan untuk sisi lokal, cocok untuk .bashrc:
sumber
Saya telah mencapai hal yang sama dengan membuat pipa pada klien lokal, kemudian mengarahkan stderr ke pipa yang juga diarahkan ke input ssh. Tidak diperlukan banyak koneksi ssh untuk menganggap porta yang dikenal bebas bisa gagal. Dengan cara ini spanduk masuk dan teks "Alokasi port ### ..." dialihkan ke host jarak jauh.
Saya memiliki skrip sederhana pada host
getsshport.sh
yang dijalankan pada host jarak jauh yang membaca input diarahkan dan mem-parsing port. Selama skrip ini tidak berakhir, ssh remote forward tetap terbuka.sisi lokal
3>&1 1>&2 2>&3
adalah sedikit trik untuk menukar stderr dan stdout, sehingga stderr disalurkan ke cat, dan semua output normal dari ssh ditampilkan pada stderr.sisi jarak jauh ~ / getsshport.sh
Saya memang mencoba
grep
pesan "dialokasikan port" di sisi lokal terlebih dahulu sebelum mengirimnya melalui ssh, tetapi tampaknya ssh akan memblokir menunggu pipa untuk terbuka di stdin. grep tidak membuka pipa untuk menulis sampai menerima sesuatu, jadi ini pada dasarnya deadlock.cat
Namun tampaknya tidak memiliki perilaku yang sama, dan membuka pipa untuk menulis segera memungkinkan ssh untuk membuka koneksi.ini adalah masalah yang sama di sisi remote, dan mengapa
read
baris demi baris bukan hanya grep dari stdin - jika tidak `/ tmp / dialokasikanports 'tidak ditulis sampai terowongan ssh ditutup yang mengalahkan seluruh tujuanMemipipkan stderr ssh ke perintah seperti
~/getsshport.sh
lebih disukai, karena tanpa menentukan perintah, teks spanduk, atau apa pun yang ada di dalam pipa dieksekusi pada shell jauh.sumber
renice +10 $$; exec cat
sebelumdone
untuk menghemat sumber daya.Ini rumit, penanganan sisi server tambahan sepanjang
SSH_CONNECTION
atauDISPLAY
akan bagus, tetapi tidak mudah untuk ditambahkan: bagian dari masalahnya adalah bahwa hanyassh
klien yang tahu tujuan lokal, paket permintaan (ke server) berisi hanya alamat dan port jarak jauh.Jawaban lain di sini memiliki berbagai solusi tidak wajar untuk menangkap sisi klien ini dan mengirimkannya ke server. Inilah pendekatan alternatif yang tidak terlalu cantik untuk jujur, tapi setidaknya pesta jelek ini disimpan di sisi klien ;-)
SendEnv
sehingga kami dapat mengirim beberapa variabel lingkungan secara native di atas ssh (mungkin tidak default)AcceptEnv
untuk menerima yang sama (mungkin tidak diaktifkan secara default)ssh
output stderr klien dengan pustaka yang dimuat secara dinamis, dan perbarui lingkungan klien ssh selama pengaturanIni berfungsi (dengan senang hati, untuk sekarang) karena remote forward diatur dan dicatat sebelum lingkungan dipertukarkan (konfirmasi dengan
ssh -vv ...
). Pustaka yang dimuat secara dinamis harus menangkapwrite()
fungsi libc (ssh_confirm_remote_forward()
→logit()
→do_log()
→write()
). Mengarahkan atau membungkus fungsi dalam biner ELF (tanpa kompilasi ulang) adalah urutan besarnya lebih kompleks daripada melakukan hal yang sama untuk suatu fungsi di perpustakaan dinamis.Di klien
.ssh/config
(atau baris perintah-o SendEnv ...
)Di server
sshd_config
(diperlukan root / perubahan administrasi)Pendekatan ini bekerja untuk klien Linux dan tidak memerlukan apa-apa khusus pada server, itu harus bekerja untuk * nix lain dengan beberapa perubahan kecil. Bekerja dari setidaknya OpenSSH 5.8p1 hingga 7.5p1.
Kompilasi dengan
gcc -Wall -shared -ldl -Wl,-soname,rfwd -o rfwd.so rfwd.c
Invoke dengan:Kode:
(Ada beberapa jebakan beruang glibc terkait dengan versi simbol dengan pendekatan ini, tetapi
write()
tidak memiliki masalah ini.)Jika Anda merasa berani, Anda bisa mengambil
setenv()
kode terkait dan menambalnya kessh.c
ssh_confirm_remote_forward()
fungsi callback.Ini menetapkan variabel lingkungan bernama
SSH_RFWD_nnn
, periksa ini di profil Anda, misalnya dibash
Peringatan:
ssh
saat ini tidak dengan jelas mencatat penerusan penuh bentuk * local: port: remote: port * (jika diperlukan penguraian lebih lanjut daridebug1
pesanssh -v
akan diperlukan), tetapi Anda tidak memerlukan ini untuk kasus penggunaan AndaAnda dapat (sebagian) melakukan ini secara interaktif dengan melarikan diri
~#
, anehnya implementasi melompati saluran yang mendengarkan, itu hanya daftar yang terbuka (yaitu TCP ESTABLISHED), dan itu tidak mencetak bidang yang berguna dalam hal apa pun. Lihatchannels.c
channel_open_message()
Anda dapat menambal yang berfungsi untuk mencetak rincian untuk
SSH_CHANNEL_PORT_LISTENER
slot, tapi itu hanya membuat Anda yang forwardings lokal ( saluran tidak sama dengan yang sebenarnya depan ). Atau, Anda bisa menambalnya untuk membuang dua tabel penerusan darioptions
struct global :Ini berfungsi dengan baik, meskipun itu bukan solusi "terprogram", dengan peringatan bahwa kode klien tidak (belum ditandai oleh XXX di sumbernya) memperbarui daftar ketika Anda menambahkan / menghapus penerusan on-the-fly (
~C
)Jika server adalah Linux Anda memiliki satu opsi lagi, ini adalah yang saya gunakan secara umum, meskipun untuk penerusan lokal daripada remote.
lo
adalah 127.0.0.1/8, di Linux Anda dapat secara transparan mengikat ke alamat mana pun di 127/8 , sehingga Anda dapat menggunakan port tetap jika Anda menggunakan alamat 127.xyz yang unik, misalnya:Ini tunduk pada pengikatan port dengan hak istimewa <1024, OpenSSH tidak mendukung kemampuan Linux dan memiliki pemeriksaan UID hard-kode pada sebagian besar platform.
Oktet yang dipilih dengan bijak (mnemonik ordinal ASCII dalam kasus saya) membantu mengurai kekacauan di akhir hari.
sumber