Skip to content

Commit 3c54f02

Browse files
Introduce KeyTransparencyAuditorService
1 parent 2ffd5a3 commit 3c54f02

File tree

9 files changed

+306
-15
lines changed

9 files changed

+306
-15
lines changed

cmd/kt-client/audit.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
"github.com/signalapp/keytransparency/cmd/kt-server/pb"
1515
)
1616

17-
func handleAudit(client pb.KeyTransparencyServiceClient) {
17+
func handleAudit(client pb.KeyTransparencyAuditorServiceClient) {
1818
if flag.Arg(1) == "" {
1919
log.Fatal("No starting position given. Usage: kt-client audit <start> <limit>")
2020
} else if flag.Arg(2) == "" {

cmd/kt-client/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func main() {
7474

7575
defer ktConn.Close()
7676

77-
ktClient := pb.NewKeyTransparencyServiceClient(ktConn)
77+
ktAuditorClient := pb.NewKeyTransparencyAuditorServiceClient(ktConn)
7878

7979
switch flag.Arg(0) {
8080
case "distinguished":
@@ -90,7 +90,7 @@ func main() {
9090
case "monitor-timing":
9191
handleMonitorTiming(ktQueryClient)
9292
case "audit":
93-
handleAudit(ktClient)
93+
handleAudit(ktAuditorClient)
9494
default:
9595
log.Fatal("Unexpected operation requested. Allowed arguments: search, update, audit, config")
9696
}

cmd/kt-server/kt_handler.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ type KtHandler struct {
3131
auditorTreeHeadsCh chan<- updateAuditorTreeHeadRequest // Channel used to set auditor tree heads
3232

3333
pb.UnimplementedKeyTransparencyServiceServer
34+
pb.UnimplementedKeyTransparencyAuditorServiceServer
3435
}
3536

3637
func (h *KtHandler) TreeSize(ctx context.Context, req *emptypb.Empty) (*pb.TreeSizeResponse, error) {

cmd/kt-server/main.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,8 +212,14 @@ func main() {
212212
util.Log().Fatalf("Failed to create listener for kt server: %v", err)
213213
}
214214

215-
ktServer := grpc.NewServer(getServerOptions(config.KtServiceConfig, []grpc.UnaryServerInterceptor{storeAuditorNameInterceptor(config.KtServiceConfig)})...)
215+
ktServer := grpc.NewServer(getServerOptions(config.KtServiceConfig, []grpc.UnaryServerInterceptor{
216+
// Downstream interceptors expect the auditor name to be stored in the context, so this interceptor must
217+
// be listed first.
218+
storeAuditorNameInterceptor(config.KtServiceConfig),
219+
grpcServiceNameMetricsInterceptor()})...)
216220
pb.RegisterKeyTransparencyServiceServer(ktServer, ktHandler)
221+
pb.RegisterKeyTransparencyAuditorServiceServer(ktServer, ktHandler)
222+
217223
util.Log().Infof("Starting kt server at: %v", ktServiceConfig.ServerAddr)
218224
if config.KtTestServiceConfig == nil {
219225
healthCheck.SetServingStatus(readiness, healthpb.HealthCheckResponse_SERVING)

cmd/kt-server/pb/key_transparency.pb.go

Lines changed: 30 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/kt-server/pb/key_transparency.proto

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,15 @@ service KeyTransparencyService {
3838
// Auditors use this endpoint to return a signature on the log tree root hash corresponding to the last audited update.
3939
rpc SetAuditorHead(transparency.AuditorTreeHead) returns (google.protobuf.Empty) {}
4040
}
41+
42+
// A key transparency service used to provide data to auditors and to accept auditor-signed tree heads.
43+
// Outside of third-party auditors, this service's endpoints are *not* intended to be used by external clients.
44+
// It is exposed to the public internet by necessity but will reject calls from unauthenticated callers.
45+
service KeyTransparencyAuditorService {
46+
// Auditors can query this endpoint to learn the current size of the transparency log.
47+
rpc TreeSize(google.protobuf.Empty) returns (TreeSizeResponse) {}
48+
// Auditors use this endpoint to request a batch of key transparency service updates to audit.
49+
rpc Audit(AuditRequest) returns (AuditResponse) {}
50+
// Auditors use this endpoint to return a signature on the log tree root hash corresponding to the last audited update.
51+
rpc SetAuditorHead(transparency.AuditorTreeHead) returns (google.protobuf.Empty) {}
52+
}

cmd/kt-server/pb/key_transparency_grpc.pb.go

Lines changed: 193 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/kt-server/util.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import (
1010
"context"
1111
"crypto/subtle"
1212
"fmt"
13+
"strings"
1314

15+
"github.com/hashicorp/go-metrics"
1416
"google.golang.org/grpc"
1517
"google.golang.org/grpc/codes"
1618
"google.golang.org/grpc/metadata"
@@ -97,6 +99,37 @@ func storeAuditorNameInterceptor(config *config.ServiceConfig) func(ctx context.
9799
}
98100
}
99101

102+
func grpcServiceNameMetricsInterceptor() grpc.UnaryServerInterceptor {
103+
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
104+
service, method, err := parseFullMethodString(info.FullMethod)
105+
if err != nil {
106+
return nil, err
107+
}
108+
auditorName, ok := ctx.Value(AuditorNameContextKey).(string)
109+
if !ok {
110+
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("invalid type for auditor name. expected string, got %T", auditorName))
111+
}
112+
labels := []metrics.Label{{Name: "service", Value: service}, {Name: "method", Value: method}, {Name: "auditor", Value: auditorName}}
113+
metrics.IncrCounterWithLabels([]string{"kt_handler"}, 1, labels)
114+
115+
return handler(ctx, req)
116+
}
117+
}
118+
119+
// Parses the full RPC method string for the service and method names.
120+
// The string is expected to be in the format: /package.service/method.
121+
func parseFullMethodString(fullMethod string) (string, string, error) {
122+
parts := strings.Split(fullMethod, "/")
123+
if len(parts) != 3 || len(parts[1]) == 0 || len(parts[2]) == 0 {
124+
return "", "", status.Error(codes.InvalidArgument, fmt.Sprintf("unexpected RPC path: %s", fullMethod))
125+
}
126+
parsedPackageService := strings.Split(parts[1], ".")
127+
if len(parsedPackageService) != 2 || len(parsedPackageService[0]) == 0 || len(parsedPackageService[1]) == 0 {
128+
return "", "", status.Error(codes.InvalidArgument, fmt.Sprintf("unexpected RPC path: %s", fullMethod))
129+
}
130+
return parsedPackageService[1], parts[2], nil
131+
}
132+
100133
// validateAuthorizedHeaders ensures that at least one of the specified header to value mappings is present on the request
101134
// Returns the last header value that matched.
102135
func validateAuthorizedHeaders(authorizedHeaders map[string][]string, md metadata.MD) (string, error) {

0 commit comments

Comments
 (0)