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

feat: add api key for authz #31

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ build:
.PHONY: start
start:
make build
./bin/$(APP_NAME) --log-level=debug
./bin/$(APP_NAME) --log-level=debug --enable-admin

.PHONY: fmt
fmt: ## formats all go files
Expand Down Expand Up @@ -41,3 +41,8 @@ docker: ## runs docker build
migrate: ## runs database migrations
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
migrate -path internal/database/migrations/ -database "postgres://user:password@localhost:5432/cerberus?sslmode=disable" --verbose up

.PHONY: create-migration
create-migration: ## creates a new database migration
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
migrate create -dir internal/database/migrations/ -ext sql $(name)
32 changes: 26 additions & 6 deletions cmd/cerberus/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,34 @@ var (
EnvVars: []string{"KEYSTORE_DIR"},
}

grpcPortFlag = &cli.StringFlag{
grpcPortFlag = &cli.IntFlag{
Name: "grpc-port",
Usage: "Port for the gRPC server",
Value: "50051",
Value: 50051,
EnvVars: []string{"GRPC_PORT"},
}

metricsPortFlag = &cli.StringFlag{
adminPortFlag = &cli.IntFlag{
Name: "admin-port",
Usage: "Port for the admin server",
Value: 50052,
EnvVars: []string{"ADMIN_PORT"},
}

metricsPortFlag = &cli.IntFlag{
Name: "metrics-port",
Usage: "Port for the metrics server",
Value: "9091",
Value: 9091,
EnvVars: []string{"METRICS_PORT"},
}

enableAdminFlag = &cli.BoolFlag{
Name: "enable-admin",
Usage: "Enable the admin server",
Value: false,
EnvVars: []string{"ENABLE_ADMIN"},
}

logLevelFlag = &cli.StringFlag{
Name: "log-level",
Usage: "Log level - supported levels: debug, info, warn, error",
Expand Down Expand Up @@ -142,6 +156,7 @@ func main() {
logFormatFlag,
logLevelFlag,
metricsPortFlag,
enableAdminFlag,
tlsCaCertFlag,
tlsServerKeyFlag,
storageTypeFlag,
Expand All @@ -152,6 +167,7 @@ func main() {
awsSecretAccessKeyFlag,
gcpProjectIDFlag,
postgresDatabaseURLFlag,
adminPortFlag,
}
sort.Sort(cli.FlagsByName(app.Flags))

Expand All @@ -168,8 +184,9 @@ func main() {

func start(c *cli.Context) error {
keystoreDir := c.String(keystoreDirFlag.Name)
grpcPort := c.String(grpcPortFlag.Name)
metricsPort := c.String(metricsPortFlag.Name)
grpcPort := c.Int(grpcPortFlag.Name)
adminPort := c.Int(adminPortFlag.Name)
metricsPort := c.Int(metricsPortFlag.Name)
logLevel := c.String(logLevelFlag.Name)
logFormat := c.String(logFormatFlag.Name)
tlsCaCert := c.String(tlsCaCertFlag.Name)
Expand All @@ -182,9 +199,11 @@ func start(c *cli.Context) error {
awsSecretAccessKey := c.String(awsSecretAccessKeyFlag.Name)
gcpProjectID := c.String(gcpProjectIDFlag.Name)
postgresDatabaseURL := c.String(postgresDatabaseURLFlag.Name)
enableAdmin := c.Bool(enableAdminFlag.Name)
cfg := &configuration.Configuration{
KeystoreDir: keystoreDir,
GrpcPort: grpcPort,
AdminPort: adminPort,
MetricsPort: metricsPort,
TLSCACert: tlsCaCert,
TLSServerKey: tlsServerKey,
Expand All @@ -196,6 +215,7 @@ func start(c *cli.Context) error {
AWSSecretAccessKey: awsSecretAccessKey,
GCPProjectID: gcpProjectID,
PostgresDatabaseURL: postgresDatabaseURL,
EnableAdmin: enableAdmin,
}

if err := cfg.Validate(); err != nil {
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ toolchain go1.22.3
require (
cloud.google.com/go/secretmanager v1.14.2
github.com/Layr-Labs/bn254-keystore-go v0.0.0-20250107020618-26bd412fae87
github.com/Layr-Labs/cerberus-api v0.0.2-0.20250108174619-d5e1eb03fbd5
github.com/Layr-Labs/cerberus-api v0.0.2-0.20250117015901-0b1220ea735f
github.com/aws/aws-sdk-go-v2 v1.32.5
github.com/aws/aws-sdk-go-v2/config v1.28.5
github.com/aws/aws-sdk-go-v2/credentials v1.17.46
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.6
github.com/consensys/gnark-crypto v0.12.1
github.com/golang-migrate/migrate/v4 v4.18.1
github.com/google/uuid v1.6.0
github.com/prometheus/client_golang v1.20.3
github.com/stretchr/testify v1.10.0
github.com/testcontainers/testcontainers-go v0.34.0
Expand All @@ -37,7 +38,6 @@ require (
github.com/docker/go-units v0.5.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg6
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Layr-Labs/bn254-keystore-go v0.0.0-20250107020618-26bd412fae87 h1:EkaBNT0o8RTgtFeYSKaoNHNbnCVxrcsAyRpUeN29hiQ=
github.com/Layr-Labs/bn254-keystore-go v0.0.0-20250107020618-26bd412fae87/go.mod h1:7J8hptSX8cFq7KmVb+rEO5aEifj7E44c3i0afIyr4WA=
github.com/Layr-Labs/cerberus-api v0.0.2-0.20250108174619-d5e1eb03fbd5 h1:s24M6HYObEuV9OSY36jUM09kp5fOhuz/g1ev2qWDPzU=
github.com/Layr-Labs/cerberus-api v0.0.2-0.20250108174619-d5e1eb03fbd5/go.mod h1:Lm4fhzy0S3P7GjerzuseGaBFVczsIKmEhIjcT52Hluo=
github.com/Layr-Labs/cerberus-api v0.0.2-0.20250117015901-0b1220ea735f h1:Od50IBPPjsAF9w8QGUsTFcFwmKA0UEH9zNtUN0PDM68=
github.com/Layr-Labs/cerberus-api v0.0.2-0.20250117015901-0b1220ea735f/go.mod h1:Lm4fhzy0S3P7GjerzuseGaBFVczsIKmEhIjcT52Hluo=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/aws/aws-sdk-go-v2 v1.32.5 h1:U8vdWJuY7ruAkzaOdD7guwJjD06YSKmnKCJs7s3IkIo=
Expand Down
23 changes: 23 additions & 0 deletions internal/common/common.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
package common

import (
"crypto/sha256"
"encoding/hex"

"github.com/google/uuid"
)

func Trim0x(s string) string {
if len(s) >= 2 && s[0:2] == "0x" {
return s[2:]
Expand All @@ -13,3 +20,19 @@ func RemovePrefix(s string, prefix string) string {
}
return s
}

func CreateSHA256Hash(s string) string {
hash := sha256.New()
hash.Write([]byte(s))
return hex.EncodeToString(hash.Sum(nil))
}

func GenerateNewAPIKeyAndHash() (string, string, error) {
newUUID, err := uuid.NewV7()
if err != nil {
return "", "", err
}
apiKey := newUUID.String()
apiKeyHash := CreateSHA256Hash(apiKey)
return apiKey, apiKeyHash, nil
}
11 changes: 7 additions & 4 deletions internal/configuration/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@ type Configuration struct {
// Google Secrets Manager storage parameters
GCPProjectID string

GrpcPort string
MetricsPort string
GrpcPort int
MetricsPort int
AdminPort int

EnableAdmin bool

TLSCACert string
TLSServerKey string
Expand Down Expand Up @@ -72,11 +75,11 @@ func (s *Configuration) Validate() error {
return fmt.Errorf("unsupported storage type: %s", s.StorageType)
}

if s.GrpcPort == "" {
if s.GrpcPort == 0 {
return fmt.Errorf("gRPC port is required")
}

if s.MetricsPort == "" {
if s.MetricsPort == 0 {
return fmt.Errorf("metrics port is required")
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE public.keys_metadata ADD COLUMN api_key_hash text;
ALTER TABLE public.keys_metadata ADD COLUMN locked boolean DEFAULT false;
2 changes: 2 additions & 0 deletions internal/database/model/key_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ type KeyMetadata struct {
PublicKeyG2 string `db:"public_key_g2"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
ApiKeyHash string `db:"api_key_hash"`
Locked bool `db:"locked"`
}
2 changes: 2 additions & 0 deletions internal/database/repository/key_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ type KeyMetadataRepository interface {
Create(ctx context.Context, metadata *model.KeyMetadata) error
Get(ctx context.Context, publicKeyG1 string) (*model.KeyMetadata, error)
Update(ctx context.Context, metadata *model.KeyMetadata) error
UpdateAPIKeyHash(ctx context.Context, publicKeyG1 string, apiKeyHash string) error
UpdateLockStatus(ctx context.Context, publicKeyG1 string, locked bool) error
Delete(ctx context.Context, publicKeyG1 string) error
List(ctx context.Context) ([]*model.KeyMetadata, error)
}
66 changes: 60 additions & 6 deletions internal/database/repository/postgres/key_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ func NewKeyMetadataRepository(db *sql.DB) repository.KeyMetadataRepository {
const (
createKeyMetadataQuery = `
INSERT INTO public.keys_metadata (
public_key_g1, public_key_g2, created_at, updated_at
) VALUES ($1, $2, $3, $4)
public_key_g1, public_key_g2, created_at, updated_at, api_key_hash
) VALUES ($1, $2, $3, $4, $5)
`

getKeyMetadataQuery = `
SELECT public_key_g1, public_key_g2, created_at, updated_at
SELECT public_key_g1, public_key_g2, created_at, updated_at, api_key_hash, locked
FROM public.keys_metadata
WHERE public_key_g1 = $1
`
Expand All @@ -39,13 +39,25 @@ const (
WHERE public_key_g1 = $2
`

updateAPIKeyHashQuery = `
UPDATE public.keys_metadata
SET api_key_hash = $1, updated_at = $2
WHERE public_key_g1 = $3
`

updateLockStatusQuery = `
UPDATE public.keys_metadata
SET locked = $1, updated_at = $2
WHERE public_key_g1 = $3
`

deleteKeyMetadataQuery = `
DELETE FROM public.keys_metadata
WHERE public_key_g1 = $1
`

listKeyMetadataQuery = `
SELECT public_key_g1, public_key_g2, created_at, updated_at
listAllKeysQuery = `
SELECT public_key_g1, public_key_g2, created_at, updated_at, locked
FROM public.keys_metadata
ORDER BY created_at DESC
`
Expand All @@ -68,6 +80,7 @@ func (r *keyMetadataRepo) Create(ctx context.Context, metadata *model.KeyMetadat
metadata.PublicKeyG2,
metadata.CreatedAt,
metadata.UpdatedAt,
metadata.ApiKeyHash,
)
return err
}
Expand All @@ -79,6 +92,8 @@ func (r *keyMetadataRepo) Get(ctx context.Context, publicKeyG1 string) (*model.K
&metadata.PublicKeyG2,
&metadata.CreatedAt,
&metadata.UpdatedAt,
&metadata.ApiKeyHash,
&metadata.Locked,
)
if err == sql.ErrNoRows {
return nil, errors.New("key metadata not found")
Expand Down Expand Up @@ -113,6 +128,29 @@ func (r *keyMetadataRepo) Update(ctx context.Context, metadata *model.KeyMetadat
return nil
}

func (r *keyMetadataRepo) UpdateAPIKeyHash(
ctx context.Context,
publicKeyG1 string,
apiKeyHash string,
) error {
if publicKeyG1 == "" {
return errors.New("public key g1 is required")
}

if apiKeyHash == "" {
return errors.New("api key hash is required")
}

updatedAt := time.Now().UTC()

_, err := r.db.ExecContext(ctx, updateAPIKeyHashQuery,
apiKeyHash,
updatedAt,
publicKeyG1,
)
return err
}

func (r *keyMetadataRepo) Delete(ctx context.Context, publicKeyG1 string) error {
if publicKeyG1 == "" {
return errors.New("public key g1 is required")
Expand All @@ -134,7 +172,7 @@ func (r *keyMetadataRepo) Delete(ctx context.Context, publicKeyG1 string) error
}

func (r *keyMetadataRepo) List(ctx context.Context) ([]*model.KeyMetadata, error) {
rows, err := r.db.QueryContext(ctx, listKeyMetadataQuery)
rows, err := r.db.QueryContext(ctx, listAllKeysQuery)
if err != nil {
return nil, err
}
Expand All @@ -148,6 +186,7 @@ func (r *keyMetadataRepo) List(ctx context.Context) ([]*model.KeyMetadata, error
&m.PublicKeyG2,
&m.CreatedAt,
&m.UpdatedAt,
&m.Locked,
)
if err != nil {
return nil, err
Expand All @@ -160,3 +199,18 @@ func (r *keyMetadataRepo) List(ctx context.Context) ([]*model.KeyMetadata, error
}
return metadata, nil
}

func (r *keyMetadataRepo) UpdateLockStatus(
ctx context.Context,
publicKeyG1 string,
locked bool,
) error {
updatedAt := time.Now().UTC()

_, err := r.db.ExecContext(ctx, updateLockStatusQuery,
locked,
updatedAt,
publicKeyG1,
)
return err
}
25 changes: 25 additions & 0 deletions internal/database/repository/postgres/key_metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,28 @@ func TestKeyMetadataRepository_List(t *testing.T) {
assert.Equal(t, "test_key_1", results[1].PublicKeyG1)
})
}

func TestKeyMetadataRepository_UpdateAPIKeyHash(t *testing.T) {
testDB := SetupTestDB(t)
// No need to defer db.Close() as it's handled by t.Cleanup

// Create initial test data
initialKey := &model.KeyMetadata{
PublicKeyG1: "test_key_1",
PublicKeyG2: "test_key_2",
ApiKeyHash: "test_api_key_hash",
}
err := testDB.Repo.Create(context.Background(), initialKey)
require.NoError(t, err)

apiKeyHash := "test_api_key_hash_2"

err = testDB.Repo.UpdateAPIKeyHash(context.Background(), initialKey.PublicKeyG1, apiKeyHash)
require.NoError(t, err)

// Verify the update
result, err := testDB.Repo.Get(context.Background(), initialKey.PublicKeyG1)
assert.NoError(t, err)
assert.Equal(t, apiKeyHash, result.ApiKeyHash)
assert.WithinDuration(t, time.Now(), result.UpdatedAt, 2*time.Second)
}
4 changes: 3 additions & 1 deletion internal/database/repository/postgres/test_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ func CreateTestContainer(t *testing.T) (*TestContainer, error) {
public_key_g1 VARCHAR(255) PRIMARY KEY,
public_key_g2 VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
api_key_hash text,
locked boolean DEFAULT false
);
`)
if err != nil {
Expand Down
6 changes: 4 additions & 2 deletions internal/database/sql/cerberus.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ CREATE TABLE IF NOT EXISTS public.keys_metadata (
public_key_g1 VARCHAR(255) PRIMARY KEY,
public_key_g2 VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
api_key_hash text,
locked boolean DEFAULT false
);
Loading
Loading