SSD Nodes Learn Hosting plans →
指南 Matt Connor作者: Matt Connor · 更新于 2026-09-11

VPS 单节点 k3s 集群安全加固指南

了解新装 k3s 在 VPS 上默认暴露的 5 个位置:6443、10250、kubeconfig 权限、绕过防火墙的 NodePort,以及可请求主机权限的特权 Pod,并逐项修复。

单节点 k3s 集群初始暴露的内容

在公共 VPS 上,单节点 k3s 集群在单行安装程序完成后的第 1 天会在 5 个具体位置暴露:TCP 6443 上的 Kubernetes API 服务器、TCP 10250 上的 kubelet、存放在磁盘上的 kubeconfig 文件、防火墙无法识别的 NodePort 范围,以及任何被允许请求 privileged 或 hostPath 的 Pod。每一项都可在几分钟内修复。本指南假设 k3s 已在运行。如果尚未运行,请先阅读在 VPS 上安装单节点 k3s,完成后再返回。

在修改任何配置前,先查看当前正在监听的端口。

sudo ss -tulpn | grep -E '6443|10250|10256|8472'

默认安装会显示 6443(API 服务器)、10250(kubelet)、10256(kube-proxy 健康检查)以及 8472/udp(Flannel 覆盖网络,它使用 VXLAN,即虚拟可扩展局域网)。k3s 默认绑定到 0.0.0.0,因此这些端口都会位于公共地址上,而不仅仅监听 loopback。

6443 端口为何关系到整个集群

任何能以管理员权限通过 6443 端口完成身份验证的用户,都可以创建 Pod,而 Pod 可以在主机上以 root 身份运行。6443 端口就是进入这台机器的入口。

开放 6443 并不意味着服务器会立即被入侵,因为 Kubernetes 不接受密码。它要求客户端证书或 bearer token。但以下两点仍然成立。

首先,API server 会在完全没有凭据的情况下响应某些请求。默认的 Kubernetes RBAC(基于角色的访问控制)会将组 system:unauthenticated 绑定到名为 system:public-info-viewer 的角色,该角色允许执行 /version、/healthz、/livez 和 /readyz。从另一台机器运行:

curl -sk https://YOUR_SERVER_IP:6443/version

该命令会返回 Kubernetes 的确切版本。这个版本可用于搜索 CVE(常见漏洞和暴露),也是扫描器判断你的服务器值得关注的依据。超出这些路径的请求都会被拒绝,并且拒绝信息会暴露你的身份:

forbidden: User "system:anonymous" cannot get path "/api"

其次,只要该端口保持开放,API server 中的每个漏洞都可被远程访问。此时,打补丁不再是可选项。

最简单的修复方法是添加防火墙规则。这里 ufw 需要多条规则,因为 k3s 会通过同一个内核路由集群流量。

sudo ufw default deny incoming
sudo ufw allow OpenSSH
sudo ufw allow from 203.0.113.10 to any port 6443 proto tcp
sudo ufw allow from 10.42.0.0/16 to any
sudo ufw allow from 10.43.0.0/16 to any
sudo ufw enable

最后两条规则直接来自 k3s 文档。10.42.0.0/16 是默认的 Pod 网络,10.43.0.0/16 是默认的 Service 网络。如果缺少这些规则,ufw 会丢弃集群内部流量,导致 Pod 无法访问 API server 和彼此通信。k3s 示例允许来自所有地址的 6443 流量;将其替换为你自己的地址,是最值得进行的修改。如果你不熟悉 ufw,VPS 的 ufw 防火墙基础介绍了这些规则所依赖的默认策略。

k3s 文档明确说明了覆盖网络端口的风险:“节点上的 VXLAN 端口不应暴露给公网,否则任何人都可以访问你的集群网络。”对入站流量设置默认拒绝策略,无需单独指定该端口即可阻止访问。

更稳妥的修复方法是完全停止通过公网地址访问 API,改用 VPN 或网状网络地址。服务器证书必须包含你连接时使用的地址,因此应将该地址作为 SAN(主题备用名称)添加到 /etc/rancher/k3s/config.yaml 中:

tls-san:
  - 10.8.0.1
  - k3s.example.com
secrets-encryption: true
sudo systemctl restart k3s
sudo k3s secrets-encrypt status

secrets-encryption: true 会加密数据存储中的 Secret 对象。k3s 文档指出:“现有服务器必须重启后才能启用 Secret 加密。”在修改前写入的 Secret 会保留旧格式,直到你运行 sudo k3s secrets-encrypt reencrypt。需要明确这项设置的作用。它可以保护从备份中复制出来的数据存储文件。但它无法防御能够访问 API server 的人员,因为 API server 会为获准读取 Secret 的用户解密 Secret。任何自托管 Secret 存储都存在同样的区别,这也是为什么 Vaultwarden 加固检查重点关注管理员令牌和备份文件,而不是加密本身。

为什么必须关注 10250 端口上的 kubelet

kubelet 是负责启动容器的代理。它在 10250 端口上提供的 API 可以列出 Pod,并在其中执行命令。接受匿名请求的 kubelet,等于为机器上的每个工作负载提供远程 Shell。

检查当前配置:

curl -sk https://127.0.0.1:10250/pods | head -c 60

当前的 k3s 会返回 Unauthorized,因为 kubelet 要求 API server 对每个调用方进行身份验证和授权。如果返回 JSON 格式的 Pod 列表,说明已启用匿名访问。任何能够访问 10250 端口的人员都可以读取容器中的数据并执行命令。

无论结果如何,都应阻止外部访问。在单节点环境中,kubelet 的唯一客户端是同一台机器上的 control plane,其流量通过 loopback 接口进入,而 ufw 默认接受该接口的流量。拒绝来自互联网的 10250 端口访问不会造成任何影响。如果您是因为故障的 kubectl top 或无法稳定运行的 metrics-server 来到这里,相关原因汇总在 kubelet 10250 端口错误。

您的 kubeconfig 是集群管理员凭据

k3s 会写入 /etc/rancher/k3s/k3s.yaml,该文件归 root 所有,权限模式为 600。文档说明了修改权限模式的后果:“kubeconfig 文件归 root 所有,默认权限模式为 600。将权限模式修改为 644 后,主机上的其他非特权用户也可以读取该文件。”

应将其理解为:权限模式 644 会使每个本地账户都成为集群管理员。许多教程确实建议这样做,通常将其设置为 --write-kubeconfig-mode 644,以便 kubectl 无需 sudo 即可运行。其原理是将管理员凭据交给任何拥有 shell 访问权限的用户。

改为仅将该文件复制给一个用户。

mkdir -p ~/.kube
sudo install -o "$USER" -g "$USER" -m 600 /etc/rancher/k3s/k3s.yaml ~/.kube/config
kubectl get nodes

然后确认原文件的权限仍然严格:

stat -c '%a %U:%G' /etc/rancher/k3s/k3s.yaml

600 root:root 才是所需的结果。该文件包含一个属于 system:masters 成员的客户端证书;API 服务器会无条件允许该组,因此不会对该证书查询 RBAC 规则。Kubernetes 没有证书吊销列表,这意味着泄露的副本会一直有效,直到轮换集群证书颁发机构。应将其视为 SSH 私钥,并严格限制可以访问它的账户数量。这与 VPS 上的最小权限用户账户 的原则相同。

防火墙无法检测 NodePort 流量

type: NodePort 服务会在节点持有的每个地址上开放 30000 到 32767 之间的端口,包括公网地址。type: LoadBalancer 服务在 k3s 上的行为更进一步:捆绑的负载均衡器 ServiceLB 会在 kube-system 中为每个服务调度一个小型 Pod,直接在主机上占用该服务端口。

kubectl -n kube-system get pods | grep svclb
sudo ss -tulpn | grep -E ':3[0-2][0-9]{3}'

现在说一个容易令人意外的情况。使用 ufw 阻止该端口后,它仍然可以响应。

sudo ufw deny 30080/tcp
curl http://YOUR_SERVER_IP:30080

页面仍然可以加载,因为数据包经过的路径不同。kube-proxy 会将 DNAT(目标网络地址转换)规则写入 nat 表的 PREROUTING 链,而 PREROUTING 在任何过滤决策之前运行。目标地址会变成 Pod 地址,而不是主机地址,因此内核会将数据包发送到 FORWARD 链,而不会经过 INPUT。ufw 的规则位于 INPUT 中。数据包根本不会经过这些规则。跳转顺序如下:

sudo iptables -S PREROUTING -t nat | head
sudo iptables -S FORWARD | head

KUBE-SERVICES 位于 PREROUTING 的顶部,Kubernetes 在 FORWARD 中的跳转规则也位于 ufw 自身链的上方。这与 Docker 绕过 ufw 发布端口 使用的是同一机制,解决方法也相同。

  • 在云服务提供商的网络防火墙中进行过滤。它位于机器前方运行,不受内核路由方式影响。
  • 不使用 NodePort 和 LoadBalancer。将服务保留在 ClusterIP,并通过已有的 SSH 会话使用 kubectl port-forward 访问它们。
  • 只在 80 和 443 上暴露一个入口,不开放其他端口。
  • 在 kube-apiserver-arg 下使用 service-node-port-range 缩小端口范围,使意外创建的 NodePort 落在你正在监控的范围内。

仍然值得运行 ufw。它管理目标为主机本身的流量,包括 SSH 和 API server。它不会过滤 Pod 流量;如果误以为它可以过滤,最终可能导致数据库处于可访问状态。

在 VPS 上,使用 hostPath 或 privileged 的 Pod 等同于 root

容器只是运行在宿主机内核上的普通进程,但只能看到内核的受限视图。以下 Pod 字段会解除这些限制。

  • securityContext.privileged: true 为容器提供所有 Linux capability,并允许访问宿主机设备。
  • hostPath 将宿主机目录挂载到 Pod 中。以读写方式挂载 / 的 Pod 可以向 /root/.ssh/authorized_keys 追加密钥。
  • hostPID: true 让容器加入宿主机的进程命名空间。在其中对 PID 1 执行 nsenter,即可打开宿主机 shell。
  • hostNetwork: true 让容器使用宿主机网络栈,因此可以绑定宿主机端口,并访问绑定到 loopback 的服务。

因此,“谁可以在这里创建 Pod”和“谁是这台 VPS 上的 root”是同一个问题。只要某个 ServiceAccount 在任意命名空间的 pods 上拥有 create,它就等同于 root,除非 Pod 会先被其他机制拒绝。

这个机制就是 Pod Security admission,它内置于 API server。快速配置方式是在每个命名空间添加标签,无需重启。

kubectl label namespace default \
  pod-security.kubernetes.io/enforce=baseline \
  pod-security.kubernetes.io/enforce-version=latest \
  pod-security.kubernetes.io/warn=restricted

baseline 会拒绝以上 4 个字段。restricted 的限制更严格:要求使用非 root 用户和 seccomp(secure computing mode)配置文件,禁止权限提升,并将 capabilities 丢弃到 ALL。这会导致许多公开发布的 chart 无法运行。在强制执行 baseline 的同时,仅对 restricted 发出警告,可以在正式启用前查看哪些配置会失效。

验证配置是否生效:

kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: pstest
spec:
  containers:
  - name: app
    image: busybox
    command: ["sleep", "60"]
    securityContext:
      privileged: true
EOF

API server 会拒绝该请求,并说明拒绝了哪个字段:

Error from server (Forbidden): error when creating "STDIN": pods "pstest" is forbidden: violates PodSecurity "baseline:latest": privileged (container "app" must not set securityContext.privileged=true)

如果不想为每个命名空间分别添加标签,而是设置整个集群的默认策略,k3s 文档说明可以在 /var/lib/rancher/k3s/server/psa.yaml 使用 admission 配置文件:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
  configuration:
    apiVersion: pod-security.admission.config.k8s.io/v1beta1
    kind: PodSecurityConfiguration
    defaults:
      enforce: "baseline"
      enforce-version: "latest"
      warn: "restricted"
      warn-version: "latest"
    exemptions:
      namespaces: [kube-system]

在 /etc/rancher/k3s/config.yaml 中让 API server 使用该文件,然后重启 k3s:

kube-apiserver-arg:
  - 'admission-control-config-file=/var/lib/rancher/k3s/server/psa.yaml'

kube-system 豁免项不是可选配置。k3s 自带的 ServiceLB Pod 会占用宿主机端口,而 baseline 会禁止这种行为。如果从列表中删除 kube-system,这些 Pod 下次被重新创建时就会遭到拒绝。修改 admission 配置后重启 k3s 时,请保持第二个 SSH 会话处于打开状态。

此外,k3s 自带网络策略控制器,并且默认启用。因此,NetworkPolicy 对象会在此集群中直接生效,无需额外安装组件。并非所有 Kubernetes 发行版都如此;它是阻止某个已被入侵的 Pod 访问其他资源的工具。

删除不使用的内置组件

安装程序会部署一组附加组件。每个组件都会增加一个监听器,也会增加一个需要打补丁的组件。--disable 接受以下值:coredns、servicelb、traefik、local-storage、metrics-server、runtimes。

保留 coredns。集群中没有它就无法解析名称。其余组件可按需选择。在 /etc/rancher/k3s/config.yaml 中:

disable:
  - traefik
  - servicelb
disable-helm-controller: true
sudo systemctl restart k3s
kubectl get pods -A

k3s 会删除已禁用的组件,因此 traefik Pod 和 svclb- Pod 会自动消失。先了解禁用它们的后果。删除 servicelb 后,所有 type: LoadBalancer 服务都会永久保持为 <pending>,因为没有任何组件为其分配地址。删除 traefik 后,集群中没有 Ingress 控制器,因此 Ingress 对象完全不起作用。只有在通过其他方式处理流量时才禁用它们,例如在主机上运行反向代理;如果正在使用这些组件,则保留它们。disable-helm-controller: true 会删除监视 HelmChart 资源的控制器;如果您自行运行 helm,就不会使用这个具有特权的组件。

停止自动挂载默认 ServiceAccount 令牌

除非另行指定,否则每个 Pod 都会在 /var/run/secrets/kubernetes.io/serviceaccount/token 获取一个 ServiceAccount 令牌。default ServiceAccount 不具备任何 RBAC 权限,因此令牌本身作用有限。对于已被入侵的容器内攻击者而言,它提供的是有效凭据和可访问的 API 服务器,而这通常是集群权限提升过程的第一步。

k3s 加固指南按命名空间关闭此功能:

kubectl patch serviceaccount --namespace default default --patch '{"automountServiceAccountToken": false}'
kubectl patch serviceaccount --namespace kube-node-lease default --patch '{"automountServiceAccountToken": false}'
kubectl patch serviceaccount --namespace kube-public default --patch '{"automountServiceAccountToken": false}'

确认设置已生效。先等待 Pod 启动几秒。

kubectl run t --image=busybox --restart=Never --command -- sleep 30
kubectl exec t -- ls /var/run/secrets/kubernetes.io/serviceaccount

该路径已不存在,因此 ls 会输出 No such file or directory。确实需要访问 API 的工作负载可在自己的 Pod 规范中设置 automountServiceAccountToken: true,因此不会永久阻断任何功能。单独来看,这只是一个小改进。更重要的是,不要为工作负载授予具有实际权限的 ServiceAccount。可以通过以下命令查看令牌当前具有什么权限:

kubectl auth can-i --list --as=system:serviceaccount:default:default

资源限制,避免单个 Pod 导致整个集群宕机

在单节点环境中,控制平面和工作负载共享同一个内核和同一块内存。发生内存泄漏的 Pod 不一定只会自行终止。内核的 OOM(内存不足)杀手会根据进程评分选择终止目标,并倾向于选择占用内存较大的进程。k3s 是一个长期运行且占用内存较大的进程,因此消失的可能不是 Pod,而是整个集群。此时不会有其他组件重新启动工作负载,因为负责重新启动工作负载的组件已经终止。

LimitRange 会为未设置资源限制的 Pod 填充默认限制:

apiVersion: v1
kind: LimitRange
metadata:
  name: defaults
  namespace: default
spec:
  limits:
  - type: Container
    default:
      cpu: 500m
      memory: 512Mi
    defaultRequest:
      cpu: 50m
      memory: 128Mi

ResourceQuota 会限制整个命名空间可以申请的资源总量:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: cap
  namespace: default
spec:
  hard:
    limits.cpu: "3"
    limits.memory: 3Gi
    pods: "20"

然后在 /etc/rancher/k3s/config.yaml 中为 k3s 本身预留资源:

kubelet-arg:
  - 'system-reserved=cpu=250m,memory=512Mi'

两者的差异会体现在两个方面。因超过自身限制而被终止的容器,会在 kubectl describe pod 的 Last State 下报告 Reason: OOMKilled,节点上的其他工作负载仍会继续运行。完全耗尽内存的节点会在 dmesg 中留下 Killed process 行,并且通常会连带终止其他工作负载。前者说明资源限制正在发挥作用。后者正是设置资源限制要避免的情况。

在 CI 中扫描清单,并按计划扫描 k3s 集群

扫描应放在两个位置,因为它们发现的问题不同。先安装 Trivy。该项目会在每个版本发布时提供 Debian 软件包。2026 年 8 月的当前版本是 0.74.0,因此在将版本固定到自动化流程前,请先检查发布页面是否有更新版本。

sudo apt-get install -y wget
wget -q https://github.com/aquasecurity/trivy/releases/download/v0.74.0/trivy_0.74.0_Linux-64bit.deb
sudo dpkg -i trivy_0.74.0_Linux-64bit.deb
trivy --version

第一个位置是清单进入集群之前。

trivy fs --scanners misconfig --severity HIGH,CRITICAL --exit-code 1 ./deploy

--exit-code 1会在发现问题时使 CI(持续集成)作业失败。每条结果都会指出失败的字段及其严重性。因此,原本不打算提交的 privileged: true 会使构建失败,而不会到达 API server。决定接受的问题应写入 .trivyignore 文件。这样,该决定会和导致问题的清单一起保存在 git 中。

第二个位置是运行中的集群,并按计划执行扫描。

trivy k8s --compliance=k8s-cis-1.23 --report summary

需要说明该命令的一个限制。trivy k8s 会部署一个需要访问主机的节点采集器 Pod,以检查节点级设置。如果您刚开始拒绝特权 Pod,这一点值得注意,不应通过绕过策略来处理。trivy k8s --report summary --disable-node-collector 会跳过采集器,同时失去节点级检查。

kube-bench 检查主机侧配置:文件权限和进程参数。大多数 CIS(互联网安全中心)基准检查实际上都集中在这里。它提供 k3s 配置文件。请使用上游 Job 清单并进行调整。

curl -sfL -o kube-bench-job.yaml https://raw.githubusercontent.com/aquasecurity/kube-bench/main/job.yaml

将容器命令改为 ["kube-bench", "--benchmark", "k3s-cis-1.7"],并将 /etc/kubernetes 和 /var/lib/etcd 挂载替换为 /etc/rancher 和 /var/lib/rancher,因为 k3s 将文件保存在这些位置。然后:

kubectl apply -f kube-bench-job.yaml
kubectl logs -l app=kube-bench --tail=-1

请注意该 Job 的组成:hostPID: true 加上主机目录挂载。这正是您在上一节开始拒绝的 Pod 结构。请在已豁免的 kube-system 命名空间中运行它,读取输出,然后 kubectl delete job kube-bench。扫描器必须使用特权并不意味着应停止禁止特权工作负载。

镜像内容是另一个问题。trivy image ghcr.io/example/app:1.4 会读取镜像中的软件包数据库,并列出已知存在漏洞的软件包。这与检查服务器中已知的 CVE是同一类工作。

下面说明如何正确理解扫描器输出。单节点个人集群会有很长一列 CIS 控制项失败,其中大多数失败既是正确的,也与您的环境无关。该基准面向多节点、多租户集群编写:etcd 位于独立主机上,审计日志发送到其他位置,使用独立的 kubelet 证书颁发机构,并启用适用于您并不受其约束的合规制度的准入插件。k3s 特意将控制平面作为单个进程运行,并使用一个配置文件。因此,检查 kube-scheduler 清单文件权限的控制项无法通过,因为该文件根本不存在。

请按以下顺序阅读失败项,并在收益不足时停止:检查 /etc/rancher 和 /var/lib/rancher 下的文件模式及所有权;检查提及匿名或未认证访问的项目;检查报告某个组件绑定到 0.0.0.0 的项目;以及检查没有正当理由却以 UID 0 运行的容器。其余问题可以等有第二个节点或第二位具备访问权限的人员后再处理。一份您不会处理的百行报告,不如一份您会采取行动的五行报告。

单节点 k3s 的加固顺序

  1. 将 ufw 的默认入站策略设置为 deny,允许 SSH,从您自己的地址允许访问 6443,并允许 Pod 网络和 Service 网络。
  2. 将 kubeconfig 复制到您的用户目录,并设置权限为 600;永远不要设置 --write-kubeconfig-mode 644。
  3. 确认 kubelet 在 10250 上拒绝匿名请求,并确保该端口不暴露到互联网。
  4. 禁用不使用的内置组件,然后重启 k3s。
  5. 使用 enforce=baseline 和 warn=restricted 为命名空间设置 Pod Security admission 标签。
  6. 关闭默认的 ServiceAccount token 自动挂载。
  7. 添加 LimitRange 和 ResourceQuota,并为 k3s 预留 CPU 和内存。
  8. 将 trivy fs --scanners misconfig 加入 CI,并每月运行一次 CIS 扫描。

底层主机仍需要像其他服务器一样进行维护,而 k3s 还增加了一个需要注意的问题。k3s 由脚本安装,而不是通过 apt 安装,因此 apt upgrade 永远不会处理它。按照独立的维护计划为操作系统安装补丁,并参考 Ubuntu 上的 unattended upgrades,然后通过重新运行安装程序,使用所需的 channel 或版本,有计划地升级 k3s。同一台机器上存在两条更新路径,很容易被遗忘,因此应记录每个组件分别遵循哪条路径。

FAQ

将 k3s API 服务器的 6443 端口暴露到互联网是否安全?

API 服务器本身并不是完全开放的入口,因为它要求客户端证书或令牌,并会使用 forbidden: User "system:anonymous" 拒绝其他请求。但仍有两个风险。匿名调用者仍可读取 /version,扫描器可据此准确查找对应的 Kubernetes 版本。只要端口保持开放,未来 API 服务器中的任何漏洞都可能被远程利用。对于单节点集群,除您自己的 kubectl 外,没有任何外部客户端需要访问 6443,因此应使用 sudo ufw allow from YOUR_IP to any port 6443 proto tcp 仅允许来自您的地址的访问,并让默认拒绝策略处理其他请求。

为什么我的 ufw 规则无法阻止 NodePort 服务?

因为数据包根本不会到达规则所在的链。kube-proxy 会在 nat 表的 PREROUTING 链中添加 DNAT 规则。该链会先运行,并将目标地址改写为 Pod 地址。随后数据包会被转发,而不是在本地交付,因此会经过 FORWARD,跳过 ufw 规则所在的 INPUT。使用 sudo iptables -S PREROUTING -t nat | head 确认这一点。在提供商的网络防火墙中过滤 NodePort,或者避免使用 type: NodePort,改用 kubectl port-forward 访问服务。

我应该使用 --write-kubeconfig-mode 644 运行 k3s 吗?

不应该。k3s 文档明确说明了它的作用:“将模式改为 644 后,主机上的其他非特权用户也可以读取该文件。”该文件包含用于 system:masters 的客户端证书,因此模式 644 会使每个本地账户都成为集群管理员。改用 sudo install -o "$USER" -g "$USER" -m 600 /etc/rancher/k3s/k3s.yaml ~/.kube/config 将它复制给单个用户,并将原文件保留为 600 root:root。

k3s 应使用哪个 kube-bench 基准?

使用 k3s-cis-1.7。kube-bench 文档说明:“kube-bench 包含适用于 Rancher K3S 平台的基准。运行 kube-bench 命令时,需要指定 --benchmark k3s-cis-1.7。”请显式传入该参数,因为自动检测会假定使用 kubeadm 目录结构,而 k3s 将文件保存在 /etc/rancher 和 /var/lib/rancher 下。单节点环境中有些检查失败并不适用,应首先处理文件权限和匿名访问问题。

Pod Security 准入会破坏 k3s 自带的组件吗?

如果在 kube-system 上强制执行,就会破坏这些组件。k3s 为 type: LoadBalancer 服务创建的 ServiceLB Pod 会在主机上占用端口,而 baseline 禁止使用主机端口,因此这些 Pod 在下次重新创建时会被拒绝。在准入配置文件中排除 kube-system,或者只通过标签在您管理的命名空间中应用 Pod Security。先使用 enforce=baseline 和 warn=restricted,以便在启用前查看 restricted 会导致哪些问题。