From 47608c19378630c155466a438104b009e776b302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 31 May 2021 13:38:14 +0200 Subject: [PATCH 1/3] wallet: Handle jwt headers --- cmd/lotus-wallet/main.go | 128 ++++++++++++++++++++++++++++++--------- 1 file changed, 101 insertions(+), 27 deletions(-) diff --git a/cmd/lotus-wallet/main.go b/cmd/lotus-wallet/main.go index 2c86c6180..d6ca41c24 100644 --- a/cmd/lotus-wallet/main.go +++ b/cmd/lotus-wallet/main.go @@ -2,27 +2,33 @@ package main import ( "context" + "fmt" "net" "net/http" "os" "github.com/filecoin-project/lotus/api/v0api" + "github.com/gbrlsnchs/jwt/v3" "github.com/gorilla/mux" logging "github.com/ipfs/go-log/v2" "github.com/urfave/cli/v2" "go.opencensus.io/stats/view" "go.opencensus.io/tag" + "golang.org/x/xerrors" "github.com/filecoin-project/go-jsonrpc" + "github.com/filecoin-project/go-jsonrpc/auth" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/wallet" ledgerwallet "github.com/filecoin-project/lotus/chain/wallet/ledger" lcli "github.com/filecoin-project/lotus/cli" "github.com/filecoin-project/lotus/lib/lotuslog" "github.com/filecoin-project/lotus/metrics" + "github.com/filecoin-project/lotus/node/modules" "github.com/filecoin-project/lotus/node/repo" ) @@ -30,11 +36,16 @@ var log = logging.Logger("main") const FlagWalletRepo = "wallet-repo" +type jwtPayload struct { + Allow []auth.Permission +} + func main() { lotuslog.SetupLogLevels() local := []*cli.Command{ runCmd, + getApiKeyCmd, } app := &cli.App{ @@ -65,6 +76,35 @@ func main() { } } +var getApiKeyCmd = &cli.Command{ + Name: "get-api-key", + Usage: "Print API Key", + Action: func(cctx *cli.Context) error { + lr, ks, err := openRepo(cctx) + if err != nil { + return err + } + defer lr.Close() // nolint + + p := jwtPayload{ + Allow: []auth.Permission{api.PermAdmin}, + } + + authKey, err := modules.APISecret(ks, lr) + if err != nil { + return xerrors.Errorf("setting up api secret: %w", err) + } + + k, err := jwt.Sign(&p, (*jwt.HMACSHA)(authKey)) + if err != nil { + return xerrors.Errorf("jwt sign: %w", err) + } + + fmt.Println(string(k)) + return nil + }, +} + var runCmd = &cli.Command{ Name: "run", Usage: "Start lotus wallet", @@ -86,6 +126,11 @@ var runCmd = &cli.Command{ Name: "offline", Usage: "don't query chain state in interactive mode", }, + &cli.BoolFlag{ + Name: "disable-auth", + Usage: "(insecure) disable api auth", + Hidden: true, + }, }, Action: func(cctx *cli.Context) error { log.Info("Starting lotus wallet") @@ -101,31 +146,11 @@ var runCmd = &cli.Command{ log.Fatalf("Cannot register the view: %v", err) } - repoPath := cctx.String(FlagWalletRepo) - r, err := repo.NewFS(repoPath) - if err != nil { - return err - } - - ok, err := r.Exists() - if err != nil { - return err - } - if !ok { - if err := r.Init(repo.Worker); err != nil { - return err - } - } - - lr, err := r.Lock(repo.Wallet) - if err != nil { - return err - } - - ks, err := lr.KeyStore() + lr, ks, err := openRepo(cctx) if err != nil { return err } + defer lr.Close() // nolint lw, err := wallet.NewWallet(ks) if err != nil { @@ -173,13 +198,32 @@ var runCmd = &cli.Command{ mux.Handle("/rpc/v0", rpcServer) mux.PathPrefix("/").Handler(http.DefaultServeMux) // pprof - /*ah := &auth.Handler{ - Verify: nodeApi.AuthVerify, - Next: mux.ServeHTTP, - }*/ + var handler http.Handler = mux + + if cctx.Bool("disable-auth") { + log.Info("API auth enabled, use 'lotus wallet get-api-key' to get API key") + authKey, err := modules.APISecret(ks, lr) + if err != nil { + return xerrors.Errorf("setting up api secret: %w", err) + } + + authVerify := func(ctx context.Context, token string) ([]auth.Permission, error) { + var payload jwtPayload + if _, err := jwt.Verify([]byte(token), (*jwt.HMACSHA)(authKey), &payload); err != nil { + return nil, xerrors.Errorf("JWT Verification failed: %w", err) + } + + return payload.Allow, nil + } + + handler = &auth.Handler{ + Verify: authVerify, + Next: mux.ServeHTTP, + } + } srv := &http.Server{ - Handler: mux, + Handler: handler, BaseContext: func(listener net.Listener) context.Context { ctx, _ := tag.New(context.Background(), tag.Upsert(metrics.APIInterface, "lotus-wallet")) return ctx @@ -203,3 +247,33 @@ var runCmd = &cli.Command{ return srv.Serve(nl) }, } + +func openRepo(cctx *cli.Context) (repo.LockedRepo, types.KeyStore ,error) { + repoPath := cctx.String(FlagWalletRepo) + r, err := repo.NewFS(repoPath) + if err != nil { + return nil, nil, err + } + + ok, err := r.Exists() + if err != nil { + return nil, nil, err + } + if !ok { + if err := r.Init(repo.Worker); err != nil { + return nil, nil, err + } + } + + lr, err := r.Lock(repo.Wallet) + if err != nil { + return nil, nil, err + } + + ks, err := lr.KeyStore() + if err != nil { + return nil, nil, err + } + + return lr, ks, nil +} From 7d1ae7daf5138f80a984e1a90ef778831d9a0060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 31 May 2021 13:44:15 +0200 Subject: [PATCH 2/3] lotus-wallet: Support permissioned api --- api/api_wallet.go | 14 +++++++------- api/proxy_gen.go | 14 +++++++------- cmd/lotus-wallet/main.go | 17 +++++++++++------ 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/api/api_wallet.go b/api/api_wallet.go index 891b2fabb..973aaaf6d 100644 --- a/api/api_wallet.go +++ b/api/api_wallet.go @@ -35,13 +35,13 @@ type MsgMeta struct { } type Wallet interface { - WalletNew(context.Context, types.KeyType) (address.Address, error) - WalletHas(context.Context, address.Address) (bool, error) - WalletList(context.Context) ([]address.Address, error) + WalletNew(context.Context, types.KeyType) (address.Address, error) //perm:admin + WalletHas(context.Context, address.Address) (bool, error) //perm:admin + WalletList(context.Context) ([]address.Address, error) //perm:admin - WalletSign(ctx context.Context, signer address.Address, toSign []byte, meta MsgMeta) (*crypto.Signature, error) + WalletSign(ctx context.Context, signer address.Address, toSign []byte, meta MsgMeta) (*crypto.Signature, error) //perm:admin - WalletExport(context.Context, address.Address) (*types.KeyInfo, error) - WalletImport(context.Context, *types.KeyInfo) (address.Address, error) - WalletDelete(context.Context, address.Address) error + WalletExport(context.Context, address.Address) (*types.KeyInfo, error) //perm:admin + WalletImport(context.Context, *types.KeyInfo) (address.Address, error) //perm:admin + WalletDelete(context.Context, address.Address) error //perm:admin } diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 8880fb24c..79d7b0ac6 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -731,19 +731,19 @@ type StorageMinerStub struct { type WalletStruct struct { Internal struct { - WalletDelete func(p0 context.Context, p1 address.Address) error `` + WalletDelete func(p0 context.Context, p1 address.Address) error `perm:"admin"` - WalletExport func(p0 context.Context, p1 address.Address) (*types.KeyInfo, error) `` + WalletExport func(p0 context.Context, p1 address.Address) (*types.KeyInfo, error) `perm:"admin"` - WalletHas func(p0 context.Context, p1 address.Address) (bool, error) `` + WalletHas func(p0 context.Context, p1 address.Address) (bool, error) `perm:"admin"` - WalletImport func(p0 context.Context, p1 *types.KeyInfo) (address.Address, error) `` + WalletImport func(p0 context.Context, p1 *types.KeyInfo) (address.Address, error) `perm:"admin"` - WalletList func(p0 context.Context) ([]address.Address, error) `` + WalletList func(p0 context.Context) ([]address.Address, error) `perm:"admin"` - WalletNew func(p0 context.Context, p1 types.KeyType) (address.Address, error) `` + WalletNew func(p0 context.Context, p1 types.KeyType) (address.Address, error) `perm:"admin"` - WalletSign func(p0 context.Context, p1 address.Address, p2 []byte, p3 MsgMeta) (*crypto.Signature, error) `` + WalletSign func(p0 context.Context, p1 address.Address, p2 []byte, p3 MsgMeta) (*crypto.Signature, error) `perm:"admin"` } } diff --git a/cmd/lotus-wallet/main.go b/cmd/lotus-wallet/main.go index d6ca41c24..4e0f2a577 100644 --- a/cmd/lotus-wallet/main.go +++ b/cmd/lotus-wallet/main.go @@ -127,8 +127,8 @@ var runCmd = &cli.Command{ Usage: "don't query chain state in interactive mode", }, &cli.BoolFlag{ - Name: "disable-auth", - Usage: "(insecure) disable api auth", + Name: "disable-auth", + Usage: "(insecure) disable api auth", Hidden: true, }, }, @@ -192,16 +192,20 @@ var runCmd = &cli.Command{ w = &LoggedWallet{under: w} } + rpcApi := metrics.MetricedWalletAPI(w) + if !cctx.Bool("disable-auth") { + rpcApi = api.PermissionedWalletAPI(rpcApi) + } + rpcServer := jsonrpc.NewServer() - rpcServer.Register("Filecoin", metrics.MetricedWalletAPI(w)) + rpcServer.Register("Filecoin", rpcApi) mux.Handle("/rpc/v0", rpcServer) mux.PathPrefix("/").Handler(http.DefaultServeMux) // pprof var handler http.Handler = mux - if cctx.Bool("disable-auth") { - log.Info("API auth enabled, use 'lotus wallet get-api-key' to get API key") + if !cctx.Bool("disable-auth") { authKey, err := modules.APISecret(ks, lr) if err != nil { return xerrors.Errorf("setting up api secret: %w", err) @@ -216,6 +220,7 @@ var runCmd = &cli.Command{ return payload.Allow, nil } + log.Info("API auth enabled, use 'lotus-wallet get-api-key' to get API key") handler = &auth.Handler{ Verify: authVerify, Next: mux.ServeHTTP, @@ -248,7 +253,7 @@ var runCmd = &cli.Command{ }, } -func openRepo(cctx *cli.Context) (repo.LockedRepo, types.KeyStore ,error) { +func openRepo(cctx *cli.Context) (repo.LockedRepo, types.KeyStore, error) { repoPath := cctx.String(FlagWalletRepo) r, err := repo.NewFS(repoPath) if err != nil { From 937366f04af70351f3c43e3010934adb78aac9e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 31 May 2021 14:33:54 +0200 Subject: [PATCH 3/3] lotus-wallet: Describe setup with auth --- cmd/lotus-wallet/main.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/cmd/lotus-wallet/main.go b/cmd/lotus-wallet/main.go index 4e0f2a577..3e3aa1a58 100644 --- a/cmd/lotus-wallet/main.go +++ b/cmd/lotus-wallet/main.go @@ -52,6 +52,17 @@ func main() { Name: "lotus-wallet", Usage: "Basic external wallet", Version: build.UserVersion(), + Description: ` +lotus-wallet provides a remote wallet service for lotus. + +To configure your lotus node to use a remote wallet: +* Run 'lotus-wallet get-api-key' to generate API key +* Start lotus-wallet using 'lotus-wallet run' (see --help for additional flags) +* Edit lotus config (~/.lotus/config.toml) + * Find the '[Wallet]' section + * Set 'RemoteBackend' to '[api key]:http://[wallet ip]:[wallet port]' + (the default port is 1777) +* Start (or restart) the lotus daemon`, Flags: []cli.Flag{ &cli.StringFlag{ Name: FlagWalletRepo, @@ -78,7 +89,7 @@ func main() { var getApiKeyCmd = &cli.Command{ Name: "get-api-key", - Usage: "Print API Key", + Usage: "Generate API Key", Action: func(cctx *cli.Context) error { lr, ks, err := openRepo(cctx) if err != nil { @@ -132,6 +143,7 @@ var runCmd = &cli.Command{ Hidden: true, }, }, + Description: "For setup instructions see 'lotus-wallet --help'", Action: func(cctx *cli.Context) error { log.Info("Starting lotus wallet")