Skip to content

Commit

Permalink
use library-go config observer for access token inactivity timeout
Browse files Browse the repository at this point in the history
  • Loading branch information
vareti committed Jun 30, 2020
1 parent 6f15fe2 commit 59c903d
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
libgoapiserver "github.com/openshift/library-go/pkg/operator/configobserver/apiserver"
"github.com/openshift/library-go/pkg/operator/configobserver/cloudprovider"
"github.com/openshift/library-go/pkg/operator/configobserver/featuregates"
configobserveroauth "github.com/openshift/library-go/pkg/operator/configobserver/oauth"
"github.com/openshift/library-go/pkg/operator/configobserver/proxy"
encryption "github.com/openshift/library-go/pkg/operator/encryption/observer"
"github.com/openshift/library-go/pkg/operator/events"
Expand Down Expand Up @@ -60,6 +61,7 @@ func NewConfigObserver(
configInformer.Config().V1().Images().Informer(),
configInformer.Config().V1().Infrastructures().Informer(),
configInformer.Config().V1().Authentications().Informer(),
configInformer.Config().V1().OAuths().Informer(),
configInformer.Config().V1().APIServers().Informer(),
configInformer.Config().V1().Networks().Informer(),
configInformer.Config().V1().Proxies().Informer(),
Expand All @@ -80,6 +82,7 @@ func NewConfigObserver(
ImageConfigLister: configInformer.Config().V1().Images().Lister(),
InfrastructureLister_: configInformer.Config().V1().Infrastructures().Lister(),
NetworkLister: configInformer.Config().V1().Networks().Lister(),
OAuthConfigLister: configInformer.Config().V1().OAuths().Lister(),
ProxyLister_: configInformer.Config().V1().Proxies().Lister(),
SchedulerLister: configInformer.Config().V1().Schedulers().Lister(),

Expand All @@ -101,6 +104,7 @@ func NewConfigObserver(
configInformer.Config().V1().Images().Informer().HasSynced,
configInformer.Config().V1().Infrastructures().Informer().HasSynced,
configInformer.Config().V1().Networks().Informer().HasSynced,
configInformer.Config().V1().OAuths().Informer().HasSynced,
configInformer.Config().V1().Proxies().Informer().HasSynced,
configInformer.Config().V1().Schedulers().Informer().HasSynced,
),
Expand Down Expand Up @@ -133,6 +137,7 @@ func NewConfigObserver(
network.ObserveRestrictedCIDRs,
network.ObserveServicesSubnet,
network.ObserveExternalIPPolicy,
configobserveroauth.ObserveAccessTokenInactivityTimeout,
proxy.NewProxyObserveFunc([]string{"targetconfigcontroller", "proxy"}),
images.ObserveInternalRegistryHostname,
images.ObserveExternalRegistryHostnames,
Expand Down
5 changes: 5 additions & 0 deletions pkg/operator/configobservation/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type Listers struct {
InfrastructureLister_ configlistersv1.InfrastructureLister
ImageConfigLister configlistersv1.ImageLister
NetworkLister configlistersv1.NetworkLister
OAuthConfigLister configlistersv1.OAuthLister
ProxyLister_ configlistersv1.ProxyLister
SchedulerLister configlistersv1.SchedulerLister

Expand Down Expand Up @@ -45,6 +46,10 @@ func (l Listers) ResourceSyncer() resourcesynccontroller.ResourceSyncer {
return l.ResourceSync
}

func (l Listers) OAuthLister() configlistersv1.OAuthLister {
return l.OAuthConfigLister
}

func (l Listers) SecretLister() corelistersv1.SecretLister {
return l.SecretLister_
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,7 @@ func manageKubeAPIServerConfig(client coreclientv1.ConfigMapsGetter, recorder ev
configMap := resourceread.ReadConfigMapV1OrDie(v410_00_assets.MustAsset("v4.1.0/kube-apiserver/cm.yaml"))
defaultConfig := v410_00_assets.MustAsset("v4.1.0/config/defaultconfig.yaml")
configOverrides := v410_00_assets.MustAsset("v4.1.0/config/config-overrides.yaml")
specialMergeRules := map[string]resourcemerge.MergeFunc{
".oauthConfig": RemoveConfig,
}
specialMergeRules := map[string]resourcemerge.MergeFunc{}

requiredConfigMap, _, err := resourcemerge.MergePrunedConfigMap(
&kubecontrolplanev1.KubeAPIServerConfig{},
Expand Down
166 changes: 166 additions & 0 deletions test/e2e/token_inactivity_timeout_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package e2e

import (
"bytes"
"context"
"crypto/tls"
"fmt"
"net/http"
"testing"
"time"

oauthapi "github.com/openshift/api/oauth/v1"
userapi "github.com/openshift/api/user/v1"
configclient "github.com/openshift/client-go/config/clientset/versioned/typed/config/v1"
oauthclient "github.com/openshift/client-go/oauth/clientset/versioned/typed/oauth/v1"
userclient "github.com/openshift/client-go/user/clientset/versioned/typed/user/v1"
test "github.com/openshift/cluster-kube-apiserver-operator/test/library"
"github.com/stretchr/testify/require"

corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
defaultAccessTokenMaxAgeSeconds = 86400
)

func TestTokenInactivityTimeout(t *testing.T) {
kubeConfig, err := test.NewClientConfigForTest()
require.NoError(t, err)

userClient := userclient.NewForConfigOrDie(kubeConfig)
oauthClientClient := oauthclient.NewForConfigOrDie(kubeConfig)
configClient := configclient.NewForConfigOrDie(kubeConfig)

// Update OAuth cluster config with token inactivity timeout
updateOAuthServerClusterConfigSpec(t, configClient, 300*time.Second)

// Wait for kube-apiserver pods to start progressing
test.WaitForKubeAPIServerStartProgressing(t, configClient)

// Wait until all kube-apiserver pods are restarted with new config.
test.WaitForKubeAPIServerClusterOperatorAvailableNotProgressingNotDegraded(t, configClient)

// Create the user, identity, oauthclient and oauthaccesstoken objects needed for authentication using Bearer tokens.
user := &userapi.User{
ObjectMeta: metav1.ObjectMeta{
Name: "testuser2",
},
Identities: []string{"htpasswd:testuser2"},
}
userInfo, err := userClient.Users().Create(context.Background(), user, metav1.CreateOptions{})
require.NoError(t, err)
defer func() {
if err := userClient.Users().Delete(context.Background(), user.Name, metav1.DeleteOptions{}); err != nil {
t.Logf("%v", err)
}
}()

identity := &userapi.Identity{
ObjectMeta: metav1.ObjectMeta{
Name: "htpasswd:testuser2",
},
ProviderName: "htpasswd",
ProviderUserName: user.Name,
User: corev1.ObjectReference{
Name: userInfo.Name,
UID: userInfo.UID,
},
}
_, err = userClient.Identities().Create(context.Background(), identity, metav1.CreateOptions{})
require.NoError(t, err)
defer func() {
if err := userClient.Identities().Delete(context.Background(), identity.Name, metav1.DeleteOptions{}); err != nil {
t.Logf("%v", err)
}
}()

oauthClient := &oauthapi.OAuthClient{
ObjectMeta: metav1.ObjectMeta{
Name: "demooauthclient",
},
Secret: "ThisIsATopSecret",
RedirectURIs: []string{"https://localhost"},
GrantMethod: "auto",
}
_, err = oauthClientClient.OAuthClients().Create(context.Background(), oauthClient, metav1.CreateOptions{})
require.NoError(t, err)
defer func() {
if err := oauthClientClient.OAuthClients().Delete(context.Background(), oauthClient.Name, metav1.DeleteOptions{}); err != nil {
t.Logf("%v", err)
}
}()

shortLivedOAuthAccessToken := &oauthapi.OAuthAccessToken{
ObjectMeta: metav1.ObjectMeta{
Name: "Wf0AA1DphiD1FFLQV7BQIsesaaba-15RoSPZ3SvvXXS",
},
ClientName: oauthClient.Name,
ExpiresIn: 86400,
Scopes: []string{"user:full"},
RedirectURI: oauthClient.RedirectURIs[0],
UserName: user.Name,
UserUID: string(userInfo.UID),
AuthorizeToken: "mJOQ7Es5l9V7WYDl0bvl3E_hRjnJ21ZZxXH6YZj3yeS",
InactivityTimeoutSeconds: 300,
}
_, err = oauthClientClient.OAuthAccessTokens().Create(context.Background(), shortLivedOAuthAccessToken, metav1.CreateOptions{})
require.NoError(t, err)
defer func() {
if err := oauthClientClient.OAuthAccessTokens().Delete(context.Background(), shortLivedOAuthAccessToken.Name, metav1.DeleteOptions{}); err != nil {
t.Logf("%v", err)
}
}()

buf := bytes.Buffer{}
req, err := http.NewRequest(http.MethodGet, kubeConfig.Host+"/apis/user.openshift.io/v1/users/~", &buf)
require.NoError(t, err)
req.Header.Set("Authorization", "Bearer "+shortLivedOAuthAccessToken.Name)

httpClient := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
},
}

resp, err := httpClient.Do(req)
require.NoError(t, err)

if resp.StatusCode != http.StatusOK {
t.Fatalf("access token should work. Recieved status %v", resp.Status)
}

// Wait for token to expire.
fmt.Println("Waiting for token to expire due to inactivity")
time.Sleep(305 * time.Second)

resp, err = httpClient.Do(req)
require.NoError(t, err)

if resp.StatusCode == http.StatusOK {
t.Fatalf("access token should not work. Recieved status %v", resp.Status)
}

updateOAuthServerClusterConfigSpec(t, configClient, 0)

// Wait for kube-apiserver to start the new rollout
test.WaitForKubeAPIServerStartProgressing(t, configClient)

// Wait until all kube-apiserver pods are restarted with new config.
test.WaitForKubeAPIServerClusterOperatorAvailableNotProgressingNotDegraded(t, configClient)

}

func updateOAuthServerClusterConfigSpec(t *testing.T, client *configclient.ConfigV1Client, duration time.Duration) {
oauthConfig, err := client.OAuths().Get(context.Background(), "cluster", metav1.GetOptions{})
require.NoError(t, err)

oauthConfig.Spec.TokenConfig.AccessTokenMaxAgeSeconds = int32(defaultAccessTokenMaxAgeSeconds)
oauthConfig.Spec.TokenConfig.AccessTokenInactivityTimeout = &metav1.Duration{Duration: duration}

oauthConfig, err = client.OAuths().Update(context.Background(), oauthConfig, metav1.UpdateOptions{})
require.NoError(t, err)
}
26 changes: 26 additions & 0 deletions test/library/cluster_operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,29 @@ func WaitForKubeAPIServerClusterOperatorAvailableNotProgressingNotDegraded(t *te
t.Fatal(err)
}
}

// WaitForKubeAPIServer waits for ClusterOperator/kube-apiserver to report
// status as active, progressing, and not failing.
func WaitForKubeAPIServerStartProgressing(t *testing.T, client configclient.ConfigV1Interface) {
err := wait.Poll(WaitPollInterval, WaitPollTimeout, func() (bool, error) {
clusterOperator, err := client.ClusterOperators().Get(context.TODO(), "kube-apiserver", metav1.GetOptions{})
if errors.IsNotFound(err) {
fmt.Println("ClusterOperator/kube-apiserver does not yet exist.")
return false, nil
}
if err != nil {
fmt.Println("Unable to retrieve ClusterOperator/kube-apiserver:", err)
return false, err
}
conditions := clusterOperator.Status.Conditions
available := clusteroperatorhelpers.IsStatusConditionPresentAndEqual(conditions, configv1.OperatorAvailable, configv1.ConditionTrue)
progressing := clusteroperatorhelpers.IsStatusConditionPresentAndEqual(conditions, configv1.OperatorProgressing, configv1.ConditionTrue)
notDegraded := clusteroperatorhelpers.IsStatusConditionPresentAndEqual(conditions, configv1.OperatorDegraded, configv1.ConditionFalse)
done := available && progressing && notDegraded
fmt.Printf("ClusterOperator/kube-apiserver: Available: %v Progressing: %v Degraded: %v\n", available, progressing, !notDegraded)
return done, nil
})
if err != nil {
t.Fatal(err)
}
}

0 comments on commit 59c903d

Please sign in to comment.