Skip to content

Commit

Permalink
feat: add API key check (#9)
Browse files Browse the repository at this point in the history
* feat: add API key check

* fix: make CA certificate optional

* fix: add `db` dir to gitignore
  • Loading branch information
hydra-yse authored Dec 17, 2024
1 parent 702add0 commit 420a90b
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
db
35 changes: 32 additions & 3 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,49 @@
package config

import (
"crypto/x509"
"encoding/pem"
"log"
"os"

"github.com/Netflix/go-env"
)

type Config struct {
GrpcListenAddress string `env:"GRPC_LISTEN_ADDRESS,default=0.0.0.0:8080"`
SQLiteDirPath string `env:"SQLITE_DIR_PATH,default=db"`
PgDatabaseUrl string `env:"DATABASE_URL"`
GrpcListenAddress string `env:"GRPC_LISTEN_ADDRESS,default=0.0.0.0:8080"`
SQLiteDirPath string `env:"SQLITE_DIR_PATH,default=db"`
PgDatabaseUrl string `env:"DATABASE_URL"`
CACertPath *string `env:"CA_CERT_PATH"`
CACert *x509.Certificate
}

func initializeCACert(certPath string) *x509.Certificate {
certData, err := os.ReadFile(certPath)
if err != nil {
log.Fatal("CA certificate not found")
}

CACertBlock, _ := pem.Decode(certData)
if CACertBlock == nil {
log.Fatal("CA certificate is invalid")
}

CACert, err := x509.ParseCertificate(CACertBlock.Bytes)
if err != nil {
log.Fatal("Could not parse CA cert:", err)
}

return CACert
}

func NewConfig() (*Config, error) {
var config Config
if _, err := env.UnmarshalFromEnviron(&config); err != nil {
return nil, err
}
if config.CACertPath != nil {
config.CACert = initializeCACert(*config.CACertPath)
}

return &config, nil
}
50 changes: 50 additions & 0 deletions middleware/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@ package middleware
import (
"context"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"fmt"
"strings"

"github.com/breez/data-sync/config"
"github.com/breez/data-sync/proto"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/tv42/zbase32"
"google.golang.org/grpc/metadata"
)

const (
Expand All @@ -22,7 +26,53 @@ var ErrInternalError = fmt.Errorf("internal error")
var ErrInvalidSignature = fmt.Errorf("invalid signature")
var SignedMsgPrefix = []byte("realtimesync:")

func checkApiKey(config *config.Config, ctx context.Context, req interface{}) error {
if config.CACert == nil {
return nil
}

md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return fmt.Errorf("Could not read request metadata")
}

authHeader := md.Get("Authorization")[0]
if len(authHeader) <= 7 || !strings.HasPrefix(authHeader, "Bearer ") {
return fmt.Errorf("Invalid auth header")
}

apiKey := authHeader[7:]
block, err := base64.StdEncoding.DecodeString(apiKey)
if err != nil {
return fmt.Errorf("Could not decode auth header: %v", err)
}

cert, err := x509.ParseCertificate(block)
if err != nil {
return fmt.Errorf("Could not parse certificate: %v", err)
}

rootPool := x509.NewCertPool()
rootPool.AddCert(config.CACert)

chains, err := cert.Verify(x509.VerifyOptions{
Roots: rootPool,
})
if err != nil {
return fmt.Errorf("Certificate verification error: %v", err)
}
if len(chains) != 1 || len(chains[0]) != 2 || !chains[0][0].Equal(cert) || !chains[0][1].Equal(config.CACert) {
return fmt.Errorf("Certificate verification error: invalid chain of trust")
}

return nil
}

func Authenticate(config *config.Config, ctx context.Context, req interface{}) (context.Context, error) {
if err := checkApiKey(config, ctx, req); err != nil {
return nil, err
}

var toVerify string
var signature string
setRecordReq, ok := req.(*proto.SetRecordRequest)
Expand Down

0 comments on commit 420a90b

Please sign in to comment.