Merge pull request #3914 from filecoin-project/feat/lotus-shed-more

Add keyinfo verify and jwt token command to lotus-shed
This commit is contained in:
Łukasz Magiera 2020-09-18 12:34:52 +02:00 committed by GitHub
commit 0dfd4f3e63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 189 additions and 0 deletions

View File

@ -1,6 +1,7 @@
package main
import (
"bufio"
"crypto/rand"
"encoding/hex"
"encoding/json"
@ -8,10 +9,12 @@ import (
"io"
"io/ioutil"
"os"
"strings"
"github.com/gbrlsnchs/jwt/v3"
"github.com/urfave/cli/v2"
"github.com/filecoin-project/go-jsonrpc/auth"
"github.com/filecoin-project/lotus/api/apistruct"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/node/modules"
@ -24,6 +27,102 @@ var jwtCmd = &cli.Command{
having to run the lotus daemon.`,
Subcommands: []*cli.Command{
jwtNewCmd,
jwtTokenCmd,
},
}
var jwtTokenCmd = &cli.Command{
Name: "token",
Usage: "create a token for a given jwt secret",
ArgsUsage: "<name>",
Description: `The jwt tokens have four different levels of permissions that provide some ability
to control access to what methods can be invoked by the holder of the token.
This command only works on jwt secrets that are base16 encoded files, such as those produced by the
sibling 'new' command.
`,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "output",
Value: "token",
Usage: "specify a name",
},
&cli.BoolFlag{
Name: "read",
Value: false,
Usage: "add read permissions to the token",
},
&cli.BoolFlag{
Name: "write",
Value: false,
Usage: "add write permissions to the token",
},
&cli.BoolFlag{
Name: "sign",
Value: false,
Usage: "add sign permissions to the token",
},
&cli.BoolFlag{
Name: "admin",
Value: false,
Usage: "add admin permissions to the token",
},
},
Action: func(cctx *cli.Context) error {
if !cctx.Args().Present() {
return fmt.Errorf("please specify a name")
}
inputFile, err := os.Open(cctx.Args().First())
if err != nil {
return err
}
defer inputFile.Close() //nolint:errcheck
input := bufio.NewReader(inputFile)
encoded, err := ioutil.ReadAll(input)
if err != nil {
return err
}
decoded, err := hex.DecodeString(strings.TrimSpace(string(encoded)))
if err != nil {
return err
}
var keyInfo types.KeyInfo
if err := json.Unmarshal(decoded, &keyInfo); err != nil {
return err
}
perms := []auth.Permission{}
if cctx.Bool("read") {
perms = append(perms, apistruct.PermRead)
}
if cctx.Bool("write") {
perms = append(perms, apistruct.PermWrite)
}
if cctx.Bool("sign") {
perms = append(perms, apistruct.PermSign)
}
if cctx.Bool("admin") {
perms = append(perms, apistruct.PermAdmin)
}
p := modules.JwtPayload{
Allow: perms,
}
token, err := jwt.Sign(&p, jwt.NewHS256(keyInfo.PrivateKey))
if err != nil {
return err
}
return ioutil.WriteFile(cctx.String("output"), token, 0600)
},
}

View File

@ -9,16 +9,22 @@ import (
"io"
"io/ioutil"
"os"
"path"
"strings"
"text/template"
"github.com/urfave/cli/v2"
"golang.org/x/xerrors"
"github.com/multiformats/go-base32"
"github.com/libp2p/go-libp2p-core/crypto"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet"
"github.com/filecoin-project/lotus/node/modules"
"github.com/filecoin-project/lotus/node/modules/lp2p"
"github.com/filecoin-project/lotus/node/repo"
@ -43,6 +49,90 @@ var keyinfoCmd = &cli.Command{
keyinfoNewCmd,
keyinfoInfoCmd,
keyinfoImportCmd,
keyinfoVerifyCmd,
},
}
var keyinfoVerifyCmd = &cli.Command{
Name: "verify",
Usage: "verify the filename of a keystore object on disk with it's contents",
Description: `Keystore objects are base32 enocded strings, with wallets being dynamically named via
the wallet address. This command can ensure that the naming of these keystore objects are correct`,
Action: func(cctx *cli.Context) error {
filePath := cctx.Args().First()
fileName := path.Base(filePath)
inputFile, err := os.Open(filePath)
if err != nil {
return err
}
defer inputFile.Close() //nolint:errcheck
input := bufio.NewReader(inputFile)
keyContent, err := ioutil.ReadAll(input)
if err != nil {
return err
}
var keyInfo types.KeyInfo
if err := json.Unmarshal(keyContent, &keyInfo); err != nil {
return err
}
switch keyInfo.Type {
case lp2p.KTLibp2pHost:
name, err := base32.RawStdEncoding.DecodeString(fileName)
if err != nil {
return xerrors.Errorf("decoding key: '%s': %w", fileName, err)
}
if string(name) != keyInfo.Type {
return fmt.Errorf("%s of type %s is incorrect", fileName, keyInfo.Type)
}
case modules.KTJwtHmacSecret:
name, err := base32.RawStdEncoding.DecodeString(fileName)
if err != nil {
return xerrors.Errorf("decoding key: '%s': %w", fileName, err)
}
if string(name) != modules.JWTSecretName {
return fmt.Errorf("%s of type %s is incorrect", fileName, keyInfo.Type)
}
case wallet.KTSecp256k1, wallet.KTBLS:
keystore := wallet.NewMemKeyStore()
w, err := wallet.NewWallet(keystore)
if err != nil {
return err
}
if _, err := w.Import(&keyInfo); err != nil {
return err
}
list, err := keystore.List()
if err != nil {
return err
}
if len(list) != 1 {
return fmt.Errorf("Unexpected number of keys, expected 1, found %d", len(list))
}
name, err := base32.RawStdEncoding.DecodeString(fileName)
if err != nil {
return xerrors.Errorf("decoding key: '%s': %w", fileName, err)
}
if string(name) != list[0] {
return fmt.Errorf("%s of type %s; file is named for %s, but key is actually %s", fileName, keyInfo.Type, string(name), list[0])
}
break
default:
return fmt.Errorf("Unknown keytype %s", keyInfo.Type)
}
return nil
},
}