Skip to content

第一百六十三章:安全上下文

渡劫期

涉及内核源码:

林小源进入 SELinux 的城堡,来到一间档案室。档案室的墙壁上挂满了标签,每个标签上都写着一串字符。

"这是安全上下文,"context.c 说,他是一位档案管理员,戴着厚厚的眼镜。"每个进程和文件都有一个上下文,格式为 user:role:type:level。上下文决定了访问权限。"

林小源拿起一个标签,上面写着 "system_u:system_r:httpd_t:s0"。

"这是 httpd 进程的上下文,"档案管理员说,"user 是 system_u,role 是 system_r,type 是 httpd_t,level 是 s0。"

"这些字段分别代表什么?"

"user 是 SELinux 用户,role 是角色,type 是类型——这是最重要的字段。level 是安全级别,用于 MLS/MCS。"

"为什么 type 最重要?"林小源问。

档案管理员放下手中的册子,说:"因为策略规则基于 type 进行访问控制。进程的 type 叫 domain,文件的 type 叫 type。allow 规则定义 domain 对 type 的访问权限。"

"能举个例子吗?"

"allow httpd_t httpd_sys_content_t:file { read getattr open },"档案管理员说,"这条规则允许 httpd_t 类型的进程读取 httpd_sys_content_t 类型的文件。"

"那怎么查看进程和文件的上下文?"

"ps auxZ 查看进程上下文,ls -Z 查看文件上下文,"档案管理员说,"chcon 修改文件上下文,restorecon 恢复默认上下文。"

林小源盯着那串字符,忽然意识到:这不只是一个标签,它决定了你能打开哪些门。

"level 字段呢?"林小源问。

档案管理员走到档案室深处,指着一排标签说:"level 用于 MLS (Multi-Level Security) 和 MCS (Multi-Category Security)。s0 是基本级别,s0:c0.c1023 是范围。"

"MCS 用于容器隔离?"

"是的,"档案管理员说,"每个容器有不同的类别,MCS 确保容器之间无法互相访问。这比 namespaces 更细粒度。"

林小源望着那些标签,心中感慨。在这片内核的深处,每一个标签都是一份信任。


c
/*
 * 安全上下文的组成:
 *
 * user — SELinux 用户
 *   unconfined_u — 非限制用户
 *   system_u — 系统用户
 *   user_u — 普通用户
 *
 * role — 角色
 *   system_r — 系统角色
 *   object_r — 对象角色
 *   unconfined_r — 非限制角色
 *
 * type — 类型(最重要)
 *   httpd_t — httpd 进程类型
 *   httpd_sys_content_t — 网页文件类型
 *   etc_t — /etc 文件类型
 *
 * level — 安全级别 (MLS/MCS)
 *   s0 — 级别 0
 *   s0:c0.c1023 — 范围
 */

/* 解析安全上下文 */
struct context_parts {
    char user[32];
    char role[32];
    char type[64];
    char level[32];
};

int parse_context(const char *ctx_str, struct context_parts *parts) {
    char buf[256];
    strncpy(buf, ctx_str, sizeof(buf) - 1);
    buf[sizeof(buf) - 1] = '\0';

    char *token;
    char *rest = buf;

    /* user */
    token = strtok_r(rest, ":", &rest);
    if (!token) return -1;
    strncpy(parts->user, token, sizeof(parts->user) - 1);

    /* role */
    token = strtok_r(rest, ":", &rest);
    if (!token) return -1;
    strncpy(parts->role, token, sizeof(parts->role) - 1);

    /* type */
    token = strtok_r(rest, ":", &rest);
    if (!token) return -1;
    strncpy(parts->type, token, sizeof(parts->type) - 1);

    /* level */
    token = strtok_r(rest, ":", &rest);
    if (!token) return -1;
    strncpy(parts->level, token, sizeof(parts->level) - 1);

    return 0;
}

void print_context_parts(const char *label, struct context_parts *p) {
    printf("%s:\n", label);
    printf("  user:  %s\n", p->user);
    printf("  role:  %s\n", p->role);
    printf("  type:  %s\n", p->type);
    printf("  level: %s\n", p->level);
}

printf("=== 安全上下文 — 身份的标签 ===\n\n");

/* 解析不同的安全上下文 */
const char *contexts[] = {
    "system_u:system_r:httpd_t:s0",
    "system_u:object_r:httpd_sys_content_t:s0",
    "unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023",
    "system_u:object_r:etc_t:s0",
    "system_u:system_r:sshd_t:s0-s0:c0.c1023",
};

int n = sizeof(contexts) / sizeof(contexts[0]);

printf("--- 解析安全上下文 ---\n");
for (int i = 0; i < n; i++) {
    struct context_parts parts;
    if (parse_context(contexts[i], &parts) == 0) {
        print_context_parts(contexts[i], &parts);
        printf("\n");
    }
}

printf("--- type 的作用 ---\n");
printf("进程类型 (domain):\n");
printf("  httpd_t — httpd 进程\n");
printf("  sshd_t — sshd 进程\n");
printf("  unconfined_t — 非限制进程\n\n");
printf("文件类型:\n");
printf("  httpd_sys_content_t — 网页文件\n");
printf("  etc_t — /etc 文件\n");
printf("  shadow_t — /etc/shadow\n\n");

printf("--- 查看上下文 ---\n");
printf("进程:\n");
printf("  ps auxZ | grep httpd\n\n");
printf("文件:\n");
printf("  ls -Z /var/www/html/\n\n");
printf("修改上下文:\n");
printf("  chcon -t httpd_sys_content_t file\n");
printf("  restorecon -Rv /var/www/html/\n");

printf("\n--- level (MLS/MCS) ---\n");
printf("s0 — 级别 0\n");
printf("s0:c0.c1023 — 范围\n\n");
printf("MCS (Multi-Category Security):\n");
printf("  用于容器隔离\n");
printf("  每个容器有不同类别\n");

道藏笔记

内核启示

SELinux 里每个东西都挂着一串标签,格式是 user:role:type:level。看起来像地址,其实是钥匙——你挂着什么标签,就决定你能开哪些门。

四个字段里 type 最关键。进程的 type 叫 domain,文件的 type 就叫 type,所有 allow 规则都是围绕 type 写的。ps auxZ 看进程标签,ls -Z 看文件标签,chcon 改标签,restorecon 恢复默认。level 那一截是 MLS/MCS 用的,s0 是基础级别,容器隔离靠的就是 MCS 给每个容器打不同的类别。

安全上下文是标签——标签决定权限。


破关试炼

安全上下文之试

安全上下文 user:role:type:level 中,SELinux 策略判断访问时最关键的字段是哪一个?

答对后才能继续滑动和进入下一章。

以修仙之名,悟内核之道