diff --git a/deploy_carvel/concierge/config/README.md b/deploy_carvel/concierge/config/README.md new file mode 100644 index 00000000..39ce06b2 --- /dev/null +++ b/deploy_carvel/concierge/config/README.md @@ -0,0 +1,3 @@ +# Pinniped Concierge Deployment + +See [the how-to guide for details](https://pinniped.dev/docs/howto/install-concierge/). diff --git a/deploy_carvel/concierge/config/authentication.concierge.pinniped.dev_jwtauthenticators.yaml b/deploy_carvel/concierge/config/authentication.concierge.pinniped.dev_jwtauthenticators.yaml new file mode 100644 index 00000000..a1a77773 --- /dev/null +++ b/deploy_carvel/concierge/config/authentication.concierge.pinniped.dev_jwtauthenticators.yaml @@ -0,0 +1,176 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: jwtauthenticators.authentication.concierge.pinniped.dev +spec: + group: authentication.concierge.pinniped.dev + names: + categories: + - pinniped + - pinniped-authenticator + - pinniped-authenticators + kind: JWTAuthenticator + listKind: JWTAuthenticatorList + plural: jwtauthenticators + singular: jwtauthenticator + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .spec.issuer + name: Issuer + type: string + - jsonPath: .spec.audience + name: Audience + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: "JWTAuthenticator describes the configuration of a JWT authenticator. + \n Upon receiving a signed JWT, a JWTAuthenticator will performs some validation + on it (e.g., valid signature, existence of claims, etc.) and extract the + username and groups from the token." + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec for configuring the authenticator. + properties: + audience: + description: Audience is the required value of the "aud" JWT claim. + minLength: 1 + type: string + claims: + description: Claims allows customization of the claims that will be + mapped to user identity for Kubernetes access. + properties: + groups: + description: Groups is the name of the claim which should be read + to extract the user's group membership from the JWT token. When + not specified, it will default to "groups". + type: string + username: + description: Username is the name of the claim which should be + read to extract the username from the JWT token. When not specified, + it will default to "username". + type: string + type: object + issuer: + description: Issuer is the OIDC issuer URL that will be used to discover + public signing keys. Issuer is also used to validate the "iss" JWT + claim. + minLength: 1 + pattern: ^https:// + type: string + tls: + description: TLS configuration for communicating with the OIDC provider. + properties: + certificateAuthorityData: + description: X.509 Certificate Authority (base64-encoded PEM bundle). + If omitted, a default set of system roots will be trusted. + type: string + type: object + required: + - audience + - issuer + type: object + status: + description: Status of the authenticator. + properties: + conditions: + description: Represents the observations of the authenticator's current + state. + items: + description: Condition status of a resource (mirrored from the metav1.Condition + type added in Kubernetes 1.19). In a future API version we can + switch to using the upstream type. See https://github.com/kubernetes/apimachinery/blob/v0.19.0/pkg/apis/meta/v1/types.go#L1353-L1413. + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/deploy_carvel/concierge/config/authentication.concierge.pinniped.dev_webhookauthenticators.yaml b/deploy_carvel/concierge/config/authentication.concierge.pinniped.dev_webhookauthenticators.yaml new file mode 100644 index 00000000..07c7f1e5 --- /dev/null +++ b/deploy_carvel/concierge/config/authentication.concierge.pinniped.dev_webhookauthenticators.yaml @@ -0,0 +1,149 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: webhookauthenticators.authentication.concierge.pinniped.dev +spec: + group: authentication.concierge.pinniped.dev + names: + categories: + - pinniped + - pinniped-authenticator + - pinniped-authenticators + kind: WebhookAuthenticator + listKind: WebhookAuthenticatorList + plural: webhookauthenticators + singular: webhookauthenticator + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .spec.endpoint + name: Endpoint + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: WebhookAuthenticator describes the configuration of a webhook + authenticator. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec for configuring the authenticator. + properties: + endpoint: + description: Webhook server endpoint URL. + minLength: 1 + pattern: ^https:// + type: string + tls: + description: TLS configuration. + properties: + certificateAuthorityData: + description: X.509 Certificate Authority (base64-encoded PEM bundle). + If omitted, a default set of system roots will be trusted. + type: string + type: object + required: + - endpoint + type: object + status: + description: Status of the authenticator. + properties: + conditions: + description: Represents the observations of the authenticator's current + state. + items: + description: Condition status of a resource (mirrored from the metav1.Condition + type added in Kubernetes 1.19). In a future API version we can + switch to using the upstream type. See https://github.com/kubernetes/apimachinery/blob/v0.19.0/pkg/apis/meta/v1/types.go#L1353-L1413. + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition + transitioned from one status to another. This should be when + the underlying condition changed. If that is not known, then + using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating + details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation + that the condition was set based upon. For instance, if .metadata.generation + is currently 12, but the .status.conditions[x].observedGeneration + is 9, the condition is out of date with respect to the current + state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating + the reason for the condition's last transition. Producers + of specific condition types may define expected values and + meanings for this field, and whether the values are considered + a guaranteed API. The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + --- Many .condition.type values are consistent across resources + like Available, but because arbitrary conditions can be useful + (see .node.status.conditions), the ability to deconflict is + important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/deploy_carvel/concierge/config/config.concierge.pinniped.dev_credentialissuers.yaml b/deploy_carvel/concierge/config/config.concierge.pinniped.dev_credentialissuers.yaml new file mode 100644 index 00000000..f2710862 --- /dev/null +++ b/deploy_carvel/concierge/config/config.concierge.pinniped.dev_credentialissuers.yaml @@ -0,0 +1,264 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.8.0 + creationTimestamp: null + name: credentialissuers.config.concierge.pinniped.dev +spec: + group: config.concierge.pinniped.dev + names: + categories: + - pinniped + kind: CredentialIssuer + listKind: CredentialIssuerList + plural: credentialissuers + singular: credentialissuer + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .spec.impersonationProxy.mode + name: ProxyMode + type: string + - jsonPath: .status.strategies[?(@.status == "Success")].type + name: DefaultStrategy + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: CredentialIssuer describes the configuration and status of the + Pinniped Concierge credential issuer. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: Spec describes the intended configuration of the Concierge. + properties: + impersonationProxy: + description: ImpersonationProxy describes the intended configuration + of the Concierge impersonation proxy. + properties: + externalEndpoint: + description: "ExternalEndpoint describes the HTTPS endpoint where + the proxy will be exposed. If not set, the proxy will be served + using the external name of the LoadBalancer service or the cluster + service DNS name. \n This field must be non-empty when spec.impersonationProxy.service.type + is \"None\"." + type: string + mode: + description: 'Mode configures whether the impersonation proxy + should be started: - "disabled" explicitly disables the impersonation + proxy. This is the default. - "enabled" explicitly enables the + impersonation proxy. - "auto" enables or disables the impersonation + proxy based upon the cluster in which it is running.' + enum: + - auto + - enabled + - disabled + type: string + service: + default: + type: LoadBalancer + description: Service describes the configuration of the Service + provisioned to expose the impersonation proxy to clients. + properties: + annotations: + additionalProperties: + type: string + description: Annotations specifies zero or more key/value + pairs to set as annotations on the provisioned Service. + type: object + loadBalancerIP: + description: LoadBalancerIP specifies the IP address to set + in the spec.loadBalancerIP field of the provisioned Service. + This is not supported on all cloud providers. + maxLength: 255 + minLength: 1 + type: string + type: + default: LoadBalancer + description: "Type specifies the type of Service to provision + for the impersonation proxy. \n If the type is \"None\", + then the \"spec.impersonationProxy.externalEndpoint\" field + must be set to a non-empty value so that the Concierge can + properly advertise the endpoint in the CredentialIssuer's + status." + enum: + - LoadBalancer + - ClusterIP + - None + type: string + type: object + tls: + description: "TLS contains information about how the Concierge + impersonation proxy should serve TLS. \n If this field is empty, + the impersonation proxy will generate its own TLS certificate." + properties: + certificateAuthorityData: + description: X.509 Certificate Authority (base64-encoded PEM + bundle). Used to advertise the CA bundle for the impersonation + proxy endpoint. + type: string + secretName: + description: SecretName is the name of a Secret in the same + namespace, of type `kubernetes.io/tls`, which contains the + TLS serving certificate for the Concierge impersonation + proxy endpoint. + minLength: 1 + type: string + type: object + required: + - mode + - service + type: object + required: + - impersonationProxy + type: object + status: + description: CredentialIssuerStatus describes the status of the Concierge. + properties: + kubeConfigInfo: + description: Information needed to form a valid Pinniped-based kubeconfig + using this credential issuer. This field is deprecated and will + be removed in a future version. + properties: + certificateAuthorityData: + description: The K8s API server CA bundle. + minLength: 1 + type: string + server: + description: The K8s API server URL. + minLength: 1 + pattern: ^https://|^http:// + type: string + required: + - certificateAuthorityData + - server + type: object + strategies: + description: List of integration strategies that were attempted by + Pinniped. + items: + description: CredentialIssuerStrategy describes the status of an + integration strategy that was attempted by Pinniped. + properties: + frontend: + description: Frontend describes how clients can connect using + this strategy. + properties: + impersonationProxyInfo: + description: ImpersonationProxyInfo describes the parameters + for the impersonation proxy on this Concierge. This field + is only set when Type is "ImpersonationProxy". + properties: + certificateAuthorityData: + description: CertificateAuthorityData is the base64-encoded + PEM CA bundle of the impersonation proxy. + minLength: 1 + type: string + endpoint: + description: Endpoint is the HTTPS endpoint of the impersonation + proxy. + minLength: 1 + pattern: ^https:// + type: string + required: + - certificateAuthorityData + - endpoint + type: object + tokenCredentialRequestInfo: + description: TokenCredentialRequestAPIInfo describes the + parameters for the TokenCredentialRequest API on this + Concierge. This field is only set when Type is "TokenCredentialRequestAPI". + properties: + certificateAuthorityData: + description: CertificateAuthorityData is the base64-encoded + Kubernetes API server CA bundle. + minLength: 1 + type: string + server: + description: Server is the Kubernetes API server URL. + minLength: 1 + pattern: ^https://|^http:// + type: string + required: + - certificateAuthorityData + - server + type: object + type: + description: Type describes which frontend mechanism clients + can use with a strategy. + enum: + - TokenCredentialRequestAPI + - ImpersonationProxy + type: string + required: + - type + type: object + lastUpdateTime: + description: When the status was last checked. + format: date-time + type: string + message: + description: Human-readable description of the current status. + minLength: 1 + type: string + reason: + description: Reason for the current status. + enum: + - Listening + - Pending + - Disabled + - ErrorDuringSetup + - CouldNotFetchKey + - CouldNotGetClusterInfo + - FetchedKey + type: string + status: + description: Status of the attempted integration strategy. + enum: + - Success + - Error + type: string + type: + description: Type of integration attempted. + enum: + - KubeClusterSigningCertificate + - ImpersonationProxy + type: string + required: + - lastUpdateTime + - message + - reason + - status + - type + type: object + type: array + required: + - strategies + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/deploy_carvel/concierge/config/deployment-HACKED.yaml b/deploy_carvel/concierge/config/deployment-HACKED.yaml new file mode 100644 index 00000000..2b00fc08 --- /dev/null +++ b/deploy_carvel/concierge/config/deployment-HACKED.yaml @@ -0,0 +1,177 @@ +#! Copyright 2020-2022 the Pinniped contributors. All Rights Reserved. +#! SPDX-License-Identifier: Apache-2.0 + +#@ load("@ytt:data", "data") +#@ load("@ytt:json", "json") +#@ load("helpers.lib.yaml", "defaultLabel", "labels", "deploymentPodLabel", "namespace", "defaultResourceName", "defaultResourceNameWithSuffix", "getAndValidateLogLevel", "pinnipedDevAPIGroupWithPrefix") +#@ load("@ytt:template", "template") + +#@ if not data.values.into_namespace: +--- +apiVersion: v1 +kind: Namespace +metadata: + name: #@ data.values.namespace + labels: + _: #@ template.replace(labels()) + #! When deploying onto a cluster which has PSAs enabled by default for namespaces, + #! effectively disable them for this namespace. The kube-cert-agent Deployment's pod + #! created by the Concierge in this namespace needs to be able to perform privileged + #! actions. The regular Concierge pod containers created by the Deployment below do + #! not need special privileges and are marked as such in their securityContext settings. + pod-security.kubernetes.io/enforce: privileged +#@ end +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: #@ defaultResourceName() + namespace: #@ namespace() + labels: #@ labels() +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: #@ defaultResourceNameWithSuffix("kube-cert-agent") + namespace: #@ namespace() + labels: #@ labels() +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: #@ defaultResourceNameWithSuffix("impersonation-proxy") + namespace: #@ namespace() + labels: #@ labels() + annotations: + #! we need to create this service account before we create the secret + kapp.k14s.io/change-group: "impersonation-proxy.concierge.pinniped.dev/serviceaccount" +secrets: #! make sure the token controller does not create any other secrets +- name: #@ defaultResourceNameWithSuffix("impersonation-proxy") +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: #@ defaultResourceNameWithSuffix("config") + namespace: #@ namespace() + labels: #@ labels() +data: + #! If names.apiService is changed in this ConfigMap, must also change name of the ClusterIP Service resource below. + #@yaml/text-templated-strings + pinniped.yaml: | + discovery: + url: (@= data.values.discovery_url or "null" @) + api: + servingCertificate: + durationSeconds: (@= str(data.values.api_serving_certificate_duration_seconds) @) + renewBeforeSeconds: (@= str(data.values.api_serving_certificate_renew_before_seconds) @) + 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 + # impersonationProxyServerPort may be set here, although other YAML references to the default port (8444) may also need to be updated + names: + servingCertificateSecret: (@= defaultResourceNameWithSuffix("api-tls-serving-certificate") @) + credentialIssuer: (@= defaultResourceNameWithSuffix("config") @) + apiService: (@= defaultResourceNameWithSuffix("api") @) + impersonationLoadBalancerService: (@= defaultResourceNameWithSuffix("impersonation-proxy-load-balancer") @) + impersonationClusterIPService: (@= defaultResourceNameWithSuffix("impersonation-proxy-cluster-ip") @) + impersonationTLSCertificateSecret: (@= defaultResourceNameWithSuffix("impersonation-proxy-tls-serving-certificate") @) + impersonationCACertificateSecret: (@= defaultResourceNameWithSuffix("impersonation-proxy-ca-certificate") @) + impersonationSignerSecret: (@= defaultResourceNameWithSuffix("impersonation-proxy-signer-ca-certificate") @) + agentServiceAccount: (@= defaultResourceNameWithSuffix("kube-cert-agent") @) + labels: (@= json.encode(labels()).rstrip() @) + kubeCertAgent: + namePrefix: (@= defaultResourceNameWithSuffix("kube-cert-agent-") @) + (@ if data.values.kube_cert_agent_image: @) + image: (@= data.values.kube_cert_agent_image @) + (@ else: @) + (@ if data.values.image_digest: @) + image: (@= data.values.image_repo + "@" + data.values.image_digest @) + (@ else: @) + image: (@= data.values.image_repo + ":" + data.values.image_tag @) + (@ end @) + (@ end @) + (@ if data.values.image_pull_dockerconfigjson: @) + imagePullSecrets: + - image-pull-secret + (@ end @) + (@ if data.values.log_level or data.values.deprecated_log_format: @) + log: + (@ if data.values.log_level: @) + level: (@= getAndValidateLogLevel() @) + (@ end @) + (@ if data.values.deprecated_log_format: @) + format: (@= data.values.deprecated_log_format @) + (@ end @) + (@ end @) +--- +#@ if data.values.image_pull_dockerconfigjson and data.values.image_pull_dockerconfigjson != "": +apiVersion: v1 +kind: Secret +metadata: + name: image-pull-secret + namespace: #@ namespace() + labels: #@ labels() +type: kubernetes.io/dockerconfigjson +data: + .dockerconfigjson: #@ data.values.image_pull_dockerconfigjson +#@ end +--- +#! THE DEPLOYMENT IS GONE!!! +#! THE DEPLOYMENT IS GONE!!! +#! THE DEPLOYMENT IS GONE!!! For initial prototype, just installing some simple things. +#! THE DEPLOYMENT IS GONE!!! +#! THE DEPLOYMENT IS GONE!!! +--- +#! THE SERVICE IS GONE!!! +#! THE SERVICE IS GONE!!! +#! THE SERVICE IS GONE!!! For initial prototype, just installing some simple things. +#! THE SERVICE IS GONE!!! +#! THE SERVICE IS GONE!!! +--- +#! THE SECOND SERVICE IS GONE!!! +#! THE SECOND SERVICE IS GONE!!! +#! THE SECOND SERVICE IS GONE!!! For initial prototype, just installing some simple things. +#! THE SECOND SERVICE IS GONE!!! +#! THE SECOND SERVICE IS GONE!!! +--- +#! THE API SERVICE IS GONE!!! +#! THE API SERVICE IS GONE!!! +#! THE API SERVICE IS GONE!!! For initial prototype, just installing some simple things. +#! THE API SERVICE IS GONE!!! +#! THE API SERVICE IS GONE!!! +--- +#! THE SECOND API SERVICE IS GONE!!! +#! THE SECOND API SERVICE IS GONE!!! +#! THE SECOND API SERVICE IS GONE!!! For initial prototype, just installing some simple things. +#! THE SECOND API SERVICE IS GONE!!! +#! THE SECOND API SERVICE IS GONE!!! +--- +apiVersion: #@ pinnipedDevAPIGroupWithPrefix("config.concierge") + "/v1alpha1" +kind: CredentialIssuer +metadata: + name: #@ defaultResourceNameWithSuffix("config") + labels: #@ labels() +spec: + impersonationProxy: + mode: #@ data.values.impersonation_proxy_spec.mode + #@ if data.values.impersonation_proxy_spec.external_endpoint: + externalEndpoint: #@ data.values.impersonation_proxy_spec.external_endpoint + #@ end + service: + type: #@ data.values.impersonation_proxy_spec.service.type + #@ if data.values.impersonation_proxy_spec.service.load_balancer_ip: + loadBalancerIP: #@ data.values.impersonation_proxy_spec.service.load_balancer_ip + #@ end + annotations: #@ data.values.impersonation_proxy_spec.service.annotations +--- +apiVersion: v1 +kind: Secret +metadata: + name: #@ defaultResourceNameWithSuffix("impersonation-proxy") + namespace: #@ namespace() + labels: #@ labels() + annotations: + #! wait until the SA exists to create this secret so that the token controller does not delete it + #! we have this secret at the end so that kubectl will create the service account first + kapp.k14s.io/change-rule: "upsert after upserting impersonation-proxy.concierge.pinniped.dev/serviceaccount" + kubernetes.io/service-account.name: #@ defaultResourceNameWithSuffix("impersonation-proxy") +type: kubernetes.io/service-account-token diff --git a/deploy_carvel/concierge/config/helpers.lib.yaml b/deploy_carvel/concierge/config/helpers.lib.yaml new file mode 100644 index 00000000..542fe069 --- /dev/null +++ b/deploy_carvel/concierge/config/helpers.lib.yaml @@ -0,0 +1,47 @@ +#! Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. +#! SPDX-License-Identifier: Apache-2.0 + +#@ load("@ytt:data", "data") +#@ load("@ytt:template", "template") + +#@ def defaultResourceName(): +#@ return data.values.app_name +#@ end + +#@ def defaultResourceNameWithSuffix(suffix): +#@ return data.values.app_name + "-" + suffix +#@ end + +#@ def pinnipedDevAPIGroupWithPrefix(prefix): +#@ return prefix + "." + data.values.api_group_suffix +#@ end + +#@ def namespace(): +#@ if data.values.into_namespace: +#@ return data.values.into_namespace +#@ else: +#@ return data.values.namespace +#@ end +#@ end + +#@ def defaultLabel(): +#! Note that the name of this label's key is also assumed by kubecertagent.go and impersonator_config.go +app: #@ data.values.app_name +#@ end + +#@ def deploymentPodLabel(): +deployment.pinniped.dev: concierge +#@ end + +#@ def labels(): +_: #@ template.replace(defaultLabel()) +_: #@ template.replace(data.values.custom_labels) +#@ end + +#@ def getAndValidateLogLevel(): +#@ log_level = data.values.log_level +#@ if log_level != "info" and log_level != "debug" and log_level != "trace" and log_level != "all": +#@ fail("log_level '" + log_level + "' is invalid") +#@ end +#@ return log_level +#@ end diff --git a/deploy_carvel/concierge/config/rbac.yaml b/deploy_carvel/concierge/config/rbac.yaml new file mode 100644 index 00000000..61ffa57b --- /dev/null +++ b/deploy_carvel/concierge/config/rbac.yaml @@ -0,0 +1,296 @@ +#! Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. +#! SPDX-License-Identifier: Apache-2.0 + +#@ load("@ytt:data", "data") +#@ load("helpers.lib.yaml", "labels", "namespace", "defaultResourceName", "defaultResourceNameWithSuffix", "pinnipedDevAPIGroupWithPrefix") + +#! Give permission to various cluster-scoped objects +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: #@ defaultResourceNameWithSuffix("aggregated-api-server") + labels: #@ labels() +rules: + - apiGroups: [ "" ] + resources: [ namespaces ] + verbs: [ get, list, watch ] + - apiGroups: [ apiregistration.k8s.io ] + resources: [ apiservices ] + verbs: [ get, list, patch, update, watch ] + - apiGroups: [ admissionregistration.k8s.io ] + resources: [ validatingwebhookconfigurations, mutatingwebhookconfigurations ] + verbs: [ get, list, watch ] + - apiGroups: [ flowcontrol.apiserver.k8s.io ] + resources: [ flowschemas, prioritylevelconfigurations ] + verbs: [ get, list, watch ] + - apiGroups: [ security.openshift.io ] + resources: [ securitycontextconstraints ] + verbs: [ use ] + resourceNames: [ nonroot ] + - apiGroups: [ "" ] + resources: [ nodes ] + verbs: [ list ] + - apiGroups: + - #@ pinnipedDevAPIGroupWithPrefix("config.concierge") + resources: [ credentialissuers ] + verbs: [ get, list, watch, create ] + - apiGroups: + - #@ pinnipedDevAPIGroupWithPrefix("config.concierge") + resources: [ credentialissuers/status ] + verbs: [ get, patch, update ] + - apiGroups: + - #@ pinnipedDevAPIGroupWithPrefix("authentication.concierge") + resources: [ jwtauthenticators, webhookauthenticators ] + verbs: [ get, list, watch ] +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: #@ defaultResourceNameWithSuffix("aggregated-api-server") + labels: #@ labels() +subjects: + - kind: ServiceAccount + name: #@ defaultResourceName() + namespace: #@ namespace() +roleRef: + kind: ClusterRole + name: #@ defaultResourceNameWithSuffix("aggregated-api-server") + apiGroup: rbac.authorization.k8s.io + +#! Give minimal permissions to impersonation proxy service account +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: #@ defaultResourceNameWithSuffix("impersonation-proxy") + labels: #@ labels() +rules: + - apiGroups: [ "" ] + resources: [ "users", "groups", "serviceaccounts" ] + verbs: [ "impersonate" ] + - apiGroups: [ "authentication.k8s.io" ] + resources: [ "*" ] #! What we really want is userextras/* but the RBAC authorizer only supports */subresource, not resource/* + verbs: [ "impersonate" ] +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: #@ defaultResourceNameWithSuffix("impersonation-proxy") + labels: #@ labels() +subjects: + - kind: ServiceAccount + name: #@ defaultResourceNameWithSuffix("impersonation-proxy") + namespace: #@ namespace() +roleRef: + kind: ClusterRole + name: #@ defaultResourceNameWithSuffix("impersonation-proxy") + apiGroup: rbac.authorization.k8s.io + +#! Give permission to the kube-cert-agent Pod to run privileged. +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: #@ defaultResourceNameWithSuffix("kube-cert-agent") + namespace: #@ namespace() + labels: #@ labels() +rules: + - apiGroups: [ policy ] + resources: [ podsecuritypolicies ] + verbs: [ use ] +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: #@ defaultResourceNameWithSuffix("kube-cert-agent") + namespace: #@ namespace() + labels: #@ labels() +subjects: + - kind: ServiceAccount + name: #@ defaultResourceNameWithSuffix("kube-cert-agent") + namespace: #@ namespace() +roleRef: + kind: Role + name: #@ defaultResourceNameWithSuffix("kube-cert-agent") + apiGroup: rbac.authorization.k8s.io + +#! Give permission to various objects within the app's own namespace +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: #@ defaultResourceNameWithSuffix("aggregated-api-server") + namespace: #@ namespace() + labels: #@ labels() +rules: + - apiGroups: [ "" ] + resources: [ services ] + verbs: [ create, get, list, patch, update, watch, delete ] + - apiGroups: [ "" ] + resources: [ secrets ] + verbs: [ create, get, list, patch, update, watch, delete ] + #! We need to be able to watch pods in our namespace so we can find the kube-cert-agent pods. + - apiGroups: [ "" ] + resources: [ pods ] + verbs: [ get, list, watch ] + #! We need to be able to exec into pods in our namespace so we can grab the API server's private key + - apiGroups: [ "" ] + resources: [ pods/exec ] + verbs: [ create ] + #! We need to be able to delete pods in our namespace so we can clean up legacy kube-cert-agent pods. + - apiGroups: [ "" ] + resources: [ pods ] + verbs: [ delete ] + #! We need to be able to create and update deployments in our namespace so we can manage the kube-cert-agent Deployment. + - apiGroups: [ apps ] + resources: [ deployments ] + verbs: [ create, get, list, patch, update, watch, delete ] + #! We need to be able to get replicasets so we can form the correct owner references on our generated objects. + - apiGroups: [ apps ] + resources: [ replicasets ] + verbs: [ get ] + - apiGroups: [ "" ] + resources: [ configmaps ] + verbs: [ list, get, watch ] + - apiGroups: [ coordination.k8s.io ] + resources: [ leases ] + verbs: [ create, get, update ] +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: #@ defaultResourceNameWithSuffix("aggregated-api-server") + namespace: #@ namespace() + labels: #@ labels() +subjects: + - kind: ServiceAccount + name: #@ defaultResourceName() + namespace: #@ namespace() +roleRef: + kind: Role + name: #@ defaultResourceNameWithSuffix("aggregated-api-server") + apiGroup: rbac.authorization.k8s.io + +#! Give permission to read pods in the kube-system namespace so we can find the API server's private key +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: #@ defaultResourceNameWithSuffix("kube-system-pod-read") + namespace: kube-system + labels: #@ labels() +rules: + - apiGroups: [ "" ] + resources: [ pods ] + verbs: [ get, list, watch ] +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: #@ defaultResourceNameWithSuffix("kube-system-pod-read") + namespace: kube-system + labels: #@ labels() +subjects: + - kind: ServiceAccount + name: #@ defaultResourceName() + namespace: #@ namespace() +roleRef: + kind: Role + name: #@ defaultResourceNameWithSuffix("kube-system-pod-read") + apiGroup: rbac.authorization.k8s.io + +#! Allow both authenticated and unauthenticated TokenCredentialRequests (i.e. allow all requests) +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: #@ defaultResourceNameWithSuffix("pre-authn-apis") + labels: #@ labels() +rules: + - apiGroups: + - #@ pinnipedDevAPIGroupWithPrefix("login.concierge") + resources: [ tokencredentialrequests ] + verbs: [ create, list ] + - apiGroups: + - #@ pinnipedDevAPIGroupWithPrefix("identity.concierge") + resources: [ whoamirequests ] + verbs: [ create, list ] +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: #@ defaultResourceNameWithSuffix("pre-authn-apis") + labels: #@ labels() +subjects: + - kind: Group + name: system:authenticated + apiGroup: rbac.authorization.k8s.io + - kind: Group + name: system:unauthenticated + apiGroup: rbac.authorization.k8s.io +roleRef: + kind: ClusterRole + name: #@ defaultResourceNameWithSuffix("pre-authn-apis") + apiGroup: rbac.authorization.k8s.io + +#! Give permissions for subjectaccessreviews, tokenreview that is needed by aggregated api servers +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: #@ defaultResourceName() + labels: #@ labels() +subjects: + - kind: ServiceAccount + name: #@ defaultResourceName() + namespace: #@ namespace() +roleRef: + kind: ClusterRole + name: system:auth-delegator + apiGroup: rbac.authorization.k8s.io + +#! Give permissions for a special configmap of CA bundles that is needed by aggregated api servers +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: #@ defaultResourceNameWithSuffix("extension-apiserver-authentication-reader") + namespace: kube-system + labels: #@ labels() +subjects: + - kind: ServiceAccount + name: #@ defaultResourceName() + namespace: #@ namespace() +roleRef: + kind: Role + name: extension-apiserver-authentication-reader + apiGroup: rbac.authorization.k8s.io + +#! Give permission to list and watch ConfigMaps in kube-public +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: #@ defaultResourceNameWithSuffix("cluster-info-lister-watcher") + namespace: kube-public + labels: #@ labels() +rules: + - apiGroups: [ "" ] + resources: [ configmaps ] + verbs: [ list, watch ] +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: #@ defaultResourceNameWithSuffix("cluster-info-lister-watcher") + namespace: kube-public + labels: #@ labels() +subjects: + - kind: ServiceAccount + name: #@ defaultResourceName() + namespace: #@ namespace() +roleRef: + kind: Role + name: #@ defaultResourceNameWithSuffix("cluster-info-lister-watcher") + apiGroup: rbac.authorization.k8s.io diff --git a/deploy_carvel/concierge/config/values.yaml b/deploy_carvel/concierge/config/values.yaml new file mode 100644 index 00000000..78631f0e --- /dev/null +++ b/deploy_carvel/concierge/config/values.yaml @@ -0,0 +1,107 @@ +#! Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. +#! SPDX-License-Identifier: Apache-2.0 + +#@data/values-schema +--- + +app_name: pinniped-concierge + +#! Creates a new namespace statically in yaml with the given name and installs the app into that namespace. +namespace: pinniped-concierge +#! If specified, assumes that a namespace of the given name already exists and installs the app into that namespace. +#! If both `namespace` and `into_namespace` are specified, then only `into_namespace` is used. +into_namespace: #! e.g. my-preexisting-namespace + +#! All resources created statically by yaml at install-time and all resources created dynamically +#! by controllers at runtime will be labelled with `app: $app_name` and also with the labels +#! specified here. The value of `custom_labels` must be a map of string keys to string values. +#! The app can be uninstalled either by: +#! 1. Deleting the static install-time yaml resources including the static namespace, which will cascade and also delete +#! resources that were dynamically created by controllers at runtime +#! 2. Or, deleting all resources by label, which does not assume that there was a static install-time yaml namespace. +custom_labels: {} #! e.g. {myCustomLabelName: myCustomLabelValue, otherCustomLabelName: otherCustomLabelValue} + +#! Specify how many replicas of the Pinniped server to run. +replicas: 2 + +#! Specify either an image_digest or an image_tag. If both are given, only image_digest will be used. +image_repo: projects.registry.vmware.com/pinniped/pinniped-server +image_digest: #! e.g. sha256:f3c4fdfd3ef865d4b97a1fd295d94acc3f0c654c46b6f27ffad5cf80216903c8 +image_tag: latest + +#! Optionally specify a different image for the "kube-cert-agent" pod which is scheduled +#! on the control plane. This image needs only to include `sleep` and `cat` binaries. +#! By default, the same image specified for image_repo/image_digest/image_tag will be re-used. +kube_cert_agent_image: + +#! Specifies a secret to be used when pulling the above `image_repo` container image. +#! Can be used when the above image_repo is a private registry. +#! Typically the value would be the output of: kubectl create secret docker-registry x --docker-server=https://example.io --docker-username="USERNAME" --docker-password="PASSWORD" --dry-run=client -o json | jq -r '.data[".dockerconfigjson"]' +#! Optional. +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. +#! This setting allows the guess to be overridden. +#! Optional. +discovery_url: #! e.g., https://example.com + +#! Specify the duration and renewal interval for the API serving certificate. +#! The defaults are set to expire the cert about every 30 days, and to rotate it +#! about every 25 days. +api_serving_certificate_duration_seconds: 2592000 +api_serving_certificate_renew_before_seconds: 2160000 + +#! Specify the verbosity of logging: info ("nice to know" information), debug (developer +#! information), trace (timing information), all (kitchen sink). +log_level: #! By default, when this value is left unset, only warnings and errors are printed. There is no way to suppress warning and error logs. +#! Specify the format of logging: json (for machine parsable logs) and text (for legacy klog formatted logs). +#! By default, when this value is left unset, logs are formatted in json. +#! This configuration is deprecated and will be removed in a future release at which point logs will always be formatted as json. +deprecated_log_format: + +run_as_user: 65532 #! run_as_user specifies the user ID that will own the process, see the Dockerfile for the reasoning behind this choice +run_as_group: 65532 #! run_as_group specifies the group ID that will own the process, see the Dockerfile for the reasoning behind this choice + +#! Specify the API group suffix for all Pinniped API groups. By default, this is set to +#! pinniped.dev, so Pinniped API groups will look like foo.pinniped.dev, +#! authentication.concierge.pinniped.dev, etc. As an example, if this is set to tuna.io, then +#! Pinniped API groups will look like foo.tuna.io. authentication.concierge.tuna.io, etc. +api_group_suffix: pinniped.dev + +#! Customize CredentialIssuer.spec.impersonationProxy to change how the concierge +#! handles impersonation. +impersonation_proxy_spec: + #! options are "auto", "disabled" or "enabled". + #! If auto, the impersonation proxy will run only if the cluster signing key is not available + #! and the other strategy does not work. + #! If disabled, the impersonation proxy will never run, which could mean that the concierge + #! doesn't work at all. + #! If enabled, the impersonation proxy will always run regardless of other strategies available. + mode: auto + #! The endpoint which the client should use to connect to the impersonation proxy. + #! If left unset, the client will default to connecting based on the ClusterIP or LoadBalancer + #! endpoint. + external_endpoint: + service: + #! Options are "LoadBalancer", "ClusterIP" and "None". + #! LoadBalancer automatically provisions a Service of type LoadBalancer pointing at + #! the impersonation proxy. Some cloud providers will allocate + #! a public IP address by default even on private clusters. + #! ClusterIP automatically provisions a Service of type ClusterIP pointing at the + #! impersonation proxy. + #! None does not provision either and assumes that you have set the external_endpoint + #! and set up your own ingress to connect to the impersonation proxy. + type: LoadBalancer + #! The annotations that should be set on the ClusterIP or LoadBalancer Service. + annotations: + {service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "4000"} + #! When mode LoadBalancer is set, this will set the LoadBalancer Service's Spec.LoadBalancerIP. + load_balancer_ip: + +#! Set the standard golang HTTPS_PROXY and NO_PROXY environment variables on the Concierge containers. +#! These will be used when the Concierge makes backend-to-backend calls to authenticators using HTTPS, +#! e.g. when the Concierge fetches discovery documents, JWKS keys, and POSTs to token webhooks. +#! The Concierge never makes insecure HTTP calls, so there is no reason to set HTTP_PROXY. +#! Optional. +https_proxy: #! e.g. http://proxy.example.com +no_proxy: "$(KUBERNETES_SERVICE_HOST),169.254.169.254,127.0.0.1,localhost,.svc,.cluster.local" #! do not proxy Kubernetes endpoints diff --git a/deploy_carvel/concierge/config/z0_crd_overlay.yaml b/deploy_carvel/concierge/config/z0_crd_overlay.yaml new file mode 100644 index 00000000..935d5f8c --- /dev/null +++ b/deploy_carvel/concierge/config/z0_crd_overlay.yaml @@ -0,0 +1,33 @@ +#! Copyright 2020-2021 the Pinniped contributors. All Rights Reserved. +#! SPDX-License-Identifier: Apache-2.0 + +#@ load("@ytt:overlay", "overlay") +#@ load("helpers.lib.yaml", "labels", "pinnipedDevAPIGroupWithPrefix") +#@ load("@ytt:data", "data") + +#@overlay/match by=overlay.subset({"kind": "CustomResourceDefinition", "metadata":{"name":"credentialissuers.config.concierge.pinniped.dev"}}), expects=1 +--- +metadata: + #@overlay/match missing_ok=True + labels: #@ labels() + name: #@ pinnipedDevAPIGroupWithPrefix("credentialissuers.config.concierge") +spec: + group: #@ pinnipedDevAPIGroupWithPrefix("config.concierge") + +#@overlay/match by=overlay.subset({"kind": "CustomResourceDefinition", "metadata":{"name":"webhookauthenticators.authentication.concierge.pinniped.dev"}}), expects=1 +--- +metadata: + #@overlay/match missing_ok=True + labels: #@ labels() + name: #@ pinnipedDevAPIGroupWithPrefix("webhookauthenticators.authentication.concierge") +spec: + group: #@ pinnipedDevAPIGroupWithPrefix("authentication.concierge") + +#@overlay/match by=overlay.subset({"kind": "CustomResourceDefinition", "metadata":{"name":"jwtauthenticators.authentication.concierge.pinniped.dev"}}), expects=1 +--- +metadata: + #@overlay/match missing_ok=True + labels: #@ labels() + name: #@ pinnipedDevAPIGroupWithPrefix("jwtauthenticators.authentication.concierge") +spec: + group: #@ pinnipedDevAPIGroupWithPrefix("authentication.concierge")