ทำไม bashrc ตรวจสอบว่าเชลล์ปัจจุบันมีการโต้ตอบหรือไม่?


62

บน Arch ของฉันติดตั้ง/etc/bash.bashrcและ/etc/skel/.bashrcมีบรรทัดเหล่านี้:

# If not running interactively, don't do anything
[[ $- != *i* ]] && return

บน Debian /etc/bash.bashrcมี:

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

และ/etc/skel/.bashrc:

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

man bashอย่างไรก็ตามตามเชลล์ที่ไม่มีการโต้ตอบไม่แม้แต่จะอ่านไฟล์เหล่านี้:

   When  bash  is  started  non-interactively,  to run a shell script, for
   example, it looks for the variable BASH_ENV in the environment, expands
   its  value if it appears there, and uses the expanded value as the name
   of a file to read and execute.  Bash behaves as if the  following  com
   mand were executed:
          if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
   but  the value of the PATH variable is not used to search for the file
   name.

หากฉันเข้าใจถูกต้อง*.bashrcไฟล์จะถูกอ่านBASH_ENVก็ต่อเมื่อตั้งค่าให้ชี้ไปที่ไฟล์เหล่านั้น นี่เป็นสิ่งที่ไม่สามารถเกิดขึ้นได้โดยบังเอิญและจะเกิดขึ้นก็ต่อเมื่อมีคนตั้งค่าตัวแปรไว้อย่างชัดเจน

ดูเหมือนจะทำลายความเป็นไปได้ที่จะให้สคริปต์ของผู้ใช้มา.bashrcโดยอัตโนมัติโดยการตั้งค่าBASH_ENVสิ่งที่อาจมีประโยชน์ เนื่องจาก bash นั้นจะไม่อ่านไฟล์เหล่านี้เมื่อทำงานแบบไม่โต้ตอบเว้นแต่จะได้รับคำสั่งให้ทำอย่างชัดเจนทำไม*bashrcไฟล์เริ่มต้นไม่อนุญาต?


5
สำหรับคำตอบที่ใช้งานได้ดูSCP ล้มเหลวโดยไม่มีข้อผิดพลาด
Gilles

คำตอบ:


69

นี่เป็นคำถามที่ฉันจะโพสต์ที่นี่ไม่กี่สัปดาห์ที่ผ่านมา เช่นเดียวกับterdonฉันเข้าใจว่า a .bashrcนั้นมาจากเชลล์ Bash แบบโต้ตอบเท่านั้นดังนั้นจึงไม่จำเป็นต้อง.bashrcตรวจสอบว่ามันทำงานในเชลล์เชิงโต้ตอบหรือไม่ พลุกพล่านทั้งหมดกระจายที่ผมใช้ (อูบุนตู RHEL และ Cygwin) มีประเภทของการตรวจสอบ (การทดสอบบางส่วน$-หรือ$PS1) เพื่อให้แน่ใจเปลือกปัจจุบันมีการโต้ตอบ ฉันไม่ชอบการเขียนโปรแกรมสินค้าศาสนา.bashrcดังนั้นผมจึงตั้งค่าเกี่ยวกับการทำความเข้าใจวัตถุประสงค์ของรหัสนี้ในของฉัน

Bash มีเคสพิเศษสำหรับเชลล์ระยะไกล

หลังจากค้นคว้าปัญหาฉันพบว่าเปลือกระยะไกลได้รับการปฏิบัติแตกต่างกัน แม้ว่าโดยปกติแล้วเปลือก Bash แบบเชลล์จะไม่เรียกใช้~/.bashrcคำสั่งเมื่อเริ่มต้น แต่จะมีกรณีพิเศษเกิดขึ้นเมื่อเชลล์ถูกเรียกใช้โดย remote shell daemon :

ทุบตีพยายามที่จะตรวจสอบเมื่อมันจะถูกเรียกใช้ด้วยการป้อนข้อมูลมาตรฐานการเชื่อมต่อกับเชื่อมต่อเครือข่ายเช่นเมื่อดำเนินการโดยภูตระยะไกลเปลือกมักหรือภูตเปลือกที่เชื่อถือได้rshd sshdหาก Bash ระบุว่ากำลังรันในโหมดนี้มันจะอ่านและดำเนินการคำสั่งจาก ~ / .bashrc หากไฟล์นั้นมีอยู่และอ่านได้ shมันจะไม่ทำเช่นนี้หากเรียกว่าเป็น --norcตัวเลือกที่อาจจะถูกใช้ในการยับยั้งการทำงานนี้และ--rcfileตัวเลือกที่อาจถูกใช้เพื่อบังคับให้ไฟล์อื่นจะอ่าน แต่ไม่rshdว่ามิได้sshdทั่วไปเรียกเปลือกที่มีตัวเลือกเหล่านั้นหรือช่วยให้พวกเขามีการระบุ

ตัวอย่าง

.bashrcใส่ต่อไปนี้ที่จุดเริ่มต้นของระยะไกลได้ (หาก.bashrcมีที่มาจาก.profileหรือ.bash_profileปิดใช้งานสิ่งนี้ชั่วคราวขณะทดสอบ):

echo bashrc
fun()
{
    echo functions work
}

รันคำสั่งต่อไปนี้แบบโลคัล:

$ ssh remote_host 'echo $- $0'
bashrc
hBc bash
  • ไม่มีiใน$-ระบุว่าเปลือกเป็นที่ไม่ใช่แบบโต้ตอบ
  • ไม่มีชั้นนำ-ในการ$0แสดงให้เห็นว่าเปลือกไม่ได้เป็นเปลือกเข้าสู่ระบบ

ฟังก์ชันเชลล์ที่กำหนดในรีโมต.bashrcสามารถรันได้:

$ ssh remote_host fun
bashrc
functions work

ผมสังเกตเห็นว่า~/.bashrcเป็นเพียงsshมาเมื่อมีคำสั่งระบุเป็นอาร์กิวเมนต์สำหรับ สิ่งนี้สมเหตุสมผล: เมื่อsshใช้เพื่อเริ่มเชลล์การเข้าสู่ระบบปกติ.profileหรือ.bash_profileถูกเรียกใช้ (และ.bashrcจะจัดหาเฉพาะเมื่อทำโดยหนึ่งในไฟล์เหล่านี้อย่างชัดเจน)

ประโยชน์หลักที่ฉันเห็นว่ามี.bashrcที่มาเมื่อเรียกใช้คำสั่งระยะไกล (ไม่โต้ตอบ) คือสามารถใช้ฟังก์ชันเชลล์ได้ อย่างไรก็ตามคำสั่งส่วนใหญ่ในแบบทั่วไป.bashrcนั้นจะเกี่ยวข้องเฉพาะในเชลล์แบบโต้ตอบเท่านั้นเช่นนามแฝงจะไม่ถูกขยายเว้นแต่ว่าเชลล์เป็นแบบโต้ตอบ

การถ่ายโอนไฟล์ระยะไกลอาจล้มเหลว

นี่ไม่ใช่ปัญหาเมื่อใช้rshหรือsshเริ่มใช้เชลล์ล็อกอินแบบโต้ตอบหรือเมื่อใช้เชลล์เพื่อเรียกใช้คำสั่ง แต่ก็อาจจะมีปัญหาสำหรับโปรแกรมเช่นrcp, scpและsftpที่ใช้เปลือกหอยระยะไกลสำหรับการถ่ายโอนข้อมูล

ปรากฎว่าเชลล์เริ่มต้นของผู้ใช้ระยะไกล (เช่น Bash) เริ่มต้นโดยปริยายเมื่อใช้scpคำสั่ง ไม่มีการพูดถึงเรื่องนี้ใน man page - มีเพียงการกล่าวถึงที่scpใช้sshสำหรับการถ่ายโอนข้อมูล นี้มีผลที่ตามมาว่าถ้า.bashrcมีคำสั่งใด ๆ ที่พิมพ์ไปออกมาตรฐานการถ่ายโอนไฟล์จะล้มเหลวเช่น SCP ล้มเหลวไม่มีข้อผิดพลาด

ดูเพิ่มเติมที่รายงานข้อผิดพลาด Red Hat ที่เกี่ยวข้องจาก 15 ปีที่ผ่านมาscp พักเมื่อมีคำสั่ง echo ใน / etc / bashrc (ซึ่งปิดในที่สุดWONTFIX)

ทำไมscpและsftpล้มเหลว

SCP (Secure copy)และSFTP (Secure File Transfer Protocol)มีโปรโตคอลของตัวเองสำหรับ Local และ Remote ที่สิ้นสุดเพื่อแลกเปลี่ยนข้อมูลเกี่ยวกับไฟล์ที่ถูกถ่ายโอน ข้อความที่ไม่คาดคิดใด ๆ จากปลายรีโมตนั้นตีความผิด ๆ ว่าเป็นส่วนหนึ่งของโปรโตคอลและการถ่ายโอนล้มเหลว ตามคำถามที่พบบ่อยจาก Snail Book

สิ่งที่มักจะเกิดขึ้น แต่เป็นว่ามีงบทั้งในระบบหรือแฟ้มเริ่มต้นเปลือกต่อผู้ใช้บนเซิร์ฟเวอร์ ( .bashrc, .profile, /etc/csh.cshrc, .loginฯลฯ ) ซึ่งข้อความการส่งออกในการเข้าสู่ระบบตั้งใจจะอ่านได้โดยมนุษย์ (เช่นfortune, echo "Hi there!", ฯลฯ )

รหัสดังกล่าวควรสร้างผลลัพธ์ในการเข้าสู่ระบบแบบโต้ตอบเท่านั้นเมื่อมีสิ่งที่ ttyแนบมากับอินพุตมาตรฐาน ถ้ามันไม่ได้ทำให้การทดสอบนี้ก็จะใส่ข้อความเหล่านี้ที่พวกเขาไม่ได้อยู่ในกรณีนี้ก่อให้เกิดมลพิษกระแสโปรโตคอลระหว่างscp2/ และsftpsftp-server

เหตุผลที่ไฟล์เริ่มต้นของเชลล์มีความเกี่ยวข้องเลยก็คือsshd ใช้เชลล์ของผู้ใช้เมื่อเริ่มต้นโปรแกรมใด ๆ ในนามของผู้ใช้ (ใช้เช่น / bin / sh -c "คำสั่ง") นี่เป็นประเพณี Unix และมีข้อดี:

  • การตั้งค่าปกติของผู้ใช้ (นามแฝงคำสั่งตัวแปรสภาพแวดล้อม umask ฯลฯ ) จะมีผลเมื่อเรียกใช้คำสั่งระยะไกล
  • การปฏิบัติโดยทั่วไปของการตั้งค่าเชลล์ของบัญชีเป็น / bin / false เพื่อปิดการใช้งานจะเป็นการป้องกันเจ้าของไม่ให้รันคำสั่งใด ๆ หากการพิสูจน์ตัวตนยังคงประสบความสำเร็จโดยไม่ได้ตั้งใจด้วยเหตุผลบางประการ

รายละเอียดโปรโตคอล SCP

สำหรับผู้ที่สนใจรายละเอียดเกี่ยวกับวิธีการทำงานของ SCP ฉันพบข้อมูลที่น่าสนใจในวิธีการทำงานของ SCPซึ่งรวมถึงรายละเอียดเกี่ยวกับการเรียกใช้ scp ด้วยโปรไฟล์เชลล์แบบพูดคุยที่ด้านไกล :

ตัวอย่างเช่นสิ่งนี้สามารถเกิดขึ้นได้หากคุณเพิ่มสิ่งนี้ลงในโปรไฟล์เชลล์ของคุณบนระบบรีโมต:

echo ""

ทำไมมันถึงหยุด ที่มาจากวิธีการที่scpในแหล่งโหมดรอการยืนยันจากข้อความโปรโตคอลแรก หากไม่ใช่ไบนารี 0 คาดว่าจะเป็นการแจ้งเตือนปัญหาระยะไกลและรอให้อักขระเพิ่มขึ้นเพื่อสร้างข้อความแสดงข้อผิดพลาดจนกว่าจะถึงบรรทัดใหม่ เนื่องจากคุณไม่ได้พิมพ์อีกบรรทัดใหม่หลังจากบรรทัดแรกท้องถิ่นของคุณscpก็ยังคงวนซ้ำถูกบล็อกread(2)ไว้ ในระหว่างนี้หลังจากที่ประมวลผลโปรไฟล์เชลล์บนรีโมตแล้วscpในโหมด sink เริ่มต้นซึ่งยังบล็อกอยู่read(2)โดยรอเป็นศูนย์ไบนารี่ซึ่งแสดงถึงการเริ่มการถ่ายโอนข้อมูล

สรุป / TLDR

ส่วนใหญ่ของงบในทั่วไป.bashrcจะมีประโยชน์สำหรับเปลือกโต้ตอบ - ไม่ได้เมื่อใช้คำสั่งระยะไกลด้วยหรือrsh sshในสถานการณ์เช่นนี้ส่วนใหญ่การตั้งค่าตัวแปรเปลือกนามแฝงและการกำหนดฟังก์ชั่นไม่ได้ต้องการ - และการพิมพ์ข้อความใด ๆที่จะออกมาตรฐานเป็นอันตรายอย่างแข็งขันถ้าการถ่ายโอนไฟล์โดยใช้โปรแกรมเช่นหรือscp การออกหลังจากการตรวจสอบว่าเปลือกปัจจุบันคือไม่โต้ตอบเป็นพฤติกรรมที่ปลอดภัยที่สุดสำหรับsftp.bashrc


บทความที่ดี แต่ฉันควรทำอย่างไร ฉันจะมีการตรวจสอบโดย .bashrc แต่ยังคง scp ทางออกด้วย 0 รหัสและไม่ได้คัดลอกอะไรไปยังกล่องระยะไกล
ses

@ses เป็นการดีที่สุดที่จะถามคำถามใหม่ที่คุณสามารถอธิบายสถานการณ์ของคุณโดยละเอียดยิ่งขึ้น
Anthony G - ความยุติธรรมสำหรับโมนิก้า

16

หน้าคนละเลยที่จะพูดถึงว่าbashยังเป็นแหล่ง.bashrcสำหรับเปลือกระยะไกลแบบไม่โต้ตอบเช่นเดียวกับใน

ssh hostname command

http://git.savannah.gnu.org/cgit/bash.git/tree/shell.c#n1010

 COMMAND        EXECUTE BASHRC
 --------------------------------
 bash -c foo        NO
 bash foo           NO
 foo                NO
 rsh machine ls     YES (for rsh, which calls 'bash -c')
 rsh machine foo    YES (for shell started by rsh) NO (for foo!)
 echo ls | bash     NO
 login              NO
 bash               YES

http://git.savannah.gnu.org/cgit/bash.git/tree/shell.c#n1050

          /* If we were run by sshd or we think we were run by rshd, execute
             ~/.bashrc if we are a top-level shell. */
          if ((run_by_ssh || isnetconn (fileno (stdin))) && shell_level < 2)
            {
              maybe_execute_file (SYS_BASHRC, 1);
              maybe_execute_file (bashrc_file, 1);

คุณมา. bashrc ของคุณจาก. bash_profile (หรือ. profile) หรือไม่
เกล็

ใช่ แต่นั่นไม่ใช่ประเด็น ที่ว่างเปล่า.bash_profileจะแสดงพฤติกรรมเดียวกัน
Mikel

ไม่แน่ใจว่าสิ่งที่แสดงให้ฉัน คุณกำลังพูดถึงแหล่งที่มาrsh(ซึ่งฉันไม่เคยใช้) ที่มา~/.bashrc? และนั่นก็คือโดยส่วนขยาย Linux distros จะบล็อกไว้bashในกรณีที่ใครก็ตามที่พยายามเรียกใช้สคริปต์ด้วยrsh? ssh serverไม่ควรแตะ.bashrcเนื่องจากเป็นเชลล์การเข้าสู่ระบบ ไม่เว้นแต่ระบบของคุณเป็นหนึ่งในผู้ที่แหล่ง ~ / .bashrc` ~/.profileจาก และssh server commandไม่ควรทำเช่นนั้น ไม่แน่นอนในระบบของฉัน
terdon

2
ไม่ฉันเชื่อมโยงคุณกับbashแหล่งที่มาไม่ใช่rshแหล่งที่มา :) ความคิดเห็นใช้กับsshเช่นกันมันเพิ่งเขียนเมื่อนานมาแล้ว ดูคำตอบที่อัปเดตพร้อมแหล่งข้อมูลเพิ่มเติม
Mikel

อาขอบคุณที่เป็นประโยชน์ ฉันจะทดสอบสิ่งนี้อย่างไร ฉันพยายามเพิ่มechoที่ด้านบนสุดของ/etc/bash.bashrcและ~/.bashrc(ก่อนการทดสอบเชลล์แบบโต้ตอบ) จากนั้น ssh ลงในเครื่องเป้าหมายด้วยssh server hostnameและในขณะที่hostnameทำงานฉันไม่เห็นเสียงสะท้อนใด ๆ ของฉัน มันคือการที่ฉันshell_level(สิ่งที่) ไม่<2?
terdon

5

โดยการประชุม.bashrcเป็นสถานที่ที่ผู้ใช้จัดเก็บการกำหนดค่าปรับแต่งสำหรับเปลือก

การกำหนดค่าแบบกำหนดเองเหล่านี้อาจเป็นตัวแปรสภาพแวดล้อมนามแฝงพร้อมท์แฟนซี ด้วยกระสุนที่ไม่มีการโต้ตอบสิ่งที่ขาดนั้นไม่มีความหมาย ยิ่งไปกว่านั้นเชลล์แบบไม่โต้ตอบสามารถโทรได้ในหลายบริบทคุณไม่แน่ใจว่าตัวแปรสภาพแวดล้อมเหล่านั้นสามารถนำไปสู่กรณีที่เป็นลบที่เป็นเท็จหรือแม้แต่ความเสี่ยงด้านความปลอดภัย

ตัวอย่างที่ใกล้เคียงที่สุดคือนามแฝงที่ชอบ:

alias cp='cp -i'

จากนั้นมันจะวางเชลล์ที่ไม่โต้ตอบของคุณตลอดไป

ดังนั้นการตรวจสอบจะดำเนินการที่ด้านบนของ.bashrcเพื่อให้แน่ใจว่าเราจะไม่เกิดปัญหา


เนื่องจากเชลล์สามารถถูกเรียกว่าเป็นเชลล์ล็อกอินแบบไม่โต้ตอบดังนั้นการบล็อกการจัดหาอย่างชัดเจนจึง*bashrcไม่สมเหตุสมผล

เมื่อเปลือกถูกเรียกว่าเป็นเปลือกเข้าสู่ระบบไม่โต้ตอบมันแหล่งที่มา/etc/profileแล้วมาเป็นคนแรกที่พบใน~/.bash_profile, ~/.bash_loginและ~/.profile:

เมื่อ bash ถูกเรียกใช้เป็นเชลล์ล็อกอินแบบโต้ตอบหรือเป็นเชลล์ที่ไม่มีการโต้ตอบกับตัวเลือก --loginมันจะอ่านและเรียกใช้คำสั่งจากไฟล์ / etc / profile ก่อนหากไฟล์นั้นมีอยู่ หลังจากอ่านไฟล์นั้นจะค้นหา ~ / .bash_profile, ~ / .bash_login และ ~ / .profile ตามลำดับและอ่านและเรียกใช้คำสั่งจากไฟล์แรกที่มีอยู่และอ่านได้

ไม่มีสิ่งใดป้องกันไฟล์เหล่านั้นจากการจัดหา.bashrcเองดังนั้นการตรวจสอบภายใน.bashrcจึงปลอดภัยและทำให้ทุกอย่างง่ายขึ้น


ใช่ฉันรู้แล้ว นี่คือเหตุผลที่เชลล์ที่ไม่มีการโต้ตอบไม่มี*bashrcไฟล์ต้นฉบับเลย คำถามของฉันคือเหตุผลที่ผู้จำหน่าย Linux หลายรายประสบปัญหาในการบล็อกเชลล์ที่ไม่ใช่แบบโต้ตอบอย่างชัดเจนไม่ให้อ่านไฟล์เหล่านี้หากไฟล์เหล่านี้ไม่ได้อ่านโดยเชลล์ที่ไม่ใช่แบบโต้ตอบ
terdon

1
@terdon: เนื่องจากเชลล์สามารถเรียกได้ว่าเป็นเชลล์ล็อกอินแบบไม่โต้ตอบดังนั้นเชลล์ล็อกอินจะเป็นแหล่ง.bash_profileซึ่งสามารถเป็นซอร์ส.bashrcได้
cuonglm

$BASH_ENVไม่มีในเปลือกหอยที่ไม่ใช่แบบโต้ตอบสิ่งเดียวที่อ่าน ทั้ง*profileและ*bashrcจะถูกละเว้น หรืออย่างน้อยนั่นคือสิ่งที่หน้าคนพูด อย่างไรก็ตามในขณะที่มิเคลแสดงให้เห็นหน้าคนอาจจะโกหก
terdon

@terdon: อ่านคำตอบที่อัปเดตของฉันพร้อมลิงก์ในนั้น คุณลองbash -l -c :เรียกใช้เชลล์การล็อกอินที่ไม่โต้ตอบหรือไม่?
cuonglm

ว้าว. คุณพูดถูก ฉันได้อ่านส่วนนั้นประมาณ--login100 ครั้ง แต่เห็นได้ชัดว่ามันไม่เคยลงทะเบียน ขอบคุณ!
terdon
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.