Permalink
Browse files

Non-verifying monitor (#776)

* implements a monitor: binary, communication to the kt-server, simple in-memory storage of monitoring results, skip actual verification logic (sign and publish everything)
  • Loading branch information...
Liamsi committed Aug 24, 2017
1 parent df5f639 commit 205ae4d3103e799c7a2287768568578ead35a2e5
@@ -0,0 +1,12 @@
FROM golang
ADD keytransparency/genfiles/* /kt/
ADD ./keytransparency /go/src/github.com/google/keytransparency
ADD ./trillian /go/src/github.com/google/trillian
WORKDIR /go/src/github.com/google/keytransparency
RUN go get -tags="mysql" ./cmd/keytransparency-monitor
ENTRYPOINT ["/go/bin/keytransparency-monitor"]
EXPOSE 8099
@@ -0,0 +1,228 @@
// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"crypto/tls"
"flag"
"net"
"net/http"
"strings"
"time"
"github.com/golang/glog"
"github.com/google/keytransparency/impl/monitor"
"github.com/google/trillian"
"github.com/google/trillian/crypto"
"github.com/google/trillian/crypto/keys/pem"
"github.com/grpc-ecosystem/go-grpc-prometheus"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/reflection"
cmon "github.com/google/keytransparency/core/monitor"
"github.com/google/keytransparency/core/monitor/storage"
kpb "github.com/google/keytransparency/core/proto/keytransparency_v1_types"
"github.com/google/keytransparency/impl/monitor/client"
spb "github.com/google/keytransparency/impl/proto/keytransparency_v1_service"
mopb "github.com/google/keytransparency/impl/proto/monitor_v1_service"
mupb "github.com/google/keytransparency/impl/proto/mutation_v1_service"
_ "github.com/google/trillian/merkle/coniks" // Register coniks
_ "github.com/google/trillian/merkle/objhasher" // Register objhasher
)
var (
addr = flag.String("addr", ":8099", "The ip:port combination to listen on")
keyFile = flag.String("tls-key", "genfiles/server.key", "TLS private key file")
certFile = flag.String("tls-cert", "genfiles/server.pem", "TLS cert file")
signingKey = flag.String("sign-key", "genfiles/monitor_sign-key.pem", "Path to private key PEM for SMH signing")
signingKeyPassword = flag.String("password", "towel", "Password of the private key PEM file for SMH signing")
ktURL = flag.String("kt-url", "localhost:8080", "URL of key-server.")
insecure = flag.Bool("insecure", false, "Skip TLS checks")
ktCert = flag.String("kt-cert", "genfiles/server.crt", "Path to kt-server's public key")
pollPeriod = flag.Duration("poll-period", time.Second*5, "Maximum time between polling the key-server. Ideally, this is equal to the min-period of paramerter of the keyserver.")
// TODO(ismail): expose prometheus metrics: a variable that tracks valid/invalid MHs
// metricsAddr = flag.String("metrics-addr", ":8081", "The ip:port to publish metrics on")
)
func grpcGatewayMux(addr string) (*runtime.ServeMux, error) {
ctx := context.Background()
creds, err := credentials.NewClientTLSFromFile(*certFile, "")
if err != nil {
return nil, err
}
dopts := []grpc.DialOption{grpc.WithTransportCredentials(creds)}
gwmux := runtime.NewServeMux()
if err := mopb.RegisterMonitorServiceHandlerFromEndpoint(ctx, gwmux, addr, dopts); err != nil {
return nil, err
}
return gwmux, nil
}
// grpcHandlerFunc returns an http.Handler that delegates to grpcServer on incoming gRPC
// connections or otherHandler otherwise. Copied from cockroachdb.
func grpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// This is a partial recreation of gRPC's internal checks.
// https://github.com/grpc/grpc-go/blob/master/transport/handler_server.go#L62
if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") {
grpcServer.ServeHTTP(w, r)
} else {
otherHandler.ServeHTTP(w, r)
}
})
}
func main() {
flag.Parse()
creds, err := credentials.NewServerTLSFromFile(*certFile, *keyFile)
if err != nil {
glog.Exitf("Failed to load server credentials %v", err)
}
// Create gRPC server.
grpcServer := grpc.NewServer(
grpc.Creds(creds),
grpc.StreamInterceptor(grpc_prometheus.StreamServerInterceptor),
grpc.UnaryInterceptor(grpc_prometheus.UnaryServerInterceptor),
)
// Connect to the kt-server's mutation API:
grpcc, err := dial()
if err != nil {
glog.Fatalf("Error Dialing %v: %v", ktURL, err)
}
mcc := mupb.NewMutationServiceClient(grpcc)
// Read signing key:
key, err := pem.ReadPrivateKeyFile(*signingKey, *signingKeyPassword)
if err != nil {
glog.Fatalf("Could not create signer from %v: %v", *signingKey, err)
}
ctx := context.Background()
logTree, mapTree, err := getTrees(ctx, grpcc)
if err != nil {
glog.Fatalf("Could not read domain info %v:", err)
}
store := storage.New()
srv := monitor.New(store)
mopb.RegisterMonitorServiceServer(grpcServer, srv)
reflection.Register(grpcServer)
grpc_prometheus.Register(grpcServer)
grpc_prometheus.EnableHandlingTimeHistogram()
// Create HTTP handlers and gRPC gateway.
gwmux, err := grpcGatewayMux(*addr)
if err != nil {
glog.Exitf("Failed setting up REST proxy: %v", err)
}
// Insert handlers for other http paths here.
mux := http.NewServeMux()
mux.Handle("/", gwmux)
// initialize the mutations API client and feed the responses it got
// into the monitor:
mon, err := cmon.New(logTree, mapTree, crypto.NewSHA256Signer(key), store)
if err != nil {
glog.Exitf("Failed to initialize monitor: %v", err)
}
mutCli := client.New(mcc, *pollPeriod)
responses, errs := mutCli.StartPolling(1)
go func() {
for {
select {
case mutResp := <-responses:
glog.Infof("Received mutations response: %v", mutResp.Epoch)
if err := mon.Process(mutResp); err != nil {
glog.Infof("Error processing mutations response: %v", err)
}
case err := <-errs:
// this is OK if there were no mutations in between:
// TODO(ismail): handle the case when the known maxDuration has
// passed and no epoch was issued?
glog.Infof("Could not retrieve mutations API response %v", err)
}
}
}()
// Serve HTTP2 server over TLS.
glog.Infof("Listening on %v", *addr)
if err := http.ListenAndServeTLS(*addr, *certFile, *keyFile,
grpcHandlerFunc(grpcServer, mux)); err != nil {
glog.Errorf("ListenAndServeTLS: %v", err)
}
}
func dial() (*grpc.ClientConn, error) {
var opts []grpc.DialOption
transportCreds, err := transportCreds(*ktURL, *ktCert, *insecure)
if err != nil {
return nil, err
}
opts = append(opts, grpc.WithTransportCredentials(transportCreds))
// TODO(ismail): authenticate the monitor to the kt-server:
cc, err := grpc.Dial(*ktURL, opts...)
if err != nil {
return nil, err
}
return cc, nil
}
// TODO(ismail): refactor client and monitor to use the same methods
func transportCreds(ktURL string, ktCert string, insecure bool) (credentials.TransportCredentials, error) {
// copied from keytransparency-client/cmd/root.go: transportCreds
host, _, err := net.SplitHostPort(ktURL)
if err != nil {
return nil, err
}
switch {
case insecure: // Impatient insecure.
return credentials.NewTLS(&tls.Config{
InsecureSkipVerify: true,
}), nil
case ktCert != "": // Custom CA Cert.
return credentials.NewClientTLSFromFile(ktCert, host)
default: // Use the local set of root certs.
return credentials.NewClientTLSFromCert(nil, host), nil
}
}
// config selects a source for and returns the client configuration.
func getTrees(ctx context.Context, cc *grpc.ClientConn) (logTree *trillian.Tree, mapTree *trillian.Tree, err error) {
ktClient := spb.NewKeyTransparencyServiceClient(cc)
resp, err2 := ktClient.GetDomainInfo(ctx, &kpb.GetDomainInfoRequest{})
if err2 != nil {
err = err2
return
}
logTree = resp.GetLog()
mapTree = resp.GetMap()
return
}
View
@@ -0,0 +1,87 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package monitor
import (
"crypto"
"fmt"
"time"
"github.com/golang/glog"
"github.com/google/keytransparency/core/monitor/storage"
ktpb "github.com/google/keytransparency/core/proto/keytransparency_v1_types"
"github.com/google/trillian"
tcrypto "github.com/google/trillian/crypto"
"github.com/google/trillian/merkle"
"github.com/google/trillian/merkle/hashers"
)
// Monitor holds the internal state for a monitor accessing the mutations API
// and for verifying its responses.
type Monitor struct {
hasher hashers.MapHasher
logPubKey crypto.PublicKey
mapPubKey crypto.PublicKey
logVerifier merkle.LogVerifier
signer *tcrypto.Signer
// TODO(ismail): update last trusted signed log root
//trusted trillian.SignedLogRoot
store *storage.Storage
}
// New creates a new instance of the monitor.
func New(logTree, mapTree *trillian.Tree, signer *tcrypto.Signer, store *storage.Storage) (*Monitor, error) {
logHasher, err := hashers.NewLogHasher(logTree.GetHashStrategy())
if err != nil {
return nil, fmt.Errorf("Failed creating LogHasher: %v", err)
}
mapHasher, err := hashers.NewMapHasher(mapTree.GetHashStrategy())
if err != nil {
return nil, fmt.Errorf("Failed creating MapHasher: %v", err)
}
return &Monitor{
hasher: mapHasher,
logVerifier: merkle.NewLogVerifier(logHasher),
logPubKey: logTree.GetPublicKey(),
mapPubKey: mapTree.GetPublicKey(),
signer: signer,
store: store,
}, nil
}
// Process processes a mutation response received from the keytransparency
// server. Processing includes verifying, signing and storing the resulting
// monitoring response.
func (m *Monitor) Process(resp *ktpb.GetMutationsResponse) error {
var smr *trillian.SignedMapRoot
var err error
seen := time.Now().Unix()
errs := m.verifyMutationsResponse(resp)
if len(errs) == 0 {
glog.Infof("Successfully verified mutations response for epoch: %v", resp.Epoch)
smr, err = m.signMapRoot(resp)
if err != nil {
glog.Errorf("Failed to sign map root for epoch %v: %v", resp.Epoch, err)
return fmt.Errorf("m.signMapRoot(_): %v", err)
}
}
if err := m.store.Set(resp.Epoch, seen, smr, resp, errs); err != nil {
glog.Errorf("m.store.Set(%v, %v, _, _, %v): %v", resp.Epoch, seen, errs, err)
return err
}
return nil
}
View
@@ -0,0 +1,37 @@
// Copyright 2017 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package monitor
import (
"fmt"
ktpb "github.com/google/keytransparency/core/proto/keytransparency_v1_types"
"github.com/google/trillian"
)
func (m *Monitor) signMapRoot(in *ktpb.GetMutationsResponse) (*trillian.SignedMapRoot, error) {
// copy of received SMR:
smr := *in.Smr
smr.Signature = nil
sig, err := m.signer.SignObject(smr)
if err != nil {
return nil, fmt.Errorf("SignObject(): %v", err)
}
smr.Signature = sig
return &smr, nil
}
Oops, something went wrong.

0 comments on commit 205ae4d

Please sign in to comment.