Commit d3a50acf authored by Axel Kingsley's avatar Axel Kingsley Committed by GitHub

fix: First Time JWT Generation Bug (#13431)

* fix first-time JWT generation bug

* op-service: improve and test JWT secret loading code

---------
Co-authored-by: default avatarprotolambda <proto@protolambda.com>
parent 284913be
...@@ -32,20 +32,29 @@ func ObtainJWTSecret(logger log.Logger, jwtSecretPath string, generateMissing bo ...@@ -32,20 +32,29 @@ func ObtainJWTSecret(logger log.Logger, jwtSecretPath string, generateMissing bo
return eth.Bytes32{}, fmt.Errorf("JWT-secret in path %q does not exist: %w", jwtSecretPath, err) return eth.Bytes32{}, fmt.Errorf("JWT-secret in path %q does not exist: %w", jwtSecretPath, err)
} }
logger.Warn("Failed to read JWT secret from file, generating a new one now.", "path", jwtSecretPath) logger.Warn("Failed to read JWT secret from file, generating a new one now.", "path", jwtSecretPath)
var secret eth.Bytes32 return generateJWTSecret(jwtSecretPath)
if _, err := io.ReadFull(rand.Reader, secret[:]); err != nil {
return eth.Bytes32{}, fmt.Errorf("failed to generate jwt secret: %w", err)
}
if err := os.WriteFile(jwtSecretPath, []byte(hexutil.Encode(secret[:])), 0o600); err != nil {
return eth.Bytes32{}, err
}
} else { } else {
return eth.Bytes32{}, fmt.Errorf("failed to read JWT secret from file path %q", jwtSecretPath) return eth.Bytes32{}, fmt.Errorf("failed to read JWT secret from file path %q", jwtSecretPath)
} }
} }
// Parse the JWT secret data we just read
jwtSecret := common.FromHex(strings.TrimSpace(string(data))) // FromHex handles optional '0x' prefix jwtSecret := common.FromHex(strings.TrimSpace(string(data))) // FromHex handles optional '0x' prefix
if len(jwtSecret) != 32 { if len(jwtSecret) != 32 {
return eth.Bytes32{}, fmt.Errorf("invalid jwt secret in path %q, not 32 hex-formatted bytes", jwtSecretPath) return eth.Bytes32{}, fmt.Errorf("invalid jwt secret in path %q, not 32 hex-formatted bytes", jwtSecretPath)
} }
return eth.Bytes32(jwtSecret), nil return eth.Bytes32(jwtSecret), nil
} }
// generateJWTSecret generates a new JWT secret and writes it to the file at the given path.
// Prior status of the file is not checked, and the file is always overwritten.
// Callers should ensure the file does not exist, or that overwriting is acceptable.
func generateJWTSecret(path string) (eth.Bytes32, error) {
var secret eth.Bytes32
if _, err := io.ReadFull(rand.Reader, secret[:]); err != nil {
return eth.Bytes32{}, fmt.Errorf("failed to generate jwt secret: %w", err)
}
if err := os.WriteFile(path, []byte(hexutil.Encode(secret[:])), 0o600); err != nil {
return eth.Bytes32{}, err
}
return secret, nil
}
package rpc
import (
"io/fs"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-service/eth"
"github.com/ethereum-optimism/optimism/op-service/testlog"
)
func TestObtainJWTSecret(t *testing.T) {
testPath := t.TempDir()
logger := testlog.Logger(t, log.LvlInfo)
t.Run("no-generate", func(t *testing.T) {
secret, err := ObtainJWTSecret(logger, filepath.Join(testPath, "non_existent.txt"), false)
require.ErrorIs(t, err, fs.ErrNotExist, "secret does not exist")
require.Equal(t, eth.Bytes32{}, secret)
// Not generated, still not there
againSecret, err := ObtainJWTSecret(logger, filepath.Join(testPath, "non_existent.txt"), false)
require.ErrorIs(t, err, fs.ErrNotExist, "secret does not exist")
require.Equal(t, eth.Bytes32{}, againSecret)
})
t.Run("yes-generate", func(t *testing.T) {
secret, err := ObtainJWTSecret(logger, filepath.Join(testPath, "will_generate.txt"), true)
require.NoError(t, err)
require.NotEqual(t, eth.Bytes32{}, secret)
// it was generated, and should be there now
againSecret, err := ObtainJWTSecret(logger, filepath.Join(testPath, "will_generate.txt"), false)
require.NoError(t, err)
require.Equal(t, secret, againSecret, "read the secret that was persisted")
// now read again, but suggest generating it if missing. It's not missing, so shouldn't override
stillSameSecret, err := ObtainJWTSecret(logger, filepath.Join(testPath, "will_generate.txt"), true)
require.NoError(t, err)
require.Equal(t, secret, stillSameSecret)
})
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment