Custom metadata headers with dots not transmitted to server on streaming calls over Unix sockets
メンテナーはふだん 2 日以内に返信
まだ誰も着手していません。
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 35/100
- issue の種類
- バグ
- 明瞭さ
- おおむね明確
- 活発さ
- 停滞
- 技術スタック
- grpc, typescript
- 領域
- api, networking
調査の方向性
まず、grpc.Metadata から makeServerStreamRequest、Unix ソケットトランスポートに至るメタデータ処理を追跡し、名前にドットを含むヘッダーに注目します。SPIRE に対する server-streaming の FetchX509SVID 呼び出しを再現し、workload.spiffe.io がサーバーに到達してリクエストが成功することを確認し、このケースのリグレッションカバレッジを追加します。
索引モデルが issue の本文から書いたものです。
説明
Problem description
When making server-streaming gRPC calls over Unix sockets, custom metadata headers containing dots in the header name (e.g., workload.spiffe.io) are not transmitted to the server, even though they appear correctly in client-side metadata objects.
Expected Behavior
The server should receive the workload.spiffe.io: true header and process the request normally, returning X.509 SVID credentials.
Actual Behavior
The server responds immediately with:
gRPC status: 12 (UNIMPLEMENTED)
Message: "unknown service spiffe.workload.SpiffeWorkloadAPI"
This is the error SPIRE returns when the workload.spiffe.io header is missing (a security measure to prevent unauthorized access).
Reproduction steps
- Set up a SPIRE agent listening on a Unix socket (e.g., /run/spire/sockets/agent.sock)
- Create a gRPC client using @grpc/grpc-js:
import * as grpc from "@grpc/grpc-js";
import * as protoLoader from "@grpc/proto-loader";
const packageDefinition = protoLoader.loadSync("workload.proto", {
keepCase: true,
longs: String,
enums: String,
defaults: true,
oneofs: true,
});
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition) as any;
const SpiffeWorkloadAPI = protoDescriptor.spiffe.workload.SpiffeWorkloadAPI;
// Connect to Unix socket
const client = new SpiffeWorkloadAPI(
"unix:/run/spire/sockets/agent.sock",
grpc.credentials.createInsecure()
);
// Create metadata with required header
const metadata = new grpc.Metadata();
metadata.set("workload.spiffe.io", "true");
console.log("Metadata:", metadata.getMap());
// Output: { 'workload.spiffe.io': [ 'true' ] }
// Make streaming call
const call = client.FetchX509SVID({}, metadata);
call.on("error", (err) => {
console.error("Error:", err.code, err.details);
});
call.on("data", (response) => {
console.log("Response:", response);
});
Also attempted using low-level makeServerStreamRequest:
const metadata = new grpc.Metadata();
metadata.set("workload.spiffe.io", "true");
const call = client.makeServerStreamRequest(
"/spiffe.workload.SpiffeWorkloadAPI/FetchX509SVID",
(arg: any) => Buffer.alloc(0),
(buf: Buffer) => deserializeResponse(buf),
{},
metadata,
{}
);
Environment
- OS: Linux Alpine 3.23 docker container
- Node: version 24.12.0
- Node installation method: docker image node24-alpine
- @grpc/grpc-js version: 1.12.x (also tested with 1.14.3)
- Transport: Unix domain socket
Additional context
With GRPC_TRACE=all GRPC_VERBOSITY=DEBUG, the client logs show:
Calling FetchX509SVID with metadata:
Keys: { 'workload.spiffe.io': [ 'true' ] }
D ... | subchannel_call | [3] Received server headers:
:status: 200
content-type: application/grpc
grpc-status: 12
grpc-message: unknown service spiffe.workload.SpiffeWorkloadAPI
Note that the debug output does not show the outgoing headers being sent, and the server immediately rejects the request.
Additional Attempts
All of the following approaches resulted in the same error:
Using metadata.add() instead of metadata.set()
Using interceptors to inject metadata
Using createFromMetadataGenerator call credentials (cannot combine with insecure channel credentials)
Using the generated client's method directly: client.FetchX509SVID({}, metadata)
Using low-level makeServerStreamRequest with explicit metadata parameter
Verification
The same SPIRE agent works correctly with:
Go clients using github.com/spiffe/go-spiffe/v2/workloadapi
The official spire-agent api fetch CLI command
This confirms the server is functioning correctly and the issue is specific to @grpc/grpc-js.
Hypothesis
The header name containing dots (workload.spiffe.io) may be getting filtered, normalized, or lost during transmission over Unix sockets. Alternatively, there may be an issue with how metadata is attached to server-streaming calls specifically.
Workaround
We implemented a Go sidecar that communicates with SPIRE and exposes credentials via a simple HTTP API, which the Node.js service consumes.
Proto Definition
protobufsyntax = "proto3";
package spiffe.workload;
message X509SVIDRequest {}
message X509SVIDResponse {
repeated X509SVID svids = 1;
map<string, bytes> federated_bundles = 3;
}
message X509SVID {
string spiffe_id = 1;
bytes x509_svid = 2;
bytes x509_svid_key = 3;
bytes bundle = 4;
string hint = 5;
}
service SpiffeWorkloadAPI {
rpc FetchX509SVID(X509SVIDRequest) returns (stream X509SVIDResponse);
}
Related
SPIFFE Workload API specification: https://github.com/spiffe/spiffe/blob/main/standards/SPIFFE_Workload_API.md
The workload.spiffe.io header requirement is documented in the SPIFFE Workload API spec as a security measure
- 主要言語
- TypeScript
- スター
- 4.8k
- フォーク
- 717
- 平均マージ
- 1日 18時間
- マージ済み PR(30日)
- 17
環境構築
- Dockerfile・Docker Compose ファイルなし
- プルリクエストのテンプレートなし
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
grpc/grpc-node のほかの issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
メンテナーはふだん 2 日以内に返信
-
package: @grpc/grpc-js
難易度 2/5 1〜3時間 初心者へのやさしさ 62/100
grpc/grpc-node#2993 · コメント 3 件 · リアクション 4 件 ·
メンテナーはふだん 2 日以内に返信
-
難易度 3/5 1〜2日 初心者へのやさしさ 65/100
grpc/grpc-node#3091 · リアクション 1 件 ·
メンテナーはふだん 2 日以内に返信
-
feature request
難易度 3/5 1〜2日 初心者へのやさしさ 68/100
grpc/grpc-node#3077 · コメント 1 件 ·
メンテナーはふだん 2 日以内に返信
-
難易度 3/5 1〜2日 初心者へのやさしさ 76/100
grpc/grpc-node#3068 · コメント 2 件 · リアクション 1 件 ·
メンテナーはふだん 2 日以内に返信
似ている issue
-
bug via-triage
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
pingdotgg/t3code#14452 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
solana-foundation/program-examples#747 · コメント 1 件 ·
メンテナーはふだん 9 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 65/100
remotion-dev/remotion#11847 ·
メンテナーはふだん 1 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
openwatersio/slackwater#355 ·
メンテナーはふだん 1 日以内に返信
-
bug
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
melgarafael/DeskcommCRM#1998 · コメント 3 件 ·
メンテナーはふだん 1 日以内に返信