Merge branch 'main' into ldap_docs
This commit is contained in:
commit
8f2e8b8a6c
@ -3,17 +3,23 @@
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// StrategyType enumerates a type of "strategy" used to implement credential access on a cluster.
|
||||
// +kubebuilder:validation:Enum=KubeClusterSigningCertificate;ImpersonationProxy
|
||||
type StrategyType string
|
||||
|
||||
// FrontendType enumerates a type of "frontend" used to provide access to users of a cluster.
|
||||
// +kubebuilder:validation:Enum=TokenCredentialRequestAPI;ImpersonationProxy
|
||||
type FrontendType string
|
||||
|
||||
// StrategyStatus enumerates whether a strategy is working on a cluster.
|
||||
// +kubebuilder:validation:Enum=Success;Error
|
||||
type StrategyStatus string
|
||||
|
||||
// StrategyReason enumerates the detailed reason why a strategy is in a particular status.
|
||||
// +kubebuilder:validation:Enum=Listening;Pending;Disabled;ErrorDuringSetup;CouldNotFetchKey;CouldNotGetClusterInfo;FetchedKey
|
||||
type StrategyReason string
|
||||
|
||||
@ -36,7 +42,91 @@ const (
|
||||
FetchedKeyStrategyReason = StrategyReason("FetchedKey")
|
||||
)
|
||||
|
||||
// Status of a credential issuer.
|
||||
// CredentialIssuerSpec describes the intended configuration of the Concierge.
|
||||
type CredentialIssuerSpec struct {
|
||||
// ImpersonationProxy describes the intended configuration of the Concierge impersonation proxy.
|
||||
ImpersonationProxy *ImpersonationProxySpec `json:"impersonationProxy"`
|
||||
}
|
||||
|
||||
// ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy.
|
||||
//
|
||||
// +kubebuilder:validation:Enum=auto;enabled;disabled
|
||||
type ImpersonationProxyMode string
|
||||
|
||||
const (
|
||||
// ImpersonationProxyModeDisabled explicitly disables the impersonation proxy.
|
||||
ImpersonationProxyModeDisabled = ImpersonationProxyMode("disabled")
|
||||
|
||||
// ImpersonationProxyModeEnabled explicitly enables the impersonation proxy.
|
||||
ImpersonationProxyModeEnabled = ImpersonationProxyMode("enabled")
|
||||
|
||||
// ImpersonationProxyModeAuto enables or disables the impersonation proxy based upon the cluster in which it is running.
|
||||
ImpersonationProxyModeAuto = ImpersonationProxyMode("auto")
|
||||
)
|
||||
|
||||
// ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy.
|
||||
//
|
||||
// +kubebuilder:validation:Enum=LoadBalancer;ClusterIP;None
|
||||
type ImpersonationProxyServiceType string
|
||||
|
||||
const (
|
||||
// ImpersonationProxyServiceTypeLoadBalancer provisions a service of type LoadBalancer.
|
||||
ImpersonationProxyServiceTypeLoadBalancer = ImpersonationProxyServiceType("LoadBalancer")
|
||||
|
||||
// ImpersonationProxyServiceTypeClusterIP provisions a service of type ClusterIP.
|
||||
ImpersonationProxyServiceTypeClusterIP = ImpersonationProxyServiceType("ClusterIP")
|
||||
|
||||
// ImpersonationProxyServiceTypeNone does not automatically provision any service.
|
||||
ImpersonationProxyServiceTypeNone = ImpersonationProxyServiceType("None")
|
||||
)
|
||||
|
||||
// ImpersonationProxySpec describes the intended configuration of the Concierge impersonation proxy.
|
||||
type ImpersonationProxySpec struct {
|
||||
// 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.
|
||||
Mode ImpersonationProxyMode `json:"mode"`
|
||||
|
||||
// Service describes the configuration of the Service provisioned to expose the impersonation proxy to clients.
|
||||
//
|
||||
// +kubebuilder:default:={"type": "LoadBalancer"}
|
||||
Service ImpersonationProxyServiceSpec `json:"service"`
|
||||
|
||||
// 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.
|
||||
//
|
||||
// This field must be non-empty when spec.impersonationProxy.service.mode is "None".
|
||||
//
|
||||
// +optional
|
||||
ExternalEndpoint string `json:"externalEndpoint,omitempty"`
|
||||
}
|
||||
|
||||
// ImpersonationProxyServiceSpec describes how the Concierge should provision a Service to expose the impersonation proxy.
|
||||
type ImpersonationProxyServiceSpec struct {
|
||||
// Type specifies the type of Service to provision for the impersonation proxy.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// +kubebuilder:default:="LoadBalancer"
|
||||
Type ImpersonationProxyServiceType `json:"type,omitempty"`
|
||||
|
||||
// LoadBalancerIP specifies the IP address to set in the spec.loadBalancerIP field of the provisioned Service.
|
||||
// This is not supported on all cloud providers.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=255
|
||||
// +optional
|
||||
LoadBalancerIP string `json:"loadBalancerIP,omitempty"`
|
||||
|
||||
// Annotations specifies zero or more key/value pairs to set as annotations on the provisioned Service.
|
||||
//
|
||||
// +optional
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
}
|
||||
|
||||
// CredentialIssuerStatus describes the status of the Concierge.
|
||||
type CredentialIssuerStatus struct {
|
||||
// List of integration strategies that were attempted by Pinniped.
|
||||
Strategies []CredentialIssuerStrategy `json:"strategies"`
|
||||
@ -47,7 +137,8 @@ type CredentialIssuerStatus struct {
|
||||
KubeConfigInfo *CredentialIssuerKubeConfigInfo `json:"kubeConfigInfo,omitempty"`
|
||||
}
|
||||
|
||||
// Information needed to form a valid Pinniped-based kubeconfig using this credential issuer.
|
||||
// CredentialIssuerKubeConfigInfo provides the information needed to form a valid Pinniped-based kubeconfig using this credential issuer.
|
||||
// This type is deprecated and will be removed in a future version.
|
||||
type CredentialIssuerKubeConfigInfo struct {
|
||||
// The K8s API server URL.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
@ -59,7 +150,7 @@ type CredentialIssuerKubeConfigInfo struct {
|
||||
CertificateAuthorityData string `json:"certificateAuthorityData"`
|
||||
}
|
||||
|
||||
// Status of an integration strategy that was attempted by Pinniped.
|
||||
// CredentialIssuerStrategy describes the status of an integration strategy that was attempted by Pinniped.
|
||||
type CredentialIssuerStrategy struct {
|
||||
// Type of integration attempted.
|
||||
Type StrategyType `json:"type"`
|
||||
@ -81,6 +172,7 @@ type CredentialIssuerStrategy struct {
|
||||
Frontend *CredentialIssuerFrontend `json:"frontend,omitempty"`
|
||||
}
|
||||
|
||||
// CredentialIssuerFrontend describes how to connect using a particular integration strategy.
|
||||
type CredentialIssuerFrontend struct {
|
||||
// Type describes which frontend mechanism clients can use with a strategy.
|
||||
Type FrontendType `json:"type"`
|
||||
@ -118,7 +210,7 @@ type ImpersonationProxyInfo struct {
|
||||
CertificateAuthorityData string `json:"certificateAuthorityData"`
|
||||
}
|
||||
|
||||
// Describes the configuration status of a Pinniped credential issuer.
|
||||
// CredentialIssuer describes the configuration and status of the Pinniped Concierge credential issuer.
|
||||
// +genclient
|
||||
// +genclient:nonNamespaced
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
@ -128,12 +220,18 @@ type CredentialIssuer struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Status of the credential issuer.
|
||||
// Spec describes the intended configuration of the Concierge.
|
||||
//
|
||||
// +optional
|
||||
Spec CredentialIssuerSpec `json:"spec"`
|
||||
|
||||
// CredentialIssuerStatus describes the status of the Concierge.
|
||||
//
|
||||
// +optional
|
||||
Status CredentialIssuerStatus `json:"status"`
|
||||
}
|
||||
|
||||
// List of CredentialIssuer objects.
|
||||
// CredentialIssuerList is a list of CredentialIssuer objects.
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type CredentialIssuerList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
@ -68,8 +68,8 @@ type LDAPIdentityProviderGroupSearchAttributes struct {
|
||||
// GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name
|
||||
// in the user's list of groups after a successful authentication.
|
||||
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
||||
// server in the user's entry. Distinguished names can be used by specifying lower-case "dn".
|
||||
// Optional. When not specified, the default will act as if the GroupName were specified as "cn" (common name).
|
||||
// server in the user's entry. E.g. "cn" for common name. Distinguished names can be used by specifying lower-case "dn".
|
||||
// Optional. When not specified, the default will act as if the GroupName were specified as "dn" (distinguished name).
|
||||
// +optional
|
||||
GroupName string `json:"groupName,omitempty"`
|
||||
}
|
||||
|
@ -21,7 +21,8 @@ spec:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Describes the configuration status of a Pinniped credential issuer.
|
||||
description: CredentialIssuer describes the configuration and status of the
|
||||
Pinniped Concierge credential issuer.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
@ -35,8 +36,73 @@ spec:
|
||||
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.mode
|
||||
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
|
||||
required:
|
||||
- mode
|
||||
- service
|
||||
type: object
|
||||
required:
|
||||
- impersonationProxy
|
||||
type: object
|
||||
status:
|
||||
description: Status of the credential issuer.
|
||||
description: CredentialIssuerStatus describes the status of the Concierge.
|
||||
properties:
|
||||
kubeConfigInfo:
|
||||
description: Information needed to form a valid Pinniped-based kubeconfig
|
||||
@ -60,8 +126,8 @@ spec:
|
||||
description: List of integration strategies that were attempted by
|
||||
Pinniped.
|
||||
items:
|
||||
description: Status of an integration strategy that was attempted
|
||||
by Pinniped.
|
||||
description: CredentialIssuerStrategy describes the status of an
|
||||
integration strategy that was attempted by Pinniped.
|
||||
properties:
|
||||
frontend:
|
||||
description: Frontend describes how clients can connect using
|
||||
|
@ -49,8 +49,8 @@ data:
|
||||
servingCertificateSecret: (@= defaultResourceNameWithSuffix("api-tls-serving-certificate") @)
|
||||
credentialIssuer: (@= defaultResourceNameWithSuffix("config") @)
|
||||
apiService: (@= defaultResourceNameWithSuffix("api") @)
|
||||
impersonationConfigMap: (@= defaultResourceNameWithSuffix("impersonation-proxy-config") @)
|
||||
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") @)
|
||||
@ -253,3 +253,15 @@ 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:
|
||||
mode: #@ data.values.impersonation_proxy_spec.service.mode
|
||||
#@ 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
|
||||
|
@ -63,3 +63,33 @@ run_as_group: 1001 #! run_as_group specifies the group ID that will own the proc
|
||||
#! 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.
|
||||
mode: 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:
|
||||
|
@ -86,10 +86,10 @@ spec:
|
||||
in the user's list of groups after a successful authentication.
|
||||
The value of this field is case-sensitive and must match
|
||||
the case of the attribute name returned by the LDAP server
|
||||
in the user's entry. Distinguished names can be used by
|
||||
specifying lower-case "dn". Optional. When not specified,
|
||||
the default will act as if the GroupName were specified
|
||||
as "cn" (common name).
|
||||
in the user's entry. E.g. "cn" for common name. Distinguished
|
||||
names can be used by specifying lower-case "dn". Optional.
|
||||
When not specified, the default will act as if the GroupName
|
||||
were specified as "dn" (distinguished name).
|
||||
type: string
|
||||
type: object
|
||||
base:
|
||||
|
90
generated/1.17/README.adoc
generated
90
generated/1.17/README.adoc
generated
@ -220,7 +220,7 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge configuration
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-credentialissuer"]
|
||||
==== CredentialIssuer
|
||||
|
||||
Describes the configuration status of a Pinniped credential issuer.
|
||||
CredentialIssuer describes the configuration and status of the Pinniped Concierge credential issuer.
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
@ -232,7 +232,8 @@ Describes the configuration status of a Pinniped credential issuer.
|
||||
| Field | Description
|
||||
| *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`.
|
||||
|
||||
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-credentialissuerstatus[$$CredentialIssuerStatus$$]__ | Status of the credential issuer.
|
||||
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-credentialissuerspec[$$CredentialIssuerSpec$$]__ | Spec describes the intended configuration of the Concierge.
|
||||
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-credentialissuerstatus[$$CredentialIssuerStatus$$]__ | CredentialIssuerStatus describes the status of the Concierge.
|
||||
|===
|
||||
|
||||
|
||||
@ -275,10 +276,27 @@ Describes the configuration status of a Pinniped credential issuer.
|
||||
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-credentialissuerspec"]
|
||||
==== CredentialIssuerSpec
|
||||
|
||||
CredentialIssuerSpec describes the intended configuration of the Concierge.
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-credentialissuer[$$CredentialIssuer$$]
|
||||
****
|
||||
|
||||
[cols="25a,75a", options="header"]
|
||||
|===
|
||||
| Field | Description
|
||||
| *`impersonationProxy`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-impersonationproxyspec[$$ImpersonationProxySpec$$]__ | ImpersonationProxy describes the intended configuration of the Concierge impersonation proxy.
|
||||
|===
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-credentialissuerstatus"]
|
||||
==== CredentialIssuerStatus
|
||||
|
||||
Status of a credential issuer.
|
||||
CredentialIssuerStatus describes the status of the Concierge.
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
@ -333,6 +351,70 @@ Status of a credential issuer.
|
||||
|===
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-impersonationproxymode"]
|
||||
==== ImpersonationProxyMode (string)
|
||||
|
||||
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-impersonationproxyspec[$$ImpersonationProxySpec$$]
|
||||
****
|
||||
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-impersonationproxyservicespec"]
|
||||
==== ImpersonationProxyServiceSpec
|
||||
|
||||
ImpersonationProxyServiceSpec describes how the Concierge should provision a Service to expose the impersonation proxy.
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-impersonationproxyspec[$$ImpersonationProxySpec$$]
|
||||
****
|
||||
|
||||
[cols="25a,75a", options="header"]
|
||||
|===
|
||||
| Field | Description
|
||||
| *`type`* __ImpersonationProxyServiceType__ | Type specifies the type of Service to provision for the impersonation proxy.
|
||||
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.
|
||||
| *`loadBalancerIP`* __string__ | LoadBalancerIP specifies the IP address to set in the spec.loadBalancerIP field of the provisioned Service. This is not supported on all cloud providers.
|
||||
| *`annotations`* __object (keys:string, values:string)__ | Annotations specifies zero or more key/value pairs to set as annotations on the provisioned Service.
|
||||
|===
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-impersonationproxyservicetype"]
|
||||
==== ImpersonationProxyServiceType (string)
|
||||
|
||||
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-impersonationproxyservicespec[$$ImpersonationProxyServiceSpec$$]
|
||||
****
|
||||
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-impersonationproxyspec"]
|
||||
==== ImpersonationProxySpec
|
||||
|
||||
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-credentialissuerspec[$$CredentialIssuerSpec$$]
|
||||
****
|
||||
|
||||
[cols="25a,75a", options="header"]
|
||||
|===
|
||||
| Field | Description
|
||||
| *`mode`* __ImpersonationProxyMode__ | 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.
|
||||
| *`service`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-impersonationproxyservicespec[$$ImpersonationProxyServiceSpec$$]__ | Service describes the configuration of the Service provisioned to expose the impersonation proxy to clients.
|
||||
| *`externalEndpoint`* __string__ | 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.
|
||||
This field must be non-empty when spec.impersonationProxy.service.mode is "None".
|
||||
|===
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-17-apis-concierge-config-v1alpha1-tokencredentialrequestapiinfo"]
|
||||
==== TokenCredentialRequestAPIInfo
|
||||
|
||||
@ -770,7 +852,7 @@ LDAPIdentityProvider describes the configuration of an upstream Lightweight Dire
|
||||
[cols="25a,75a", options="header"]
|
||||
|===
|
||||
| Field | Description
|
||||
| *`groupName`* __string__ | GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name in the user's list of groups after a successful authentication. The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn". Optional. When not specified, the default will act as if the GroupName were specified as "cn" (common name).
|
||||
| *`groupName`* __string__ | GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name in the user's list of groups after a successful authentication. The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. E.g. "cn" for common name. Distinguished names can be used by specifying lower-case "dn". Optional. When not specified, the default will act as if the GroupName were specified as "dn" (distinguished name).
|
||||
|===
|
||||
|
||||
|
||||
|
@ -3,17 +3,23 @@
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// StrategyType enumerates a type of "strategy" used to implement credential access on a cluster.
|
||||
// +kubebuilder:validation:Enum=KubeClusterSigningCertificate;ImpersonationProxy
|
||||
type StrategyType string
|
||||
|
||||
// FrontendType enumerates a type of "frontend" used to provide access to users of a cluster.
|
||||
// +kubebuilder:validation:Enum=TokenCredentialRequestAPI;ImpersonationProxy
|
||||
type FrontendType string
|
||||
|
||||
// StrategyStatus enumerates whether a strategy is working on a cluster.
|
||||
// +kubebuilder:validation:Enum=Success;Error
|
||||
type StrategyStatus string
|
||||
|
||||
// StrategyReason enumerates the detailed reason why a strategy is in a particular status.
|
||||
// +kubebuilder:validation:Enum=Listening;Pending;Disabled;ErrorDuringSetup;CouldNotFetchKey;CouldNotGetClusterInfo;FetchedKey
|
||||
type StrategyReason string
|
||||
|
||||
@ -36,7 +42,91 @@ const (
|
||||
FetchedKeyStrategyReason = StrategyReason("FetchedKey")
|
||||
)
|
||||
|
||||
// Status of a credential issuer.
|
||||
// CredentialIssuerSpec describes the intended configuration of the Concierge.
|
||||
type CredentialIssuerSpec struct {
|
||||
// ImpersonationProxy describes the intended configuration of the Concierge impersonation proxy.
|
||||
ImpersonationProxy *ImpersonationProxySpec `json:"impersonationProxy"`
|
||||
}
|
||||
|
||||
// ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy.
|
||||
//
|
||||
// +kubebuilder:validation:Enum=auto;enabled;disabled
|
||||
type ImpersonationProxyMode string
|
||||
|
||||
const (
|
||||
// ImpersonationProxyModeDisabled explicitly disables the impersonation proxy.
|
||||
ImpersonationProxyModeDisabled = ImpersonationProxyMode("disabled")
|
||||
|
||||
// ImpersonationProxyModeEnabled explicitly enables the impersonation proxy.
|
||||
ImpersonationProxyModeEnabled = ImpersonationProxyMode("enabled")
|
||||
|
||||
// ImpersonationProxyModeAuto enables or disables the impersonation proxy based upon the cluster in which it is running.
|
||||
ImpersonationProxyModeAuto = ImpersonationProxyMode("auto")
|
||||
)
|
||||
|
||||
// ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy.
|
||||
//
|
||||
// +kubebuilder:validation:Enum=LoadBalancer;ClusterIP;None
|
||||
type ImpersonationProxyServiceType string
|
||||
|
||||
const (
|
||||
// ImpersonationProxyServiceTypeLoadBalancer provisions a service of type LoadBalancer.
|
||||
ImpersonationProxyServiceTypeLoadBalancer = ImpersonationProxyServiceType("LoadBalancer")
|
||||
|
||||
// ImpersonationProxyServiceTypeClusterIP provisions a service of type ClusterIP.
|
||||
ImpersonationProxyServiceTypeClusterIP = ImpersonationProxyServiceType("ClusterIP")
|
||||
|
||||
// ImpersonationProxyServiceTypeNone does not automatically provision any service.
|
||||
ImpersonationProxyServiceTypeNone = ImpersonationProxyServiceType("None")
|
||||
)
|
||||
|
||||
// ImpersonationProxySpec describes the intended configuration of the Concierge impersonation proxy.
|
||||
type ImpersonationProxySpec struct {
|
||||
// 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.
|
||||
Mode ImpersonationProxyMode `json:"mode"`
|
||||
|
||||
// Service describes the configuration of the Service provisioned to expose the impersonation proxy to clients.
|
||||
//
|
||||
// +kubebuilder:default:={"type": "LoadBalancer"}
|
||||
Service ImpersonationProxyServiceSpec `json:"service"`
|
||||
|
||||
// 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.
|
||||
//
|
||||
// This field must be non-empty when spec.impersonationProxy.service.mode is "None".
|
||||
//
|
||||
// +optional
|
||||
ExternalEndpoint string `json:"externalEndpoint,omitempty"`
|
||||
}
|
||||
|
||||
// ImpersonationProxyServiceSpec describes how the Concierge should provision a Service to expose the impersonation proxy.
|
||||
type ImpersonationProxyServiceSpec struct {
|
||||
// Type specifies the type of Service to provision for the impersonation proxy.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// +kubebuilder:default:="LoadBalancer"
|
||||
Type ImpersonationProxyServiceType `json:"type,omitempty"`
|
||||
|
||||
// LoadBalancerIP specifies the IP address to set in the spec.loadBalancerIP field of the provisioned Service.
|
||||
// This is not supported on all cloud providers.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=255
|
||||
// +optional
|
||||
LoadBalancerIP string `json:"loadBalancerIP,omitempty"`
|
||||
|
||||
// Annotations specifies zero or more key/value pairs to set as annotations on the provisioned Service.
|
||||
//
|
||||
// +optional
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
}
|
||||
|
||||
// CredentialIssuerStatus describes the status of the Concierge.
|
||||
type CredentialIssuerStatus struct {
|
||||
// List of integration strategies that were attempted by Pinniped.
|
||||
Strategies []CredentialIssuerStrategy `json:"strategies"`
|
||||
@ -47,7 +137,8 @@ type CredentialIssuerStatus struct {
|
||||
KubeConfigInfo *CredentialIssuerKubeConfigInfo `json:"kubeConfigInfo,omitempty"`
|
||||
}
|
||||
|
||||
// Information needed to form a valid Pinniped-based kubeconfig using this credential issuer.
|
||||
// CredentialIssuerKubeConfigInfo provides the information needed to form a valid Pinniped-based kubeconfig using this credential issuer.
|
||||
// This type is deprecated and will be removed in a future version.
|
||||
type CredentialIssuerKubeConfigInfo struct {
|
||||
// The K8s API server URL.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
@ -59,7 +150,7 @@ type CredentialIssuerKubeConfigInfo struct {
|
||||
CertificateAuthorityData string `json:"certificateAuthorityData"`
|
||||
}
|
||||
|
||||
// Status of an integration strategy that was attempted by Pinniped.
|
||||
// CredentialIssuerStrategy describes the status of an integration strategy that was attempted by Pinniped.
|
||||
type CredentialIssuerStrategy struct {
|
||||
// Type of integration attempted.
|
||||
Type StrategyType `json:"type"`
|
||||
@ -81,6 +172,7 @@ type CredentialIssuerStrategy struct {
|
||||
Frontend *CredentialIssuerFrontend `json:"frontend,omitempty"`
|
||||
}
|
||||
|
||||
// CredentialIssuerFrontend describes how to connect using a particular integration strategy.
|
||||
type CredentialIssuerFrontend struct {
|
||||
// Type describes which frontend mechanism clients can use with a strategy.
|
||||
Type FrontendType `json:"type"`
|
||||
@ -118,7 +210,7 @@ type ImpersonationProxyInfo struct {
|
||||
CertificateAuthorityData string `json:"certificateAuthorityData"`
|
||||
}
|
||||
|
||||
// Describes the configuration status of a Pinniped credential issuer.
|
||||
// CredentialIssuer describes the configuration and status of the Pinniped Concierge credential issuer.
|
||||
// +genclient
|
||||
// +genclient:nonNamespaced
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
@ -128,12 +220,18 @@ type CredentialIssuer struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Status of the credential issuer.
|
||||
// Spec describes the intended configuration of the Concierge.
|
||||
//
|
||||
// +optional
|
||||
Spec CredentialIssuerSpec `json:"spec"`
|
||||
|
||||
// CredentialIssuerStatus describes the status of the Concierge.
|
||||
//
|
||||
// +optional
|
||||
Status CredentialIssuerStatus `json:"status"`
|
||||
}
|
||||
|
||||
// List of CredentialIssuer objects.
|
||||
// CredentialIssuerList is a list of CredentialIssuer objects.
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type CredentialIssuerList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
@ -16,6 +16,7 @@ func (in *CredentialIssuer) DeepCopyInto(out *CredentialIssuer) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
return
|
||||
}
|
||||
@ -113,6 +114,27 @@ func (in *CredentialIssuerList) DeepCopyObject() runtime.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CredentialIssuerSpec) DeepCopyInto(out *CredentialIssuerSpec) {
|
||||
*out = *in
|
||||
if in.ImpersonationProxy != nil {
|
||||
in, out := &in.ImpersonationProxy, &out.ImpersonationProxy
|
||||
*out = new(ImpersonationProxySpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CredentialIssuerSpec.
|
||||
func (in *CredentialIssuerSpec) DeepCopy() *CredentialIssuerSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CredentialIssuerSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CredentialIssuerStatus) DeepCopyInto(out *CredentialIssuerStatus) {
|
||||
*out = *in
|
||||
@ -179,6 +201,46 @@ func (in *ImpersonationProxyInfo) DeepCopy() *ImpersonationProxyInfo {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ImpersonationProxyServiceSpec) DeepCopyInto(out *ImpersonationProxyServiceSpec) {
|
||||
*out = *in
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImpersonationProxyServiceSpec.
|
||||
func (in *ImpersonationProxyServiceSpec) DeepCopy() *ImpersonationProxyServiceSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ImpersonationProxyServiceSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ImpersonationProxySpec) DeepCopyInto(out *ImpersonationProxySpec) {
|
||||
*out = *in
|
||||
in.Service.DeepCopyInto(&out.Service)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImpersonationProxySpec.
|
||||
func (in *ImpersonationProxySpec) DeepCopy() *ImpersonationProxySpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ImpersonationProxySpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TokenCredentialRequestAPIInfo) DeepCopyInto(out *TokenCredentialRequestAPIInfo) {
|
||||
*out = *in
|
||||
|
@ -68,8 +68,8 @@ type LDAPIdentityProviderGroupSearchAttributes struct {
|
||||
// GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name
|
||||
// in the user's list of groups after a successful authentication.
|
||||
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
||||
// server in the user's entry. Distinguished names can be used by specifying lower-case "dn".
|
||||
// Optional. When not specified, the default will act as if the GroupName were specified as "cn" (common name).
|
||||
// server in the user's entry. E.g. "cn" for common name. Distinguished names can be used by specifying lower-case "dn".
|
||||
// Optional. When not specified, the default will act as if the GroupName were specified as "dn" (distinguished name).
|
||||
// +optional
|
||||
GroupName string `json:"groupName,omitempty"`
|
||||
}
|
||||
|
@ -21,7 +21,8 @@ spec:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Describes the configuration status of a Pinniped credential issuer.
|
||||
description: CredentialIssuer describes the configuration and status of the
|
||||
Pinniped Concierge credential issuer.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
@ -35,8 +36,73 @@ spec:
|
||||
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.mode
|
||||
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
|
||||
required:
|
||||
- mode
|
||||
- service
|
||||
type: object
|
||||
required:
|
||||
- impersonationProxy
|
||||
type: object
|
||||
status:
|
||||
description: Status of the credential issuer.
|
||||
description: CredentialIssuerStatus describes the status of the Concierge.
|
||||
properties:
|
||||
kubeConfigInfo:
|
||||
description: Information needed to form a valid Pinniped-based kubeconfig
|
||||
@ -60,8 +126,8 @@ spec:
|
||||
description: List of integration strategies that were attempted by
|
||||
Pinniped.
|
||||
items:
|
||||
description: Status of an integration strategy that was attempted
|
||||
by Pinniped.
|
||||
description: CredentialIssuerStrategy describes the status of an
|
||||
integration strategy that was attempted by Pinniped.
|
||||
properties:
|
||||
frontend:
|
||||
description: Frontend describes how clients can connect using
|
||||
|
@ -86,10 +86,10 @@ spec:
|
||||
in the user's list of groups after a successful authentication.
|
||||
The value of this field is case-sensitive and must match
|
||||
the case of the attribute name returned by the LDAP server
|
||||
in the user's entry. Distinguished names can be used by
|
||||
specifying lower-case "dn". Optional. When not specified,
|
||||
the default will act as if the GroupName were specified
|
||||
as "cn" (common name).
|
||||
in the user's entry. E.g. "cn" for common name. Distinguished
|
||||
names can be used by specifying lower-case "dn". Optional.
|
||||
When not specified, the default will act as if the GroupName
|
||||
were specified as "dn" (distinguished name).
|
||||
type: string
|
||||
type: object
|
||||
base:
|
||||
|
90
generated/1.18/README.adoc
generated
90
generated/1.18/README.adoc
generated
@ -220,7 +220,7 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge configuration
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-concierge-config-v1alpha1-credentialissuer"]
|
||||
==== CredentialIssuer
|
||||
|
||||
Describes the configuration status of a Pinniped credential issuer.
|
||||
CredentialIssuer describes the configuration and status of the Pinniped Concierge credential issuer.
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
@ -232,7 +232,8 @@ Describes the configuration status of a Pinniped credential issuer.
|
||||
| Field | Description
|
||||
| *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`.
|
||||
|
||||
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-concierge-config-v1alpha1-credentialissuerstatus[$$CredentialIssuerStatus$$]__ | Status of the credential issuer.
|
||||
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-concierge-config-v1alpha1-credentialissuerspec[$$CredentialIssuerSpec$$]__ | Spec describes the intended configuration of the Concierge.
|
||||
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-concierge-config-v1alpha1-credentialissuerstatus[$$CredentialIssuerStatus$$]__ | CredentialIssuerStatus describes the status of the Concierge.
|
||||
|===
|
||||
|
||||
|
||||
@ -275,10 +276,27 @@ Describes the configuration status of a Pinniped credential issuer.
|
||||
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-concierge-config-v1alpha1-credentialissuerspec"]
|
||||
==== CredentialIssuerSpec
|
||||
|
||||
CredentialIssuerSpec describes the intended configuration of the Concierge.
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-concierge-config-v1alpha1-credentialissuer[$$CredentialIssuer$$]
|
||||
****
|
||||
|
||||
[cols="25a,75a", options="header"]
|
||||
|===
|
||||
| Field | Description
|
||||
| *`impersonationProxy`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-concierge-config-v1alpha1-impersonationproxyspec[$$ImpersonationProxySpec$$]__ | ImpersonationProxy describes the intended configuration of the Concierge impersonation proxy.
|
||||
|===
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-concierge-config-v1alpha1-credentialissuerstatus"]
|
||||
==== CredentialIssuerStatus
|
||||
|
||||
Status of a credential issuer.
|
||||
CredentialIssuerStatus describes the status of the Concierge.
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
@ -333,6 +351,70 @@ Status of a credential issuer.
|
||||
|===
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-concierge-config-v1alpha1-impersonationproxymode"]
|
||||
==== ImpersonationProxyMode (string)
|
||||
|
||||
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-concierge-config-v1alpha1-impersonationproxyspec[$$ImpersonationProxySpec$$]
|
||||
****
|
||||
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-concierge-config-v1alpha1-impersonationproxyservicespec"]
|
||||
==== ImpersonationProxyServiceSpec
|
||||
|
||||
ImpersonationProxyServiceSpec describes how the Concierge should provision a Service to expose the impersonation proxy.
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-concierge-config-v1alpha1-impersonationproxyspec[$$ImpersonationProxySpec$$]
|
||||
****
|
||||
|
||||
[cols="25a,75a", options="header"]
|
||||
|===
|
||||
| Field | Description
|
||||
| *`type`* __ImpersonationProxyServiceType__ | Type specifies the type of Service to provision for the impersonation proxy.
|
||||
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.
|
||||
| *`loadBalancerIP`* __string__ | LoadBalancerIP specifies the IP address to set in the spec.loadBalancerIP field of the provisioned Service. This is not supported on all cloud providers.
|
||||
| *`annotations`* __object (keys:string, values:string)__ | Annotations specifies zero or more key/value pairs to set as annotations on the provisioned Service.
|
||||
|===
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-concierge-config-v1alpha1-impersonationproxyservicetype"]
|
||||
==== ImpersonationProxyServiceType (string)
|
||||
|
||||
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-concierge-config-v1alpha1-impersonationproxyservicespec[$$ImpersonationProxyServiceSpec$$]
|
||||
****
|
||||
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-concierge-config-v1alpha1-impersonationproxyspec"]
|
||||
==== ImpersonationProxySpec
|
||||
|
||||
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-concierge-config-v1alpha1-credentialissuerspec[$$CredentialIssuerSpec$$]
|
||||
****
|
||||
|
||||
[cols="25a,75a", options="header"]
|
||||
|===
|
||||
| Field | Description
|
||||
| *`mode`* __ImpersonationProxyMode__ | 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.
|
||||
| *`service`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-concierge-config-v1alpha1-impersonationproxyservicespec[$$ImpersonationProxyServiceSpec$$]__ | Service describes the configuration of the Service provisioned to expose the impersonation proxy to clients.
|
||||
| *`externalEndpoint`* __string__ | 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.
|
||||
This field must be non-empty when spec.impersonationProxy.service.mode is "None".
|
||||
|===
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-18-apis-concierge-config-v1alpha1-tokencredentialrequestapiinfo"]
|
||||
==== TokenCredentialRequestAPIInfo
|
||||
|
||||
@ -770,7 +852,7 @@ LDAPIdentityProvider describes the configuration of an upstream Lightweight Dire
|
||||
[cols="25a,75a", options="header"]
|
||||
|===
|
||||
| Field | Description
|
||||
| *`groupName`* __string__ | GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name in the user's list of groups after a successful authentication. The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn". Optional. When not specified, the default will act as if the GroupName were specified as "cn" (common name).
|
||||
| *`groupName`* __string__ | GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name in the user's list of groups after a successful authentication. The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. E.g. "cn" for common name. Distinguished names can be used by specifying lower-case "dn". Optional. When not specified, the default will act as if the GroupName were specified as "dn" (distinguished name).
|
||||
|===
|
||||
|
||||
|
||||
|
@ -3,17 +3,23 @@
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// StrategyType enumerates a type of "strategy" used to implement credential access on a cluster.
|
||||
// +kubebuilder:validation:Enum=KubeClusterSigningCertificate;ImpersonationProxy
|
||||
type StrategyType string
|
||||
|
||||
// FrontendType enumerates a type of "frontend" used to provide access to users of a cluster.
|
||||
// +kubebuilder:validation:Enum=TokenCredentialRequestAPI;ImpersonationProxy
|
||||
type FrontendType string
|
||||
|
||||
// StrategyStatus enumerates whether a strategy is working on a cluster.
|
||||
// +kubebuilder:validation:Enum=Success;Error
|
||||
type StrategyStatus string
|
||||
|
||||
// StrategyReason enumerates the detailed reason why a strategy is in a particular status.
|
||||
// +kubebuilder:validation:Enum=Listening;Pending;Disabled;ErrorDuringSetup;CouldNotFetchKey;CouldNotGetClusterInfo;FetchedKey
|
||||
type StrategyReason string
|
||||
|
||||
@ -36,7 +42,91 @@ const (
|
||||
FetchedKeyStrategyReason = StrategyReason("FetchedKey")
|
||||
)
|
||||
|
||||
// Status of a credential issuer.
|
||||
// CredentialIssuerSpec describes the intended configuration of the Concierge.
|
||||
type CredentialIssuerSpec struct {
|
||||
// ImpersonationProxy describes the intended configuration of the Concierge impersonation proxy.
|
||||
ImpersonationProxy *ImpersonationProxySpec `json:"impersonationProxy"`
|
||||
}
|
||||
|
||||
// ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy.
|
||||
//
|
||||
// +kubebuilder:validation:Enum=auto;enabled;disabled
|
||||
type ImpersonationProxyMode string
|
||||
|
||||
const (
|
||||
// ImpersonationProxyModeDisabled explicitly disables the impersonation proxy.
|
||||
ImpersonationProxyModeDisabled = ImpersonationProxyMode("disabled")
|
||||
|
||||
// ImpersonationProxyModeEnabled explicitly enables the impersonation proxy.
|
||||
ImpersonationProxyModeEnabled = ImpersonationProxyMode("enabled")
|
||||
|
||||
// ImpersonationProxyModeAuto enables or disables the impersonation proxy based upon the cluster in which it is running.
|
||||
ImpersonationProxyModeAuto = ImpersonationProxyMode("auto")
|
||||
)
|
||||
|
||||
// ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy.
|
||||
//
|
||||
// +kubebuilder:validation:Enum=LoadBalancer;ClusterIP;None
|
||||
type ImpersonationProxyServiceType string
|
||||
|
||||
const (
|
||||
// ImpersonationProxyServiceTypeLoadBalancer provisions a service of type LoadBalancer.
|
||||
ImpersonationProxyServiceTypeLoadBalancer = ImpersonationProxyServiceType("LoadBalancer")
|
||||
|
||||
// ImpersonationProxyServiceTypeClusterIP provisions a service of type ClusterIP.
|
||||
ImpersonationProxyServiceTypeClusterIP = ImpersonationProxyServiceType("ClusterIP")
|
||||
|
||||
// ImpersonationProxyServiceTypeNone does not automatically provision any service.
|
||||
ImpersonationProxyServiceTypeNone = ImpersonationProxyServiceType("None")
|
||||
)
|
||||
|
||||
// ImpersonationProxySpec describes the intended configuration of the Concierge impersonation proxy.
|
||||
type ImpersonationProxySpec struct {
|
||||
// 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.
|
||||
Mode ImpersonationProxyMode `json:"mode"`
|
||||
|
||||
// Service describes the configuration of the Service provisioned to expose the impersonation proxy to clients.
|
||||
//
|
||||
// +kubebuilder:default:={"type": "LoadBalancer"}
|
||||
Service ImpersonationProxyServiceSpec `json:"service"`
|
||||
|
||||
// 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.
|
||||
//
|
||||
// This field must be non-empty when spec.impersonationProxy.service.mode is "None".
|
||||
//
|
||||
// +optional
|
||||
ExternalEndpoint string `json:"externalEndpoint,omitempty"`
|
||||
}
|
||||
|
||||
// ImpersonationProxyServiceSpec describes how the Concierge should provision a Service to expose the impersonation proxy.
|
||||
type ImpersonationProxyServiceSpec struct {
|
||||
// Type specifies the type of Service to provision for the impersonation proxy.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// +kubebuilder:default:="LoadBalancer"
|
||||
Type ImpersonationProxyServiceType `json:"type,omitempty"`
|
||||
|
||||
// LoadBalancerIP specifies the IP address to set in the spec.loadBalancerIP field of the provisioned Service.
|
||||
// This is not supported on all cloud providers.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=255
|
||||
// +optional
|
||||
LoadBalancerIP string `json:"loadBalancerIP,omitempty"`
|
||||
|
||||
// Annotations specifies zero or more key/value pairs to set as annotations on the provisioned Service.
|
||||
//
|
||||
// +optional
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
}
|
||||
|
||||
// CredentialIssuerStatus describes the status of the Concierge.
|
||||
type CredentialIssuerStatus struct {
|
||||
// List of integration strategies that were attempted by Pinniped.
|
||||
Strategies []CredentialIssuerStrategy `json:"strategies"`
|
||||
@ -47,7 +137,8 @@ type CredentialIssuerStatus struct {
|
||||
KubeConfigInfo *CredentialIssuerKubeConfigInfo `json:"kubeConfigInfo,omitempty"`
|
||||
}
|
||||
|
||||
// Information needed to form a valid Pinniped-based kubeconfig using this credential issuer.
|
||||
// CredentialIssuerKubeConfigInfo provides the information needed to form a valid Pinniped-based kubeconfig using this credential issuer.
|
||||
// This type is deprecated and will be removed in a future version.
|
||||
type CredentialIssuerKubeConfigInfo struct {
|
||||
// The K8s API server URL.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
@ -59,7 +150,7 @@ type CredentialIssuerKubeConfigInfo struct {
|
||||
CertificateAuthorityData string `json:"certificateAuthorityData"`
|
||||
}
|
||||
|
||||
// Status of an integration strategy that was attempted by Pinniped.
|
||||
// CredentialIssuerStrategy describes the status of an integration strategy that was attempted by Pinniped.
|
||||
type CredentialIssuerStrategy struct {
|
||||
// Type of integration attempted.
|
||||
Type StrategyType `json:"type"`
|
||||
@ -81,6 +172,7 @@ type CredentialIssuerStrategy struct {
|
||||
Frontend *CredentialIssuerFrontend `json:"frontend,omitempty"`
|
||||
}
|
||||
|
||||
// CredentialIssuerFrontend describes how to connect using a particular integration strategy.
|
||||
type CredentialIssuerFrontend struct {
|
||||
// Type describes which frontend mechanism clients can use with a strategy.
|
||||
Type FrontendType `json:"type"`
|
||||
@ -118,7 +210,7 @@ type ImpersonationProxyInfo struct {
|
||||
CertificateAuthorityData string `json:"certificateAuthorityData"`
|
||||
}
|
||||
|
||||
// Describes the configuration status of a Pinniped credential issuer.
|
||||
// CredentialIssuer describes the configuration and status of the Pinniped Concierge credential issuer.
|
||||
// +genclient
|
||||
// +genclient:nonNamespaced
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
@ -128,12 +220,18 @@ type CredentialIssuer struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Status of the credential issuer.
|
||||
// Spec describes the intended configuration of the Concierge.
|
||||
//
|
||||
// +optional
|
||||
Spec CredentialIssuerSpec `json:"spec"`
|
||||
|
||||
// CredentialIssuerStatus describes the status of the Concierge.
|
||||
//
|
||||
// +optional
|
||||
Status CredentialIssuerStatus `json:"status"`
|
||||
}
|
||||
|
||||
// List of CredentialIssuer objects.
|
||||
// CredentialIssuerList is a list of CredentialIssuer objects.
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type CredentialIssuerList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
@ -16,6 +16,7 @@ func (in *CredentialIssuer) DeepCopyInto(out *CredentialIssuer) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
return
|
||||
}
|
||||
@ -113,6 +114,27 @@ func (in *CredentialIssuerList) DeepCopyObject() runtime.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CredentialIssuerSpec) DeepCopyInto(out *CredentialIssuerSpec) {
|
||||
*out = *in
|
||||
if in.ImpersonationProxy != nil {
|
||||
in, out := &in.ImpersonationProxy, &out.ImpersonationProxy
|
||||
*out = new(ImpersonationProxySpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CredentialIssuerSpec.
|
||||
func (in *CredentialIssuerSpec) DeepCopy() *CredentialIssuerSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CredentialIssuerSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CredentialIssuerStatus) DeepCopyInto(out *CredentialIssuerStatus) {
|
||||
*out = *in
|
||||
@ -179,6 +201,46 @@ func (in *ImpersonationProxyInfo) DeepCopy() *ImpersonationProxyInfo {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ImpersonationProxyServiceSpec) DeepCopyInto(out *ImpersonationProxyServiceSpec) {
|
||||
*out = *in
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImpersonationProxyServiceSpec.
|
||||
func (in *ImpersonationProxyServiceSpec) DeepCopy() *ImpersonationProxyServiceSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ImpersonationProxyServiceSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ImpersonationProxySpec) DeepCopyInto(out *ImpersonationProxySpec) {
|
||||
*out = *in
|
||||
in.Service.DeepCopyInto(&out.Service)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImpersonationProxySpec.
|
||||
func (in *ImpersonationProxySpec) DeepCopy() *ImpersonationProxySpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ImpersonationProxySpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TokenCredentialRequestAPIInfo) DeepCopyInto(out *TokenCredentialRequestAPIInfo) {
|
||||
*out = *in
|
||||
|
@ -68,8 +68,8 @@ type LDAPIdentityProviderGroupSearchAttributes struct {
|
||||
// GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name
|
||||
// in the user's list of groups after a successful authentication.
|
||||
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
||||
// server in the user's entry. Distinguished names can be used by specifying lower-case "dn".
|
||||
// Optional. When not specified, the default will act as if the GroupName were specified as "cn" (common name).
|
||||
// server in the user's entry. E.g. "cn" for common name. Distinguished names can be used by specifying lower-case "dn".
|
||||
// Optional. When not specified, the default will act as if the GroupName were specified as "dn" (distinguished name).
|
||||
// +optional
|
||||
GroupName string `json:"groupName,omitempty"`
|
||||
}
|
||||
|
@ -21,7 +21,8 @@ spec:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Describes the configuration status of a Pinniped credential issuer.
|
||||
description: CredentialIssuer describes the configuration and status of the
|
||||
Pinniped Concierge credential issuer.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
@ -35,8 +36,73 @@ spec:
|
||||
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.mode
|
||||
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
|
||||
required:
|
||||
- mode
|
||||
- service
|
||||
type: object
|
||||
required:
|
||||
- impersonationProxy
|
||||
type: object
|
||||
status:
|
||||
description: Status of the credential issuer.
|
||||
description: CredentialIssuerStatus describes the status of the Concierge.
|
||||
properties:
|
||||
kubeConfigInfo:
|
||||
description: Information needed to form a valid Pinniped-based kubeconfig
|
||||
@ -60,8 +126,8 @@ spec:
|
||||
description: List of integration strategies that were attempted by
|
||||
Pinniped.
|
||||
items:
|
||||
description: Status of an integration strategy that was attempted
|
||||
by Pinniped.
|
||||
description: CredentialIssuerStrategy describes the status of an
|
||||
integration strategy that was attempted by Pinniped.
|
||||
properties:
|
||||
frontend:
|
||||
description: Frontend describes how clients can connect using
|
||||
|
@ -86,10 +86,10 @@ spec:
|
||||
in the user's list of groups after a successful authentication.
|
||||
The value of this field is case-sensitive and must match
|
||||
the case of the attribute name returned by the LDAP server
|
||||
in the user's entry. Distinguished names can be used by
|
||||
specifying lower-case "dn". Optional. When not specified,
|
||||
the default will act as if the GroupName were specified
|
||||
as "cn" (common name).
|
||||
in the user's entry. E.g. "cn" for common name. Distinguished
|
||||
names can be used by specifying lower-case "dn". Optional.
|
||||
When not specified, the default will act as if the GroupName
|
||||
were specified as "dn" (distinguished name).
|
||||
type: string
|
||||
type: object
|
||||
base:
|
||||
|
90
generated/1.19/README.adoc
generated
90
generated/1.19/README.adoc
generated
@ -220,7 +220,7 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge configuration
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-concierge-config-v1alpha1-credentialissuer"]
|
||||
==== CredentialIssuer
|
||||
|
||||
Describes the configuration status of a Pinniped credential issuer.
|
||||
CredentialIssuer describes the configuration and status of the Pinniped Concierge credential issuer.
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
@ -232,7 +232,8 @@ Describes the configuration status of a Pinniped credential issuer.
|
||||
| Field | Description
|
||||
| *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.19/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`.
|
||||
|
||||
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-concierge-config-v1alpha1-credentialissuerstatus[$$CredentialIssuerStatus$$]__ | Status of the credential issuer.
|
||||
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-concierge-config-v1alpha1-credentialissuerspec[$$CredentialIssuerSpec$$]__ | Spec describes the intended configuration of the Concierge.
|
||||
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-concierge-config-v1alpha1-credentialissuerstatus[$$CredentialIssuerStatus$$]__ | CredentialIssuerStatus describes the status of the Concierge.
|
||||
|===
|
||||
|
||||
|
||||
@ -275,10 +276,27 @@ Describes the configuration status of a Pinniped credential issuer.
|
||||
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-concierge-config-v1alpha1-credentialissuerspec"]
|
||||
==== CredentialIssuerSpec
|
||||
|
||||
CredentialIssuerSpec describes the intended configuration of the Concierge.
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-concierge-config-v1alpha1-credentialissuer[$$CredentialIssuer$$]
|
||||
****
|
||||
|
||||
[cols="25a,75a", options="header"]
|
||||
|===
|
||||
| Field | Description
|
||||
| *`impersonationProxy`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-concierge-config-v1alpha1-impersonationproxyspec[$$ImpersonationProxySpec$$]__ | ImpersonationProxy describes the intended configuration of the Concierge impersonation proxy.
|
||||
|===
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-concierge-config-v1alpha1-credentialissuerstatus"]
|
||||
==== CredentialIssuerStatus
|
||||
|
||||
Status of a credential issuer.
|
||||
CredentialIssuerStatus describes the status of the Concierge.
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
@ -333,6 +351,70 @@ Status of a credential issuer.
|
||||
|===
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-concierge-config-v1alpha1-impersonationproxymode"]
|
||||
==== ImpersonationProxyMode (string)
|
||||
|
||||
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-concierge-config-v1alpha1-impersonationproxyspec[$$ImpersonationProxySpec$$]
|
||||
****
|
||||
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-concierge-config-v1alpha1-impersonationproxyservicespec"]
|
||||
==== ImpersonationProxyServiceSpec
|
||||
|
||||
ImpersonationProxyServiceSpec describes how the Concierge should provision a Service to expose the impersonation proxy.
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-concierge-config-v1alpha1-impersonationproxyspec[$$ImpersonationProxySpec$$]
|
||||
****
|
||||
|
||||
[cols="25a,75a", options="header"]
|
||||
|===
|
||||
| Field | Description
|
||||
| *`type`* __ImpersonationProxyServiceType__ | Type specifies the type of Service to provision for the impersonation proxy.
|
||||
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.
|
||||
| *`loadBalancerIP`* __string__ | LoadBalancerIP specifies the IP address to set in the spec.loadBalancerIP field of the provisioned Service. This is not supported on all cloud providers.
|
||||
| *`annotations`* __object (keys:string, values:string)__ | Annotations specifies zero or more key/value pairs to set as annotations on the provisioned Service.
|
||||
|===
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-concierge-config-v1alpha1-impersonationproxyservicetype"]
|
||||
==== ImpersonationProxyServiceType (string)
|
||||
|
||||
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-concierge-config-v1alpha1-impersonationproxyservicespec[$$ImpersonationProxyServiceSpec$$]
|
||||
****
|
||||
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-concierge-config-v1alpha1-impersonationproxyspec"]
|
||||
==== ImpersonationProxySpec
|
||||
|
||||
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-concierge-config-v1alpha1-credentialissuerspec[$$CredentialIssuerSpec$$]
|
||||
****
|
||||
|
||||
[cols="25a,75a", options="header"]
|
||||
|===
|
||||
| Field | Description
|
||||
| *`mode`* __ImpersonationProxyMode__ | 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.
|
||||
| *`service`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-concierge-config-v1alpha1-impersonationproxyservicespec[$$ImpersonationProxyServiceSpec$$]__ | Service describes the configuration of the Service provisioned to expose the impersonation proxy to clients.
|
||||
| *`externalEndpoint`* __string__ | 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.
|
||||
This field must be non-empty when spec.impersonationProxy.service.mode is "None".
|
||||
|===
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-19-apis-concierge-config-v1alpha1-tokencredentialrequestapiinfo"]
|
||||
==== TokenCredentialRequestAPIInfo
|
||||
|
||||
@ -770,7 +852,7 @@ LDAPIdentityProvider describes the configuration of an upstream Lightweight Dire
|
||||
[cols="25a,75a", options="header"]
|
||||
|===
|
||||
| Field | Description
|
||||
| *`groupName`* __string__ | GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name in the user's list of groups after a successful authentication. The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn". Optional. When not specified, the default will act as if the GroupName were specified as "cn" (common name).
|
||||
| *`groupName`* __string__ | GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name in the user's list of groups after a successful authentication. The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. E.g. "cn" for common name. Distinguished names can be used by specifying lower-case "dn". Optional. When not specified, the default will act as if the GroupName were specified as "dn" (distinguished name).
|
||||
|===
|
||||
|
||||
|
||||
|
@ -3,17 +3,23 @@
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// StrategyType enumerates a type of "strategy" used to implement credential access on a cluster.
|
||||
// +kubebuilder:validation:Enum=KubeClusterSigningCertificate;ImpersonationProxy
|
||||
type StrategyType string
|
||||
|
||||
// FrontendType enumerates a type of "frontend" used to provide access to users of a cluster.
|
||||
// +kubebuilder:validation:Enum=TokenCredentialRequestAPI;ImpersonationProxy
|
||||
type FrontendType string
|
||||
|
||||
// StrategyStatus enumerates whether a strategy is working on a cluster.
|
||||
// +kubebuilder:validation:Enum=Success;Error
|
||||
type StrategyStatus string
|
||||
|
||||
// StrategyReason enumerates the detailed reason why a strategy is in a particular status.
|
||||
// +kubebuilder:validation:Enum=Listening;Pending;Disabled;ErrorDuringSetup;CouldNotFetchKey;CouldNotGetClusterInfo;FetchedKey
|
||||
type StrategyReason string
|
||||
|
||||
@ -36,7 +42,91 @@ const (
|
||||
FetchedKeyStrategyReason = StrategyReason("FetchedKey")
|
||||
)
|
||||
|
||||
// Status of a credential issuer.
|
||||
// CredentialIssuerSpec describes the intended configuration of the Concierge.
|
||||
type CredentialIssuerSpec struct {
|
||||
// ImpersonationProxy describes the intended configuration of the Concierge impersonation proxy.
|
||||
ImpersonationProxy *ImpersonationProxySpec `json:"impersonationProxy"`
|
||||
}
|
||||
|
||||
// ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy.
|
||||
//
|
||||
// +kubebuilder:validation:Enum=auto;enabled;disabled
|
||||
type ImpersonationProxyMode string
|
||||
|
||||
const (
|
||||
// ImpersonationProxyModeDisabled explicitly disables the impersonation proxy.
|
||||
ImpersonationProxyModeDisabled = ImpersonationProxyMode("disabled")
|
||||
|
||||
// ImpersonationProxyModeEnabled explicitly enables the impersonation proxy.
|
||||
ImpersonationProxyModeEnabled = ImpersonationProxyMode("enabled")
|
||||
|
||||
// ImpersonationProxyModeAuto enables or disables the impersonation proxy based upon the cluster in which it is running.
|
||||
ImpersonationProxyModeAuto = ImpersonationProxyMode("auto")
|
||||
)
|
||||
|
||||
// ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy.
|
||||
//
|
||||
// +kubebuilder:validation:Enum=LoadBalancer;ClusterIP;None
|
||||
type ImpersonationProxyServiceType string
|
||||
|
||||
const (
|
||||
// ImpersonationProxyServiceTypeLoadBalancer provisions a service of type LoadBalancer.
|
||||
ImpersonationProxyServiceTypeLoadBalancer = ImpersonationProxyServiceType("LoadBalancer")
|
||||
|
||||
// ImpersonationProxyServiceTypeClusterIP provisions a service of type ClusterIP.
|
||||
ImpersonationProxyServiceTypeClusterIP = ImpersonationProxyServiceType("ClusterIP")
|
||||
|
||||
// ImpersonationProxyServiceTypeNone does not automatically provision any service.
|
||||
ImpersonationProxyServiceTypeNone = ImpersonationProxyServiceType("None")
|
||||
)
|
||||
|
||||
// ImpersonationProxySpec describes the intended configuration of the Concierge impersonation proxy.
|
||||
type ImpersonationProxySpec struct {
|
||||
// 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.
|
||||
Mode ImpersonationProxyMode `json:"mode"`
|
||||
|
||||
// Service describes the configuration of the Service provisioned to expose the impersonation proxy to clients.
|
||||
//
|
||||
// +kubebuilder:default:={"type": "LoadBalancer"}
|
||||
Service ImpersonationProxyServiceSpec `json:"service"`
|
||||
|
||||
// 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.
|
||||
//
|
||||
// This field must be non-empty when spec.impersonationProxy.service.mode is "None".
|
||||
//
|
||||
// +optional
|
||||
ExternalEndpoint string `json:"externalEndpoint,omitempty"`
|
||||
}
|
||||
|
||||
// ImpersonationProxyServiceSpec describes how the Concierge should provision a Service to expose the impersonation proxy.
|
||||
type ImpersonationProxyServiceSpec struct {
|
||||
// Type specifies the type of Service to provision for the impersonation proxy.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// +kubebuilder:default:="LoadBalancer"
|
||||
Type ImpersonationProxyServiceType `json:"type,omitempty"`
|
||||
|
||||
// LoadBalancerIP specifies the IP address to set in the spec.loadBalancerIP field of the provisioned Service.
|
||||
// This is not supported on all cloud providers.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=255
|
||||
// +optional
|
||||
LoadBalancerIP string `json:"loadBalancerIP,omitempty"`
|
||||
|
||||
// Annotations specifies zero or more key/value pairs to set as annotations on the provisioned Service.
|
||||
//
|
||||
// +optional
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
}
|
||||
|
||||
// CredentialIssuerStatus describes the status of the Concierge.
|
||||
type CredentialIssuerStatus struct {
|
||||
// List of integration strategies that were attempted by Pinniped.
|
||||
Strategies []CredentialIssuerStrategy `json:"strategies"`
|
||||
@ -47,7 +137,8 @@ type CredentialIssuerStatus struct {
|
||||
KubeConfigInfo *CredentialIssuerKubeConfigInfo `json:"kubeConfigInfo,omitempty"`
|
||||
}
|
||||
|
||||
// Information needed to form a valid Pinniped-based kubeconfig using this credential issuer.
|
||||
// CredentialIssuerKubeConfigInfo provides the information needed to form a valid Pinniped-based kubeconfig using this credential issuer.
|
||||
// This type is deprecated and will be removed in a future version.
|
||||
type CredentialIssuerKubeConfigInfo struct {
|
||||
// The K8s API server URL.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
@ -59,7 +150,7 @@ type CredentialIssuerKubeConfigInfo struct {
|
||||
CertificateAuthorityData string `json:"certificateAuthorityData"`
|
||||
}
|
||||
|
||||
// Status of an integration strategy that was attempted by Pinniped.
|
||||
// CredentialIssuerStrategy describes the status of an integration strategy that was attempted by Pinniped.
|
||||
type CredentialIssuerStrategy struct {
|
||||
// Type of integration attempted.
|
||||
Type StrategyType `json:"type"`
|
||||
@ -81,6 +172,7 @@ type CredentialIssuerStrategy struct {
|
||||
Frontend *CredentialIssuerFrontend `json:"frontend,omitempty"`
|
||||
}
|
||||
|
||||
// CredentialIssuerFrontend describes how to connect using a particular integration strategy.
|
||||
type CredentialIssuerFrontend struct {
|
||||
// Type describes which frontend mechanism clients can use with a strategy.
|
||||
Type FrontendType `json:"type"`
|
||||
@ -118,7 +210,7 @@ type ImpersonationProxyInfo struct {
|
||||
CertificateAuthorityData string `json:"certificateAuthorityData"`
|
||||
}
|
||||
|
||||
// Describes the configuration status of a Pinniped credential issuer.
|
||||
// CredentialIssuer describes the configuration and status of the Pinniped Concierge credential issuer.
|
||||
// +genclient
|
||||
// +genclient:nonNamespaced
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
@ -128,12 +220,18 @@ type CredentialIssuer struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Status of the credential issuer.
|
||||
// Spec describes the intended configuration of the Concierge.
|
||||
//
|
||||
// +optional
|
||||
Spec CredentialIssuerSpec `json:"spec"`
|
||||
|
||||
// CredentialIssuerStatus describes the status of the Concierge.
|
||||
//
|
||||
// +optional
|
||||
Status CredentialIssuerStatus `json:"status"`
|
||||
}
|
||||
|
||||
// List of CredentialIssuer objects.
|
||||
// CredentialIssuerList is a list of CredentialIssuer objects.
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type CredentialIssuerList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
@ -16,6 +16,7 @@ func (in *CredentialIssuer) DeepCopyInto(out *CredentialIssuer) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
return
|
||||
}
|
||||
@ -113,6 +114,27 @@ func (in *CredentialIssuerList) DeepCopyObject() runtime.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CredentialIssuerSpec) DeepCopyInto(out *CredentialIssuerSpec) {
|
||||
*out = *in
|
||||
if in.ImpersonationProxy != nil {
|
||||
in, out := &in.ImpersonationProxy, &out.ImpersonationProxy
|
||||
*out = new(ImpersonationProxySpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CredentialIssuerSpec.
|
||||
func (in *CredentialIssuerSpec) DeepCopy() *CredentialIssuerSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CredentialIssuerSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CredentialIssuerStatus) DeepCopyInto(out *CredentialIssuerStatus) {
|
||||
*out = *in
|
||||
@ -179,6 +201,46 @@ func (in *ImpersonationProxyInfo) DeepCopy() *ImpersonationProxyInfo {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ImpersonationProxyServiceSpec) DeepCopyInto(out *ImpersonationProxyServiceSpec) {
|
||||
*out = *in
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImpersonationProxyServiceSpec.
|
||||
func (in *ImpersonationProxyServiceSpec) DeepCopy() *ImpersonationProxyServiceSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ImpersonationProxyServiceSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ImpersonationProxySpec) DeepCopyInto(out *ImpersonationProxySpec) {
|
||||
*out = *in
|
||||
in.Service.DeepCopyInto(&out.Service)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImpersonationProxySpec.
|
||||
func (in *ImpersonationProxySpec) DeepCopy() *ImpersonationProxySpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ImpersonationProxySpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TokenCredentialRequestAPIInfo) DeepCopyInto(out *TokenCredentialRequestAPIInfo) {
|
||||
*out = *in
|
||||
|
@ -68,8 +68,8 @@ type LDAPIdentityProviderGroupSearchAttributes struct {
|
||||
// GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name
|
||||
// in the user's list of groups after a successful authentication.
|
||||
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
||||
// server in the user's entry. Distinguished names can be used by specifying lower-case "dn".
|
||||
// Optional. When not specified, the default will act as if the GroupName were specified as "cn" (common name).
|
||||
// server in the user's entry. E.g. "cn" for common name. Distinguished names can be used by specifying lower-case "dn".
|
||||
// Optional. When not specified, the default will act as if the GroupName were specified as "dn" (distinguished name).
|
||||
// +optional
|
||||
GroupName string `json:"groupName,omitempty"`
|
||||
}
|
||||
|
@ -21,7 +21,8 @@ spec:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Describes the configuration status of a Pinniped credential issuer.
|
||||
description: CredentialIssuer describes the configuration and status of the
|
||||
Pinniped Concierge credential issuer.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
@ -35,8 +36,73 @@ spec:
|
||||
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.mode
|
||||
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
|
||||
required:
|
||||
- mode
|
||||
- service
|
||||
type: object
|
||||
required:
|
||||
- impersonationProxy
|
||||
type: object
|
||||
status:
|
||||
description: Status of the credential issuer.
|
||||
description: CredentialIssuerStatus describes the status of the Concierge.
|
||||
properties:
|
||||
kubeConfigInfo:
|
||||
description: Information needed to form a valid Pinniped-based kubeconfig
|
||||
@ -60,8 +126,8 @@ spec:
|
||||
description: List of integration strategies that were attempted by
|
||||
Pinniped.
|
||||
items:
|
||||
description: Status of an integration strategy that was attempted
|
||||
by Pinniped.
|
||||
description: CredentialIssuerStrategy describes the status of an
|
||||
integration strategy that was attempted by Pinniped.
|
||||
properties:
|
||||
frontend:
|
||||
description: Frontend describes how clients can connect using
|
||||
|
@ -86,10 +86,10 @@ spec:
|
||||
in the user's list of groups after a successful authentication.
|
||||
The value of this field is case-sensitive and must match
|
||||
the case of the attribute name returned by the LDAP server
|
||||
in the user's entry. Distinguished names can be used by
|
||||
specifying lower-case "dn". Optional. When not specified,
|
||||
the default will act as if the GroupName were specified
|
||||
as "cn" (common name).
|
||||
in the user's entry. E.g. "cn" for common name. Distinguished
|
||||
names can be used by specifying lower-case "dn". Optional.
|
||||
When not specified, the default will act as if the GroupName
|
||||
were specified as "dn" (distinguished name).
|
||||
type: string
|
||||
type: object
|
||||
base:
|
||||
|
90
generated/1.20/README.adoc
generated
90
generated/1.20/README.adoc
generated
@ -220,7 +220,7 @@ Package v1alpha1 is the v1alpha1 version of the Pinniped concierge configuration
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-concierge-config-v1alpha1-credentialissuer"]
|
||||
==== CredentialIssuer
|
||||
|
||||
Describes the configuration status of a Pinniped credential issuer.
|
||||
CredentialIssuer describes the configuration and status of the Pinniped Concierge credential issuer.
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
@ -232,7 +232,8 @@ Describes the configuration status of a Pinniped credential issuer.
|
||||
| Field | Description
|
||||
| *`metadata`* __link:https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.2/#objectmeta-v1-meta[$$ObjectMeta$$]__ | Refer to Kubernetes API documentation for fields of `metadata`.
|
||||
|
||||
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-concierge-config-v1alpha1-credentialissuerstatus[$$CredentialIssuerStatus$$]__ | Status of the credential issuer.
|
||||
| *`spec`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-concierge-config-v1alpha1-credentialissuerspec[$$CredentialIssuerSpec$$]__ | Spec describes the intended configuration of the Concierge.
|
||||
| *`status`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-concierge-config-v1alpha1-credentialissuerstatus[$$CredentialIssuerStatus$$]__ | CredentialIssuerStatus describes the status of the Concierge.
|
||||
|===
|
||||
|
||||
|
||||
@ -275,10 +276,27 @@ Describes the configuration status of a Pinniped credential issuer.
|
||||
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-concierge-config-v1alpha1-credentialissuerspec"]
|
||||
==== CredentialIssuerSpec
|
||||
|
||||
CredentialIssuerSpec describes the intended configuration of the Concierge.
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-concierge-config-v1alpha1-credentialissuer[$$CredentialIssuer$$]
|
||||
****
|
||||
|
||||
[cols="25a,75a", options="header"]
|
||||
|===
|
||||
| Field | Description
|
||||
| *`impersonationProxy`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-concierge-config-v1alpha1-impersonationproxyspec[$$ImpersonationProxySpec$$]__ | ImpersonationProxy describes the intended configuration of the Concierge impersonation proxy.
|
||||
|===
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-concierge-config-v1alpha1-credentialissuerstatus"]
|
||||
==== CredentialIssuerStatus
|
||||
|
||||
Status of a credential issuer.
|
||||
CredentialIssuerStatus describes the status of the Concierge.
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
@ -333,6 +351,70 @@ Status of a credential issuer.
|
||||
|===
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-concierge-config-v1alpha1-impersonationproxymode"]
|
||||
==== ImpersonationProxyMode (string)
|
||||
|
||||
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-concierge-config-v1alpha1-impersonationproxyspec[$$ImpersonationProxySpec$$]
|
||||
****
|
||||
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-concierge-config-v1alpha1-impersonationproxyservicespec"]
|
||||
==== ImpersonationProxyServiceSpec
|
||||
|
||||
ImpersonationProxyServiceSpec describes how the Concierge should provision a Service to expose the impersonation proxy.
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-concierge-config-v1alpha1-impersonationproxyspec[$$ImpersonationProxySpec$$]
|
||||
****
|
||||
|
||||
[cols="25a,75a", options="header"]
|
||||
|===
|
||||
| Field | Description
|
||||
| *`type`* __ImpersonationProxyServiceType__ | Type specifies the type of Service to provision for the impersonation proxy.
|
||||
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.
|
||||
| *`loadBalancerIP`* __string__ | LoadBalancerIP specifies the IP address to set in the spec.loadBalancerIP field of the provisioned Service. This is not supported on all cloud providers.
|
||||
| *`annotations`* __object (keys:string, values:string)__ | Annotations specifies zero or more key/value pairs to set as annotations on the provisioned Service.
|
||||
|===
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-concierge-config-v1alpha1-impersonationproxyservicetype"]
|
||||
==== ImpersonationProxyServiceType (string)
|
||||
|
||||
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-concierge-config-v1alpha1-impersonationproxyservicespec[$$ImpersonationProxyServiceSpec$$]
|
||||
****
|
||||
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-concierge-config-v1alpha1-impersonationproxyspec"]
|
||||
==== ImpersonationProxySpec
|
||||
|
||||
|
||||
|
||||
.Appears In:
|
||||
****
|
||||
- xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-concierge-config-v1alpha1-credentialissuerspec[$$CredentialIssuerSpec$$]
|
||||
****
|
||||
|
||||
[cols="25a,75a", options="header"]
|
||||
|===
|
||||
| Field | Description
|
||||
| *`mode`* __ImpersonationProxyMode__ | 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.
|
||||
| *`service`* __xref:{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-concierge-config-v1alpha1-impersonationproxyservicespec[$$ImpersonationProxyServiceSpec$$]__ | Service describes the configuration of the Service provisioned to expose the impersonation proxy to clients.
|
||||
| *`externalEndpoint`* __string__ | 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.
|
||||
This field must be non-empty when spec.impersonationProxy.service.mode is "None".
|
||||
|===
|
||||
|
||||
|
||||
[id="{anchor_prefix}-go-pinniped-dev-generated-1-20-apis-concierge-config-v1alpha1-tokencredentialrequestapiinfo"]
|
||||
==== TokenCredentialRequestAPIInfo
|
||||
|
||||
@ -770,7 +852,7 @@ LDAPIdentityProvider describes the configuration of an upstream Lightweight Dire
|
||||
[cols="25a,75a", options="header"]
|
||||
|===
|
||||
| Field | Description
|
||||
| *`groupName`* __string__ | GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name in the user's list of groups after a successful authentication. The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. Distinguished names can be used by specifying lower-case "dn". Optional. When not specified, the default will act as if the GroupName were specified as "cn" (common name).
|
||||
| *`groupName`* __string__ | GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name in the user's list of groups after a successful authentication. The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP server in the user's entry. E.g. "cn" for common name. Distinguished names can be used by specifying lower-case "dn". Optional. When not specified, the default will act as if the GroupName were specified as "dn" (distinguished name).
|
||||
|===
|
||||
|
||||
|
||||
|
@ -3,17 +3,23 @@
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// StrategyType enumerates a type of "strategy" used to implement credential access on a cluster.
|
||||
// +kubebuilder:validation:Enum=KubeClusterSigningCertificate;ImpersonationProxy
|
||||
type StrategyType string
|
||||
|
||||
// FrontendType enumerates a type of "frontend" used to provide access to users of a cluster.
|
||||
// +kubebuilder:validation:Enum=TokenCredentialRequestAPI;ImpersonationProxy
|
||||
type FrontendType string
|
||||
|
||||
// StrategyStatus enumerates whether a strategy is working on a cluster.
|
||||
// +kubebuilder:validation:Enum=Success;Error
|
||||
type StrategyStatus string
|
||||
|
||||
// StrategyReason enumerates the detailed reason why a strategy is in a particular status.
|
||||
// +kubebuilder:validation:Enum=Listening;Pending;Disabled;ErrorDuringSetup;CouldNotFetchKey;CouldNotGetClusterInfo;FetchedKey
|
||||
type StrategyReason string
|
||||
|
||||
@ -36,7 +42,91 @@ const (
|
||||
FetchedKeyStrategyReason = StrategyReason("FetchedKey")
|
||||
)
|
||||
|
||||
// Status of a credential issuer.
|
||||
// CredentialIssuerSpec describes the intended configuration of the Concierge.
|
||||
type CredentialIssuerSpec struct {
|
||||
// ImpersonationProxy describes the intended configuration of the Concierge impersonation proxy.
|
||||
ImpersonationProxy *ImpersonationProxySpec `json:"impersonationProxy"`
|
||||
}
|
||||
|
||||
// ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy.
|
||||
//
|
||||
// +kubebuilder:validation:Enum=auto;enabled;disabled
|
||||
type ImpersonationProxyMode string
|
||||
|
||||
const (
|
||||
// ImpersonationProxyModeDisabled explicitly disables the impersonation proxy.
|
||||
ImpersonationProxyModeDisabled = ImpersonationProxyMode("disabled")
|
||||
|
||||
// ImpersonationProxyModeEnabled explicitly enables the impersonation proxy.
|
||||
ImpersonationProxyModeEnabled = ImpersonationProxyMode("enabled")
|
||||
|
||||
// ImpersonationProxyModeAuto enables or disables the impersonation proxy based upon the cluster in which it is running.
|
||||
ImpersonationProxyModeAuto = ImpersonationProxyMode("auto")
|
||||
)
|
||||
|
||||
// ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy.
|
||||
//
|
||||
// +kubebuilder:validation:Enum=LoadBalancer;ClusterIP;None
|
||||
type ImpersonationProxyServiceType string
|
||||
|
||||
const (
|
||||
// ImpersonationProxyServiceTypeLoadBalancer provisions a service of type LoadBalancer.
|
||||
ImpersonationProxyServiceTypeLoadBalancer = ImpersonationProxyServiceType("LoadBalancer")
|
||||
|
||||
// ImpersonationProxyServiceTypeClusterIP provisions a service of type ClusterIP.
|
||||
ImpersonationProxyServiceTypeClusterIP = ImpersonationProxyServiceType("ClusterIP")
|
||||
|
||||
// ImpersonationProxyServiceTypeNone does not automatically provision any service.
|
||||
ImpersonationProxyServiceTypeNone = ImpersonationProxyServiceType("None")
|
||||
)
|
||||
|
||||
// ImpersonationProxySpec describes the intended configuration of the Concierge impersonation proxy.
|
||||
type ImpersonationProxySpec struct {
|
||||
// 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.
|
||||
Mode ImpersonationProxyMode `json:"mode"`
|
||||
|
||||
// Service describes the configuration of the Service provisioned to expose the impersonation proxy to clients.
|
||||
//
|
||||
// +kubebuilder:default:={"type": "LoadBalancer"}
|
||||
Service ImpersonationProxyServiceSpec `json:"service"`
|
||||
|
||||
// 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.
|
||||
//
|
||||
// This field must be non-empty when spec.impersonationProxy.service.mode is "None".
|
||||
//
|
||||
// +optional
|
||||
ExternalEndpoint string `json:"externalEndpoint,omitempty"`
|
||||
}
|
||||
|
||||
// ImpersonationProxyServiceSpec describes how the Concierge should provision a Service to expose the impersonation proxy.
|
||||
type ImpersonationProxyServiceSpec struct {
|
||||
// Type specifies the type of Service to provision for the impersonation proxy.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// +kubebuilder:default:="LoadBalancer"
|
||||
Type ImpersonationProxyServiceType `json:"type,omitempty"`
|
||||
|
||||
// LoadBalancerIP specifies the IP address to set in the spec.loadBalancerIP field of the provisioned Service.
|
||||
// This is not supported on all cloud providers.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=255
|
||||
// +optional
|
||||
LoadBalancerIP string `json:"loadBalancerIP,omitempty"`
|
||||
|
||||
// Annotations specifies zero or more key/value pairs to set as annotations on the provisioned Service.
|
||||
//
|
||||
// +optional
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
}
|
||||
|
||||
// CredentialIssuerStatus describes the status of the Concierge.
|
||||
type CredentialIssuerStatus struct {
|
||||
// List of integration strategies that were attempted by Pinniped.
|
||||
Strategies []CredentialIssuerStrategy `json:"strategies"`
|
||||
@ -47,7 +137,8 @@ type CredentialIssuerStatus struct {
|
||||
KubeConfigInfo *CredentialIssuerKubeConfigInfo `json:"kubeConfigInfo,omitempty"`
|
||||
}
|
||||
|
||||
// Information needed to form a valid Pinniped-based kubeconfig using this credential issuer.
|
||||
// CredentialIssuerKubeConfigInfo provides the information needed to form a valid Pinniped-based kubeconfig using this credential issuer.
|
||||
// This type is deprecated and will be removed in a future version.
|
||||
type CredentialIssuerKubeConfigInfo struct {
|
||||
// The K8s API server URL.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
@ -59,7 +150,7 @@ type CredentialIssuerKubeConfigInfo struct {
|
||||
CertificateAuthorityData string `json:"certificateAuthorityData"`
|
||||
}
|
||||
|
||||
// Status of an integration strategy that was attempted by Pinniped.
|
||||
// CredentialIssuerStrategy describes the status of an integration strategy that was attempted by Pinniped.
|
||||
type CredentialIssuerStrategy struct {
|
||||
// Type of integration attempted.
|
||||
Type StrategyType `json:"type"`
|
||||
@ -81,6 +172,7 @@ type CredentialIssuerStrategy struct {
|
||||
Frontend *CredentialIssuerFrontend `json:"frontend,omitempty"`
|
||||
}
|
||||
|
||||
// CredentialIssuerFrontend describes how to connect using a particular integration strategy.
|
||||
type CredentialIssuerFrontend struct {
|
||||
// Type describes which frontend mechanism clients can use with a strategy.
|
||||
Type FrontendType `json:"type"`
|
||||
@ -118,7 +210,7 @@ type ImpersonationProxyInfo struct {
|
||||
CertificateAuthorityData string `json:"certificateAuthorityData"`
|
||||
}
|
||||
|
||||
// Describes the configuration status of a Pinniped credential issuer.
|
||||
// CredentialIssuer describes the configuration and status of the Pinniped Concierge credential issuer.
|
||||
// +genclient
|
||||
// +genclient:nonNamespaced
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
@ -128,12 +220,18 @@ type CredentialIssuer struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Status of the credential issuer.
|
||||
// Spec describes the intended configuration of the Concierge.
|
||||
//
|
||||
// +optional
|
||||
Spec CredentialIssuerSpec `json:"spec"`
|
||||
|
||||
// CredentialIssuerStatus describes the status of the Concierge.
|
||||
//
|
||||
// +optional
|
||||
Status CredentialIssuerStatus `json:"status"`
|
||||
}
|
||||
|
||||
// List of CredentialIssuer objects.
|
||||
// CredentialIssuerList is a list of CredentialIssuer objects.
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type CredentialIssuerList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
@ -16,6 +16,7 @@ func (in *CredentialIssuer) DeepCopyInto(out *CredentialIssuer) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
return
|
||||
}
|
||||
@ -113,6 +114,27 @@ func (in *CredentialIssuerList) DeepCopyObject() runtime.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CredentialIssuerSpec) DeepCopyInto(out *CredentialIssuerSpec) {
|
||||
*out = *in
|
||||
if in.ImpersonationProxy != nil {
|
||||
in, out := &in.ImpersonationProxy, &out.ImpersonationProxy
|
||||
*out = new(ImpersonationProxySpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CredentialIssuerSpec.
|
||||
func (in *CredentialIssuerSpec) DeepCopy() *CredentialIssuerSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CredentialIssuerSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CredentialIssuerStatus) DeepCopyInto(out *CredentialIssuerStatus) {
|
||||
*out = *in
|
||||
@ -179,6 +201,46 @@ func (in *ImpersonationProxyInfo) DeepCopy() *ImpersonationProxyInfo {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ImpersonationProxyServiceSpec) DeepCopyInto(out *ImpersonationProxyServiceSpec) {
|
||||
*out = *in
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImpersonationProxyServiceSpec.
|
||||
func (in *ImpersonationProxyServiceSpec) DeepCopy() *ImpersonationProxyServiceSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ImpersonationProxyServiceSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ImpersonationProxySpec) DeepCopyInto(out *ImpersonationProxySpec) {
|
||||
*out = *in
|
||||
in.Service.DeepCopyInto(&out.Service)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImpersonationProxySpec.
|
||||
func (in *ImpersonationProxySpec) DeepCopy() *ImpersonationProxySpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ImpersonationProxySpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TokenCredentialRequestAPIInfo) DeepCopyInto(out *TokenCredentialRequestAPIInfo) {
|
||||
*out = *in
|
||||
|
@ -68,8 +68,8 @@ type LDAPIdentityProviderGroupSearchAttributes struct {
|
||||
// GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name
|
||||
// in the user's list of groups after a successful authentication.
|
||||
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
||||
// server in the user's entry. Distinguished names can be used by specifying lower-case "dn".
|
||||
// Optional. When not specified, the default will act as if the GroupName were specified as "cn" (common name).
|
||||
// server in the user's entry. E.g. "cn" for common name. Distinguished names can be used by specifying lower-case "dn".
|
||||
// Optional. When not specified, the default will act as if the GroupName were specified as "dn" (distinguished name).
|
||||
// +optional
|
||||
GroupName string `json:"groupName,omitempty"`
|
||||
}
|
||||
|
@ -21,7 +21,8 @@ spec:
|
||||
- name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: Describes the configuration status of a Pinniped credential issuer.
|
||||
description: CredentialIssuer describes the configuration and status of the
|
||||
Pinniped Concierge credential issuer.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
@ -35,8 +36,73 @@ spec:
|
||||
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.mode
|
||||
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
|
||||
required:
|
||||
- mode
|
||||
- service
|
||||
type: object
|
||||
required:
|
||||
- impersonationProxy
|
||||
type: object
|
||||
status:
|
||||
description: Status of the credential issuer.
|
||||
description: CredentialIssuerStatus describes the status of the Concierge.
|
||||
properties:
|
||||
kubeConfigInfo:
|
||||
description: Information needed to form a valid Pinniped-based kubeconfig
|
||||
@ -60,8 +126,8 @@ spec:
|
||||
description: List of integration strategies that were attempted by
|
||||
Pinniped.
|
||||
items:
|
||||
description: Status of an integration strategy that was attempted
|
||||
by Pinniped.
|
||||
description: CredentialIssuerStrategy describes the status of an
|
||||
integration strategy that was attempted by Pinniped.
|
||||
properties:
|
||||
frontend:
|
||||
description: Frontend describes how clients can connect using
|
||||
|
@ -86,10 +86,10 @@ spec:
|
||||
in the user's list of groups after a successful authentication.
|
||||
The value of this field is case-sensitive and must match
|
||||
the case of the attribute name returned by the LDAP server
|
||||
in the user's entry. Distinguished names can be used by
|
||||
specifying lower-case "dn". Optional. When not specified,
|
||||
the default will act as if the GroupName were specified
|
||||
as "cn" (common name).
|
||||
in the user's entry. E.g. "cn" for common name. Distinguished
|
||||
names can be used by specifying lower-case "dn". Optional.
|
||||
When not specified, the default will act as if the GroupName
|
||||
were specified as "dn" (distinguished name).
|
||||
type: string
|
||||
type: object
|
||||
base:
|
||||
|
@ -3,17 +3,23 @@
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// StrategyType enumerates a type of "strategy" used to implement credential access on a cluster.
|
||||
// +kubebuilder:validation:Enum=KubeClusterSigningCertificate;ImpersonationProxy
|
||||
type StrategyType string
|
||||
|
||||
// FrontendType enumerates a type of "frontend" used to provide access to users of a cluster.
|
||||
// +kubebuilder:validation:Enum=TokenCredentialRequestAPI;ImpersonationProxy
|
||||
type FrontendType string
|
||||
|
||||
// StrategyStatus enumerates whether a strategy is working on a cluster.
|
||||
// +kubebuilder:validation:Enum=Success;Error
|
||||
type StrategyStatus string
|
||||
|
||||
// StrategyReason enumerates the detailed reason why a strategy is in a particular status.
|
||||
// +kubebuilder:validation:Enum=Listening;Pending;Disabled;ErrorDuringSetup;CouldNotFetchKey;CouldNotGetClusterInfo;FetchedKey
|
||||
type StrategyReason string
|
||||
|
||||
@ -36,7 +42,91 @@ const (
|
||||
FetchedKeyStrategyReason = StrategyReason("FetchedKey")
|
||||
)
|
||||
|
||||
// Status of a credential issuer.
|
||||
// CredentialIssuerSpec describes the intended configuration of the Concierge.
|
||||
type CredentialIssuerSpec struct {
|
||||
// ImpersonationProxy describes the intended configuration of the Concierge impersonation proxy.
|
||||
ImpersonationProxy *ImpersonationProxySpec `json:"impersonationProxy"`
|
||||
}
|
||||
|
||||
// ImpersonationProxyMode enumerates the configuration modes for the impersonation proxy.
|
||||
//
|
||||
// +kubebuilder:validation:Enum=auto;enabled;disabled
|
||||
type ImpersonationProxyMode string
|
||||
|
||||
const (
|
||||
// ImpersonationProxyModeDisabled explicitly disables the impersonation proxy.
|
||||
ImpersonationProxyModeDisabled = ImpersonationProxyMode("disabled")
|
||||
|
||||
// ImpersonationProxyModeEnabled explicitly enables the impersonation proxy.
|
||||
ImpersonationProxyModeEnabled = ImpersonationProxyMode("enabled")
|
||||
|
||||
// ImpersonationProxyModeAuto enables or disables the impersonation proxy based upon the cluster in which it is running.
|
||||
ImpersonationProxyModeAuto = ImpersonationProxyMode("auto")
|
||||
)
|
||||
|
||||
// ImpersonationProxyServiceType enumerates the types of service that can be provisioned for the impersonation proxy.
|
||||
//
|
||||
// +kubebuilder:validation:Enum=LoadBalancer;ClusterIP;None
|
||||
type ImpersonationProxyServiceType string
|
||||
|
||||
const (
|
||||
// ImpersonationProxyServiceTypeLoadBalancer provisions a service of type LoadBalancer.
|
||||
ImpersonationProxyServiceTypeLoadBalancer = ImpersonationProxyServiceType("LoadBalancer")
|
||||
|
||||
// ImpersonationProxyServiceTypeClusterIP provisions a service of type ClusterIP.
|
||||
ImpersonationProxyServiceTypeClusterIP = ImpersonationProxyServiceType("ClusterIP")
|
||||
|
||||
// ImpersonationProxyServiceTypeNone does not automatically provision any service.
|
||||
ImpersonationProxyServiceTypeNone = ImpersonationProxyServiceType("None")
|
||||
)
|
||||
|
||||
// ImpersonationProxySpec describes the intended configuration of the Concierge impersonation proxy.
|
||||
type ImpersonationProxySpec struct {
|
||||
// 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.
|
||||
Mode ImpersonationProxyMode `json:"mode"`
|
||||
|
||||
// Service describes the configuration of the Service provisioned to expose the impersonation proxy to clients.
|
||||
//
|
||||
// +kubebuilder:default:={"type": "LoadBalancer"}
|
||||
Service ImpersonationProxyServiceSpec `json:"service"`
|
||||
|
||||
// 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.
|
||||
//
|
||||
// This field must be non-empty when spec.impersonationProxy.service.mode is "None".
|
||||
//
|
||||
// +optional
|
||||
ExternalEndpoint string `json:"externalEndpoint,omitempty"`
|
||||
}
|
||||
|
||||
// ImpersonationProxyServiceSpec describes how the Concierge should provision a Service to expose the impersonation proxy.
|
||||
type ImpersonationProxyServiceSpec struct {
|
||||
// Type specifies the type of Service to provision for the impersonation proxy.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// +kubebuilder:default:="LoadBalancer"
|
||||
Type ImpersonationProxyServiceType `json:"type,omitempty"`
|
||||
|
||||
// LoadBalancerIP specifies the IP address to set in the spec.loadBalancerIP field of the provisioned Service.
|
||||
// This is not supported on all cloud providers.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=255
|
||||
// +optional
|
||||
LoadBalancerIP string `json:"loadBalancerIP,omitempty"`
|
||||
|
||||
// Annotations specifies zero or more key/value pairs to set as annotations on the provisioned Service.
|
||||
//
|
||||
// +optional
|
||||
Annotations map[string]string `json:"annotations,omitempty"`
|
||||
}
|
||||
|
||||
// CredentialIssuerStatus describes the status of the Concierge.
|
||||
type CredentialIssuerStatus struct {
|
||||
// List of integration strategies that were attempted by Pinniped.
|
||||
Strategies []CredentialIssuerStrategy `json:"strategies"`
|
||||
@ -47,7 +137,8 @@ type CredentialIssuerStatus struct {
|
||||
KubeConfigInfo *CredentialIssuerKubeConfigInfo `json:"kubeConfigInfo,omitempty"`
|
||||
}
|
||||
|
||||
// Information needed to form a valid Pinniped-based kubeconfig using this credential issuer.
|
||||
// CredentialIssuerKubeConfigInfo provides the information needed to form a valid Pinniped-based kubeconfig using this credential issuer.
|
||||
// This type is deprecated and will be removed in a future version.
|
||||
type CredentialIssuerKubeConfigInfo struct {
|
||||
// The K8s API server URL.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
@ -59,7 +150,7 @@ type CredentialIssuerKubeConfigInfo struct {
|
||||
CertificateAuthorityData string `json:"certificateAuthorityData"`
|
||||
}
|
||||
|
||||
// Status of an integration strategy that was attempted by Pinniped.
|
||||
// CredentialIssuerStrategy describes the status of an integration strategy that was attempted by Pinniped.
|
||||
type CredentialIssuerStrategy struct {
|
||||
// Type of integration attempted.
|
||||
Type StrategyType `json:"type"`
|
||||
@ -81,6 +172,7 @@ type CredentialIssuerStrategy struct {
|
||||
Frontend *CredentialIssuerFrontend `json:"frontend,omitempty"`
|
||||
}
|
||||
|
||||
// CredentialIssuerFrontend describes how to connect using a particular integration strategy.
|
||||
type CredentialIssuerFrontend struct {
|
||||
// Type describes which frontend mechanism clients can use with a strategy.
|
||||
Type FrontendType `json:"type"`
|
||||
@ -118,7 +210,7 @@ type ImpersonationProxyInfo struct {
|
||||
CertificateAuthorityData string `json:"certificateAuthorityData"`
|
||||
}
|
||||
|
||||
// Describes the configuration status of a Pinniped credential issuer.
|
||||
// CredentialIssuer describes the configuration and status of the Pinniped Concierge credential issuer.
|
||||
// +genclient
|
||||
// +genclient:nonNamespaced
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
@ -128,12 +220,18 @@ type CredentialIssuer struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Status of the credential issuer.
|
||||
// Spec describes the intended configuration of the Concierge.
|
||||
//
|
||||
// +optional
|
||||
Spec CredentialIssuerSpec `json:"spec"`
|
||||
|
||||
// CredentialIssuerStatus describes the status of the Concierge.
|
||||
//
|
||||
// +optional
|
||||
Status CredentialIssuerStatus `json:"status"`
|
||||
}
|
||||
|
||||
// List of CredentialIssuer objects.
|
||||
// CredentialIssuerList is a list of CredentialIssuer objects.
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type CredentialIssuerList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
@ -16,6 +16,7 @@ func (in *CredentialIssuer) DeepCopyInto(out *CredentialIssuer) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
return
|
||||
}
|
||||
@ -113,6 +114,27 @@ func (in *CredentialIssuerList) DeepCopyObject() runtime.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CredentialIssuerSpec) DeepCopyInto(out *CredentialIssuerSpec) {
|
||||
*out = *in
|
||||
if in.ImpersonationProxy != nil {
|
||||
in, out := &in.ImpersonationProxy, &out.ImpersonationProxy
|
||||
*out = new(ImpersonationProxySpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CredentialIssuerSpec.
|
||||
func (in *CredentialIssuerSpec) DeepCopy() *CredentialIssuerSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CredentialIssuerSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CredentialIssuerStatus) DeepCopyInto(out *CredentialIssuerStatus) {
|
||||
*out = *in
|
||||
@ -179,6 +201,46 @@ func (in *ImpersonationProxyInfo) DeepCopy() *ImpersonationProxyInfo {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ImpersonationProxyServiceSpec) DeepCopyInto(out *ImpersonationProxyServiceSpec) {
|
||||
*out = *in
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImpersonationProxyServiceSpec.
|
||||
func (in *ImpersonationProxyServiceSpec) DeepCopy() *ImpersonationProxyServiceSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ImpersonationProxyServiceSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ImpersonationProxySpec) DeepCopyInto(out *ImpersonationProxySpec) {
|
||||
*out = *in
|
||||
in.Service.DeepCopyInto(&out.Service)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImpersonationProxySpec.
|
||||
func (in *ImpersonationProxySpec) DeepCopy() *ImpersonationProxySpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ImpersonationProxySpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *TokenCredentialRequestAPIInfo) DeepCopyInto(out *TokenCredentialRequestAPIInfo) {
|
||||
*out = *in
|
||||
|
@ -68,8 +68,8 @@ type LDAPIdentityProviderGroupSearchAttributes struct {
|
||||
// GroupName specifies the name of the attribute in the LDAP entries whose value shall become a group name
|
||||
// in the user's list of groups after a successful authentication.
|
||||
// The value of this field is case-sensitive and must match the case of the attribute name returned by the LDAP
|
||||
// server in the user's entry. Distinguished names can be used by specifying lower-case "dn".
|
||||
// Optional. When not specified, the default will act as if the GroupName were specified as "cn" (common name).
|
||||
// server in the user's entry. E.g. "cn" for common name. Distinguished names can be used by specifying lower-case "dn".
|
||||
// Optional. When not specified, the default will act as if the GroupName were specified as "dn" (distinguished name).
|
||||
// +optional
|
||||
GroupName string `json:"groupName,omitempty"`
|
||||
}
|
||||
|
12
go.mod
12
go.mod
@ -17,9 +17,8 @@ require (
|
||||
github.com/google/gofuzz v1.2.0
|
||||
github.com/gorilla/securecookie v1.1.1
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/oleiade/reflections v1.0.1 // indirect
|
||||
github.com/onsi/ginkgo v1.13.0 // indirect
|
||||
github.com/ory/fosite v0.39.0
|
||||
github.com/ory/fosite v0.40.2
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/sclevine/agouti v3.0.0+incompatible
|
||||
@ -39,7 +38,7 @@ require (
|
||||
k8s.io/client-go v0.21.1
|
||||
k8s.io/component-base v0.21.1
|
||||
k8s.io/gengo v0.0.0-20210203185629-de9496dff47b
|
||||
k8s.io/klog/v2 v2.8.0
|
||||
k8s.io/klog/v2 v2.9.0
|
||||
k8s.io/kube-aggregator v0.21.1
|
||||
k8s.io/utils v0.0.0-20210521133846-da695404a2bc
|
||||
sigs.k8s.io/yaml v1.2.0
|
||||
@ -56,3 +55,10 @@ replace github.com/oleiade/reflections v1.0.0 => github.com/oleiade/reflections
|
||||
// We use the SHA of github.com/form3tech-oss/jwt-go@v3.2.2 to get around "used for two different module paths"
|
||||
// https://golang.org/issues/26904
|
||||
replace github.com/dgrijalva/jwt-go v3.2.0+incompatible => github.com/form3tech-oss/jwt-go v0.0.0-20200915135329-9162a5abdbc0
|
||||
|
||||
// Pin gRPC back to v1.29.1 (the version required by Kubernetes), but also override a module that's only used in some tests.
|
||||
// This is required because sometime after v1.29.1, they moved this package into a separate module.
|
||||
replace (
|
||||
google.golang.org/grpc => google.golang.org/grpc v1.29.1
|
||||
google.golang.org/grpc/examples => ./hack/dependencyhacks/grpcexamples/
|
||||
)
|
||||
|
190
go.sum
190
go.sum
@ -1,5 +1,4 @@
|
||||
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.41.0/go.mod h1:OauMR7DV8fzvZIl2qg6rkaIhD/vmgk4iwEw/h6ercmg=
|
||||
@ -59,6 +58,7 @@ github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMo
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
@ -78,6 +78,7 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
||||
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
||||
github.com/aws/aws-sdk-go v1.23.19/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
@ -104,7 +105,7 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk=
|
||||
github.com/cockroachdb/cockroach-go v0.0.0-20190925194419-606b3d062051/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk=
|
||||
@ -113,6 +114,7 @@ github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa h1:OaNxuTZr
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0=
|
||||
github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/continuity v0.0.0-20200107194136-26c1120b8d41/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY=
|
||||
@ -154,6 +156,8 @@ github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70d
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v17.12.0-ce-rc1.0.20201201034508-7d75c1d40d88+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
@ -173,8 +177,7 @@ github.com/elazarl/goproxy v0.0.0-20181003060214-f58a169a71a5/go.mod h1:/Zj4wYkg
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
|
||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
|
||||
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
@ -192,6 +195,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-bindata/go-bindata v3.1.1+incompatible/go.mod h1:xK8Dsgwmeed+BBsSy2XTopBn/8uK2HWuGSnA11C3Joo=
|
||||
@ -210,26 +215,75 @@ github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
|
||||
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-logr/stdr v0.4.0 h1:ijk9G/xzDRZdMU1QRhLYdHuWvNZWqte+NZMOGsiKWbc=
|
||||
github.com/go-logr/stdr v0.4.0/go.mod h1:NO1vneyJDqKVgJYnxhwXWWmQPOvNM391IG3H8ql3jiA=
|
||||
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
|
||||
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
||||
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
||||
github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
|
||||
github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
|
||||
github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
|
||||
github.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ=
|
||||
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
|
||||
github.com/go-openapi/errors v0.19.3/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
|
||||
github.com/go-openapi/errors v0.19.6/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||
github.com/go-openapi/errors v0.20.0/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||
github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
|
||||
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
|
||||
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
|
||||
github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
|
||||
github.com/go-openapi/loads v0.19.3/go.mod h1:YVfqhUCdahYwR3f3iiwQLhicVRvLlU/WO5WPaZvcvSI=
|
||||
github.com/go-openapi/loads v0.19.5/go.mod h1:dswLCAdonkRufe/gSUC3gN8nTSaB9uaS2es0x5/IbjY=
|
||||
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
|
||||
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
|
||||
github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
|
||||
github.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo=
|
||||
github.com/go-openapi/runtime v0.19.26/go.mod h1:BvrQtn6iVb2QmiVXRsFAm6ZCAZBpbVKFfN6QWCp582M=
|
||||
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
|
||||
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
|
||||
github.com/go-openapi/spec v0.19.6/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
|
||||
github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
|
||||
github.com/go-openapi/spec v0.20.3 h1:uH9RQ6vdyPSs2pSy9fL8QPspDF2AMIMPtmK5coSSjtQ=
|
||||
github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg=
|
||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
|
||||
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
|
||||
github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
|
||||
github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
|
||||
github.com/go-openapi/strfmt v0.19.4/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
|
||||
github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=
|
||||
github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=
|
||||
github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=
|
||||
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
|
||||
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
|
||||
github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo=
|
||||
github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbNMAuKvKB+IaGx8=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
|
||||
github.com/gobuffalo/attrs v0.1.0/go.mod h1:fmNpaWyHM0tRm8gCZWKx8yY9fvaNLo2PyzBNSrBZ5Hw=
|
||||
github.com/gobuffalo/buffalo v0.12.8-0.20181004233540-fac9bb505aa8/go.mod h1:sLyT7/dceRXJUxSsE813JTQtA3Eb1vjxWfo/N//vXIY=
|
||||
github.com/gobuffalo/buffalo v0.13.0/go.mod h1:Mjn1Ba9wpIbpbrD+lIDMy99pQ0H0LiddMIIDGse7qT4=
|
||||
@ -249,6 +303,8 @@ github.com/gobuffalo/buffalo-plugins v1.10.0/go.mod h1:4osg8d9s60txLuGwXnqH+RCjP
|
||||
github.com/gobuffalo/buffalo-plugins v1.11.0/go.mod h1:rtIvAYRjYibgmWhnjKmo7OadtnxuMG5ZQLr25ozAzjg=
|
||||
github.com/gobuffalo/buffalo-plugins v1.15.0/go.mod h1:BqSx01nwgKUQr/MArXzFkSD0QvdJidiky1OKgyfgrK8=
|
||||
github.com/gobuffalo/buffalo-pop v1.0.5/go.mod h1:Fw/LfFDnSmB/vvQXPvcXEjzP98Tc+AudyNWUBWKCwQ8=
|
||||
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
|
||||
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
|
||||
github.com/gobuffalo/envy v1.6.4/go.mod h1:Abh+Jfw475/NWtYMEt+hnJWRiC8INKWibIMyNt1w2Mc=
|
||||
github.com/gobuffalo/envy v1.6.5/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ=
|
||||
github.com/gobuffalo/envy v1.6.6/go.mod h1:N+GkhhZ/93bGZc6ZKhJLP6+m+tCNPKwgSpH9kaifseQ=
|
||||
@ -276,6 +332,7 @@ github.com/gobuffalo/events v1.3.1/go.mod h1:9JOkQVoyRtailYVE/JJ2ZQ/6i4gTjM5t2Hs
|
||||
github.com/gobuffalo/events v1.4.1/go.mod h1:SjXgWKpeSuvQDvGhgMz5IXx3Czu+IbL+XPLR41NvVQY=
|
||||
github.com/gobuffalo/fizz v1.0.12/go.mod h1:C0sltPxpYK8Ftvf64kbsQa2yiCZY4RZviurNxXdAKwc=
|
||||
github.com/gobuffalo/fizz v1.9.8/go.mod h1:w1FEn1yKNVCc49KnADGyYGRPH7jFON3ak4Bj1yUudHo=
|
||||
github.com/gobuffalo/fizz v1.10.0/go.mod h1:J2XGPO0AfJ1zKw7+2BA+6FEGAkyEsdCOLvN93WCT2WI=
|
||||
github.com/gobuffalo/flect v0.0.0-20180907193754-dc14d8acaf9f/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA=
|
||||
github.com/gobuffalo/flect v0.0.0-20181002182613-4571df4b1daf/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA=
|
||||
github.com/gobuffalo/flect v0.0.0-20181007231023-ae7ed6bfe683/go.mod h1:rCiQgmAE4axgBNl3jZWzS5rETRYTGOsrixTRaCPzNdA=
|
||||
@ -287,6 +344,9 @@ github.com/gobuffalo/flect v0.0.0-20181114183036-47375f6d8328/go.mod h1:0HvNbHdf
|
||||
github.com/gobuffalo/flect v0.0.0-20181210151238-24a2b68e0316/go.mod h1:en58vff74S9b99Eg42Dr+/9yPu437QjlNsO/hBYPuOk=
|
||||
github.com/gobuffalo/flect v0.0.0-20190104192022-4af577e09bf2/go.mod h1:en58vff74S9b99Eg42Dr+/9yPu437QjlNsO/hBYPuOk=
|
||||
github.com/gobuffalo/flect v0.0.0-20190117212819-a62e61d96794/go.mod h1:397QT6v05LkZkn07oJXXT6y9FCfwC8Pug0WA2/2mE9k=
|
||||
github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
|
||||
github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
|
||||
github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
|
||||
github.com/gobuffalo/flect v0.1.5/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
|
||||
github.com/gobuffalo/flect v0.2.0/go.mod h1:W3K3X9ksuZfir8f/LrfVtWmCDQFfayuylOJ7sz/Fj80=
|
||||
github.com/gobuffalo/flect v0.2.1/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc=
|
||||
@ -313,6 +373,10 @@ github.com/gobuffalo/genny v0.0.0-20181207164119-84844398a37d/go.mod h1:y0ysCHGG
|
||||
github.com/gobuffalo/genny v0.0.0-20181211165820-e26c8466f14d/go.mod h1:sHnK+ZSU4e2feXP3PA29ouij6PUEiN+RCwECjCTB3yM=
|
||||
github.com/gobuffalo/genny v0.0.0-20190104222617-a71664fc38e7/go.mod h1:QPsQ1FnhEsiU8f+O0qKWXz2RE4TiDqLVChWkBuh1WaY=
|
||||
github.com/gobuffalo/genny v0.0.0-20190112155932-f31a84fcacf5/go.mod h1:CIaHCrSIuJ4il6ka3Hub4DR4adDrGoXGEEt2FbBxoIo=
|
||||
github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
|
||||
github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
|
||||
github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
|
||||
github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
|
||||
github.com/gobuffalo/genny v0.2.0/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
|
||||
github.com/gobuffalo/genny v0.3.0/go.mod h1:ywJ2CoXrTZj7rbS8HTbzv7uybnLKlsNSBhEQ+yFI3E8=
|
||||
github.com/gobuffalo/genny v0.6.0/go.mod h1:Vigx9VDiNscYpa/LwrURqGXLSIbzTfapt9+K6gF1kTA=
|
||||
@ -322,6 +386,9 @@ github.com/gobuffalo/github_flavored_markdown v1.0.4/go.mod h1:uRowCdK+q8d/RF0Kt
|
||||
github.com/gobuffalo/github_flavored_markdown v1.0.5/go.mod h1:U0643QShPF+OF2tJvYNiYDLDGDuQmJZXsf/bHOJPsMY=
|
||||
github.com/gobuffalo/github_flavored_markdown v1.0.7/go.mod h1:w93Pd9Lz6LvyQXEG6DktTPHkOtCbr+arAD5mkwMzXLI=
|
||||
github.com/gobuffalo/github_flavored_markdown v1.1.0/go.mod h1:TSpTKWcRTI0+v7W3x8dkSKMLJSUpuVitlptCkpeY8ic=
|
||||
github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
|
||||
github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
|
||||
github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
|
||||
github.com/gobuffalo/gogen v0.2.0/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
|
||||
github.com/gobuffalo/helpers v0.2.2/go.mod h1:xYbzUdCUpVzLwLnqV8HIjT6hmG0Cs7YIBCJkNM597jw=
|
||||
github.com/gobuffalo/helpers v0.2.4/go.mod h1:NX7v27yxPDOPTgUFYmJ5ow37EbxdoLraucOGvMNawyk=
|
||||
@ -381,6 +448,7 @@ github.com/gobuffalo/packd v0.0.0-20181114190715-f25c5d2471d7/go.mod h1:Yf2toFaI
|
||||
github.com/gobuffalo/packd v0.0.0-20181124090624-311c6248e5fb/go.mod h1:Foenia9ZvITEvG05ab6XpiD5EfBHPL8A6hush8SJ0o8=
|
||||
github.com/gobuffalo/packd v0.0.0-20181207120301-c49825f8f6f4/go.mod h1:LYc0TGKFBBFTRC9dg2pcRcMqGCTMD7T2BIMP7OBuQAA=
|
||||
github.com/gobuffalo/packd v0.0.0-20181212173646-eca3b8fd6687/go.mod h1:LYc0TGKFBBFTRC9dg2pcRcMqGCTMD7T2BIMP7OBuQAA=
|
||||
github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
|
||||
github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
|
||||
github.com/gobuffalo/packd v0.2.0/go.mod h1:k2CkHP3bjbqL2GwxwhxUy1DgnlbW644hkLC9iIUvZwY=
|
||||
github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
|
||||
@ -400,9 +468,12 @@ github.com/gobuffalo/packr/v2 v2.0.0-rc.12/go.mod h1:FV1zZTsVFi1DSCboO36Xgs4pzCZ
|
||||
github.com/gobuffalo/packr/v2 v2.0.0-rc.13/go.mod h1:2Mp7GhBFMdJlOK8vGfl7SYtfMP3+5roE39ejlfjw0rA=
|
||||
github.com/gobuffalo/packr/v2 v2.0.0-rc.14/go.mod h1:06otbrNvDKO1eNQ3b8hst+1010UooI2MFg+B2Ze4MV8=
|
||||
github.com/gobuffalo/packr/v2 v2.0.0-rc.15/go.mod h1:IMe7H2nJvcKXSF90y4X1rjYIRlNMJYCxEhssBXNZwWs=
|
||||
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
|
||||
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
|
||||
github.com/gobuffalo/packr/v2 v2.4.0/go.mod h1:ra341gygw9/61nSjAbfwcwh8IrYL4WmR4IsPkPBhQiY=
|
||||
github.com/gobuffalo/packr/v2 v2.5.2/go.mod h1:sgEE1xNZ6G0FNN5xn9pevVu4nywaxHvgup67xisti08=
|
||||
github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc=
|
||||
github.com/gobuffalo/packr/v2 v2.8.0/go.mod h1:PDk2k3vGevNE3SwVyVRgQCCXETC9SaONCNSXT1Q8M1g=
|
||||
github.com/gobuffalo/plush v3.7.16+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI=
|
||||
github.com/gobuffalo/plush v3.7.20+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI=
|
||||
github.com/gobuffalo/plush v3.7.21+incompatible/go.mod h1:rQ4zdtUUyZNqULlc6bqd5scsPfLKfT0+TGMChgduDvI=
|
||||
@ -424,6 +495,7 @@ github.com/gobuffalo/pop v4.8.3+incompatible/go.mod h1:DwBz3SD5SsHpTZiTubcsFWcVD
|
||||
github.com/gobuffalo/pop v4.8.4+incompatible/go.mod h1:DwBz3SD5SsHpTZiTubcsFWcVDpJWGsxjVjMPnkiThWg=
|
||||
github.com/gobuffalo/pop v4.13.1+incompatible/go.mod h1:DwBz3SD5SsHpTZiTubcsFWcVDpJWGsxjVjMPnkiThWg=
|
||||
github.com/gobuffalo/pop/v5 v5.0.11/go.mod h1:mZJHJbA3cy2V18abXYuVop2ldEJ8UZ2DK6qOekC5u5g=
|
||||
github.com/gobuffalo/pop/v5 v5.3.1/go.mod h1:vcEDhh6cJ3WVENqJDFt/6z7zNb7lLnlN8vj3n5G9rYA=
|
||||
github.com/gobuffalo/release v1.0.35/go.mod h1:VtHFAKs61vO3wboCec5xr9JPTjYyWYcvaM3lclkc4x4=
|
||||
github.com/gobuffalo/release v1.0.38/go.mod h1:VtHFAKs61vO3wboCec5xr9JPTjYyWYcvaM3lclkc4x4=
|
||||
github.com/gobuffalo/release v1.0.42/go.mod h1:RPs7EtafH4oylgetOJpGP0yCZZUiO4vqHfTHJjSdpug=
|
||||
@ -464,7 +536,6 @@ github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14j
|
||||
github.com/gofrs/uuid v3.1.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gofrs/uuid/v3 v3.1.2/go.mod h1:xPwMqoocQ1L5G6pXX5BcE7N5jlzn2o19oqAKxwZW/kI=
|
||||
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
@ -473,7 +544,6 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/gddo v0.0.0-20180828051604-96d2a289f41e/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4=
|
||||
github.com/golang/gddo v0.0.0-20190904175337-72a348e765d2/go.mod h1:xEhNfoBDX1hzLm2Nf80qUvZ2sVwoMZ8d6IE2SrsQfh4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@ -505,6 +575,7 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
||||
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
@ -568,9 +639,11 @@ github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBt
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-retryablehttp v0.6.8/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
@ -592,6 +665,7 @@ github.com/imdario/mergo v0.3.5 h1:JboBksRwiiAJWvIYJVo46AfV+IAIKZpfrSzVKj42R4Q=
|
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inhies/go-bytesize v0.0.0-20201103132853-d0aed0d254f8/go.mod h1:KrtyD5PFj++GKkFS/7/RRrfnRhAMGQwy75GLCHWrCNs=
|
||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
@ -601,6 +675,7 @@ github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW
|
||||
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
||||
github.com/jackc/pgconn v1.3.2/go.mod h1:LvCquS3HbBKwgl7KbX9KyqEIumJAbm1UMcTvGaIf3bM=
|
||||
github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
|
||||
github.com/jackc/pgconn v1.6.0/go.mod h1:yeseQo4xhQbgyJs2c87RAXOH2i624N0Fh1KSPJya7qo=
|
||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
@ -610,20 +685,24 @@ github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||
github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.0.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||
github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
|
||||
github.com/jackc/pgtype v1.3.0/go.mod h1:b0JqxHvPmljG+HQ5IsvQ0yqeSi4nGcDTVjFoiLDb0Ik=
|
||||
github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
|
||||
github.com/jackc/pgx v3.6.2+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||
github.com/jackc/pgx/v4 v4.4.1/go.mod h1:6iSW+JznC0YT+SgBn7rNxoEBsBgSmnC5FwyCekOGUiE=
|
||||
github.com/jackc/pgx/v4 v4.6.0/go.mod h1:vPh43ZzxijXUVJ+t/EmXBtFmbFVO72cuneCT9oAlxAg=
|
||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jandelgado/gcov2lcov v1.0.4-0.20210120124023-b83752c6dc08/go.mod h1:NnSxK6TMlg1oGDBfGelGbjgorT5/L3cchlbtgFYZSss=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmoiron/sqlx v0.0.0-20180614180643-0dae4fefe7c0/go.mod h1:IiEW3SEiiErVyFdH8NTuWjSifiEQKUoyK3LNqr2kCHU=
|
||||
@ -649,14 +728,20 @@ github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E
|
||||
github.com/karrick/godirwalk v1.7.5/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34=
|
||||
github.com/karrick/godirwalk v1.7.7/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34=
|
||||
github.com/karrick/godirwalk v1.7.8/go.mod h1:2c9FRhkDxdIbgkOnCEvnSWs71Bhugbl46shStcFDJ34=
|
||||
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
|
||||
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
|
||||
github.com/karrick/godirwalk v1.10.9/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
|
||||
github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
|
||||
github.com/karrick/godirwalk v1.15.3/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
|
||||
github.com/karrick/godirwalk v1.15.5/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
|
||||
github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/knadh/koanf v0.14.1-0.20201201075439-e0853799f9ec/go.mod h1:H5mEFsTeWizwFXHKtsITL5ipsLTuAMQoGuQpp+1JL9U=
|
||||
github.com/konsorten/go-windows-terminal-sequences v0.0.0-20180402223658-b729f2633dfe/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
@ -679,18 +764,22 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/luna-duclos/instrumentedsql v0.0.0-20181127104832-b7d587d28109/go.mod h1:PWUIzhtavmOR965zfawVsHXbEuU1G29BPZ/CB3C7jXk=
|
||||
github.com/luna-duclos/instrumentedsql v1.1.2/go.mod h1:4LGbEqDnopzNAiyxPPDXhLspyunZxgPTMJBKtC6U0BQ=
|
||||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
||||
github.com/luna-duclos/instrumentedsql v1.1.3/go.mod h1:9J1njvFds+zN7y85EDhN9XNQLANWwZt2ULeIC8yMNYs=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
|
||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/markbates/deplist v1.0.4/go.mod h1:gRRbPbbuA8TmMiRvaOzUlRfzfjeCCBqX2A6arxN01MM=
|
||||
github.com/markbates/deplist v1.0.5/go.mod h1:gRRbPbbuA8TmMiRvaOzUlRfzfjeCCBqX2A6arxN01MM=
|
||||
github.com/markbates/deplist v1.1.3/go.mod h1:BF7ioVzAJYEtzQN/os4rt8H8Ti3h0T7EoN+7eyALktE=
|
||||
github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc=
|
||||
github.com/markbates/going v1.0.2/go.mod h1:UWCk3zm0UKefHZ7l8BNqi26UyiEMniznk8naLdTcy6c=
|
||||
github.com/markbates/grift v1.0.4/go.mod h1:wbmtW74veyx+cgfwFhlnnMWqhoz55rnHR47oMXzsyVs=
|
||||
github.com/markbates/hmax v1.0.0/go.mod h1:cOkR9dktiESxIMu+65oc/r/bdY4bE8zZw3OLhLx0X2c=
|
||||
@ -750,6 +839,7 @@ github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYG
|
||||
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
|
||||
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
||||
github.com/moby/term v0.0.0-20200915141129-7f0af18e79f2/go.mod h1:TjQg8pa4iejrUrjiz0MCtMV38jdMNW4doKSiBrEvCQQ=
|
||||
github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
@ -760,6 +850,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/monoculum/formam v0.0.0-20180901015400-4e68be1d79ba/go.mod h1:RKgILGEJq24YyJ2ban8EO0RUVSJlF1pGsEvoLEACr/Q=
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/moul/http2curl v0.0.0-20170919181001-9ac6cf4d929b/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
@ -803,19 +895,20 @@ github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rm
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/opentracing/opentracing-go v1.1.1-0.20190913142402-a7454ce5950e/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA=
|
||||
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/ory/analytics-go/v4 v4.0.0/go.mod h1:FMx9cLRD9xN+XevPvZ5FDMfignpmcqPP6FUKnJ9/MmE=
|
||||
github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
|
||||
github.com/ory/dockertest/v3 v3.5.4/go.mod h1:J8ZUbNB2FOhm1cFZW9xBpDsODqsSWcyYgtJYVPcnF70=
|
||||
github.com/ory/dockertest/v3 v3.6.3/go.mod h1:EFLcVUOl8qCwp9NyDAcCDtq/QviLtYswW/VbWzUnTNE=
|
||||
github.com/ory/fosite v0.29.0/go.mod h1:0atSZmXO7CAcs6NPMI/Qtot8tmZYj04Nddoold4S2h0=
|
||||
github.com/ory/fosite v0.39.0 h1:u1Ct/ME7XYzREvufr7ehBIdq/KatjVLIYg/ABqWzprw=
|
||||
github.com/ory/fosite v0.39.0/go.mod h1:37r59qkOSPueYKmaA7EHiXrDMF1B+XPN+MgkZgTRg3Y=
|
||||
github.com/ory/fosite v0.40.2 h1:xOS/1kPOlk1LqnAEnNXUqNGdMwvHBqw20ZbURaK+qoM=
|
||||
github.com/ory/fosite v0.40.2/go.mod h1:KbDZzqSDLaOLDN2haRIsBQHUhl8MQp66cYMqCpO/8Mg=
|
||||
github.com/ory/go-acc v0.0.0-20181118080137-ddc355013f90/go.mod h1:sxnvPCxChFuSmTJGj8FdMupeq1BezCiEpDjTUXQ4hf4=
|
||||
github.com/ory/go-acc v0.2.5 h1:31irXHzG2vnKQSE4weJm7AdfrnpaVjVCq3nD7viXCJE=
|
||||
github.com/ory/go-acc v0.2.5/go.mod h1:4Kb/UnPcT8qRAk3IAxta+hvVapdxTLWtrr7bFLlEgpw=
|
||||
github.com/ory/go-acc v0.2.6 h1:YfI+L9dxI7QCtWn2RbawqO0vXhiThdXu/RgizJBbaq0=
|
||||
github.com/ory/go-acc v0.2.6/go.mod h1:4Kb/UnPcT8qRAk3IAxta+hvVapdxTLWtrr7bFLlEgpw=
|
||||
github.com/ory/go-convenience v0.1.0 h1:zouLKfF2GoSGnJwGq+PE/nJAE6dj2Zj5QlTgmMTsTS8=
|
||||
github.com/ory/go-convenience v0.1.0/go.mod h1:uEY/a60PL5c12nYz4V5cHY03IBmwIAEm8TWB0yn9KNs=
|
||||
github.com/ory/gojsonreference v0.0.0-20190720135523-6b606c2d8ee8/go.mod h1:wsH1C4nIeeQClDtD5AH7kF1uTS6zWyqfjVDTmB0Em7A=
|
||||
@ -823,6 +916,7 @@ github.com/ory/gojsonschema v1.1.1-0.20190919112458-f254ca73d5e9/go.mod h1:BNZpd
|
||||
github.com/ory/herodot v0.6.2/go.mod h1:3BOneqcyBsVybCPAJoi92KN2BpJHcmDqAMcAAaJiJow=
|
||||
github.com/ory/herodot v0.7.0/go.mod h1:YXKOfAXYdQojDP5sD8m0ajowq3+QXNdtxA+QiUXBwn0=
|
||||
github.com/ory/herodot v0.8.3/go.mod h1:rvLjxOAlU5omtmgjCfazQX2N82EpMfl3BytBWc1jjsk=
|
||||
github.com/ory/herodot v0.9.2/go.mod h1:Da2HXR8mpwPbPrH+Gv9qV8mM5gI3v+PoJ69BA4l2RAk=
|
||||
github.com/ory/jsonschema/v3 v3.0.1/go.mod h1:jgLHekkFk0uiGdEWGleC+tOm6JSSP8cbf17PnBuGXlw=
|
||||
github.com/ory/viper v1.5.6/go.mod h1:TYmpFpKLxjQwvT4f0QPpkOn4sDXU1kDgAwJpgLYiQ28=
|
||||
github.com/ory/viper v1.7.4/go.mod h1:T6sodNZKNGPpashUOk7EtXz2isovz8oCd57GNVkkNmE=
|
||||
@ -831,8 +925,9 @@ github.com/ory/viper v1.7.5/go.mod h1:ypOuyJmEUb3oENywQZRgeAMwqgOyDqwboO1tj3DjTa
|
||||
github.com/ory/x v0.0.84/go.mod h1:RXLPBG7B+hAViONVg0sHwK+U/ie1Y/NeXrq1JcARfoE=
|
||||
github.com/ory/x v0.0.93/go.mod h1:lfcTaGXpTZs7IEQAW00r9EtTCOxD//SiP5uWtNiz31g=
|
||||
github.com/ory/x v0.0.110/go.mod h1:DJfkE3GdakhshNhw4zlKoRaL/ozg/lcTahA9OCih2BE=
|
||||
github.com/ory/x v0.0.162 h1:xE/UBmmMlInTvlgGXUyo+VeZAcWU5gyWb/xh6jmBWsI=
|
||||
github.com/ory/x v0.0.162/go.mod h1:sj3z/MeCrAyNFFTfN6yK1nTmHXGSFnw+QwIIQ/Rowec=
|
||||
github.com/ory/x v0.0.127/go.mod h1:FwUujfFuCj5d+xgLn4fGMYPnzriR5bdAIulFXMtnK0M=
|
||||
github.com/ory/x v0.0.212 h1:7PpEOdrXGP/tMGyv0m1ERy74oCS/Z11hlkxVs9OC1GE=
|
||||
github.com/ory/x v0.0.212/go.mod h1:RDxYOolvMdzumYnHWha8D+RoLjYtGszyDDed4OCGC54=
|
||||
github.com/parnurzeal/gorequest v0.2.15/go.mod h1:3Kh2QUMJoqw3icWAecsyzkpY7UzRfDhbRdTjtNwNiUE=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
|
||||
@ -840,9 +935,11 @@ github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtP
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
|
||||
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
|
||||
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
|
||||
github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw=
|
||||
github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI=
|
||||
@ -885,6 +982,7 @@ github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rhnvrm/simples3 v0.5.0/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.0.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
@ -911,6 +1009,7 @@ github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZ
|
||||
github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
|
||||
github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/seatgeek/logrus-gelf-formatter v0.0.0-20210219220335-367fa274be2c/go.mod h1:/THDZYi7F/BsVEcYzYPqdcWFQ+1C2InkawTKfLOAnzg=
|
||||
github.com/segmentio/analytics-go v3.0.1+incompatible/go.mod h1:C7CYBtQWk4vRk2RyLu0qOcbHJ18E3F1HV2C/8JvKN48=
|
||||
github.com/segmentio/analytics-go v3.1.0+incompatible/go.mod h1:C7CYBtQWk4vRk2RyLu0qOcbHJ18E3F1HV2C/8JvKN48=
|
||||
github.com/segmentio/backo-go v0.0.0-20160424052352-204274ad699c/go.mod h1:kJ9mm9YmoWSkk+oQ+5Cj8DEoRCX2JT6As4kEtIIOp1M=
|
||||
@ -936,6 +1035,7 @@ github.com/sirupsen/logrus v1.1.0/go.mod h1:zrgwTnHtNr00buQ1vSptGe8m1f/BbgsPukg8
|
||||
github.com/sirupsen/logrus v1.1.1/go.mod h1:zrgwTnHtNr00buQ1vSptGe8m1f/BbgsPukg8qsT7A+A=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
|
||||
@ -960,11 +1060,13 @@ github.com/spf13/afero v1.3.2 h1:GDarE4TJQI52kYSbSAmLiId1Elfj+xgSDqrUZxFhxlU=
|
||||
github.com/spf13/afero v1.3.2/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
||||
github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.2-0.20200723214538-8d17101741c8 h1:GbJaXkBXPYlxE45H4g2wo0Hb4TGzv/YbHVA1OGqx+mo=
|
||||
github.com/spf13/cast v1.3.2-0.20200723214538-8d17101741c8/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/cobra v0.0.7/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||
@ -986,6 +1088,7 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518/go.mod h1:CKI4AZ4XmGV240rTHfO0hfE83S6/a3/Q1siZJ/vXf7A=
|
||||
github.com/square/go-jose/v3 v3.0.0-20200630053402-0a67ce9b0693/go.mod h1:6hSY48PjDm4UObWmGLyJE9DxYVKTgR9kbCspXXJEhcU=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
@ -1001,9 +1104,15 @@ github.com/subosito/gotenv v1.1.1/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tidwall/gjson v1.3.2/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
|
||||
github.com/tidwall/gjson v1.6.8/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI=
|
||||
github.com/tidwall/gjson v1.7.1/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
|
||||
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
|
||||
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tidwall/sjson v1.0.4/go.mod h1:bURseu1nuBkFpIES5cz6zBtjmYeOQmEESshn7VpF15Y=
|
||||
github.com/tidwall/sjson v1.1.5/go.mod h1:VuJzsZnTowhSxWdOgsAnb886i4AjEyTkk7tNtsL7EYE=
|
||||
github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
|
||||
@ -1019,13 +1128,14 @@ github.com/unrolled/secure v0.0.0-20180918153822-f340ee86eb8b/go.mod h1:mnPT77IA
|
||||
github.com/unrolled/secure v0.0.0-20181005190816-ff9db2ff917f/go.mod h1:mnPT77IAdsi/kV7+Es7y+pXALeV3h7G6dQF6mNYjcLA=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
|
||||
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
|
||||
github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
@ -1039,6 +1149,10 @@ go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
|
||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489 h1:1JFLBqwIgdyHN1ZtgjTBwO+blA6gVOmZurpiMEsETKo=
|
||||
go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=
|
||||
go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
|
||||
go.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA=
|
||||
@ -1073,11 +1187,14 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf
|
||||
golang.org/x/crypto v0.0.0-20190102171810-8d7daa0c54b3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
@ -1112,7 +1229,6 @@ golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMk
|
||||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
@ -1135,7 +1251,6 @@ golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449 h1:xUIPaMhvROX9dhPvRCenIJt
|
||||
golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180816102801-aaf60122140d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180921000356-2f5d2388922f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180926154720-4dfa2610cdf3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -1152,6 +1267,7 @@ golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73r
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
@ -1174,7 +1290,7 @@ golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
@ -1192,16 +1308,15 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180816055513-1c9583448a9c/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180831094639-fa5fdf94c789/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -1225,12 +1340,15 @@ golang.org/x/sys v0.0.0-20190116161447-11f53e031339/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -1261,7 +1379,6 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -1299,6 +1416,7 @@ golang.org/x/tools v0.0.0-20181013182035-5e66757b835f/go.mod h1:n7NCudcB/nEzxVGm
|
||||
golang.org/x/tools v0.0.0-20181017214349-06f26fdaaa28/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181024171208-a2dc47679d30/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181026183834-f60e5f99f081/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181105230042-78dc5bac0cac/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181107215632-34b416bd17b3/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181114190951-94339b83286c/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@ -1313,7 +1431,6 @@ golang.org/x/tools v0.0.0-20181212172921-837e80568c09/go.mod h1:n7NCudcB/nEzxVGm
|
||||
golang.org/x/tools v0.0.0-20190102213336-ca9055ed7d04/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190104182027-498d95493402/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190111214448-fc1d57b08d7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190118193359-16909d206f00/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
@ -1321,13 +1438,18 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190613204242-ed0dc450797f/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190624190245-7f2218787638/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
@ -1358,10 +1480,10 @@ golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapK
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200308013534-11ec41452d41/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200721223218-6123e77877b2/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
@ -1388,7 +1510,6 @@ google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
@ -1416,20 +1537,11 @@ google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfG
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a h1:pOwg4OoaRYScjmR4LlLgdtnyoHYTSAVhhqe5uPdpII8=
|
||||
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1 h1:zvIju4sqAGvwKspUQOhwnpcqSbzi7/H6QomNNjTL4sk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@ -1487,6 +1599,7 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
@ -1516,8 +1629,9 @@ k8s.io/gengo v0.0.0-20210203185629-de9496dff47b h1:bAU8IlrMA6KbP0dIg/sVSJn95pDCU
|
||||
k8s.io/gengo v0.0.0-20210203185629-de9496dff47b/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
|
||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||
k8s.io/klog/v2 v2.8.0 h1:Q3gmuM9hKEjefWFFYF0Mat+YyFJvsUyYuwyNNJ5C9Ts=
|
||||
k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
|
||||
k8s.io/klog/v2 v2.9.0 h1:D7HV+n1V57XeZ0m6tdRkfknthUaM06VFbWldOFh8kzM=
|
||||
k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
|
||||
k8s.io/kube-aggregator v0.21.1 h1:3pPRhOXZcJYjNDjPDizFx0G5//DArWKANZE03J5z8Ck=
|
||||
k8s.io/kube-aggregator v0.21.1/go.mod h1:cAZ0n02IiSl57sQSHz4vvrz3upQRMbytOiZnpPJaQzQ=
|
||||
k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 h1:vEx13qjvaZ4yfObSSXW7BrMc/KQBBT/Jyee8XtLf4x0=
|
||||
|
3
hack/dependencyhacks/grpcexamples/go.mod
Normal file
3
hack/dependencyhacks/grpcexamples/go.mod
Normal file
@ -0,0 +1,3 @@
|
||||
module google.golang.org/grpc/examples
|
||||
|
||||
go 1.14
|
@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
# Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set -euo pipefail
|
||||
@ -46,7 +46,7 @@ function with_modules() {
|
||||
env_vars="KUBE_CACHE_MUTATION_DETECTOR=${kube_cache_mutation_detector} KUBE_PANIC_WATCH_DECODE_ERROR=${kube_panic_watch_decode_error}"
|
||||
|
||||
pushd "${ROOT}" >/dev/null
|
||||
for mod_file in $(find . -maxdepth 4 -not -path "./generated/*" -name go.mod | sort); do
|
||||
for mod_file in $(find . -maxdepth 4 -not -path "./generated/*" -not -path "./hack/*" -name go.mod | sort); do
|
||||
mod_dir="$(dirname "${mod_file}")"
|
||||
(
|
||||
echo "=> "
|
||||
|
@ -1,65 +0,0 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package impersonator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
type Mode string
|
||||
|
||||
const (
|
||||
// Explicitly enable the impersonation proxy.
|
||||
ModeEnabled Mode = "enabled"
|
||||
|
||||
// Explicitly disable the impersonation proxy.
|
||||
ModeDisabled Mode = "disabled"
|
||||
|
||||
// Allow the proxy to decide if it should be enabled or disabled based upon the cluster in which it is running.
|
||||
ModeAuto Mode = "auto"
|
||||
)
|
||||
|
||||
const (
|
||||
ConfigMapDataKey = "config.yaml"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
// Enable or disable the impersonation proxy. Optional. Defaults to ModeAuto.
|
||||
Mode Mode `json:"mode,omitempty"`
|
||||
|
||||
// Used when creating TLS certificates and for clients to discover the endpoint. Optional. When not specified, if the
|
||||
// impersonation proxy is started, then it will automatically create a LoadBalancer Service and use its ingress as the
|
||||
// endpoint.
|
||||
//
|
||||
// When specified, it may be a hostname or IP address, optionally with a port number, of the impersonation proxy
|
||||
// for clients to use from outside the cluster. E.g. myhost.mycompany.com:8443. Clients should assume that they should
|
||||
// connect via HTTPS to this service.
|
||||
Endpoint string `json:"endpoint,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Config) HasEndpoint() bool {
|
||||
return c.Endpoint != ""
|
||||
}
|
||||
|
||||
func NewConfig() *Config {
|
||||
return &Config{Mode: ModeAuto}
|
||||
}
|
||||
|
||||
func ConfigFromConfigMap(configMap *v1.ConfigMap) (*Config, error) {
|
||||
stringConfig, ok := configMap.Data[ConfigMapDataKey]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(`ConfigMap is missing expected key "%s"`, ConfigMapDataKey)
|
||||
}
|
||||
config := NewConfig()
|
||||
if err := yaml.Unmarshal([]byte(stringConfig), config); err != nil {
|
||||
return nil, fmt.Errorf("decode yaml: %w", err)
|
||||
}
|
||||
if config.Mode != ModeAuto && config.Mode != ModeEnabled && config.Mode != ModeDisabled {
|
||||
return nil, fmt.Errorf(`illegal value for "mode": %s`, config.Mode)
|
||||
}
|
||||
return config, nil
|
||||
}
|
@ -1,155 +0,0 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package impersonator
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"go.pinniped.dev/internal/here"
|
||||
)
|
||||
|
||||
func TestNewConfig(t *testing.T) {
|
||||
// It defaults the mode.
|
||||
require.Equal(t, &Config{Mode: ModeAuto}, NewConfig())
|
||||
}
|
||||
|
||||
func TestHasEndpoint(t *testing.T) {
|
||||
configWithoutEndpoint := Config{}
|
||||
configWithEndpoint := Config{Endpoint: "something"}
|
||||
require.False(t, configWithoutEndpoint.HasEndpoint())
|
||||
require.True(t, configWithEndpoint.HasEndpoint())
|
||||
}
|
||||
|
||||
func TestConfigFromConfigMap(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
configMap *v1.ConfigMap
|
||||
wantConfig *Config
|
||||
wantError string
|
||||
}{
|
||||
{
|
||||
name: "fully configured, valid config",
|
||||
configMap: &v1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
ObjectMeta: metav1.ObjectMeta{},
|
||||
Data: map[string]string{
|
||||
"config.yaml": here.Doc(`
|
||||
mode: enabled
|
||||
endpoint: proxy.example.com:8443
|
||||
`),
|
||||
},
|
||||
},
|
||||
wantConfig: &Config{
|
||||
Mode: "enabled",
|
||||
Endpoint: "proxy.example.com:8443",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty, valid config",
|
||||
configMap: &v1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
ObjectMeta: metav1.ObjectMeta{},
|
||||
Data: map[string]string{
|
||||
"config.yaml": "",
|
||||
},
|
||||
},
|
||||
wantConfig: &Config{
|
||||
Mode: "auto",
|
||||
Endpoint: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid config with mode enabled",
|
||||
configMap: &v1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
ObjectMeta: metav1.ObjectMeta{},
|
||||
Data: map[string]string{
|
||||
"config.yaml": "mode: enabled",
|
||||
},
|
||||
},
|
||||
wantConfig: &Config{
|
||||
Mode: "enabled",
|
||||
Endpoint: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid config with mode disabled",
|
||||
configMap: &v1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
ObjectMeta: metav1.ObjectMeta{},
|
||||
Data: map[string]string{
|
||||
"config.yaml": "mode: disabled",
|
||||
},
|
||||
},
|
||||
wantConfig: &Config{
|
||||
Mode: "disabled",
|
||||
Endpoint: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "valid config with mode auto",
|
||||
configMap: &v1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
ObjectMeta: metav1.ObjectMeta{},
|
||||
Data: map[string]string{
|
||||
"config.yaml": "mode: auto",
|
||||
},
|
||||
},
|
||||
wantConfig: &Config{
|
||||
Mode: "auto",
|
||||
Endpoint: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "wrong key in configmap",
|
||||
configMap: &v1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
ObjectMeta: metav1.ObjectMeta{},
|
||||
Data: map[string]string{
|
||||
"wrong-key": "",
|
||||
},
|
||||
},
|
||||
wantError: `ConfigMap is missing expected key "config.yaml"`,
|
||||
},
|
||||
{
|
||||
name: "illegal yaml in configmap",
|
||||
configMap: &v1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
ObjectMeta: metav1.ObjectMeta{},
|
||||
Data: map[string]string{
|
||||
"config.yaml": "this is not yaml",
|
||||
},
|
||||
},
|
||||
wantError: "decode yaml: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type impersonator.Config",
|
||||
},
|
||||
{
|
||||
name: "illegal value for mode in configmap",
|
||||
configMap: &v1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
ObjectMeta: metav1.ObjectMeta{},
|
||||
Data: map[string]string{
|
||||
"config.yaml": "mode: unexpected-value",
|
||||
},
|
||||
},
|
||||
wantError: `illegal value for "mode": unexpected-value`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
test := tt
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
config, err := ConfigFromConfigMap(test.configMap)
|
||||
require.Equal(t, test.wantConfig, config)
|
||||
if test.wantError != "" {
|
||||
require.EqualError(t, err, test.wantError)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -195,7 +195,6 @@ func newInternal( //nolint:funlen // yeah, it's kind of long.
|
||||
}
|
||||
|
||||
// wire up a fake audit backend at the metadata level so we can preserve the original user during nested impersonation
|
||||
// TODO: wire up the real std out logging audit backend based on plog log level
|
||||
serverConfig.AuditPolicyChecker = policy.FakeChecker(auditinternal.LevelMetadata, nil)
|
||||
serverConfig.AuditBackend = &auditfake.Backend{}
|
||||
|
||||
|
@ -108,12 +108,12 @@ func validateNames(names *NamesConfigSpec) error {
|
||||
if names.APIService == "" {
|
||||
missingNames = append(missingNames, "apiService")
|
||||
}
|
||||
if names.ImpersonationConfigMap == "" {
|
||||
missingNames = append(missingNames, "impersonationConfigMap")
|
||||
}
|
||||
if names.ImpersonationLoadBalancerService == "" {
|
||||
missingNames = append(missingNames, "impersonationLoadBalancerService")
|
||||
}
|
||||
if names.ImpersonationClusterIPService == "" {
|
||||
missingNames = append(missingNames, "impersonationClusterIPService")
|
||||
}
|
||||
if names.ImpersonationTLSCertificateSecret == "" {
|
||||
missingNames = append(missingNames, "impersonationTLSCertificateSecret")
|
||||
}
|
||||
|
@ -38,13 +38,14 @@ func TestFromPath(t *testing.T) {
|
||||
credentialIssuer: pinniped-config
|
||||
apiService: pinniped-api
|
||||
kubeCertAgentPrefix: kube-cert-agent-prefix
|
||||
impersonationConfigMap: impersonationConfigMap-value
|
||||
impersonationLoadBalancerService: impersonationLoadBalancerService-value
|
||||
impersonationClusterIPService: impersonationClusterIPService-value
|
||||
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
|
||||
impersonationCACertificateSecret: impersonationCACertificateSecret-value
|
||||
impersonationSignerSecret: impersonationSignerSecret-value
|
||||
impersonationSignerSecret: impersonationSignerSecret-value
|
||||
agentServiceAccount: agentServiceAccount-value
|
||||
extraName: extraName-value
|
||||
labels:
|
||||
myLabelKey1: myLabelValue1
|
||||
myLabelKey2: myLabelValue2
|
||||
@ -69,8 +70,8 @@ func TestFromPath(t *testing.T) {
|
||||
ServingCertificateSecret: "pinniped-concierge-api-tls-serving-certificate",
|
||||
CredentialIssuer: "pinniped-config",
|
||||
APIService: "pinniped-api",
|
||||
ImpersonationConfigMap: "impersonationConfigMap-value",
|
||||
ImpersonationLoadBalancerService: "impersonationLoadBalancerService-value",
|
||||
ImpersonationClusterIPService: "impersonationClusterIPService-value",
|
||||
ImpersonationTLSCertificateSecret: "impersonationTLSCertificateSecret-value",
|
||||
ImpersonationCACertificateSecret: "impersonationCACertificateSecret-value",
|
||||
ImpersonationSignerSecret: "impersonationSignerSecret-value",
|
||||
@ -96,8 +97,8 @@ func TestFromPath(t *testing.T) {
|
||||
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
|
||||
credentialIssuer: pinniped-config
|
||||
apiService: pinniped-api
|
||||
impersonationConfigMap: impersonationConfigMap-value
|
||||
impersonationLoadBalancerService: impersonationLoadBalancerService-value
|
||||
impersonationClusterIPService: impersonationClusterIPService-value
|
||||
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
|
||||
impersonationCACertificateSecret: impersonationCACertificateSecret-value
|
||||
impersonationSignerSecret: impersonationSignerSecret-value
|
||||
@ -118,8 +119,8 @@ func TestFromPath(t *testing.T) {
|
||||
ServingCertificateSecret: "pinniped-concierge-api-tls-serving-certificate",
|
||||
CredentialIssuer: "pinniped-config",
|
||||
APIService: "pinniped-api",
|
||||
ImpersonationConfigMap: "impersonationConfigMap-value",
|
||||
ImpersonationLoadBalancerService: "impersonationLoadBalancerService-value",
|
||||
ImpersonationClusterIPService: "impersonationClusterIPService-value",
|
||||
ImpersonationTLSCertificateSecret: "impersonationTLSCertificateSecret-value",
|
||||
ImpersonationCACertificateSecret: "impersonationCACertificateSecret-value",
|
||||
ImpersonationSignerSecret: "impersonationSignerSecret-value",
|
||||
@ -136,8 +137,8 @@ func TestFromPath(t *testing.T) {
|
||||
name: "Empty",
|
||||
yaml: here.Doc(``),
|
||||
wantError: "validate names: missing required names: servingCertificateSecret, credentialIssuer, " +
|
||||
"apiService, impersonationConfigMap, impersonationLoadBalancerService, " +
|
||||
"impersonationTLSCertificateSecret, impersonationCACertificateSecret, " +
|
||||
"apiService, impersonationLoadBalancerService, " +
|
||||
"impersonationClusterIPService, impersonationTLSCertificateSecret, impersonationCACertificateSecret, " +
|
||||
"impersonationSignerSecret, agentServiceAccount",
|
||||
},
|
||||
{
|
||||
@ -147,8 +148,8 @@ func TestFromPath(t *testing.T) {
|
||||
names:
|
||||
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
|
||||
credentialIssuer: pinniped-config
|
||||
impersonationConfigMap: impersonationConfigMap-value
|
||||
impersonationLoadBalancerService: impersonationLoadBalancerService-value
|
||||
impersonationClusterIPService: impersonationClusterIPService-value
|
||||
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
|
||||
impersonationCACertificateSecret: impersonationCACertificateSecret-value
|
||||
impersonationSignerSecret: impersonationSignerSecret-value
|
||||
@ -163,8 +164,8 @@ func TestFromPath(t *testing.T) {
|
||||
names:
|
||||
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
|
||||
apiService: pinniped-api
|
||||
impersonationConfigMap: impersonationConfigMap-value
|
||||
impersonationLoadBalancerService: impersonationLoadBalancerService-value
|
||||
impersonationClusterIPService: impersonationClusterIPService-value
|
||||
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
|
||||
impersonationCACertificateSecret: impersonationCACertificateSecret-value
|
||||
impersonationSignerSecret: impersonationSignerSecret-value
|
||||
@ -179,8 +180,8 @@ func TestFromPath(t *testing.T) {
|
||||
names:
|
||||
credentialIssuer: pinniped-config
|
||||
apiService: pinniped-api
|
||||
impersonationConfigMap: impersonationConfigMap-value
|
||||
impersonationLoadBalancerService: impersonationLoadBalancerService-value
|
||||
impersonationClusterIPService: impersonationClusterIPService-value
|
||||
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
|
||||
impersonationCACertificateSecret: impersonationCACertificateSecret-value
|
||||
impersonationSignerSecret: impersonationSignerSecret-value
|
||||
@ -188,22 +189,6 @@ func TestFromPath(t *testing.T) {
|
||||
`),
|
||||
wantError: "validate names: missing required names: servingCertificateSecret",
|
||||
},
|
||||
{
|
||||
name: "Missing impersonationConfigMap name",
|
||||
yaml: here.Doc(`
|
||||
---
|
||||
names:
|
||||
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
|
||||
credentialIssuer: pinniped-config
|
||||
apiService: pinniped-api
|
||||
impersonationLoadBalancerService: impersonationLoadBalancerService-value
|
||||
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
|
||||
impersonationCACertificateSecret: impersonationCACertificateSecret-value
|
||||
impersonationSignerSecret: impersonationSignerSecret-value
|
||||
agentServiceAccount: agentServiceAccount-value
|
||||
`),
|
||||
wantError: "validate names: missing required names: impersonationConfigMap",
|
||||
},
|
||||
{
|
||||
name: "Missing impersonationLoadBalancerService name",
|
||||
yaml: here.Doc(`
|
||||
@ -212,7 +197,7 @@ func TestFromPath(t *testing.T) {
|
||||
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
|
||||
credentialIssuer: pinniped-config
|
||||
apiService: pinniped-api
|
||||
impersonationConfigMap: impersonationConfigMap-value
|
||||
impersonationClusterIPService: impersonationClusterIPService-value
|
||||
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
|
||||
impersonationCACertificateSecret: impersonationCACertificateSecret-value
|
||||
impersonationSignerSecret: impersonationSignerSecret-value
|
||||
@ -220,6 +205,22 @@ func TestFromPath(t *testing.T) {
|
||||
`),
|
||||
wantError: "validate names: missing required names: impersonationLoadBalancerService",
|
||||
},
|
||||
{
|
||||
name: "Missing impersonationClusterIPService name",
|
||||
yaml: here.Doc(`
|
||||
---
|
||||
names:
|
||||
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
|
||||
credentialIssuer: pinniped-config
|
||||
apiService: pinniped-api
|
||||
impersonationLoadBalancerService: impersonationLoadBalancerService-value
|
||||
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
|
||||
impersonationCACertificateSecret: impersonationCACertificateSecret-value
|
||||
impersonationSignerSecret: impersonationSignerSecret-value
|
||||
agentServiceAccount: agentServiceAccount-value
|
||||
`),
|
||||
wantError: "validate names: missing required names: impersonationClusterIPService",
|
||||
},
|
||||
{
|
||||
name: "Missing impersonationTLSCertificateSecret name",
|
||||
yaml: here.Doc(`
|
||||
@ -228,8 +229,8 @@ func TestFromPath(t *testing.T) {
|
||||
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
|
||||
credentialIssuer: pinniped-config
|
||||
apiService: pinniped-api
|
||||
impersonationConfigMap: impersonationConfigMap-value
|
||||
impersonationLoadBalancerService: impersonationLoadBalancerService-value
|
||||
impersonationClusterIPService: impersonationClusterIPService-value
|
||||
impersonationCACertificateSecret: impersonationCACertificateSecret-value
|
||||
impersonationSignerSecret: impersonationSignerSecret-value
|
||||
agentServiceAccount: agentServiceAccount-value
|
||||
@ -244,8 +245,8 @@ func TestFromPath(t *testing.T) {
|
||||
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
|
||||
credentialIssuer: pinniped-config
|
||||
apiService: pinniped-api
|
||||
impersonationConfigMap: impersonationConfigMap-value
|
||||
impersonationLoadBalancerService: impersonationLoadBalancerService-value
|
||||
impersonationClusterIPService: impersonationClusterIPService-value
|
||||
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
|
||||
impersonationSignerSecret: impersonationSignerSecret-value
|
||||
agentServiceAccount: agentServiceAccount-value
|
||||
@ -260,8 +261,8 @@ func TestFromPath(t *testing.T) {
|
||||
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
|
||||
credentialIssuer: pinniped-config
|
||||
apiService: pinniped-api
|
||||
impersonationConfigMap: impersonationConfigMap-value
|
||||
impersonationLoadBalancerService: impersonationLoadBalancerService-value
|
||||
impersonationClusterIPService: impersonationClusterIPService-value
|
||||
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
|
||||
impersonationCACertificateSecret: impersonationCACertificateSecret-value
|
||||
agentServiceAccount: agentServiceAccount-value
|
||||
@ -277,10 +278,11 @@ func TestFromPath(t *testing.T) {
|
||||
credentialIssuer: pinniped-config
|
||||
apiService: pinniped-api
|
||||
impersonationLoadBalancerService: impersonationLoadBalancerService-value
|
||||
impersonationClusterIPService: impersonationClusterIPService-value
|
||||
impersonationSignerSecret: impersonationSignerSecret-value
|
||||
agentServiceAccount: agentServiceAccount-value
|
||||
`),
|
||||
wantError: "validate names: missing required names: impersonationConfigMap, " +
|
||||
wantError: "validate names: missing required names: " +
|
||||
"impersonationTLSCertificateSecret, impersonationCACertificateSecret",
|
||||
},
|
||||
{
|
||||
@ -295,7 +297,6 @@ func TestFromPath(t *testing.T) {
|
||||
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
|
||||
credentialIssuer: pinniped-config
|
||||
apiService: pinniped-api
|
||||
impersonationConfigMap: impersonationConfigMap-value
|
||||
impersonationLoadBalancerService: impersonationLoadBalancerService-value
|
||||
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
|
||||
impersonationCACertificateSecret: impersonationCACertificateSecret-value
|
||||
@ -315,7 +316,6 @@ func TestFromPath(t *testing.T) {
|
||||
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
|
||||
credentialIssuer: pinniped-config
|
||||
apiService: pinniped-api
|
||||
impersonationConfigMap: impersonationConfigMap-value
|
||||
impersonationLoadBalancerService: impersonationLoadBalancerService-value
|
||||
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
|
||||
impersonationCACertificateSecret: impersonationCACertificateSecret-value
|
||||
@ -335,7 +335,6 @@ func TestFromPath(t *testing.T) {
|
||||
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
|
||||
credentialIssuer: pinniped-config
|
||||
apiService: pinniped-api
|
||||
impersonationConfigMap: impersonationConfigMap-value
|
||||
impersonationLoadBalancerService: impersonationLoadBalancerService-value
|
||||
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
|
||||
impersonationCACertificateSecret: impersonationCACertificateSecret-value
|
||||
@ -356,7 +355,6 @@ func TestFromPath(t *testing.T) {
|
||||
servingCertificateSecret: pinniped-concierge-api-tls-serving-certificate
|
||||
credentialIssuer: pinniped-config
|
||||
apiService: pinniped-api
|
||||
impersonationConfigMap: impersonationConfigMap-value
|
||||
impersonationLoadBalancerService: impersonationLoadBalancerService-value
|
||||
impersonationTLSCertificateSecret: impersonationTLSCertificateSecret-value
|
||||
impersonationCACertificateSecret: impersonationCACertificateSecret-value
|
||||
|
@ -36,8 +36,8 @@ type NamesConfigSpec struct {
|
||||
ServingCertificateSecret string `json:"servingCertificateSecret"`
|
||||
CredentialIssuer string `json:"credentialIssuer"`
|
||||
APIService string `json:"apiService"`
|
||||
ImpersonationConfigMap string `json:"impersonationConfigMap"`
|
||||
ImpersonationLoadBalancerService string `json:"impersonationLoadBalancerService"`
|
||||
ImpersonationClusterIPService string `json:"impersonationClusterIPService"`
|
||||
ImpersonationTLSCertificateSecret string `json:"impersonationTLSCertificateSecret"`
|
||||
ImpersonationCACertificateSecret string `json:"impersonationCACertificateSecret"`
|
||||
ImpersonationSignerSecret string `json:"impersonationSignerSecret"`
|
||||
|
@ -14,19 +14,25 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/equality"
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
"k8s.io/apimachinery/pkg/util/errors"
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
corev1informers "k8s.io/client-go/informers/core/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
"go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
|
||||
pinnipedclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
|
||||
conciergeconfiginformers "go.pinniped.dev/generated/latest/client/concierge/informers/externalversions/config/v1alpha1"
|
||||
"go.pinniped.dev/internal/certauthority"
|
||||
"go.pinniped.dev/internal/clusterhost"
|
||||
"go.pinniped.dev/internal/concierge/impersonator"
|
||||
@ -36,7 +42,7 @@ import (
|
||||
"go.pinniped.dev/internal/controller/issuerconfig"
|
||||
"go.pinniped.dev/internal/controllerlib"
|
||||
"go.pinniped.dev/internal/dynamiccert"
|
||||
"go.pinniped.dev/internal/plog"
|
||||
"go.pinniped.dev/internal/endpointaddr"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -51,9 +57,9 @@ const (
|
||||
|
||||
type impersonatorConfigController struct {
|
||||
namespace string
|
||||
configMapResourceName string
|
||||
credentialIssuerResourceName string
|
||||
generatedLoadBalancerServiceName string
|
||||
generatedClusterIPServiceName string
|
||||
tlsSecretName string
|
||||
caSecretName string
|
||||
impersonationSignerSecretName string
|
||||
@ -61,7 +67,7 @@ type impersonatorConfigController struct {
|
||||
k8sClient kubernetes.Interface
|
||||
pinnipedAPIClient pinnipedclientset.Interface
|
||||
|
||||
configMapsInformer corev1informers.ConfigMapInformer
|
||||
credIssuerInformer conciergeconfiginformers.CredentialIssuerInformer
|
||||
servicesInformer corev1informers.ServiceInformer
|
||||
secretsInformer corev1informers.SecretInformer
|
||||
|
||||
@ -74,20 +80,21 @@ type impersonatorConfigController struct {
|
||||
serverStopCh chan struct{}
|
||||
errorCh chan error
|
||||
tlsServingCertDynamicCertProvider dynamiccert.Private
|
||||
infoLog logr.Logger
|
||||
debugLog logr.Logger
|
||||
}
|
||||
|
||||
func NewImpersonatorConfigController(
|
||||
namespace string,
|
||||
configMapResourceName string,
|
||||
credentialIssuerResourceName string,
|
||||
k8sClient kubernetes.Interface,
|
||||
pinnipedAPIClient pinnipedclientset.Interface,
|
||||
configMapsInformer corev1informers.ConfigMapInformer,
|
||||
credentialIssuerInformer conciergeconfiginformers.CredentialIssuerInformer,
|
||||
servicesInformer corev1informers.ServiceInformer,
|
||||
secretsInformer corev1informers.SecretInformer,
|
||||
withInformer pinnipedcontroller.WithInformerOptionFunc,
|
||||
withInitialEvent pinnipedcontroller.WithInitialEventOptionFunc,
|
||||
generatedLoadBalancerServiceName string,
|
||||
generatedClusterIPServiceName string,
|
||||
tlsSecretName string,
|
||||
caSecretName string,
|
||||
labels map[string]string,
|
||||
@ -95,22 +102,24 @@ func NewImpersonatorConfigController(
|
||||
impersonatorFunc impersonator.FactoryFunc,
|
||||
impersonationSignerSecretName string,
|
||||
impersonationSigningCertProvider dynamiccert.Provider,
|
||||
log logr.Logger,
|
||||
) controllerlib.Controller {
|
||||
secretNames := sets.NewString(tlsSecretName, caSecretName, impersonationSignerSecretName)
|
||||
log = log.WithName("impersonator-config-controller")
|
||||
return controllerlib.New(
|
||||
controllerlib.Config{
|
||||
Name: "impersonator-config-controller",
|
||||
Syncer: &impersonatorConfigController{
|
||||
namespace: namespace,
|
||||
configMapResourceName: configMapResourceName,
|
||||
credentialIssuerResourceName: credentialIssuerResourceName,
|
||||
generatedLoadBalancerServiceName: generatedLoadBalancerServiceName,
|
||||
generatedClusterIPServiceName: generatedClusterIPServiceName,
|
||||
tlsSecretName: tlsSecretName,
|
||||
caSecretName: caSecretName,
|
||||
impersonationSignerSecretName: impersonationSignerSecretName,
|
||||
k8sClient: k8sClient,
|
||||
pinnipedAPIClient: pinnipedAPIClient,
|
||||
configMapsInformer: configMapsInformer,
|
||||
credIssuerInformer: credentialIssuerInformer,
|
||||
servicesInformer: servicesInformer,
|
||||
secretsInformer: secretsInformer,
|
||||
labels: labels,
|
||||
@ -118,45 +127,48 @@ func NewImpersonatorConfigController(
|
||||
impersonationSigningCertProvider: impersonationSigningCertProvider,
|
||||
impersonatorFunc: impersonatorFunc,
|
||||
tlsServingCertDynamicCertProvider: dynamiccert.NewServingCert("impersonation-proxy-serving-cert"),
|
||||
infoLog: log.V(2),
|
||||
debugLog: log.V(4),
|
||||
},
|
||||
},
|
||||
withInformer(
|
||||
configMapsInformer,
|
||||
pinnipedcontroller.NameAndNamespaceExactMatchFilterFactory(configMapResourceName, namespace),
|
||||
withInformer(credentialIssuerInformer,
|
||||
pinnipedcontroller.SimpleFilterWithSingletonQueue(func(obj metav1.Object) bool {
|
||||
return obj.GetName() == credentialIssuerResourceName
|
||||
}),
|
||||
controllerlib.InformerOption{},
|
||||
),
|
||||
withInformer(
|
||||
servicesInformer,
|
||||
pinnipedcontroller.NameAndNamespaceExactMatchFilterFactory(generatedLoadBalancerServiceName, namespace),
|
||||
pinnipedcontroller.SimpleFilterWithSingletonQueue(func(obj metav1.Object) bool {
|
||||
return obj.GetNamespace() == namespace && obj.GetName() == generatedLoadBalancerServiceName
|
||||
}),
|
||||
controllerlib.InformerOption{},
|
||||
),
|
||||
withInformer(
|
||||
secretsInformer,
|
||||
pinnipedcontroller.SimpleFilter(func(obj metav1.Object) bool {
|
||||
pinnipedcontroller.SimpleFilterWithSingletonQueue(func(obj metav1.Object) bool {
|
||||
return obj.GetNamespace() == namespace && secretNames.Has(obj.GetName())
|
||||
}, nil),
|
||||
}),
|
||||
controllerlib.InformerOption{},
|
||||
),
|
||||
// Be sure to run once even if the ConfigMap that the informer is watching doesn't exist so we can implement
|
||||
// the default configuration behavior.
|
||||
withInitialEvent(controllerlib.Key{
|
||||
Namespace: namespace,
|
||||
Name: configMapResourceName,
|
||||
}),
|
||||
// TODO fix these controller options to make this a singleton queue
|
||||
)
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) Sync(syncCtx controllerlib.Context) error {
|
||||
plog.Debug("Starting impersonatorConfigController Sync")
|
||||
c.debugLog.Info("starting impersonatorConfigController Sync")
|
||||
|
||||
strategy, err := c.doSync(syncCtx)
|
||||
// Load the CredentialIssuer that we'll update with status.
|
||||
credIssuer, err := c.credIssuerInformer.Lister().Get(c.credentialIssuerResourceName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get CredentialIssuer to update: %w", err)
|
||||
}
|
||||
|
||||
strategy, err := c.doSync(syncCtx, credIssuer)
|
||||
if err != nil {
|
||||
strategy = &v1alpha1.CredentialIssuerStrategy{
|
||||
Type: v1alpha1.ImpersonationProxyStrategyType,
|
||||
Status: v1alpha1.ErrorStrategyStatus,
|
||||
Reason: v1alpha1.ErrorDuringSetupStrategyReason,
|
||||
Reason: strategyReasonForError(err),
|
||||
Message: err.Error(),
|
||||
LastUpdateTime: metav1.NewTime(c.clock.Now()),
|
||||
}
|
||||
@ -164,20 +176,31 @@ func (c *impersonatorConfigController) Sync(syncCtx controllerlib.Context) error
|
||||
c.clearSignerCA()
|
||||
}
|
||||
|
||||
updateStrategyErr := c.updateStrategy(syncCtx.Context, strategy)
|
||||
if updateStrategyErr != nil {
|
||||
plog.Error("error while updating the CredentialIssuer status", err)
|
||||
if err == nil {
|
||||
err = updateStrategyErr
|
||||
}
|
||||
}
|
||||
err = utilerrors.NewAggregate([]error{err, issuerconfig.Update(
|
||||
syncCtx.Context,
|
||||
c.pinnipedAPIClient,
|
||||
credIssuer,
|
||||
*strategy,
|
||||
)})
|
||||
|
||||
if err == nil {
|
||||
plog.Debug("Successfully finished impersonatorConfigController Sync")
|
||||
c.debugLog.Info("successfully finished impersonatorConfigController Sync")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// strategyReasonForError returns the proper v1alpha1.StrategyReason for a sync error. Some errors are occasionally
|
||||
// expected because there are multiple pods running, in these cases we should report a Pending reason and we'll
|
||||
// recover on a following sync.
|
||||
func strategyReasonForError(err error) v1alpha1.StrategyReason {
|
||||
switch {
|
||||
case k8serrors.IsConflict(err), k8serrors.IsAlreadyExists(err):
|
||||
return v1alpha1.PendingStrategyReason
|
||||
default:
|
||||
return v1alpha1.ErrorDuringSetupStrategyReason
|
||||
}
|
||||
}
|
||||
|
||||
type certNameInfo struct {
|
||||
// ready will be true when the certificate name information is known.
|
||||
// ready will be false when it is pending because we are waiting for a load balancer to get assigned an ip/hostname.
|
||||
@ -186,7 +209,7 @@ type certNameInfo struct {
|
||||
|
||||
// The IP address or hostname which was selected to be used as the name in the cert.
|
||||
// Either selectedIP or selectedHostname will be set, but not both.
|
||||
selectedIP net.IP
|
||||
selectedIPs []net.IP
|
||||
selectedHostname string
|
||||
|
||||
// The name of the endpoint to which a client should connect to talk to the impersonator.
|
||||
@ -194,10 +217,10 @@ type certNameInfo struct {
|
||||
clientEndpoint string
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) doSync(syncCtx controllerlib.Context) (*v1alpha1.CredentialIssuerStrategy, error) {
|
||||
func (c *impersonatorConfigController) doSync(syncCtx controllerlib.Context, credIssuer *v1alpha1.CredentialIssuer) (*v1alpha1.CredentialIssuerStrategy, error) {
|
||||
ctx := syncCtx.Context
|
||||
|
||||
config, err := c.loadImpersonationProxyConfiguration()
|
||||
impersonationSpec, err := c.loadImpersonationProxyConfiguration(credIssuer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -212,21 +235,21 @@ func (c *impersonatorConfigController) doSync(syncCtx controllerlib.Context) (*v
|
||||
return nil, err
|
||||
}
|
||||
c.hasControlPlaneNodes = &hasControlPlaneNodes
|
||||
plog.Debug("Queried for control plane nodes", "foundControlPlaneNodes", hasControlPlaneNodes)
|
||||
c.debugLog.Info("queried for control plane nodes", "foundControlPlaneNodes", hasControlPlaneNodes)
|
||||
}
|
||||
|
||||
if c.shouldHaveImpersonator(config) {
|
||||
if c.shouldHaveImpersonator(impersonationSpec) {
|
||||
if err = c.ensureImpersonatorIsStarted(syncCtx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err = c.ensureImpersonatorIsStopped(true); err != nil {
|
||||
return nil, err // TODO write unit test that errors during stopping the server are returned by sync
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if c.shouldHaveLoadBalancer(config) {
|
||||
if err = c.ensureLoadBalancerIsStarted(ctx); err != nil {
|
||||
if c.shouldHaveLoadBalancer(impersonationSpec) {
|
||||
if err = c.ensureLoadBalancerIsStarted(ctx, impersonationSpec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
@ -235,7 +258,17 @@ func (c *impersonatorConfigController) doSync(syncCtx controllerlib.Context) (*v
|
||||
}
|
||||
}
|
||||
|
||||
nameInfo, err := c.findDesiredTLSCertificateName(config)
|
||||
if c.shouldHaveClusterIPService(impersonationSpec) {
|
||||
if err = c.ensureClusterIPServiceIsStarted(ctx, impersonationSpec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err = c.ensureClusterIPServiceIsStopped(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
nameInfo, err := c.findDesiredTLSCertificateName(impersonationSpec)
|
||||
if err != nil {
|
||||
// Unexpected error while determining the name that should go into the certs, so clear any existing certs.
|
||||
c.tlsServingCertDynamicCertProvider.UnsetCertKeyContent()
|
||||
@ -243,7 +276,7 @@ func (c *impersonatorConfigController) doSync(syncCtx controllerlib.Context) (*v
|
||||
}
|
||||
|
||||
var impersonationCA *certauthority.CA
|
||||
if c.shouldHaveTLSSecret(config) {
|
||||
if c.shouldHaveTLSSecret(impersonationSpec) {
|
||||
if impersonationCA, err = c.ensureCASecretIsCreated(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -254,7 +287,7 @@ func (c *impersonatorConfigController) doSync(syncCtx controllerlib.Context) (*v
|
||||
return nil, err
|
||||
}
|
||||
|
||||
credentialIssuerStrategyResult := c.doSyncResult(nameInfo, config, impersonationCA)
|
||||
credentialIssuerStrategyResult := c.doSyncResult(nameInfo, impersonationSpec, impersonationCA)
|
||||
|
||||
if err = c.loadSignerCA(credentialIssuerStrategyResult.Status); err != nil {
|
||||
return nil, err
|
||||
@ -263,64 +296,55 @@ func (c *impersonatorConfigController) doSync(syncCtx controllerlib.Context) (*v
|
||||
return credentialIssuerStrategyResult, nil
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) loadImpersonationProxyConfiguration() (*impersonator.Config, error) {
|
||||
configMap, err := c.configMapsInformer.Lister().ConfigMaps(c.namespace).Get(c.configMapResourceName)
|
||||
notFound := k8serrors.IsNotFound(err)
|
||||
if err != nil && !notFound {
|
||||
return nil, fmt.Errorf("failed to get %s/%s configmap: %w", c.namespace, c.configMapResourceName, err)
|
||||
func (c *impersonatorConfigController) loadImpersonationProxyConfiguration(credIssuer *v1alpha1.CredentialIssuer) (*v1alpha1.ImpersonationProxySpec, error) {
|
||||
// Make a copy of the spec since we got this object from informer cache.
|
||||
spec := credIssuer.Spec.DeepCopy().ImpersonationProxy
|
||||
if spec == nil {
|
||||
return nil, fmt.Errorf("could not load CredentialIssuer: spec.impersonationProxy is nil")
|
||||
}
|
||||
|
||||
var config *impersonator.Config
|
||||
if notFound {
|
||||
plog.Info("Did not find impersonation proxy config: using default config values",
|
||||
"configmap", c.configMapResourceName,
|
||||
"namespace", c.namespace,
|
||||
)
|
||||
config = impersonator.NewConfig() // use default configuration options
|
||||
} else {
|
||||
config, err = impersonator.ConfigFromConfigMap(configMap)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid impersonator configuration: %v", err)
|
||||
}
|
||||
plog.Info("Read impersonation proxy config",
|
||||
"configmap", c.configMapResourceName,
|
||||
"namespace", c.namespace,
|
||||
)
|
||||
// Default service type to LoadBalancer (this is normally already done via CRD defaulting).
|
||||
if spec.Service.Type == "" {
|
||||
spec.Service.Type = v1alpha1.ImpersonationProxyServiceTypeLoadBalancer
|
||||
}
|
||||
|
||||
return config, nil
|
||||
if err := validateCredentialIssuerSpec(spec); err != nil {
|
||||
return nil, fmt.Errorf("could not load CredentialIssuer spec.impersonationProxy: %w", err)
|
||||
}
|
||||
c.debugLog.Info("read impersonation proxy config", "credentialIssuer", c.credentialIssuerResourceName)
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) shouldHaveImpersonator(config *impersonator.Config) bool {
|
||||
return c.enabledByAutoMode(config) || config.Mode == impersonator.ModeEnabled
|
||||
func (c *impersonatorConfigController) shouldHaveImpersonator(config *v1alpha1.ImpersonationProxySpec) bool {
|
||||
return c.enabledByAutoMode(config) || config.Mode == v1alpha1.ImpersonationProxyModeEnabled
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) enabledByAutoMode(config *impersonator.Config) bool {
|
||||
return config.Mode == impersonator.ModeAuto && !*c.hasControlPlaneNodes
|
||||
func (c *impersonatorConfigController) enabledByAutoMode(config *v1alpha1.ImpersonationProxySpec) bool {
|
||||
return config.Mode == v1alpha1.ImpersonationProxyModeAuto && !*c.hasControlPlaneNodes
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) disabledByAutoMode(config *impersonator.Config) bool {
|
||||
return config.Mode == impersonator.ModeAuto && *c.hasControlPlaneNodes
|
||||
func (c *impersonatorConfigController) disabledByAutoMode(config *v1alpha1.ImpersonationProxySpec) bool {
|
||||
return config.Mode == v1alpha1.ImpersonationProxyModeAuto && *c.hasControlPlaneNodes
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) disabledExplicitly(config *impersonator.Config) bool {
|
||||
return config.Mode == impersonator.ModeDisabled
|
||||
func (c *impersonatorConfigController) disabledExplicitly(config *v1alpha1.ImpersonationProxySpec) bool {
|
||||
return config.Mode == v1alpha1.ImpersonationProxyModeDisabled
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) shouldHaveLoadBalancer(config *impersonator.Config) bool {
|
||||
return c.shouldHaveImpersonator(config) && !config.HasEndpoint()
|
||||
func (c *impersonatorConfigController) shouldHaveLoadBalancer(config *v1alpha1.ImpersonationProxySpec) bool {
|
||||
return c.shouldHaveImpersonator(config) && config.Service.Type == v1alpha1.ImpersonationProxyServiceTypeLoadBalancer
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) shouldHaveTLSSecret(config *impersonator.Config) bool {
|
||||
func (c *impersonatorConfigController) shouldHaveClusterIPService(config *v1alpha1.ImpersonationProxySpec) bool {
|
||||
return c.shouldHaveImpersonator(config) && config.Service.Type == v1alpha1.ImpersonationProxyServiceTypeClusterIP
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) shouldHaveTLSSecret(config *v1alpha1.ImpersonationProxySpec) bool {
|
||||
return c.shouldHaveImpersonator(config)
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) updateStrategy(ctx context.Context, strategy *v1alpha1.CredentialIssuerStrategy) error {
|
||||
return issuerconfig.UpdateStrategy(ctx, c.credentialIssuerResourceName, c.labels, c.pinnipedAPIClient, *strategy)
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) loadBalancerExists() (bool, error) {
|
||||
_, err := c.servicesInformer.Lister().Services(c.namespace).Get(c.generatedLoadBalancerServiceName)
|
||||
func (c *impersonatorConfigController) serviceExists(serviceName string) (bool, error) {
|
||||
_, err := c.servicesInformer.Lister().Services(c.namespace).Get(serviceName)
|
||||
notFound := k8serrors.IsNotFound(err)
|
||||
if notFound {
|
||||
return false, nil
|
||||
@ -367,7 +391,7 @@ func (c *impersonatorConfigController) ensureImpersonatorIsStarted(syncCtx contr
|
||||
}
|
||||
}
|
||||
|
||||
plog.Info("Starting impersonation proxy", "port", impersonationProxyPort)
|
||||
c.infoLog.Info("starting impersonation proxy", "port", impersonationProxyPort)
|
||||
startImpersonatorFunc, err := c.impersonatorFunc(
|
||||
impersonationProxyPort,
|
||||
c.tlsServingCertDynamicCertProvider,
|
||||
@ -402,7 +426,7 @@ func (c *impersonatorConfigController) ensureImpersonatorIsStopped(shouldCloseEr
|
||||
return nil
|
||||
}
|
||||
|
||||
plog.Info("Stopping impersonation proxy", "port", impersonationProxyPort)
|
||||
c.infoLog.Info("stopping impersonation proxy", "port", impersonationProxyPort)
|
||||
close(c.serverStopCh)
|
||||
stopErr := <-c.errorCh
|
||||
|
||||
@ -416,14 +440,7 @@ func (c *impersonatorConfigController) ensureImpersonatorIsStopped(shouldCloseEr
|
||||
return stopErr
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) ensureLoadBalancerIsStarted(ctx context.Context) error {
|
||||
running, err := c.loadBalancerExists()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if running {
|
||||
return nil
|
||||
}
|
||||
func (c *impersonatorConfigController) ensureLoadBalancerIsStarted(ctx context.Context, config *v1alpha1.ImpersonationProxySpec) error {
|
||||
appNameLabel := c.labels[appLabelKey]
|
||||
loadBalancer := v1.Service{
|
||||
Spec: v1.ServiceSpec{
|
||||
@ -435,26 +452,21 @@ func (c *impersonatorConfigController) ensureLoadBalancerIsStarted(ctx context.C
|
||||
Protocol: v1.ProtocolTCP,
|
||||
},
|
||||
},
|
||||
Selector: map[string]string{appLabelKey: appNameLabel},
|
||||
LoadBalancerIP: config.Service.LoadBalancerIP,
|
||||
Selector: map[string]string{appLabelKey: appNameLabel},
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: c.generatedLoadBalancerServiceName,
|
||||
Namespace: c.namespace,
|
||||
Labels: c.labels,
|
||||
Annotations: map[string]string{
|
||||
"service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "4000", // AWS' default is to time out after 60 seconds idle. Prevent that.
|
||||
},
|
||||
Name: c.generatedLoadBalancerServiceName,
|
||||
Namespace: c.namespace,
|
||||
Labels: c.labels,
|
||||
Annotations: config.Service.Annotations,
|
||||
},
|
||||
}
|
||||
plog.Info("creating load balancer for impersonation proxy",
|
||||
"service", c.generatedLoadBalancerServiceName,
|
||||
"namespace", c.namespace)
|
||||
_, err = c.k8sClient.CoreV1().Services(c.namespace).Create(ctx, &loadBalancer, metav1.CreateOptions{})
|
||||
return err
|
||||
return c.createOrUpdateService(ctx, &loadBalancer)
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) ensureLoadBalancerIsStopped(ctx context.Context) error {
|
||||
running, err := c.loadBalancerExists()
|
||||
running, err := c.serviceExists(c.generatedLoadBalancerServiceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -462,12 +474,82 @@ func (c *impersonatorConfigController) ensureLoadBalancerIsStopped(ctx context.C
|
||||
return nil
|
||||
}
|
||||
|
||||
plog.Info("Deleting load balancer for impersonation proxy",
|
||||
"service", c.generatedLoadBalancerServiceName,
|
||||
"namespace", c.namespace)
|
||||
c.infoLog.Info("deleting load balancer for impersonation proxy",
|
||||
"service", klog.KRef(c.namespace, c.generatedLoadBalancerServiceName),
|
||||
)
|
||||
return c.k8sClient.CoreV1().Services(c.namespace).Delete(ctx, c.generatedLoadBalancerServiceName, metav1.DeleteOptions{})
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) ensureClusterIPServiceIsStarted(ctx context.Context, config *v1alpha1.ImpersonationProxySpec) error {
|
||||
appNameLabel := c.labels[appLabelKey]
|
||||
clusterIP := v1.Service{
|
||||
Spec: v1.ServiceSpec{
|
||||
Type: v1.ServiceTypeClusterIP,
|
||||
Ports: []v1.ServicePort{
|
||||
{
|
||||
TargetPort: intstr.FromInt(impersonationProxyPort),
|
||||
Port: defaultHTTPSPort,
|
||||
Protocol: v1.ProtocolTCP,
|
||||
},
|
||||
},
|
||||
Selector: map[string]string{appLabelKey: appNameLabel},
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: c.generatedClusterIPServiceName,
|
||||
Namespace: c.namespace,
|
||||
Labels: c.labels,
|
||||
Annotations: config.Service.Annotations,
|
||||
},
|
||||
}
|
||||
return c.createOrUpdateService(ctx, &clusterIP)
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) ensureClusterIPServiceIsStopped(ctx context.Context) error {
|
||||
running, err := c.serviceExists(c.generatedClusterIPServiceName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !running {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.infoLog.Info("deleting cluster ip for impersonation proxy",
|
||||
"service", klog.KRef(c.namespace, c.generatedClusterIPServiceName),
|
||||
)
|
||||
return c.k8sClient.CoreV1().Services(c.namespace).Delete(ctx, c.generatedClusterIPServiceName, metav1.DeleteOptions{})
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) createOrUpdateService(ctx context.Context, service *v1.Service) error {
|
||||
log := c.infoLog.WithValues("serviceType", service.Spec.Type, "service", klog.KObj(service))
|
||||
existing, err := c.servicesInformer.Lister().Services(c.namespace).Get(service.Name)
|
||||
if k8serrors.IsNotFound(err) {
|
||||
log.Info("creating service for impersonation proxy")
|
||||
_, err := c.k8sClient.CoreV1().Services(c.namespace).Create(ctx, service, metav1.CreateOptions{})
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update only the specific fields that are meaningfully part of our desired state.
|
||||
updated := existing.DeepCopy()
|
||||
updated.ObjectMeta.Labels = service.ObjectMeta.Labels
|
||||
updated.ObjectMeta.Annotations = service.ObjectMeta.Annotations
|
||||
updated.Spec.LoadBalancerIP = service.Spec.LoadBalancerIP
|
||||
updated.Spec.Type = service.Spec.Type
|
||||
updated.Spec.Selector = service.Spec.Selector
|
||||
|
||||
// If our updates didn't change anything, we're done.
|
||||
if equality.Semantic.DeepEqual(existing, updated) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Otherwise apply the updates.
|
||||
c.infoLog.Info("updating service for impersonation proxy")
|
||||
_, err = c.k8sClient.CoreV1().Services(c.namespace).Update(ctx, updated, metav1.UpdateOptions{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) ensureTLSSecret(ctx context.Context, nameInfo *certNameInfo, ca *certauthority.CA) error {
|
||||
secretFromInformer, err := c.secretsInformer.Lister().Secrets(c.namespace).Get(c.tlsSecretName)
|
||||
notFound := k8serrors.IsNotFound(err)
|
||||
@ -494,10 +576,10 @@ func (c *impersonatorConfigController) deleteTLSSecretWhenCertificateDoesNotMatc
|
||||
certPEM := secret.Data[v1.TLSCertKey]
|
||||
block, _ := pem.Decode(certPEM)
|
||||
if block == nil {
|
||||
plog.Warning("Found missing or not PEM-encoded data in TLS Secret",
|
||||
c.infoLog.Info("found missing or not PEM-encoded data in TLS Secret",
|
||||
"invalidCertPEM", string(certPEM),
|
||||
"secret", c.tlsSecretName,
|
||||
"namespace", c.namespace)
|
||||
"secret", klog.KObj(secret),
|
||||
)
|
||||
deleteErr := c.ensureTLSSecretIsRemoved(ctx)
|
||||
if deleteErr != nil {
|
||||
return false, fmt.Errorf("found missing or not PEM-encoded data in TLS Secret, but got error while deleting it: %w", deleteErr)
|
||||
@ -507,10 +589,10 @@ func (c *impersonatorConfigController) deleteTLSSecretWhenCertificateDoesNotMatc
|
||||
|
||||
actualCertFromSecret, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
plog.Error("Found invalid PEM data in TLS Secret", err,
|
||||
c.infoLog.Error(err, "found missing or not PEM-encoded data in TLS Secret",
|
||||
"invalidCertPEM", string(certPEM),
|
||||
"secret", c.tlsSecretName,
|
||||
"namespace", c.namespace)
|
||||
"secret", klog.KObj(secret),
|
||||
)
|
||||
if err = c.ensureTLSSecretIsRemoved(ctx); err != nil {
|
||||
return false, fmt.Errorf("PEM data represented an invalid cert, but got error while deleting it: %w", err)
|
||||
}
|
||||
@ -520,9 +602,10 @@ func (c *impersonatorConfigController) deleteTLSSecretWhenCertificateDoesNotMatc
|
||||
keyPEM := secret.Data[v1.TLSPrivateKeyKey]
|
||||
_, err = tls.X509KeyPair(certPEM, keyPEM)
|
||||
if err != nil {
|
||||
plog.Error("Found invalid private key PEM data in TLS Secret", err,
|
||||
"secret", c.tlsSecretName,
|
||||
"namespace", c.namespace)
|
||||
c.infoLog.Error(err, "found invalid private key PEM data in TLS Secret",
|
||||
"invalidCertPEM", string(certPEM),
|
||||
"secret", klog.KObj(secret),
|
||||
)
|
||||
if err = c.ensureTLSSecretIsRemoved(ctx); err != nil {
|
||||
return false, fmt.Errorf("cert had an invalid private key, but got error while deleting it: %w", err)
|
||||
}
|
||||
@ -550,15 +633,15 @@ func (c *impersonatorConfigController) deleteTLSSecretWhenCertificateDoesNotMatc
|
||||
|
||||
actualIPs := actualCertFromSecret.IPAddresses
|
||||
actualHostnames := actualCertFromSecret.DNSNames
|
||||
plog.Info("Checking TLS certificate names",
|
||||
"desiredIP", nameInfo.selectedIP,
|
||||
c.infoLog.Info("checking TLS certificate names",
|
||||
"desiredIPs", nameInfo.selectedIPs,
|
||||
"desiredHostname", nameInfo.selectedHostname,
|
||||
"actualIPs", actualIPs,
|
||||
"actualHostnames", actualHostnames,
|
||||
"secret", c.tlsSecretName,
|
||||
"namespace", c.namespace)
|
||||
"secret", klog.KObj(secret),
|
||||
)
|
||||
|
||||
if certHostnameAndIPMatchDesiredState(nameInfo.selectedIP, actualIPs, nameInfo.selectedHostname, actualHostnames) {
|
||||
if certHostnameAndIPMatchDesiredState(nameInfo.selectedIPs, actualIPs, nameInfo.selectedHostname, actualHostnames) {
|
||||
// The cert already matches the desired state, so there is no need to delete/recreate it.
|
||||
return false, nil
|
||||
}
|
||||
@ -569,8 +652,13 @@ func (c *impersonatorConfigController) deleteTLSSecretWhenCertificateDoesNotMatc
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func certHostnameAndIPMatchDesiredState(desiredIP net.IP, actualIPs []net.IP, desiredHostname string, actualHostnames []string) bool {
|
||||
if desiredIP != nil && len(actualIPs) == 1 && desiredIP.Equal(actualIPs[0]) && len(actualHostnames) == 0 {
|
||||
func certHostnameAndIPMatchDesiredState(desiredIPs []net.IP, actualIPs []net.IP, desiredHostname string, actualHostnames []string) bool {
|
||||
if len(desiredIPs) > 0 && len(actualIPs) > 0 && len(actualIPs) == len(desiredIPs) && len(actualHostnames) == 0 {
|
||||
for i := range desiredIPs {
|
||||
if !actualIPs[i].Equal(desiredIPs[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
if desiredHostname != "" && len(actualHostnames) == 1 && desiredHostname == actualHostnames[0] && len(actualIPs) == 0 {
|
||||
@ -592,7 +680,7 @@ func (c *impersonatorConfigController) ensureTLSSecretIsCreatedAndLoaded(ctx con
|
||||
return nil
|
||||
}
|
||||
|
||||
newTLSSecret, err := c.createNewTLSSecret(ctx, ca, nameInfo.selectedIP, nameInfo.selectedHostname)
|
||||
newTLSSecret, err := c.createNewTLSSecret(ctx, ca, nameInfo.selectedIPs, nameInfo.selectedHostname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -650,9 +738,9 @@ func (c *impersonatorConfigController) createCASecret(ctx context.Context) (*cer
|
||||
Type: v1.SecretTypeOpaque,
|
||||
}
|
||||
|
||||
plog.Info("Creating CA certificates for impersonation proxy",
|
||||
"secret", c.caSecretName,
|
||||
"namespace", c.namespace)
|
||||
c.infoLog.Info("creating CA certificates for impersonation proxy",
|
||||
"secret", klog.KObj(&secret),
|
||||
)
|
||||
if _, err = c.k8sClient.CoreV1().Secrets(c.namespace).Create(ctx, &secret, metav1.CreateOptions{}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -660,21 +748,23 @@ func (c *impersonatorConfigController) createCASecret(ctx context.Context) (*cer
|
||||
return impersonationCA, nil
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) findDesiredTLSCertificateName(config *impersonator.Config) (*certNameInfo, error) {
|
||||
if config.HasEndpoint() {
|
||||
func (c *impersonatorConfigController) findDesiredTLSCertificateName(config *v1alpha1.ImpersonationProxySpec) (*certNameInfo, error) {
|
||||
if config.ExternalEndpoint != "" {
|
||||
return c.findTLSCertificateNameFromEndpointConfig(config), nil
|
||||
} else if config.Service.Type == v1alpha1.ImpersonationProxyServiceTypeClusterIP {
|
||||
return c.findTLSCertificateNameFromClusterIPService()
|
||||
}
|
||||
return c.findTLSCertificateNameFromLoadBalancer()
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) findTLSCertificateNameFromEndpointConfig(config *impersonator.Config) *certNameInfo {
|
||||
endpointMaybeWithPort := config.Endpoint
|
||||
endpointWithoutPort := strings.Split(endpointMaybeWithPort, ":")[0]
|
||||
parsedAsIP := net.ParseIP(endpointWithoutPort)
|
||||
if parsedAsIP != nil {
|
||||
return &certNameInfo{ready: true, selectedIP: parsedAsIP, clientEndpoint: endpointMaybeWithPort}
|
||||
func (c *impersonatorConfigController) findTLSCertificateNameFromEndpointConfig(config *v1alpha1.ImpersonationProxySpec) *certNameInfo {
|
||||
addr, _ := endpointaddr.Parse(config.ExternalEndpoint, 443)
|
||||
endpoint := strings.TrimSuffix(addr.Endpoint(), ":443")
|
||||
|
||||
if ip := net.ParseIP(addr.Host); ip != nil {
|
||||
return &certNameInfo{ready: true, selectedIPs: []net.IP{ip}, clientEndpoint: endpoint}
|
||||
}
|
||||
return &certNameInfo{ready: true, selectedHostname: endpointWithoutPort, clientEndpoint: endpointMaybeWithPort}
|
||||
return &certNameInfo{ready: true, selectedHostname: addr.Host, clientEndpoint: endpoint}
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) findTLSCertificateNameFromLoadBalancer() (*certNameInfo, error) {
|
||||
@ -689,9 +779,9 @@ func (c *impersonatorConfigController) findTLSCertificateNameFromLoadBalancer()
|
||||
}
|
||||
ingresses := lb.Status.LoadBalancer.Ingress
|
||||
if len(ingresses) == 0 || (ingresses[0].Hostname == "" && ingresses[0].IP == "") {
|
||||
plog.Info("load balancer for impersonation proxy does not have an ingress yet, so skipping tls cert generation while we wait",
|
||||
"service", c.generatedLoadBalancerServiceName,
|
||||
"namespace", c.namespace)
|
||||
c.infoLog.Info("load balancer for impersonation proxy does not have an ingress yet, so skipping tls cert generation while we wait",
|
||||
"service", klog.KObj(lb),
|
||||
)
|
||||
return &certNameInfo{ready: false}, nil
|
||||
}
|
||||
for _, ingress := range ingresses {
|
||||
@ -704,22 +794,45 @@ func (c *impersonatorConfigController) findTLSCertificateNameFromLoadBalancer()
|
||||
ip := ingress.IP
|
||||
parsedIP := net.ParseIP(ip)
|
||||
if parsedIP != nil {
|
||||
return &certNameInfo{ready: true, selectedIP: parsedIP, clientEndpoint: ip}, nil
|
||||
return &certNameInfo{ready: true, selectedIPs: []net.IP{parsedIP}, clientEndpoint: ip}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("could not find valid IP addresses or hostnames from load balancer %s/%s", c.namespace, lb.Name)
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) createNewTLSSecret(ctx context.Context, ca *certauthority.CA, ip net.IP, hostname string) (*v1.Secret, error) {
|
||||
func (c *impersonatorConfigController) findTLSCertificateNameFromClusterIPService() (*certNameInfo, error) {
|
||||
clusterIP, err := c.servicesInformer.Lister().Services(c.namespace).Get(c.generatedClusterIPServiceName)
|
||||
notFound := k8serrors.IsNotFound(err)
|
||||
if notFound {
|
||||
// We aren't ready and will try again later in this case.
|
||||
return &certNameInfo{ready: false}, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ip := clusterIP.Spec.ClusterIP
|
||||
ips := clusterIP.Spec.ClusterIPs
|
||||
if ip != "" {
|
||||
// clusterIP will always exist when clusterIPs does, but not vice versa
|
||||
var parsedIPs []net.IP
|
||||
if len(ips) > 0 {
|
||||
for _, ipFromIPs := range ips {
|
||||
parsedIPs = append(parsedIPs, net.ParseIP(ipFromIPs))
|
||||
}
|
||||
} else {
|
||||
parsedIPs = []net.IP{net.ParseIP(ip)}
|
||||
}
|
||||
return &certNameInfo{ready: true, selectedIPs: parsedIPs, clientEndpoint: ip}, nil
|
||||
}
|
||||
return &certNameInfo{ready: false}, nil
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) createNewTLSSecret(ctx context.Context, ca *certauthority.CA, ips []net.IP, hostname string) (*v1.Secret, error) {
|
||||
var hostnames []string
|
||||
var ips []net.IP
|
||||
if hostname != "" {
|
||||
hostnames = []string{hostname}
|
||||
}
|
||||
if ip != nil {
|
||||
ips = []net.IP{ip}
|
||||
}
|
||||
|
||||
impersonationCert, err := ca.IssueServerCert(hostnames, ips, approximatelyOneHundredYears)
|
||||
if err != nil {
|
||||
@ -744,11 +857,11 @@ func (c *impersonatorConfigController) createNewTLSSecret(ctx context.Context, c
|
||||
Type: v1.SecretTypeTLS,
|
||||
}
|
||||
|
||||
plog.Info("Creating TLS certificates for impersonation proxy",
|
||||
c.infoLog.Info("creating TLS certificates for impersonation proxy",
|
||||
"ips", ips,
|
||||
"hostnames", hostnames,
|
||||
"secret", c.tlsSecretName,
|
||||
"namespace", c.namespace)
|
||||
"secret", klog.KObj(newTLSSecret),
|
||||
)
|
||||
return c.k8sClient.CoreV1().Secrets(c.namespace).Create(ctx, newTLSSecret, metav1.CreateOptions{})
|
||||
}
|
||||
|
||||
@ -761,10 +874,10 @@ func (c *impersonatorConfigController) loadTLSCertFromSecret(tlsSecret *v1.Secre
|
||||
return fmt.Errorf("could not parse TLS cert PEM data from Secret: %w", err)
|
||||
}
|
||||
|
||||
plog.Info("Loading TLS certificates for impersonation proxy",
|
||||
c.infoLog.Info("loading TLS certificates for impersonation proxy",
|
||||
"certPEM", string(certPEM),
|
||||
"secret", c.tlsSecretName,
|
||||
"namespace", c.namespace)
|
||||
"secret", klog.KObj(tlsSecret),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -777,9 +890,9 @@ func (c *impersonatorConfigController) ensureTLSSecretIsRemoved(ctx context.Cont
|
||||
if !tlsSecretExists {
|
||||
return nil
|
||||
}
|
||||
plog.Info("Deleting TLS certificates for impersonation proxy",
|
||||
"secret", c.tlsSecretName,
|
||||
"namespace", c.namespace)
|
||||
c.infoLog.Info("deleting TLS certificates for impersonation proxy",
|
||||
"secret", klog.KRef(c.namespace, c.tlsSecretName),
|
||||
)
|
||||
err = c.k8sClient.CoreV1().Secrets(c.namespace).Delete(ctx, c.tlsSecretName, metav1.DeleteOptions{})
|
||||
notFound := k8serrors.IsNotFound(err)
|
||||
if notFound {
|
||||
@ -815,20 +928,20 @@ func (c *impersonatorConfigController) loadSignerCA(status v1alpha1.StrategyStat
|
||||
return fmt.Errorf("could not load the impersonator's credential signing secret: %w", err)
|
||||
}
|
||||
|
||||
plog.Info("Loading credential signing certificate for impersonation proxy",
|
||||
c.infoLog.Info("loading credential signing certificate for impersonation proxy",
|
||||
"certPEM", string(certPEM),
|
||||
"fromSecret", c.impersonationSignerSecretName,
|
||||
"namespace", c.namespace)
|
||||
"secret", klog.KObj(signingCertSecret),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) clearSignerCA() {
|
||||
plog.Info("Clearing credential signing certificate for impersonation proxy")
|
||||
c.debugLog.Info("clearing credential signing certificate for impersonation proxy")
|
||||
c.impersonationSigningCertProvider.UnsetCertKeyContent()
|
||||
}
|
||||
|
||||
func (c *impersonatorConfigController) doSyncResult(nameInfo *certNameInfo, config *impersonator.Config, ca *certauthority.CA) *v1alpha1.CredentialIssuerStrategy {
|
||||
func (c *impersonatorConfigController) doSyncResult(nameInfo *certNameInfo, config *v1alpha1.ImpersonationProxySpec, ca *certauthority.CA) *v1alpha1.CredentialIssuerStrategy {
|
||||
switch {
|
||||
case c.disabledExplicitly(config):
|
||||
return &v1alpha1.CredentialIssuerStrategy{
|
||||
@ -871,3 +984,46 @@ func (c *impersonatorConfigController) doSyncResult(nameInfo *certNameInfo, conf
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func validateCredentialIssuerSpec(spec *v1alpha1.ImpersonationProxySpec) error {
|
||||
// Validate that the mode is one of our known values.
|
||||
switch spec.Mode {
|
||||
case v1alpha1.ImpersonationProxyModeDisabled:
|
||||
case v1alpha1.ImpersonationProxyModeAuto:
|
||||
case v1alpha1.ImpersonationProxyModeEnabled:
|
||||
default:
|
||||
return fmt.Errorf("invalid proxy mode %q (expected auto, disabled, or enabled)", spec.Mode)
|
||||
}
|
||||
|
||||
// If disabled, ignore all other fields and consider the configuration valid.
|
||||
if spec.Mode == v1alpha1.ImpersonationProxyModeDisabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate that the service type is one of our known values.
|
||||
switch spec.Service.Type {
|
||||
case v1alpha1.ImpersonationProxyServiceTypeNone:
|
||||
case v1alpha1.ImpersonationProxyServiceTypeLoadBalancer:
|
||||
case v1alpha1.ImpersonationProxyServiceTypeClusterIP:
|
||||
default:
|
||||
return fmt.Errorf("invalid service type %q (expected None, LoadBalancer, or ClusterIP)", spec.Service.Type)
|
||||
}
|
||||
|
||||
// If specified, validate that the LoadBalancerIP is a valid IPv4 or IPv6 address.
|
||||
if ip := spec.Service.LoadBalancerIP; ip != "" && len(validation.IsValidIP(ip)) > 0 {
|
||||
return fmt.Errorf("invalid LoadBalancerIP %q", spec.Service.LoadBalancerIP)
|
||||
}
|
||||
|
||||
// If service is type "None", a non-empty external endpoint must be specified.
|
||||
if spec.ExternalEndpoint == "" && spec.Service.Type == v1alpha1.ImpersonationProxyServiceTypeNone {
|
||||
return fmt.Errorf("externalEndpoint must be set when service.type is None")
|
||||
}
|
||||
|
||||
if spec.ExternalEndpoint != "" {
|
||||
if _, err := endpointaddr.Parse(spec.ExternalEndpoint, 443); err != nil {
|
||||
return fmt.Errorf("invalid ExternalEndpoint %q: %w", spec.ExternalEndpoint, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,85 +0,0 @@
|
||||
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package issuerconfig
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/equality"
|
||||
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/util/retry"
|
||||
|
||||
configv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
|
||||
pinnipedclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
|
||||
)
|
||||
|
||||
func CreateOrUpdateCredentialIssuerStatus(
|
||||
ctx context.Context,
|
||||
credentialIssuerResourceName string,
|
||||
credentialIssuerLabels map[string]string,
|
||||
pinnipedClient pinnipedclientset.Interface,
|
||||
applyUpdatesToCredentialIssuerFunc func(configToUpdate *configv1alpha1.CredentialIssuerStatus),
|
||||
) error {
|
||||
err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
||||
existingCredentialIssuer, err := pinnipedClient.
|
||||
ConfigV1alpha1().
|
||||
CredentialIssuers().
|
||||
Get(ctx, credentialIssuerResourceName, metav1.GetOptions{})
|
||||
|
||||
notFound := k8serrors.IsNotFound(err)
|
||||
if err != nil && !notFound {
|
||||
return fmt.Errorf("get failed: %w", err)
|
||||
}
|
||||
|
||||
credentialIssuersClient := pinnipedClient.ConfigV1alpha1().CredentialIssuers()
|
||||
|
||||
if notFound {
|
||||
// create an empty credential issuer
|
||||
minCredentialIssuer := minimalValidCredentialIssuer(credentialIssuerResourceName, credentialIssuerLabels)
|
||||
|
||||
newCredentialIssuer, err := credentialIssuersClient.Create(ctx, minCredentialIssuer, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("create failed: %w", err)
|
||||
}
|
||||
|
||||
existingCredentialIssuer = newCredentialIssuer
|
||||
}
|
||||
|
||||
// check to see if we need to update the status
|
||||
credentialIssuer := existingCredentialIssuer.DeepCopy()
|
||||
applyUpdatesToCredentialIssuerFunc(&credentialIssuer.Status)
|
||||
|
||||
if equality.Semantic.DeepEqual(existingCredentialIssuer, credentialIssuer) {
|
||||
// Nothing interesting would change as a result of this update, so skip it
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, err := credentialIssuersClient.UpdateStatus(ctx, credentialIssuer, metav1.UpdateOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create or update credentialissuer: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func minimalValidCredentialIssuer(
|
||||
credentialIssuerName string,
|
||||
credentialIssuerLabels map[string]string,
|
||||
) *configv1alpha1.CredentialIssuer {
|
||||
return &configv1alpha1.CredentialIssuer{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: credentialIssuerName,
|
||||
Labels: credentialIssuerLabels,
|
||||
},
|
||||
}
|
||||
}
|
@ -1,301 +0,0 @@
|
||||
// Copyright 2020-2021 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package issuerconfig
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/sclevine/spec"
|
||||
"github.com/sclevine/spec/report"
|
||||
"github.com/stretchr/testify/require"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
coretesting "k8s.io/client-go/testing"
|
||||
apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1"
|
||||
|
||||
configv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
|
||||
pinnipedfake "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned/fake"
|
||||
)
|
||||
|
||||
func TestCreateOrUpdateCredentialIssuerStatus(t *testing.T) {
|
||||
spec.Run(t, "specs", func(t *testing.T, when spec.G, it spec.S) {
|
||||
var r *require.Assertions
|
||||
var ctx context.Context
|
||||
var pinnipedAPIClient *pinnipedfake.Clientset
|
||||
var credentialIssuerGVR schema.GroupVersionResource
|
||||
const credentialIssuerResourceName = "some-resource-name"
|
||||
|
||||
it.Before(func() {
|
||||
r = require.New(t)
|
||||
ctx = context.Background()
|
||||
pinnipedAPIClient = pinnipedfake.NewSimpleClientset()
|
||||
credentialIssuerGVR = schema.GroupVersionResource{
|
||||
Group: configv1alpha1.GroupName,
|
||||
Version: configv1alpha1.SchemeGroupVersion.Version,
|
||||
Resource: "credentialissuers",
|
||||
}
|
||||
})
|
||||
|
||||
when("the config does not exist", func() {
|
||||
it("creates a new config and then updates it with the func parameter", func() {
|
||||
err := CreateOrUpdateCredentialIssuerStatus(
|
||||
ctx,
|
||||
credentialIssuerResourceName,
|
||||
map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
},
|
||||
pinnipedAPIClient,
|
||||
func(configToUpdate *configv1alpha1.CredentialIssuerStatus) {
|
||||
configToUpdate.KubeConfigInfo = &configv1alpha1.CredentialIssuerKubeConfigInfo{
|
||||
CertificateAuthorityData: "some-ca-value",
|
||||
}
|
||||
},
|
||||
)
|
||||
r.NoError(err)
|
||||
|
||||
expectedGetAction := coretesting.NewRootGetAction(credentialIssuerGVR, credentialIssuerResourceName)
|
||||
|
||||
expectedCreateAction := coretesting.NewRootCreateAction(
|
||||
credentialIssuerGVR,
|
||||
&configv1alpha1.CredentialIssuer{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: credentialIssuerResourceName,
|
||||
Labels: map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
expectedUpdateAction := coretesting.NewRootUpdateSubresourceAction(credentialIssuerGVR, "status",
|
||||
&configv1alpha1.CredentialIssuer{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: credentialIssuerResourceName,
|
||||
Labels: map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
},
|
||||
},
|
||||
Status: configv1alpha1.CredentialIssuerStatus{
|
||||
KubeConfigInfo: &configv1alpha1.CredentialIssuerKubeConfigInfo{
|
||||
Server: "",
|
||||
CertificateAuthorityData: "some-ca-value",
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
r.Equal([]coretesting.Action{expectedGetAction, expectedCreateAction, expectedUpdateAction}, pinnipedAPIClient.Actions())
|
||||
})
|
||||
|
||||
when("there is an unexpected error while creating the existing object", func() {
|
||||
it.Before(func() {
|
||||
pinnipedAPIClient.PrependReactor("create", "credentialissuers", func(_ coretesting.Action) (bool, runtime.Object, error) {
|
||||
return true, nil, fmt.Errorf("error on create")
|
||||
})
|
||||
})
|
||||
|
||||
it("returns an error", func() {
|
||||
err := CreateOrUpdateCredentialIssuerStatus(
|
||||
ctx,
|
||||
credentialIssuerResourceName,
|
||||
map[string]string{},
|
||||
pinnipedAPIClient,
|
||||
func(configToUpdate *configv1alpha1.CredentialIssuerStatus) {},
|
||||
)
|
||||
r.EqualError(err, "could not create or update credentialissuer: create failed: error on create")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
when("the config already exists", func() {
|
||||
var existingConfig *configv1alpha1.CredentialIssuer
|
||||
|
||||
it.Before(func() {
|
||||
existingConfig = &configv1alpha1.CredentialIssuer{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: credentialIssuerResourceName,
|
||||
Labels: map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
},
|
||||
},
|
||||
Status: configv1alpha1.CredentialIssuerStatus{
|
||||
Strategies: []configv1alpha1.CredentialIssuerStrategy{
|
||||
{
|
||||
Type: configv1alpha1.KubeClusterSigningCertificateStrategyType,
|
||||
Status: configv1alpha1.SuccessStrategyStatus,
|
||||
Reason: configv1alpha1.FetchedKeyStrategyReason,
|
||||
Message: "initial-message",
|
||||
LastUpdateTime: metav1.Now(),
|
||||
},
|
||||
},
|
||||
KubeConfigInfo: &configv1alpha1.CredentialIssuerKubeConfigInfo{
|
||||
Server: "initial-server-value",
|
||||
CertificateAuthorityData: "initial-ca-value",
|
||||
},
|
||||
},
|
||||
}
|
||||
r.NoError(pinnipedAPIClient.Tracker().Add(existingConfig))
|
||||
})
|
||||
|
||||
it("updates the existing config to only apply the updates made by the func parameter", func() {
|
||||
err := CreateOrUpdateCredentialIssuerStatus(
|
||||
ctx,
|
||||
credentialIssuerResourceName,
|
||||
map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
},
|
||||
pinnipedAPIClient,
|
||||
func(configToUpdate *configv1alpha1.CredentialIssuerStatus) {
|
||||
configToUpdate.KubeConfigInfo.CertificateAuthorityData = "new-ca-value"
|
||||
},
|
||||
)
|
||||
r.NoError(err)
|
||||
|
||||
expectedGetAction := coretesting.NewRootGetAction(credentialIssuerGVR, credentialIssuerResourceName)
|
||||
|
||||
// Only the edited field should be changed.
|
||||
expectedUpdatedConfig := existingConfig.DeepCopy()
|
||||
expectedUpdatedConfig.Status.KubeConfigInfo.CertificateAuthorityData = "new-ca-value"
|
||||
expectedUpdateAction := coretesting.NewRootUpdateSubresourceAction(credentialIssuerGVR, "status", expectedUpdatedConfig)
|
||||
|
||||
r.Equal([]coretesting.Action{expectedGetAction, expectedUpdateAction}, pinnipedAPIClient.Actions())
|
||||
})
|
||||
|
||||
it("avoids the cost of an update if the local updates made by the func parameter did not actually change anything", func() {
|
||||
err := CreateOrUpdateCredentialIssuerStatus(
|
||||
ctx,
|
||||
credentialIssuerResourceName,
|
||||
map[string]string{},
|
||||
pinnipedAPIClient,
|
||||
func(configToUpdate *configv1alpha1.CredentialIssuerStatus) {
|
||||
configToUpdate.KubeConfigInfo.CertificateAuthorityData = "initial-ca-value"
|
||||
|
||||
t := configToUpdate.Strategies[0].LastUpdateTime
|
||||
loc, err := time.LoadLocation("Asia/Shanghai")
|
||||
r.NoError(err)
|
||||
configToUpdate.Strategies[0].LastUpdateTime = metav1.NewTime(t.In(loc))
|
||||
},
|
||||
)
|
||||
r.NoError(err)
|
||||
|
||||
expectedGetAction := coretesting.NewRootGetAction(credentialIssuerGVR, credentialIssuerResourceName)
|
||||
r.Equal([]coretesting.Action{expectedGetAction}, pinnipedAPIClient.Actions())
|
||||
})
|
||||
|
||||
when("there is an unexpected error while getting the existing object", func() {
|
||||
it.Before(func() {
|
||||
pinnipedAPIClient.PrependReactor("get", "credentialissuers", func(_ coretesting.Action) (bool, runtime.Object, error) {
|
||||
return true, nil, fmt.Errorf("error on get")
|
||||
})
|
||||
})
|
||||
|
||||
it("returns an error", func() {
|
||||
err := CreateOrUpdateCredentialIssuerStatus(
|
||||
ctx,
|
||||
credentialIssuerResourceName,
|
||||
map[string]string{},
|
||||
pinnipedAPIClient,
|
||||
func(configToUpdate *configv1alpha1.CredentialIssuerStatus) {},
|
||||
)
|
||||
r.EqualError(err, "could not create or update credentialissuer: get failed: error on get")
|
||||
})
|
||||
})
|
||||
|
||||
when("there is an unexpected error while updating the existing object", func() {
|
||||
it.Before(func() {
|
||||
pinnipedAPIClient.PrependReactor("update", "credentialissuers", func(_ coretesting.Action) (bool, runtime.Object, error) {
|
||||
return true, nil, fmt.Errorf("error on update")
|
||||
})
|
||||
})
|
||||
|
||||
it("returns an error", func() {
|
||||
err := CreateOrUpdateCredentialIssuerStatus(
|
||||
ctx,
|
||||
credentialIssuerResourceName,
|
||||
map[string]string{},
|
||||
pinnipedAPIClient,
|
||||
func(configToUpdate *configv1alpha1.CredentialIssuerStatus) {
|
||||
configToUpdate.KubeConfigInfo.CertificateAuthorityData = "new-ca-value"
|
||||
},
|
||||
)
|
||||
r.EqualError(err, "could not create or update credentialissuer: error on update")
|
||||
})
|
||||
})
|
||||
|
||||
when("there is a conflict error while updating the existing object on the first try and the next try succeeds", func() {
|
||||
var slightlyDifferentExistingConfig *configv1alpha1.CredentialIssuer
|
||||
|
||||
it.Before(func() {
|
||||
hit := false
|
||||
slightlyDifferentExistingConfig = existingConfig.DeepCopy()
|
||||
slightlyDifferentExistingConfig.Status.KubeConfigInfo.Server = "some-other-server-value-from-conflicting-update"
|
||||
|
||||
pinnipedAPIClient.PrependReactor("update", "credentialissuers", func(_ coretesting.Action) (bool, runtime.Object, error) {
|
||||
// Return an error on the first call, then fall through to the default (successful) response.
|
||||
if !hit {
|
||||
// Before the update fails, also change the object that will be returned by the next Get(),
|
||||
// to make sure that the production code does a fresh Get() after detecting a conflict.
|
||||
r.NoError(pinnipedAPIClient.Tracker().Update(credentialIssuerGVR, slightlyDifferentExistingConfig, ""))
|
||||
hit = true
|
||||
return true, nil, apierrors.NewConflict(schema.GroupResource{
|
||||
Group: apiregistrationv1.GroupName,
|
||||
Resource: "credentialissuers",
|
||||
}, "alphav1.pinniped.dev", fmt.Errorf("there was a conflict"))
|
||||
}
|
||||
return false, nil, nil
|
||||
})
|
||||
})
|
||||
|
||||
it("retries updates on conflict", func() {
|
||||
err := CreateOrUpdateCredentialIssuerStatus(
|
||||
ctx,
|
||||
credentialIssuerResourceName,
|
||||
map[string]string{
|
||||
"myLabelKey1": "myLabelValue1",
|
||||
"myLabelKey2": "myLabelValue2",
|
||||
},
|
||||
pinnipedAPIClient,
|
||||
func(configToUpdate *configv1alpha1.CredentialIssuerStatus) {
|
||||
configToUpdate.KubeConfigInfo.CertificateAuthorityData = "new-ca-value"
|
||||
},
|
||||
)
|
||||
r.NoError(err)
|
||||
|
||||
expectedGetAction := coretesting.NewRootGetAction(credentialIssuerGVR, credentialIssuerResourceName)
|
||||
|
||||
// The first attempted update only includes its own edits.
|
||||
firstExpectedUpdatedConfig := existingConfig.DeepCopy()
|
||||
firstExpectedUpdatedConfig.Status.KubeConfigInfo.CertificateAuthorityData = "new-ca-value"
|
||||
firstExpectedUpdateAction := coretesting.NewRootUpdateSubresourceAction(credentialIssuerGVR, "status", firstExpectedUpdatedConfig)
|
||||
|
||||
// Both the edits made by this update and the edits made by the conflicting update should be included.
|
||||
secondExpectedUpdatedConfig := existingConfig.DeepCopy()
|
||||
secondExpectedUpdatedConfig.Status.KubeConfigInfo.Server = "some-other-server-value-from-conflicting-update"
|
||||
secondExpectedUpdatedConfig.Status.KubeConfigInfo.CertificateAuthorityData = "new-ca-value"
|
||||
secondExpectedUpdateAction := coretesting.NewRootUpdateSubresourceAction(credentialIssuerGVR, "status", secondExpectedUpdatedConfig)
|
||||
|
||||
expectedActions := []coretesting.Action{
|
||||
expectedGetAction,
|
||||
firstExpectedUpdateAction,
|
||||
expectedGetAction,
|
||||
secondExpectedUpdateAction,
|
||||
}
|
||||
r.Equal(expectedActions, pinnipedAPIClient.Actions())
|
||||
})
|
||||
})
|
||||
})
|
||||
}, spec.Parallel(), spec.Report(report.Terminal{}))
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package issuerconfig contains controller(s) for reconciling CredentialIssuer's.
|
||||
package issuerconfig
|
@ -1,6 +1,7 @@
|
||||
// Copyright 2021 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Package issuerconfig contains helpers for updating CredentialIssuer status entries.
|
||||
package issuerconfig
|
||||
|
||||
import (
|
||||
@ -15,23 +16,6 @@ import (
|
||||
"go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
|
||||
)
|
||||
|
||||
// UpdateStrategy creates or updates the desired strategy in the CredentialIssuer status.strategies field.
|
||||
// The CredentialIssuer will be created if it does not already exist.
|
||||
func UpdateStrategy(ctx context.Context,
|
||||
name string,
|
||||
credentialIssuerLabels map[string]string,
|
||||
pinnipedAPIClient versioned.Interface,
|
||||
strategy v1alpha1.CredentialIssuerStrategy,
|
||||
) error {
|
||||
return CreateOrUpdateCredentialIssuerStatus(
|
||||
ctx,
|
||||
name,
|
||||
credentialIssuerLabels,
|
||||
pinnipedAPIClient,
|
||||
func(configToUpdate *v1alpha1.CredentialIssuerStatus) { mergeStrategy(configToUpdate, strategy) },
|
||||
)
|
||||
}
|
||||
|
||||
// Update a strategy on an existing CredentialIssuer, merging into any existing strategy entries.
|
||||
func Update(ctx context.Context, client versioned.Interface, issuer *v1alpha1.CredentialIssuer, strategy v1alpha1.CredentialIssuerStrategy) error {
|
||||
// Update the existing object to merge in the new strategy.
|
||||
@ -58,7 +42,9 @@ func mergeStrategy(configToUpdate *v1alpha1.CredentialIssuerStatus, strategy v1a
|
||||
}
|
||||
}
|
||||
if existing != nil {
|
||||
strategy.DeepCopyInto(existing)
|
||||
if !equalExceptLastUpdated(existing, &strategy) {
|
||||
strategy.DeepCopyInto(existing)
|
||||
}
|
||||
} else {
|
||||
configToUpdate.Strategies = append(configToUpdate.Strategies, strategy)
|
||||
}
|
||||
@ -91,3 +77,11 @@ func (s sortableStrategies) Less(i, j int) bool {
|
||||
return s[i].Type < s[j].Type
|
||||
}
|
||||
func (s sortableStrategies) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
func equalExceptLastUpdated(s1, s2 *v1alpha1.CredentialIssuerStrategy) bool {
|
||||
s1 = s1.DeepCopy()
|
||||
s2 = s2.DeepCopy()
|
||||
s1.LastUpdateTime = metav1.Time{}
|
||||
s2.LastUpdateTime = metav1.Time{}
|
||||
return apiequality.Semantic.DeepEqual(s1, s2)
|
||||
}
|
@ -125,6 +125,38 @@ func TestMergeStrategy(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "existing entry matches except for LastUpdated time",
|
||||
configToUpdate: v1alpha1.CredentialIssuerStatus{
|
||||
Strategies: []v1alpha1.CredentialIssuerStrategy{
|
||||
{
|
||||
Type: "Type1",
|
||||
Status: v1alpha1.ErrorStrategyStatus,
|
||||
Reason: "some starting reason",
|
||||
Message: "some starting message",
|
||||
LastUpdateTime: t1,
|
||||
},
|
||||
},
|
||||
},
|
||||
strategy: v1alpha1.CredentialIssuerStrategy{
|
||||
Type: "Type1",
|
||||
Status: v1alpha1.ErrorStrategyStatus,
|
||||
Reason: "some starting reason",
|
||||
Message: "some starting message",
|
||||
LastUpdateTime: t2,
|
||||
},
|
||||
expected: v1alpha1.CredentialIssuerStatus{
|
||||
Strategies: []v1alpha1.CredentialIssuerStrategy{
|
||||
{
|
||||
Type: "Type1",
|
||||
Status: v1alpha1.ErrorStrategyStatus,
|
||||
Reason: "some starting reason",
|
||||
Message: "some starting message",
|
||||
LastUpdateTime: t1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "new entry among others",
|
||||
configToUpdate: v1alpha1.CredentialIssuerStatus{
|
@ -252,16 +252,15 @@ func PrepareControllers(c *Config) (func(ctx context.Context), error) {
|
||||
WithController(
|
||||
impersonatorconfig.NewImpersonatorConfigController(
|
||||
c.ServerInstallationInfo.Namespace,
|
||||
c.NamesConfig.ImpersonationConfigMap,
|
||||
c.NamesConfig.CredentialIssuer,
|
||||
client.Kubernetes,
|
||||
client.PinnipedConcierge,
|
||||
informers.installationNamespaceK8s.Core().V1().ConfigMaps(),
|
||||
informers.pinniped.Config().V1alpha1().CredentialIssuers(),
|
||||
informers.installationNamespaceK8s.Core().V1().Services(),
|
||||
informers.installationNamespaceK8s.Core().V1().Secrets(),
|
||||
controllerlib.WithInformer,
|
||||
controllerlib.WithInitialEvent,
|
||||
c.NamesConfig.ImpersonationLoadBalancerService,
|
||||
c.NamesConfig.ImpersonationClusterIPService,
|
||||
c.NamesConfig.ImpersonationTLSCertificateSecret,
|
||||
c.NamesConfig.ImpersonationCACertificateSecret,
|
||||
c.Labels,
|
||||
@ -269,6 +268,7 @@ func PrepareControllers(c *Config) (func(ctx context.Context), error) {
|
||||
impersonator.New,
|
||||
c.NamesConfig.ImpersonationSignerSecret,
|
||||
c.ImpersonationSigningCertProvider,
|
||||
klogr.New(),
|
||||
),
|
||||
singletonWorker,
|
||||
).
|
||||
|
@ -799,7 +799,7 @@ func TestTokenExchange(t *testing.T) {
|
||||
require.NoError(t, json.Unmarshal(parsedJWT.UnsafePayloadWithoutVerification(), &tokenClaims))
|
||||
|
||||
// Make sure that these are the only fields in the token.
|
||||
idTokenFields := []string{"sub", "aud", "iss", "jti", "nonce", "auth_time", "exp", "iat", "rat", "groups", "username"}
|
||||
idTokenFields := []string{"sub", "aud", "iss", "jti", "auth_time", "exp", "iat", "rat", "groups", "username"}
|
||||
require.ElementsMatch(t, idTokenFields, getMapKeys(tokenClaims))
|
||||
|
||||
// Assert that the returned token has expected claims values.
|
||||
@ -808,7 +808,6 @@ func TestTokenExchange(t *testing.T) {
|
||||
require.NotEmpty(t, tokenClaims["exp"])
|
||||
require.NotEmpty(t, tokenClaims["iat"])
|
||||
require.NotEmpty(t, tokenClaims["rat"])
|
||||
require.Empty(t, tokenClaims["nonce"]) // ID tokens only contain nonce during an authcode exchange
|
||||
require.Len(t, tokenClaims["aud"], 1)
|
||||
require.Contains(t, tokenClaims["aud"], test.requestedAudience)
|
||||
require.Equal(t, goodSubject, tokenClaims["sub"])
|
||||
@ -1717,10 +1716,13 @@ func requireValidIDToken(
|
||||
// Note that there is a bug in fosite which prevents the `at_hash` claim from appearing in this ID token
|
||||
// during the initial authcode exchange, but does not prevent `at_hash` from appearing in the refreshed ID token.
|
||||
// We can add a workaround for this later.
|
||||
idTokenFields := []string{"sub", "aud", "iss", "jti", "nonce", "auth_time", "exp", "iat", "rat", "groups", "username"}
|
||||
idTokenFields := []string{"sub", "aud", "iss", "jti", "auth_time", "exp", "iat", "rat", "groups", "username"}
|
||||
if wantAtHashClaimInIDToken {
|
||||
idTokenFields = append(idTokenFields, "at_hash")
|
||||
}
|
||||
if wantNonceValueInIDToken {
|
||||
idTokenFields = append(idTokenFields, "nonce")
|
||||
}
|
||||
|
||||
// make sure that these are the only fields in the token
|
||||
var m map[string]interface{}
|
||||
|
@ -31,7 +31,6 @@ import (
|
||||
const (
|
||||
ldapsScheme = "ldaps"
|
||||
distinguishedNameAttributeName = "dn"
|
||||
commonNameAttributeName = "cn"
|
||||
searchFilterInterpolationLocationMarker = "{}"
|
||||
groupSearchPageSize = uint32(250)
|
||||
defaultLDAPPort = uint16(389)
|
||||
@ -367,7 +366,7 @@ func (p *Provider) searchGroupsForUserDN(conn Conn, userDN string) ([]string, er
|
||||
|
||||
groupAttributeName := p.c.GroupSearch.GroupNameAttribute
|
||||
if len(groupAttributeName) == 0 {
|
||||
groupAttributeName = commonNameAttributeName
|
||||
groupAttributeName = distinguishedNameAttributeName
|
||||
}
|
||||
|
||||
groups := []string{}
|
||||
@ -396,13 +395,27 @@ func (p *Provider) validateConfig() error {
|
||||
func (p *Provider) searchAndBindUser(conn Conn, username string, bindFunc func(conn Conn, foundUserDN string) error) (string, string, []string, error) {
|
||||
searchResult, err := conn.Search(p.userSearchRequest(username))
|
||||
if err != nil {
|
||||
return "", "", nil, fmt.Errorf(`error searching for user "%s": %w`, username, err)
|
||||
plog.All(`error searching for user`,
|
||||
"upstreamName", p.GetName(),
|
||||
"username", username,
|
||||
"err", err,
|
||||
)
|
||||
return "", "", nil, fmt.Errorf(`error searching for user: %w`, err)
|
||||
}
|
||||
if len(searchResult.Entries) == 0 {
|
||||
plog.Debug("error finding user: user not found (if this username is valid, please check the user search configuration)",
|
||||
"upstreamName", p.GetName(), "username", username)
|
||||
if plog.Enabled(plog.LevelAll) {
|
||||
plog.All("error finding user: user not found (if this username is valid, please check the user search configuration)",
|
||||
"upstreamName", p.GetName(),
|
||||
"username", username,
|
||||
)
|
||||
} else {
|
||||
plog.Debug("error finding user: user not found (cowardly avoiding printing username because log level is not 'all')", "upstreamName", p.GetName())
|
||||
}
|
||||
return "", "", nil, nil
|
||||
}
|
||||
|
||||
// At this point, we have matched at least one entry, so we can be confident that the username is not actually
|
||||
// someone's password mistakenly entered into the username field, so we can log it without concern.
|
||||
if len(searchResult.Entries) > 1 {
|
||||
return "", "", nil, fmt.Errorf(`searching for user "%s" resulted in %d search results, but expected 1 result`,
|
||||
username, len(searchResult.Entries),
|
||||
@ -493,7 +506,7 @@ func (p *Provider) userSearchRequestedAttributes() []string {
|
||||
func (p *Provider) groupSearchRequestedAttributes() []string {
|
||||
switch p.c.GroupSearch.GroupNameAttribute {
|
||||
case "":
|
||||
return []string{commonNameAttributeName}
|
||||
return []string{}
|
||||
case distinguishedNameAttributeName:
|
||||
return []string{}
|
||||
default:
|
||||
|
@ -315,6 +315,29 @@ func TestEndUserAuthentication(t *testing.T) {
|
||||
r.UID = base64.RawURLEncoding.EncodeToString([]byte(testUserSearchResultDNValue))
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "when the GroupNameAttribute is empty then it defaults to dn",
|
||||
username: testUpstreamUsername,
|
||||
password: testUpstreamPassword,
|
||||
providerConfig: providerConfig(func(p *ProviderConfig) {
|
||||
p.GroupSearch.GroupNameAttribute = "" // blank means to use dn
|
||||
}),
|
||||
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||
conn.EXPECT().Search(expectedUserSearch(nil)).Return(exampleUserSearchResult, nil).Times(1)
|
||||
conn.EXPECT().SearchWithPaging(expectedGroupSearch(func(r *ldap.SearchRequest) {
|
||||
r.Attributes = []string{}
|
||||
}), expectedGroupSearchPageSize).
|
||||
Return(exampleGroupSearchResult, nil).Times(1)
|
||||
conn.EXPECT().Close().Times(1)
|
||||
},
|
||||
bindEndUserMocks: func(conn *mockldapconn.MockConn) {
|
||||
conn.EXPECT().Bind(testUserSearchResultDNValue, testUpstreamPassword).Times(1)
|
||||
},
|
||||
wantAuthResponse: expectedAuthResponse(func(r *user.DefaultInfo) {
|
||||
r.Groups = []string{testGroupSearchResultDNValue1, testGroupSearchResultDNValue2}
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "when the GroupNameAttribute is dn",
|
||||
username: testUpstreamUsername,
|
||||
@ -339,11 +362,11 @@ func TestEndUserAuthentication(t *testing.T) {
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "when the GroupNameAttribute is empty then it defaults to cn",
|
||||
name: "when the GroupNameAttribute is cn",
|
||||
username: testUpstreamUsername,
|
||||
password: testUpstreamPassword,
|
||||
providerConfig: providerConfig(func(p *ProviderConfig) {
|
||||
p.GroupSearch.GroupNameAttribute = "" // blank means to use cn
|
||||
p.GroupSearch.GroupNameAttribute = "cn"
|
||||
}),
|
||||
searchMocks: func(conn *mockldapconn.MockConn) {
|
||||
conn.EXPECT().Bind(testBindUsername, testBindPassword).Times(1)
|
||||
@ -523,7 +546,7 @@ func TestEndUserAuthentication(t *testing.T) {
|
||||
conn.EXPECT().Search(expectedUserSearch(nil)).Return(nil, errors.New("some user search error")).Times(1)
|
||||
conn.EXPECT().Close().Times(1)
|
||||
},
|
||||
wantError: fmt.Sprintf(`error searching for user "%s": some user search error`, testUpstreamUsername),
|
||||
wantError: `error searching for user: some user search error`,
|
||||
},
|
||||
{
|
||||
name: "when searching for the user's groups returns an error",
|
||||
|
@ -18,6 +18,8 @@ You should have a [supported Kubernetes cluster]({{< ref "../reference/supported
|
||||
|
||||
- `kubectl apply -f https://get.pinniped.dev/latest/install-pinniped-concierge.yaml`
|
||||
|
||||
Warning: the default configuration may create a public LoadBalancer Service on your cluster.
|
||||
|
||||
## With specific version and default options
|
||||
|
||||
1. Choose your preferred [release](https://github.com/vmware-tanzu/pinniped/releases) version number and use it to replace the version number in the URL below.
|
||||
|
@ -13,3 +13,6 @@ capabilities:
|
||||
# Does the cluster allow requests without authentication?
|
||||
# https://kubernetes.io/docs/reference/access-authn-authz/authentication/#anonymous-requests
|
||||
anonymousAuthenticationSupported: false
|
||||
|
||||
# Are LDAP ports on the Internet reachable without interference from network firewalls or proxies?
|
||||
canReachInternetLDAPPorts: true
|
||||
|
@ -13,3 +13,6 @@ capabilities:
|
||||
# Does the cluster allow requests without authentication?
|
||||
# https://kubernetes.io/docs/reference/access-authn-authz/authentication/#anonymous-requests
|
||||
anonymousAuthenticationSupported: true
|
||||
|
||||
# Are LDAP ports on the Internet reachable without interference from network firewalls or proxies?
|
||||
canReachInternetLDAPPorts: true
|
||||
|
@ -13,3 +13,6 @@ capabilities:
|
||||
# Does the cluster allow requests without authentication?
|
||||
# https://kubernetes.io/docs/reference/access-authn-authz/authentication/#anonymous-requests
|
||||
anonymousAuthenticationSupported: true
|
||||
|
||||
# Are LDAP ports on the Internet reachable without interference from network firewalls or proxies?
|
||||
canReachInternetLDAPPorts: true
|
||||
|
@ -13,3 +13,6 @@ capabilities:
|
||||
# Does the cluster allow requests without authentication?
|
||||
# https://kubernetes.io/docs/reference/access-authn-authz/authentication/#anonymous-requests
|
||||
anonymousAuthenticationSupported: true
|
||||
|
||||
# Are LDAP ports on the Internet reachable without interference from network firewalls or proxies?
|
||||
canReachInternetLDAPPorts: true
|
||||
|
@ -13,3 +13,6 @@ capabilities:
|
||||
# Does the cluster allow requests without authentication?
|
||||
# https://kubernetes.io/docs/reference/access-authn-authz/authentication/#anonymous-requests
|
||||
anonymousAuthenticationSupported: true
|
||||
|
||||
# Are LDAP ports on the Internet reachable without interference from network firewalls or proxies?
|
||||
canReachInternetLDAPPorts: false
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
@ -51,13 +52,12 @@ import (
|
||||
"k8s.io/client-go/util/cert"
|
||||
"k8s.io/client-go/util/certificate/csr"
|
||||
"k8s.io/client-go/util/keyutil"
|
||||
"sigs.k8s.io/yaml"
|
||||
"k8s.io/client-go/util/retry"
|
||||
|
||||
conciergev1alpha "go.pinniped.dev/generated/latest/apis/concierge/config/v1alpha1"
|
||||
identityv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/identity/v1alpha1"
|
||||
loginv1alpha1 "go.pinniped.dev/generated/latest/apis/concierge/login/v1alpha1"
|
||||
pinnipedconciergeclientset "go.pinniped.dev/generated/latest/client/concierge/clientset/versioned"
|
||||
"go.pinniped.dev/internal/concierge/impersonator"
|
||||
"go.pinniped.dev/internal/httputil/roundtripper"
|
||||
"go.pinniped.dev/internal/kubeclient"
|
||||
"go.pinniped.dev/internal/testutil"
|
||||
@ -121,7 +121,10 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl
|
||||
mostRecentTokenCredentialRequestResponse *loginv1alpha1.TokenCredentialRequest
|
||||
mostRecentTokenCredentialRequestResponseLock sync.Mutex
|
||||
)
|
||||
refreshCredential := func(t *testing.T, impersonationProxyURL string, impersonationProxyCACertPEM []byte) *loginv1alpha1.ClusterCredential {
|
||||
|
||||
refreshCredentialHelper := func(t *testing.T, client pinnipedconciergeclientset.Interface) *loginv1alpha1.ClusterCredential {
|
||||
t.Helper()
|
||||
|
||||
mostRecentTokenCredentialRequestResponseLock.Lock()
|
||||
defer mostRecentTokenCredentialRequestResponseLock.Unlock()
|
||||
if mostRecentTokenCredentialRequestResponse == nil || credentialAlmostExpired(t, mostRecentTokenCredentialRequestResponse) {
|
||||
@ -133,18 +136,13 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl
|
||||
// However, we issue short-lived certs, so this cert will only be valid for a few minutes.
|
||||
// Cache it until it is almost expired and then refresh it whenever it is close to expired.
|
||||
//
|
||||
// Also, use an anonymous client which goes through the impersonation proxy to make the request because that's
|
||||
// what would normally happen when a user is using a kubeconfig where the server is the impersonation proxy,
|
||||
// so it more closely simulates the normal use case, and also because we want this to work on AKS clusters
|
||||
// which do not allow anonymous requests.
|
||||
client := newAnonymousImpersonationProxyClient(t, impersonationProxyURL, impersonationProxyCACertPEM, nil).PinnipedConcierge
|
||||
require.Eventually(t, func() bool {
|
||||
mostRecentTokenCredentialRequestResponse, err = createTokenCredentialRequest(credentialRequestSpecWithWorkingCredentials, client)
|
||||
if err != nil {
|
||||
t.Logf("failed to make TokenCredentialRequest: %s", library.Sdump(err))
|
||||
return false
|
||||
}
|
||||
return true
|
||||
return mostRecentTokenCredentialRequestResponse.Status.Credential != nil
|
||||
}, 5*time.Minute, 5*time.Second)
|
||||
|
||||
require.Nil(t, mostRecentTokenCredentialRequestResponse.Status.Message,
|
||||
@ -156,36 +154,38 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl
|
||||
// tokens, we should revisit this test's rest config below.
|
||||
require.Empty(t, mostRecentTokenCredentialRequestResponse.Status.Credential.Token)
|
||||
}
|
||||
|
||||
return mostRecentTokenCredentialRequestResponse.Status.Credential
|
||||
}
|
||||
|
||||
oldConfigMap, err := adminClient.CoreV1().ConfigMaps(env.ConciergeNamespace).Get(ctx, impersonationProxyConfigMapName(env), metav1.GetOptions{})
|
||||
if !k8serrors.IsNotFound(err) {
|
||||
require.NoError(t, err) // other errors aside from NotFound are unexpected
|
||||
t.Logf("stashing a pre-existing configmap %s", oldConfigMap.Name)
|
||||
require.NoError(t, adminClient.CoreV1().ConfigMaps(env.ConciergeNamespace).Delete(ctx, impersonationProxyConfigMapName(env), metav1.DeleteOptions{}))
|
||||
refreshCredential := func(t *testing.T, impersonationProxyURL string, impersonationProxyCACertPEM []byte) *loginv1alpha1.ClusterCredential {
|
||||
// Use an anonymous client which goes through the impersonation proxy to make the request because that's
|
||||
// what would normally happen when a user is using a kubeconfig where the server is the impersonation proxy,
|
||||
// so it more closely simulates the normal use case, and also because we want this to work on AKS clusters
|
||||
// which do not allow anonymous requests.
|
||||
client := newAnonymousImpersonationProxyClient(t, impersonationProxyURL, impersonationProxyCACertPEM, nil).PinnipedConcierge
|
||||
return refreshCredentialHelper(t, client)
|
||||
}
|
||||
// At the end of the test, clean up the ConfigMap.
|
||||
|
||||
oldCredentialIssuer, err := adminConciergeClient.ConfigV1alpha1().CredentialIssuers().Get(ctx, credentialIssuerName(env), metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
// At the end of the test, clean up the CredentialIssuer
|
||||
t.Cleanup(func() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
|
||||
defer cancel()
|
||||
|
||||
// Delete any version that was created by this test.
|
||||
t.Logf("cleaning up configmap at end of test %s", impersonationProxyConfigMapName(env))
|
||||
err := adminClient.CoreV1().ConfigMaps(env.ConciergeNamespace).Delete(ctx, impersonationProxyConfigMapName(env), metav1.DeleteOptions{})
|
||||
if !k8serrors.IsNotFound(err) {
|
||||
require.NoError(t, err) // only not found errors are acceptable
|
||||
}
|
||||
|
||||
// Only recreate it if it already existed at the start of this test.
|
||||
if len(oldConfigMap.Data) != 0 {
|
||||
t.Log(oldConfigMap)
|
||||
oldConfigMap.UID = "" // cant have a UID yet
|
||||
oldConfigMap.ResourceVersion = ""
|
||||
t.Logf("restoring a pre-existing configmap %s", oldConfigMap.Name)
|
||||
_, err = adminClient.CoreV1().ConfigMaps(env.ConciergeNamespace).Create(ctx, oldConfigMap, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
t.Logf("cleaning up credentialissuer at end of test %s", credentialIssuerName(env))
|
||||
err = retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
||||
newCredentialIssuer, err := adminConciergeClient.ConfigV1alpha1().CredentialIssuers().Get(ctx, credentialIssuerName(env), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
oldCredentialIssuer.Spec.DeepCopyInto(&newCredentialIssuer.Spec)
|
||||
_, err = adminConciergeClient.ConfigV1alpha1().CredentialIssuers().Update(ctx, newCredentialIssuer, metav1.UpdateOptions{})
|
||||
return err
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// If we are running on an environment that has a load balancer, expect that the
|
||||
// CredentialIssuer will be updated eventually with a successful impersonation proxy frontend.
|
||||
@ -200,6 +200,15 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl
|
||||
// this point depending on the capabilities of the cluster under test. We handle each possible case here.
|
||||
switch {
|
||||
case impersonatorShouldHaveStartedAutomaticallyByDefault && clusterSupportsLoadBalancers:
|
||||
// configure the credential issuer spec to have the impersonation proxy in auto mode
|
||||
updateCredentialIssuer(ctx, t, env, adminConciergeClient, conciergev1alpha.CredentialIssuerSpec{
|
||||
ImpersonationProxy: &conciergev1alpha.ImpersonationProxySpec{
|
||||
Mode: conciergev1alpha.ImpersonationProxyModeAuto,
|
||||
Service: conciergev1alpha.ImpersonationProxyServiceSpec{
|
||||
Type: conciergev1alpha.ImpersonationProxyServiceTypeLoadBalancer,
|
||||
},
|
||||
},
|
||||
})
|
||||
// Auto mode should have decided that the impersonator will run and should have started a load balancer,
|
||||
// and we will be able to use the load balancer to access the impersonator. (e.g. GKE, AKS, EKS)
|
||||
// Check that load balancer has been automatically created by the impersonator's "auto" mode.
|
||||
@ -221,10 +230,11 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl
|
||||
requireDisabledStrategy(ctx, t, env, adminConciergeClient)
|
||||
|
||||
// Create configuration to make the impersonation proxy turn on with no endpoint (i.e. automatically create a load balancer).
|
||||
configMap := impersonationProxyConfigMapForConfig(t, env, impersonator.Config{Mode: impersonator.ModeEnabled})
|
||||
t.Logf("creating configmap %s", configMap.Name)
|
||||
_, err = adminClient.CoreV1().ConfigMaps(env.ConciergeNamespace).Create(ctx, &configMap, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
updateCredentialIssuer(ctx, t, env, adminConciergeClient, conciergev1alpha.CredentialIssuerSpec{
|
||||
ImpersonationProxy: &conciergev1alpha.ImpersonationProxySpec{
|
||||
Mode: conciergev1alpha.ImpersonationProxyModeEnabled,
|
||||
},
|
||||
})
|
||||
|
||||
default:
|
||||
// Auto mode should have decided that the impersonator will be disabled. We need to manually enable it.
|
||||
@ -246,13 +256,12 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl
|
||||
require.Truef(t, isErr, "wanted error %q to be service unavailable via squid error, but: %s", err, message)
|
||||
|
||||
// Create configuration to make the impersonation proxy turn on with a hard coded endpoint (without a load balancer).
|
||||
configMap := impersonationProxyConfigMapForConfig(t, env, impersonator.Config{
|
||||
Mode: impersonator.ModeEnabled,
|
||||
Endpoint: proxyServiceEndpoint,
|
||||
updateCredentialIssuer(ctx, t, env, adminConciergeClient, conciergev1alpha.CredentialIssuerSpec{
|
||||
ImpersonationProxy: &conciergev1alpha.ImpersonationProxySpec{
|
||||
Mode: conciergev1alpha.ImpersonationProxyModeEnabled,
|
||||
ExternalEndpoint: proxyServiceEndpoint,
|
||||
},
|
||||
})
|
||||
t.Logf("creating configmap %s", configMap.Name)
|
||||
_, err = adminClient.CoreV1().ConfigMaps(env.ConciergeNamespace).Create(ctx, &configMap, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// At this point the impersonator should be starting/running. When it is ready, the CredentialIssuer's
|
||||
@ -376,6 +385,24 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl
|
||||
t.Parallel()
|
||||
kubeconfigPath, envVarsWithProxy, _ := getImpersonationKubeconfig(t, env, impersonationProxyURL, impersonationProxyCACertPEM, credentialRequestSpecWithWorkingCredentials.Authenticator)
|
||||
|
||||
// set credential issuer to have new annotation to ensure the idle timeout is long enough on AWS.
|
||||
// this also ensures that updates to the credential issuer impersonation proxy spec go through
|
||||
if impersonatorShouldHaveStartedAutomaticallyByDefault && clusterSupportsLoadBalancers {
|
||||
updateCredentialIssuer(ctx, t, env, adminConciergeClient, conciergev1alpha.CredentialIssuerSpec{
|
||||
ImpersonationProxy: &conciergev1alpha.ImpersonationProxySpec{
|
||||
Mode: conciergev1alpha.ImpersonationProxyModeAuto,
|
||||
Service: conciergev1alpha.ImpersonationProxyServiceSpec{
|
||||
Type: conciergev1alpha.ImpersonationProxyServiceTypeLoadBalancer,
|
||||
Annotations: map[string]string{"service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "4000"},
|
||||
},
|
||||
},
|
||||
})
|
||||
// Wait until the annotation shows up on the load balancer
|
||||
library.RequireEventuallyWithoutError(t, func() (bool, error) {
|
||||
return loadBalancerHasAnnotations(ctx, env, adminClient, map[string]string{"service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout": "4000"})
|
||||
}, 30*time.Second, 500*time.Millisecond)
|
||||
}
|
||||
|
||||
// Run the kubectl port-forward command.
|
||||
timeout, cancelFunc := context.WithTimeout(ctx, 2*time.Minute)
|
||||
defer cancelFunc()
|
||||
@ -1181,18 +1208,47 @@ func TestImpersonationProxy(t *testing.T) { //nolint:gocyclo // yeah, it's compl
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("running impersonation proxy with ClusterIP service", func(t *testing.T) {
|
||||
if env.Proxy == "" {
|
||||
t.Skip("Skipping ClusterIP test because squid proxy is not present")
|
||||
}
|
||||
clusterIPServiceURL := fmt.Sprintf("%s.%s.svc.cluster.local", impersonationProxyClusterIPName(env), env.ConciergeNamespace)
|
||||
updateCredentialIssuer(ctx, t, env, adminConciergeClient, conciergev1alpha.CredentialIssuerSpec{
|
||||
ImpersonationProxy: &conciergev1alpha.ImpersonationProxySpec{
|
||||
Mode: conciergev1alpha.ImpersonationProxyModeEnabled,
|
||||
ExternalEndpoint: clusterIPServiceURL,
|
||||
Service: conciergev1alpha.ImpersonationProxyServiceSpec{
|
||||
Type: conciergev1alpha.ImpersonationProxyServiceTypeClusterIP,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// wait until the credential issuer is updated with the new url
|
||||
require.Eventually(t, func() bool {
|
||||
newImpersonationProxyURL, _ := performImpersonatorDiscovery(ctx, t, env, adminConciergeClient)
|
||||
return newImpersonationProxyURL == "https://"+clusterIPServiceURL
|
||||
}, 30*time.Second, 500*time.Millisecond)
|
||||
newImpersonationProxyURL, newImpersonationProxyCACertPEM := performImpersonatorDiscovery(ctx, t, env, adminConciergeClient)
|
||||
|
||||
anonymousClient := newAnonymousImpersonationProxyClientWithProxy(t, newImpersonationProxyURL, newImpersonationProxyCACertPEM, nil).PinnipedConcierge
|
||||
refreshedCredentials := refreshCredentialHelper(t, anonymousClient)
|
||||
|
||||
client := newImpersonationProxyClientWithCredentialsAndProxy(t, refreshedCredentials, newImpersonationProxyURL, newImpersonationProxyCACertPEM, nil).Kubernetes
|
||||
|
||||
// everything should work properly through the cluster ip service
|
||||
t.Run(
|
||||
"access as user",
|
||||
library.AccessAsUserTest(ctx, env.TestUser.ExpectedUsername, client),
|
||||
)
|
||||
})
|
||||
|
||||
t.Run("manually disabling the impersonation proxy feature", func(t *testing.T) {
|
||||
// Update configuration to force the proxy to disabled mode
|
||||
configMap := impersonationProxyConfigMapForConfig(t, env, impersonator.Config{Mode: impersonator.ModeDisabled})
|
||||
if clusterSupportsLoadBalancers {
|
||||
t.Logf("creating configmap %s", configMap.Name)
|
||||
_, err := adminClient.CoreV1().ConfigMaps(env.ConciergeNamespace).Create(ctx, &configMap, metav1.CreateOptions{})
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
t.Logf("updating configmap %s", configMap.Name)
|
||||
_, err := adminClient.CoreV1().ConfigMaps(env.ConciergeNamespace).Update(ctx, &configMap, metav1.UpdateOptions{})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
updateCredentialIssuer(ctx, t, env, adminConciergeClient, conciergev1alpha.CredentialIssuerSpec{
|
||||
ImpersonationProxy: &conciergev1alpha.ImpersonationProxySpec{
|
||||
Mode: conciergev1alpha.ImpersonationProxyModeDisabled,
|
||||
},
|
||||
})
|
||||
|
||||
if clusterSupportsLoadBalancers {
|
||||
// The load balancer should have been deleted when we disabled the impersonation proxy.
|
||||
@ -1450,19 +1506,19 @@ func kubeconfigProxyFunc(t *testing.T, squidProxyURL string) func(req *http.Requ
|
||||
}
|
||||
}
|
||||
|
||||
func impersonationProxyConfigMapForConfig(t *testing.T, env *library.TestEnv, config impersonator.Config) corev1.ConfigMap {
|
||||
func updateCredentialIssuer(ctx context.Context, t *testing.T, env *library.TestEnv, adminConciergeClient pinnipedconciergeclientset.Interface, spec conciergev1alpha.CredentialIssuerSpec) {
|
||||
t.Helper()
|
||||
|
||||
configString, err := yaml.Marshal(config)
|
||||
err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
|
||||
newCredentialIssuer, err := adminConciergeClient.ConfigV1alpha1().CredentialIssuers().Get(ctx, credentialIssuerName(env), metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
spec.DeepCopyInto(&newCredentialIssuer.Spec)
|
||||
_, err = adminConciergeClient.ConfigV1alpha1().CredentialIssuers().Update(ctx, newCredentialIssuer, metav1.UpdateOptions{})
|
||||
return err
|
||||
})
|
||||
require.NoError(t, err)
|
||||
configMap := corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: impersonationProxyConfigMapName(env),
|
||||
},
|
||||
Data: map[string]string{
|
||||
"config.yaml": string(configString),
|
||||
}}
|
||||
return configMap
|
||||
}
|
||||
|
||||
func hasImpersonationProxyLoadBalancerService(ctx context.Context, env *library.TestEnv, client kubernetes.Interface) (bool, error) {
|
||||
@ -1476,8 +1532,16 @@ func hasImpersonationProxyLoadBalancerService(ctx context.Context, env *library.
|
||||
return service.Spec.Type == corev1.ServiceTypeLoadBalancer, nil
|
||||
}
|
||||
|
||||
func impersonationProxyConfigMapName(env *library.TestEnv) string {
|
||||
return env.ConciergeAppName + "-impersonation-proxy-config"
|
||||
func loadBalancerHasAnnotations(ctx context.Context, env *library.TestEnv, client kubernetes.Interface, annotations map[string]string) (bool, error) {
|
||||
service, err := client.CoreV1().Services(env.ConciergeNamespace).Get(ctx, impersonationProxyLoadBalancerName(env), metav1.GetOptions{})
|
||||
if k8serrors.IsNotFound(err) {
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
hasExactAnnotations := reflect.DeepEqual(annotations, service.Annotations)
|
||||
return service.Spec.Type == corev1.ServiceTypeLoadBalancer && hasExactAnnotations, nil
|
||||
}
|
||||
|
||||
func impersonationProxyTLSSecretName(env *library.TestEnv) string {
|
||||
@ -1492,6 +1556,10 @@ func impersonationProxyLoadBalancerName(env *library.TestEnv) string {
|
||||
return env.ConciergeAppName + "-impersonation-proxy-load-balancer"
|
||||
}
|
||||
|
||||
func impersonationProxyClusterIPName(env *library.TestEnv) string {
|
||||
return env.ConciergeAppName + "-impersonation-proxy-cluster-ip"
|
||||
}
|
||||
|
||||
func credentialIssuerName(env *library.TestEnv) string {
|
||||
return env.ConciergeAppName + "-config"
|
||||
}
|
||||
@ -1643,6 +1711,29 @@ func newAnonymousImpersonationProxyClient(t *testing.T, impersonationProxyURL st
|
||||
return newImpersonationProxyClientWithCredentials(t, emptyCredentials, impersonationProxyURL, impersonationProxyCACertPEM, nestedImpersonationConfig)
|
||||
}
|
||||
|
||||
func newImpersonationProxyClientWithCredentialsAndProxy(t *testing.T, credentials *loginv1alpha1.ClusterCredential, impersonationProxyURL string, impersonationProxyCACertPEM []byte, nestedImpersonationConfig *rest.ImpersonationConfig) *kubeclient.Client {
|
||||
t.Helper()
|
||||
|
||||
env := library.IntegrationEnv(t)
|
||||
|
||||
kubeconfig := impersonationProxyRestConfig(credentials, impersonationProxyURL, impersonationProxyCACertPEM, nestedImpersonationConfig)
|
||||
kubeconfig.Proxy = kubeconfigProxyFunc(t, env.Proxy)
|
||||
return library.NewKubeclient(t, kubeconfig)
|
||||
}
|
||||
|
||||
// this uses a proxy in all cases, the other will only use it if you don't have load balancer capabilities.
|
||||
func newAnonymousImpersonationProxyClientWithProxy(t *testing.T, impersonationProxyURL string, impersonationProxyCACertPEM []byte, nestedImpersonationConfig *rest.ImpersonationConfig) *kubeclient.Client {
|
||||
t.Helper()
|
||||
env := library.IntegrationEnv(t)
|
||||
|
||||
emptyCredentials := &loginv1alpha1.ClusterCredential{}
|
||||
kubeconfig := impersonationProxyRestConfig(emptyCredentials, impersonationProxyURL, impersonationProxyCACertPEM, nestedImpersonationConfig)
|
||||
|
||||
kubeconfig.Proxy = kubeconfigProxyFunc(t, env.Proxy)
|
||||
|
||||
return library.NewKubeclient(t, kubeconfig)
|
||||
}
|
||||
|
||||
func impersonationProxyViaSquidKubeClientWithoutCredential(t *testing.T, proxyServiceEndpoint string) kubernetes.Interface {
|
||||
t.Helper()
|
||||
|
||||
|
@ -277,8 +277,12 @@ func TestE2EFullIntegration(t *testing.T) {
|
||||
|
||||
// Add an LDAP upstream IDP and try using it to authenticate during kubectl commands.
|
||||
t.Run("with Supervisor LDAP upstream IDP", func(t *testing.T) {
|
||||
if len(env.ToolsNamespace) == 0 && !env.HasCapability(library.CanReachInternetLDAPPorts) {
|
||||
t.Skip("LDAP integration test requires connectivity to an LDAP server")
|
||||
}
|
||||
|
||||
expectedUsername := env.SupervisorUpstreamLDAP.TestUserMailAttributeValue
|
||||
expectedGroups := env.SupervisorUpstreamLDAP.TestUserDirectGroupsCNs
|
||||
expectedGroups := env.SupervisorUpstreamLDAP.TestUserDirectGroupsDNs
|
||||
|
||||
// Create a ClusterRoleBinding to give our test user from the upstream read-only access to the cluster.
|
||||
library.CreateTestClusterRoleBinding(t,
|
||||
@ -321,7 +325,7 @@ func TestE2EFullIntegration(t *testing.T) {
|
||||
Base: env.SupervisorUpstreamLDAP.GroupSearchBase,
|
||||
Filter: "", // use the default value of "member={}"
|
||||
Attributes: idpv1alpha1.LDAPIdentityProviderGroupSearchAttributes{
|
||||
GroupName: "", // use the default value of "cn"
|
||||
GroupName: "", // use the default value of "dn"
|
||||
},
|
||||
},
|
||||
}, idpv1alpha1.LDAPPhaseReady)
|
||||
|
@ -228,6 +228,11 @@ func TestKubeClientOwnerRef(t *testing.T) {
|
||||
GenerateName: "owner-ref-test-",
|
||||
OwnerReferences: nil, // no owner refs set
|
||||
},
|
||||
Spec: conciergeconfigv1alpha1.CredentialIssuerSpec{
|
||||
ImpersonationProxy: &conciergeconfigv1alpha1.ImpersonationProxySpec{
|
||||
Mode: conciergeconfigv1alpha1.ImpersonationProxyModeDisabled,
|
||||
},
|
||||
},
|
||||
},
|
||||
metav1.CreateOptions{},
|
||||
)
|
||||
|
@ -243,6 +243,20 @@ func TestLDAPSearch(t *testing.T) {
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "using the default group name attribute, which is dn",
|
||||
username: "pinny",
|
||||
password: pinnyPassword,
|
||||
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) {
|
||||
p.GroupSearch.GroupNameAttribute = ""
|
||||
})),
|
||||
wantAuthResponse: &authenticator.Response{
|
||||
User: &user.DefaultInfo{Name: "pinny", UID: b64("1000"), Groups: []string{
|
||||
"cn=ball-game-players,ou=beach-groups,ou=groups,dc=pinniped,dc=dev",
|
||||
"cn=seals,ou=groups,dc=pinniped,dc=dev",
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "using some other custom group name attribute",
|
||||
username: "pinny",
|
||||
@ -334,7 +348,7 @@ func TestLDAPSearch(t *testing.T) {
|
||||
username: "pinny",
|
||||
password: pinnyPassword,
|
||||
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.Filter = "*" })),
|
||||
wantError: `error searching for user "pinny": LDAP Result Code 201 "Filter Compile Error": ldap: error parsing filter`,
|
||||
wantError: `error searching for user: LDAP Result Code 201 "Filter Compile Error": ldap: error parsing filter`,
|
||||
},
|
||||
{
|
||||
name: "when the group search filter does not compile",
|
||||
@ -350,7 +364,7 @@ func TestLDAPSearch(t *testing.T) {
|
||||
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) {
|
||||
p.UserSearch.Filter = "objectClass=*" // overly broad search filter
|
||||
})),
|
||||
wantError: `error searching for user "pinny": LDAP Result Code 4 "Size Limit Exceeded": `,
|
||||
wantError: `error searching for user: LDAP Result Code 4 "Size Limit Exceeded": `,
|
||||
},
|
||||
{
|
||||
name: "when the server is unreachable with TLS",
|
||||
@ -522,7 +536,7 @@ func TestLDAPSearch(t *testing.T) {
|
||||
username: "pinny",
|
||||
password: pinnyPassword,
|
||||
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.Base = "invalid-base" })),
|
||||
wantError: `error searching for user "pinny": LDAP Result Code 34 "Invalid DN Syntax": invalid DN`,
|
||||
wantError: `error searching for user: LDAP Result Code 34 "Invalid DN Syntax": invalid DN`,
|
||||
},
|
||||
{
|
||||
name: "when the group search base is invalid",
|
||||
@ -536,7 +550,7 @@ func TestLDAPSearch(t *testing.T) {
|
||||
username: "pinny",
|
||||
password: pinnyPassword,
|
||||
provider: upstreamldap.New(*providerConfig(func(p *upstreamldap.ProviderConfig) { p.UserSearch.Base = "ou=does-not-exist,dc=pinniped,dc=dev" })),
|
||||
wantError: `error searching for user "pinny": LDAP Result Code 32 "No Such Object": `,
|
||||
wantError: `error searching for user: LDAP Result Code 32 "No Such Object": `,
|
||||
},
|
||||
{
|
||||
name: "when the group search base does not exist",
|
||||
@ -675,8 +689,8 @@ func defaultProviderConfig(env *library.TestEnv, port string) *upstreamldap.Prov
|
||||
},
|
||||
GroupSearch: upstreamldap.GroupSearchConfig{
|
||||
Base: "ou=groups,dc=pinniped,dc=dev",
|
||||
Filter: "", // defaults to member={}
|
||||
GroupNameAttribute: "", // defaults to cn
|
||||
Filter: "", // defaults to member={}
|
||||
GroupNameAttribute: "cn", // defaults to dn, but here we set it to cn
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ func TestSupervisorLogin(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
maybeSkip func(t *testing.T)
|
||||
createIDP func(t *testing.T)
|
||||
requestAuthorization func(t *testing.T, downstreamAuthorizeURL, downstreamCallbackURL string, httpClient *http.Client)
|
||||
wantDownstreamIDTokenSubjectToMatch string
|
||||
@ -48,7 +49,7 @@ func TestSupervisorLogin(t *testing.T) {
|
||||
wantDownstreamIDTokenGroups []string
|
||||
}{
|
||||
{
|
||||
name: "oidc",
|
||||
name: "oidc with default username and groups claim settings",
|
||||
createIDP: func(t *testing.T) {
|
||||
t.Helper()
|
||||
library.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{
|
||||
@ -66,10 +67,41 @@ func TestSupervisorLogin(t *testing.T) {
|
||||
wantDownstreamIDTokenSubjectToMatch: regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
|
||||
// the ID token Username should include the upstream user ID after the upstream issuer name
|
||||
wantDownstreamIDTokenUsernameToMatch: regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
|
||||
},
|
||||
{
|
||||
name: "oidc with custom username and groups claim settings",
|
||||
createIDP: func(t *testing.T) {
|
||||
t.Helper()
|
||||
library.CreateTestOIDCIdentityProvider(t, idpv1alpha1.OIDCIdentityProviderSpec{
|
||||
Issuer: env.SupervisorUpstreamOIDC.Issuer,
|
||||
TLS: &idpv1alpha1.TLSSpec{
|
||||
CertificateAuthorityData: base64.StdEncoding.EncodeToString([]byte(env.SupervisorUpstreamOIDC.CABundle)),
|
||||
},
|
||||
Client: idpv1alpha1.OIDCClient{
|
||||
SecretName: library.CreateClientCredsSecret(t, env.SupervisorUpstreamOIDC.ClientID, env.SupervisorUpstreamOIDC.ClientSecret).Name,
|
||||
},
|
||||
Claims: idpv1alpha1.OIDCClaims{
|
||||
Username: env.SupervisorUpstreamOIDC.UsernameClaim,
|
||||
Groups: env.SupervisorUpstreamOIDC.GroupsClaim,
|
||||
},
|
||||
AuthorizationConfig: idpv1alpha1.OIDCAuthorizationConfig{
|
||||
AdditionalScopes: env.SupervisorUpstreamOIDC.AdditionalScopes,
|
||||
},
|
||||
}, idpv1alpha1.PhaseReady)
|
||||
},
|
||||
requestAuthorization: requestAuthorizationUsingOIDCIdentityProvider,
|
||||
wantDownstreamIDTokenSubjectToMatch: regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Issuer+"?sub=") + ".+",
|
||||
wantDownstreamIDTokenUsernameToMatch: regexp.QuoteMeta(env.SupervisorUpstreamOIDC.Username),
|
||||
wantDownstreamIDTokenGroups: env.SupervisorUpstreamOIDC.ExpectedGroups,
|
||||
},
|
||||
{
|
||||
name: "ldap with email as username and groups names as DNs and using an LDAP provider which supports TLS",
|
||||
maybeSkip: func(t *testing.T) {
|
||||
t.Helper()
|
||||
if len(env.ToolsNamespace) == 0 && !env.HasCapability(library.CanReachInternetLDAPPorts) {
|
||||
t.Skip("LDAP integration test requires connectivity to an LDAP server")
|
||||
}
|
||||
},
|
||||
createIDP: func(t *testing.T) {
|
||||
t.Helper()
|
||||
secret := library.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth,
|
||||
@ -129,6 +161,12 @@ func TestSupervisorLogin(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "ldap with CN as username and group names as CNs and using an LDAP provider which only supports StartTLS", // try another variation of configuration options
|
||||
maybeSkip: func(t *testing.T) {
|
||||
t.Helper()
|
||||
if len(env.ToolsNamespace) == 0 && !env.HasCapability(library.CanReachInternetLDAPPorts) {
|
||||
t.Skip("LDAP integration test requires connectivity to an LDAP server")
|
||||
}
|
||||
},
|
||||
createIDP: func(t *testing.T) {
|
||||
t.Helper()
|
||||
secret := library.CreateTestSecret(t, env.SupervisorNamespace, "ldap-service-account", v1.SecretTypeBasicAuth,
|
||||
@ -188,14 +226,16 @@ func TestSupervisorLogin(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
tt := test
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.maybeSkip(t)
|
||||
|
||||
testSupervisorLogin(t,
|
||||
test.createIDP,
|
||||
test.requestAuthorization,
|
||||
test.wantDownstreamIDTokenSubjectToMatch,
|
||||
test.wantDownstreamIDTokenUsernameToMatch,
|
||||
test.wantDownstreamIDTokenGroups,
|
||||
tt.createIDP,
|
||||
tt.requestAuthorization,
|
||||
tt.wantDownstreamIDTokenSubjectToMatch,
|
||||
tt.wantDownstreamIDTokenUsernameToMatch,
|
||||
tt.wantDownstreamIDTokenGroups,
|
||||
)
|
||||
})
|
||||
}
|
||||
@ -383,10 +423,11 @@ func testSupervisorLogin(
|
||||
refreshedTokenResponse, err := refreshSource.Token()
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedIDTokenClaims = append(expectedIDTokenClaims, "at_hash")
|
||||
// When refreshing, expect to get an "at_hash" claim, but no "nonce" claim.
|
||||
expectRefreshedIDTokenClaims := []string{"iss", "exp", "sub", "aud", "auth_time", "iat", "jti", "rat", "username", "groups", "at_hash"}
|
||||
verifyTokenResponse(t,
|
||||
refreshedTokenResponse, discovery, downstreamOAuth2Config, "",
|
||||
expectedIDTokenClaims, wantDownstreamIDTokenSubjectToMatch, wantDownstreamIDTokenUsernameToMatch, wantDownstreamIDTokenGroups)
|
||||
expectRefreshedIDTokenClaims, wantDownstreamIDTokenSubjectToMatch, wantDownstreamIDTokenUsernameToMatch, wantDownstreamIDTokenGroups)
|
||||
|
||||
require.NotEqual(t, tokenResponse.AccessToken, refreshedTokenResponse.AccessToken)
|
||||
require.NotEqual(t, tokenResponse.RefreshToken, refreshedTokenResponse.RefreshToken)
|
||||
|
@ -24,6 +24,7 @@ const (
|
||||
ClusterSigningKeyIsAvailable Capability = "clusterSigningKeyIsAvailable"
|
||||
AnonymousAuthenticationSupported Capability = "anonymousAuthenticationSupported"
|
||||
HasExternalLoadBalancerProvider Capability = "hasExternalLoadBalancerProvider"
|
||||
CanReachInternetLDAPPorts Capability = "canReachInternetLDAPPorts"
|
||||
)
|
||||
|
||||
// TestEnv captures all the external parameters consumed by our integration tests.
|
||||
|
Loading…
Reference in New Issue
Block a user