การใช้งานฟังก์ชั่น Bash Script กับ Sudo


22

ฉันมีสคริปต์ที่ทำสิ่งต่าง ๆ จำนวนมากซึ่งส่วนใหญ่ไม่ต้องการสิทธิ์พิเศษใด ๆ อย่างไรก็ตามหนึ่งส่วนเฉพาะซึ่งฉันมีอยู่ในฟังก์ชั่นต้องการสิทธิ์รูท

ฉันไม่ต้องการให้สคริปต์ทั้งหมดทำงานเป็นรูทและฉันต้องการที่จะสามารถเรียกใช้ฟังก์ชั่นนี้ได้โดยใช้สิทธิ์รูทจากภายในสคริปต์ การขอรหัสผ่านหากจำเป็นไม่ใช่ปัญหาเนื่องจากส่วนใหญ่เป็นแบบโต้ตอบอยู่แล้ว อย่างไรก็ตามเมื่อฉันพยายามใช้sudo functionxฉันจะได้รับ:

sudo: functionx: command not found

อย่างที่ฉันคาดไว้exportก็ไม่ได้สร้างความแตกต่าง ฉันต้องการที่จะสามารถใช้งานฟังก์ชั่นโดยตรงในสคริปต์แทนที่จะแยกมันออกและดำเนินการเป็นสคริปต์แยกต่างหากด้วยเหตุผลหลายประการ

มีวิธีที่ฉันสามารถทำให้ฟังก์ชั่น "มองเห็น" ของฉันไปยัง sudo โดยไม่ต้องแตกไฟล์ค้นหาไดเรกทอรีที่เหมาะสม

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

ฉันคาดหวังว่าใครบางคนที่มี sudo ใด ๆ เท่านั้นที่สามารถเรียกใช้ฟังก์ชันได้เนื่องจากหนึ่งในสิ่งที่มันทำคือเปลี่ยนรหัสผ่าน


ความจริงที่ว่ามีฟังก์ชั่นหลายอย่างที่เกี่ยวข้องทำให้ยิ่งซับซ้อนและมีแนวโน้มที่จะล้มเหลว ตอนนี้คุณต้องค้นหาการพึ่งพาทั้งหมด (และการพึ่งพาทั้งหมดของพวกเขาด้วยหากมี ... ถึงระดับที่ลึกมาก) รวมถึงฟังก์ชั่นอื่น ๆ ที่ฟังก์ชั่นเมนูอาจเรียกใช้และdeclareพวกเขาด้วย
cas

เห็นด้วยและฉันอาจต้องกัดกระสุนและทำลายมัน (และทำอย่างดีที่สุดเพื่อกำหนดเส้นทางที่ถูกเรียกใช้อย่างถูกต้องและหวังว่าผู้ใช้จะเก็บไฟล์ไว้ด้วยกัน) หากไม่มีทางเลือกอื่นที่ดีกว่า
BryKKan

คำตอบ:


19

ฉันจะยอมรับว่าไม่มีวิธีที่ง่ายและใช้งานง่ายในการทำเช่นนี้และนี่คือแฮ็คบิต แต่คุณสามารถทำสิ่งนี้ได้:

function hello()
{
    echo "Hello!"
}

# Test that it works.
hello

FUNC=$(declare -f hello)
sudo bash -c "$FUNC; hello"

หรือมากกว่านั้น:

sudo bash -c "$(declare -f hello); hello"

มันเหมาะกับฉัน:

$ bash --version
GNU bash, version 4.3.42(1)-release (x86_64-apple-darwin14.5.0)
$ hello
Hello!
$
$ FUNC=$(declare -f hello)
$ sudo bash -c "$FUNC; hello"
Hello!

โดยทั่วไปแล้วdeclare -fจะส่งคืนเนื้อหาของฟังก์ชั่นซึ่งคุณจะส่งผ่านไปยังbash -cอินไลน์

หากคุณต้องการที่จะส่งออกการทำงานทั้งหมดจากอินสแตนซ์ด้านนอกของทุบตีเปลี่ยนไปFUNC=$(declare -f hello)FUNC=$(declare -f)

แก้ไข

ในการจัดการกับความคิดเห็นเกี่ยวกับการอ้างอิงดูตัวอย่างนี้:

$ hello()
> {
> echo "This 'is a' test."
> }
$ declare -f hello
hello ()
{
    echo "This 'is a' test."
}
$ FUNC=$(declare -f hello)
$ sudo bash -c "$FUNC; hello"
Password:
This 'is a' test.

1
สิ่งนี้สามารถทำงานได้โดยไม่ตั้งใจเพราะecho "Hello!"มีประสิทธิภาพเช่นเดียวกับecho Hello!(เช่นเครื่องหมายคำพูดคู่ไม่สร้างความแตกต่างสำหรับคำสั่ง echo นี้โดยเฉพาะ) ในสถานการณ์อื่น ๆ / ส่วนใหญ่คำพูดสองครั้งในฟังก์ชั่นมีแนวโน้มที่จะทำลายbash -cคำสั่ง
cas

1
นี่จะตอบคำถามเดิมดังนั้นถ้าฉันไม่ได้คำตอบที่ดีกว่าฉันจะยอมรับมัน อย่างไรก็ตามมันจะทำลายฟังก์ชั่นเฉพาะของฉัน (ดูการแก้ไขของฉัน) เนื่องจากมันขึ้นอยู่กับฟังก์ชั่นที่กำหนดไว้ในที่อื่น
BryKKan

1
ฉันได้ทำการทดสอบก่อนหน้านี้เมื่อบ่ายนี้ (โดยใช้bash -xcมากกว่าแค่bash -c) และดูเหมือนว่าbashฉลาดพอที่จะอ้างถึงสิ่งต่าง ๆ ในสถานการณ์นี้อีกครั้งแม้ว่าจะมีการแทนที่เครื่องหมายคำพูดคู่ด้วยคำพูดเดี่ยวและเปลี่ยน'เป็นเท่าที่'\''จำเป็น ฉันแน่ใจว่าจะมีบางกรณีที่ไม่สามารถจัดการได้ แต่มันใช้งานได้กับกรณีที่เรียบง่ายและซับซ้อนอย่างน้อย - เช่นลองfunction hello() { filename="this is a 'filename' with single quotes and spaces" ; echo "$filename" ; } ; FUNC=$(declare -f hello) ; bash -xc "$FUNC ; hello"
cas

4
@cas declare -fพิมพ์ออกนิยามฟังก์ชันในทางที่สามารถใหม่แยกวิเคราะห์โดยทุบตีเพื่อbash -c "$(declare -f)" ไม่ทำงานอย่างถูกต้อง (สมมติว่าเปลือกนอกยังเป็นทุบตี) ตัวอย่างที่คุณโพสต์แสดงให้เห็นว่าทำงานได้อย่างถูกต้อง - ที่ราคาถูกเปลี่ยนแปลงอยู่ในการติดตามเพราะทุบตีพิมพ์ร่องรอยในไวยากรณ์เชลล์เช่นลองbash -xc 'echo "hello world"'
Gilles 'ดังนั้น - หยุดเป็นความชั่วร้าย'

3
คำตอบที่ยอดเยี่ยม ฉันใช้วิธีการแก้ปัญหาของคุณ - ฉันจะทราบว่าคุณสามารถนำเข้าสคริปต์ตัวเองจากภายในสคริปต์หากคุณทำมันภายในเงื่อนไขที่ตรวจสอบว่าsudo yourFunctionจะไม่พบ (มิฉะนั้นคุณจะได้รับข้อผิดพลาดการแบ่งส่วนจากการสอบถามซ้ำ)
GrayedFox

5

"ปัญหา" คือการsudoล้างสภาพแวดล้อม (ยกเว้นหยิบตัวแปรที่อนุญาตจำนวนหนึ่ง) และตั้งค่าตัวแปรบางอย่างให้เป็นค่าความปลอดภัยที่กำหนดไว้ล่วงหน้าเพื่อป้องกันความเสี่ยงด้านความปลอดภัย กล่าวอีกนัยหนึ่งนี่ไม่ใช่ปัญหาจริง มันเป็นคุณสมบัติ

ตัวอย่างเช่นหากคุณตั้งค่าPATH="/path/to/myevildirectory:$PATH"และsudoไม่ได้ตั้งค่า PATH เป็นค่าที่กำหนดไว้ล่วงหน้าดังนั้นสคริปต์ใด ๆ ที่ไม่ได้ระบุชื่อพา ธ แบบเต็มเป็นคำสั่ง ALL ที่รัน (เช่นสคริปต์ส่วนใหญ่) จะมองหา/path/to/myevildirectoryไดเรกทอรีอื่นก่อน ใส่คำสั่งเช่นlsหรือgrepเครื่องมือทั่วไปอื่น ๆ ในนั้นและคุณสามารถทำสิ่งที่คุณต้องการในระบบ

วิธีที่ง่ายที่สุด / ดีที่สุดคือการเขียนฟังก์ชั่นใหม่เป็นสคริปต์และบันทึกไว้ที่ใดที่หนึ่งในพา ธ (หรือระบุพา ธ เต็มไปยังสคริปต์ในsudoบรรทัดคำสั่ง - ซึ่งคุณจะต้องทำต่อไปยกเว้นว่าsudoมีการกำหนดค่าให้คุณ เพื่อรันคำสั่งใดก็ได้ในฐานะรูท) และทำให้มันสามารถเรียกใช้งานได้chmod +x /path/to/scriptname.sh

ฟังก์ชั่นการเขียนใหม่เปลือกเป็นสคริปต์เป็นง่ายๆเป็นเพียงการบันทึกคำสั่งภายในกำหนดฟังก์ชันไปยังแฟ้ม (โดยไม่มีfunction ..., {และ}บรรทัด)


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

1
ยังsudo -Eหลีกเลี่ยงการล้างสภาพแวดล้อม
จะ

ฉันเข้าใจว่าทำไมมันถึงเกิดขึ้น ฉันหวังว่าจะมีวิธีแก้ไขพฤติกรรมนี้ชั่วคราว ที่อื่นมีการพูดถึงตัวเลือก -E แม้ว่าจะไม่ได้ผลในกรณีนี้ น่าเสียดายที่ในขณะที่ฉันขอขอบคุณคำอธิบายวิธีทำให้เป็นสคริปต์แบบสแตนด์อโลน แต่โดยเฉพาะนั้นไม่ได้ตอบคำถามเพราะฉันต้องการวิธีการหลีกเลี่ยง ฉันไม่สามารถควบคุมได้ว่าผู้ใช้วางสคริปต์ไว้ที่ใดและฉันต้องการหลีกเลี่ยงทั้งไดเรกทอรีที่เขียนโค้ดยากและเพลงและการเต้นรำที่พยายามระบุว่าสคริปต์หลักนั้นทำงานจากที่ใด
BryKKan

ไม่สำคัญว่าเป็นสิ่งที่ OP ขอหรือไม่ หากสิ่งที่เขาต้องการไม่ทำงานหรือสามารถทำงานได้โดยทำสิ่งที่ไม่ปลอดภัยอย่างยิ่งพวกเขาจำเป็นต้องได้รับการบอกกล่าวและจัดหาทางเลือก - แม้ว่าทางเลือกนั้นเป็นสิ่งที่พวกเขาระบุไว้อย่างชัดเจนว่าพวกเขาไม่ต้องการ (เพราะ บางครั้งนั่นเป็นวิธีเดียวหรือวิธีที่ดีที่สุดในการทำอย่างปลอดภัย) มันจะไม่รับผิดชอบที่จะบอกใครบางคนถึงวิธีการยิงตัวเองที่เท้าโดยไม่เตือนพวกเขาเกี่ยวกับผลกระทบที่อาจเกิดขึ้นจากการเล็งปืนไปที่เท้าและดึงไกปืน
cas

1
@cas นั่นคือความจริง ไม่สามารถทำได้อย่างปลอดภัยเป็นคำตอบที่ยอมรับได้ในบางสถานการณ์ ดูการแก้ไขครั้งล่าสุดของฉัน ฉันอยากรู้ว่าคุณมีความคิดเห็นเกี่ยวกับผลกระทบด้านความปลอดภัยหรือไม่
BryKKan

3

ฉันได้เขียนSudoฟังก์ชั่นทุบตีของฉันเองเพื่อใช้เรียกฟังก์ชันและนามแฝง:

function Sudo {
        local firstArg=$1
        if [ $(type -t $firstArg) = function ]
        then
                shift && command sudo bash -c "$(declare -f $firstArg);$firstArg $*"
        elif [ $(type -t $firstArg) = alias ]
        then
                alias sudo='\sudo '
                eval "sudo $@"
        else
                command sudo "$@"
        fi
}

2

คุณสามารถรวมฟังก์ชั่นและชื่อแทน

ตัวอย่าง:

function hello_fn() {
    echo "Hello!" 
}

alias hello='bash -c "$(declare -f hello_fn); hello_fn"' 
alias sudo='sudo '

จากนั้นsudo helloทำงาน


1

นี่เป็นรูปแบบในคำตอบของวิล มันเกี่ยวข้องกับcatกระบวนการเพิ่มเติมแต่ให้ความสะดวกสบายของ heredoc โดยสรุปแล้วมันจะเป็นเช่นนี้:

f () 
{
    echo ok;
}

cat <<EOS | sudo bash
$(declare -f f)
f
EOS

หากคุณต้องการอาหารมากขึ้นสำหรับความคิดลองสิ่งนี้:

#!/bin/bash

f () 
{ 
    x="a b"; 
    menu "$x"; 
    y="difficult thing"; 
    echo "a $y to parse"; 
}

menu () 
{
    [ "$1" == "a b" ] && 
    echo "here's the menu"; 
}

cat <<EOS | sudo bash
$(declare -f f)
$(declare -f menu)
f
EOS

ผลลัพธ์คือ:

here's the menu
a difficult thing to pass

ที่นี่เรามีmenuฟังก์ชั่นที่สอดคล้องกับหนึ่งในคำถามซึ่งคือ "นิยามที่อื่นในสคริปต์หลัก" หาก "ที่อื่น" หมายถึงคำจำกัดความของมันได้ถูกอ่านในขั้นตอนนี้เมื่อฟังก์ชั่นที่มีความต้องการsudoกำลังดำเนินการแล้วสถานการณ์จะคล้ายคลึง แต่มันอาจยังไม่ได้อ่าน อาจมีฟังก์ชั่นอื่นที่จะเรียกคำจำกัดความของมัน ในกรณีdeclare -f menuนี้จะต้องถูกแทนที่ด้วยบางสิ่งที่ซับซ้อนมากขึ้นหรือสคริปต์ทั้งหมดได้รับการแก้ไขในลักษณะที่menuฟังก์ชั่นได้รับการประกาศแล้ว


น่าสนใจมาก. ฉันจะลองดูซักครั้ง และใช่menuฟังก์ชั่นจะได้รับการประกาศก่อนจุดนี้ตามที่fถูกเรียกจากเมนู
BryKKan

0

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

  • ใช้$0ชื่อพา ธ สำหรับสคริปต์โดยใช้ชื่อนั้นในsudoคำสั่งและผ่านตัวเลือกที่สคริปต์จะตรวจสอบเพื่อเรียกการอัปเดตรหัสผ่าน ตราบใดที่คุณพึ่งพาการหาสคริปต์ในเส้นทาง (มากกว่าแค่วิ่ง./myscript), คุณควรจะได้รับชื่อพา ธ $0สัมบูรณ์ใน
  • ตั้งแต่sudoรันสคริปต์มันสามารถเข้าถึงฟังก์ชันที่ต้องการในสคริปต์
  • ที่ด้านบนของสคริปต์ (แทนที่จะผ่านการประกาศฟังก์ชั่น) สคริปต์จะตรวจสอบuidและรู้ว่ามันถูกเรียกใช้ในฐานะผู้ใช้รูทและเห็นว่ามีตัวเลือกที่กำหนดให้บอกให้อัปเดตรหัสผ่านไปทำ ที่.

สคริปต์สามารถเกิดขึ้นอีกได้ด้วยเหตุผลต่าง ๆ : การเปลี่ยนแปลงสิทธิพิเศษเป็นหนึ่ง

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