Skip to content

Commit

Permalink
update pubkey of all origin exports; align new key API and manual add…
Browse files Browse the repository at this point in the history
… behavior; linter problems fix
  • Loading branch information
h2zh committed Nov 27, 2024
1 parent dceede7 commit 8cb8090
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 41 deletions.
3 changes: 1 addition & 2 deletions config/init_server_creds.go
Original file line number Diff line number Diff line change
Expand Up @@ -724,8 +724,7 @@ func GeneratePEMandSetActiveKey(dir string) (jwk.Key, error) {
return newKey, nil
}

// Helper function to load the issuer/server's private key to sign tokens it issues.
// Only intended to be called internally
// Re-scan the disk to load the issuer/server's private keys, return the active key to sign tokens it issues
func LoadIssuerPrivateKey(issuerKeysDir string) (jwk.Key, error) {
// Ensure initKeysMap is only called once across the program’s runtime
var initErr error
Expand Down
48 changes: 44 additions & 4 deletions launcher_utils/register_namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import (
"github.com/pelicanplatform/pelican/param"
"github.com/pelicanplatform/pelican/registry"
"github.com/pelicanplatform/pelican/server_structs"
"github.com/pelicanplatform/pelican/server_utils"
)

type (
Expand Down Expand Up @@ -243,12 +244,13 @@ func registerNamespacePrep(ctx context.Context, prefix string) (key jwk.Key, reg
}
}

keyStatus, err := keyIsRegistered(privateKey, registrationUrl, prefix)
var keyStatusVar keyStatus
keyStatusVar, err = keyIsRegistered(privateKey, registrationUrl, prefix)
if err != nil {
err = errors.Wrap(err, "Failed to determine whether namespace is already registered")
break
return
}
switch keyStatus {
switch keyStatusVar {
case keyMatch:
isRegistered = true
case keyMismatch:
Expand All @@ -263,7 +265,7 @@ func registerNamespacePrep(ctx context.Context, prefix string) (key jwk.Key, reg
err = errors.Errorf("Namespace %v already registered under a different key", prefix)
return
}
// Else, there is at least one private key from the origin match the registered public key,
// If there is at least one private key from the origin match the registered public key,
// we will update the public key of the namespace in registry db with the active private key
// held by this origin; the verified new private key also get added to in-memory map in this process
key, err = config.LoadIssuerPrivateKey(param.IssuerKeysDirectory.GetString())
Expand Down Expand Up @@ -332,3 +334,41 @@ func RegisterNamespaceWithRetry(ctx context.Context, egrp *errgroup.Group, prefi
})
return nil
}

// Check the directory containing .pem files every 5 minutes, load new private key(s)
// if new file(s) are detected, then register the new public key
func LaunchIssuerKeysDirRefresh(ctx context.Context, egrp *errgroup.Group) {
egrp.Go(func() error {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()

for {
select {
case <-ctx.Done():
log.Debugln("Stopping periodic check for private keys directory.")
return nil
case <-ticker.C:
extUrlStr := param.Server_ExternalWebUrl.GetString()
extUrl, _ := url.Parse(extUrlStr)
namespace := server_structs.GetOriginNs(extUrl.Host)
if err := RegisterNamespaceWithRetry(ctx, egrp, namespace); err != nil {
log.Errorf("Error refreshing private keys: %v", err)
}

originExports, err := server_utils.GetOriginExports()
if err != nil {
return err
}
for _, export := range originExports {
if err := RegisterNamespaceWithRetry(ctx, egrp, export.FederationPrefix); err != nil {
return err
}
}

if key, err := config.GetIssuerPrivateJWK(); err != nil {
log.Debugln("Private keys directory refreshed successfully. The active (latest) private key is", key.KeyID())
}
}
}
})
}
1 change: 1 addition & 0 deletions launcher_utils/register_namespace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ func TestMultiKeysRegistration(t *testing.T) {
secondPubKey, err := secondKey.PublicKey()
require.NoError(t, err)
activeKey, err := config.GetIssuerPrivateJWK()
require.NoError(t, err)
require.Equal(t, secondKey, activeKey)
keysMap = config.GetIssuerPrivateKeys()
require.Equal(t, secondKey, keysMap[secondKey.KeyID()])
Expand Down
32 changes: 1 addition & 31 deletions launchers/origin_serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import (
"github.com/spf13/viper"
"golang.org/x/sync/errgroup"

"github.com/pelicanplatform/pelican/config"
"github.com/pelicanplatform/pelican/launcher_utils"
"github.com/pelicanplatform/pelican/metrics"
"github.com/pelicanplatform/pelican/oa4mp"
Expand All @@ -44,35 +43,6 @@ import (
"github.com/pelicanplatform/pelican/xrootd"
)

// Check the directory containing .pem files every 5 minutes, load new private key(s)
// if new file(s) are detected, then register the new public key
func launchIssuerKeysDirRefresh(ctx context.Context, egrp *errgroup.Group) {
egrp.Go(func() error {
ticker := time.NewTicker(5 * time.Minute)
defer ticker.Stop()

for {
select {
case <-ctx.Done():
log.Debugln("Stopping periodic check for private keys directory.")
return nil
case <-ticker.C:
extUrlStr := param.Server_ExternalWebUrl.GetString()
extUrl, _ := url.Parse(extUrlStr)
namespace := server_structs.GetOriginNs(extUrl.Host)
if err := launcher_utils.RegisterNamespaceWithRetry(ctx, egrp, namespace); err != nil {
log.Errorf("Error refreshing private keys: %v", err)
} else {
if key, err := config.GetIssuerPrivateJWK(); err != nil {
log.Debugln("Private keys directory refreshed successfully. The active (latest) private key is", key.KeyID())
}
}

}
}
})
}

func OriginServe(ctx context.Context, engine *gin.Engine, egrp *errgroup.Group, modules server_structs.ServerType) (server_structs.XRootDServer, error) {
metrics.SetComponentHealthStatus(metrics.OriginCache_XRootD, metrics.StatusWarning, "XRootD is initializing")
metrics.SetComponentHealthStatus(metrics.OriginCache_CMSD, metrics.StatusWarning, "CMSD is initializting")
Expand Down Expand Up @@ -108,7 +78,7 @@ func OriginServe(ctx context.Context, engine *gin.Engine, egrp *errgroup.Group,

// Start a routine to periodically refresh the private key directory.
// This ensures that new or updated private keys are automatically loaded and registered
launchIssuerKeysDirRefresh(ctx, egrp)
launcher_utils.LaunchIssuerKeysDirRefresh(ctx, egrp)

// Set up the APIs unrelated to UI, which only contains director-based health test reporting endpoint for now
if err = origin.RegisterOriginAPI(engine, ctx, egrp); err != nil {
Expand Down
10 changes: 6 additions & 4 deletions origin/origin_ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,16 +161,18 @@ func handleExports(ctx *gin.Context) {
ctx.JSON(http.StatusOK, res)
}

// Create a new private key in the given directory and set it as the active key
// Create a new private key in the given directory, but without loading it immediately
// This new key will be detected and loaded later by the ongoing goroutine `LaunchIssuerKeysDirRefresh`,
// which periodically refreshes the keys in the issuer keys directory
func createNewIssuerKey(ctx *gin.Context) {
issuerKeysDir := param.IssuerKeysDirectory.GetString()

_, err := config.GeneratePEMandSetActiveKey(issuerKeysDir)
_, err := config.GeneratePEM(issuerKeysDir)
if err != nil {
log.Errorf("Error creating and loading a new private key in a new .pem file: %v", err)
log.Errorf("Error creating a new private key in a new .pem file: %v", err)
ctx.JSON(http.StatusInternalServerError, server_structs.SimpleApiResp{
Status: server_structs.RespFailed,
Msg: "Error creating and loading a new private key in a new .pem file"})
Msg: "Error creating a new private key in a new .pem file"})
}

ctx.JSON(http.StatusOK,
Expand Down

0 comments on commit 8cb8090

Please sign in to comment.