Step 14: SSH key auth for gRPC.
Server: AuthInterceptor parses authorized_keys, extracts SSH signature from gRPC metadata (nonce + timestamp signed by client's SSH key), verifies against authorized public keys with 5-minute timestamp window. Client: SSHCredentials implements PerRPCCredentials, signs nonce+timestamp per request. LoadSigner resolves key from flag, ssh-agent, or default paths. 8 tests: valid auth, reject unauthenticated, reject unauthorized key, reject expired timestamp, metadata generation, plus 2 integration tests (authenticated succeeds, unauthenticated rejected). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
57
client/auth_test.go
Normal file
57
client/auth_test.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
func TestSSHCredentialsMetadata(t *testing.T) {
|
||||
_, priv, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("generating key: %v", err)
|
||||
}
|
||||
signer, err := ssh.NewSignerFromKey(priv)
|
||||
if err != nil {
|
||||
t.Fatalf("creating signer: %v", err)
|
||||
}
|
||||
|
||||
creds := NewSSHCredentials(signer)
|
||||
|
||||
md, err := creds.GetRequestMetadata(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("GetRequestMetadata: %v", err)
|
||||
}
|
||||
|
||||
// Verify all required fields are present and non-empty.
|
||||
for _, key := range []string{
|
||||
"x-sgard-auth-nonce",
|
||||
"x-sgard-auth-timestamp",
|
||||
"x-sgard-auth-signature",
|
||||
"x-sgard-auth-pubkey",
|
||||
} {
|
||||
val, ok := md[key]
|
||||
if !ok || val == "" {
|
||||
t.Errorf("missing or empty metadata key %s", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSSHCredentialsNoTransportSecurity(t *testing.T) {
|
||||
_, priv, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
t.Fatalf("generating key: %v", err)
|
||||
}
|
||||
signer, err := ssh.NewSignerFromKey(priv)
|
||||
if err != nil {
|
||||
t.Fatalf("creating signer: %v", err)
|
||||
}
|
||||
|
||||
creds := NewSSHCredentials(signer)
|
||||
if creds.RequireTransportSecurity() {
|
||||
t.Error("RequireTransportSecurity should be false")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user