cmd/tools: tag-signer utility to create signed node tags
Change-Id: I2983d688a109325a02fcd060ca1a2d4eb8e9e931
This commit is contained in:
parent
01e33e7753
commit
20a47034a5
186
cmd/tools/tag-signer/main.go
Normal file
186
cmd/tools/tag-signer/main.go
Normal file
@ -0,0 +1,186 @@
|
||||
// Copyright (C) 2023 Storj Labs, Inc.
|
||||
// See LICENSE for copying information.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/zeebo/errs"
|
||||
|
||||
"storj.io/common/identity"
|
||||
"storj.io/common/nodetag"
|
||||
"storj.io/common/pb"
|
||||
"storj.io/common/signing"
|
||||
"storj.io/common/storj"
|
||||
"storj.io/private/process"
|
||||
)
|
||||
|
||||
var (
|
||||
rootCmd = &cobra.Command{
|
||||
Use: "tag-signer",
|
||||
Short: "Sign key=value pairs with identity",
|
||||
Long: "Node tags are arbitrary key value pairs signed by an authority. If the public key is configured on " +
|
||||
"Satellite side, Satellite will check the signatures and save the tags, which can be used (for example)" +
|
||||
" during node selection. Storagenodes can be configured to send encoded node tags to the Satellite. " +
|
||||
"This utility helps creating/managing the values of this specific configuration value, which is encoded by default.",
|
||||
}
|
||||
|
||||
signCmd = &cobra.Command{
|
||||
Use: "sign <key=value> <key2=value> ...",
|
||||
Short: "Create signed tagset",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ctx, _ := process.Ctx(cmd)
|
||||
encoded, err := signTags(ctx, config, args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(encoded)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
inspectCmd = &cobra.Command{
|
||||
Use: "inspect <encoded string>",
|
||||
Short: "Print out the details from an encoded node set",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ctx, _ := process.Ctx(cmd)
|
||||
return inspect(ctx, args[0])
|
||||
},
|
||||
}
|
||||
|
||||
config Config
|
||||
)
|
||||
|
||||
// Config contains configuration required for signing.
|
||||
type Config struct {
|
||||
IdentityDir string `help:"location if the identity files" path:"true"`
|
||||
NodeID string `help:"the ID of the node, which will used this tag "`
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(signCmd)
|
||||
rootCmd.AddCommand(inspectCmd)
|
||||
process.Bind(signCmd, &config)
|
||||
}
|
||||
|
||||
func signTags(ctx context.Context, cfg Config, tagPairs []string) (string, error) {
|
||||
|
||||
if cfg.IdentityDir == "" {
|
||||
return "", errs.New("Please specify the identity, used as a signer with --identity-dir")
|
||||
}
|
||||
|
||||
if cfg.NodeID == "" {
|
||||
return "", errs.New("Please specify the --node-id")
|
||||
}
|
||||
|
||||
identityConfig := identity.Config{
|
||||
CertPath: filepath.Join(cfg.IdentityDir, "identity.cert"),
|
||||
KeyPath: filepath.Join(cfg.IdentityDir, "identity.key"),
|
||||
}
|
||||
|
||||
fullIdentity, err := identityConfig.Load()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
signer := signing.SignerFromFullIdentity(fullIdentity)
|
||||
|
||||
nodeID, err := storj.NodeIDFromString(cfg.NodeID)
|
||||
if err != nil {
|
||||
return "", errs.New("Wrong NodeID format: %v", err)
|
||||
}
|
||||
tagSet := &pb.NodeTagSet{
|
||||
NodeId: nodeID.Bytes(),
|
||||
Timestamp: time.Now().Unix(),
|
||||
}
|
||||
|
||||
for _, tag := range tagPairs {
|
||||
tag = strings.TrimSpace(tag)
|
||||
if len(tag) == 0 {
|
||||
continue
|
||||
}
|
||||
parts := strings.SplitN(tag, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return "", errs.New("tags should be in KEY=VALUE format, but it was %s", tag)
|
||||
}
|
||||
tagSet.Tags = append(tagSet.Tags, &pb.Tag{
|
||||
Name: parts[0],
|
||||
Value: []byte(parts[1]),
|
||||
})
|
||||
}
|
||||
|
||||
signedMessage, err := nodetag.Sign(ctx, tagSet, signer)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
all := &pb.SignedNodeTagSets{
|
||||
Tags: []*pb.SignedNodeTagSet{
|
||||
signedMessage,
|
||||
},
|
||||
}
|
||||
|
||||
raw, err := proto.Marshal(all)
|
||||
if err != nil {
|
||||
return "", errs.Wrap(err)
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(raw), nil
|
||||
}
|
||||
|
||||
func inspect(ctx context.Context, s string) error {
|
||||
raw, err := base64.StdEncoding.DecodeString(s)
|
||||
if err != nil {
|
||||
return errs.New("Input is not in base64 format")
|
||||
}
|
||||
|
||||
sets := &pb.SignedNodeTagSets{}
|
||||
err = proto.Unmarshal(raw, sets)
|
||||
if err != nil {
|
||||
return errs.New("Input is not a protobuf encoded *pb.SignedNodeTagSets message")
|
||||
}
|
||||
|
||||
for _, msg := range sets.Tags {
|
||||
|
||||
signerNodeID, err := storj.NodeIDFromBytes(msg.SignerNodeId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Signer: ", signerNodeID.String())
|
||||
fmt.Println("Signature: ", hex.EncodeToString(msg.Signature))
|
||||
|
||||
tags := &pb.NodeTagSet{}
|
||||
err = proto.Unmarshal(msg.SerializedTag, tags)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
nodeID, err := storj.NodeIDFromBytes(tags.NodeId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("Timestamp: ", time.Unix(tags.Timestamp, 0).Format(time.RFC3339))
|
||||
fmt.Println("NodeID: ", nodeID.String())
|
||||
fmt.Println("Tags:")
|
||||
for _, tag := range tags.Tags {
|
||||
fmt.Printf(" %s=%s\n", tag.Name, string(tag.Value))
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
process.Exec(rootCmd)
|
||||
}
|
Loading…
Reference in New Issue
Block a user