Merge pull request #1517 from venezia/iss-1513

storage/kubernetes: Removing Kubernetes TPR support
This commit is contained in:
Joel Speed 2019-08-14 14:45:12 +01:00 committed by GitHub
commit ab08d7b3a4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 23 additions and 261 deletions

View file

@ -115,92 +115,16 @@ subjects:
``` ```
## DEPRECATED: Kubernetes third party resources(TPRs) ### Removed: Kubernetes third party resources(TPRs)
__NOTE:__ TPRs are deprecated as of Kubernetes version 1.8. TPR support in dex has been removed. The last version to support TPR
is [v2.17.0](https://github.com/dexidp/dex/tree/v2.17.0)
The default behavior of dex from release v2.7.0 onwards is to utilize CRDs to manage its custom resources. If users would like to use dex with a Kubernetes version lower than 1.7, they will have to force dex to use TPRs instead of CRDs. If you are currently running dex using TPRs, you will need to [migrate to CRDs](https://github.com/dexidp/dex/blob/v2.17.0/Documentation/storage.md#migrating-from-tprs-to-crds)
before you upgrade to a post v2.17 dex. The script mentioned in the instructions can be [found here](https://github.com/dexidp/dex/blob/v2.17.0/scripts/dump-tprs)
These instructions have been preserved for anybody who needs to use an older version of Dex and/or Kubernetes, but this is not the recommended approach. See [Migrating from TPRs to CRDs](#migrating-from-tprs-to-crds) below for information on migrating an existing installation to the new approach.
If you do wish to use TPRs, you may do so by setting the `UseTPR` flag in the storage configuration as shown below: ### Configuration
```
storage:
type: kubernetes
config:
kubeConfigFile: kubeconfig
useTPR: true
```
The `ThirdPartyResource` type acts as a description for the new resource a user wishes to create. The following an example of a resource managed by dex:
```
kind: ThirdPartyResource
apiVersion: extensions/v1beta1
metadata:
name: o-auth2-client.oidc.coreos.com
versions:
- name: v1
description: "An OAuth2 client."
```
Once the `ThirdPartyResource` is created, custom resources can be created at a namespace level (though there will be a gap between the `ThirdPartyResource` being created and the API server accepting the custom resource). While most fields are user defined, the API server still respects the common `ObjectMeta` and `TypeMeta` values. For example names are still restricted to a small set of characters, and the `resourceVersion` field can be used for an [atomic compare and swap][k8s-api].
The following is an example of a custom `OAuth2Client` resource:
```
# Standard Kubernetes resource fields
kind: OAuth2Client
apiVersion: oidc.coreos.com/v1
metadata:
namespace: foobar
name: ( opaque hash )
# Custom fields defined by dex.
clientID: "aclientid"
clientSecret: "clientsecret"
redirectURIs:
- "https://app.example.com/callback"
```
The `ThirdPartyResource` type and the custom resources can be queried, deleted, and edited like any other resource using `kubectl`.
```
kubectl get thirdpartyresources # list third party resources registered on the clusters
kubectl get --namespace=foobar oauth2clients # list oauth2 clients in a given namespace
```
To reduce administrative overhead, dex creates and manages its own third party resources and may create new ones during upgrades. While not strictly required we feel this is important for reasonable updates. Though, as a result, dex requires access to the non-namespaced `ThirdPartyResource` type. For example, clusters using RBAC authorization would need to create the following roles and bindings:
```
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1alpha1
metadata:
name: dex
rules:
- apiGroups: ["oidc.coreos.com"] # API group created by dex
resources: ["*"]
verbs: ["*"]
nonResourceURLs: []
- apiGroups: ["extensions"]
resources: ["thirdpartyresources"]
verbs: ["create"] # To manage its own resources identity must be able to create thirdpartyresources.
nonResourceURLs: []
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1alpha1
metadata:
name: dex
subjects:
- kind: ServiceAccount
name: dex # Service account assigned to the dex pod.
namespace: demo-namespace # The namespace dex is running in.
roleRef:
kind: ClusterRole
name: identity
apiVersion: rbac.authorization.k8s.io/v1alpha1
```
The storage configuration is extremely limited since installations running outside a Kubernetes cluster would likely prefer a different storage option. An example configuration for dex running inside Kubernetes: The storage configuration is extremely limited since installations running outside a Kubernetes cluster would likely prefer a different storage option. An example configuration for dex running inside Kubernetes:
@ -213,39 +137,6 @@ storage:
Dex determines the namespace it's running in by parsing the service account token automatically mounted into its pod. Dex determines the namespace it's running in by parsing the service account token automatically mounted into its pod.
## Migrating from TPRs to CRDs
This section descibes how users can migrate storage data in dex when upgrading from an older version of kubernetes (lower than 1.7). This involves creating new CRDs and moving over the data from TPRs.
The flow of the migration process is as follows:
1. Stop running old version of Dex (lower than v2.7.0).
2. Create new CRDs by running the following command:
```
kubectl apply -f scripts/manifests/crds/
```
Note that the newly created CRDs have `dex.coreos.com` as their group and will not conflict with the existing TPR resources which have `oidc.coreos.com` as the group.
3. Migrate data from existing TPRs to CRDs by running the following commands for each of the TPRs:
1. Export `DEX_NAMESPACE` to be the namespace in which the TPRs exist and run the following script to store TPR definition in a temporary yaml file:
```
export DEX_NAMESPACE="<namespace-value>"
./scripts/dump-tprs > out.yaml
```
2. Update `out.yaml` to change the apiVersion to `apiVersion: dex.coreos.com/v1` and delete the `resourceVersion` field.
```
sed 's/oidc.coreos.com/dex.coreos.com/' out.yaml
```
```
sed 's/resourceVersion: ".*"//' out.yaml
```
3. Create the resource object using the following command:
```
kubectl apply -f out.yaml
```
4. Confirm that the resource got created using the following get command:
```
kubectl get --namespace=tectonic-system <TPR-name>.dex.coreos.com -o yaml
```
4. Update to new version of Dex (v2.7.0 or higher) which will use CRDs instead of TPRs.
## SQL ## SQL
Dex supports two flavors of SQL: SQLite3 and Postgres. Dex supports two flavors of SQL: SQLite3 and Postgres.

View file

@ -1,13 +0,0 @@
#!/bin/bash
set -e
if [ -z $DEX_NAMESPACE ];then
echo "Must export \$DEX_NAMESPACE"
exit
fi
for RESOURCE in authcodes authrequests connectors oauth2clients offlinesessionses refreshtokens passwords signingkeies; do
kubectl get --namespace=$DEX_NAMESPACE $RESOURCE.oidc.coreos.com -o yaml
done

View file

@ -253,7 +253,7 @@ func (c *client) put(resource, name string, v interface{}) error {
return checkHTTPErr(resp, http.StatusOK) return checkHTTPErr(resp, http.StatusOK)
} }
func newClient(cluster k8sapi.Cluster, user k8sapi.AuthInfo, namespace string, logger log.Logger, useTPR bool) (*client, error) { func newClient(cluster k8sapi.Cluster, user k8sapi.AuthInfo, namespace string, logger log.Logger) (*client, error) {
tlsConfig := cryptopasta.DefaultTLSConfig() tlsConfig := cryptopasta.DefaultTLSConfig()
data := func(b string, file string) ([]byte, error) { data := func(b string, file string) ([]byte, error) {
if b != "" { if b != "" {
@ -329,11 +329,7 @@ func newClient(cluster k8sapi.Cluster, user k8sapi.AuthInfo, namespace string, l
} }
} }
// the API Group and version differ depending on if CRDs or TPRs are used.
apiVersion := "dex.coreos.com/v1" apiVersion := "dex.coreos.com/v1"
if useTPR {
apiVersion = "oidc.coreos.com/v1"
}
logger.Infof("kubernetes client apiVersion = %s", apiVersion) logger.Infof("kubernetes client apiVersion = %s", apiVersion)
return &client{ return &client{

View file

@ -16,32 +16,6 @@ limitations under the License.
package k8sapi package k8sapi
// A ThirdPartyResource is a generic representation of a resource, it is used by add-ons and plugins to add new resource
// types to the API. It consists of one or more Versions of the api.
type ThirdPartyResource struct {
TypeMeta `json:",inline"`
// Standard object metadata
ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Description is the description of this object.
Description string `json:"description,omitempty" protobuf:"bytes,2,opt,name=description"`
// Versions are versions for this third party object
Versions []APIVersion `json:"versions,omitempty" protobuf:"bytes,3,rep,name=versions"`
}
// ThirdPartyResourceList is a list of ThirdPartyResources.
type ThirdPartyResourceList struct {
TypeMeta `json:",inline"`
// Standard list metadata.
ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Items is the list of ThirdPartyResources.
Items []ThirdPartyResource `json:"items" protobuf:"bytes,2,rep,name=items"`
}
// An APIVersion represents a single concrete version of an object model. // An APIVersion represents a single concrete version of an object model.
type APIVersion struct { type APIVersion struct {
// Name of this version (e.g. 'v1'). // Name of this version (e.g. 'v1').

View file

@ -38,7 +38,6 @@ const (
type Config struct { type Config struct {
InCluster bool `json:"inCluster"` InCluster bool `json:"inCluster"`
KubeConfigFile string `json:"kubeConfigFile"` KubeConfigFile string `json:"kubeConfigFile"`
UseTPR bool `json:"useTPR"` // Flag option to use TPRs instead of CRDs
} }
// Open returns a storage using Kubernetes third party resource. // Open returns a storage using Kubernetes third party resource.
@ -78,7 +77,7 @@ func (c *Config) open(logger log.Logger, waitForResources bool) (*client, error)
return nil, err return nil, err
} }
cli, err := newClient(cluster, user, namespace, logger, c.UseTPR) cli, err := newClient(cluster, user, namespace, logger)
if err != nil { if err != nil {
return nil, fmt.Errorf("create client: %v", err) return nil, fmt.Errorf("create client: %v", err)
} }
@ -86,7 +85,7 @@ func (c *Config) open(logger log.Logger, waitForResources bool) (*client, error)
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
logger.Info("creating custom Kubernetes resources") logger.Info("creating custom Kubernetes resources")
if !cli.registerCustomResources(c.UseTPR) { if !cli.registerCustomResources() {
if waitForResources { if waitForResources {
cancel() cancel()
return nil, fmt.Errorf("failed creating custom resources") return nil, fmt.Errorf("failed creating custom resources")
@ -98,7 +97,7 @@ func (c *Config) open(logger log.Logger, waitForResources bool) (*client, error)
logger.Errorf("failed creating custom resources: %v", err) logger.Errorf("failed creating custom resources: %v", err)
go func() { go func() {
for { for {
if cli.registerCustomResources(c.UseTPR) { if cli.registerCustomResources() {
return return
} }
@ -125,39 +124,28 @@ func (c *Config) open(logger log.Logger, waitForResources bool) (*client, error)
// registerCustomResources attempts to create the custom resources dex // registerCustomResources attempts to create the custom resources dex
// requires or identifies that they're already enabled. This function creates // requires or identifies that they're already enabled. This function creates
// third party resources(TPRs) or custom resource definitions(CRDs) depending // custom resource definitions(CRDs)
// on the `useTPR` flag passed in as an argument.
// It logs all errors, returning true if the resources were created successfully. // It logs all errors, returning true if the resources were created successfully.
// //
// Creating a custom resource does not mean that they'll be immediately available. // Creating a custom resource does not mean that they'll be immediately available.
func (cli *client) registerCustomResources(useTPR bool) (ok bool) { func (cli *client) registerCustomResources() (ok bool) {
ok = true ok = true
length := len(customResourceDefinitions) length := len(customResourceDefinitions)
if useTPR {
length = len(thirdPartyResources)
}
for i := 0; i < length; i++ { for i := 0; i < length; i++ {
var err error var err error
var resourceName string var resourceName string
if useTPR { r := customResourceDefinitions[i]
r := thirdPartyResources[i] var i interface{}
err = cli.postResource("extensions/v1beta1", "", "thirdpartyresources", r) cli.logger.Infof("checking if custom resource %s has been created already...", r.ObjectMeta.Name)
resourceName = r.ObjectMeta.Name if err := cli.list(r.Spec.Names.Plural, &i); err == nil {
cli.logger.Infof("The custom resource %s already available, skipping create", r.ObjectMeta.Name)
continue
} else { } else {
r := customResourceDefinitions[i] cli.logger.Infof("failed to list custom resource %s, attempting to create: %v", r.ObjectMeta.Name, err)
var i interface{}
cli.logger.Infof("checking if custom resource %s has been created already...", r.ObjectMeta.Name)
if err := cli.list(r.Spec.Names.Plural, &i); err == nil {
cli.logger.Infof("The custom resource %s already available, skipping create", r.ObjectMeta.Name)
continue
} else {
cli.logger.Infof("failed to list custom resource %s, attempting to create: %v", r.ObjectMeta.Name, err)
}
err = cli.postResource("apiextensions.k8s.io/v1beta1", "", "customresourcedefinitions", r)
resourceName = r.ObjectMeta.Name
} }
err = cli.postResource("apiextensions.k8s.io/v1beta1", "", "customresourcedefinitions", r)
resourceName = r.ObjectMeta.Name
if err != nil { if err != nil {
switch err { switch err {
@ -424,7 +412,7 @@ func (cli *client) DeleteRefresh(id string) error {
} }
func (cli *client) DeletePassword(email string) error { func (cli *client) DeletePassword(email string) error {
// Check for hash collition. // Check for hash collision.
p, err := cli.getPassword(email) p, err := cli.getPassword(email)
if err != nil { if err != nil {
return err return err
@ -433,7 +421,7 @@ func (cli *client) DeletePassword(email string) error {
} }
func (cli *client) DeleteOfflineSessions(userID string, connID string) error { func (cli *client) DeleteOfflineSessions(userID string, connID string) error {
// Check for hash collition. // Check for hash collision.
o, err := cli.getOfflineSessions(userID, connID) o, err := cli.getOfflineSessions(userID, connID)
if err != nil { if err != nil {
return err return err

View file

@ -10,80 +10,6 @@ import (
"github.com/dexidp/dex/storage/kubernetes/k8sapi" "github.com/dexidp/dex/storage/kubernetes/k8sapi"
) )
var tprMeta = k8sapi.TypeMeta{
APIVersion: "extensions/v1beta1",
Kind: "ThirdPartyResource",
}
// The set of third party resources required by the storage. These are managed by
// the storage so it can migrate itself by creating new resources.
var thirdPartyResources = []k8sapi.ThirdPartyResource{
{
ObjectMeta: k8sapi.ObjectMeta{
Name: "auth-code.oidc.coreos.com",
},
TypeMeta: tprMeta,
Description: "A code which can be claimed for an access token.",
Versions: []k8sapi.APIVersion{{Name: "v1"}},
},
{
ObjectMeta: k8sapi.ObjectMeta{
Name: "auth-request.oidc.coreos.com",
},
TypeMeta: tprMeta,
Description: "A request for an end user to authorize a client.",
Versions: []k8sapi.APIVersion{{Name: "v1"}},
},
{
ObjectMeta: k8sapi.ObjectMeta{
Name: "o-auth2-client.oidc.coreos.com",
},
TypeMeta: tprMeta,
Description: "An OpenID Connect client.",
Versions: []k8sapi.APIVersion{{Name: "v1"}},
},
{
ObjectMeta: k8sapi.ObjectMeta{
Name: "signing-key.oidc.coreos.com",
},
TypeMeta: tprMeta,
Description: "Keys used to sign and verify OpenID Connect tokens.",
Versions: []k8sapi.APIVersion{{Name: "v1"}},
},
{
ObjectMeta: k8sapi.ObjectMeta{
Name: "refresh-token.oidc.coreos.com",
},
TypeMeta: tprMeta,
Description: "Refresh tokens for clients to continuously act on behalf of an end user.",
Versions: []k8sapi.APIVersion{{Name: "v1"}},
},
{
ObjectMeta: k8sapi.ObjectMeta{
Name: "password.oidc.coreos.com",
},
TypeMeta: tprMeta,
Description: "Passwords managed by the OIDC server.",
Versions: []k8sapi.APIVersion{{Name: "v1"}},
},
{
ObjectMeta: k8sapi.ObjectMeta{
Name: "offline-sessions.oidc.coreos.com",
},
TypeMeta: tprMeta,
Description: "User sessions with an active refresh token.",
Versions: []k8sapi.APIVersion{{Name: "v1"}},
},
{
ObjectMeta: k8sapi.ObjectMeta{
Name: "connector.oidc.coreos.com",
},
TypeMeta: tprMeta,
Description: "Connectors available for login",
Versions: []k8sapi.APIVersion{{Name: "v1"}},
},
}
var crdMeta = k8sapi.TypeMeta{ var crdMeta = k8sapi.TypeMeta{
APIVersion: "apiextensions.k8s.io/v1beta1", APIVersion: "apiextensions.k8s.io/v1beta1",
Kind: "CustomResourceDefinition", Kind: "CustomResourceDefinition",