2010-05-10 14 views
45

Tôi có một tập lệnh Python sẽ thực hiện rất nhiều thứ đòi hỏi các đặc quyền ở cấp độ gốc, chẳng hạn như di chuyển các tệp trong/etc, cài đặt bằng apt-get, v.v. Tôi hiện có:Cách tốt nhất để kiểm tra xem người dùng tập lệnh có đặc quyền giống gốc không?

if os.geteuid() != 0: 
    exit("You need to have root privileges to run this script.\nPlease try again, this time using 'sudo'. Exiting.") 

Đây có phải là cách tốt nhất để kiểm tra không? Có các phương pháp hay nhất khác không?

Trả lời

26

Dưới sự "dễ dàng hơn để hỏi Tha thứ hơn Permission" nguyên tắc:

try: 
    os.rename('/etc/foo', '/etc/bar') 
except IOError as e: 
    if (e[0] == errno.EPERM): 
     print >> sys.stderr, "You need root permissions to do this, laterz!" 
     sys.exit(1) 

Nếu bạn lo lắng về! không có tính di động của os.geteuid() có thể bạn không nên mucking với /etc.

+0

Đây là cách di động nhất và thậm chí hoạt động trong một hoàn cảnh kỳ lạ hơn một chút, giống như với quyền hạn giới hạn của SELinux. –

+3

Điều này thực sự có thể tồi tệ hơn tùy thuộc vào tình hình. Hmm nhưng Florian có một điểm. –

+0

@Longpoke - là bạn đang đề cập đến tình huống mà người dùng có thể đổi tên quyền trong/etc nhưng - giả sử thiếu khả năng xóa tệp khỏi đường dẫn đó? Nếu đó là những gì bạn có nghĩa là, sau đó cố gắng để xác định những gì khả năng người dùng có trước là errand của một kẻ ngốc. Tốt hơn là cấu trúc mã của bạn cho ngữ nghĩa "giao dịch" trong đó A và B có thể được cuộn lại nếu C không thành công. – msw

48

os.geteuid lấy id người dùng hiệu quả, chính xác là những gì bạn muốn, vì vậy tôi không thể nghĩ ra bất kỳ cách nào tốt hơn để thực hiện kiểm tra như vậy. Một bit không chắc chắn là "root-like" trong tiêu đề: mã của bạn kiểm tra chính xácroot, không "thích" về nó và thực sự tôi sẽ không biết "root nhưng không root" nghĩa là gì - vì vậy, nếu bạn có ý nghĩa gì đó khác biệt hơn là "chính xác gốc", có lẽ bạn có thể làm rõ, nhờ

+2

Tôi giả Paul đang lo lắng về hệ thống khác nhau sử dụng uids khác nhau cho các quản trị viên. Thông thường id (root) == 0, nhưng nó không phải là phải và trên một số hệ thống nó thực sự không bằng nhau. – Stan

+0

@Stan: sau đó EAFP thực sự sẽ là tốt hơn – msw

+8

Giả định rằng hiệu quả UID == 0 có nghĩa là "root" là khá sâu sắc ăn sâu vào mã UNIX (bao gồm trong hầu hết các nguồn hạt nhân giống UNIX). Về mặt kỹ thuật dưới Linux, giả thiết KHÔNG nhất thiết phải chính xác. Mô hình "khả năng" của Linux cho phép hệ thống chạy với các điều khiển chi tiết hơn được ủy nhiệm thông qua quá trình kế thừa (các trình bao bọc lcap2) hoặc có khả năng thông qua các thuộc tính hệ thống tệp mở rộng. Các tính năng của SELinux cũng có thể gây ra những giả định như vậy. Đối với 99% các hệ thống trên mạng, geteuid() == 0 là đủ; cho phần còn lại hãy thử: ... ngoại trừ: là bạn của bạn. –

0

Tất cả phụ thuộc vào mức độ di động bạn muốn ứng dụng của bạn. Nếu bạn có nghĩa là kinh doanh, chúng ta phải giả định rằng tài khoản quản trị không phải lúc nào cũng bằng 0. Điều này có nghĩa là việc kiểm tra euid 0 là không đủ. Vấn đề là, có những tình huống mà một lệnh sẽ hoạt động như thể bạn là người chủ và tiếp theo sẽ thất bại với sự cho phép bị từ chối (nghĩ SELinux & đồng.). Vì vậy nó thực sự tốt hơn để thất bại một cách duyên dáng và kiểm tra EPERM errno bất cứ khi nào nó phù hợp.

7

Nếu bạn thực sự muốn mã của mình mạnh mẽ trên nhiều cấu hình Linux, tôi khuyên bạn nên xem xét các trường hợp góc có thể sử dụng SELinux hoặc ACL của hệ thống tệp hoặc các tính năng "khả năng" trong nhân Linux kể từ phiên bản 2.2 trở lên. Quy trình của bạn có thể chạy dưới một số trình bao bọc đã sử dụng SELinux hoặc một số thư viện khả năng của Linux, chẳng hạn như libcap2libcap-ng hoặc fscaps hoặc elfcap thông qua một điều gì đó kỳ lạ hơn như hệ thống systrace tuyệt vời và đáng buồn của Niels Provos.

Tất cả những cách bạn mã có thể chạy không phải là gốc và quá trình của bạn có thể đã được ủy quyền truy cập cần thiết để thực hiện công việc của mình mà không có EUID == 0. Vì vậy, tôi khuyên bạn nên cân nhắc viết mã của mình nhiều hơn bằng Python, bằng cách gói các hoạt động có thể bị lỗi do quyền hoặc các vấn đề khác với mã xử lý ngoại lệ. Nếu bạn kích hoạt các hoạt động khác nhau (ví dụ: sử dụng mô-đun subprocess), bạn có thể cung cấp tiền tố cho tất cả các cuộc gọi như vậy với sudo (ví dụ như dòng lệnh, môi trường hoặc tùy chọn tệp .rc). Nếu nó đang chạy tương tác, bạn có thể cung cấp để thực hiện lại bất kỳ lệnh nào nâng cao các ngoại lệ liên quan đến quyền sử dụng sudo (chỉ có tùy chọn nếu bạn tìm thấy sudo trên os.environ ['PATH']).

Nhìn chung, hầu hết các hệ thống Linux và UNIX vẫn có hầu hết quyền quản trị của họ được thực hiện bởi người dùng đặc quyền 'gốc'. Tuy nhiên, đó là trường học cũ và chúng tôi, là lập trình viên, nên cố gắng hỗ trợ các mô hình mới hơn. Việc thử các thao tác của bạn và cho phép xử lý ngoại lệ thực hiện công việc của nó cho phép mã của bạn hoạt động dưới bất kỳ hệ thống nào cho phép các thao tác bạn cần một cách minh bạch và nhận thức và sẵn sàng sử dụng sudo là một liên lạc tốt đẹp. công cụ phổ biến cho việc ủy ​​quyền kiểm soát các đặc quyền hệ thống).

3

trả lời cho phần thứ hai của câu hỏi

(xin lỗi hộp bình luận là quá nhỏ)

Paul Hoffman, bạn đang đúng, tôi chỉ đề cập đến một phần của câu hỏi của bạn đối phó với intrinsics, nhưng nó sẽ không phải là một ngôn ngữ kịch bản xứng đáng nếu nó không thể xử lý apt-get. Thư viện ưa thích là một chút dài dòng nhưng nó không được công việc:

>>> apt_get = ['/usr/bin/apt-get', 'install', 'python'] 
>>> p = subprocess.Popen(apt_get, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 
>>> p.wait() 
100     # Houston, we have a problem. 
>>> p.stderr.read() 
'E: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied)' 
'E: Unable to lock the administration directory (/var/lib/dpkg/), are you root?\n' 

Nhưng Popen là một công cụ tổng quát và có thể được bao bọc cho tiện theo dõi:

$ cat apt.py 
import errno 
import subprocess 

def get_install(package): 
    cmd = '/usr/bin/apt-get install'.split() 
    cmd.append(package) 
    output_kw = {'stdout': subprocess.PIPE, 'stderr': subprocess.PIPE} 
    p = subprocess.Popen(cmd, **output_kw) 
    status = p.wait() 
    error = p.stderr.read().lower() 
    if status and 'permission denied' in error: 
     raise OSError(errno.EACCES, 'Permission denied running apt-get') 
    # other conditions here as you require 
$ python 
>>> import apt 
>>> apt.get_install('python') 
Traceback ... 
OSError: [Errno 13] Permission denied running apt-get 

Và bây giờ chúng tôi đang trở lại để xử lý ngoại lệ. Tôi sẽ từ chối bình luận về tính tổng quát quá mức của Java của mô-đun subprocess.

+1

Nó sẽ không có ý nghĩa hơn để chỉnh sửa điều này vào câu trả lời khác của bạn? – jpmc26

1

Tôi muốn kiểm tra cho sudo trong các biến môi trường:

 
if not 'SUDO_UID' in os.environ.keys(): 
    print "this program requires super user priv." 
    sys.exit(1) 
5

Bạn có thể nhắc nhở người dùng để truy cập sudo:

import os, subprocess 

def prompt_sudo(): 
    ret = 0 
    if os.geteuid() != 0: 
     msg = "[sudo] password for %u:" 
     ret = subprocess.check_call("sudo -v -p '%s'" % msg, shell=True) 
    return ret 

if prompt_sudo() != 0: 
    # the user wasn't authenticated as a sudoer, exit? 

Việc chuyển đổi sudo -v cập nhật thông tin lưu trữ của người dùng (xem man sudo) .

2

Ứng dụng của tôi làm việc với mã này:

import os 
user = os.getenv("SUDO_USER") 
if user is None: 
    print "This program need 'sudo'" 
    exit() 
+0

Mã lỗi. Lỗi được đưa ra ở đây là 'TypeError', vì nếu bạn không chạy root,' os.getenv ("SUDO_USER") 'return' None' là kiểu 'NoneType', và bạn không thể nối' str' và ' NoneType'. Nếu bạn muốn sử dụng 'os.getenv (" SUDO_USER ")' để giải quyết vấn đề, bạn nên làm như sau: 'nếu os.getenv (" SUDO_USER ") == Không: in" Chương trình này cần ' sudo '"; exit() ' –

+2

Tôi đã chỉnh sửa câu trả lời của mình – Guillaume

+1

Cảm ơn bạn đã cập nhật, Mã này sẽ chỉ kiểm tra sudo chứ không phải quyền truy cập gốc. Bởi vì nó không kiểm tra root, nếu bạn đăng nhập như root (hoặc bạn đã viết cronjob như tôi), bạn vẫn cần gọi script với sudo, vì sudo không hỏi password trên root, tôi có thể sửa cronjob của mình chỉ cần gọi kịch bản python của tôi với sudo. – Cediddi