Implement Phase 7: gRPC dual-stack interface
- proto/mcias/v1/: AdminService, AuthService, TokenService, AccountService, CredentialService; generated Go stubs in gen/ - internal/grpcserver: full handler implementations sharing all business logic (auth, token, db, crypto) with REST server; interceptor chain: logging -> auth (JWT alg-first + revocation) -> rate-limit (token bucket, 10 req/s, burst 10, per-IP) - internal/config: optional grpc_addr field in [server] section - cmd/mciassrv: dual-stack startup; gRPC/TLS listener on grpc_addr when configured; graceful shutdown of both servers in 15s window - cmd/mciasgrpcctl: companion gRPC CLI mirroring mciasctl commands (health, pubkey, account, role, token, pgcreds) using TLS with optional custom CA cert - internal/grpcserver/grpcserver_test.go: 20 tests via bufconn covering public RPCs, auth interceptor (no token, invalid, revoked -> 401), non-admin -> 403, Login/Logout/RenewToken/ValidateToken flows, AccountService CRUD, SetPGCreds/GetPGCreds AES-GCM round-trip, credential fields absent from all responses Security: JWT validation path identical to REST: alg header checked before signature, alg:none rejected, revocation table checked after sig. Authorization metadata value never logged by any interceptor. Credential fields (PasswordHash, TOTPSecret*, PGPassword) absent from all proto response messages — enforced by proto design and confirmed by test TestCredentialFieldsAbsentFromAccountResponse. Login dummy-Argon2 timing guard preserves timing uniformity for unknown users (same as REST handleLogin). TLS required at listener level; cmd/mciassrv uses credentials.NewServerTLSFromFile; no h2c offered. 137 tests pass, zero race conditions (go test -race ./...)
This commit is contained in:
88
PROGRESS.md
88
PROGRESS.md
@@ -4,10 +4,10 @@ Source of truth for current development state.
|
||||
|
||||
---
|
||||
|
||||
## Current Status: Phase 6 Complete — Phases 7–9 Planned
|
||||
## Current Status: Phase 7 Complete — Phases 8–9 Planned
|
||||
|
||||
117 tests pass with zero race conditions. Phases 7–9 are designed and
|
||||
documented; implementation not yet started.
|
||||
137 tests pass with zero race conditions. Phase 7 (gRPC dual-stack) is
|
||||
complete. Phases 8–9 are designed and documented; implementation not yet started.
|
||||
|
||||
### Completed Phases
|
||||
|
||||
@@ -18,10 +18,10 @@ documented; implementation not yet started.
|
||||
- [x] Phase 4: Admin CLI (mciasctl binary)
|
||||
- [x] Phase 5: E2E tests, security hardening, commit
|
||||
- [x] Phase 6: mciasdb — direct SQLite maintenance tool
|
||||
- [x] Phase 7: gRPC interface (alternate transport; dual-stack with REST)
|
||||
|
||||
### Planned Phases
|
||||
|
||||
- [ ] Phase 7: gRPC interface (alternate transport; dual-stack with REST)
|
||||
- [ ] Phase 8: Operational artifacts (systemd unit, man pages, Makefile, install script)
|
||||
- [ ] Phase 9: Client libraries (Go, Rust, Common Lisp, Python)
|
||||
|
||||
@@ -29,6 +29,86 @@ documented; implementation not yet started.
|
||||
|
||||
## Implementation Log
|
||||
|
||||
### 2026-03-11 — Phase 7: gRPC dual-stack
|
||||
|
||||
**proto/mcias/v1/**
|
||||
- `common.proto` — shared types: Account, TokenInfo, PGCreds, Error
|
||||
- `admin.proto` — AdminService: Health (public), GetPublicKey (public)
|
||||
- `auth.proto` — AuthService: Login (public), Logout, RenewToken,
|
||||
EnrollTOTP, ConfirmTOTP, RemoveTOTP (admin)
|
||||
- `token.proto` — TokenService: ValidateToken (public),
|
||||
IssueServiceToken (admin), RevokeToken (admin)
|
||||
- `account.proto` — AccountService (CRUD + roles, all admin) +
|
||||
CredentialService (GetPGCreds, SetPGCreds, all admin)
|
||||
- `proto/generate.go` — go:generate directive for protoc regeneration
|
||||
- Generated Go stubs in `gen/mcias/v1/` via protoc + protoc-gen-go-grpc
|
||||
|
||||
**internal/grpcserver**
|
||||
- `grpcserver.go` — Server struct, interceptor chain
|
||||
(loggingInterceptor → authInterceptor → rateLimitInterceptor),
|
||||
GRPCServer() / GRPCServerWithCreds(creds) / buildServer() helpers,
|
||||
per-IP token-bucket rate limiter (same parameters as REST: 10 req/s,
|
||||
burst 10), extractBearerFromMD, requireAdmin
|
||||
- `admin.go` — Health, GetPublicKey implementations
|
||||
- `auth.go` — Login (with dummy-Argon2 timing guard), Logout, RenewToken,
|
||||
EnrollTOTP, ConfirmTOTP, RemoveTOTP
|
||||
- `tokenservice.go` — ValidateToken (returns valid=false on error, never
|
||||
an RPC error), IssueServiceToken, RevokeToken
|
||||
- `accountservice.go` — ListAccounts, CreateAccount, GetAccount,
|
||||
UpdateAccount, DeleteAccount, GetRoles, SetRoles
|
||||
- `credentialservice.go` — GetPGCreds (AES-GCM decrypt), SetPGCreds
|
||||
(AES-GCM encrypt)
|
||||
|
||||
**Security invariants (same as REST server):**
|
||||
- Authorization metadata value never logged by any interceptor
|
||||
- Credential fields (PasswordHash, TOTPSecret*, PGPassword) absent from
|
||||
all proto response messages by proto design + grpcserver enforcement
|
||||
- JWT validation: alg-first, then signature, then revocation table lookup
|
||||
- Public RPCs bypass auth: Health, GetPublicKey, ValidateToken, Login
|
||||
- Admin-only RPCs checked in-handler via requireAdmin(ctx)
|
||||
- Dummy Argon2 in Login for unknown users prevents timing enumeration
|
||||
|
||||
**internal/config additions**
|
||||
- `GRPCAddr string` field in ServerConfig (optional; omit to disable gRPC)
|
||||
|
||||
**cmd/mciassrv updates**
|
||||
- Dual-stack: starts both HTTPS (REST) and gRPC/TLS listeners when
|
||||
grpc_addr is configured in [server] section
|
||||
- gRPC listener uses same TLS cert/key as REST; credentials passed at
|
||||
server-construction time via GRPCServerWithCreds
|
||||
- Graceful shutdown drains both listeners within 15s window
|
||||
|
||||
**cmd/mciasgrpcctl**
|
||||
- New companion CLI for gRPC management
|
||||
- Global flags: -server (host:port), -token (or MCIAS_TOKEN), -cacert
|
||||
- Commands: health, pubkey, account (list/create/get/update/delete),
|
||||
role (list/set), token (validate/issue/revoke),
|
||||
pgcreds (get/set)
|
||||
- Connects with TLS; custom CA cert support for self-signed certs
|
||||
|
||||
**Tests**
|
||||
- `internal/grpcserver/grpcserver_test.go`: 20 tests using bufconn
|
||||
(in-process, no network sockets); covers:
|
||||
- Health and GetPublicKey (public RPCs, no auth)
|
||||
- Auth interceptor: no token, invalid token, revoked token all → 401
|
||||
- Non-admin calling admin RPC → 403
|
||||
- Login: success, wrong password, unknown user
|
||||
- Logout and RenewToken
|
||||
- ValidateToken: good token → valid=true; garbage → valid=false (no error)
|
||||
- IssueServiceToken requires admin
|
||||
- ListAccounts: non-admin → 403, admin → OK
|
||||
- CreateAccount, GetAccount, UpdateAccount, SetRoles, GetRoles lifecycle
|
||||
- SetPGCreds + GetPGCreds with AES-GCM round-trip verification
|
||||
- PGCreds requires admin
|
||||
- Credential fields absent from account responses (structural enforcement)
|
||||
|
||||
**Dependencies added**
|
||||
- `google.golang.org/grpc v1.68.0`
|
||||
- `google.golang.org/protobuf v1.36.0`
|
||||
- `google.golang.org/grpc/test/bufconn` (test only, included in grpc module)
|
||||
|
||||
Total: 137 tests, all pass, zero race conditions (go test -race ./...)
|
||||
|
||||
### 2026-03-11 — Phase 6: mciasdb
|
||||
|
||||
**cmd/mciasdb**
|
||||
|
||||
Reference in New Issue
Block a user