Use runtime-dir Unix sockets for local gRPC

This commit is contained in:
Joe Julian
2026-04-11 08:26:37 -07:00
parent c017308aa1
commit 2ef571c241
16 changed files with 346 additions and 29 deletions
+33 -4
View File
@@ -2,22 +2,26 @@ package browserbridge
import (
"context"
"crypto/sha256"
"encoding/base64"
"encoding/binary"
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"runtime"
"strings"
"git.julianfamily.org/keepassgo/internal/grpcaddr"
keepassgov1 "git.julianfamily.org/keepassgo/proto/keepassgo/v1"
)
const (
NativeHostName = "org.keepassgo.browser"
DefaultGRPCAddress = "127.0.0.1:47777"
defaultFirefoxID = "browser@keepassgo.invalid"
NativeHostName = "com.keepassgo.browser"
defaultFirefoxID = "browser@keepassgo.com"
maxNativeMessageSize = 1024 * 1024
chromiumIDBytes = 16
)
type Request struct {
@@ -136,7 +140,7 @@ func (r Request) Connection() (Connection, error) {
BearerToken: strings.TrimSpace(r.BearerToken),
}
if conn.GRPCAddress == "" {
conn.GRPCAddress = DefaultGRPCAddress
conn.GRPCAddress = grpcaddr.Default(runtime.GOOS)
}
if conn.BearerToken == "" {
return Connection{}, fmt.Errorf("browser bridge bearer token is required")
@@ -277,6 +281,31 @@ func Manifest(browser Browser, binaryPath, extensionID string) (NativeHostManife
}
}
func ChromiumExtensionIDFromManifestKey(raw string) (string, error) {
normalized := strings.TrimSpace(raw)
normalized = strings.ReplaceAll(normalized, "-----BEGIN PUBLIC KEY-----", "")
normalized = strings.ReplaceAll(normalized, "-----END PUBLIC KEY-----", "")
normalized = strings.ReplaceAll(normalized, "\n", "")
normalized = strings.ReplaceAll(normalized, "\r", "")
normalized = strings.ReplaceAll(normalized, "\t", "")
normalized = strings.ReplaceAll(normalized, " ", "")
if normalized == "" {
return "", fmt.Errorf("chromium extension key is required")
}
publicKeyDER, err := base64.StdEncoding.DecodeString(normalized)
if err != nil {
return "", fmt.Errorf("decode chromium extension key: %w", err)
}
hash := sha256.Sum256(publicKeyDER)
var builder strings.Builder
builder.Grow(chromiumIDBytes * 2)
for _, b := range hash[:chromiumIDBytes] {
builder.WriteByte('a' + ((b >> 4) & 0x0f))
builder.WriteByte('a' + (b & 0x0f))
}
return builder.String(), nil
}
func DefaultManifestPath(browser Browser) (string, error) {
home, err := os.UserHomeDir()
if err != nil {
+34
View File
@@ -7,6 +7,8 @@ import (
"encoding/json"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
keepassgov1 "git.julianfamily.org/keepassgo/proto/keepassgo/v1"
@@ -39,6 +41,9 @@ func TestReadRequestAndWriteResponse(t *testing.T) {
if req.Action != "find-logins" || req.BearerToken != "secret" {
t.Fatalf("ReadRequest() = %#v, want action and token preserved", req)
}
if conn, err := req.Connection(); err != nil || conn.GRPCAddress != "127.0.0.1:47777" {
t.Fatalf("req.Connection() = (%#v, %v), want explicit tcp address preserved", conn, err)
}
var output bytes.Buffer
if err := WriteResponse(&output, Response{Success: true, Version: "1"}); err != nil {
@@ -118,6 +123,22 @@ func TestHandleRequestRequiresBearerToken(t *testing.T) {
}
}
func TestRequestConnectionDefaultsAddress(t *testing.T) {
t.Parallel()
req := Request{Action: "status", BearerToken: "secret"}
conn, err := req.Connection()
if err != nil {
t.Fatalf("Connection() error = %v", err)
}
if conn.GRPCAddress == "" {
t.Fatal("Connection().GRPCAddress = empty, want default address")
}
if runtime.GOOS != "windows" && !strings.HasPrefix(conn.GRPCAddress, "unix://") && conn.GRPCAddress != "off" {
t.Fatalf("Connection().GRPCAddress = %q, want unix socket default on this platform", conn.GRPCAddress)
}
}
func TestInstallManifest(t *testing.T) {
t.Parallel()
@@ -147,6 +168,19 @@ func TestInstallManifest(t *testing.T) {
}
}
func TestChromiumExtensionIDFromManifestKey(t *testing.T) {
t.Parallel()
const publicKey = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAMfW0u1k4K5A0uN2s0aH7uQKpM3x5Hf8mZfY1xVh0m7E2mJ7M8GiV4m0g0I2w9U9D1yqGQ6w8jzH5v8t7qB2RjMCAwEAAQ=="
got, err := ChromiumExtensionIDFromManifestKey(publicKey)
if err != nil {
t.Fatalf("ChromiumExtensionIDFromManifestKey() error = %v", err)
}
if got != "okcdfigpojphpoecpglkkmkjmiaefmpd" {
t.Fatalf("ChromiumExtensionIDFromManifestKey() = %q, want %q", got, "okcdfigpojphpoecpglkkmkjmiaefmpd")
}
}
type fakeClient struct {
status *keepassgov1.GetSessionStatusResponse
matches []*keepassgov1.BrowserLoginMatch
+14 -5
View File
@@ -4,8 +4,10 @@ import (
"context"
"fmt"
"net"
"runtime"
"strings"
"git.julianfamily.org/keepassgo/internal/grpcaddr"
keepassgov1 "git.julianfamily.org/keepassgo/proto/keepassgo/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
@@ -18,20 +20,27 @@ type GRPCClient struct {
func Dial(ctx context.Context, conn Connection) (*grpc.ClientConn, *GRPCClient, context.Context, error) {
if strings.TrimSpace(conn.GRPCAddress) == "" {
conn.GRPCAddress = DefaultGRPCAddress
conn.GRPCAddress = grpcaddr.Default(runtime.GOOS)
}
if strings.TrimSpace(conn.BearerToken) == "" {
return nil, nil, nil, fmt.Errorf("browser bridge bearer token is required")
}
address := strings.TrimSpace(conn.GRPCAddress)
grpcConn, err := grpc.NewClient("passthrough:///"+address,
network, endpoint, err := grpcaddr.Parse(conn.GRPCAddress)
if err != nil {
return nil, nil, nil, err
}
target := endpoint
if network == "unix" {
target = "passthrough:///" + endpoint
}
grpcConn, err := grpc.NewClient(target,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) {
return net.Dial("tcp", address)
return net.Dial(network, endpoint)
}),
)
if err != nil {
return nil, nil, nil, fmt.Errorf("dial gRPC host %s: %w", address, err)
return nil, nil, nil, fmt.Errorf("dial gRPC host %s: %w", strings.TrimSpace(conn.GRPCAddress), err)
}
ctx = metadata.AppendToOutgoingContext(ctx, "authorization", "Bearer "+strings.TrimSpace(conn.BearerToken))
return grpcConn, &GRPCClient{client: keepassgov1.NewVaultServiceClient(grpcConn)}, ctx, nil