第一百六十三章:安全上下文
渡劫期涉及内核源码:
一
林小源进入 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 更细粒度。"
林小源望着那些标签,心中感慨。在这片内核的深处,每一个标签都是一份信任。
/*
* 安全上下文的组成:
*
* 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");#include <stdio.h>
#include <string.h>
/*
* 安全上下文的组成:
*
* 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);
}
int main() {
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");
return 0;
}道藏笔记
内核启示
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 策略判断访问时最关键的字段是哪一个?