aboutsummaryrefslogtreecommitdiffstats
path: root/kube2msb/src/vendor/github.com/coreos/go-oidc/key
diff options
context:
space:
mode:
Diffstat (limited to 'kube2msb/src/vendor/github.com/coreos/go-oidc/key')
-rw-r--r--kube2msb/src/vendor/github.com/coreos/go-oidc/key/key.go153
-rw-r--r--kube2msb/src/vendor/github.com/coreos/go-oidc/key/manager.go99
-rw-r--r--kube2msb/src/vendor/github.com/coreos/go-oidc/key/repo.go55
-rw-r--r--kube2msb/src/vendor/github.com/coreos/go-oidc/key/rotate.go165
-rw-r--r--kube2msb/src/vendor/github.com/coreos/go-oidc/key/sync.go91
5 files changed, 563 insertions, 0 deletions
diff --git a/kube2msb/src/vendor/github.com/coreos/go-oidc/key/key.go b/kube2msb/src/vendor/github.com/coreos/go-oidc/key/key.go
new file mode 100644
index 0000000..d0142a9
--- /dev/null
+++ b/kube2msb/src/vendor/github.com/coreos/go-oidc/key/key.go
@@ -0,0 +1,153 @@
+package key
+
+import (
+ "crypto/rand"
+ "crypto/rsa"
+ "encoding/base64"
+ "encoding/json"
+ "math/big"
+ "time"
+
+ "github.com/coreos/go-oidc/jose"
+)
+
+func NewPublicKey(jwk jose.JWK) *PublicKey {
+ return &PublicKey{jwk: jwk}
+}
+
+type PublicKey struct {
+ jwk jose.JWK
+}
+
+func (k *PublicKey) MarshalJSON() ([]byte, error) {
+ return json.Marshal(&k.jwk)
+}
+
+func (k *PublicKey) UnmarshalJSON(data []byte) error {
+ var jwk jose.JWK
+ if err := json.Unmarshal(data, &jwk); err != nil {
+ return err
+ }
+ k.jwk = jwk
+ return nil
+}
+
+func (k *PublicKey) ID() string {
+ return k.jwk.ID
+}
+
+func (k *PublicKey) Verifier() (jose.Verifier, error) {
+ return jose.NewVerifierRSA(k.jwk)
+}
+
+type PrivateKey struct {
+ KeyID string
+ PrivateKey *rsa.PrivateKey
+}
+
+func (k *PrivateKey) ID() string {
+ return k.KeyID
+}
+
+func (k *PrivateKey) Signer() jose.Signer {
+ return jose.NewSignerRSA(k.ID(), *k.PrivateKey)
+}
+
+func (k *PrivateKey) JWK() jose.JWK {
+ return jose.JWK{
+ ID: k.KeyID,
+ Type: "RSA",
+ Alg: "RS256",
+ Use: "sig",
+ Exponent: k.PrivateKey.PublicKey.E,
+ Modulus: k.PrivateKey.PublicKey.N,
+ }
+}
+
+type KeySet interface {
+ ExpiresAt() time.Time
+}
+
+type PublicKeySet struct {
+ keys []PublicKey
+ index map[string]*PublicKey
+ expiresAt time.Time
+}
+
+func NewPublicKeySet(jwks []jose.JWK, exp time.Time) *PublicKeySet {
+ keys := make([]PublicKey, len(jwks))
+ index := make(map[string]*PublicKey)
+ for i, jwk := range jwks {
+ keys[i] = *NewPublicKey(jwk)
+ index[keys[i].ID()] = &keys[i]
+ }
+ return &PublicKeySet{
+ keys: keys,
+ index: index,
+ expiresAt: exp,
+ }
+}
+
+func (s *PublicKeySet) ExpiresAt() time.Time {
+ return s.expiresAt
+}
+
+func (s *PublicKeySet) Keys() []PublicKey {
+ return s.keys
+}
+
+func (s *PublicKeySet) Key(id string) *PublicKey {
+ return s.index[id]
+}
+
+type PrivateKeySet struct {
+ keys []*PrivateKey
+ ActiveKeyID string
+ expiresAt time.Time
+}
+
+func NewPrivateKeySet(keys []*PrivateKey, exp time.Time) *PrivateKeySet {
+ return &PrivateKeySet{
+ keys: keys,
+ ActiveKeyID: keys[0].ID(),
+ expiresAt: exp.UTC(),
+ }
+}
+
+func (s *PrivateKeySet) Keys() []*PrivateKey {
+ return s.keys
+}
+
+func (s *PrivateKeySet) ExpiresAt() time.Time {
+ return s.expiresAt
+}
+
+func (s *PrivateKeySet) Active() *PrivateKey {
+ for i, k := range s.keys {
+ if k.ID() == s.ActiveKeyID {
+ return s.keys[i]
+ }
+ }
+
+ return nil
+}
+
+type GeneratePrivateKeyFunc func() (*PrivateKey, error)
+
+func GeneratePrivateKey() (*PrivateKey, error) {
+ pk, err := rsa.GenerateKey(rand.Reader, 2048)
+ if err != nil {
+ return nil, err
+ }
+
+ k := PrivateKey{
+ KeyID: base64BigInt(pk.PublicKey.N),
+ PrivateKey: pk,
+ }
+
+ return &k, nil
+}
+
+func base64BigInt(b *big.Int) string {
+ return base64.URLEncoding.EncodeToString(b.Bytes())
+}
diff --git a/kube2msb/src/vendor/github.com/coreos/go-oidc/key/manager.go b/kube2msb/src/vendor/github.com/coreos/go-oidc/key/manager.go
new file mode 100644
index 0000000..476ab6a
--- /dev/null
+++ b/kube2msb/src/vendor/github.com/coreos/go-oidc/key/manager.go
@@ -0,0 +1,99 @@
+package key
+
+import (
+ "errors"
+ "time"
+
+ "github.com/jonboulle/clockwork"
+
+ "github.com/coreos/go-oidc/jose"
+ "github.com/coreos/pkg/health"
+)
+
+type PrivateKeyManager interface {
+ ExpiresAt() time.Time
+ Signer() (jose.Signer, error)
+ JWKs() ([]jose.JWK, error)
+ PublicKeys() ([]PublicKey, error)
+
+ WritableKeySetRepo
+ health.Checkable
+}
+
+func NewPrivateKeyManager() PrivateKeyManager {
+ return &privateKeyManager{
+ clock: clockwork.NewRealClock(),
+ }
+}
+
+type privateKeyManager struct {
+ keySet *PrivateKeySet
+ clock clockwork.Clock
+}
+
+func (m *privateKeyManager) ExpiresAt() time.Time {
+ if m.keySet == nil {
+ return m.clock.Now().UTC()
+ }
+
+ return m.keySet.ExpiresAt()
+}
+
+func (m *privateKeyManager) Signer() (jose.Signer, error) {
+ if err := m.Healthy(); err != nil {
+ return nil, err
+ }
+
+ return m.keySet.Active().Signer(), nil
+}
+
+func (m *privateKeyManager) JWKs() ([]jose.JWK, error) {
+ if err := m.Healthy(); err != nil {
+ return nil, err
+ }
+
+ keys := m.keySet.Keys()
+ jwks := make([]jose.JWK, len(keys))
+ for i, k := range keys {
+ jwks[i] = k.JWK()
+ }
+ return jwks, nil
+}
+
+func (m *privateKeyManager) PublicKeys() ([]PublicKey, error) {
+ jwks, err := m.JWKs()
+ if err != nil {
+ return nil, err
+ }
+ keys := make([]PublicKey, len(jwks))
+ for i, jwk := range jwks {
+ keys[i] = *NewPublicKey(jwk)
+ }
+ return keys, nil
+}
+
+func (m *privateKeyManager) Healthy() error {
+ if m.keySet == nil {
+ return errors.New("private key manager uninitialized")
+ }
+
+ if len(m.keySet.Keys()) == 0 {
+ return errors.New("private key manager zero keys")
+ }
+
+ if m.keySet.ExpiresAt().Before(m.clock.Now().UTC()) {
+ return errors.New("private key manager keys expired")
+ }
+
+ return nil
+}
+
+func (m *privateKeyManager) Set(keySet KeySet) error {
+ privKeySet, ok := keySet.(*PrivateKeySet)
+ if !ok {
+ return errors.New("unable to cast to PrivateKeySet")
+ }
+
+ m.keySet = privKeySet
+ return nil
+}
diff --git a/kube2msb/src/vendor/github.com/coreos/go-oidc/key/repo.go b/kube2msb/src/vendor/github.com/coreos/go-oidc/key/repo.go
new file mode 100644
index 0000000..1acdeb3
--- /dev/null
+++ b/kube2msb/src/vendor/github.com/coreos/go-oidc/key/repo.go
@@ -0,0 +1,55 @@
+package key
+
+import (
+ "errors"
+ "sync"
+)
+
+var ErrorNoKeys = errors.New("no keys found")
+
+type WritableKeySetRepo interface {
+ Set(KeySet) error
+}
+
+type ReadableKeySetRepo interface {
+ Get() (KeySet, error)
+}
+
+type PrivateKeySetRepo interface {
+ WritableKeySetRepo
+ ReadableKeySetRepo
+}
+
+func NewPrivateKeySetRepo() PrivateKeySetRepo {
+ return &memPrivateKeySetRepo{}
+}
+
+type memPrivateKeySetRepo struct {
+ mu sync.RWMutex
+ pks PrivateKeySet
+}
+
+func (r *memPrivateKeySetRepo) Set(ks KeySet) error {
+ pks, ok := ks.(*PrivateKeySet)
+ if !ok {
+ return errors.New("unable to cast to PrivateKeySet")
+ } else if pks == nil {
+ return errors.New("nil KeySet")
+ }
+
+ r.mu.Lock()
+ defer r.mu.Unlock()
+
+ r.pks = *pks
+ return nil
+}
+
+func (r *memPrivateKeySetRepo) Get() (KeySet, error) {
+ r.mu.RLock()
+ defer r.mu.RUnlock()
+
+ if r.pks.keys == nil {
+ return nil, ErrorNoKeys
+ }
+ return KeySet(&r.pks), nil
+}
diff --git a/kube2msb/src/vendor/github.com/coreos/go-oidc/key/rotate.go b/kube2msb/src/vendor/github.com/coreos/go-oidc/key/rotate.go
new file mode 100644
index 0000000..9c5508b
--- /dev/null
+++ b/kube2msb/src/vendor/github.com/coreos/go-oidc/key/rotate.go
@@ -0,0 +1,165 @@
+package key
+
+import (
+ "errors"
+ "time"
+
+ "github.com/coreos/pkg/capnslog"
+ ptime "github.com/coreos/pkg/timeutil"
+ "github.com/jonboulle/clockwork"
+)
+
+var (
+ log = capnslog.NewPackageLogger("github.com/coreos/go-oidc", "key")
+
+ ErrorPrivateKeysExpired = errors.New("private keys have expired")
+)
+
+func NewPrivateKeyRotator(repo PrivateKeySetRepo, ttl time.Duration) *PrivateKeyRotator {
+ return &PrivateKeyRotator{
+ repo: repo,
+ ttl: ttl,
+
+ keep: 2,
+ generateKey: GeneratePrivateKey,
+ clock: clockwork.NewRealClock(),
+ }
+}
+
+type PrivateKeyRotator struct {
+ repo PrivateKeySetRepo
+ generateKey GeneratePrivateKeyFunc
+ clock clockwork.Clock
+ keep int
+ ttl time.Duration
+}
+
+func (r *PrivateKeyRotator) expiresAt() time.Time {
+ return r.clock.Now().UTC().Add(r.ttl)
+}
+
+func (r *PrivateKeyRotator) Healthy() error {
+ pks, err := r.privateKeySet()
+ if err != nil {
+ return err
+ }
+
+ if r.clock.Now().After(pks.ExpiresAt()) {
+ return ErrorPrivateKeysExpired
+ }
+
+ return nil
+}
+
+func (r *PrivateKeyRotator) privateKeySet() (*PrivateKeySet, error) {
+ ks, err := r.repo.Get()
+ if err != nil {
+ return nil, err
+ }
+
+ pks, ok := ks.(*PrivateKeySet)
+ if !ok {
+ return nil, errors.New("unable to cast to PrivateKeySet")
+ }
+ return pks, nil
+}
+
+func (r *PrivateKeyRotator) nextRotation() (time.Duration, error) {
+ pks, err := r.privateKeySet()
+ if err == ErrorNoKeys {
+ log.Infof("No keys in private key set; must rotate immediately")
+ return 0, nil
+ }
+ if err != nil {
+ return 0, err
+ }
+
+ now := r.clock.Now()
+
+ // Ideally, we want to rotate after half the TTL has elapsed.
+ idealRotationTime := pks.ExpiresAt().Add(-r.ttl / 2)
+
+ // If we are past the ideal rotation time, rotate immediatly.
+ return max(0, idealRotationTime.Sub(now)), nil
+}
+
+func max(a, b time.Duration) time.Duration {
+ if a > b {
+ return a
+ }
+ return b
+}
+
+func (r *PrivateKeyRotator) Run() chan struct{} {
+ attempt := func() {
+ k, err := r.generateKey()
+ if err != nil {
+ log.Errorf("Failed generating signing key: %v", err)
+ return
+ }
+
+ exp := r.expiresAt()
+ if err := rotatePrivateKeys(r.repo, k, r.keep, exp); err != nil {
+ log.Errorf("Failed key rotation: %v", err)
+ return
+ }
+
+ log.Infof("Rotated signing keys: id=%s expiresAt=%s", k.ID(), exp)
+ }
+
+ stop := make(chan struct{})
+ go func() {
+ for {
+ var nextRotation time.Duration
+ var sleep time.Duration
+ var err error
+ for {
+ if nextRotation, err = r.nextRotation(); err == nil {
+ break
+ }
+ sleep = ptime.ExpBackoff(sleep, time.Minute)
+ log.Errorf("error getting nextRotation, retrying in %v: %v", sleep, err)
+ time.Sleep(sleep)
+ }
+
+ log.Infof("will rotate keys in %v", nextRotation)
+ select {
+ case <-r.clock.After(nextRotation):
+ attempt()
+ case <-stop:
+ return
+ }
+ }
+ }()
+
+ return stop
+}
+
+func rotatePrivateKeys(repo PrivateKeySetRepo, k *PrivateKey, keep int, exp time.Time) error {
+ ks, err := repo.Get()
+ if err != nil && err != ErrorNoKeys {
+ return err
+ }
+
+ var keys []*PrivateKey
+ if ks != nil {
+ pks, ok := ks.(*PrivateKeySet)
+ if !ok {
+ return errors.New("unable to cast to PrivateKeySet")
+ }
+ keys = pks.Keys()
+ }
+
+ keys = append([]*PrivateKey{k}, keys...)
+ if l := len(keys); l > keep {
+ keys = keys[0:keep]
+ }
+
+ nks := PrivateKeySet{
+ keys: keys,
+ ActiveKeyID: k.ID(),
+ expiresAt: exp,
+ }
+
+ return repo.Set(KeySet(&nks))
+}
diff --git a/kube2msb/src/vendor/github.com/coreos/go-oidc/key/sync.go b/kube2msb/src/vendor/github.com/coreos/go-oidc/key/sync.go
new file mode 100644
index 0000000..e8d5d03
--- /dev/null
+++ b/kube2msb/src/vendor/github.com/coreos/go-oidc/key/sync.go
@@ -0,0 +1,91 @@
+package key
+
+import (
+ "errors"
+ "time"
+
+ "github.com/jonboulle/clockwork"
+
+ "github.com/coreos/pkg/timeutil"
+)
+
+func NewKeySetSyncer(r ReadableKeySetRepo, w WritableKeySetRepo) *KeySetSyncer {
+ return &KeySetSyncer{
+ readable: r,
+ writable: w,
+ clock: clockwork.NewRealClock(),
+ }
+}
+
+type KeySetSyncer struct {
+ readable ReadableKeySetRepo
+ writable WritableKeySetRepo
+ clock clockwork.Clock
+}
+
+func (s *KeySetSyncer) Run() chan struct{} {
+ stop := make(chan struct{})
+ go func() {
+ var failing bool
+ var next time.Duration
+ for {
+ exp, err := syncKeySet(s.readable, s.writable, s.clock)
+ if err != nil || exp == 0 {
+ if !failing {
+ failing = true
+ next = time.Second
+ } else {
+ next = timeutil.ExpBackoff(next, time.Minute)
+ }
+ if exp == 0 {
+ log.Errorf("Synced to already expired key set, retrying in %v: %v", next, err)
+
+ } else {
+ log.Errorf("Failed syncing key set, retrying in %v: %v", next, err)
+ }
+ } else {
+ failing = false
+ next = exp / 2
+ log.Infof("Synced key set, checking again in %v", next)
+ }
+
+ select {
+ case <-s.clock.After(next):
+ continue
+ case <-stop:
+ return
+ }
+ }
+ }()
+
+ return stop
+}
+
+func Sync(r ReadableKeySetRepo, w WritableKeySetRepo) (time.Duration, error) {
+ return syncKeySet(r, w, clockwork.NewRealClock())
+}
+
+// syncKeySet copies the keyset from r to the KeySet at w and returns the duration in which the KeySet will expire.
+// If keyset has already expired, returns a zero duration.
+func syncKeySet(r ReadableKeySetRepo, w WritableKeySetRepo, clock clockwork.Clock) (exp time.Duration, err error) {
+ var ks KeySet
+ ks, err = r.Get()
+ if err != nil {
+ return
+ }
+
+ if ks == nil {
+ err = errors.New("no source KeySet")
+ return
+ }
+
+ if err = w.Set(ks); err != nil {
+ return
+ }
+
+ now := clock.Now()
+ if ks.ExpiresAt().After(now) {
+ exp = ks.ExpiresAt().Sub(now)
+ }
+ return
+}