Implement generate-auth-secret on beacon node CLI (#10733)

* s

* s

* typo

* typo

* s

* s

* fixes based on PR feedback

* PR feedback

* reverting log changes

* adding flag per feedback

* conventions

* main fixes

* Update cmd/beacon-chain/jwt/jwt.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update cmd/beacon-chain/jwt/jwt.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* Update cmd/flags.go

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>

* s

* tests

* test attempt

* test

* Update cmd/beacon-chain/jwt/jwt.go

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>

* err fix

* s

* further simplify

* cleanup

* namefix

* tests pass

* gaz

* rem deadcode

* Gaz

* shorthand

* naming

* test pass

* dedup

* success

* Ignore jwt.hex file

* logrus

* feedback

* junk

* Also check that no file was written

* local run config

* small fix

* jwt

* testfix

* s

* disabling test

* reverting main changes

* main revert

* removing temp folder

* comment

* gaz

* clarity

* rem

Co-authored-by: Raul Jordan <raul@prysmaticlabs.com>
Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
This commit is contained in:
mick
2022-06-07 03:37:12 -04:00
committed by GitHub
parent dd65622441
commit cc1ea81d4a
5 changed files with 196 additions and 1 deletions

3
.gitignore vendored
View File

@@ -35,3 +35,6 @@ bin
# p2p metaData
metaData
# execution API authentication
jwt.hex

View File

@@ -0,0 +1,29 @@
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["jwt.go"],
importpath = "github.com/prysmaticlabs/prysm/cmd/beacon-chain/jwt",
visibility = ["//visibility:public"],
deps = [
"//cmd:go_default_library",
"//crypto/rand:go_default_library",
"//io/file:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["jwt_test.go"],
embed = [":go_default_library"],
deps = [
"//cmd:go_default_library",
"//io/file:go_default_library",
"//testing/require:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",
],
)

View File

@@ -0,0 +1,71 @@
package jwt
import (
"errors"
"path/filepath"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/prysmaticlabs/prysm/cmd"
"github.com/prysmaticlabs/prysm/crypto/rand"
"github.com/prysmaticlabs/prysm/io/file"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
)
const (
secretFileName = "jwt.hex"
)
var Commands = &cli.Command{
Name: "generate-auth-secret",
Usage: "creates a random, 32 byte hex string in a plaintext file to be used for authenticating JSON-RPC requests. If no --output-file flag is defined, the file will be created in the current working directory",
Description: `creates a random, 32 byte hex string in a plaintext file to be used for authenticating JSON-RPC requests. If no --output-file flag is defined, the file will be created in the current working directory`,
Flags: cmd.WrapFlags([]cli.Flag{
cmd.JwtOutputFileFlag,
}),
Action: generateAuthSecretInFile,
}
func generateAuthSecretInFile(c *cli.Context) error {
fileName := secretFileName
specifiedFilePath := c.String(cmd.JwtOutputFileFlag.Name)
if len(specifiedFilePath) > 0 {
fileName = specifiedFilePath
}
var err error
fileName, err = file.ExpandPath(fileName)
if err != nil {
return err
}
fileDir := filepath.Dir(fileName)
exists, err := file.HasDir(fileDir)
if err != nil {
return err
}
if !exists {
if err := file.MkdirAll(fileDir); err != nil {
return err
}
}
secret, err := generateRandomHexString()
if err != nil {
return err
}
if err := file.WriteFile(fileName, []byte(secret)); err != nil {
return err
}
logrus.Infof("Successfully wrote JSON-RPC authentication secret to file %s", fileName)
return nil
}
func generateRandomHexString() (string, error) {
secret := make([]byte, 32)
randGen := rand.NewGenerator()
n, err := randGen.Read(secret)
if err != nil {
return "", err
} else if n <= 0 {
return "", errors.New("rand: unexpected length")
}
return hexutil.Encode(secret), nil
}

View File

@@ -0,0 +1,86 @@
package jwt
import (
"flag"
"os"
"path/filepath"
"testing"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/prysmaticlabs/prysm/cmd"
"github.com/prysmaticlabs/prysm/io/file"
"github.com/prysmaticlabs/prysm/testing/require"
"github.com/urfave/cli/v2"
)
func Test_generateJWTSecret(t *testing.T) {
t.Run("command should be available", func(t *testing.T) {
generateJwtCommand := Commands
require.Equal(t, true, generateJwtCommand.Name == "generate-auth-secret")
})
t.Run("should create proper file in current directory", func(t *testing.T) {
require.NoError(t, os.RemoveAll(secretFileName))
t.Cleanup(func() {
require.NoError(t, os.RemoveAll(secretFileName))
})
app := cli.App{}
set := flag.NewFlagSet("test", 0)
cliCtx := cli.NewContext(&app, set, nil)
err := generateAuthSecretInFile(cliCtx)
require.NoError(t, err)
// We check the file has the contents we expect.
checkAuthFileIntegrity(t, secretFileName)
})
t.Run("should create proper file in specified folder", func(t *testing.T) {
customOutput := filepath.Join("data", "item.txt")
require.NoError(t, os.RemoveAll(filepath.Dir(customOutput)))
t.Cleanup(func() {
require.NoError(t, os.RemoveAll(filepath.Dir(customOutput)))
})
app := cli.App{}
set := flag.NewFlagSet("test", 0)
set.String(cmd.JwtOutputFileFlag.Name, customOutput, "")
require.NoError(t, set.Set(cmd.JwtOutputFileFlag.Name, customOutput))
cliCtx := cli.NewContext(&app, set, nil)
err := generateAuthSecretInFile(cliCtx)
require.NoError(t, err)
// We check the file has the contents we expect.
checkAuthFileIntegrity(t, customOutput)
})
t.Run("creates proper file in nested specified folder", func(t *testing.T) {
rootDirectory := "data"
customOutputPath := filepath.Join(rootDirectory, "nest", "nested", "item.txt")
require.NoError(t, os.RemoveAll(filepath.Dir(customOutputPath)))
t.Cleanup(func() {
require.NoError(t, os.RemoveAll(rootDirectory))
_, err := os.Stat(customOutputPath)
require.Equal(t, true, err != nil)
})
app := cli.App{}
set := flag.NewFlagSet("test", 0)
set.String(cmd.JwtOutputFileFlag.Name, customOutputPath, "")
require.NoError(t, set.Set(cmd.JwtOutputFileFlag.Name, customOutputPath))
cliCtx := cli.NewContext(&app, set, nil)
err := generateAuthSecretInFile(cliCtx)
require.NoError(t, err)
// We check the file has the contents we expect.
checkAuthFileIntegrity(t, customOutputPath)
})
}
func checkAuthFileIntegrity(t testing.TB, fPath string) {
fileInfo, err := os.Stat(fPath)
require.NoError(t, err)
require.Equal(t, true, fileInfo != nil)
enc, err := file.ReadFileAsBytes(fPath)
require.NoError(t, err)
decoded, err := hexutil.Decode(string(enc))
require.NoError(t, err)
require.Equal(t, 32, len(decoded))
}

View File

@@ -1,4 +1,4 @@
// Package cmd defines the command line flags for the shared utlities.
// Package cmd defines the command line flags for the shared utilities.
package cmd
import (
@@ -255,6 +255,12 @@ var (
Usage: "Specifies the timeout value for API requests in seconds",
Value: 120,
}
// JwtOutputFileFlag specifies the JWT file path that gets generated into when invoked by generate-jwt-secret.
JwtOutputFileFlag = &cli.StringFlag{
Name: "output-file",
Usage: "Target file path for outputting a generated JWT secret to be used for JSON-RPC authentication",
Aliases: []string{"o"},
}
)
// LoadFlagsFromConfig sets flags values from config file if ConfigFileFlag is set.