被拒绝直接读取的主体,仍能创建 Pod 并接收一个选定的合成示例键。

Kubernetes Secret 需要明确的访问边界

Kubernetes Secret 是用于保存工作负载所需机密值的 API 对象,提供专门的类型、消费路径和访问控制目标。但它不会让所有其他参与者都无法读取内容。设计边界时,需要同时考虑谁能取回对象、谁能创建消费者、谁能查看消费者输出,以及谁能管理运行它们的节点。仅区分配置对象与秘密对象,还不足以完成权限设计。 编码与保护处于不同层次 在 JSON API 表示中,Secret 的 data 使用 base64;stringData 则便于在写入时提交字符串。Base64 无需密钥就能还原,并不是加密。这种表示也不能证明某个具体集群怎样保护底层存储。Kubernetes 文档说明,若未配置相应加密,秘密数据默认不在存储层加密。应核对实际 API 服务器和存储设置,不能看到编码文字就推断保密措施已经生效。 存储加密同样不会阻止获得授权的 API 读取者拿到数据。RBAC 中的 get、list 和 watch 权限都可能暴露 Secret 内容,广泛的列表权限并不只是允许查看对象名称。备份和诊断输出也需要保护。正常应用最终仍要获得可用值,因此,一旦有权消费数据的程序被控制,攻击者就已经进入这份数据的信任边界,不能靠编码再次隔离它。 Secret 适合小规模值,单对象有一 MiB 限制。应只挂载需要的键,并只交付给真正需要它的容器。文件模式可以缩小工作负载内部的读取范围,却不能替代 API 授权或对创建工作负载权限的控制。环境变量注入也有暴露范围和生命周期方面的取舍,应根据应用需求选择,不能默认它一定比文件更安全。 坏掉的样子 禁止直接 get Secret,不一定禁止间接取得内容。主体如果可以在命名空间创建 Pod,通常就能在自己提交的规范中指定一个 Secret 卷,由 kubelet 把数据交给该工作负载。创建相应控制器的权限也可能产生类似效果。因此,除了直接读取数据的规则,还必须一起审查创建工作负载的权限及适用的准入限制,否则边界会漏掉实际消费路径。 ...

八月 21, 2027 · 3 分钟 · XB Field Notes
一个常驻帮手,用只装在内存里的已解锁密钥来应答签名请求。

ssh-agent 在会话期间替你记住已解密的密钥

ssh-agent 是一个持有私钥、并以公钥认证方式替你应答签名请求的程序。ssh 通过 SSH_AUTH_SOCK 找到它,那是一个 Unix 域套接字的路径;于是密钥只解锁一次,之后每一次连接都借用这份身份。私钥本身从不交给客户端。 这既是一项真实的安全属性,也是一项真实的负债。已解密的密钥不会落到磁盘上,但 agent 是一座活着的签名服务:任何能够到那个套接字的东西都能签名,只要身份还装着就一只能签。 socket 就是那份能力 agent 协议里没有任何一个动词用于导出密钥。请求身份列表(消息 11)返回消息 12,内容是公钥 blob 与注释——本文这次实测里是 51 字节,从来不是私钥材料。请它对一段任意挑战签名(消息 13)返回消息 14,一枚 88 字节的 Ed25519 签名。未定义的 opcode(99)返回消息 5,也就是 SSH_AGENT_FAILURE。把私钥文件改名挪走之后,下一次签名依然成功,这正是要点:材料活在 agent 的内存里。 这份内存靠套接字上的文件权限来保护,除此之外没有更强的屏障。该套接字是 Unix 域套接字,模式 0600,属主是启动 agent 的那个 uid。另一个只拿到 SSH_AUTH_SOCK 这一个环境变量、既没有密钥文件也没有口令的进程,照样列出了身份并拿到了一枚签名。 只解锁一次,然后给期限 ssh-add 是唯一需要口令的一步。它读取密钥文件(该文件应当是 0600;ssh-add 会忽略任何他人可读的身份文件),把解密后的身份交给 agent。此后就由 agent 替你应答。ssh-add -t 8h 设定最长存活时间,单位可以是秒,也可以是 sshd_config(5) 里的时间格式;本文这次实测中,ssh-add -t 2 加完之后身份仍在列表里,三秒后再查已经一个都没有了。 ...

二月 27, 2027 · 1 分钟 · XB Field Notes
验证方在密码截止时间之后拒绝新的密码登录。

凭据到期必须由验证方执行

只有负责决定是否允许访问的系统检查了适用截止时间,凭据才真的会到期。在资产清单里写一个日期,不会自动改变数据库或接口接受什么。较短有效期可以压缩被盗凭据继续建立新访问的时间窗口,但具体效果取决于认证机制,以及此前已经建立的会话、签发的其他凭据如何处理。秘密不是因为自身附带一行日期,就能独立让所有访问失效。 坏掉的样子 到期不会撤销已经发生的行为。复制出去的数据仍在外面,截止时间之前建立的会话也可能继续工作。攻击者还可能在原本有效的权限下建立另一条访问路径。因此,有效期只是限制后续使用的一种手段,还要结合权限范围、撤销机制、会话管理和对实际影响的调查。它并不能为一次泄漏造成的全部损失设定通用上限,也不能把过去的访问自动变成没有发生过。 PostgreSQL 的 VALID UNTIL 控制角色密码的有效时间,不控制角色是否存在,也不覆盖所有认证方式。使用非密码方式建立连接,就不会获得同样的截止检查。如果通过本地信任规则来测试密码到期,便可能误以为到期功能失效。判断结果之前,应先确认客户端实际命中了哪条认证路径,不能只看两次连接用了同一个用户名。 轮换与续期是两个不同变化。替换密码不一定改变到期日期,延长日期也不等于替换值。更新流程如果只修改其中一项,可能导致应用依然无法重新连接,也可能让原值继续有效。一个连接池里的旧连接查询成功,不足以证明新凭据能够完成新的认证,因为它可能根本没有重新交出密码接受检查。 快速验证 准备隔离的 Linux 实验环境,让 PostgreSQL 18 程序位于 PATH 中,以非 root 用户运行,并确认回环端口 55477 未被使用。将下列内容保存为脚本,用 sh 执行。它初始化一次性数据库集群,只使用明确虚构的密码,TCP 仅监听回环地址,退出时删除临时目录。本地信任认证只为展示另一条认证路径而启用,不应照搬到真实服务: #!/bin/sh set -eu lab_root=$(mktemp -d /tmp/credential-expiry.XXXXXX) cleanup() { if test -f "$lab_root/data/postmaster.pid"; then pg_ctl -D "$lab_root/data" -m immediate -w stop >/dev/null fi rm -rf "$lab_root" } trap cleanup EXIT initdb -D "$lab_root/data" --locale=C --auth-local=trust --auth-host=scram-sha-256 >/dev/null pg_ctl -D "$lab_root/data" -l "$lab_root/server.log" \ -o "-k $lab_root -p 55477 -c listen_addresses=127.0.0.1" -w start >/dev/null admin() { psql -X -qAt -v ON_ERROR_STOP=1 -h "$lab_root" -p 55477 -d postgres "$@" } # Both passwords below are invented, disposable lab fixtures. admin <<'SQL' SET password_encryption='scram-sha-256'; CREATE ROLE article_expiry LOGIN PASSWORD 'LAB-ONLY-OLD-PASSWORD'; SQL mkfifo "$lab_root/input" PGPASSWORD=LAB-ONLY-OLD-PASSWORD psql -X -qAt -v ON_ERROR_STOP=1 \ -h 127.0.0.1 -p 55477 -U article_expiry -d postgres \ <"$lab_root/input" >"$lab_root/held.out" 2>"$lab_root/held.err" & held_pid=$! exec 3>"$lab_root/input" printf "SELECT 'ready';\n" >&3 attempt=0 until grep -qx ready "$lab_root/held.out"; do attempt=$((attempt + 1)) test "$attempt" -lt 100 sleep 0.05 done admin <<'SQL' SELECT format('ALTER ROLE article_expiry VALID UNTIL %L', clock_timestamp()+interval '1 second') \gexec SELECT pg_sleep(1.2); SELECT 'deadline passed='||(rolvaliduntil<clock_timestamp()) FROM pg_authid WHERE rolname='article_expiry'; SQL if PGPASSWORD=LAB-ONLY-OLD-PASSWORD psql -X -qAt -h 127.0.0.1 -p 55477 \ -U article_expiry -d postgres -c 'SELECT 1' >"$lab_root/new.out" 2>"$lab_root/new.err"; then exit 1 fi grep -q 'password authentication failed' "$lab_root/new.err" printf '%s\n' 'fresh SCRAM login rejected after expiry' printf "SELECT 'held after expiry='||current_user;\n" >&3 psql -X -qAt -v ON_ERROR_STOP=1 -h "$lab_root" -p 55477 \ -U article_expiry -d postgres -c "SELECT 'local trust login='||current_user" admin <<'SQL' SET password_encryption='scram-sha-256'; ALTER ROLE article_expiry PASSWORD 'LAB-ONLY-NEW-PASSWORD'; SQL if PGPASSWORD=LAB-ONLY-NEW-PASSWORD psql -X -qAt -h 127.0.0.1 -p 55477 \ -U article_expiry -d postgres -c 'SELECT 1' >"$lab_root/rotated.out" 2>"$lab_root/rotated.err"; then exit 1 fi grep -q 'password authentication failed' "$lab_root/rotated.err" printf '%s\n' 'rotating password alone preserves expired deadline' admin <<'SQL' SELECT format('ALTER ROLE article_expiry VALID UNTIL %L', clock_timestamp()+interval '1 hour') \gexec SQL if PGPASSWORD=LAB-ONLY-OLD-PASSWORD psql -X -qAt -h 127.0.0.1 -p 55477 \ -U article_expiry -d postgres -c 'SELECT 1' >"$lab_root/old.out" 2>"$lab_root/old.err"; then exit 1 fi grep -q 'password authentication failed' "$lab_root/old.err" printf '%s\n' 'old password rejected after rotation' PGPASSWORD=LAB-ONLY-NEW-PASSWORD psql -X -qAt -v ON_ERROR_STOP=1 \ -h 127.0.0.1 -p 55477 -U article_expiry -d postgres \ -c "SELECT 'new password login='||current_user" printf "SELECT 'held after rotation='||current_user;\n" >&3 printf '\\q\n' >&3 exec 3>&- wait "$held_pid" grep -qx 'held after expiry=article_expiry' "$lab_root/held.out" grep -qx 'held after rotation=article_expiry' "$lab_root/held.out" cat "$lab_root/held.out" 记录中的 PostgreSQL 18.6 实测输出确认截止时间已经过去。脚本把期限设为数据库当前时间的一秒之后,然后等待数据库时钟越过它,所以这里实际经历了到期,而不是只拿一个编造日期做字符串比较。随后,通过 SCRAM 密码认证建立的新连接被拒绝,验证了真正决定认证结果的服务端规则。 ...

九月 27, 2026 · 2 分钟 · XB Field Notes