<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>IPC · XB 现场笔记</title><link>https://www.xbcatm.com/zh/tags/ipc/</link><description>基于 HTTP、DNS、TLS、Caddy、SSH、发布与恢复的可复现运维现场笔记。</description><generator>Hugo</generator><language>zh-CN</language><atom:link href="https://www.xbcatm.com/zh/tags/ipc/index.xml" rel="self" type="application/rss+xml"/><item><title>Unix 套接字提供本地通信，名称与访问规则仍须明确</title><link>https://www.xbcatm.com/zh/posts/unix-sockets-skip-the-network-stack/</link><pubDate>Sat, 28 Nov 2026 00:00:00 +0000</pubDate><guid>https://www.xbcatm.com/zh/posts/unix-sockets-skip-the-network-stack/</guid><description>验证 Linux 路径权限与对端凭据，复现遗留套接字文件，并在确认归属后重新启动监听。</description><content:encoded>&lt;p&gt;Unix 域套接字提供本地进程间通信，无须 IP 地址、TCP 端口或外部网络路由。它仍使用内核套接字设施、缓冲区、调度和应用协议。路径套接字具有文件系统名称，但 Unix 套接字也可以没有名称，Linux 还支持抽象名称。这些形式具有不同访问规则，因此“Unix 套接字就是一个受权限保护的文件”并不是完整解释。&lt;/p&gt;
&lt;h2 id="坏掉的样子"&gt;坏掉的样子&lt;/h2&gt;
&lt;p&gt;应用找得到套接字路径，却可能因为监听已经退出而无法连接。关闭路径套接字，不会自动删除对应文件系统条目。重启时直接绑定相同路径，便可能得到 EADDRINUSE，即使那里已经没有服务接受连接。文件存在是有用诊断信息，但不能作为健康检查；要知道服务是否可用，仍需要真实通信与预期应用响应。&lt;/p&gt;
&lt;p&gt;Linux 连接路径流套接字，需要对套接字拥有写权限，同时能够搜索沿途目录。套接字文件上的执行位，与目录遍历所需执行位并不是同一用途。宽松的套接字权限无法补偿不可访问的父目录。反过来，让敏感控制套接字对所有本地进程可写，也可能授予很强的应用能力，不能因为没有 TCP 端口就忽略其权限。&lt;/p&gt;
&lt;p&gt;这些规则不会自动适用于所有名称形式与操作系统。Linux 抽象套接字不依赖文件路径，修改某个路径的权限无法保护抽象名称。可移植程序也不能假定每种 Unix 实现都同样执行套接字文件权限。对端凭据和应用授权，应按照实际平台与身份模型设计，不能只根据一个看起来像文件的名字推测安全属性。&lt;/p&gt;
&lt;h2 id="快速验证"&gt;快速验证&lt;/h2&gt;
&lt;p&gt;将脚本保存为 &lt;code&gt;check-unix-socket.py&lt;/code&gt;，准备 Docker 后执行 &lt;code&gt;python3 check-unix-socket.py&lt;/code&gt;。固定 Linux 环境使用 Python 3.11.16，服务器在一次性目录中建立路径流套接字。独立客户端进程能够降低到用户号和组号六万五千五百三十四；实验不会修改宿主账号、现有套接字或权限，所有改动随容器清理：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;json&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nn"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="nn"&gt;uuid&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;python@sha256:528257d48c1da0dcecc2e725d1ae34498d60c965f1241e39cd6a85a8859bdf84&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;program&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;from pathlib import Path
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;from tempfile import TemporaryDirectory
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;import errno,json,socket,socketserver,stat,struct,subprocess,sys,threading
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;class Handler(socketserver.StreamRequestHandler):
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; def handle(self):
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; self.request.settimeout(1);assert self.rfile.readline()==b&amp;#39;ping\n&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; pid,uid,gid=struct.unpack(&amp;#39;3i&amp;#39;,self.request.getsockopt(socket.SOL_SOCKET,socket.SO_PEERCRED,struct.calcsize(&amp;#39;3i&amp;#39;)))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; self.wfile.write(json.dumps({&amp;#39;uid&amp;#39;:uid,&amp;#39;gid&amp;#39;:gid,&amp;#39;pid_positive&amp;#39;:pid&amp;gt;0}).encode()+b&amp;#39;\n&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;client_code=r&amp;#39;&amp;#39;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;import json,os,socket,sys
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;if sys.argv[2]==&amp;#39;drop&amp;#39;:os.setgroups([]);os.setgid(65534);os.setuid(65534)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;try:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; with socket.socket(socket.AF_UNIX,socket.SOCK_STREAM) as s:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; s.settimeout(1);s.connect(sys.argv[1]);s.sendall(b&amp;#39;ping\n&amp;#39;);out=s.makefile(&amp;#39;rb&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; with out:print(out.readline().decode().strip())
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;except PermissionError:print(json.dumps({&amp;#39;error&amp;#39;:&amp;#39;EACCES&amp;#39;,&amp;#39;uid&amp;#39;:os.geteuid()}))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#39;&amp;#39;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;def client(path,drop=False):
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; p=subprocess.run([sys.executable,&amp;#39;-c&amp;#39;,client_code,str(path),&amp;#39;drop&amp;#39; if drop else &amp;#39;keep&amp;#39;],capture_output=True,text=True,timeout=3)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; assert p.returncode==0,p.stderr
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; return json.loads(p.stdout)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;with TemporaryDirectory(prefix=&amp;#39;unix-socket-&amp;#39;) as directory:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; root=Path(directory);root.chmod(0o755);path=root/&amp;#39;service.sock&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; server=socketserver.UnixStreamServer(str(path),Handler)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; path.chmod(0o600);thread=threading.Thread(target=server.serve_forever,daemon=True);thread.start()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; try:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; owner=client(path);denied=client(path,True)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; assert owner[&amp;#39;uid&amp;#39;]==0 and denied=={&amp;#39;error&amp;#39;:&amp;#39;EACCES&amp;#39;,&amp;#39;uid&amp;#39;:65534}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; path.chmod(0o666);allowed=client(path,True);assert allowed[&amp;#39;uid&amp;#39;]==65534 and allowed[&amp;#39;gid&amp;#39;]==65534
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; root.chmod(0o700);directory_denied=client(path,True);assert directory_denied==denied
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; root.chmod(0o755)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; finally:server.shutdown();server.server_close();thread.join(timeout=2);assert not thread.is_alive()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; assert stat.S_ISSOCK(path.stat().st_mode)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; with socket.socket(socket.AF_UNIX,socket.SOCK_STREAM) as probe:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; try:probe.connect(str(path));raise AssertionError(&amp;#39;stale socket accepted&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; except ConnectionRefusedError:stale=&amp;#39;ECONNREFUSED&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; with socket.socket(socket.AF_UNIX,socket.SOCK_STREAM) as duplicate:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; try:duplicate.bind(str(path));raise AssertionError(&amp;#39;stale path reused&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; except OSError as error:assert error.errno==errno.EADDRINUSE
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; path.unlink()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; with socket.socket(socket.AF_UNIX,socket.SOCK_STREAM) as replacement,socket.socket(socket.AF_UNIX,socket.SOCK_STREAM) as probe:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; replacement.bind(str(path));replacement.listen(1);replacement.settimeout(1);probe.settimeout(1);probe.connect(str(path))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; peer,_=replacement.accept()
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; with peer,probe.makefile(&amp;#39;rb&amp;#39;) as incoming:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; peer.sendall(b&amp;#39;restarted&amp;#39;);assert incoming.read(9)==b&amp;#39;restarted&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; print(json.dumps({&amp;#39;owner_peer&amp;#39;:owner,&amp;#39;mode_0600_denied&amp;#39;:denied,&amp;#39;mode_0666_peer&amp;#39;:allowed,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#39;directory_0700_denied&amp;#39;:directory_denied,&amp;#39;closed_path_remains_socket&amp;#39;:True,&amp;#39;stale_connect&amp;#39;:stale,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#39;stale_bind&amp;#39;:&amp;#39;EADDRINUSE&amp;#39;,&amp;#39;restarted_after_owned_path_unlink&amp;#39;:True}))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;xbcatm-socket-&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uuid4&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hex&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;docker&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;run&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;--rm&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;-i&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;--name&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;--network&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;none&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;--memory&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;128m&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;--pids-limit&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;64&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;python3&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;-&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;returncode&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="n"&gt;ensure_ascii&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;sort_keys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;docker&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;rm&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;-f&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;父目录允许搜索、套接字权限为零六零零时，root 客户端能够连接，并收到服务器读取的对端凭据；降权客户端得到 EACCES。服务器通过 SO_PEERCRED 取得已经连接的进程身份，没有相信请求自行声明的用户字符串。脚本核对连接边界实际报告的有效用户号，以真实结果验证身份读取，而不只是检查选项名称出现过。&lt;/p&gt;
&lt;p&gt;临时把套接字改成零六六六，降权客户端便能连接，服务器报告其用户号与组号均为六万五千五百三十四。随后仅把父目录改成零七零零，同一客户端再次被拒绝，尽管套接字仍允许所有用户写入。宽松模式只用于一次性容器中隔离两种权限检查，不是高权限生产服务的建议配置。目录可达性和套接字写权限必须分别考虑。&lt;/p&gt;
&lt;p&gt;关闭服务器后，路径仍具有套接字文件类型。向它连接得到 ECONNREFUSED，新套接字绑定这个名称则得到 EADDRINUSE。这些观察区分遗留条目与正在监听的服务。程序随后删除自己明确拥有的路径，重新绑定并建立真实连接，确认收到 &lt;code&gt;restarted&lt;/code&gt;。恢复步骤既验证名字可以复用，也验证新监听实际提供了响应。&lt;/p&gt;
&lt;p&gt;请求使用换行分隔，响应读取遵循相应边界。Unix 流套接字不承诺一次写入等于一次读取。本地传输排除了 IP 路由，却没有取消处理部分读取、截止时间或对端意外关闭的责任。其他 Unix 套接字类型可以具有消息边界，本例明确使用 SOCK_STREAM，不能把不同类型的语义混到同一段程序解释中。&lt;/p&gt;
&lt;h2 id="把本地访问当作应用能力管理"&gt;把本地访问当作应用能力管理&lt;/h2&gt;
&lt;p&gt;按合法调用方选择目录归属和套接字组，再以真实组内、组外身份验证。能够访问管理套接字的进程，即使无法连接某个 TCP 端口，也可能执行重要操作。应审阅服务器提供的命令和授权行为，不能仅按是否存在 IP 监听来判断能力大小。用户号还需要按部署的身份范围解释，而不是直接等同于某个自然人。&lt;/p&gt;
&lt;p&gt;路径套接字还要求客户端具有正确文件系统视图。容器看不到服务器路径时，不会因为共享宿主就自动发现服务；有意共享套接字路径，则可能把服务能力交给该容器。Linux 抽象名称具有另一种命名空间边界。文档应说明选择了哪种命名方式，以及调用方怎样取得访问范围，避免把所有本地通信端点视为可互换对象。&lt;/p&gt;
&lt;p&gt;重启流程应明确路径归属。只有确认服务管理者拥有该名称，且不会移除另一个活跃实例的入口时，才清理遗留路径。如果性能是切换传输的理由，还应使用真实负载测量；本实验验证访问和生命周期语义，没有承诺统一延迟优势。把监听健康、调用方身份与协议结果一起观察，才能说明本地通信实际满足了什么要求。&lt;/p&gt;
&lt;h2 id="来源"&gt;来源&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://man7.org/linux/man-pages/man7/unix.7.html"&gt;Linux unix(7)&lt;/a&gt;，路径权限、抽象名称、流语义与对端凭据，已核验 2026-09-12。&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.python.org/3.11/library/socket.html"&gt;Python 3.11 socket&lt;/a&gt;，AF_UNIX 流、套接字选项与描述符生命周期，已核验 2026-09-12。&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>