mirror of
https://github.com/kemko/nomad.git
synced 2026-01-06 18:35:44 +03:00
core: add jwks rpc and http api (#18035)
Add JWKS endpoint to HTTP API for exposing the root public signing keys used for signing workload identity JWTs. Part 1 of N components as part of making workload identities consumable by third party services such as Consul and Vault. Identity attenuation (audience) and expiration (+renewal) are necessary to securely use workload identities with 3rd parties, so this merge does not yet document this endpoint. --------- Co-authored-by: Tim Gross <tgross@hashicorp.com>
This commit is contained in:
@@ -501,6 +501,9 @@ func (s *HTTPServer) registerHandlers(enableDebug bool) {
|
||||
s.mux.Handle("/v1/vars", wrapCORS(s.wrap(s.VariablesListRequest)))
|
||||
s.mux.Handle("/v1/var/", wrapCORSWithAllowedMethods(s.wrap(s.VariableSpecificRequest), "HEAD", "GET", "PUT", "DELETE"))
|
||||
|
||||
// JWKS Handler
|
||||
s.mux.HandleFunc("/.well-known/jwks.json", s.wrap(s.JWKSRequest))
|
||||
|
||||
agentConfig := s.agent.GetConfig()
|
||||
uiConfigEnabled := agentConfig.UI != nil && agentConfig.UI.Enabled
|
||||
|
||||
|
||||
@@ -4,12 +4,79 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-jose/go-jose/v3"
|
||||
"github.com/hashicorp/nomad/helper"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
)
|
||||
|
||||
// jwksMinMaxAge is the minimum amount of time the JWKS endpoint will instruct
|
||||
// consumers to cache a response for.
|
||||
const jwksMinMaxAge = 15 * time.Minute
|
||||
|
||||
// JWKSRequest is used to handle JWKS requests. JWKS stands for JSON Web Key
|
||||
// Sets and returns the public keys used for signing workload identities. Third
|
||||
// parties may use this endpoint to validate workload identities. Consumers
|
||||
// should cache this endpoint, preferably until an unknown kid is encountered.
|
||||
func (s *HTTPServer) JWKSRequest(resp http.ResponseWriter, req *http.Request) (any, error) {
|
||||
if req.Method != http.MethodGet {
|
||||
return nil, CodedError(405, ErrInvalidMethod)
|
||||
}
|
||||
|
||||
args := structs.GenericRequest{}
|
||||
if s.parse(resp, req, &args.Region, &args.QueryOptions) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var rpcReply structs.KeyringListPublicResponse
|
||||
if err := s.agent.RPC("Keyring.ListPublic", &args, &rpcReply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
setMeta(resp, &rpcReply.QueryMeta)
|
||||
|
||||
// Key set will change after max(CreateTime) + RotationThreshold.
|
||||
var newestKey int64
|
||||
jwks := make([]jose.JSONWebKey, 0, len(rpcReply.PublicKeys))
|
||||
for _, pubKey := range rpcReply.PublicKeys {
|
||||
if pubKey.CreateTime > newestKey {
|
||||
newestKey = pubKey.CreateTime
|
||||
}
|
||||
|
||||
jwk := jose.JSONWebKey{
|
||||
KeyID: pubKey.KeyID,
|
||||
Algorithm: pubKey.Algorithm,
|
||||
Use: pubKey.Use,
|
||||
}
|
||||
switch alg := pubKey.Algorithm; alg {
|
||||
case structs.PubKeyAlgEdDSA:
|
||||
// Convert public key bytes to an ed25519 public key
|
||||
jwk.Key = ed25519.PublicKey(pubKey.PublicKey)
|
||||
default:
|
||||
s.logger.Warn("unknown public key algorithm. server is likely newer than client", "alg", alg)
|
||||
}
|
||||
|
||||
jwks = append(jwks, jwk)
|
||||
}
|
||||
|
||||
// Have nonzero create times and threshold so set a reasonable cache time.
|
||||
if newestKey > 0 && rpcReply.RotationThreshold > 0 {
|
||||
exp := time.Unix(0, newestKey).Add(rpcReply.RotationThreshold)
|
||||
maxAge := helper.ExpiryToRenewTime(exp, time.Now, jwksMinMaxAge)
|
||||
resp.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d", int(maxAge.Seconds())))
|
||||
}
|
||||
|
||||
out := &jose.JSONWebKeySet{
|
||||
Keys: jwks,
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// KeyringRequest is used route operator/raft API requests to the implementing
|
||||
// functions.
|
||||
func (s *HTTPServer) KeyringRequest(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
|
||||
|
||||
Reference in New Issue
Block a user