diff --git a/node/cmd/guardiand/node.go b/node/cmd/guardiand/node.go index 2cb32b07..259bbd41 100644 --- a/node/cmd/guardiand/node.go +++ b/node/cmd/guardiand/node.go @@ -2,9 +2,12 @@ package guardiand import ( "context" + "encoding/base64" "fmt" "github.com/certusone/wormhole/node/pkg/db" "github.com/certusone/wormhole/node/pkg/notify/discord" + "github.com/certusone/wormhole/node/pkg/telemetry" + "github.com/certusone/wormhole/node/pkg/version" "github.com/gagliardetto/solana-go/rpc" "go.uber.org/zap/zapcore" "log" @@ -102,6 +105,9 @@ var ( tlsProdEnv *bool disableHeartbeatVerify *bool + disableTelemetry *bool + + telemetryKey *string discordToken *string discordChannel *string @@ -175,6 +181,11 @@ func init() { disableHeartbeatVerify = NodeCmd.Flags().Bool("disableHeartbeatVerify", false, "Disable heartbeat signature verification (useful during network startup)") + disableTelemetry = NodeCmd.Flags().Bool("disableTelemetry", false, + "Disable telemetry") + + telemetryKey = NodeCmd.Flags().String("telemetryKey", "", + "Telemetry write key") discordToken = NodeCmd.Flags().String("discordToken", "", "Discord bot token (optional)") discordChannel = NodeCmd.Flags().String("discordChannel", "", "Discord channel name (optional)") @@ -257,9 +268,6 @@ func runNode(cmd *cobra.Command, args []string) { // Override the default go-log config, which uses a magic environment variable. ipfslog.SetAllLoggers(lvl) - // Redirect ipfs logs to plain zap - ipfslog.SetPrimaryCore(logger.Core()) - // Register components for readiness checks. readiness.RegisterComponent(common.ReadinessEthSyncing) readiness.RegisterComponent(common.ReadinessSolanaSyncing) @@ -543,6 +551,45 @@ func runNode(cmd *cobra.Command, args []string) { } } + // Enable unless it is disabled. For devnet, only when --telemetryKey is set. + if !*disableTelemetry && (!*unsafeDevMode || *unsafeDevMode && *telemetryKey != "") { + logger.Info("Telemetry enabled") + + if *telemetryKey == "" { + logger.Fatal("Please specify --telemetryKey") + } + + creds, err := decryptTelemetryServiceAccount() + if err != nil { + logger.Fatal("Failed to decrypt telemetry service account", zap.Error(err)) + } + + // Get libp2p peer ID from private key + pk := priv.GetPublic() + peerID, err := peer.IDFromPublicKey(pk) + if err != nil { + logger.Fatal("Failed to get peer ID from private key", zap.Error(err)) + } + + tm, err := telemetry.New(context.Background(), telemetryProject, creds, map[string]string{ + "node_name": *nodeName, + "node_key": peerID.Pretty(), + "guardian_addr": guardianAddr, + "network": *p2pNetworkID, + "version": version.Version(), + }) + if err != nil { + logger.Fatal("Failed to initialize telemetry", zap.Error(err)) + } + defer tm.Close() + logger = tm.WrapLogger(logger) + } else { + logger.Info("Telemetry disabled") + } + + // Redirect ipfs logs to plain zap + ipfslog.SetPrimaryCore(logger.Core()) + // provides methods for reporting progress toward message attestation, and channels for receiving attestation lifecyclye events. attestationEvents := reporter.EventListener(logger) @@ -694,3 +741,23 @@ func runNode(cmd *cobra.Command, args []string) { logger.Info("root context cancelled, exiting...") // TODO: wait for things to shut down gracefully } + +func decryptTelemetryServiceAccount() ([]byte, error) { + // Decrypt service account credentials + key, err := base64.StdEncoding.DecodeString(*telemetryKey) + if err != nil { + return nil, fmt.Errorf("failed to decode: %w", err) + } + + ciphertext, err := base64.StdEncoding.DecodeString(telemetryServiceAccount) + if err != nil { + panic(err) + } + + creds, err := common.DecryptAESGCM(ciphertext, key) + if err != nil { + return nil, fmt.Errorf("failed to decrypt: %w", err) + } + + return creds, err +} diff --git a/node/cmd/guardiand/telemetry.go b/node/cmd/guardiand/telemetry.go new file mode 100644 index 00000000..590c2cd0 --- /dev/null +++ b/node/cmd/guardiand/telemetry.go @@ -0,0 +1,57 @@ +package guardiand + +// Hardcoded telemetry GCP service account. Does not grant any access except for writing logs. +// +// Network operators can opt to send their logs to a shared Cloud Logging project for debugging. +// Logs are available to all network operators. There is no secret data logged anywhere. +// +// We encrypt the service account using a hardcoded symmetric key shared with guardians to +// prevent GitHub credential checkers from freaking out and to stop people from sending gigabytes of "gm". +// +// By using a separate key, we can keep the configuration decoupled from the telemetry backend, +// allowing the key to be replaced or even a different provider to be used without changing the config. + +const telemetryProject = "projects/wormhole-logging" + +const telemetryServiceAccount = ` +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 +3SWVv2wizHdu +` diff --git a/node/go.mod b/node/go.mod index c14b1388..961992e3 100644 --- a/node/go.mod +++ b/node/go.mod @@ -51,9 +51,16 @@ require ( nhooyr.io/websocket v1.8.7 // indirect ) +require ( + cloud.google.com/go/logging v1.4.2 + cloud.google.com/go/pubsub v1.17.1 + github.com/blendle/zapdriver v1.3.1 + github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce + github.com/google/uuid v1.2.0 +) + require ( cloud.google.com/go v0.97.0 // indirect - cloud.google.com/go/pubsub v1.17.1 // indirect contrib.go.opencensus.io/exporter/stackdriver v0.13.4 // indirect github.com/99designs/keyring v1.1.3 // indirect github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect @@ -63,9 +70,7 @@ require ( github.com/benbjohnson/clock v1.0.3 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect - github.com/blendle/zapdriver v1.3.1 // indirect github.com/btcsuite/btcd v0.21.0-beta // indirect - github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce // indirect github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.1.1 // indirect github.com/cheekybits/genny v1.0.0 // indirect @@ -101,7 +106,6 @@ require ( github.com/google/flatbuffers v1.12.0 // indirect github.com/google/go-cmp v0.5.6 // indirect github.com/google/gopacket v1.1.19 // indirect - github.com/google/uuid v1.2.0 // indirect github.com/googleapis/gax-go/v2 v2.1.1 // indirect github.com/gorilla/schema v1.2.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect @@ -125,7 +129,6 @@ require ( github.com/jbenet/goprocess v0.1.4 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/json-iterator/go v1.1.11 // indirect - github.com/jstemmer/go-junit-report v0.9.1 // indirect github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356 // indirect github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d // indirect github.com/klauspost/compress v1.13.1 // indirect @@ -236,8 +239,6 @@ require ( go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/ratelimit v0.2.0 // indirect - golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect - golang.org/x/mod v0.4.2 // indirect golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect diff --git a/node/go.sum b/node/go.sum index eb34e120..eebd285c 100644 --- a/node/go.sum +++ b/node/go.sum @@ -22,7 +22,6 @@ cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmW cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.82.0 h1:FZ4B2YAzCzkwzGEOp1dqG8sAa3zNIvro1fHRTrB81RU= cloud.google.com/go v0.82.0/go.mod h1:vlKccHJGuFBFufnAnuB08dfEH9Y3H7dzDzRECFdC2TA= cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= @@ -44,7 +43,10 @@ cloud.google.com/go/bigtable v1.10.1/go.mod h1:cyHeKlx6dcZCO0oSQucYdauseD8kIENGu cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/kms v1.0.0 h1:YkIeqPXqTAlwXk3Z2/WG0d6h1tqJQjU354WftjEoP9E= cloud.google.com/go/kms v1.0.0/go.mod h1:nhUehi+w7zht2XrUfvTRNpxrfayBHqP4lu2NSywui/0= +cloud.google.com/go/logging v1.4.2 h1:Mu2Q75VBDQlW1HlBMjTX4X84UFR73G1TiLlRYc/b7tA= +cloud.google.com/go/logging v1.4.2/go.mod h1:jco9QZSx8HiVVqLJReq7z7bVdj0P1Jb9PDFs63T+axo= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -492,7 +494,6 @@ github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= github.com/googleapis/gax-go/v2 v2.1.1 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU= @@ -523,8 +524,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92Bcuy github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0 h1:ajue7SzQMywqRjg2fK7dcpc0QhFGpTR2plWfV4EZWR4= github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0/go.mod h1:r1hZAcvfFXuYmcKyCJI9wlyOPIZUJl6FCB8Cpca/NLE= @@ -676,7 +677,6 @@ github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/ github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= @@ -1611,7 +1611,6 @@ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210427180440-81ed05c6b58c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210615190721-d04028783cf1 h1:x622Z2o4hgCr/4CiKWc51jHVKaWdtVpBNmEI8wI9Qns= golang.org/x/oauth2 v0.0.0-20210615190721-d04028783cf1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= @@ -1718,7 +1717,6 @@ golang.org/x/sys v0.0.0-20210503080704-8803ae5d1324/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b h1:qh4f65QIVFjq9eBURLEYWqaEXmOyqdUyiBSgaXWccWk= golang.org/x/sys v0.0.0-20210601080250-7ecdf8ef093b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1746,7 +1744,6 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1818,9 +1815,9 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3 h1:L69ShwSZEyCsLKoAxDKeMvLDZkumEe8gXUZAjab0tX8= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1860,7 +1857,6 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= google.golang.org/api v0.46.0/go.mod h1:ceL4oozhkAiTID8XMmJBsIxID/9wMXJVVFXPg4ylg3I= -google.golang.org/api v0.47.0 h1:sQLWZQvP6jPGIP4JGPkJu4zHswrv81iobiyszr3b/0I= google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= @@ -1937,7 +1933,6 @@ google.golang.org/genproto v0.0.0-20210601144548-a796c710e9b6/go.mod h1:P3QM42oQ google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced h1:c5geK1iMU3cDKtFrCVQIcjR3W+JOZMuhIyICMCTbtus= google.golang.org/genproto v0.0.0-20210617175327-b9e0b3197ced/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= @@ -1984,7 +1979,6 @@ google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= @@ -2002,7 +1996,6 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= diff --git a/node/hack/encrypt/encrypt.go b/node/hack/encrypt/encrypt.go new file mode 100644 index 00000000..ca4c05dd --- /dev/null +++ b/node/hack/encrypt/encrypt.go @@ -0,0 +1,46 @@ +package main + +import ( + "encoding/base64" + "fmt" + "io" + "log" + "math/rand" + "os" + + "github.com/certusone/wormhole/node/pkg/common" +) + +func main() { + in, err := io.ReadAll(os.Stdin) + if err != nil { + log.Fatalf("failed to read stdin: %v", err) + } + + // Generate 128-bit key + key := make([]byte, 16) + if _, err := rand.Read(key); err != nil { + log.Fatalf("failed to generate key: %v", err) + } + + // Log key as base64 string + log.Printf("key: %s", base64.StdEncoding.EncodeToString(key)) + + // Encrypt + ciphertext, err := common.EncryptAESGCM(in, key) + if err != nil { + log.Fatalf("failed to encrypt: %v", err) + } + + // Convert ciphertext as base64 string. + b64 := base64.StdEncoding.EncodeToString(ciphertext) + + // Hard-wrap to 80 characters per line. + for i := 0; i < len(b64); i += 80 { + j := i + 80 + if j > len(b64) { + j = len(b64) + } + fmt.Println(b64[i:j]) + } +} diff --git a/node/pkg/common/symmetric.go b/node/pkg/common/symmetric.go new file mode 100644 index 00000000..378c82bc --- /dev/null +++ b/node/pkg/common/symmetric.go @@ -0,0 +1,46 @@ +package common + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "fmt" +) + +func DecryptAESGCM(data, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, fmt.Errorf("failed to create cipher: %v", err) + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, fmt.Errorf("failed to create gcm: %v", err) + } + nonceSize := gcm.NonceSize() + if len(data) < nonceSize { + return nil, fmt.Errorf("data is too short") + } + nonce, data := data[:nonceSize], data[nonceSize:] + out, err := gcm.Open(nil, nonce, data, nil) + if err != nil { + return nil, fmt.Errorf("failed to decrypt: %v", err) + } + return out, nil +} + +func EncryptAESGCM(plaintext, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, fmt.Errorf("failed to create cipher: %v", err) + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, fmt.Errorf("failed to create gcm: %v", err) + } + nonce := make([]byte, gcm.NonceSize()) + if _, err = rand.Read(nonce); err != nil { + return nil, fmt.Errorf("failed to read random data: %v", err) + } + out := gcm.Seal(nil, nonce, plaintext, nil) + return append(nonce, out...), nil +} diff --git a/node/pkg/common/symmetric_test.go b/node/pkg/common/symmetric_test.go new file mode 100644 index 00000000..5dcdda03 --- /dev/null +++ b/node/pkg/common/symmetric_test.go @@ -0,0 +1,24 @@ +package common + +import ( + "testing" +) + +func TestAESGCM(t *testing.T) { + data := []byte("cat") + key := []byte("01234567890123456789012345678901") + + enc, err := EncryptAESGCM(data, key) + if err != nil { + t.Fatal(err) + } + + dec, err := DecryptAESGCM(enc, key) + if err != nil { + t.Fatal(err) + } + + if string(dec) != string(data) { + t.Fatalf("expected %s got %s", string(data), string(dec)) + } +} diff --git a/node/pkg/telemetry/telemetry.go b/node/pkg/telemetry/telemetry.go new file mode 100644 index 00000000..3da5978d --- /dev/null +++ b/node/pkg/telemetry/telemetry.go @@ -0,0 +1,98 @@ +package telemetry + +import ( + "cloud.google.com/go/logging" + "context" + "encoding/json" + "fmt" + "github.com/blendle/zapdriver" + "go.uber.org/zap" + "go.uber.org/zap/buffer" + "go.uber.org/zap/zapcore" + "google.golang.org/api/option" + "io/ioutil" +) + +type Telemetry struct { + encoder *encoder + serviceAccountJSON []byte +} + +type encoder struct { + zapcore.Encoder + logger *logging.Logger + labels map[string]string +} + +// Mirrors the conversion done by zapdriver. We need to convert this +// to proto severity for usage with the SDK client library +// (the JSON value encoded by zapdriver is ignored). +var logLevelSeverity = map[zapcore.Level]logging.Severity{ + zapcore.DebugLevel: logging.Debug, + zapcore.InfoLevel: logging.Info, + zapcore.WarnLevel: logging.Warning, + zapcore.ErrorLevel: logging.Error, + zapcore.DPanicLevel: logging.Critical, + zapcore.PanicLevel: logging.Alert, + zapcore.FatalLevel: logging.Emergency, +} + +func (enc *encoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Field) (*buffer.Buffer, error) { + buf, err := enc.Encoder.EncodeEntry(entry, fields) + if err != nil { + return nil, err + } + + // Create a copy of buf (zap will reuse the same buffer otherwise) + bufCopy := make([]byte, len(buf.Bytes())) + copy(bufCopy, buf.Bytes()) + + // Convert the zapcore.Level to a logging.Severity + severity := logLevelSeverity[entry.Level] + + // Write raw message to log + enc.logger.Log(logging.Entry{ + Timestamp: entry.Time, + Payload: json.RawMessage(bufCopy), + Severity: severity, + Labels: enc.labels, + }) + + return buf, nil +} + +func New(ctx context.Context, project string, serviceAccountJSON []byte, labels map[string]string) (*Telemetry, error) { + gc, err := logging.NewClient(ctx, project, option.WithCredentialsJSON(serviceAccountJSON)) + if err != nil { + return nil, fmt.Errorf("unable to create logging client: %v", err) + } + + gc.OnError = func(err error) { + fmt.Printf("telemetry: logging client error: %v\n", err) + } + + return &Telemetry{ + serviceAccountJSON: serviceAccountJSON, + encoder: &encoder{ + Encoder: zapcore.NewJSONEncoder(zapdriver.NewProductionEncoderConfig()), + logger: gc.Logger("wormhole"), + labels: labels, + }, + }, nil +} + +func (s *Telemetry) WrapLogger(logger *zap.Logger) *zap.Logger { + tc := zapcore.NewCore( + s.encoder, + zapcore.AddSync(ioutil.Discard), + zap.DebugLevel, + ) + + return logger.WithOptions(zap.WrapCore(func(core zapcore.Core) zapcore.Core { + return zapcore.NewTee(core, tc) + })) +} + +func (s *Telemetry) Close() error { + return s.encoder.logger.Flush() +}