Add aggregatedAPIServerPort to the Concierge's static ConfigMap

- Allow the port number to be configured to any value within the
  range 1024 to 65535
- This commit does not include adding new config knobs to the ytt
  values file, so while it is possible to change this port without
  needing to recompile, it is not convenient
This commit is contained in:
Ryan Richard 2021-11-16 16:43:51 -08:00
parent c570f08b2b
commit 2383a88612
7 changed files with 66 additions and 16 deletions

View File

@ -29,8 +29,8 @@ FROM gcr.io/distroless/static:nonroot@sha256:bca3c203cdb36f5914ab8568e4c25165643
# Copy the server binary from the build-env stage. # Copy the server binary from the build-env stage.
COPY --from=build-env /usr/local/bin /usr/local/bin COPY --from=build-env /usr/local/bin /usr/local/bin
# Document the ports # Document the default server ports for the various server apps
EXPOSE 8080 8443 EXPOSE 8080 8443 8444 10250
# Run as non-root for security posture # Run as non-root for security posture
# Use the same non-root user as https://github.com/GoogleContainerTools/distroless/blob/fc3c4eaceb0518900f886aae90407c43be0a42d9/base/base.bzl#L9 # Use the same non-root user as https://github.com/GoogleContainerTools/distroless/blob/fc3c4eaceb0518900f886aae90407c43be0a42d9/base/base.bzl#L9

View File

@ -58,6 +58,7 @@ data:
durationSeconds: (@= str(data.values.api_serving_certificate_duration_seconds) @) durationSeconds: (@= str(data.values.api_serving_certificate_duration_seconds) @)
renewBeforeSeconds: (@= str(data.values.api_serving_certificate_renew_before_seconds) @) renewBeforeSeconds: (@= str(data.values.api_serving_certificate_renew_before_seconds) @)
apiGroupSuffix: (@= data.values.api_group_suffix @) apiGroupSuffix: (@= data.values.api_group_suffix @)
# aggregatedAPIServerPort may be set here, although other YAML references to the default port (10250) may also need to be updated
names: names:
servingCertificateSecret: (@= defaultResourceNameWithSuffix("api-tls-serving-certificate") @) servingCertificateSecret: (@= defaultResourceNameWithSuffix("api-tls-serving-certificate") @)
credentialIssuer: (@= defaultResourceNameWithSuffix("config") @) credentialIssuer: (@= defaultResourceNameWithSuffix("config") @)
@ -175,7 +176,7 @@ spec:
livenessProbe: livenessProbe:
httpGet: httpGet:
path: /healthz path: /healthz
port: 8443 port: 10250
scheme: HTTPS scheme: HTTPS
initialDelaySeconds: 2 initialDelaySeconds: 2
timeoutSeconds: 15 timeoutSeconds: 15
@ -184,7 +185,7 @@ spec:
readinessProbe: readinessProbe:
httpGet: httpGet:
path: /healthz path: /healthz
port: 8443 port: 10250
scheme: HTTPS scheme: HTTPS
initialDelaySeconds: 2 initialDelaySeconds: 2
timeoutSeconds: 3 timeoutSeconds: 3
@ -251,7 +252,7 @@ spec:
ports: ports:
- protocol: TCP - protocol: TCP
port: 443 port: 443
targetPort: 8443 targetPort: 10250
--- ---
apiVersion: v1 apiVersion: v1
kind: Service kind: Service

View File

@ -41,7 +41,7 @@ kube_cert_agent_image:
image_pull_dockerconfigjson: #! e.g. {"auths":{"https://registry.example.com":{"username":"USERNAME","password":"PASSWORD","auth":"BASE64_ENCODED_USERNAME_COLON_PASSWORD"}}} image_pull_dockerconfigjson: #! e.g. {"auths":{"https://registry.example.com":{"username":"USERNAME","password":"PASSWORD","auth":"BASE64_ENCODED_USERNAME_COLON_PASSWORD"}}}
#! Pinniped will try to guess the right K8s API URL for sharing that information with potential clients. #! Pinniped will try to guess the right K8s API URL for sharing that information with potential clients.
#! This settings allows the guess to be overridden. #! This setting allows the guess to be overridden.
#! Optional. #! Optional.
discovery_url: #! e.g., https://example.com discovery_url: #! e.g., https://example.com

View File

@ -168,6 +168,7 @@ func (a *App) runServer(ctx context.Context) error {
certIssuer, certIssuer,
buildControllers, buildControllers,
*cfg.APIGroupSuffix, *cfg.APIGroupSuffix,
*cfg.AggregatedAPIServerPort,
scheme, scheme,
loginGV, loginGV,
identityGV, identityGV,
@ -193,6 +194,7 @@ func getAggregatedAPIServerConfig(
issuer issuer.ClientCertIssuer, issuer issuer.ClientCertIssuer,
buildControllers controllerinit.RunnerBuilder, buildControllers controllerinit.RunnerBuilder,
apiGroupSuffix string, apiGroupSuffix string,
aggregatedAPIServerPort int64,
scheme *runtime.Scheme, scheme *runtime.Scheme,
loginConciergeGroupVersion, identityConciergeGroupVersion schema.GroupVersion, loginConciergeGroupVersion, identityConciergeGroupVersion schema.GroupVersion,
) (*apiserver.Config, error) { ) (*apiserver.Config, error) {
@ -207,7 +209,9 @@ func getAggregatedAPIServerConfig(
) )
recommendedOptions.Etcd = nil // turn off etcd storage because we don't need it yet recommendedOptions.Etcd = nil // turn off etcd storage because we don't need it yet
recommendedOptions.SecureServing.ServerCert.GeneratedCert = dynamicCertProvider recommendedOptions.SecureServing.ServerCert.GeneratedCert = dynamicCertProvider
recommendedOptions.SecureServing.BindPort = 8443 // Don't run on default 443 because that requires root
// This port is configurable. It should be safe to cast because the config reader already validated it.
recommendedOptions.SecureServing.BindPort = int(aggregatedAPIServerPort)
serverConfig := genericapiserver.NewRecommendedConfig(codecs) serverConfig := genericapiserver.NewRecommendedConfig(codecs)
// Note that among other things, this ApplyTo() function copies // Note that among other things, this ApplyTo() function copies

View File

@ -21,6 +21,12 @@ import (
const ( const (
aboutAYear = 60 * 60 * 24 * 365 aboutAYear = 60 * 60 * 24 * 365
about9Months = 60 * 60 * 24 * 30 * 9 about9Months = 60 * 60 * 24 * 30 * 9
// Use 10250 because it happens to be the same port on which the Kubelet listens, so some cluster types
// are more permissive with servers that run on this port. For example, GKE private clusters do not
// allow traffic from the control plane to most ports, but do allow traffic to port 10250. This allows
// the Concierge to work without additional configuration on these types of clusters.
aggregatedAPIServerPortDefault = 10250
) )
// FromPath loads an Config from a provided local file path, inserts any // FromPath loads an Config from a provided local file path, inserts any
@ -42,6 +48,7 @@ func FromPath(path string) (*Config, error) {
} }
maybeSetAPIDefaults(&config.APIConfig) maybeSetAPIDefaults(&config.APIConfig)
maybeSetAggregatedAPIServerPortDefaults(&config.AggregatedAPIServerPort)
maybeSetAPIGroupSuffixDefault(&config.APIGroupSuffix) maybeSetAPIGroupSuffixDefault(&config.APIGroupSuffix)
maybeSetKubeCertAgentDefaults(&config.KubeCertAgentConfig) maybeSetKubeCertAgentDefaults(&config.KubeCertAgentConfig)
@ -53,6 +60,10 @@ func FromPath(path string) (*Config, error) {
return nil, fmt.Errorf("validate apiGroupSuffix: %w", err) return nil, fmt.Errorf("validate apiGroupSuffix: %w", err)
} }
if err := validateAggregatedAPIServerPort(config.AggregatedAPIServerPort); err != nil {
return nil, fmt.Errorf("validate aggregatedAPIServerPort: %w", err)
}
if err := validateNames(&config.NamesConfig); err != nil { if err := validateNames(&config.NamesConfig); err != nil {
return nil, fmt.Errorf("validate names: %w", err) return nil, fmt.Errorf("validate names: %w", err)
} }
@ -84,6 +95,12 @@ func maybeSetAPIGroupSuffixDefault(apiGroupSuffix **string) {
} }
} }
func maybeSetAggregatedAPIServerPortDefaults(aggregatedAPIServerPort **int64) {
if *aggregatedAPIServerPort == nil {
*aggregatedAPIServerPort = pointer.Int64Ptr(aggregatedAPIServerPortDefault)
}
}
func maybeSetKubeCertAgentDefaults(cfg *KubeCertAgentSpec) { func maybeSetKubeCertAgentDefaults(cfg *KubeCertAgentSpec) {
if cfg.NamePrefix == nil { if cfg.NamePrefix == nil {
cfg.NamePrefix = pointer.StringPtr("pinniped-kube-cert-agent-") cfg.NamePrefix = pointer.StringPtr("pinniped-kube-cert-agent-")
@ -147,3 +164,11 @@ func validateAPI(apiConfig *APIConfigSpec) error {
func validateAPIGroupSuffix(apiGroupSuffix string) error { func validateAPIGroupSuffix(apiGroupSuffix string) error {
return groupsuffix.Validate(apiGroupSuffix) return groupsuffix.Validate(apiGroupSuffix)
} }
func validateAggregatedAPIServerPort(aggregatedAPIServerPort *int64) error {
// It cannot be below 1024 because the container is not running as root.
if *aggregatedAPIServerPort < 1024 || *aggregatedAPIServerPort > 65535 {
return constable.Error("must be within range 1024 to 65535")
}
return nil
}

View File

@ -33,6 +33,7 @@ func TestFromPath(t *testing.T) {
durationSeconds: 3600 durationSeconds: 3600
renewBeforeSeconds: 2400 renewBeforeSeconds: 2400
apiGroupSuffix: some.suffix.com apiGroupSuffix: some.suffix.com
aggregatedAPIServerPort: 12345
names: names:
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
credentialIssuer: pinniped-config credentialIssuer: pinniped-config
@ -66,6 +67,7 @@ func TestFromPath(t *testing.T) {
}, },
}, },
APIGroupSuffix: pointer.StringPtr("some.suffix.com"), APIGroupSuffix: pointer.StringPtr("some.suffix.com"),
AggregatedAPIServerPort: pointer.Int64Ptr(12345),
NamesConfig: NamesConfigSpec{ NamesConfig: NamesConfigSpec{
ServingCertificateSecret: "pinniped-concierge-api-tls-serving-certificate", ServingCertificateSecret: "pinniped-concierge-api-tls-serving-certificate",
CredentialIssuer: "pinniped-config", CredentialIssuer: "pinniped-config",
@ -109,6 +111,7 @@ func TestFromPath(t *testing.T) {
URL: nil, URL: nil,
}, },
APIGroupSuffix: pointer.StringPtr("pinniped.dev"), APIGroupSuffix: pointer.StringPtr("pinniped.dev"),
AggregatedAPIServerPort: pointer.Int64Ptr(10250),
APIConfig: APIConfigSpec{ APIConfig: APIConfigSpec{
ServingCertificateConfig: ServingCertificateConfigSpec{ ServingCertificateConfig: ServingCertificateConfigSpec{
DurationSeconds: pointer.Int64Ptr(60 * 60 * 24 * 365), // about a year DurationSeconds: pointer.Int64Ptr(60 * 60 * 24 * 365), // about a year
@ -323,6 +326,22 @@ func TestFromPath(t *testing.T) {
`), `),
wantError: "validate api: renewBefore must be positive", wantError: "validate api: renewBefore must be positive",
}, },
{
name: "AggregatedAPIServerPortDefault too small",
yaml: here.Doc(`
---
aggregatedAPIServerPort: 1023
`),
wantError: "validate aggregatedAPIServerPort: must be within range 1024 to 65535",
},
{
name: "AggregatedAPIServerPortDefault too large",
yaml: here.Doc(`
---
aggregatedAPIServerPort: 65536
`),
wantError: "validate aggregatedAPIServerPort: must be within range 1024 to 65535",
},
{ {
name: "ZeroRenewBefore", name: "ZeroRenewBefore",
yaml: here.Doc(` yaml: here.Doc(`

View File

@ -10,6 +10,7 @@ type Config struct {
DiscoveryInfo DiscoveryInfoSpec `json:"discovery"` DiscoveryInfo DiscoveryInfoSpec `json:"discovery"`
APIConfig APIConfigSpec `json:"api"` APIConfig APIConfigSpec `json:"api"`
APIGroupSuffix *string `json:"apiGroupSuffix,omitempty"` APIGroupSuffix *string `json:"apiGroupSuffix,omitempty"`
AggregatedAPIServerPort *int64 `json:"aggregatedAPIServerPort"`
NamesConfig NamesConfigSpec `json:"names"` NamesConfig NamesConfigSpec `json:"names"`
KubeCertAgentConfig KubeCertAgentSpec `json:"kubeCertAgent"` KubeCertAgentConfig KubeCertAgentSpec `json:"kubeCertAgent"`
Labels map[string]string `json:"labels"` Labels map[string]string `json:"labels"`