อาร์กิวเมนต์บรรทัดคำสั่ง ssh รีโมตรับการวิเคราะห์คำได้อย่างไร


11

ฉันเห็นคำถามและคำตอบเกี่ยวกับการต้องหลีกเลี่ยงการขัดแย้งกับคำสั่ง ssh จากระยะไกล คำถามของฉันคือ: การแยกวิเคราะห์ครั้งที่สองเสร็จสมบูรณ์เมื่อใดและเมื่อไร

ถ้าฉันเรียกใช้ต่อไปนี้:

$ ssh otherhost pstree -a -p

ฉันเห็นสิ่งต่อไปนี้ในผลลัพธ์:

  |-sshd,3736
  |   `-sshd,1102
  |       `-sshd,1109
  |           `-pstree,1112 -a -p

กระบวนการหลักสำหรับคำสั่งระยะไกล ( pstree) คือsshdไม่ปรากฏว่ามีเปลือกใด ๆ ที่จะแยกวิเคราะห์บรรทัดคำสั่งไปยังคำสั่งระยะไกลดังนั้นมันดูเหมือนว่าจะไม่จำเป็นต้องมีการอ้างถึงหรือหนีสองครั้ง ( แต่มันก็แน่นอน) ถ้าฉันกลับไปที่นั่นก่อนและรับเชลล์ล็อกอินจากนั้นเรียกใช้pstree -a -pฉันจะเห็นสิ่งต่อไปนี้ในผลลัพธ์:

  ├─sshd,3736
     └─sshd,3733
         └─sshd,3735
             └─bash,3737
                 └─pstree,4130 -a -p

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

คำตอบ:


22

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

เป็นไปไม่ได้ที่จะหลีกเลี่ยงเปลือกระยะไกล โพรโทคอลไม่มีอะไรที่เหมือนกับการส่งอาร์เรย์ของสตริงที่สามารถแยกวิเคราะห์เป็นอาร์เรย์ argv บนเซิร์ฟเวอร์ และเซิร์ฟเวอร์ SSH จะไม่บายพาสเชลล์ระยะไกลเพราะอาจเป็นข้อ จำกัด ด้านความปลอดภัย: การใช้โปรแกรมที่ถูก จำกัด เนื่องจากเชลล์ของผู้ใช้เป็นวิธีการจัดทำบัญชีที่ถูก จำกัด ซึ่งอนุญาตให้เรียกใช้คำสั่งบางอย่างเท่านั้น (เช่นบัญชี rsync เท่านั้นหรือ บัญชี git-only)

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

ssh otherhost pstree -a -p
ssh otherhost 'pstree -a -p'
ssh otherhost 'pstree -a -p; true'

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


2
ฮ่า! ไม่น่าเชื่อว่าคุณจะเอาชนะฉันเพื่อตอบคำถามของฉันเอง ฉันคิดครึ่งทางผ่านการโพสต์คำถามและคิดว่าฉันควรผ่านการถามและตอบด้วยตนเอง
ไม่มี

10

ฉันคิดว่าฉันคิดออก:

$ ssh otherhost pstree -a -p -s '$$'
init,1         
  `-sshd,3736
      `-sshd,11998
          `-sshd,12000
              `-pstree,12001 -a -p -s 12001

อาร์กิวเมนต์ที่ต้องใช้pstreeคือ: แสดงอาร์กิวเมนต์บรรทัดคำสั่งแสดง pids และแสดงเพียงกระบวนการหลักของ pid ที่กำหนด '$$'เป็นตัวแปรเปลือกพิเศษที่จะเข้ามาแทนที่ทุบตีด้วย pid ของตัวเองเมื่อทุบตีประเมินอาร์กิวเมนต์บรรทัดคำสั่ง มันยกมาหนึ่งครั้งเพื่อหยุดมันจากการตีความโดยเปลือกท้องถิ่นของฉัน แต่มันไม่ได้อ้างหรือหลบหนีเป็นสองเท่าเพื่อให้สามารถตีความได้โดยเปลือกระยะไกล

อย่างที่เราเห็นมันถูกแทนที่ด้วย12001นั่นคือ pid ของเชลล์ เราสามารถเห็นได้จากผลลัพธ์: pstree,12001ว่ากระบวนการที่มี pid ที่ 12001 นั้นเป็น pstree นั้นเอง ดังนั้นpstreeเปลือกคืออะไร?

สิ่งที่ฉันรวบรวมกำลังเกิดขึ้นนั่นคือสิ่งที่bashกำลังถูกเรียกใช้และแยกวิเคราะห์อาร์กิวเมนต์บรรทัดคำสั่ง แต่จากนั้นจะเรียกใช้execเพื่อแทนที่ตัวเองด้วยคำสั่งที่ถูกเรียกใช้

ดูเหมือนว่าจะทำสิ่งนี้ในกรณีของคำสั่งระยะไกลเดียว:

$ ssh otherhost pstree -a -p -s '$$' \; echo hi
init,1         
  `-sshd,3736
      `-sshd,17687
          `-sshd,17690
              `-bash,17691 -c pstree -a -p -s $$ ; echo hi
                  `-pstree,17692 -a -p -s 17691
hi

ในกรณีนี้ผมขอสองคำสั่งเรียกใช้: ตามมาด้วยpstree echoและเราสามารถดูที่นี่ที่ในความเป็นจริงปรากฏขึ้นในต้นไม้กระบวนการเป็นผู้ปกครองของbashpstree


ได้ ! + 1. มันเป็นตัวอย่างของสิ่งที่ Gilles ใส่อย่างเป็นทางการมากขึ้นเป็นครั้งแรกและเป็นตัวอย่างที่สอง บางทีการให้เครดิตเขากับคำตอบแรกของเขานั้นเป็นไปตามลำดับหรือไม่
Cbhihe

0

สนับสนุนสิ่งที่คำตอบอื่น ๆ ได้กล่าวไว้ฉันค้นหาโค้ดที่เรียกใช้คำสั่งบนรีโมทhttps://github.com/openssh/openssh-portable/blob/4f29309c4cb19bcb1774931db84cacc414f17d29/session.c#L1660 ...

1660    /*
1661     * Execute the command using the user's shell.  This uses the -c
1662     * option to execute the command.
1663     */
1664    argv[0] = (char *) shell0;
1665    argv[1] = "-c";
1666    argv[2] = (char *) command;
1667    argv[3] = NULL;
1668    execve(shell, argv, env);
1669    perror(shell);
1670    exit(1);

... ซึ่งในขณะที่คุณสามารถดูจะเรียกโดยไม่มีเงื่อนไขshellกับอาร์กิวเมนต์แรกและอาร์กิวเมนต์ที่สอง-c commandก่อนหน้านี้ตัวแปรถูกกำหนดให้เปลือกเข้าสู่ระบบของผู้ใช้ที่บันทึกไว้ใน shell เป็นอาร์กิวเมนต์ของฟังก์ชั่นนี้และในที่สุดก็ถูกตั้งค่าเป็นสตริงอ่านคำต่อคำแบบปิดสาย (ดูในไฟล์เดียวกัน ) ดังนั้นเซิร์ฟเวอร์จะไม่ตีความคำสั่งเลย แต่เชลล์จะถูกเรียกใช้บนรีโมตเสมอ/etc/passwdcommandsession_exec_req

อย่างไรก็ตามส่วนที่เกี่ยวข้องของข้อกำหนด SSH โปรโตคอลไม่ไม่ปรากฏว่าจำเป็นต้องมีพฤติกรรมเช่นนี้; มันพูดเท่านั้น

 byte      SSH_MSG_CHANNEL_REQUEST
 uint32    recipient channel
 string    "exec"
 boolean   want reply
 string    command

ข้อความนี้จะขอให้เซิร์ฟเวอร์เริ่มการทำงานของคำสั่งที่กำหนด สตริง 'คำสั่ง' อาจมีเส้นทาง ข้อควรระวังปกติจะต้องดำเนินการเพื่อป้องกันการดำเนินการตามคำสั่งที่ไม่ได้รับอนุญาต

อาจเป็นเพราะระบบปฏิบัติการบางระบบไม่มีแนวคิดของเชลล์บรรทัดคำสั่ง ตัวอย่างเช่นมันจะไม่บ้าสำหรับเซิร์ฟเวอร์ Classic MacOS ssh ที่จะป้อนสตริงคำสั่ง "exec" ให้กับล่ามAppleScriptแทน

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