ZetCode

Python os.setreuid 函数

上次修改时间:2025 年 4 月 11 日

本综合指南探讨了 Python 的 os.setreuid 函数,该函数设置实际用户 ID 和有效用户 ID。我们将介绍 Unix 用户权限、安全隐患和实际权限管理示例。

基本定义

os.setreuid 函数设置当前进程的实际用户 ID 和有效用户 ID。 它在类 Unix 系统上可用,用于权限管理。

关键参数:ruid(实际用户 ID),euid(有效用户 ID)。 都可以是 -1 以保持不变。 需要适当的权限才能更改 ID。

setreuid 的基本用法

此示例演示了 os.setreuid 在用户 ID 之间切换的基本用法。 请注意,这需要适当的权限。

basic_setreuid.py
import os

def show_ids():
    print(f"Real UID: {os.getuid()}, Effective UID: {os.geteuid()}")

print("Original IDs:")
show_ids()

# Change both real and effective UID to 1000
try:
    os.setreuid(1000, 1000)
    print("\nAfter setreuid(1000, 1000):")
    show_ids()
except PermissionError as e:
    print(f"\nFailed to change UIDs: {e}")

# Restore original IDs (requires root)
try:
    os.setreuid(0, 0)
    print("\nAfter restoring root:")
    show_ids()
except PermissionError as e:
    print(f"\nFailed to restore root: {e}")

此脚本尝试将实际 UID 和有效 UID 都更改为 1000(典型的普通用户)。 然后,它尝试恢复 root 权限(UID 0)。

请注意,更改 UID 通常需要 root 权限或适当的功能,例如 CAP_SETUID。

仅更改实际 UID

您可以通过为有效 UID 参数传递 -1 来仅更改实际 UID。 这会影响进程记帐,但不影响权限。

real_uid_only.py
import os

def show_ids():
    print(f"Real UID: {os.getuid()}, Effective UID: {os.geteuid()}")

print("Original IDs:")
show_ids()

# Change only real UID to 1000
try:
    os.setreuid(1000, -1)
    print("\nAfter setreuid(1000, -1):")
    show_ids()
except PermissionError as e:
    print(f"\nFailed to change real UID: {e}")

# Verify file access behavior
file_path = "/etc/shadow"
print(f"\nAccess to {file_path}:")
print(f"os.access: {os.access(file_path, os.R_OK)}")
try:
    with open(file_path) as f:
        print("File opened successfully")
except IOError as e:
    print(f"Open failed: {e}")

这仅更改实际 UID,同时保持有效 UID 不变。 文件访问检查使用有效 UID,因此权限保持不变。

该示例显示了 os.access 和实际文件操作如何依赖于有效 UID,而不是实际 UID。

仅更改有效 UID

您可以通过为实际 UID 参数传递 -1 来仅更改有效 UID。 这会影响权限,但不影响进程记帐。

effective_uid_only.py
import os

def show_ids():
    print(f"Real UID: {os.getuid()}, Effective UID: {os.geteuid()}")

print("Original IDs:")
show_ids()

# Change only effective UID to 1000
try:
    os.setreuid(-1, 1000)
    print("\nAfter setreuid(-1, 1000):")
    show_ids()
except PermissionError as e:
    print(f"\nFailed to change effective UID: {e}")

# Verify privilege change
print("\nTrying privileged operation:")
try:
    os.mkdir("/system_dir")
    print("Created /system_dir successfully")
except PermissionError as e:
    print(f"Failed to create directory: {e}")

这仅更改有效 UID,同时保持实际 UID 不变。 对于需要原始有效 UID 的操作,进程将失去权限。

该示例演示了更改有效 UID 如何影响执行特权操作(例如创建系统目录)的权限。

临时权限降级

一种常见的安全模式是临时降低权限,然后在以后恢复它们。 此示例演示了如何使用 setreuid 实现此目的。

temp_privilege_drop.py
import os

def show_ids():
    print(f"Real UID: {os.getuid()}, Effective UID: {os.geteuid()}")

def drop_privileges():
    """Drop privileges to regular user"""
    if os.geteuid() == 0:  # root
        regular_uid = 1000  # typical regular user
        os.setreuid(-1, regular_uid)
        print("Dropped privileges to regular user")

def restore_privileges():
    """Restore original privileges"""
    if os.geteuid() != 0 and os.getuid() == 0:
        os.setreuid(-1, 0)
        print("Restored root privileges")

print("Starting as root:")
show_ids()

# Perform privileged operation
print("\nCreating system file:")
try:
    with open("/etc/test_file", "w") as f:
        f.write("test")
    print("Created file successfully")
except PermissionError as e:
    print(f"Failed: {e}")

# Drop privileges
drop_privileges()
show_ids()

# Try privileged operation
print("\nAttempting privileged operation:")
try:
    with open("/etc/test_file", "a") as f:
        f.write("more data")
    print("Modified file successfully")
except PermissionError as e:
    print(f"Failed: {e}")

# Restore privileges
restore_privileges()
show_ids()

这演示了一种安全模式,即在大多数操作中都降低权限,仅在必要时才提升权限。 可以恢复原始权限。

请注意,完整的权限分离还需要管理保存的设置 UID,而这仅由 setreuid 不能覆盖。

在多个用户之间切换

此示例演示了如何使用 setreuid 在多个用户上下文之间切换,这在权限分离的应用程序中可能很有用。

user_switching.py
import os

def show_ids():
    print(f"Real UID: {os.getuid()}, Effective UID: {os.geteuid()}")

def run_as_user(ruid, euid):
    """Run code with specified user IDs"""
    print(f"\nSwitching to UIDs (real: {ruid}, effective: {euid})")
    try:
        os.setreuid(ruid, euid)
        show_ids()
        
        # Demonstrate user-specific behavior
        home_dir = os.path.expanduser("~")
        print(f"Home directory: {home_dir}")
        print(f"Can access /tmp: {os.access('/tmp', os.R_OK | os.W_OK)}")
        
    except PermissionError as e:
        print(f"Failed to switch users: {e}")
    finally:
        # Restore original IDs
        os.setreuid(0, 0)

print("Starting as root:")
show_ids()

# Switch to different user contexts
run_as_user(1000, 1000)  # Regular user
run_as_user(1001, 1001)  # Another user
run_as_user(0, 1000)     # Real root, effective user
run_as_user(1000, 0)     # Real user, effective root

这演示了在不同的用户上下文之间切换以执行具有不同权限级别的操作。 每个上下文都有不同的权限和环境。

该示例显示了实际 UID 和有效 UID 如何影响各种系统交互,例如主目录解析和文件访问。

setreuid 的错误处理

此示例重点关注使用 setreuid 时的正确错误处理,包括权限检查和回退行为。

error_handling.py
import os
import sys

def show_ids():
    print(f"Real UID: {os.getuid()}, Effective UID: {os.geteuid()}")

def safely_drop_privileges(target_uid):
    """Attempt to drop privileges with proper error handling"""
    original_euid = os.geteuid()
    
    try:
        # First try to set both IDs
        os.setreuid(target_uid, target_uid)
        print(f"Successfully set both UIDs to {target_uid}")
        return True
        
    except PermissionError:
        print(f"Couldn't set both UIDs to {target_uid}, trying effective only")
        try:
            os.setreuid(-1, target_uid)
            print(f"Set effective UID to {target_uid}")
            return True
        except PermissionError:
            print(f"Failed to set any UIDs to {target_uid}")
            return False

print("Current IDs:")
show_ids()

# Try to drop privileges
if not safely_drop_privileges(1000):
    print("Falling back to restricted mode")
    try:
        # Try to at least give up root privileges
        if os.geteuid() == 0:
            os.setreuid(-1, os.getuid())
            print("Dropped effective root privileges")
    except PermissionError:
        print("Couldn't modify any UIDs - running with full privileges")

print("\nFinal IDs:")
show_ids()

# Verify we can still work
try:
    with open("user_file.txt", "w") as f:
        f.write("test")
    print("\nSuccessfully created user file")
except IOError as e:
    print(f"\nFile operation failed: {e}")
    sys.exit(1)

这演示了更改 UID 时的强大错误处理,如果无法完全降低权限,则采用回退策略。 它在最大化安全性的同时保持功能。

该示例显示了如何在仅可能进行部分权限缩减的情况下优雅地处理,同时仍保持应用程序功能。

真实的权限分离

此示例显示了在具有多个权限级别的真实场景中使用 setreuid 的更完整的权限分离实现。

privilege_separation.py
import os
import sys

class PrivilegeManager:
    def __init__(self):
        self.original_ruid = os.getuid()
        self.original_euid = os.geteuid()
        
    def drop_privileges(self):
        """Drop to unprivileged user"""
        if self.original_euid != 0:
            return True  # Already unprivileged
            
        try:
            # Try to set both IDs to original real UID
            os.setreuid(self.original_ruid, self.original_ruid)
            return True
        except PermissionError:
            try:
                # Fall back to just dropping effective UID
                os.setreuid(-1, self.original_ruid)
                return True
            except PermissionError:
                return False
                
    def restore_privileges(self):
        """Restore original privileges"""
        try:
            os.setreuid(self.original_ruid, self.original_euid)
            return True
        except PermissionError:
            return False
            
    def run_unprivileged(self, func, *args, **kwargs):
        """Run function with dropped privileges"""
        if not self.drop_privileges():
            raise RuntimeError("Failed to drop privileges")
        try:
            return func(*args, **kwargs)
        finally:
            if not self.restore_privileges():
                print("Warning: Failed to fully restore privileges",
                      file=sys.stderr)

def show_system_status():
    """Function to run with reduced privileges"""
    print("\nRunning with reduced privileges:")
    print(f"User IDs: {os.getuid()}/{os.geteuid()}")
    print(f"Process ID: {os.getpid()}")
    print("System time:", os.times())
    
    try:
        with open("/etc/passwd") as f:
            print("First 3 users:")
            for i, line in enumerate(f):
                if i >= 3: break
                print(line.strip())
    except IOError as e:
        print(f"Couldn't read passwd file: {e}")

# Main execution
if __name__ == "__main__":
    pm = PrivilegeManager()
    
    print("Starting with original privileges:")
    print(f"User IDs: {os.getuid()}/{os.geteuid()}")
    
    # Perform privileged operation
    try:
        with open("/root/test.log", "w") as f:
            f.write("Privileged operation\n")
        print("Performed privileged file operation")
    except IOError as e:
        print(f"Privileged operation failed: {e}")
    
    # Run unprivileged code
    pm.run_unprivileged(show_system_status)
    
    # Verify privileges restored
    print("\nAfter privilege restoration:")
    print(f"User IDs: {os.getuid()}/{os.geteuid()}")

这演示了一个更完整的权限分离实现,其中 PrivilegeManager 类安全地处理降低和恢复权限的详细信息。

该示例显示了如何构造代码以使用降低的权限运行特定操作,同时保持在需要时执行特权操作的能力。

安全注意事项

最佳实践

资料来源

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。 我自 2007 年以来一直撰写编程文章。到目前为止,我已经撰写了超过 1,400 篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 Python 教程