docs/notes/credentials_proxy.mdXem bản gốc Bản dịch đầy đủ theo đúng cấu trúc tài liệu; code, liên kết và định danh kỹ thuật được giữ nguyên.
Proxy thông tin xác thực
Đã xóa trong cam kết 51d7457 (PR #32), mỗi ADR-0016.
Đọc nó tại git show 51d7457^:src/decode/sandbox/proxy.py. Ghi chú này giải thích nó là gì và
nó hoạt động như thế nào, bởi vì ý tưởng đáng được giữ lại mặc dù mã thì không.
Phiên bản một câu
Vùng chứa Worker được đóng hộp cát có thể thực hiện lệnh gọi được xác thực tới GitHub trong khi giữ no
thông tin xác thực: nó đã gửi yêu cầu mà không có token và một thùng chứa mitmproxy nằm trên
cùng một mạng docker được đính kèm Authorization tiêu đề sau yêu cầu đã rời khỏi
Công nhân.
Thuộc tính mà nó đã mua: quy trình chạy các lệnh shell do mô hình chọn không thể đọc được token, vì token không có trong môi trường, hệ thống tệp hoặc bộ nhớ của nó.
Phạm vi - nơi áp dụng
| Trục | Giá trị |
|---|---|
| Bề mặt | Chỉ mất đầu (decode run / decode replay). REPL chưa bao giờ xây dựng nó - đó là những gì được giữ nguyên decode từ việc nhập khẩu kitaru. |
| Chế độ hộp cát | docker chỉ. modal chưa bao giờ có nó (không có mạng docker để tắt bộ chứa proxy) - thay vào đó, nó đã chèn trực tiếp token (ADR-0012 §10). none là chủ nhà nên không có gì để bảo vệ. |
| Mặc định | Tắt. DEFAULT_PROXY_RULES đã vận chuyển trống; một bản đồ thông tin xác thực trống là một proxy chuyển tiếp không có tác dụng gì. |
| Đã đính hôn khi | SANDBOX_MODE=docker và (SANDBOX_CREDENTIAL_PROXY_ENABLED=true hoặc không trống SANDBOX_GIT_TOKEN). |
Chiếc bàn đó cũng chính là văn bia: tính bất biến mà nó bảo vệ là đúng một trong ba các chế độ — chứ không phải chế độ điều khiển từ xa, nơi mà sự cách ly là quan trọng nhất.
Kiến trúc
flowchart TB
subgraph host["HOST — the decode process (headless flow)"]
settings["Settings (hydrated)<br/>SANDBOX_GIT_TOKEN · SANDBOX_PROXY_IMAGE"]
rules["Proxy Rules<br/>SandboxProxyRule(name, hosts[], headers{})<br/>DEFAULT_PROXY_RULES = [] · github_token_rules(token)"]
cmap["build_credential_map(rules)<br/>resolve templates HOST-SIDE, at flow start<br/>→ {host: {header: value}}"]
sp["_sandbox_proxy() context manager<br/>start → install executor → yield → teardown"]
settings --> rules --> cmap --> sp
end
subgraph net["per-run docker network — decode-sandbox-net-<uuid12>"]
worker["WORKER container (decode-sandbox-<id>)<br/>image: uv:python3.12-bookworm-slim<br/>/workspace = bind-mount of .decode/sandbox<br/>env: http_proxy/https_proxy → proxy · GH_TOKEN=<decoy><br/>trust store: + mitmproxy CA<br/><b>HOLDS NO REAL CREDENTIAL</b>"]
proxy["PROXY container (decode-proxy-<uuid12>)<br/>image: mitmproxy/mitmproxy<br/>mitmdump --listen-port 8080 -s /opt/proxy_addon.py<br/>env: DECODE_CREDENTIAL_MAP={host:{header:value}}<br/><b>HOLDS THE CREDENTIAL</b>"]
end
internet([internet — github.com · api.github.com · everything else])
sp -- "docker network create" --> net
sp -- "docker run --env-file (0600, unlinked)" --> proxy
sp -- "docker run --network + proxy env + CA mount" --> worker
worker -- "request, NO auth header" --> proxy
proxy -- "request + injected Authorization" --> internet
internet -- "response (unmodified)" --> proxy -- "response" --> worker
classDef hostc fill:#1565c0,stroke:#0d47a1,color:#fff;
classDef sec fill:#e65100,stroke:#bf360c,color:#fff;
classDef ext fill:#6a1b9a,stroke:#38006b,color:#fff;
class settings,rules,cmap,sp hostc;
class worker,proxy sec;
class internet ext;
Ba quy trình, ba mức độ tin cậy: máy chủ biết bí mật và giải quyết nó; proxy giữ bí mật đã được giải quyết trong môi trường riêng của nó; Worker — người duy nhất chạy mã do model chọn — không giữ được gì cả.
Đầu vào và đầu ra
Đầu vào (tất cả phía máy chủ):
| Đầu vào | Từ đâu đến | Vai trò |
|---|---|---|
SANDBOX_MODE=docker | Settings | Cổng. Bất cứ điều gì khác = không có proxy. |
SANDBOX_CREDENTIAL_PROXY_ENABLED | Settings (bool, mặc định false) | Chọn tham gia rõ ràng cho đường dẫn quy tắc. |
SANDBOX_GIT_TOKEN | Settings (SecretStr) | Phím tắt GitHub một nút bấm — một giá trị không trống được tự động tương tác proxy. |
SANDBOX_PROXY_IMAGE | Settings (mặc định mitmproxy/mitmproxy) | Hình ảnh vùng chứa addon. |
DEFAULT_PROXY_RULES | Mã không đổi, đã vận chuyển [] | Quy tắc dành cho bất kỳ máy chủ khác nào. |
Tạo tác trung gian — bản đồ thông tin xác thực, đối tượng duy nhất chứa bí mật đã được giải quyết:
# build_credential_map(rules) → this, host-side, at flow start
{
"api.github.com": {"Authorization": "Bearer ghp_xxx"}, # REST API (gh, curl)
"github.com": {"Authorization": "Basic eC1hY2Nlc3MtdG9rZW46Z2hwX3h4eA=="}, # git push over HTTPS
}
Hai quy tắc cho một token, vì GitHub muốn xác thực khác cho hai lần vận chuyển: Bearer
đối với API REST, Basic base64("x-access-token:<PAT>") đối với git-over-HTTPS (từ chối Bearer).
api.github.com phải đến đầu tiên — tiện ích bổ sung trả về kết quả khớp đầu tiên và github.com
trận đấu cha mẹ api.github.com.
Đầu ra (cái gì _sandbox_proxy() được tạo ra cho phần còn lại của luồng):
| Đầu ra | Người tiêu dùng |
|---|---|
proxy.network — decode-sandbox-net-<uuid12> | DockerBackend(network=…) - Công nhân tham gia vào nó. |
proxy.worker_proxy_env — http_proxy/https_proxy/HTTP_PROXY/HTTPS_PROXY = http://decode-proxy-<uuid12>:8080, no_proxy=localhost,127.0.0.1, GH_TOKEN=<decoy> | DockerBackend(proxy_env=…) — -e cờ trên Công nhân docker run. Mang theo không có bí mật. |
proxy.ca_cert_host_path — CA mitmproxy trong thư mục tạm thời của máy chủ | DockerBackend(ca_cert_host_path=…) — chỉ đọc được gắn liên kết vào Worker, sau đó được tin cậy. |
A SandboxExecutor nối vào đó DockerBackend | install_executor() - trở thành bashngười thực thi cho nhịp luồng. |
Cách thông tin xác thực tiếp cận proxy — và chỉ proxy
Bản đồ đã giải quyết không bao giờ chạm vào Công nhân và không bao giờ chạm vào bản đồ của chủ nhà argv hoặc:
build_credential_map()giải quyết các quy tắc phía máy chủ thành{host: {header: value}}.DockerCredentialProxy._run_proxy_container()ghi bản đồ đó dưới dạng JSON vào0600tập tin tạm thời (DECODE_CREDENTIAL_MAP=<json>), chuyển nó dưới dạngdocker run --env-file <path>, vàunlinkđó là tập tin trong mộtfinallykhoảnh khắcdocker runtrở lại. Tuổi thọ trên đĩa: mili giây. Nó không bao giờ xuất hiện ởdocker rundòng lệnh của, do đó phía máy chủpskhông bao giờ nhìn thấy nó.- Bên trong thùng chứa,
proxy_addon.pyđọc nó một lần khi nhập:json.loads(os.environ.get("DECODE_CREDENTIAL_MAP", "{}")). - Nhật ký ở mọi nơi đều in tên tiêu đề và tên máy chủ, không bao giờ có giá trị.
của công nhân docker run có --network, các biến env URL proxy và một giá trị CA. Không có bí mật.
Đường dẫn yêu cầu — Worker → internet
sequenceDiagram
participant W as Worker container<br/>(git / gh / curl / urllib)
participant P as mitmproxy + CredentialAddon
participant G as github.com
Note over W: env: https_proxy=http://decode-proxy-xxx:8080<br/>trust store contains the mitmproxy CA
W->>P: CONNECT github.com:443
P-->>W: 200 Connection established
W->>P: TLS handshake — P serves a leaf cert<br/>signed by the mitmproxy CA (Worker trusts it → OK)
W->>P: POST /decode/decode.git/git-receive-pack<br/>(NO Authorization header)
Note over P: addon.request(flow)<br/>_match_host("github.com") → hit<br/>flow.request.headers["Authorization"] = "Basic …"<br/>(SET, not append — overwrites the gh decoy)
P->>G: TLS to the real github.com (upstream, normal certs)<br/>POST … + Authorization: Basic …
G-->>P: 200 (push accepted)
P-->>W: 200 (response passed through UNMODIFIED)
Những điểm quan trọng:
- TLS bị chấm dứt hai lần. mitmproxy được thiết kế theo kiểu trung gian: nó cung cấp cho Công nhân một chứng chỉ lá do CA của chính nó tạo ra (đó là lý do tại sao CA phải nằm trong kho ủy thác của Người lao động), và mở một kết nối TLS thông thường *riêng biệt ngược dòng. Nó đọc và viết lại bản rõ trong giữa. Đó là toàn bộ thủ thuật - bạn không thể đưa tiêu đề vào kết nối mà bạn không thể decode.
- Phần bổ sung chỉ chạm vào yêu cầu.
requestmóc đặt tiêu đề; không córesponsecái móc. Internet → Công nhân là một người truyền qua. headers[name] = valuethay thế. Đó là lý do để token mồi nhử hoạt động (bên dưới).- Máy chủ bỏ lỡ là vượt qua, không phải thất bại.
cli.github.com(cáighcài đặt apt) không khớp quy tắc, vì vậy nó thoát ra mà không được tiêm._match_hostkhớp chính xác hoặc tên miền gốc, không phân biệt chữ hoa chữ thường.
token mồi nhử
gh từ chối đưa ra yêu cầu bất kỳ nào khi nó không tìm thấy token nào trong môi trường - nó bị lỗi cục bộ với
"gh auth login" và không bao giờ tiếp cận được proxy đã xác thực nó. Thế là Người Công Nhân được giao
một trình giữ chỗ:
_GH_PLACEHOLDER_TOKEN = "decode-proxy-injects-the-real-token" # GH_TOKEN in the worker env
gh sau đó vui vẻ tiến hành, gửi Authorization: token decode-proxy-injects-the-real-token, và
addon ghi đè tiêu đề đó bằng thông tin xác thực thực sau khi yêu cầu rời khỏi Worker. các
chuỗi không xác thực gì cả; sự bất biến tồn tại. Đây cũng là lời nói rõ ràng nhất rằng
thiết kế đang đấu tranh với môi trường của nó.
Niềm tin của container CA - cuộc đua cần khắc phục đồng bộ
Worker phải tin cậy CA của proxy trước khi lệnh đầu tiên chạy hoặc lệnh đầu tiên git/curl
sẽ thất bại khi xác thực chứng chỉ. Vì vậy DockerBackend.create():
- Liên kết gắn CA chỉ đọc tại
/usr/local/share/ca-certificates/mitmproxy-ca-cert.crt- chính xác con đườngupdate-ca-certificatesgấp vào repository tin cậy của hệ thống. - Chạy
docker exec <worker> update-ca-certificatesđồng bộ, đang chờ nó (giới hạn ở 60 giây), trước đâycreate()trở lại. Nếu không thành công, vùng chứa vừa tạo sẽ được thu thập và lỗi xuất hiện.
Cũng chịu tải: thư mục chứng chỉ của proxy là chmod 0o777 phía máy chủ nên không phải root mitmdump có thể viết
CA được tạo của nó vào confdir được gắn liên kết và start() bị chặn cho đến khi tệp CA tồn tại và
cổng nghe đã trả lời (ngân sách của thập niên 20, được thử nghiệm bằng python3 ổ cắm kết nối bên trong container).
Vòng đời và thứ tự phá bỏ
_sandbox_proxy(repo, local): # runtime/flow.py, wraps the whole flow body
gate: sandbox_mode == "docker" and (flag or SANDBOX_GIT_TOKEN) # else pure no-op, imports nothing
rules = github_token_rules(token) + DEFAULT_PROXY_RULES
proxy = DockerCredentialProxy(build_credential_map(rules))
proxy.start() # network create → docker run → wait until ready
install_executor(SandboxExecutor(DockerBackend(network=…, proxy_env=…, ca_cert_host_path=…)))
prepare_workspace_or_empty(...) + warm the worker # CA trusted before the first bash
yield # ← the agent runs here
finally:
reap the WORKER first # docker network rm FAILS while it is attached
proxy.stop() # stop container → retry network rm (10×) → rmtree certs
Thứ tự xé bỏ không phải là một sự lựa chọn về phong cách: docker network rm thất bại trong khi bất kỳ container nào vẫn còn
được đính kèm và quá trình dọn dẹp điểm cuối của daemon sẽ làm chậm quá trình loại bỏ vùng chứa — do đó vòng lặp thử lại bị giới hạn.
Những gì nó đã làm không bảo vệ chống lại
Đi ra đã ** hợp tác **. Công nhân được chỉ vào proxy theo quy ước (http_proxy vars env
danh dự của các thư viện khách hàng hoạt động tốt); nó không bao giờ bị ép buộc thông qua nó. Một model muốn
bỏ qua nó gõ:
curl --noproxy '*' https://example.com # walks straight around the proxy
Vì vậy, proxy bảo vệ chống lại một mô hình đọc môi trường của chính nó, không chống lại một mô hình có nghĩa là hại. Đường dẫn nâng cấp là một mạng chỉ dành cho nội bộ với đầu ra từ chối mặc định — chưa bao giờ được xây dựng.
Nó cũng không bao giờ chạm tới thuộc tính thực sự quan trọng — bí mật không bao giờ chạm tới được model
bối cảnh. Điều đó xuất phát từ Settings không bao giờ được tuần tự hóa thành một dấu nhắc, từ SecretStrvà
từ nhật ký mang tên không phải giá trị. Tất cả những điều đó vẫn tồn tại sau khi xóa.
Tại sao nó bị xóa
Đối số đầy đủ trong ADR-0016. Danh sách ngắn:
- Một trong ba chế độ.
modal— phần phụ trợ từ xa, nơi mà sự cách ly quan trọng nhất — được tiêm trực tiếp dù sao thì token. Bất biến "Công nhân không có token" đã sai ở đó, với tài liệu ngoại lệ nói như vậy. - Hai cơ chế, một núm vặn.
SANDBOX_GIT_TOKENhoạt động khác nhau tùy thuộc vào phần phụ trợ người dùng đã không chọn vì lý do bảo mật. - Máy móc lớn và dễ vỡ ở những nơi chịu tải — mạng cho mỗi lần chạy, cuộc đua CA với
kiểm tra hồi quy của chính nó, một thứ tự chia nhỏ không được sắp xếp lại, một bản cài đặt thích hợp phải được thực hiện
đi ra thông qua proxy và token mồi nhử được cài đặt để xoa dịu
gh. - Nó chưa bao giờ là rào cản thoát nước (xem ở trên).
- Thứ mà nó bảo vệ là PAT có phạm vi, có thể thu hồi người dùng đã cố tình giao cho agent để nó có thể mở chi nhánh và mở PR. Không phải là thông tin xác thực cơ sở dữ liệu sản xuất.
Cái gì đã thay thế nó
Một cơ chế, cả hai phụ trợ (ADR-0016 §2): khi
SANDBOX_GIT_TOKEN được thiết lập, nó được đưa trực tiếp vào môi trường Worker dưới dạng GITHUB_TOKEN - docker qua
ít giá trị -e GITHUB_TOKEN (vì vậy giá trị không bao giờ nằm trong argv hiển thị trên máy chủ), phương thức thông qua một
modal.Secret - cộng với trình trợ giúp thông tin git (x-access-token:$GITHUB_TOKEN) vậy git push qua HTTPS
hoạt động và gh xác thực tắt nó. Chưa đặt → hộp cát không có thông tin xác thực nào cả và phía máy chủ
Hand-back vẫn vận chuyển Session Branch.
Quy trình đóng hộp cát hiện CÓ THỂ đọc $GITHUB_TOKEN. Đó là giao dịch trung thực: giao nó cho có phạm vi,
có thể hủy bỏ PAT. Câu chuyện bảo mật nhỏ hơn — và đúng như đã viết, ở mọi chế độ.