Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add keyinfo verify and jwt token command to lotus-shed #3914

Merged
merged 2 commits into from
Sep 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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