You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在不提升整个Python脚本权限的情况下获取root权限以查询物理磁盘及其容量

如何在不提升整个Python脚本权限的情况下获取root权限以查询物理磁盘及其容量

嘿,我太懂你这个需求了——谁都不想把整个Python脚本都跑在root权限下,毕竟权限越大,万一出点小错风险就越高。刚好我之前也碰到过类似的问题,给你分享几个实用的解决思路,挑适合你的来用:

思路1:把权限敏感的代码拆成独立小脚本,用sudo按需调用

这是最常用也最安全的方法:把需要root权限的磁盘查询逻辑单独抽成一个小脚本,主脚本保持普通权限,只在需要查磁盘的时候,通过sudo调用这个小脚本。

主脚本(普通权限运行)

主脚本负责你的核心业务逻辑,只有查询磁盘的时候才触发提权:

import subprocess
import json

def fetch_disk_details():
    try:
        # 调用提权的磁盘查询助手脚本
        proc = subprocess.run(
            ["sudo", "python3", "/绝对路径/到/disk_query_helper.py"],
            capture_output=True,
            text=True,
            check=True
        )
        # 把助手脚本返回的JSON转成字典用
        return json.loads(proc.stdout)
    except subprocess.CalledProcessError as e:
        print(f"查询磁盘出错:{e.stderr}")
        return {}

# 你的正常业务逻辑
if __name__ == "__main__":
    disk_info = fetch_disk_details()
    for disk, capacity in disk_info.items():
        print(f"磁盘 {disk}:{capacity}")

磁盘查询助手脚本(仅通过sudo调用)

这个脚本只做一件事:用pyparted查物理磁盘,然后把结果转成JSON输出给主脚本:

import parted
import json

def query_physical_disks():
    disks = {}
    # 遍历所有块设备,过滤掉分区、loop设备、逻辑卷
    for device in parted.getAllDevices():
        is_physical_disk = (
            not device.path.startswith("/dev/loop") 
            and not device.path.startswith("/dev/mapper")
            and not device.path.startswith("/dev/dm-")
        )
        if is_physical_disk:
            # 把字节转成易读的GB格式
            capacity_gb = device.getSize() / (1024 ** 3)
            disks[device.path] = f"{capacity_gb:.2f} GB"
    return disks

if __name__ == "__main__":
    # 输出JSON格式,方便主脚本解析
    print(json.dumps(query_physical_disks()))

配置免密sudo(可选但实用)

如果不想每次调用都输密码,可以编辑sudoers文件(必须用visudo命令,不能直接编辑!),加一行:

你的用户名 ALL=(ALL) NOPASSWD: /usr/bin/python3 /绝对路径/到/disk_query_helper.py

加完后可以用sudo visudo -c检查语法,避免出错锁死sudo权限。

思路2:直接读系统sysfs文件,完全不需要root

其实Linux系统把很多硬件信息存在/sys/class/block/目录下,普通用户默认就有读权限!我们可以直接读这些文件,根本不需要root权限,这是最省心的方法:

import os

def get_disk_info_from_sysfs():
    disk_details = {}
    block_root = "/sys/class/block"
    
    for disk_name in os.listdir(block_root):
        disk_path = os.path.join(block_root, disk_name)
        # 判断是不是物理磁盘:有device目录,且不是loop/逻辑卷设备
        is_physical = (
            os.path.exists(os.path.join(disk_path, "device"))
            and not disk_name.startswith("loop")
            and not disk_name.startswith("dm-")
        )
        if not is_physical:
            continue
        
        # 读扇区数,每个扇区默认512字节
        size_file = os.path.join(disk_path, "size")
        with open(size_file, "r") as f:
            sector_count = int(f.read().strip())
        
        # 转成GB格式
        capacity_gb = (sector_count * 512) / (1024 ** 3)
        disk_details[f"/dev/{disk_name}"] = f"{capacity_gb:.2f} GB"
    
    return disk_details

# 直接普通权限运行就行
if __name__ == "__main__":
    disks = get_disk_info_from_sysfs()
    for disk, cap in disks.items():
        print(f"{disk}: {cap}")

这个方法的好处是完全不需要任何提权操作,而且性能还快,唯一要注意的是不同发行版的sysfs结构可能略有差异,但大部分桌面/服务器Linux(Ubuntu、CentOS、Debian这些)都通用。

思路3:用C wrapper设置setuid(谨慎使用)

如果必须用pyparted又不想用sudo,可以写一个简单的C程序当wrapper,编译后设置setuid root,这样普通用户运行这个wrapper就能以root权限执行Python查询脚本。不过这个方法有安全风险,一定要确保脚本路径是绝对路径且脚本本身不能被普通用户修改。

C wrapper代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
    // 切换到root权限
    if (setuid(0) != 0) {
        perror("切换root权限失败");
        return 1;
    }
    // 调用磁盘查询脚本,注意填绝对路径
    execl("/usr/bin/python3", "python3", "/绝对路径/到/disk_query_helper.py", NULL);
    perror("执行脚本失败");
    return 1;
}

编译和设置权限

# 编译成可执行文件
gcc disk_wrapper.c -o disk_wrapper
# 把所有权改成root
sudo chown root:root disk_wrapper
# 设置setuid位,让普通用户运行时继承root权限
sudo chmod u+s disk_wrapper

之后普通用户直接运行./disk_wrapper就能得到磁盘信息了,不过一定要把disk_query_helper.py的权限设成只有root能修改,避免被篡改。

最后给你几个小提醒

  1. 优先用思路2,完全不需要提权,安全又省心;
  2. 如果必须用pyparted,选思路1,权限隔离得最好;
  3. 思路3尽量少用,除非万不得已,毕竟setuid程序的安全风险更高;
  4. 不管用哪种方法,一定要过滤掉loop设备、逻辑卷这些非物理磁盘的设备,避免拿到错误数据。

这样应该就解决你的问题了,有啥细节不清楚的随时问哈!

火山引擎 最新活动