-
Notifications
You must be signed in to change notification settings - Fork 161
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Matt Rogers
committed
Dec 4, 2018
1 parent
a7a65c9
commit 677144e
Showing
7 changed files
with
327 additions
and
0 deletions.
There are no files selected for viewing
184 changes: 184 additions & 0 deletions
184
pkg/controller/sessionsecret/session_secret_controller.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
package sessionsecret | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
"time" | ||
|
||
"github.com/golang/glog" | ||
|
||
"k8s.io/api/core/v1" | ||
"k8s.io/apimachinery/pkg/api/errors" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/runtime/serializer" | ||
utilruntime "k8s.io/apimachinery/pkg/util/runtime" | ||
"k8s.io/apimachinery/pkg/util/wait" | ||
informers "k8s.io/client-go/informers/core/v1" | ||
kcoreclient "k8s.io/client-go/kubernetes/typed/core/v1" | ||
listers "k8s.io/client-go/listers/core/v1" | ||
"k8s.io/client-go/tools/cache" | ||
"k8s.io/client-go/util/workqueue" | ||
|
||
legacyv1 "github.com/openshift/api/legacyconfig/v1" | ||
cryptohelpers "github.com/openshift/cluster-kube-apiserver-operator/pkg/operator/crypto" | ||
"github.com/openshift/library-go/pkg/operator/events" | ||
) | ||
|
||
const ( | ||
sessionSecretNamespace = "openshift-kube-apiserver" | ||
sessionSecretName = "session-secret" | ||
) | ||
|
||
var ( | ||
scheme = runtime.NewScheme() | ||
codecs = serializer.NewCodecFactory(scheme) | ||
legacySchemeBuilder = runtime.NewSchemeBuilder(legacyv1.InstallLegacy) | ||
) | ||
|
||
func init() { | ||
legacySchemeBuilder.AddToScheme(scheme) | ||
} | ||
|
||
type SessionSecretController struct { | ||
podLister listers.PodLister | ||
secretLister listers.SecretLister | ||
secretClient kcoreclient.SecretsGetter | ||
|
||
secretsHasSynced cache.InformerSynced | ||
podsHasSynced cache.InformerSynced | ||
|
||
syncHandler func(serviceKey string) error | ||
|
||
secretsQueue workqueue.RateLimitingInterface | ||
eventRecorder events.Recorder | ||
} | ||
|
||
func NewSessionSecretController(secrets informers.SecretInformer, pods informers.PodInformer, secretsClient kcoreclient.SecretsGetter, resyncInterval time.Duration, eventRecorder events.Recorder) *SessionSecretController { | ||
sc := &SessionSecretController{ | ||
secretsQueue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()), | ||
} | ||
|
||
sc.podLister = pods.Lister() | ||
sc.secretLister = secrets.Lister() | ||
|
||
pods.Informer().AddEventHandlerWithResyncPeriod( | ||
cache.FilteringResourceEventHandler{ | ||
FilterFunc: isKubeAPIServerPod, | ||
Handler: cache.ResourceEventHandlerFuncs{ | ||
AddFunc: sc.enqueueSecret, | ||
}, | ||
}, | ||
resyncInterval, | ||
) | ||
|
||
sc.secretClient = secretsClient | ||
sc.secretsHasSynced = secrets.Informer().HasSynced | ||
sc.podsHasSynced = pods.Informer().HasSynced | ||
|
||
sc.syncHandler = sc.syncSecret | ||
sc.eventRecorder = eventRecorder | ||
|
||
return sc | ||
} | ||
|
||
// Run begins watching and syncing. | ||
func (sc *SessionSecretController) Run(workers int, stopCh <-chan struct{}) { | ||
defer utilruntime.HandleCrash() | ||
defer sc.secretsQueue.ShutDown() | ||
|
||
// Wait for the stores to fill | ||
if !cache.WaitForCacheSync(stopCh, sc.secretsHasSynced, sc.podsHasSynced) { | ||
return | ||
} | ||
|
||
glog.V(4).Infof("Starting workers for SessionSecretController") | ||
for i := 0; i < workers; i++ { | ||
go wait.Until(sc.runWorker, time.Second, stopCh) | ||
} | ||
<-stopCh | ||
glog.V(4).Infof("Shutting down SessionSecretController") | ||
} | ||
|
||
// processNextWorkItem deals with one key off the secretsQueue. It returns false when it's time to quit. | ||
func (sc *SessionSecretController) processNextWorkItem() bool { | ||
key, quit := sc.secretsQueue.Get() | ||
if quit { | ||
return false | ||
} | ||
defer sc.secretsQueue.Done(key) | ||
|
||
err := sc.syncHandler(key.(string)) | ||
if err != nil { | ||
utilruntime.HandleError(fmt.Errorf("%v failed with : %v", key, err)) | ||
sc.eventRecorder.Warningf("CreateSessionSecretFailure", "%v failed with : %v", key, err) | ||
sc.secretsQueue.AddRateLimited(key) | ||
return true | ||
} | ||
|
||
sc.secretsQueue.Forget(key) | ||
return true | ||
} | ||
|
||
func (sc *SessionSecretController) runWorker() { | ||
for sc.processNextWorkItem() { | ||
} | ||
} | ||
|
||
func newSessionSecretsYAML() string { | ||
return runtime.EncodeOrDie(codecs.LegacyCodec(legacyv1.LegacySchemeGroupVersion), &legacyv1.SessionSecrets{ | ||
Secrets: []legacyv1.SessionSecret{ | ||
{ | ||
Authentication: string(cryptohelpers.RandomAuthKeyBits()), | ||
Encryption: string(cryptohelpers.RandomEncKeyBits()), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func (sc *SessionSecretController) createSessionSecret() error { | ||
secret := &v1.Secret{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: sessionSecretName, | ||
Namespace: sessionSecretNamespace, | ||
}, | ||
Data: map[string][]byte{ | ||
"secrets": []byte(newSessionSecretsYAML()), | ||
}, | ||
} | ||
_, err := sc.secretClient.Secrets(secret.Namespace).Create(secret) | ||
return err | ||
} | ||
|
||
// syncSecret creates the session secret if it doesn't exist. | ||
func (sc *SessionSecretController) syncSecret(key string) error { | ||
namespace, name, err := cache.SplitMetaNamespaceKey(key) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
_, err = sc.secretLister.Secrets(namespace).Get(name) | ||
if errors.IsNotFound(err) { | ||
glog.V(4).Infof("creating secret %s/%s", namespace, name) | ||
return sc.createSessionSecret() | ||
} | ||
if err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
func (sc *SessionSecretController) enqueueSecret(obj interface{}) { | ||
sc.secretsQueue.Add(sessionSecretNamespace + "/" + sessionSecretName) | ||
} | ||
|
||
func isKubeAPIServerPod(obj interface{}) bool { | ||
pod, ok := obj.(*v1.Pod) | ||
if !ok { | ||
return false | ||
} | ||
if strings.HasPrefix(pod.Name, "openshift-kube-apiserver") { | ||
return true | ||
} | ||
return false | ||
} |
52 changes: 52 additions & 0 deletions
52
pkg/operator/configobservation/authconfig/observe_sessionsecret.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package authconfig | ||
|
||
import ( | ||
"github.com/golang/glog" | ||
|
||
"k8s.io/apimachinery/pkg/api/errors" | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
|
||
"github.com/openshift/cluster-kube-apiserver-operator/pkg/operator/configobservation" | ||
"github.com/openshift/library-go/pkg/operator/configobserver" | ||
"github.com/openshift/library-go/pkg/operator/events" | ||
) | ||
|
||
const ( | ||
sessionSecretNamespace = "openshift-kube-apiserver" | ||
sessionSecretName = "session-secret" | ||
sessionSecretPath = "/etc/kubernetes/static-pod-resources/secrets/session-secret/secret" | ||
) | ||
|
||
// ObserveSessionSecret sets the oauthConfig sessionSecretsFile if it has not been set and the session secret exists | ||
func ObserveSessionSecret(genericListers configobserver.Listers, recorder events.Recorder, existingConfig map[string]interface{}) (map[string]interface{}, []error) { | ||
listers := genericListers.(configobservation.Listers) | ||
errs := []error{} | ||
observedConfig := map[string]interface{}{} | ||
oauthConfigSessionSecretsFilePath := []string{"oauthConfig", "sessionConfig", "sessionSecretsFile"} | ||
|
||
currentSessionSecretsFilePath, _, err := unstructured.NestedString(existingConfig, oauthConfigSessionSecretsFilePath...) | ||
if err != nil { | ||
errs = append(errs, err) | ||
} | ||
if currentSessionSecretsFilePath == sessionSecretPath { | ||
return observedConfig, errs | ||
} | ||
|
||
_, err = listers.SecretLister.Secrets(sessionSecretNamespace).Get(sessionSecretName) | ||
if errors.IsNotFound(err) { | ||
glog.Warningf("session secret %s/%s not found", sessionSecretNamespace, sessionSecretName) | ||
return observedConfig, errs | ||
} | ||
if err != nil { | ||
errs = append(errs, err) | ||
return nil, errs | ||
} | ||
|
||
err = unstructured.SetNestedField(observedConfig, sessionSecretPath, oauthConfigSessionSecretsFilePath...) | ||
if err != nil { | ||
errs = append(errs, err) | ||
return nil, errs | ||
} | ||
|
||
return observedConfig, errs | ||
} |
39 changes: 39 additions & 0 deletions
39
pkg/operator/configobservation/authconfig/observe_sessionsecret_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package authconfig | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/openshift/cluster-kube-apiserver-operator/pkg/operator/configobservation" | ||
|
||
"k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" | ||
v12 "k8s.io/client-go/listers/core/v1" | ||
"k8s.io/client-go/tools/cache" | ||
) | ||
|
||
func TestObserveSessionSecret(t *testing.T) { | ||
indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{}) | ||
secret := &v1.Secret{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: sessionSecretName, | ||
Namespace: sessionSecretNamespace, | ||
}, | ||
} | ||
indexer.Add(secret) | ||
|
||
listers := configobservation.Listers{ | ||
SecretLister: v12.NewSecretLister(indexer), | ||
} | ||
result, errs := ObserveSessionSecret(listers, nil, map[string]interface{}{}) | ||
if len(errs) > 0 { | ||
t.Error("expected len(errs) == 0") | ||
} | ||
secretPath, _, err := unstructured.NestedString(result, "oauthConfig", "sessionConfig", "sessionSecretsFile") | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if secretPath != sessionSecretPath { | ||
t.Errorf("expected oauthConfig.sessionConfig.sessionSecretsFile: %s, got %s", sessionSecretPath, secretPath) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package crypto | ||
|
||
// Taken from origin but could be moved to library-go | ||
|
||
import ( | ||
"crypto/rand" | ||
"crypto/sha256" | ||
) | ||
|
||
const ( | ||
sha256KeyLenBits = sha256.BlockSize * 8 // max key size with HMAC SHA256 | ||
aes256KeyLenBits = 256 // max key size with AES (AES-256) | ||
) | ||
|
||
func RandomAuthKeyBits() []byte { | ||
return randomBits(sha256KeyLenBits) | ||
} | ||
|
||
func RandomEncKeyBits() []byte { | ||
return randomBits(aes256KeyLenBits) | ||
} | ||
|
||
// randomBits returns a random byte slice with at least the requested bits of entropy. | ||
// Callers should avoid using a value less than 256 unless they have a very good reason. | ||
func randomBits(bits int) []byte { | ||
size := bits / 8 | ||
if bits%8 != 0 { | ||
size++ | ||
} | ||
b := make([]byte, size) | ||
if _, err := rand.Read(b); err != nil { | ||
panic(err) // rand should never fail | ||
} | ||
return b | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters