Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Managed Application Credentials #450

Closed
11 changes: 11 additions & 0 deletions cmd/gardener-extension-provider-openstack/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"fmt"
"os"

configvalidation "github.com/gardener/gardener-extension-provider-openstack/pkg/apis/config/validation"
openstackinstall "github.com/gardener/gardener-extension-provider-openstack/pkg/apis/openstack/install"
openstackcmd "github.com/gardener/gardener-extension-provider-openstack/pkg/cmd"
openstackbackupbucket "github.com/gardener/gardener-extension-provider-openstack/pkg/controller/backupbucket"
Expand All @@ -30,6 +31,7 @@ import (
"github.com/gardener/gardener-extension-provider-openstack/pkg/controller/healthcheck"
openstackinfrastructure "github.com/gardener/gardener-extension-provider-openstack/pkg/controller/infrastructure"
openstackworker "github.com/gardener/gardener-extension-provider-openstack/pkg/controller/worker"
"github.com/gardener/gardener-extension-provider-openstack/pkg/features"
"github.com/gardener/gardener-extension-provider-openstack/pkg/openstack"
openstackcontrolplaneexposure "github.com/gardener/gardener-extension-provider-openstack/pkg/webhook/controlplaneexposure"

Expand Down Expand Up @@ -163,6 +165,14 @@ func NewControllerManagerCommand(ctx context.Context) *cobra.Command {
return fmt.Errorf("error completing options: %w", err)
}

if err := configvalidation.ValidateControllerConfig(configFileOpts.Completed().Config); err != nil {
return fmt.Errorf("invalid controller config: %w", err)
}

if err := features.ExtensionFeatureGate.SetFromMap(configFileOpts.Completed().Config.FeatureGates); err != nil {
return err
}

util.ApplyClientConnectionConfigurationToRESTConfig(configFileOpts.Completed().Config.ClientConnection, restOpts.Completed().Config)

if workerReconcileOpts.Completed().DeployCRDs {
Expand Down Expand Up @@ -201,6 +211,7 @@ func NewControllerManagerCommand(ctx context.Context) *cobra.Command {
configFileOpts.Completed().ApplyETCDStorage(&openstackcontrolplaneexposure.DefaultAddOptions.ETCDStorage)
configFileOpts.Completed().ApplyHealthCheckConfig(&healthcheck.DefaultAddOptions.HealthCheckConfig)
configFileOpts.Completed().ApplyBastionConfig(&openstackbastion.DefaultAddOptions.BastionConfig)
configFileOpts.Completed().ApplyAppCredentialConfig(&openstackinfrastructure.DefaultAddOptions.AppCredentialConfig)
healthCheckCtrlOpts.Completed().Apply(&healthcheck.DefaultAddOptions.Controller)
backupBucketCtrlOpts.Completed().Apply(&openstackbackupbucket.DefaultAddOptions.Controller)
backupEntryCtrlOpts.Completed().Apply(&openstackbackupentry.DefaultAddOptions.Controller)
Expand Down
2 changes: 2 additions & 0 deletions cmd/gardener-extension-provider-openstack/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ import (
"os"

"github.com/gardener/gardener-extension-provider-openstack/cmd/gardener-extension-provider-openstack/app"
"github.com/gardener/gardener-extension-provider-openstack/pkg/features"

"github.com/gardener/gardener/pkg/logger"
runtimelog "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/manager/signals"
)

func main() {
features.RegisterExtensionFeatureGate()
runtimelog.SetLogger(logger.ZapLogger(false))
cmd := app.NewControllerManagerCommand(signals.SetupSignalHandler())

Expand Down
17 changes: 17 additions & 0 deletions docs/usage-as-end-user.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,23 @@ Alternatively, for authentication with application credentials see [Keystone App
⚠️ Depending on your API usage it can be problematic to reuse the same provider credentials for different Shoot clusters due to rate limits.
Please consider spreading your Shoots over multiple credentials from different tenants if you are hitting those limits.

### Managed Application Credentials

The Gardener Openstack extension is capable to manage for each Shoot cluster an own application credential that is used to interact with the Openstack layer on behalf of the provided Openstack user.

Those application credentials are managed by the Gardener Openstack extension.
The extension will take care that the managed application credential get created, deleted and constanntly rotated and exchanged (including the case when the Openstack user changes) as part of the regular Shoot operations like reconcilation or deletion.

Managed application credentials will be used by default
- if the operator of the Gardener installation enables the managed application credential usage (find more information [here](usage-as-operator.md#Managed-application-credentials))
- if the provided Openstack user itself is not an application credential
- for Shoot cluster larger or equal than `v1.19`

Using managed application credentials to interact with the Openstack layer has several advantages compared to the usage of the Openstack user itself e.g. the application credential will be still functional even if the credentials of the owning Openstack user are rotated and not propagated to the system or that a managed application credential does exclusivly belong to one Shoot cluster and does therefore not get influenced/throttled by other operation run with the same Openstack user etc.

Be aware: In case the Openstack user for a Shoot cluster is changed the managed application credential can end up in an orphan state where the Gardener Openstack extension cannot manage it anymore.
Therefore each managed application credential has set an expiration date on the Openstack layer.

## `InfrastructureConfig`

The infrastructure configuration mainly describes how the network layout looks like in order to create the shoot worker nodes in a later step, thus, prepares everything relevant to create VMs, load balancers, volumes, etc.
Expand Down
26 changes: 26 additions & 0 deletions docs/usage-as-operator.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,29 @@ spec:
loadBalancerProviders:
- name: haproxy
```


## Miscellaneous

### Managed application credentials

The Gardener Openstack extension is able to manage for each Shoot cluster an application credential to interact with the Openstack layer.
Find more general information about managed application credentials [here](usage-as-end-user.md#managed-application-credentials).

As an operator you can define the managed application credential configuration via the controller configuration of the Gardener Openstack extension.
Here is an example for the managed application configuration as part of the controller configuration.

```yaml
managedApplicationCredential:
enabled: true
lifetime: 48h
openstackExpirationPeriod: 720h
renewThreshold: 48h
```

Via `.managedApplicationCredential.enabled` the managed application credential usage can be globally enabled or disabled.
The `.managedApplicationCredential.lifetime` field define how long an application credential should stay after creation before it get replaced during the next Shoot operation.
The fields `.managedApplicationCredential.openstackExpirationPeriod` and `managedApplicationCredential.renewThreshold` are related to expiration on the Openstack layer.
The `openstackExpirationPeriod` field define how long an application credential live after creation before it get disabled by Openstack itself.
The `renewThreshold` is a threshold before the Openstack expiration date.
Once the threshold is reached the managed application credential will be renewed during the next Shoot operation.
102 changes: 102 additions & 0 deletions hack/api-reference/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,108 @@ BastionConfig
<p>BastionConfig the config for the Bastion</p>
</td>
</tr>
<tr>
<td>
<code>managedApplicationCredential</code></br>
<em>
<a href="#openstack.provider.extensions.config.gardener.cloud/v1alpha1.ApplicationCredentialConfig">
ApplicationCredentialConfig
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>ApplicationCrednentialConfig defines the configuration for managed application credentials.</p>
</td>
</tr>
<tr>
<td>
<code>featureGates</code></br>
<em>
map[string]bool
</em>
</td>
<td>
<em>(Optional)</em>
<p>FeatureGates is a map of feature names to bools that enable
or disable alpha/experimental features.
Default: nil</p>
</td>
</tr>
</tbody>
</table>
<h3 id="openstack.provider.extensions.config.gardener.cloud/v1alpha1.ApplicationCredentialConfig">ApplicationCredentialConfig
</h3>
<p>
(<em>Appears on:</em>
<a href="#openstack.provider.extensions.config.gardener.cloud/v1alpha1.ControllerConfiguration">ControllerConfiguration</a>)
</p>
<p>
<p>ApplicationCredentialConfig defines the configuration for managed application credentials.</p>
</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>lifetime</code></br>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.15/#duration-v1-meta">
Kubernetes meta/v1.Duration
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>Lifetime define how long a managed application credentials are valid.
Once the creation time + lifetime of an application credential is expired
it will be renewed once it is next reconciled.
Defaults to 48h.</p>
</td>
</tr>
<tr>
<td>
<code>openstackExpirationPeriod</code></br>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.15/#duration-v1-meta">
Kubernetes meta/v1.Duration
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>OpenstackExpirationPeriod is a duration to calculate the expiration time
of a managed application credential on the Openstack layer.
The expiration time will be calculated in the following way:</p>
<p>expiration time = creation time + expiration period</p>
<p>This is a security measure to ensure that managed appplication credentials
get deactivated even if the owning user of the application credential
is not available to the openstack-extension anymore and therefore
cannot be removed by the openstack-extension on its own.
Defaults to 720h = 30d.</p>
</td>
</tr>
<tr>
<td>
<code>renewThreshold</code></br>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.15/#duration-v1-meta">
Kubernetes meta/v1.Duration
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>RenewThreshold defines a threshold before the openstack expiration time.
Once the threshold is reached the managed application credential need to be renewed.
Defaults to 72h.</p>
</td>
</tr>
</tbody>
</table>
<h3 id="openstack.provider.extensions.config.gardener.cloud/v1alpha1.BastionConfig">BastionConfig
Expand Down
31 changes: 31 additions & 0 deletions pkg/apis/config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ type ControllerConfiguration struct {
HealthCheckConfig *healthcheckconfig.HealthCheckConfig
// BastionConfig is the config for the Bastion
BastionConfig *BastionConfig
// ApplicationCrednentialConfig defines the configuration for managed application credentials.
ApplicationCredentialConfig *ApplicationCredentialConfig
// FeatureGates is a map of feature names to bools that enable
// or disable alpha/experimental features.
// Default: nil
FeatureGates map[string]bool
}

// ETCD is an etcd configuration.
Expand Down Expand Up @@ -68,3 +74,28 @@ type BastionConfig struct {
// FlavorRef is the openstack flavorRef reference
FlavorRef string
}

// ApplicationCredentialConfig defines the configuration for managed application credentials.
type ApplicationCredentialConfig struct {
// Lifetime define how long a managed application credentials are valid.
// Once the creation time + lifetime of an application credential is expired
// it will be renewed once it is next reconciled.
// Defaults to 84h.
Lifetime *metav1.Duration
// OpenstackExpirationPeriod is a duration to calculate the expiration time
// of a managed application credential on the Openstack layer.
// The expiration time will be calculated in the following way:
//
// expiration time = creation time + expiration period
//
// This is a security measure to ensure that managed appplication credentials
// get deactivated even if the owning user of the application credential
// is not available to the openstack-extension anymore and therefore
// cannot be removed by the openstack-extension on its own.
// Defaults to 720h = 30d.
OpenstackExpirationPeriod *metav1.Duration
// RenewThreshold defines a threshold before the openstack expiration time.
// Once the threshold is reached the managed application credential need to be renewed.
// Defaults to 72h.
RenewThreshold *metav1.Duration
}
59 changes: 59 additions & 0 deletions pkg/apis/config/v1alpha1/default_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) 2022 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, v. 2 except as noted otherwise in the LICENSE file
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package v1alpha1_test

import (
"time"

. "github.com/gardener/gardener-extension-provider-openstack/pkg/apis/config/v1alpha1"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

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

var _ = Describe("Defaults", func() {
Context("#SetDefaults_ControllerConfiguration", func() {
var obj *ControllerConfiguration

BeforeEach(func() {
obj = &ControllerConfiguration{}
})

It("should default the controller configuration", func() {
SetDefaults_ControllerConfiguration(obj)

Expect(obj.ApplicationCredentialConfig).NotTo(BeNil())
})
})

Context("#SetDefaults_ApplicationCredentialConfig", func() {
var obj *ApplicationCredentialConfig

BeforeEach(func() {
obj = &ApplicationCredentialConfig{}
})

It("should default the application crendential config", func() {
SetDefaults_ApplicationCredentialConfig(obj)

Expect(*obj.Lifetime).To(Equal(metav1.Duration{Duration: time.Hour * 48}))
Expect(*obj.OpenstackExpirationPeriod).To(Equal(metav1.Duration{Duration: time.Hour * 24 * 30}))
Expect(*obj.RenewThreshold).To(Equal(metav1.Duration{Duration: time.Hour * 24 * 3}))
})
})

})
31 changes: 31 additions & 0 deletions pkg/apis/config/v1alpha1/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,40 @@
package v1alpha1

import (
"time"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)

func addDefaultingFuncs(scheme *runtime.Scheme) error {
return RegisterDefaults(scheme)
}

// SetDefaults_ControllerConfiguration sets defaults for the ControllerConfiguration.
func SetDefaults_ControllerConfiguration(obj *ControllerConfiguration) {
if obj.ApplicationCredentialConfig == nil {
obj.ApplicationCredentialConfig = &ApplicationCredentialConfig{}
}
}

// SetDefaults_ApplicationCredentialConfig sets defaults for the ApplicationCrendentialConfig.
func SetDefaults_ApplicationCredentialConfig(obj *ApplicationCredentialConfig) {
if obj.Lifetime == nil {
obj.Lifetime = &metav1.Duration{
Duration: time.Hour * 48,
}
}

if obj.OpenstackExpirationPeriod == nil {
obj.OpenstackExpirationPeriod = &metav1.Duration{
Duration: time.Hour * 720,
}
}

if obj.RenewThreshold == nil {
obj.RenewThreshold = &metav1.Duration{
Duration: time.Hour * 72,
}
}
}
Loading