mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-09-01 05:37:20 +00:00
Use duration and renewBefore to control API cert rotation
These configuration knobs are much more human-understandable than the previous percentage-based threshold flag. We now allow users to set the lifetime of the serving cert via a ConfigMap. Previously this was hardcoded to 1 year. Signed-off-by: Andrew Keesler <akeesler@vmware.com>
This commit is contained in:
+25
-1
@@ -9,9 +9,10 @@ package api
|
||||
type Config struct {
|
||||
WebhookConfig WebhookConfigSpec `json:"webhook"`
|
||||
DiscoveryInfo DiscoveryInfoSpec `json:"discovery"`
|
||||
APIConfig APIConfigSpec `json:"api"`
|
||||
}
|
||||
|
||||
// WebhookConfig contains configuration knobs specific to Pinniped's use
|
||||
// WebhookConfig contains configuration knobs specific to pinniped's use
|
||||
// of a webhook for token validation.
|
||||
type WebhookConfigSpec struct {
|
||||
// URL contains the URL of the webhook that pinniped will use
|
||||
@@ -31,3 +32,26 @@ type DiscoveryInfoSpec struct {
|
||||
// URL contains the URL at which pinniped can be contacted.
|
||||
URL *string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
// APIConfigSpec contains configuration knobs for the pinniped API.
|
||||
//nolint: golint
|
||||
type APIConfigSpec struct {
|
||||
ServingCertificateConfig ServingCertificateConfigSpec `json:"servingCertificate"`
|
||||
}
|
||||
|
||||
// ServingCertificateConfigSpec contains the configuration knobs for the API's
|
||||
// serving certificate, i.e., the x509 certificate that it uses for the server
|
||||
// certificate in inbound TLS connections.
|
||||
type ServingCertificateConfigSpec struct {
|
||||
// DurationSeconds is the validity period, in seconds, of the API serving
|
||||
// certificate. By default, the serving certificate is issued for 31536000
|
||||
// seconds (1 year).
|
||||
DurationSeconds *int64 `json:"durationSeconds,omitempty"`
|
||||
|
||||
// RenewBeforeSeconds is the period of time, in seconds, that pinniped will
|
||||
// wait before rotating the serving certificate. This period of time starts
|
||||
// upon issuance of the serving certificate. This must be less than
|
||||
// DurationSeconds. By default, pinniped begins rotation after 23328000
|
||||
// seconds (about 9 months).
|
||||
RenewBeforeSeconds *int64 `json:"renewBeforeSeconds,omitempty"`
|
||||
}
|
||||
|
||||
+37
-1
@@ -13,10 +13,18 @@ import (
|
||||
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"github.com/suzerain-io/pinniped/internal/constable"
|
||||
"github.com/suzerain-io/pinniped/pkg/config/api"
|
||||
)
|
||||
|
||||
// FromPath loads an api.Config from a provided local file path.
|
||||
const (
|
||||
aboutAYear = 60 * 60 * 24 * 365
|
||||
about9Months = 60 * 60 * 24 * 30 * 9
|
||||
)
|
||||
|
||||
// FromPath loads an api.Config from a provided local file path, inserts any
|
||||
// defaults (from the api.Config documentation), and verifies that the config is
|
||||
// valid (per the api.Config documentation).
|
||||
//
|
||||
// Note! The api.Config file should contain base64-encoded WebhookCABundle data.
|
||||
// This function will decode that base64-encoded data to PEM bytes to be stored
|
||||
@@ -32,5 +40,33 @@ func FromPath(path string) (*api.Config, error) {
|
||||
return nil, fmt.Errorf("decode yaml: %w", err)
|
||||
}
|
||||
|
||||
maybeSetAPIDefaults(&config.APIConfig)
|
||||
|
||||
if err := validateAPI(&config.APIConfig); err != nil {
|
||||
return nil, fmt.Errorf("validate api: %w", err)
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
func maybeSetAPIDefaults(apiConfig *api.APIConfigSpec) {
|
||||
if apiConfig.ServingCertificateConfig.DurationSeconds == nil {
|
||||
apiConfig.ServingCertificateConfig.DurationSeconds = int64Ptr(aboutAYear)
|
||||
}
|
||||
|
||||
if apiConfig.ServingCertificateConfig.RenewBeforeSeconds == nil {
|
||||
apiConfig.ServingCertificateConfig.RenewBeforeSeconds = int64Ptr(about9Months)
|
||||
}
|
||||
}
|
||||
|
||||
func validateAPI(apiConfig *api.APIConfigSpec) error {
|
||||
if *apiConfig.ServingCertificateConfig.DurationSeconds < *apiConfig.ServingCertificateConfig.RenewBeforeSeconds {
|
||||
return constable.Error("durationSeconds cannot be smaller than renewBeforeSeconds")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func int64Ptr(i int64) *int64 {
|
||||
return &i
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ func TestFromPath(t *testing.T) {
|
||||
name string
|
||||
path string
|
||||
wantConfig *api.Config
|
||||
wantError string
|
||||
}{
|
||||
{
|
||||
name: "Happy",
|
||||
@@ -30,11 +31,17 @@ func TestFromPath(t *testing.T) {
|
||||
URL: "https://tuna.com/fish?marlin",
|
||||
CABundle: []byte("-----BEGIN CERTIFICATE-----..."),
|
||||
},
|
||||
APIConfig: api.APIConfigSpec{
|
||||
ServingCertificateConfig: api.ServingCertificateConfigSpec{
|
||||
DurationSeconds: int64Ptr(3600),
|
||||
RenewBeforeSeconds: int64Ptr(2400),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "NoDiscovery",
|
||||
path: "testdata/no-discovery.yaml",
|
||||
name: "Default",
|
||||
path: "testdata/default.yaml",
|
||||
wantConfig: &api.Config{
|
||||
DiscoveryInfo: api.DiscoveryInfoSpec{
|
||||
URL: nil,
|
||||
@@ -43,21 +50,34 @@ func TestFromPath(t *testing.T) {
|
||||
URL: "https://tuna.com/fish?marlin",
|
||||
CABundle: []byte("-----BEGIN CERTIFICATE-----..."),
|
||||
},
|
||||
APIConfig: api.APIConfigSpec{
|
||||
ServingCertificateConfig: api.ServingCertificateConfigSpec{
|
||||
DurationSeconds: int64Ptr(60 * 60 * 24 * 365), // about a year
|
||||
RenewBeforeSeconds: int64Ptr(60 * 60 * 24 * 30 * 9), // about 9 months
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "InvalidDurationRenewBefore",
|
||||
path: "testdata/invalid-duration-renew-before.yaml",
|
||||
wantError: "validate api: durationSeconds cannot be smaller than renewBeforeSeconds",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
config, err := FromPath(test.path)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.wantConfig, config)
|
||||
if test.wantError != "" {
|
||||
require.EqualError(t, err, test.wantError)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, test.wantConfig, config)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func stringPtr(s string) *string {
|
||||
sPtr := new(string)
|
||||
*sPtr = s
|
||||
return sPtr
|
||||
return &s
|
||||
}
|
||||
|
||||
Vendored
+4
@@ -4,3 +4,7 @@ discovery:
|
||||
webhook:
|
||||
url: https://tuna.com/fish?marlin
|
||||
caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tLi4u
|
||||
api:
|
||||
servingCertificate:
|
||||
durationSeconds: 3600
|
||||
renewBeforeSeconds: 2400
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
webhook:
|
||||
url: https://tuna.com/fish?marlin
|
||||
caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tLi4u
|
||||
api:
|
||||
servingCertificate:
|
||||
durationSeconds: 2400
|
||||
renewBeforeSeconds: 3600
|
||||
Reference in New Issue
Block a user