Skip to content

Commit

Permalink
Merge pull request #3914 from filecoin-project/feat/lotus-shed-more
Browse files Browse the repository at this point in the history
Add keyinfo verify and jwt token command to lotus-shed
  • Loading branch information
magik6k authored Sep 18, 2020
2 parents 0cd45ab + ac7007d commit 0dfd4f3
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 0 deletions.
99 changes: 99 additions & 0 deletions cmd/lotus-shed/jwt.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
package main

import (
"bufio"
"crypto/rand"
"encoding/hex"
"encoding/json"
"fmt"
"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"
Expand All @@ -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)
},
}

Expand Down
90 changes: 90 additions & 0 deletions cmd/lotus-shed/keyinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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
},
}

Expand Down

0 comments on commit 0dfd4f3

Please sign in to comment.