mirror of https://github.com/linkerd/linkerd2.git
Introduce AuthorizationPolicy CRDs (#8007)
Issue #7709 proposes new Custom Resource types to support generalized authorization policies: - `AuthorizationPolicy` - `MeshTLSAuthentication` - `NetworkAuthentication` This change introduces these CRDs to the default linkerd installation (via the `linkerd-crds` chart) and updates the policy controller's to handle these resource types. The policy admission controller validates that these resource reference only suppported types. This new functionality is tested at multiple levels: * `linkerd-policy-controller-k8s-index` includes unit tests for the indexer to test how events update the index; * `linkerd-policy-test` includes integration tests that run in-cluster to validate that the gRPC API updates as resources are manipulated; * `linkerd-policy-test` includes integration tests that exercise the admission controller's resource validation; and * `linkerd-policy-test` includes integration tests that ensure that proxies honor authorization resources. This change does NOT update Linkerd's control plane and extensions to use these new authorization primitives. Furthermore, the `linkerd` CLI does not yet support inspecting these new resource types. These enhancements will be made in followup changes. Signed-off-by: Oliver Gould <ver@buoyant.io>
This commit is contained in:
parent
24079ab076
commit
c1a1430d1a
|
@ -958,6 +958,7 @@ dependencies = [
|
|||
"futures",
|
||||
"linkerd-policy-controller-core",
|
||||
"linkerd2-proxy-api",
|
||||
"maplit",
|
||||
"tokio",
|
||||
"tonic",
|
||||
"tracing",
|
||||
|
@ -989,6 +990,7 @@ dependencies = [
|
|||
"kubert",
|
||||
"linkerd-policy-controller-core",
|
||||
"linkerd-policy-controller-k8s-api",
|
||||
"maplit",
|
||||
"parking_lot",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
|
|
|
@ -166,6 +166,9 @@ webhooks:
|
|||
apiGroups: ["policy.linkerd.io"]
|
||||
apiVersions: ["v1alpha1", "v1beta1"]
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- networkauthentications
|
||||
- meshtlsauthentications
|
||||
- serverauthorizations
|
||||
- servers
|
||||
sideEffects: None
|
||||
|
@ -190,6 +193,9 @@ rules:
|
|||
- apiGroups:
|
||||
- policy.linkerd.io
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- meshtlsauthentications
|
||||
- networkauthentications
|
||||
- servers
|
||||
- serverauthorizations
|
||||
verbs:
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: authorizationpolicies.policy.linkerd.io
|
||||
annotations:
|
||||
{{ include "partials.annotations.created-by" . }}
|
||||
labels:
|
||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
||||
linkerd.io/control-plane-ns: {{.Release.Namespace}}
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: AuthorizationPolicy
|
||||
plural: authorizationpolicies
|
||||
singular: authorizationpolicy
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
Authorizes clients to communicate with Linkerd-proxied server
|
||||
resources.
|
||||
type: object
|
||||
required: [targetRef, requiredAuthenticationRefs]
|
||||
properties:
|
||||
targetRef:
|
||||
description: >-
|
||||
TargetRef references a resource to which the authorization
|
||||
policy applies.
|
||||
type: object
|
||||
required: [kind, name]
|
||||
# Modified from the gateway API.
|
||||
# Copyright 2020 The Kubernetes Authors
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred.
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
requiredAuthenticationRefs:
|
||||
description: >-
|
||||
RequiredAuthenticationRefs enumerates a set of required
|
||||
authentications. ALL authentications must be satisfied for
|
||||
the authorization to apply. If any of the referred objects
|
||||
cannot be found, the authorization will be ignored.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [kind, name]
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
|
@ -0,0 +1,86 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: meshtlsauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
{{ include "partials.annotations.created-by" . }}
|
||||
labels:
|
||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
||||
linkerd.io/control-plane-ns: {{.Release.Namespace}}
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: MeshTLSAuthentication
|
||||
plural: meshtlsauthentications
|
||||
singular: meshtlsauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
MeshTLSAuthentication defines a list of authenticated client IDs
|
||||
to be referenced by an `AuthorizationPolicy`. If a client
|
||||
connection has the mutually-authenticated identity that matches
|
||||
ANY of the of the provided identities, the connection is
|
||||
considered authenticated.
|
||||
type: object
|
||||
oneOf:
|
||||
- required: [identities]
|
||||
- required: [identityRefs]
|
||||
properties:
|
||||
identities:
|
||||
description: >-
|
||||
Authorizes clients with the provided proxy identity strings
|
||||
(as provided via MTLS)
|
||||
|
||||
The `*` prefix can be used to match all identities in
|
||||
a domain. An identity string of `*` indicates that
|
||||
all authentication clients are authorized.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
pattern: '^(\*|[a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'
|
||||
identityRefs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- kind
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this refers to all resources of the specified Group
|
||||
and Kind in the specified namespace.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
|
@ -0,0 +1,53 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: networkauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
{{ include "partials.annotations.created-by" . }}
|
||||
labels:
|
||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
||||
linkerd.io/control-plane-ns: {{.Release.Namespace}}
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: NetworkAuthentication
|
||||
plural: networkauthentications
|
||||
singular: networkauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
NetworkAuthentication defines a list of authenticated client
|
||||
networks to be referenced by an `AuthorizationPolicy`. If a
|
||||
client connection originates from ANY of the of the provided
|
||||
networks, the connection is considered authenticated.
|
||||
type: object
|
||||
required: [networks]
|
||||
properties:
|
||||
networks:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [cidr]
|
||||
properties:
|
||||
cidr:
|
||||
description: >-
|
||||
The CIDR of the network to be authorized.
|
||||
type: string
|
||||
except:
|
||||
description: >-
|
||||
A list of IP networks/addresses not to be included in
|
||||
the above `cidr`.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
|
@ -61,6 +61,9 @@ Otherwise, you can use the --ignore-cluster flag to overwrite the existing globa
|
|||
|
||||
var (
|
||||
templatesCrdFiles = []string{
|
||||
"templates/policy/authorization-policy.yaml",
|
||||
"templates/policy/meshtls-authentication.yaml",
|
||||
"templates/policy/network-authentication.yaml",
|
||||
"templates/policy/server.yaml",
|
||||
"templates/policy/server-authorization.yaml",
|
||||
"templates/serviceprofile.yaml",
|
||||
|
|
|
@ -1,6 +1,244 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: authorizationpolicies.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: AuthorizationPolicy
|
||||
plural: authorizationpolicies
|
||||
singular: authorizationpolicy
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
Authorizes clients to communicate with Linkerd-proxied server
|
||||
resources.
|
||||
type: object
|
||||
required: [targetRef, requiredAuthenticationRefs]
|
||||
properties:
|
||||
targetRef:
|
||||
description: >-
|
||||
TargetRef references a resource to which the authorization
|
||||
policy applies.
|
||||
type: object
|
||||
required: [kind, name]
|
||||
# Modified from the gateway API.
|
||||
# Copyright 2020 The Kubernetes Authors
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred.
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
requiredAuthenticationRefs:
|
||||
description: >-
|
||||
RequiredAuthenticationRefs enumerates a set of required
|
||||
authentications. ALL authentications must be satisfied for
|
||||
the authorization to apply. If any of the referred objects
|
||||
cannot be found, the authorization will be ignored.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [kind, name]
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: meshtlsauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: MeshTLSAuthentication
|
||||
plural: meshtlsauthentications
|
||||
singular: meshtlsauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
MeshTLSAuthentication defines a list of authenticated client IDs
|
||||
to be referenced by an `AuthorizationPolicy`. If a client
|
||||
connection has the mutually-authenticated identity that matches
|
||||
ANY of the of the provided identities, the connection is
|
||||
considered authenticated.
|
||||
type: object
|
||||
oneOf:
|
||||
- required: [identities]
|
||||
- required: [identityRefs]
|
||||
properties:
|
||||
identities:
|
||||
description: >-
|
||||
Authorizes clients with the provided proxy identity strings
|
||||
(as provided via MTLS)
|
||||
|
||||
The `*` prefix can be used to match all identities in
|
||||
a domain. An identity string of `*` indicates that
|
||||
all authentication clients are authorized.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
pattern: '^(\*|[a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'
|
||||
identityRefs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- kind
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this refers to all resources of the specified Group
|
||||
and Kind in the specified namespace.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: networkauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: NetworkAuthentication
|
||||
plural: networkauthentications
|
||||
singular: networkauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
NetworkAuthentication defines a list of authenticated client
|
||||
networks to be referenced by an `AuthorizationPolicy`. If a
|
||||
client connection originates from ANY of the of the provided
|
||||
networks, the connection is considered authenticated.
|
||||
type: object
|
||||
required: [networks]
|
||||
properties:
|
||||
networks:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [cidr]
|
||||
properties:
|
||||
cidr:
|
||||
description: >-
|
||||
The CIDR of the network to be authorized.
|
||||
type: string
|
||||
except:
|
||||
description: >-
|
||||
A list of IP networks/addresses not to be included in
|
||||
the above `cidr`.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: servers.policy.linkerd.io
|
||||
annotations:
|
||||
|
@ -859,6 +1097,9 @@ webhooks:
|
|||
apiGroups: ["policy.linkerd.io"]
|
||||
apiVersions: ["v1alpha1", "v1beta1"]
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- networkauthentications
|
||||
- meshtlsauthentications
|
||||
- serverauthorizations
|
||||
- servers
|
||||
sideEffects: None
|
||||
|
@ -883,6 +1124,9 @@ rules:
|
|||
- apiGroups:
|
||||
- policy.linkerd.io
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- meshtlsauthentications
|
||||
- networkauthentications
|
||||
- servers
|
||||
- serverauthorizations
|
||||
verbs:
|
||||
|
|
|
@ -1,6 +1,244 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: authorizationpolicies.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: AuthorizationPolicy
|
||||
plural: authorizationpolicies
|
||||
singular: authorizationpolicy
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
Authorizes clients to communicate with Linkerd-proxied server
|
||||
resources.
|
||||
type: object
|
||||
required: [targetRef, requiredAuthenticationRefs]
|
||||
properties:
|
||||
targetRef:
|
||||
description: >-
|
||||
TargetRef references a resource to which the authorization
|
||||
policy applies.
|
||||
type: object
|
||||
required: [kind, name]
|
||||
# Modified from the gateway API.
|
||||
# Copyright 2020 The Kubernetes Authors
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred.
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
requiredAuthenticationRefs:
|
||||
description: >-
|
||||
RequiredAuthenticationRefs enumerates a set of required
|
||||
authentications. ALL authentications must be satisfied for
|
||||
the authorization to apply. If any of the referred objects
|
||||
cannot be found, the authorization will be ignored.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [kind, name]
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: meshtlsauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: MeshTLSAuthentication
|
||||
plural: meshtlsauthentications
|
||||
singular: meshtlsauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
MeshTLSAuthentication defines a list of authenticated client IDs
|
||||
to be referenced by an `AuthorizationPolicy`. If a client
|
||||
connection has the mutually-authenticated identity that matches
|
||||
ANY of the of the provided identities, the connection is
|
||||
considered authenticated.
|
||||
type: object
|
||||
oneOf:
|
||||
- required: [identities]
|
||||
- required: [identityRefs]
|
||||
properties:
|
||||
identities:
|
||||
description: >-
|
||||
Authorizes clients with the provided proxy identity strings
|
||||
(as provided via MTLS)
|
||||
|
||||
The `*` prefix can be used to match all identities in
|
||||
a domain. An identity string of `*` indicates that
|
||||
all authentication clients are authorized.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
pattern: '^(\*|[a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'
|
||||
identityRefs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- kind
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this refers to all resources of the specified Group
|
||||
and Kind in the specified namespace.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: networkauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: NetworkAuthentication
|
||||
plural: networkauthentications
|
||||
singular: networkauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
NetworkAuthentication defines a list of authenticated client
|
||||
networks to be referenced by an `AuthorizationPolicy`. If a
|
||||
client connection originates from ANY of the of the provided
|
||||
networks, the connection is considered authenticated.
|
||||
type: object
|
||||
required: [networks]
|
||||
properties:
|
||||
networks:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [cidr]
|
||||
properties:
|
||||
cidr:
|
||||
description: >-
|
||||
The CIDR of the network to be authorized.
|
||||
type: string
|
||||
except:
|
||||
description: >-
|
||||
A list of IP networks/addresses not to be included in
|
||||
the above `cidr`.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: servers.policy.linkerd.io
|
||||
annotations:
|
||||
|
@ -859,6 +1097,9 @@ webhooks:
|
|||
apiGroups: ["policy.linkerd.io"]
|
||||
apiVersions: ["v1alpha1", "v1beta1"]
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- networkauthentications
|
||||
- meshtlsauthentications
|
||||
- serverauthorizations
|
||||
- servers
|
||||
sideEffects: None
|
||||
|
@ -883,6 +1124,9 @@ rules:
|
|||
- apiGroups:
|
||||
- policy.linkerd.io
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- meshtlsauthentications
|
||||
- networkauthentications
|
||||
- servers
|
||||
- serverauthorizations
|
||||
verbs:
|
||||
|
|
|
@ -1,6 +1,244 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: authorizationpolicies.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: AuthorizationPolicy
|
||||
plural: authorizationpolicies
|
||||
singular: authorizationpolicy
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
Authorizes clients to communicate with Linkerd-proxied server
|
||||
resources.
|
||||
type: object
|
||||
required: [targetRef, requiredAuthenticationRefs]
|
||||
properties:
|
||||
targetRef:
|
||||
description: >-
|
||||
TargetRef references a resource to which the authorization
|
||||
policy applies.
|
||||
type: object
|
||||
required: [kind, name]
|
||||
# Modified from the gateway API.
|
||||
# Copyright 2020 The Kubernetes Authors
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred.
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
requiredAuthenticationRefs:
|
||||
description: >-
|
||||
RequiredAuthenticationRefs enumerates a set of required
|
||||
authentications. ALL authentications must be satisfied for
|
||||
the authorization to apply. If any of the referred objects
|
||||
cannot be found, the authorization will be ignored.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [kind, name]
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: meshtlsauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: MeshTLSAuthentication
|
||||
plural: meshtlsauthentications
|
||||
singular: meshtlsauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
MeshTLSAuthentication defines a list of authenticated client IDs
|
||||
to be referenced by an `AuthorizationPolicy`. If a client
|
||||
connection has the mutually-authenticated identity that matches
|
||||
ANY of the of the provided identities, the connection is
|
||||
considered authenticated.
|
||||
type: object
|
||||
oneOf:
|
||||
- required: [identities]
|
||||
- required: [identityRefs]
|
||||
properties:
|
||||
identities:
|
||||
description: >-
|
||||
Authorizes clients with the provided proxy identity strings
|
||||
(as provided via MTLS)
|
||||
|
||||
The `*` prefix can be used to match all identities in
|
||||
a domain. An identity string of `*` indicates that
|
||||
all authentication clients are authorized.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
pattern: '^(\*|[a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'
|
||||
identityRefs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- kind
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this refers to all resources of the specified Group
|
||||
and Kind in the specified namespace.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: networkauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: NetworkAuthentication
|
||||
plural: networkauthentications
|
||||
singular: networkauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
NetworkAuthentication defines a list of authenticated client
|
||||
networks to be referenced by an `AuthorizationPolicy`. If a
|
||||
client connection originates from ANY of the of the provided
|
||||
networks, the connection is considered authenticated.
|
||||
type: object
|
||||
required: [networks]
|
||||
properties:
|
||||
networks:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [cidr]
|
||||
properties:
|
||||
cidr:
|
||||
description: >-
|
||||
The CIDR of the network to be authorized.
|
||||
type: string
|
||||
except:
|
||||
description: >-
|
||||
A list of IP networks/addresses not to be included in
|
||||
the above `cidr`.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: servers.policy.linkerd.io
|
||||
annotations:
|
||||
|
@ -859,6 +1097,9 @@ webhooks:
|
|||
apiGroups: ["policy.linkerd.io"]
|
||||
apiVersions: ["v1alpha1", "v1beta1"]
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- networkauthentications
|
||||
- meshtlsauthentications
|
||||
- serverauthorizations
|
||||
- servers
|
||||
sideEffects: None
|
||||
|
@ -883,6 +1124,9 @@ rules:
|
|||
- apiGroups:
|
||||
- policy.linkerd.io
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- meshtlsauthentications
|
||||
- networkauthentications
|
||||
- servers
|
||||
- serverauthorizations
|
||||
verbs:
|
||||
|
|
|
@ -1,6 +1,244 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: authorizationpolicies.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: AuthorizationPolicy
|
||||
plural: authorizationpolicies
|
||||
singular: authorizationpolicy
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
Authorizes clients to communicate with Linkerd-proxied server
|
||||
resources.
|
||||
type: object
|
||||
required: [targetRef, requiredAuthenticationRefs]
|
||||
properties:
|
||||
targetRef:
|
||||
description: >-
|
||||
TargetRef references a resource to which the authorization
|
||||
policy applies.
|
||||
type: object
|
||||
required: [kind, name]
|
||||
# Modified from the gateway API.
|
||||
# Copyright 2020 The Kubernetes Authors
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred.
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
requiredAuthenticationRefs:
|
||||
description: >-
|
||||
RequiredAuthenticationRefs enumerates a set of required
|
||||
authentications. ALL authentications must be satisfied for
|
||||
the authorization to apply. If any of the referred objects
|
||||
cannot be found, the authorization will be ignored.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [kind, name]
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: meshtlsauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: MeshTLSAuthentication
|
||||
plural: meshtlsauthentications
|
||||
singular: meshtlsauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
MeshTLSAuthentication defines a list of authenticated client IDs
|
||||
to be referenced by an `AuthorizationPolicy`. If a client
|
||||
connection has the mutually-authenticated identity that matches
|
||||
ANY of the of the provided identities, the connection is
|
||||
considered authenticated.
|
||||
type: object
|
||||
oneOf:
|
||||
- required: [identities]
|
||||
- required: [identityRefs]
|
||||
properties:
|
||||
identities:
|
||||
description: >-
|
||||
Authorizes clients with the provided proxy identity strings
|
||||
(as provided via MTLS)
|
||||
|
||||
The `*` prefix can be used to match all identities in
|
||||
a domain. An identity string of `*` indicates that
|
||||
all authentication clients are authorized.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
pattern: '^(\*|[a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'
|
||||
identityRefs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- kind
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this refers to all resources of the specified Group
|
||||
and Kind in the specified namespace.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: networkauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: NetworkAuthentication
|
||||
plural: networkauthentications
|
||||
singular: networkauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
NetworkAuthentication defines a list of authenticated client
|
||||
networks to be referenced by an `AuthorizationPolicy`. If a
|
||||
client connection originates from ANY of the of the provided
|
||||
networks, the connection is considered authenticated.
|
||||
type: object
|
||||
required: [networks]
|
||||
properties:
|
||||
networks:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [cidr]
|
||||
properties:
|
||||
cidr:
|
||||
description: >-
|
||||
The CIDR of the network to be authorized.
|
||||
type: string
|
||||
except:
|
||||
description: >-
|
||||
A list of IP networks/addresses not to be included in
|
||||
the above `cidr`.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: servers.policy.linkerd.io
|
||||
annotations:
|
||||
|
@ -859,6 +1097,9 @@ webhooks:
|
|||
apiGroups: ["policy.linkerd.io"]
|
||||
apiVersions: ["v1alpha1", "v1beta1"]
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- networkauthentications
|
||||
- meshtlsauthentications
|
||||
- serverauthorizations
|
||||
- servers
|
||||
sideEffects: None
|
||||
|
@ -883,6 +1124,9 @@ rules:
|
|||
- apiGroups:
|
||||
- policy.linkerd.io
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- meshtlsauthentications
|
||||
- networkauthentications
|
||||
- servers
|
||||
- serverauthorizations
|
||||
verbs:
|
||||
|
|
|
@ -1,6 +1,244 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: authorizationpolicies.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: AuthorizationPolicy
|
||||
plural: authorizationpolicies
|
||||
singular: authorizationpolicy
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
Authorizes clients to communicate with Linkerd-proxied server
|
||||
resources.
|
||||
type: object
|
||||
required: [targetRef, requiredAuthenticationRefs]
|
||||
properties:
|
||||
targetRef:
|
||||
description: >-
|
||||
TargetRef references a resource to which the authorization
|
||||
policy applies.
|
||||
type: object
|
||||
required: [kind, name]
|
||||
# Modified from the gateway API.
|
||||
# Copyright 2020 The Kubernetes Authors
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred.
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
requiredAuthenticationRefs:
|
||||
description: >-
|
||||
RequiredAuthenticationRefs enumerates a set of required
|
||||
authentications. ALL authentications must be satisfied for
|
||||
the authorization to apply. If any of the referred objects
|
||||
cannot be found, the authorization will be ignored.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [kind, name]
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: meshtlsauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: MeshTLSAuthentication
|
||||
plural: meshtlsauthentications
|
||||
singular: meshtlsauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
MeshTLSAuthentication defines a list of authenticated client IDs
|
||||
to be referenced by an `AuthorizationPolicy`. If a client
|
||||
connection has the mutually-authenticated identity that matches
|
||||
ANY of the of the provided identities, the connection is
|
||||
considered authenticated.
|
||||
type: object
|
||||
oneOf:
|
||||
- required: [identities]
|
||||
- required: [identityRefs]
|
||||
properties:
|
||||
identities:
|
||||
description: >-
|
||||
Authorizes clients with the provided proxy identity strings
|
||||
(as provided via MTLS)
|
||||
|
||||
The `*` prefix can be used to match all identities in
|
||||
a domain. An identity string of `*` indicates that
|
||||
all authentication clients are authorized.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
pattern: '^(\*|[a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'
|
||||
identityRefs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- kind
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this refers to all resources of the specified Group
|
||||
and Kind in the specified namespace.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: networkauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: NetworkAuthentication
|
||||
plural: networkauthentications
|
||||
singular: networkauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
NetworkAuthentication defines a list of authenticated client
|
||||
networks to be referenced by an `AuthorizationPolicy`. If a
|
||||
client connection originates from ANY of the of the provided
|
||||
networks, the connection is considered authenticated.
|
||||
type: object
|
||||
required: [networks]
|
||||
properties:
|
||||
networks:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [cidr]
|
||||
properties:
|
||||
cidr:
|
||||
description: >-
|
||||
The CIDR of the network to be authorized.
|
||||
type: string
|
||||
except:
|
||||
description: >-
|
||||
A list of IP networks/addresses not to be included in
|
||||
the above `cidr`.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: servers.policy.linkerd.io
|
||||
annotations:
|
||||
|
@ -859,6 +1097,9 @@ webhooks:
|
|||
apiGroups: ["policy.linkerd.io"]
|
||||
apiVersions: ["v1alpha1", "v1beta1"]
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- networkauthentications
|
||||
- meshtlsauthentications
|
||||
- serverauthorizations
|
||||
- servers
|
||||
sideEffects: None
|
||||
|
@ -883,6 +1124,9 @@ rules:
|
|||
- apiGroups:
|
||||
- policy.linkerd.io
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- meshtlsauthentications
|
||||
- networkauthentications
|
||||
- servers
|
||||
- serverauthorizations
|
||||
verbs:
|
||||
|
|
|
@ -1,6 +1,244 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: authorizationpolicies.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: AuthorizationPolicy
|
||||
plural: authorizationpolicies
|
||||
singular: authorizationpolicy
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
Authorizes clients to communicate with Linkerd-proxied server
|
||||
resources.
|
||||
type: object
|
||||
required: [targetRef, requiredAuthenticationRefs]
|
||||
properties:
|
||||
targetRef:
|
||||
description: >-
|
||||
TargetRef references a resource to which the authorization
|
||||
policy applies.
|
||||
type: object
|
||||
required: [kind, name]
|
||||
# Modified from the gateway API.
|
||||
# Copyright 2020 The Kubernetes Authors
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred.
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
requiredAuthenticationRefs:
|
||||
description: >-
|
||||
RequiredAuthenticationRefs enumerates a set of required
|
||||
authentications. ALL authentications must be satisfied for
|
||||
the authorization to apply. If any of the referred objects
|
||||
cannot be found, the authorization will be ignored.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [kind, name]
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: meshtlsauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: MeshTLSAuthentication
|
||||
plural: meshtlsauthentications
|
||||
singular: meshtlsauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
MeshTLSAuthentication defines a list of authenticated client IDs
|
||||
to be referenced by an `AuthorizationPolicy`. If a client
|
||||
connection has the mutually-authenticated identity that matches
|
||||
ANY of the of the provided identities, the connection is
|
||||
considered authenticated.
|
||||
type: object
|
||||
oneOf:
|
||||
- required: [identities]
|
||||
- required: [identityRefs]
|
||||
properties:
|
||||
identities:
|
||||
description: >-
|
||||
Authorizes clients with the provided proxy identity strings
|
||||
(as provided via MTLS)
|
||||
|
||||
The `*` prefix can be used to match all identities in
|
||||
a domain. An identity string of `*` indicates that
|
||||
all authentication clients are authorized.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
pattern: '^(\*|[a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'
|
||||
identityRefs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- kind
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this refers to all resources of the specified Group
|
||||
and Kind in the specified namespace.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: networkauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: NetworkAuthentication
|
||||
plural: networkauthentications
|
||||
singular: networkauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
NetworkAuthentication defines a list of authenticated client
|
||||
networks to be referenced by an `AuthorizationPolicy`. If a
|
||||
client connection originates from ANY of the of the provided
|
||||
networks, the connection is considered authenticated.
|
||||
type: object
|
||||
required: [networks]
|
||||
properties:
|
||||
networks:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [cidr]
|
||||
properties:
|
||||
cidr:
|
||||
description: >-
|
||||
The CIDR of the network to be authorized.
|
||||
type: string
|
||||
except:
|
||||
description: >-
|
||||
A list of IP networks/addresses not to be included in
|
||||
the above `cidr`.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: servers.policy.linkerd.io
|
||||
annotations:
|
||||
|
@ -859,6 +1097,9 @@ webhooks:
|
|||
apiGroups: ["policy.linkerd.io"]
|
||||
apiVersions: ["v1alpha1", "v1beta1"]
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- networkauthentications
|
||||
- meshtlsauthentications
|
||||
- serverauthorizations
|
||||
- servers
|
||||
sideEffects: None
|
||||
|
@ -883,6 +1124,9 @@ rules:
|
|||
- apiGroups:
|
||||
- policy.linkerd.io
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- meshtlsauthentications
|
||||
- networkauthentications
|
||||
- servers
|
||||
- serverauthorizations
|
||||
verbs:
|
||||
|
|
|
@ -1,6 +1,244 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: authorizationpolicies.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: AuthorizationPolicy
|
||||
plural: authorizationpolicies
|
||||
singular: authorizationpolicy
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
Authorizes clients to communicate with Linkerd-proxied server
|
||||
resources.
|
||||
type: object
|
||||
required: [targetRef, requiredAuthenticationRefs]
|
||||
properties:
|
||||
targetRef:
|
||||
description: >-
|
||||
TargetRef references a resource to which the authorization
|
||||
policy applies.
|
||||
type: object
|
||||
required: [kind, name]
|
||||
# Modified from the gateway API.
|
||||
# Copyright 2020 The Kubernetes Authors
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred.
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
requiredAuthenticationRefs:
|
||||
description: >-
|
||||
RequiredAuthenticationRefs enumerates a set of required
|
||||
authentications. ALL authentications must be satisfied for
|
||||
the authorization to apply. If any of the referred objects
|
||||
cannot be found, the authorization will be ignored.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [kind, name]
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: meshtlsauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: MeshTLSAuthentication
|
||||
plural: meshtlsauthentications
|
||||
singular: meshtlsauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
MeshTLSAuthentication defines a list of authenticated client IDs
|
||||
to be referenced by an `AuthorizationPolicy`. If a client
|
||||
connection has the mutually-authenticated identity that matches
|
||||
ANY of the of the provided identities, the connection is
|
||||
considered authenticated.
|
||||
type: object
|
||||
oneOf:
|
||||
- required: [identities]
|
||||
- required: [identityRefs]
|
||||
properties:
|
||||
identities:
|
||||
description: >-
|
||||
Authorizes clients with the provided proxy identity strings
|
||||
(as provided via MTLS)
|
||||
|
||||
The `*` prefix can be used to match all identities in
|
||||
a domain. An identity string of `*` indicates that
|
||||
all authentication clients are authorized.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
pattern: '^(\*|[a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'
|
||||
identityRefs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- kind
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this refers to all resources of the specified Group
|
||||
and Kind in the specified namespace.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: networkauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: NetworkAuthentication
|
||||
plural: networkauthentications
|
||||
singular: networkauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
NetworkAuthentication defines a list of authenticated client
|
||||
networks to be referenced by an `AuthorizationPolicy`. If a
|
||||
client connection originates from ANY of the of the provided
|
||||
networks, the connection is considered authenticated.
|
||||
type: object
|
||||
required: [networks]
|
||||
properties:
|
||||
networks:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [cidr]
|
||||
properties:
|
||||
cidr:
|
||||
description: >-
|
||||
The CIDR of the network to be authorized.
|
||||
type: string
|
||||
except:
|
||||
description: >-
|
||||
A list of IP networks/addresses not to be included in
|
||||
the above `cidr`.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: servers.policy.linkerd.io
|
||||
annotations:
|
||||
|
@ -859,6 +1097,9 @@ webhooks:
|
|||
apiGroups: ["policy.linkerd.io"]
|
||||
apiVersions: ["v1alpha1", "v1beta1"]
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- networkauthentications
|
||||
- meshtlsauthentications
|
||||
- serverauthorizations
|
||||
- servers
|
||||
sideEffects: None
|
||||
|
@ -883,6 +1124,9 @@ rules:
|
|||
- apiGroups:
|
||||
- policy.linkerd.io
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- meshtlsauthentications
|
||||
- networkauthentications
|
||||
- servers
|
||||
- serverauthorizations
|
||||
verbs:
|
||||
|
|
|
@ -1,6 +1,244 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: authorizationpolicies.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: AuthorizationPolicy
|
||||
plural: authorizationpolicies
|
||||
singular: authorizationpolicy
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
Authorizes clients to communicate with Linkerd-proxied server
|
||||
resources.
|
||||
type: object
|
||||
required: [targetRef, requiredAuthenticationRefs]
|
||||
properties:
|
||||
targetRef:
|
||||
description: >-
|
||||
TargetRef references a resource to which the authorization
|
||||
policy applies.
|
||||
type: object
|
||||
required: [kind, name]
|
||||
# Modified from the gateway API.
|
||||
# Copyright 2020 The Kubernetes Authors
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred.
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
requiredAuthenticationRefs:
|
||||
description: >-
|
||||
RequiredAuthenticationRefs enumerates a set of required
|
||||
authentications. ALL authentications must be satisfied for
|
||||
the authorization to apply. If any of the referred objects
|
||||
cannot be found, the authorization will be ignored.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [kind, name]
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: meshtlsauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: MeshTLSAuthentication
|
||||
plural: meshtlsauthentications
|
||||
singular: meshtlsauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
MeshTLSAuthentication defines a list of authenticated client IDs
|
||||
to be referenced by an `AuthorizationPolicy`. If a client
|
||||
connection has the mutually-authenticated identity that matches
|
||||
ANY of the of the provided identities, the connection is
|
||||
considered authenticated.
|
||||
type: object
|
||||
oneOf:
|
||||
- required: [identities]
|
||||
- required: [identityRefs]
|
||||
properties:
|
||||
identities:
|
||||
description: >-
|
||||
Authorizes clients with the provided proxy identity strings
|
||||
(as provided via MTLS)
|
||||
|
||||
The `*` prefix can be used to match all identities in
|
||||
a domain. An identity string of `*` indicates that
|
||||
all authentication clients are authorized.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
pattern: '^(\*|[a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'
|
||||
identityRefs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- kind
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this refers to all resources of the specified Group
|
||||
and Kind in the specified namespace.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: networkauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: NetworkAuthentication
|
||||
plural: networkauthentications
|
||||
singular: networkauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
NetworkAuthentication defines a list of authenticated client
|
||||
networks to be referenced by an `AuthorizationPolicy`. If a
|
||||
client connection originates from ANY of the of the provided
|
||||
networks, the connection is considered authenticated.
|
||||
type: object
|
||||
required: [networks]
|
||||
properties:
|
||||
networks:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [cidr]
|
||||
properties:
|
||||
cidr:
|
||||
description: >-
|
||||
The CIDR of the network to be authorized.
|
||||
type: string
|
||||
except:
|
||||
description: >-
|
||||
A list of IP networks/addresses not to be included in
|
||||
the above `cidr`.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: servers.policy.linkerd.io
|
||||
annotations:
|
||||
|
@ -859,6 +1097,9 @@ webhooks:
|
|||
apiGroups: ["policy.linkerd.io"]
|
||||
apiVersions: ["v1alpha1", "v1beta1"]
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- networkauthentications
|
||||
- meshtlsauthentications
|
||||
- serverauthorizations
|
||||
- servers
|
||||
sideEffects: None
|
||||
|
@ -883,6 +1124,9 @@ rules:
|
|||
- apiGroups:
|
||||
- policy.linkerd.io
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- meshtlsauthentications
|
||||
- networkauthentications
|
||||
- servers
|
||||
- serverauthorizations
|
||||
verbs:
|
||||
|
|
|
@ -1,6 +1,244 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: authorizationpolicies.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: AuthorizationPolicy
|
||||
plural: authorizationpolicies
|
||||
singular: authorizationpolicy
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
Authorizes clients to communicate with Linkerd-proxied server
|
||||
resources.
|
||||
type: object
|
||||
required: [targetRef, requiredAuthenticationRefs]
|
||||
properties:
|
||||
targetRef:
|
||||
description: >-
|
||||
TargetRef references a resource to which the authorization
|
||||
policy applies.
|
||||
type: object
|
||||
required: [kind, name]
|
||||
# Modified from the gateway API.
|
||||
# Copyright 2020 The Kubernetes Authors
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred.
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
requiredAuthenticationRefs:
|
||||
description: >-
|
||||
RequiredAuthenticationRefs enumerates a set of required
|
||||
authentications. ALL authentications must be satisfied for
|
||||
the authorization to apply. If any of the referred objects
|
||||
cannot be found, the authorization will be ignored.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [kind, name]
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: meshtlsauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: MeshTLSAuthentication
|
||||
plural: meshtlsauthentications
|
||||
singular: meshtlsauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
MeshTLSAuthentication defines a list of authenticated client IDs
|
||||
to be referenced by an `AuthorizationPolicy`. If a client
|
||||
connection has the mutually-authenticated identity that matches
|
||||
ANY of the of the provided identities, the connection is
|
||||
considered authenticated.
|
||||
type: object
|
||||
oneOf:
|
||||
- required: [identities]
|
||||
- required: [identityRefs]
|
||||
properties:
|
||||
identities:
|
||||
description: >-
|
||||
Authorizes clients with the provided proxy identity strings
|
||||
(as provided via MTLS)
|
||||
|
||||
The `*` prefix can be used to match all identities in
|
||||
a domain. An identity string of `*` indicates that
|
||||
all authentication clients are authorized.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
pattern: '^(\*|[a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'
|
||||
identityRefs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- kind
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this refers to all resources of the specified Group
|
||||
and Kind in the specified namespace.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: networkauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: NetworkAuthentication
|
||||
plural: networkauthentications
|
||||
singular: networkauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
NetworkAuthentication defines a list of authenticated client
|
||||
networks to be referenced by an `AuthorizationPolicy`. If a
|
||||
client connection originates from ANY of the of the provided
|
||||
networks, the connection is considered authenticated.
|
||||
type: object
|
||||
required: [networks]
|
||||
properties:
|
||||
networks:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [cidr]
|
||||
properties:
|
||||
cidr:
|
||||
description: >-
|
||||
The CIDR of the network to be authorized.
|
||||
type: string
|
||||
except:
|
||||
description: >-
|
||||
A list of IP networks/addresses not to be included in
|
||||
the above `cidr`.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: servers.policy.linkerd.io
|
||||
annotations:
|
||||
|
@ -859,6 +1097,9 @@ webhooks:
|
|||
apiGroups: ["policy.linkerd.io"]
|
||||
apiVersions: ["v1alpha1", "v1beta1"]
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- networkauthentications
|
||||
- meshtlsauthentications
|
||||
- serverauthorizations
|
||||
- servers
|
||||
sideEffects: None
|
||||
|
@ -883,6 +1124,9 @@ rules:
|
|||
- apiGroups:
|
||||
- policy.linkerd.io
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- meshtlsauthentications
|
||||
- networkauthentications
|
||||
- servers
|
||||
- serverauthorizations
|
||||
verbs:
|
||||
|
|
|
@ -161,6 +161,9 @@ webhooks:
|
|||
apiGroups: ["policy.linkerd.io"]
|
||||
apiVersions: ["v1alpha1", "v1beta1"]
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- networkauthentications
|
||||
- meshtlsauthentications
|
||||
- serverauthorizations
|
||||
- servers
|
||||
sideEffects: None
|
||||
|
@ -185,6 +188,9 @@ rules:
|
|||
- apiGroups:
|
||||
- policy.linkerd.io
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- meshtlsauthentications
|
||||
- networkauthentications
|
||||
- servers
|
||||
- serverauthorizations
|
||||
verbs:
|
||||
|
|
|
@ -161,6 +161,9 @@ webhooks:
|
|||
apiGroups: ["policy.linkerd.io"]
|
||||
apiVersions: ["v1alpha1", "v1beta1"]
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- networkauthentications
|
||||
- meshtlsauthentications
|
||||
- serverauthorizations
|
||||
- servers
|
||||
sideEffects: None
|
||||
|
@ -185,6 +188,9 @@ rules:
|
|||
- apiGroups:
|
||||
- policy.linkerd.io
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- meshtlsauthentications
|
||||
- networkauthentications
|
||||
- servers
|
||||
- serverauthorizations
|
||||
verbs:
|
||||
|
|
|
@ -1,4 +1,248 @@
|
|||
---
|
||||
# Source: linkerd-crds/templates/policy/authorization-policy.yaml
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: authorizationpolicies.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/helm linkerd-version
|
||||
labels:
|
||||
helm.sh/chart: linkerd-crds-
|
||||
linkerd.io/control-plane-ns: linkerd-dev
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: AuthorizationPolicy
|
||||
plural: authorizationpolicies
|
||||
singular: authorizationpolicy
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
Authorizes clients to communicate with Linkerd-proxied server
|
||||
resources.
|
||||
type: object
|
||||
required: [targetRef, requiredAuthenticationRefs]
|
||||
properties:
|
||||
targetRef:
|
||||
description: >-
|
||||
TargetRef references a resource to which the authorization
|
||||
policy applies.
|
||||
type: object
|
||||
required: [kind, name]
|
||||
# Modified from the gateway API.
|
||||
# Copyright 2020 The Kubernetes Authors
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred.
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
requiredAuthenticationRefs:
|
||||
description: >-
|
||||
RequiredAuthenticationRefs enumerates a set of required
|
||||
authentications. ALL authentications must be satisfied for
|
||||
the authorization to apply. If any of the referred objects
|
||||
cannot be found, the authorization will be ignored.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [kind, name]
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
# Source: linkerd-crds/templates/policy/meshtls-authentication.yaml
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: meshtlsauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/helm linkerd-version
|
||||
labels:
|
||||
helm.sh/chart: linkerd-crds-
|
||||
linkerd.io/control-plane-ns: linkerd-dev
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: MeshTLSAuthentication
|
||||
plural: meshtlsauthentications
|
||||
singular: meshtlsauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
MeshTLSAuthentication defines a list of authenticated client IDs
|
||||
to be referenced by an `AuthorizationPolicy`. If a client
|
||||
connection has the mutually-authenticated identity that matches
|
||||
ANY of the of the provided identities, the connection is
|
||||
considered authenticated.
|
||||
type: object
|
||||
oneOf:
|
||||
- required: [identities]
|
||||
- required: [identityRefs]
|
||||
properties:
|
||||
identities:
|
||||
description: >-
|
||||
Authorizes clients with the provided proxy identity strings
|
||||
(as provided via MTLS)
|
||||
|
||||
The `*` prefix can be used to match all identities in
|
||||
a domain. An identity string of `*` indicates that
|
||||
all authentication clients are authorized.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
pattern: '^(\*|[a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'
|
||||
identityRefs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- kind
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this refers to all resources of the specified Group
|
||||
and Kind in the specified namespace.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
# Source: linkerd-crds/templates/policy/network-authentication.yaml
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: networkauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/helm linkerd-version
|
||||
labels:
|
||||
helm.sh/chart: linkerd-crds-
|
||||
linkerd.io/control-plane-ns: linkerd-dev
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: NetworkAuthentication
|
||||
plural: networkauthentications
|
||||
singular: networkauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
NetworkAuthentication defines a list of authenticated client
|
||||
networks to be referenced by an `AuthorizationPolicy`. If a
|
||||
client connection originates from ANY of the of the provided
|
||||
networks, the connection is considered authenticated.
|
||||
type: object
|
||||
required: [networks]
|
||||
properties:
|
||||
networks:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [cidr]
|
||||
properties:
|
||||
cidr:
|
||||
description: >-
|
||||
The CIDR of the network to be authorized.
|
||||
type: string
|
||||
except:
|
||||
description: >-
|
||||
A list of IP networks/addresses not to be included in
|
||||
the above `cidr`.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
---
|
||||
# Source: linkerd-crds/templates/policy/server.yaml
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
|
|
|
@ -1,4 +1,248 @@
|
|||
---
|
||||
# Source: linkerd-crds/templates/policy/authorization-policy.yaml
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: authorizationpolicies.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/helm linkerd-version
|
||||
labels:
|
||||
helm.sh/chart: linkerd-crds-
|
||||
linkerd.io/control-plane-ns: linkerd-dev
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: AuthorizationPolicy
|
||||
plural: authorizationpolicies
|
||||
singular: authorizationpolicy
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
Authorizes clients to communicate with Linkerd-proxied server
|
||||
resources.
|
||||
type: object
|
||||
required: [targetRef, requiredAuthenticationRefs]
|
||||
properties:
|
||||
targetRef:
|
||||
description: >-
|
||||
TargetRef references a resource to which the authorization
|
||||
policy applies.
|
||||
type: object
|
||||
required: [kind, name]
|
||||
# Modified from the gateway API.
|
||||
# Copyright 2020 The Kubernetes Authors
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred.
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
requiredAuthenticationRefs:
|
||||
description: >-
|
||||
RequiredAuthenticationRefs enumerates a set of required
|
||||
authentications. ALL authentications must be satisfied for
|
||||
the authorization to apply. If any of the referred objects
|
||||
cannot be found, the authorization will be ignored.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [kind, name]
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
# Source: linkerd-crds/templates/policy/meshtls-authentication.yaml
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: meshtlsauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/helm linkerd-version
|
||||
labels:
|
||||
helm.sh/chart: linkerd-crds-
|
||||
linkerd.io/control-plane-ns: linkerd-dev
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: MeshTLSAuthentication
|
||||
plural: meshtlsauthentications
|
||||
singular: meshtlsauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
MeshTLSAuthentication defines a list of authenticated client IDs
|
||||
to be referenced by an `AuthorizationPolicy`. If a client
|
||||
connection has the mutually-authenticated identity that matches
|
||||
ANY of the of the provided identities, the connection is
|
||||
considered authenticated.
|
||||
type: object
|
||||
oneOf:
|
||||
- required: [identities]
|
||||
- required: [identityRefs]
|
||||
properties:
|
||||
identities:
|
||||
description: >-
|
||||
Authorizes clients with the provided proxy identity strings
|
||||
(as provided via MTLS)
|
||||
|
||||
The `*` prefix can be used to match all identities in
|
||||
a domain. An identity string of `*` indicates that
|
||||
all authentication clients are authorized.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
pattern: '^(\*|[a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'
|
||||
identityRefs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- kind
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this refers to all resources of the specified Group
|
||||
and Kind in the specified namespace.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
# Source: linkerd-crds/templates/policy/network-authentication.yaml
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: networkauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/helm linkerd-version
|
||||
labels:
|
||||
helm.sh/chart: linkerd-crds-
|
||||
linkerd.io/control-plane-ns: linkerd-dev
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: NetworkAuthentication
|
||||
plural: networkauthentications
|
||||
singular: networkauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
NetworkAuthentication defines a list of authenticated client
|
||||
networks to be referenced by an `AuthorizationPolicy`. If a
|
||||
client connection originates from ANY of the of the provided
|
||||
networks, the connection is considered authenticated.
|
||||
type: object
|
||||
required: [networks]
|
||||
properties:
|
||||
networks:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [cidr]
|
||||
properties:
|
||||
cidr:
|
||||
description: >-
|
||||
The CIDR of the network to be authorized.
|
||||
type: string
|
||||
except:
|
||||
description: >-
|
||||
A list of IP networks/addresses not to be included in
|
||||
the above `cidr`.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
---
|
||||
# Source: linkerd-crds/templates/policy/server.yaml
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
|
|
|
@ -161,6 +161,9 @@ webhooks:
|
|||
apiGroups: ["policy.linkerd.io"]
|
||||
apiVersions: ["v1alpha1", "v1beta1"]
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- networkauthentications
|
||||
- meshtlsauthentications
|
||||
- serverauthorizations
|
||||
- servers
|
||||
sideEffects: None
|
||||
|
@ -185,6 +188,9 @@ rules:
|
|||
- apiGroups:
|
||||
- policy.linkerd.io
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- meshtlsauthentications
|
||||
- networkauthentications
|
||||
- servers
|
||||
- serverauthorizations
|
||||
verbs:
|
||||
|
|
|
@ -161,6 +161,9 @@ webhooks:
|
|||
apiGroups: ["policy.linkerd.io"]
|
||||
apiVersions: ["v1alpha1", "v1beta1"]
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- networkauthentications
|
||||
- meshtlsauthentications
|
||||
- serverauthorizations
|
||||
- servers
|
||||
sideEffects: None
|
||||
|
@ -185,6 +188,9 @@ rules:
|
|||
- apiGroups:
|
||||
- policy.linkerd.io
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- meshtlsauthentications
|
||||
- networkauthentications
|
||||
- servers
|
||||
- serverauthorizations
|
||||
verbs:
|
||||
|
|
|
@ -1,6 +1,244 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: authorizationpolicies.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: AuthorizationPolicy
|
||||
plural: authorizationpolicies
|
||||
singular: authorizationpolicy
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
Authorizes clients to communicate with Linkerd-proxied server
|
||||
resources.
|
||||
type: object
|
||||
required: [targetRef, requiredAuthenticationRefs]
|
||||
properties:
|
||||
targetRef:
|
||||
description: >-
|
||||
TargetRef references a resource to which the authorization
|
||||
policy applies.
|
||||
type: object
|
||||
required: [kind, name]
|
||||
# Modified from the gateway API.
|
||||
# Copyright 2020 The Kubernetes Authors
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred.
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
requiredAuthenticationRefs:
|
||||
description: >-
|
||||
RequiredAuthenticationRefs enumerates a set of required
|
||||
authentications. ALL authentications must be satisfied for
|
||||
the authorization to apply. If any of the referred objects
|
||||
cannot be found, the authorization will be ignored.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [kind, name]
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: meshtlsauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: MeshTLSAuthentication
|
||||
plural: meshtlsauthentications
|
||||
singular: meshtlsauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
MeshTLSAuthentication defines a list of authenticated client IDs
|
||||
to be referenced by an `AuthorizationPolicy`. If a client
|
||||
connection has the mutually-authenticated identity that matches
|
||||
ANY of the of the provided identities, the connection is
|
||||
considered authenticated.
|
||||
type: object
|
||||
oneOf:
|
||||
- required: [identities]
|
||||
- required: [identityRefs]
|
||||
properties:
|
||||
identities:
|
||||
description: >-
|
||||
Authorizes clients with the provided proxy identity strings
|
||||
(as provided via MTLS)
|
||||
|
||||
The `*` prefix can be used to match all identities in
|
||||
a domain. An identity string of `*` indicates that
|
||||
all authentication clients are authorized.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
pattern: '^(\*|[a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'
|
||||
identityRefs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- kind
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this refers to all resources of the specified Group
|
||||
and Kind in the specified namespace.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: networkauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: NetworkAuthentication
|
||||
plural: networkauthentications
|
||||
singular: networkauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
NetworkAuthentication defines a list of authenticated client
|
||||
networks to be referenced by an `AuthorizationPolicy`. If a
|
||||
client connection originates from ANY of the of the provided
|
||||
networks, the connection is considered authenticated.
|
||||
type: object
|
||||
required: [networks]
|
||||
properties:
|
||||
networks:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [cidr]
|
||||
properties:
|
||||
cidr:
|
||||
description: >-
|
||||
The CIDR of the network to be authorized.
|
||||
type: string
|
||||
except:
|
||||
description: >-
|
||||
A list of IP networks/addresses not to be included in
|
||||
the above `cidr`.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: servers.policy.linkerd.io
|
||||
annotations:
|
||||
|
@ -859,6 +1097,9 @@ webhooks:
|
|||
apiGroups: ["policy.linkerd.io"]
|
||||
apiVersions: ["v1alpha1", "v1beta1"]
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- networkauthentications
|
||||
- meshtlsauthentications
|
||||
- serverauthorizations
|
||||
- servers
|
||||
sideEffects: None
|
||||
|
@ -883,6 +1124,9 @@ rules:
|
|||
- apiGroups:
|
||||
- policy.linkerd.io
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- meshtlsauthentications
|
||||
- networkauthentications
|
||||
- servers
|
||||
- serverauthorizations
|
||||
verbs:
|
||||
|
|
|
@ -1,6 +1,244 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: authorizationpolicies.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: CliVersion
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: AuthorizationPolicy
|
||||
plural: authorizationpolicies
|
||||
singular: authorizationpolicy
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
Authorizes clients to communicate with Linkerd-proxied server
|
||||
resources.
|
||||
type: object
|
||||
required: [targetRef, requiredAuthenticationRefs]
|
||||
properties:
|
||||
targetRef:
|
||||
description: >-
|
||||
TargetRef references a resource to which the authorization
|
||||
policy applies.
|
||||
type: object
|
||||
required: [kind, name]
|
||||
# Modified from the gateway API.
|
||||
# Copyright 2020 The Kubernetes Authors
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred.
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
requiredAuthenticationRefs:
|
||||
description: >-
|
||||
RequiredAuthenticationRefs enumerates a set of required
|
||||
authentications. ALL authentications must be satisfied for
|
||||
the authorization to apply. If any of the referred objects
|
||||
cannot be found, the authorization will be ignored.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [kind, name]
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: meshtlsauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: CliVersion
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: MeshTLSAuthentication
|
||||
plural: meshtlsauthentications
|
||||
singular: meshtlsauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
MeshTLSAuthentication defines a list of authenticated client IDs
|
||||
to be referenced by an `AuthorizationPolicy`. If a client
|
||||
connection has the mutually-authenticated identity that matches
|
||||
ANY of the of the provided identities, the connection is
|
||||
considered authenticated.
|
||||
type: object
|
||||
oneOf:
|
||||
- required: [identities]
|
||||
- required: [identityRefs]
|
||||
properties:
|
||||
identities:
|
||||
description: >-
|
||||
Authorizes clients with the provided proxy identity strings
|
||||
(as provided via MTLS)
|
||||
|
||||
The `*` prefix can be used to match all identities in
|
||||
a domain. An identity string of `*` indicates that
|
||||
all authentication clients are authorized.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
pattern: '^(\*|[a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'
|
||||
identityRefs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- kind
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this refers to all resources of the specified Group
|
||||
and Kind in the specified namespace.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: networkauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: CliVersion
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: NetworkAuthentication
|
||||
plural: networkauthentications
|
||||
singular: networkauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
NetworkAuthentication defines a list of authenticated client
|
||||
networks to be referenced by an `AuthorizationPolicy`. If a
|
||||
client connection originates from ANY of the of the provided
|
||||
networks, the connection is considered authenticated.
|
||||
type: object
|
||||
required: [networks]
|
||||
properties:
|
||||
networks:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [cidr]
|
||||
properties:
|
||||
cidr:
|
||||
description: >-
|
||||
The CIDR of the network to be authorized.
|
||||
type: string
|
||||
except:
|
||||
description: >-
|
||||
A list of IP networks/addresses not to be included in
|
||||
the above `cidr`.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: servers.policy.linkerd.io
|
||||
annotations:
|
||||
|
@ -856,6 +1094,9 @@ webhooks:
|
|||
apiGroups: ["policy.linkerd.io"]
|
||||
apiVersions: ["v1alpha1", "v1beta1"]
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- networkauthentications
|
||||
- meshtlsauthentications
|
||||
- serverauthorizations
|
||||
- servers
|
||||
sideEffects: None
|
||||
|
@ -880,6 +1121,9 @@ rules:
|
|||
- apiGroups:
|
||||
- policy.linkerd.io
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- meshtlsauthentications
|
||||
- networkauthentications
|
||||
- servers
|
||||
- serverauthorizations
|
||||
verbs:
|
||||
|
|
|
@ -1,6 +1,244 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: authorizationpolicies.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: AuthorizationPolicy
|
||||
plural: authorizationpolicies
|
||||
singular: authorizationpolicy
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
Authorizes clients to communicate with Linkerd-proxied server
|
||||
resources.
|
||||
type: object
|
||||
required: [targetRef, requiredAuthenticationRefs]
|
||||
properties:
|
||||
targetRef:
|
||||
description: >-
|
||||
TargetRef references a resource to which the authorization
|
||||
policy applies.
|
||||
type: object
|
||||
required: [kind, name]
|
||||
# Modified from the gateway API.
|
||||
# Copyright 2020 The Kubernetes Authors
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred.
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
requiredAuthenticationRefs:
|
||||
description: >-
|
||||
RequiredAuthenticationRefs enumerates a set of required
|
||||
authentications. ALL authentications must be satisfied for
|
||||
the authorization to apply. If any of the referred objects
|
||||
cannot be found, the authorization will be ignored.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [kind, name]
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: meshtlsauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: MeshTLSAuthentication
|
||||
plural: meshtlsauthentications
|
||||
singular: meshtlsauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
MeshTLSAuthentication defines a list of authenticated client IDs
|
||||
to be referenced by an `AuthorizationPolicy`. If a client
|
||||
connection has the mutually-authenticated identity that matches
|
||||
ANY of the of the provided identities, the connection is
|
||||
considered authenticated.
|
||||
type: object
|
||||
oneOf:
|
||||
- required: [identities]
|
||||
- required: [identityRefs]
|
||||
properties:
|
||||
identities:
|
||||
description: >-
|
||||
Authorizes clients with the provided proxy identity strings
|
||||
(as provided via MTLS)
|
||||
|
||||
The `*` prefix can be used to match all identities in
|
||||
a domain. An identity string of `*` indicates that
|
||||
all authentication clients are authorized.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
pattern: '^(\*|[a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'
|
||||
identityRefs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- kind
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this refers to all resources of the specified Group
|
||||
and Kind in the specified namespace.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: networkauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: NetworkAuthentication
|
||||
plural: networkauthentications
|
||||
singular: networkauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
NetworkAuthentication defines a list of authenticated client
|
||||
networks to be referenced by an `AuthorizationPolicy`. If a
|
||||
client connection originates from ANY of the of the provided
|
||||
networks, the connection is considered authenticated.
|
||||
type: object
|
||||
required: [networks]
|
||||
properties:
|
||||
networks:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [cidr]
|
||||
properties:
|
||||
cidr:
|
||||
description: >-
|
||||
The CIDR of the network to be authorized.
|
||||
type: string
|
||||
except:
|
||||
description: >-
|
||||
A list of IP networks/addresses not to be included in
|
||||
the above `cidr`.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: servers.policy.linkerd.io
|
||||
annotations:
|
||||
|
@ -859,6 +1097,9 @@ webhooks:
|
|||
apiGroups: ["policy.linkerd.io"]
|
||||
apiVersions: ["v1alpha1", "v1beta1"]
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- networkauthentications
|
||||
- meshtlsauthentications
|
||||
- serverauthorizations
|
||||
- servers
|
||||
sideEffects: None
|
||||
|
@ -883,6 +1124,9 @@ rules:
|
|||
- apiGroups:
|
||||
- policy.linkerd.io
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- meshtlsauthentications
|
||||
- networkauthentications
|
||||
- servers
|
||||
- serverauthorizations
|
||||
verbs:
|
||||
|
|
|
@ -1,6 +1,244 @@
|
|||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: authorizationpolicies.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: AuthorizationPolicy
|
||||
plural: authorizationpolicies
|
||||
singular: authorizationpolicy
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
Authorizes clients to communicate with Linkerd-proxied server
|
||||
resources.
|
||||
type: object
|
||||
required: [targetRef, requiredAuthenticationRefs]
|
||||
properties:
|
||||
targetRef:
|
||||
description: >-
|
||||
TargetRef references a resource to which the authorization
|
||||
policy applies.
|
||||
type: object
|
||||
required: [kind, name]
|
||||
# Modified from the gateway API.
|
||||
# Copyright 2020 The Kubernetes Authors
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred.
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
requiredAuthenticationRefs:
|
||||
description: >-
|
||||
RequiredAuthenticationRefs enumerates a set of required
|
||||
authentications. ALL authentications must be satisfied for
|
||||
the authorization to apply. If any of the referred objects
|
||||
cannot be found, the authorization will be ignored.
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [kind, name]
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: meshtlsauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: MeshTLSAuthentication
|
||||
plural: meshtlsauthentications
|
||||
singular: meshtlsauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
MeshTLSAuthentication defines a list of authenticated client IDs
|
||||
to be referenced by an `AuthorizationPolicy`. If a client
|
||||
connection has the mutually-authenticated identity that matches
|
||||
ANY of the of the provided identities, the connection is
|
||||
considered authenticated.
|
||||
type: object
|
||||
oneOf:
|
||||
- required: [identities]
|
||||
- required: [identityRefs]
|
||||
properties:
|
||||
identities:
|
||||
description: >-
|
||||
Authorizes clients with the provided proxy identity strings
|
||||
(as provided via MTLS)
|
||||
|
||||
The `*` prefix can be used to match all identities in
|
||||
a domain. An identity string of `*` indicates that
|
||||
all authentication clients are authorized.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
pattern: '^(\*|[a-z0-9]([-a-z0-9]*[a-z0-9])?)(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$'
|
||||
identityRefs:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required:
|
||||
- kind
|
||||
properties:
|
||||
group:
|
||||
description: >-
|
||||
Group is the group of the referent. When empty, the
|
||||
Kubernetes core API group is inferred."
|
||||
maxLength: 253
|
||||
pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
|
||||
type: string
|
||||
kind:
|
||||
description: >-
|
||||
Kind is the kind of the referent.
|
||||
maxLength: 63
|
||||
minLength: 1
|
||||
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
|
||||
type: string
|
||||
name:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this refers to all resources of the specified Group
|
||||
and Kind in the specified namespace.
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: >-
|
||||
Name is the name of the referent. When unspecified,
|
||||
this authentication refers to the local namespace.
|
||||
maxLength: 253
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: networkauthentications.policy.linkerd.io
|
||||
annotations:
|
||||
linkerd.io/created-by: linkerd/cli dev-undefined
|
||||
labels:
|
||||
helm.sh/chart: linkerd-control-plane-1.1.11-edge
|
||||
linkerd.io/control-plane-ns: linkerd
|
||||
spec:
|
||||
group: policy.linkerd.io
|
||||
scope: Namespaced
|
||||
names:
|
||||
kind: NetworkAuthentication
|
||||
plural: networkauthentications
|
||||
singular: networkauthentication
|
||||
shortNames: []
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
required: [spec]
|
||||
properties:
|
||||
spec:
|
||||
description: >-
|
||||
NetworkAuthentication defines a list of authenticated client
|
||||
networks to be referenced by an `AuthorizationPolicy`. If a
|
||||
client connection originates from ANY of the of the provided
|
||||
networks, the connection is considered authenticated.
|
||||
type: object
|
||||
required: [networks]
|
||||
properties:
|
||||
networks:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [cidr]
|
||||
properties:
|
||||
cidr:
|
||||
description: >-
|
||||
The CIDR of the network to be authorized.
|
||||
type: string
|
||||
except:
|
||||
description: >-
|
||||
A list of IP networks/addresses not to be included in
|
||||
the above `cidr`.
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: servers.policy.linkerd.io
|
||||
annotations:
|
||||
|
@ -859,6 +1097,9 @@ webhooks:
|
|||
apiGroups: ["policy.linkerd.io"]
|
||||
apiVersions: ["v1alpha1", "v1beta1"]
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- networkauthentications
|
||||
- meshtlsauthentications
|
||||
- serverauthorizations
|
||||
- servers
|
||||
sideEffects: None
|
||||
|
@ -883,6 +1124,9 @@ rules:
|
|||
- apiGroups:
|
||||
- policy.linkerd.io
|
||||
resources:
|
||||
- authorizationpolicies
|
||||
- meshtlsauthentications
|
||||
- networkauthentications
|
||||
- servers
|
||||
- serverauthorizations
|
||||
verbs:
|
||||
|
|
|
@ -24,9 +24,23 @@ pub type InboundServerStream = Pin<Box<dyn Stream<Item = InboundServer> + Send +
|
|||
/// Inbound server configuration.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct InboundServer {
|
||||
pub name: String,
|
||||
pub reference: ServerRef,
|
||||
|
||||
pub protocol: ProxyProtocol,
|
||||
pub authorizations: HashMap<String, ClientAuthorization>,
|
||||
pub authorizations: HashMap<AuthorizationRef, ClientAuthorization>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ServerRef {
|
||||
Default(String),
|
||||
Server(String),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum AuthorizationRef {
|
||||
Default(String),
|
||||
ServerAuthorization(String),
|
||||
AuthorizationPolicy(String),
|
||||
}
|
||||
|
||||
/// Describes how a proxy should handle inbound connections.
|
||||
|
@ -63,7 +77,7 @@ pub enum ClientAuthentication {
|
|||
/// Indicates that clients need not be authenticated.
|
||||
Unauthenticated,
|
||||
|
||||
/// Indicates that clients must use TLS bu need not provide a client identity.
|
||||
/// Indicates that clients must use TLS but need not provide a client identity.
|
||||
TlsUnauthenticated,
|
||||
|
||||
/// Indicates that clients must use mutually-authenticated TLS.
|
||||
|
|
|
@ -15,6 +15,7 @@ drain = "0.1"
|
|||
futures = { version = "0.3", default-features = false }
|
||||
linkerd2-proxy-api = { version = "0.3", features = ["inbound", "server"] }
|
||||
linkerd-policy-controller-core = { path = "../core" }
|
||||
maplit = "1"
|
||||
tokio = { version = "1", features = ["macros"] }
|
||||
tonic = { version = "0.6", default-features = false, features = ["transport"] }
|
||||
tracing = "0.1"
|
||||
|
|
|
@ -7,9 +7,11 @@ use linkerd2_proxy_api::inbound::{
|
|||
inbound_server_policies_server::{InboundServerPolicies, InboundServerPoliciesServer},
|
||||
};
|
||||
use linkerd_policy_controller_core::{
|
||||
ClientAuthentication, ClientAuthorization, DiscoverInboundServer, IdentityMatch, InboundServer,
|
||||
InboundServerStream, IpNet, NetworkMatch, ProxyProtocol,
|
||||
AuthorizationRef, ClientAuthentication, ClientAuthorization, DiscoverInboundServer,
|
||||
IdentityMatch, InboundServer, InboundServerStream, IpNet, NetworkMatch, ProxyProtocol,
|
||||
ServerRef,
|
||||
};
|
||||
use maplit::*;
|
||||
use std::sync::Arc;
|
||||
use tracing::trace;
|
||||
|
||||
|
@ -194,9 +196,16 @@ fn to_server(srv: &InboundServer, cluster_networks: &[IpNet]) -> proto::Server {
|
|||
.collect();
|
||||
trace!(?authorizations);
|
||||
|
||||
let labels = vec![("name".to_string(), srv.name.to_string())]
|
||||
.into_iter()
|
||||
.collect();
|
||||
let labels = match &srv.reference {
|
||||
ServerRef::Default(name) => convert_args!(hashmap!(
|
||||
"kind" => "default",
|
||||
"name" => name,
|
||||
)),
|
||||
ServerRef::Server(name) => convert_args!(hashmap!(
|
||||
"kind" => "server",
|
||||
"name" => name,
|
||||
)),
|
||||
};
|
||||
trace!(?labels);
|
||||
|
||||
proto::Server {
|
||||
|
@ -208,7 +217,7 @@ fn to_server(srv: &InboundServer, cluster_networks: &[IpNet]) -> proto::Server {
|
|||
}
|
||||
|
||||
fn to_authz(
|
||||
name: impl ToString,
|
||||
reference: &AuthorizationRef,
|
||||
ClientAuthorization {
|
||||
networks,
|
||||
authentication,
|
||||
|
@ -233,9 +242,20 @@ fn to_authz(
|
|||
.collect()
|
||||
};
|
||||
|
||||
let labels = vec![("name".to_string(), name.to_string())]
|
||||
.into_iter()
|
||||
.collect();
|
||||
let labels = match reference {
|
||||
AuthorizationRef::Default(name) => convert_args!(hashmap!(
|
||||
"kind" => "default",
|
||||
"name" => name,
|
||||
)),
|
||||
AuthorizationRef::ServerAuthorization(name) => convert_args!(hashmap!(
|
||||
"kind" => "serverauthorization",
|
||||
"name" => name,
|
||||
)),
|
||||
AuthorizationRef::AuthorizationPolicy(name) => convert_args!(hashmap!(
|
||||
"kind" => "authorizationpolicy",
|
||||
"name" => name,
|
||||
)),
|
||||
};
|
||||
|
||||
let authn = match authentication {
|
||||
ClientAuthentication::Unauthenticated => proto::Authn {
|
||||
|
|
|
@ -7,7 +7,9 @@ pub mod policy;
|
|||
pub use self::labels::Labels;
|
||||
pub use k8s_openapi::api::{
|
||||
self,
|
||||
core::v1::{Namespace, Node, NodeSpec, Pod, PodSpec, PodStatus},
|
||||
core::v1::{Namespace, Node, NodeSpec, Pod, PodSpec, PodStatus, ServiceAccount},
|
||||
};
|
||||
pub use kube::{
|
||||
api::{ObjectMeta, Resource, ResourceExt},
|
||||
runtime::watcher::Event as WatchEvent,
|
||||
};
|
||||
pub use kube::api::{ObjectMeta, Resource, ResourceExt};
|
||||
pub use kube::runtime::watcher::Event as WatchEvent;
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
pub mod network;
|
||||
pub mod authorization_policy;
|
||||
pub mod meshtls_authentication;
|
||||
mod network;
|
||||
pub mod network_authentication;
|
||||
pub mod server;
|
||||
pub mod server_authorization;
|
||||
pub mod target_ref;
|
||||
|
||||
pub use self::{
|
||||
authorization_policy::{AuthorizationPolicy, AuthorizationPolicySpec},
|
||||
meshtls_authentication::{MeshTLSAuthentication, MeshTLSAuthenticationSpec},
|
||||
network::Network,
|
||||
network_authentication::{NetworkAuthentication, NetworkAuthenticationSpec},
|
||||
server::{Server, ServerSpec},
|
||||
server_authorization::{ServerAuthorization, ServerAuthorizationSpec},
|
||||
target_ref::{ClusterTargetRef, LocalTargetRef, NamespacedTargetRef},
|
||||
};
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
use super::{LocalTargetRef, NamespacedTargetRef};
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
Default,
|
||||
kube::CustomResource,
|
||||
serde::Deserialize,
|
||||
serde::Serialize,
|
||||
schemars::JsonSchema,
|
||||
)]
|
||||
#[kube(
|
||||
group = "policy.linkerd.io",
|
||||
version = "v1alpha1",
|
||||
kind = "AuthorizationPolicy",
|
||||
namespaced
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct AuthorizationPolicySpec {
|
||||
pub target_ref: LocalTargetRef,
|
||||
pub required_authentication_refs: Vec<NamespacedTargetRef>,
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
use super::NamespacedTargetRef;
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
Default,
|
||||
PartialEq,
|
||||
kube::CustomResource,
|
||||
serde::Deserialize,
|
||||
serde::Serialize,
|
||||
schemars::JsonSchema,
|
||||
)]
|
||||
#[kube(
|
||||
group = "policy.linkerd.io",
|
||||
version = "v1alpha1",
|
||||
kind = "MeshTLSAuthentication",
|
||||
namespaced
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MeshTLSAuthenticationSpec {
|
||||
pub identities: Option<Vec<String>>,
|
||||
pub identity_refs: Option<Vec<NamespacedTargetRef>>,
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
pub use super::Network;
|
||||
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
Default,
|
||||
PartialEq,
|
||||
kube::CustomResource,
|
||||
serde::Deserialize,
|
||||
serde::Serialize,
|
||||
schemars::JsonSchema,
|
||||
)]
|
||||
#[kube(
|
||||
group = "policy.linkerd.io",
|
||||
version = "v1alpha1",
|
||||
kind = "NetworkAuthentication",
|
||||
namespaced
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NetworkAuthenticationSpec {
|
||||
pub networks: Vec<Network>,
|
||||
}
|
|
@ -0,0 +1,351 @@
|
|||
#[derive(
|
||||
Clone, Debug, Default, PartialEq, serde::Deserialize, serde::Serialize, schemars::JsonSchema,
|
||||
)]
|
||||
pub struct ClusterTargetRef {
|
||||
pub group: Option<String>,
|
||||
pub kind: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, Default, PartialEq, serde::Deserialize, serde::Serialize, schemars::JsonSchema,
|
||||
)]
|
||||
pub struct LocalTargetRef {
|
||||
pub group: Option<String>,
|
||||
pub kind: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, Default, PartialEq, serde::Deserialize, serde::Serialize, schemars::JsonSchema,
|
||||
)]
|
||||
pub struct NamespacedTargetRef {
|
||||
pub group: Option<String>,
|
||||
pub kind: String,
|
||||
pub name: String,
|
||||
pub namespace: Option<String>,
|
||||
}
|
||||
|
||||
impl ClusterTargetRef {
|
||||
pub fn from_resource<T>(resource: &T) -> Self
|
||||
where
|
||||
T: kube::Resource,
|
||||
T::DynamicType: Default,
|
||||
{
|
||||
let (group, kind, name) = group_kind_name(resource);
|
||||
Self { group, kind, name }
|
||||
}
|
||||
|
||||
/// Returns the target ref kind, qualified by its group, if necessary.
|
||||
pub fn canonical_kind(&self) -> String {
|
||||
canonical_kind(self.group.as_deref(), &self.kind)
|
||||
}
|
||||
|
||||
/// Checks whether the target references the given resource type
|
||||
pub fn targets_kind<T>(&self) -> bool
|
||||
where
|
||||
T: kube::Resource,
|
||||
T::DynamicType: Default,
|
||||
{
|
||||
targets_kind::<T>(self.group.as_deref(), &self.kind)
|
||||
}
|
||||
|
||||
/// Checks whether the target references the given cluster-level resource
|
||||
pub fn targets<T>(&self, resource: &T) -> bool
|
||||
where
|
||||
T: kube::Resource,
|
||||
T::DynamicType: Default,
|
||||
{
|
||||
if !self.targets_kind::<T>() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if resource.meta().namespace.is_some() {
|
||||
// If the reference or the resource has a namespace, that's a deal-breaker.
|
||||
return false;
|
||||
}
|
||||
|
||||
match resource.meta().name.as_deref() {
|
||||
None => return false,
|
||||
Some(rname) => {
|
||||
if !self.name.eq_ignore_ascii_case(rname) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalTargetRef {
|
||||
pub fn from_resource<T>(resource: &T) -> Self
|
||||
where
|
||||
T: kube::Resource,
|
||||
T::DynamicType: Default,
|
||||
{
|
||||
let (group, kind, name) = group_kind_name(resource);
|
||||
Self { group, kind, name }
|
||||
}
|
||||
|
||||
/// Returns the target ref kind, qualified by its group, if necessary.
|
||||
pub fn canonical_kind(&self) -> String {
|
||||
canonical_kind(self.group.as_deref(), &self.kind)
|
||||
}
|
||||
|
||||
/// Checks whether the target references the given resource type
|
||||
pub fn targets_kind<T>(&self) -> bool
|
||||
where
|
||||
T: kube::Resource,
|
||||
T::DynamicType: Default,
|
||||
{
|
||||
targets_kind::<T>(self.group.as_deref(), &self.kind)
|
||||
}
|
||||
|
||||
/// Checks whether the target references the given namespaced resource
|
||||
pub fn targets<T>(&self, resource: &T, local_ns: &str) -> bool
|
||||
where
|
||||
T: kube::Resource,
|
||||
T::DynamicType: Default,
|
||||
{
|
||||
if !self.targets_kind::<T>() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the resource specifies a namespace other than the target or the
|
||||
// default namespace, that's a deal-breaker.
|
||||
match resource.meta().namespace.as_deref() {
|
||||
Some(rns) if rns.eq_ignore_ascii_case(local_ns) => {}
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
match resource.meta().name.as_deref() {
|
||||
Some(rname) => rname.eq_ignore_ascii_case(&self.name),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NamespacedTargetRef {
|
||||
pub fn from_resource<T>(resource: &T) -> Self
|
||||
where
|
||||
T: kube::Resource,
|
||||
T::DynamicType: Default,
|
||||
{
|
||||
let (group, kind, name) = group_kind_name(resource);
|
||||
let namespace = resource.meta().namespace.clone();
|
||||
Self {
|
||||
group,
|
||||
kind,
|
||||
name,
|
||||
namespace,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the target ref kind, qualified by its group, if necessary.
|
||||
pub fn canonical_kind(&self) -> String {
|
||||
canonical_kind(self.group.as_deref(), &self.kind)
|
||||
}
|
||||
|
||||
/// Checks whether the target references the given resource type
|
||||
pub fn targets_kind<T>(&self) -> bool
|
||||
where
|
||||
T: kube::Resource,
|
||||
T::DynamicType: Default,
|
||||
{
|
||||
targets_kind::<T>(self.group.as_deref(), &self.kind)
|
||||
}
|
||||
|
||||
/// Checks whether the target references the given namespaced resource
|
||||
pub fn targets<T>(&self, resource: &T, local_ns: &str) -> bool
|
||||
where
|
||||
T: kube::Resource,
|
||||
T::DynamicType: Default,
|
||||
{
|
||||
if !self.targets_kind::<T>() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the resource specifies a namespace other than the target or the
|
||||
// default namespace, that's a deal-breaker.
|
||||
let tns = self.namespace.as_deref().unwrap_or(local_ns);
|
||||
match resource.meta().namespace.as_deref() {
|
||||
Some(rns) if rns.eq_ignore_ascii_case(tns) => {}
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
match resource.meta().name.as_deref() {
|
||||
None => return false,
|
||||
Some(rname) => {
|
||||
if !self.name.eq_ignore_ascii_case(rname) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn canonical_kind(group: Option<&str>, kind: &str) -> String {
|
||||
if let Some(group) = group {
|
||||
format!("{}.{}", kind, group)
|
||||
} else {
|
||||
kind.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn targets_kind<T>(group: Option<&str>, kind: &str) -> bool
|
||||
where
|
||||
T: kube::Resource,
|
||||
T::DynamicType: Default,
|
||||
{
|
||||
let dt = Default::default();
|
||||
|
||||
let mut t_group = &*T::group(&dt);
|
||||
if t_group.is_empty() {
|
||||
t_group = "core";
|
||||
}
|
||||
|
||||
group.unwrap_or("core").eq_ignore_ascii_case(t_group)
|
||||
&& kind.eq_ignore_ascii_case(&*T::kind(&dt))
|
||||
}
|
||||
|
||||
fn group_kind_name<T>(resource: &T) -> (Option<String>, String, String)
|
||||
where
|
||||
T: kube::Resource,
|
||||
T::DynamicType: Default,
|
||||
{
|
||||
let dt = Default::default();
|
||||
|
||||
let group = match T::group(&dt) {
|
||||
g if (*g).is_empty() => None,
|
||||
g => Some(g.to_string()),
|
||||
};
|
||||
|
||||
let kind = T::kind(&dt).to_string();
|
||||
|
||||
let name = resource
|
||||
.meta()
|
||||
.name
|
||||
.clone()
|
||||
.expect("resource must have a name");
|
||||
|
||||
(group, kind, name)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{policy::Server, Namespace, ObjectMeta, ServiceAccount};
|
||||
|
||||
#[test]
|
||||
fn cluster_targets_namespace() {
|
||||
let t = ClusterTargetRef {
|
||||
kind: "Namespace".to_string(),
|
||||
name: "appns".to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
assert!(t.targets_kind::<Namespace>());
|
||||
assert!(t.targets(&Namespace {
|
||||
metadata: ObjectMeta {
|
||||
name: Some("appns".to_string()),
|
||||
..ObjectMeta::default()
|
||||
},
|
||||
..Namespace::default()
|
||||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn namespaced_targets_service_account() {
|
||||
for tgt in &[
|
||||
NamespacedTargetRef {
|
||||
kind: "ServiceAccount".to_string(),
|
||||
name: "default".to_string(),
|
||||
namespace: Some("appns".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
NamespacedTargetRef {
|
||||
group: Some("core".to_string()),
|
||||
kind: "ServiceAccount".to_string(),
|
||||
name: "default".to_string(),
|
||||
namespace: Some("appns".to_string()),
|
||||
},
|
||||
NamespacedTargetRef {
|
||||
group: Some("CORE".to_string()),
|
||||
kind: "SERVICEACCOUNT".to_string(),
|
||||
name: "DEFAULT".to_string(),
|
||||
namespace: Some("APPNS".to_string()),
|
||||
},
|
||||
NamespacedTargetRef {
|
||||
kind: "ServiceAccount".to_string(),
|
||||
name: "default".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
] {
|
||||
assert!(tgt.targets_kind::<ServiceAccount>());
|
||||
|
||||
assert!(!tgt.targets_kind::<Namespace>());
|
||||
|
||||
let sa = ServiceAccount {
|
||||
metadata: ObjectMeta {
|
||||
namespace: Some("appns".to_string()),
|
||||
name: Some("default".to_string()),
|
||||
..ObjectMeta::default()
|
||||
},
|
||||
..ServiceAccount::default()
|
||||
};
|
||||
assert!(
|
||||
tgt.targets(&sa, "appns"),
|
||||
"ServiceAccounts are targeted by name: {:#?}",
|
||||
tgt
|
||||
);
|
||||
|
||||
let sa = ServiceAccount {
|
||||
metadata: ObjectMeta {
|
||||
namespace: Some("otherns".to_string()),
|
||||
name: Some("default".to_string()),
|
||||
..ObjectMeta::default()
|
||||
},
|
||||
..ServiceAccount::default()
|
||||
};
|
||||
assert!(
|
||||
!tgt.targets(&sa, "appns"),
|
||||
"ServiceAccounts in other namespaces should not be targeted: {:#?}",
|
||||
tgt
|
||||
);
|
||||
}
|
||||
|
||||
let tgt = NamespacedTargetRef {
|
||||
kind: "ServiceAccount".to_string(),
|
||||
name: "default".to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
assert!(
|
||||
{
|
||||
let sa = ServiceAccount {
|
||||
metadata: ObjectMeta {
|
||||
namespace: Some("appns".to_string()),
|
||||
name: Some("special".to_string()),
|
||||
..ObjectMeta::default()
|
||||
},
|
||||
..ServiceAccount::default()
|
||||
};
|
||||
!tgt.targets(&sa, "appns")
|
||||
},
|
||||
"resource comparison uses "
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn namespaced_targets_server() {
|
||||
let tgt = NamespacedTargetRef {
|
||||
group: Some("policy.linkerd.io".to_string()),
|
||||
kind: "Server".to_string(),
|
||||
name: "http".to_string(),
|
||||
namespace: Some("appns".to_string()),
|
||||
};
|
||||
|
||||
assert!(tgt.targets_kind::<Server>());
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ tokio = { version = "1", features = ["macros", "rt", "sync"] }
|
|||
tracing = "0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
maplit = "1"
|
||||
tokio-stream = "0.1"
|
||||
tokio-test = "0.4"
|
||||
tracing-subscriber = "0.3"
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
use anyhow::{bail, Result};
|
||||
use linkerd_policy_controller_k8s_api::{
|
||||
self as k8s,
|
||||
policy::{LocalTargetRef, NamespacedTargetRef},
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) struct Spec {
|
||||
pub target: Target,
|
||||
pub authentications: Vec<AuthenticationTarget>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) enum Target {
|
||||
Server(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) enum AuthenticationTarget {
|
||||
MeshTLS {
|
||||
namespace: Option<String>,
|
||||
name: String,
|
||||
},
|
||||
Network {
|
||||
namespace: Option<String>,
|
||||
name: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl TryFrom<k8s::policy::AuthorizationPolicySpec> for Spec {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(ap: k8s::policy::AuthorizationPolicySpec) -> Result<Self> {
|
||||
let target = target(ap.target_ref)?;
|
||||
|
||||
let authentications = ap
|
||||
.required_authentication_refs
|
||||
.into_iter()
|
||||
.map(authentication_ref)
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
if authentications.is_empty() {
|
||||
bail!("No authentication targets");
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
target,
|
||||
authentications,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Target {
|
||||
pub(crate) fn server(&self) -> Option<&str> {
|
||||
match self {
|
||||
Self::Server(s) => Some(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn target(t: LocalTargetRef) -> Result<Target> {
|
||||
if t.targets_kind::<k8s::policy::Server>() {
|
||||
return Ok(Target::Server(t.name));
|
||||
}
|
||||
|
||||
anyhow::bail!(
|
||||
"unsupported authorization target type: {}",
|
||||
t.canonical_kind()
|
||||
);
|
||||
}
|
||||
|
||||
fn authentication_ref(t: NamespacedTargetRef) -> Result<AuthenticationTarget> {
|
||||
if t.targets_kind::<k8s::policy::MeshTLSAuthentication>() {
|
||||
Ok(AuthenticationTarget::MeshTLS {
|
||||
namespace: t.namespace.map(Into::into),
|
||||
name: t.name,
|
||||
})
|
||||
} else if t.targets_kind::<k8s::policy::NetworkAuthentication>() {
|
||||
Ok(AuthenticationTarget::Network {
|
||||
namespace: t.namespace.map(Into::into),
|
||||
name: t.name,
|
||||
})
|
||||
} else {
|
||||
anyhow::bail!("unsupported authentication target: {}", t.canonical_kind());
|
||||
}
|
||||
}
|
|
@ -6,12 +6,15 @@
|
|||
//! implements `kubert::index::IndexNamespacedResource` for the indexed
|
||||
//! kubernetes resources.
|
||||
|
||||
use crate::{defaults::DefaultPolicy, pod, server, server_authorization, ClusterInfo};
|
||||
use crate::{
|
||||
authorization_policy, defaults::DefaultPolicy, meshtls_authentication, network_authentication,
|
||||
pod, server, server_authorization, ClusterInfo,
|
||||
};
|
||||
use ahash::{AHashMap as HashMap, AHashSet as HashSet};
|
||||
use anyhow::{bail, Result};
|
||||
use linkerd_policy_controller_core::{
|
||||
ClientAuthentication, ClientAuthorization, IdentityMatch, InboundServer, Ipv4Net, Ipv6Net,
|
||||
ProxyProtocol,
|
||||
AuthorizationRef, ClientAuthentication, ClientAuthorization, IdentityMatch, InboundServer,
|
||||
IpNet, Ipv4Net, Ipv6Net, NetworkMatch, ProxyProtocol, ServerRef,
|
||||
};
|
||||
use linkerd_policy_controller_k8s_api::{self as k8s, policy::server::Port, ResourceExt};
|
||||
use parking_lot::RwLock;
|
||||
|
@ -28,6 +31,7 @@ pub type SharedIndex = Arc<RwLock<Index>>;
|
|||
pub struct Index {
|
||||
cluster_info: Arc<ClusterInfo>,
|
||||
namespaces: NamespaceIndex,
|
||||
authentications: AuthenticationNsIndex,
|
||||
}
|
||||
|
||||
/// Holds all `Pod`, `Server`, and `ServerAuthorization` indices by-namespace.
|
||||
|
@ -37,6 +41,15 @@ struct NamespaceIndex {
|
|||
by_ns: HashMap<String, Namespace>,
|
||||
}
|
||||
|
||||
/// Holds all `NetworkAuthentication` and `MeshTLSAuthentication` indices by-namespace.
|
||||
///
|
||||
/// This is separate from `NamespaceIndex` because authorization policies may reference
|
||||
/// authentication resources across namespaces.
|
||||
#[derive(Debug, Default)]
|
||||
struct AuthenticationNsIndex {
|
||||
by_ns: HashMap<String, AuthenticationIndex>,
|
||||
}
|
||||
|
||||
/// Holds `Pod`, `Server`, and `ServerAuthorization` indices for a single namespace.
|
||||
#[derive(Debug)]
|
||||
struct Namespace {
|
||||
|
@ -45,7 +58,7 @@ struct Namespace {
|
|||
}
|
||||
|
||||
/// Holds all pod data for a single namespace.
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug)]
|
||||
struct PodIndex {
|
||||
namespace: String,
|
||||
by_name: HashMap<String, Pod>,
|
||||
|
@ -90,9 +103,24 @@ struct PodPortServer {
|
|||
/// Holds the state of policy resources for a single namespace.
|
||||
#[derive(Debug)]
|
||||
struct PolicyIndex {
|
||||
namespace: String,
|
||||
cluster_info: Arc<ClusterInfo>,
|
||||
|
||||
servers: HashMap<String, server::Server>,
|
||||
server_authorizations: HashMap<String, server_authorization::ServerAuthz>,
|
||||
|
||||
authorization_policies: HashMap<String, authorization_policy::Spec>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct AuthenticationIndex {
|
||||
meshtls: HashMap<String, meshtls_authentication::Spec>,
|
||||
network: HashMap<String, network_authentication::Spec>,
|
||||
}
|
||||
|
||||
struct NsUpdate<T> {
|
||||
added: Vec<(String, T)>,
|
||||
removed: HashSet<String>,
|
||||
}
|
||||
|
||||
// === impl Index ===
|
||||
|
@ -104,8 +132,9 @@ impl Index {
|
|||
cluster_info: cluster_info.clone(),
|
||||
namespaces: NamespaceIndex {
|
||||
cluster_info,
|
||||
by_ns: HashMap::new(),
|
||||
by_ns: HashMap::default(),
|
||||
},
|
||||
authentications: AuthenticationNsIndex::default(),
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -134,13 +163,34 @@ impl Index {
|
|||
.rx
|
||||
.clone())
|
||||
}
|
||||
|
||||
fn ns_with_reindex(&mut self, namespace: String, f: impl FnOnce(&mut Namespace) -> bool) {
|
||||
self.namespaces
|
||||
.get_with_reindex(namespace, &self.authentications, f)
|
||||
}
|
||||
|
||||
fn ns_or_default_with_reindex(
|
||||
&mut self,
|
||||
namespace: String,
|
||||
f: impl FnOnce(&mut Namespace) -> bool,
|
||||
) {
|
||||
self.namespaces
|
||||
.get_or_default_with_reindex(namespace, &self.authentications, f)
|
||||
}
|
||||
|
||||
fn reindex_all(&mut self) {
|
||||
tracing::debug!("Reindexing all namespaces");
|
||||
for ns in self.namespaces.by_ns.values_mut() {
|
||||
ns.reindex(&self.authentications);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl kubert::index::IndexNamespacedResource<k8s::Pod> for Index {
|
||||
fn apply(&mut self, pod: k8s::Pod) {
|
||||
let namespace = pod.namespace().unwrap();
|
||||
let name = pod.name();
|
||||
let _span = info_span!("apply", ns = %namespace, pod = %name).entered();
|
||||
let _span = info_span!("apply", ns = %namespace, %name).entered();
|
||||
|
||||
let port_names = pod::tcp_port_names(pod.spec);
|
||||
let meta = pod::Meta::from_metadata(pod.metadata);
|
||||
|
@ -151,21 +201,20 @@ impl kubert::index::IndexNamespacedResource<k8s::Pod> for Index {
|
|||
let ns = self.namespaces.get_or_default(namespace);
|
||||
match ns.pods.update(name, meta, port_names) {
|
||||
Ok(None) => {}
|
||||
Ok(Some(pod)) => pod.reindex_servers(&ns.policy),
|
||||
Ok(Some(pod)) => pod.reindex_servers(&ns.policy, &self.authentications),
|
||||
Err(error) => {
|
||||
tracing::error!(%error, "Illegal pod update");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn delete(&mut self, ns: String, pod: String) {
|
||||
let _span = info_span!("delete", %ns, %pod).entered();
|
||||
|
||||
fn delete(&mut self, ns: String, name: String) {
|
||||
tracing::debug!(%ns, %name, "delete");
|
||||
if let Entry::Occupied(mut ns) = self.namespaces.by_ns.entry(ns) {
|
||||
// Once the pod is removed, there's nothing else to update. Any open
|
||||
// watches will complete. No other parts of the index need to be
|
||||
// updated.
|
||||
if ns.get_mut().pods.by_name.remove(&pod).is_some() && ns.get().is_empty() {
|
||||
if ns.get_mut().pods.by_name.remove(&name).is_some() && ns.get().is_empty() {
|
||||
ns.remove();
|
||||
}
|
||||
}
|
||||
|
@ -179,30 +228,23 @@ impl kubert::index::IndexNamespacedResource<k8s::policy::Server> for Index {
|
|||
fn apply(&mut self, srv: k8s::policy::Server) {
|
||||
let ns = srv.namespace().expect("server must be namespaced");
|
||||
let name = srv.name();
|
||||
let _span = info_span!("apply", %ns, srv = %name).entered();
|
||||
let _span = info_span!("apply", %ns, %name).entered();
|
||||
|
||||
let server = server::Server::from_resource(srv, &self.cluster_info);
|
||||
self.namespaces
|
||||
.get_or_default_with_reindex(ns, |ns| ns.policy.update_server(name, server))
|
||||
self.ns_or_default_with_reindex(ns, |ns| ns.policy.update_server(name, server))
|
||||
}
|
||||
|
||||
fn delete(&mut self, ns: String, srv: String) {
|
||||
let _span = info_span!("delete", %ns, %srv).entered();
|
||||
self.namespaces
|
||||
.get_with_reindex(ns, |ns| ns.policy.servers.remove(&srv).is_some())
|
||||
fn delete(&mut self, ns: String, name: String) {
|
||||
let _span = info_span!("delete", %ns, %name).entered();
|
||||
self.ns_with_reindex(ns, |ns| ns.policy.servers.remove(&name).is_some())
|
||||
}
|
||||
|
||||
fn reset(&mut self, srvs: Vec<k8s::policy::Server>, deleted: HashMap<String, HashSet<String>>) {
|
||||
let _span = info_span!("reset").entered();
|
||||
|
||||
#[derive(Default)]
|
||||
struct Ns {
|
||||
added: Vec<(String, server::Server)>,
|
||||
removed: HashSet<String>,
|
||||
}
|
||||
|
||||
// Aggregate all of the updates by namespace so that we only reindex
|
||||
// once per namespace.
|
||||
type Ns = NsUpdate<server::Server>;
|
||||
let mut updates_by_ns = HashMap::<String, Ns>::default();
|
||||
for srv in srvs.into_iter() {
|
||||
let namespace = srv.namespace().expect("server must be namespaced");
|
||||
|
@ -224,7 +266,7 @@ impl kubert::index::IndexNamespacedResource<k8s::policy::Server> for Index {
|
|||
// want to create a default namespace instance, we just want to
|
||||
// clear out all resources for the namespace (and then drop the
|
||||
// whole namespace, if necessary).
|
||||
self.namespaces.get_with_reindex(namespace, |ns| {
|
||||
self.ns_with_reindex(namespace, |ns| {
|
||||
ns.policy.servers.clear();
|
||||
true
|
||||
});
|
||||
|
@ -232,8 +274,7 @@ impl kubert::index::IndexNamespacedResource<k8s::policy::Server> for Index {
|
|||
// Otherwise, we take greater care to reindex only when the
|
||||
// state actually changed. The vast majority of resets will see
|
||||
// no actual data change.
|
||||
self.namespaces
|
||||
.get_or_default_with_reindex(namespace, |ns| {
|
||||
self.ns_or_default_with_reindex(namespace, |ns| {
|
||||
let mut changed = !removed.is_empty();
|
||||
for name in removed.into_iter() {
|
||||
ns.policy.servers.remove(&name);
|
||||
|
@ -252,20 +293,20 @@ impl kubert::index::IndexNamespacedResource<k8s::policy::ServerAuthorization> fo
|
|||
fn apply(&mut self, saz: k8s::policy::ServerAuthorization) {
|
||||
let ns = saz.namespace().unwrap();
|
||||
let name = saz.name();
|
||||
let _span = info_span!("apply", %ns, saz = %name).entered();
|
||||
let _span = info_span!("apply", %ns, %name).entered();
|
||||
|
||||
match server_authorization::ServerAuthz::from_resource(saz, &self.cluster_info) {
|
||||
Ok(meta) => self.namespaces.get_or_default_with_reindex(ns, move |ns| {
|
||||
Ok(meta) => self.ns_or_default_with_reindex(ns, move |ns| {
|
||||
ns.policy.update_server_authz(name, meta)
|
||||
}),
|
||||
Err(error) => tracing::error!(%error, "Illegal server authorization update"),
|
||||
}
|
||||
}
|
||||
|
||||
fn delete(&mut self, ns: String, saz: String) {
|
||||
let _span = info_span!("delete", %ns, %saz).entered();
|
||||
self.namespaces.get_with_reindex(ns, |ns| {
|
||||
ns.policy.server_authorizations.remove(&saz).is_some()
|
||||
fn delete(&mut self, ns: String, name: String) {
|
||||
let _span = info_span!("delete", %ns, %name).entered();
|
||||
self.ns_with_reindex(ns, |ns| {
|
||||
ns.policy.server_authorizations.remove(&name).is_some()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -276,14 +317,9 @@ impl kubert::index::IndexNamespacedResource<k8s::policy::ServerAuthorization> fo
|
|||
) {
|
||||
let _span = info_span!("reset");
|
||||
|
||||
#[derive(Default)]
|
||||
struct Ns {
|
||||
added: Vec<(String, server_authorization::ServerAuthz)>,
|
||||
removed: HashSet<String>,
|
||||
}
|
||||
|
||||
// Aggregate all of the updates by namespace so that we only reindex
|
||||
// once per namespace.
|
||||
type Ns = NsUpdate<server_authorization::ServerAuthz>;
|
||||
let mut updates_by_ns = HashMap::<String, Ns>::default();
|
||||
for saz in sazs.into_iter() {
|
||||
let namespace = saz
|
||||
|
@ -297,7 +333,7 @@ impl kubert::index::IndexNamespacedResource<k8s::policy::ServerAuthorization> fo
|
|||
.added
|
||||
.push((name, saz)),
|
||||
Err(error) => {
|
||||
tracing::error!(ns = %namespace, saz = %name, %error, "Illegal server authorization update")
|
||||
tracing::error!(ns = %namespace, %name, %error, "Illegal server authorization update")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -311,7 +347,7 @@ impl kubert::index::IndexNamespacedResource<k8s::policy::ServerAuthorization> fo
|
|||
// want to create a default namespace instance, we just want to
|
||||
// clear out all resources for the namespace (and then drop the
|
||||
// whole namespace, if necessary).
|
||||
self.namespaces.get_with_reindex(namespace, |ns| {
|
||||
self.ns_with_reindex(namespace, |ns| {
|
||||
ns.policy.server_authorizations.clear();
|
||||
true
|
||||
});
|
||||
|
@ -319,8 +355,7 @@ impl kubert::index::IndexNamespacedResource<k8s::policy::ServerAuthorization> fo
|
|||
// Otherwise, we take greater care to reindex only when the
|
||||
// state actually changed. The vast majority of resets will see
|
||||
// no actual data change.
|
||||
self.namespaces
|
||||
.get_or_default_with_reindex(namespace, |ns| {
|
||||
self.ns_or_default_with_reindex(namespace, |ns| {
|
||||
let mut changed = !removed.is_empty();
|
||||
for name in removed.into_iter() {
|
||||
ns.policy.server_authorizations.remove(&name);
|
||||
|
@ -335,7 +370,246 @@ impl kubert::index::IndexNamespacedResource<k8s::policy::ServerAuthorization> fo
|
|||
}
|
||||
}
|
||||
|
||||
// === impl NamespaceIndex ===
|
||||
impl kubert::index::IndexNamespacedResource<k8s::policy::AuthorizationPolicy> for Index {
|
||||
fn apply(&mut self, policy: k8s::policy::AuthorizationPolicy) {
|
||||
let ns = policy.namespace().unwrap();
|
||||
let name = policy.name();
|
||||
let _span = info_span!("apply", %ns, saz = %name).entered();
|
||||
|
||||
let spec = match authorization_policy::Spec::try_from(policy.spec) {
|
||||
Ok(spec) => spec,
|
||||
Err(error) => {
|
||||
tracing::warn!(%error, "Invalid authorization policy");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
self.ns_or_default_with_reindex(ns, |ns| ns.policy.update_authz_policy(name, spec))
|
||||
}
|
||||
|
||||
fn delete(&mut self, ns: String, ap: String) {
|
||||
let _span = info_span!("delete", %ns, %ap).entered();
|
||||
self.ns_with_reindex(ns, |ns| {
|
||||
ns.policy.authorization_policies.remove(&ap).is_some()
|
||||
})
|
||||
}
|
||||
|
||||
fn reset(
|
||||
&mut self,
|
||||
policies: Vec<k8s::policy::AuthorizationPolicy>,
|
||||
deleted: HashMap<String, HashSet<String>>,
|
||||
) {
|
||||
let _span = info_span!("reset");
|
||||
|
||||
// Aggregate all of the updates by namespace so that we only reindex
|
||||
// once per namespace.
|
||||
type Ns = NsUpdate<authorization_policy::Spec>;
|
||||
let mut updates_by_ns = HashMap::<String, Ns>::default();
|
||||
for policy in policies.into_iter() {
|
||||
let namespace = policy
|
||||
.namespace()
|
||||
.expect("authorizationpolicy must be namespaced");
|
||||
let name = policy.name();
|
||||
match authorization_policy::Spec::try_from(policy.spec) {
|
||||
Ok(spec) => updates_by_ns
|
||||
.entry(namespace)
|
||||
.or_default()
|
||||
.added
|
||||
.push((name, spec)),
|
||||
Err(error) => {
|
||||
tracing::error!(ns = %namespace, %name, %error, "Illegal server authorization update")
|
||||
}
|
||||
}
|
||||
}
|
||||
for (ns, names) in deleted.into_iter() {
|
||||
updates_by_ns.entry(ns).or_default().removed = names;
|
||||
}
|
||||
|
||||
for (namespace, Ns { added, removed }) in updates_by_ns.into_iter() {
|
||||
if added.is_empty() {
|
||||
// If there are no live resources in the namespace, we do not
|
||||
// want to create a default namespace instance, we just want to
|
||||
// clear out all resources for the namespace (and then drop the
|
||||
// whole namespace, if necessary).
|
||||
self.ns_with_reindex(namespace, |ns| {
|
||||
ns.policy.authorization_policies.clear();
|
||||
true
|
||||
});
|
||||
} else {
|
||||
// Otherwise, we take greater care to reindex only when the
|
||||
// state actually changed. The vast majority of resets will see
|
||||
// no actual data change.
|
||||
self.ns_or_default_with_reindex(namespace, |ns| {
|
||||
let mut changed = !removed.is_empty();
|
||||
for name in removed.into_iter() {
|
||||
ns.policy.authorization_policies.remove(&name);
|
||||
}
|
||||
for (name, spec) in added.into_iter() {
|
||||
changed = ns.policy.update_authz_policy(name, spec) || changed;
|
||||
}
|
||||
changed
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl kubert::index::IndexNamespacedResource<k8s::policy::MeshTLSAuthentication> for Index {
|
||||
fn apply(&mut self, authn: k8s::policy::MeshTLSAuthentication) {
|
||||
let ns = authn
|
||||
.namespace()
|
||||
.expect("MeshTLSAuthentication must have a namespace");
|
||||
let name = authn.name();
|
||||
let _span = info_span!("apply", %ns, %name).entered();
|
||||
|
||||
let spec = match meshtls_authentication::Spec::try_from_resource(authn, &self.cluster_info)
|
||||
{
|
||||
Ok(spec) => spec,
|
||||
Err(error) => {
|
||||
tracing::warn!(%error, "Invalid MeshTLSAuthentication");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if self.authentications.update_meshtls(ns, name, spec) {
|
||||
self.reindex_all();
|
||||
}
|
||||
}
|
||||
|
||||
fn delete(&mut self, ns: String, name: String) {
|
||||
let _span = info_span!("delete", %ns, %name).entered();
|
||||
|
||||
if let Entry::Occupied(mut ns) = self.authentications.by_ns.entry(ns) {
|
||||
tracing::debug!("Deleting MeshTLSAuthentication");
|
||||
ns.get_mut().network.remove(&name);
|
||||
if ns.get().is_empty() {
|
||||
ns.remove();
|
||||
}
|
||||
self.reindex_all();
|
||||
} else {
|
||||
tracing::warn!("Namespace already deleted!");
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(
|
||||
&mut self,
|
||||
authns: Vec<k8s::policy::MeshTLSAuthentication>,
|
||||
deleted: HashMap<String, HashSet<String>>,
|
||||
) {
|
||||
let _span = info_span!("reset");
|
||||
|
||||
let mut changed = false;
|
||||
|
||||
for authn in authns.into_iter() {
|
||||
let namespace = authn
|
||||
.namespace()
|
||||
.expect("meshtlsauthentication must be namespaced");
|
||||
let name = authn.name();
|
||||
let spec = match meshtls_authentication::Spec::try_from_resource(
|
||||
authn,
|
||||
&self.cluster_info,
|
||||
) {
|
||||
Ok(spec) => spec,
|
||||
Err(error) => {
|
||||
tracing::warn!(ns = %namespace, %name, %error, "Invalid MeshTLSAuthentication");
|
||||
return;
|
||||
}
|
||||
};
|
||||
changed = self.authentications.update_meshtls(namespace, name, spec) || changed;
|
||||
}
|
||||
for (namespace, names) in deleted.into_iter() {
|
||||
if let Entry::Occupied(mut ns) = self.authentications.by_ns.entry(namespace) {
|
||||
for name in names.into_iter() {
|
||||
ns.get_mut().meshtls.remove(&name);
|
||||
}
|
||||
if ns.get().is_empty() {
|
||||
ns.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if changed {
|
||||
self.reindex_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl kubert::index::IndexNamespacedResource<k8s::policy::NetworkAuthentication> for Index {
|
||||
fn apply(&mut self, authn: k8s::policy::NetworkAuthentication) {
|
||||
let ns = authn.namespace().unwrap();
|
||||
let name = authn.name();
|
||||
let _span = info_span!("apply", %ns, %name).entered();
|
||||
|
||||
let spec = match network_authentication::Spec::try_from(authn.spec) {
|
||||
Ok(spec) => spec,
|
||||
Err(error) => {
|
||||
tracing::warn!(%error, "Invalid NetworkAuthentication");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if self.authentications.update_network(ns, name, spec) {
|
||||
self.reindex_all();
|
||||
}
|
||||
}
|
||||
|
||||
fn delete(&mut self, ns: String, name: String) {
|
||||
let _span = info_span!("delete", %ns, %name).entered();
|
||||
|
||||
if let Entry::Occupied(mut ns) = self.authentications.by_ns.entry(ns) {
|
||||
tracing::debug!("Deleting MeshTLSAuthentication");
|
||||
|
||||
ns.get_mut().network.remove(&name);
|
||||
if ns.get().is_empty() {
|
||||
ns.remove();
|
||||
}
|
||||
self.reindex_all();
|
||||
} else {
|
||||
tracing::warn!("Namespace already deleted!");
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(
|
||||
&mut self,
|
||||
authns: Vec<k8s::policy::NetworkAuthentication>,
|
||||
deleted: HashMap<String, HashSet<String>>,
|
||||
) {
|
||||
let _span = info_span!("reset");
|
||||
|
||||
let mut changed = false;
|
||||
|
||||
for authn in authns.into_iter() {
|
||||
let namespace = authn
|
||||
.namespace()
|
||||
.expect("meshtlsauthentication must be namespaced");
|
||||
let name = authn.name();
|
||||
let spec = match network_authentication::Spec::try_from(authn.spec) {
|
||||
Ok(spec) => spec,
|
||||
Err(error) => {
|
||||
tracing::warn!(ns = %namespace, %name, %error, "Invalid NetworkAuthentication");
|
||||
return;
|
||||
}
|
||||
};
|
||||
changed = self.authentications.update_network(namespace, name, spec) || changed;
|
||||
}
|
||||
for (namespace, names) in deleted.into_iter() {
|
||||
if let Entry::Occupied(mut ns) = self.authentications.by_ns.entry(namespace) {
|
||||
for name in names.into_iter() {
|
||||
ns.get_mut().meshtls.remove(&name);
|
||||
}
|
||||
if ns.get().is_empty() {
|
||||
ns.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if changed {
|
||||
self.reindex_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === impl NemspaceIndex ===
|
||||
|
||||
impl NamespaceIndex {
|
||||
fn get_or_default(&mut self, ns: String) -> &mut Namespace {
|
||||
|
@ -345,16 +619,21 @@ impl NamespaceIndex {
|
|||
}
|
||||
|
||||
/// Gets the given namespace and, if it exists, passes it to the given
|
||||
/// function. When the function returns `true`, all pods in the namespace are
|
||||
/// reindexed or, if the namespace is empty, the namespace is removed
|
||||
/// entirely.
|
||||
fn get_with_reindex(&mut self, namespace: String, f: impl FnOnce(&mut Namespace) -> bool) {
|
||||
/// function. If the function returns true, all pods in the namespace are
|
||||
/// reindexed; or, if the function returns false and the namespace is empty,
|
||||
/// it is removed from the index.
|
||||
fn get_with_reindex(
|
||||
&mut self,
|
||||
namespace: String,
|
||||
authns: &AuthenticationNsIndex,
|
||||
f: impl FnOnce(&mut Namespace) -> bool,
|
||||
) {
|
||||
if let Entry::Occupied(mut ns) = self.by_ns.entry(namespace) {
|
||||
if f(ns.get_mut()) {
|
||||
if ns.get().is_empty() {
|
||||
ns.remove();
|
||||
} else {
|
||||
ns.get_mut().reindex();
|
||||
ns.get_mut().reindex(authns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -366,11 +645,12 @@ impl NamespaceIndex {
|
|||
fn get_or_default_with_reindex(
|
||||
&mut self,
|
||||
namespace: String,
|
||||
authns: &AuthenticationNsIndex,
|
||||
f: impl FnOnce(&mut Namespace) -> bool,
|
||||
) {
|
||||
let ns = self.get_or_default(namespace);
|
||||
if f(ns) {
|
||||
ns.reindex();
|
||||
ns.reindex(authns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -381,13 +661,15 @@ impl Namespace {
|
|||
fn new(namespace: String, cluster_info: Arc<ClusterInfo>) -> Self {
|
||||
Namespace {
|
||||
pods: PodIndex {
|
||||
namespace,
|
||||
namespace: namespace.clone(),
|
||||
by_name: HashMap::default(),
|
||||
},
|
||||
policy: PolicyIndex {
|
||||
namespace,
|
||||
cluster_info,
|
||||
servers: HashMap::default(),
|
||||
server_authorizations: HashMap::default(),
|
||||
authorization_policies: HashMap::default(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -399,8 +681,8 @@ impl Namespace {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
fn reindex(&mut self) {
|
||||
self.pods.reindex(&self.policy);
|
||||
fn reindex(&mut self, authns: &AuthenticationNsIndex) {
|
||||
self.pods.reindex(&self.policy, authns);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -418,16 +700,12 @@ impl PodIndex {
|
|||
meta: pod::Meta,
|
||||
port_names: HashMap<String, pod::PortSet>,
|
||||
) -> Result<Option<&mut Pod>> {
|
||||
let pod = match self.by_name.entry(name) {
|
||||
Entry::Vacant(entry) => {
|
||||
tracing::debug!(?meta, ?port_names, "Creating");
|
||||
let pod = Pod {
|
||||
let pod = match self.by_name.entry(name.clone()) {
|
||||
Entry::Vacant(entry) => entry.insert(Pod {
|
||||
meta,
|
||||
port_names,
|
||||
port_servers: pod::PortMap::default(),
|
||||
};
|
||||
entry.insert(pod)
|
||||
}
|
||||
}),
|
||||
|
||||
Entry::Occupied(entry) => {
|
||||
let pod = entry.into_mut();
|
||||
|
@ -435,16 +713,16 @@ impl PodIndex {
|
|||
// Pod labels and annotations may change at runtime, but the
|
||||
// port list may not
|
||||
if pod.port_names != port_names {
|
||||
bail!("pod port names must not change");
|
||||
bail!("pod {} port names must not change", name);
|
||||
}
|
||||
|
||||
// If there aren't meaningful changes, then don't bother doing
|
||||
// any more work.
|
||||
if pod.meta == meta {
|
||||
tracing::trace!("No changes");
|
||||
tracing::debug!(pod = %name, "No changes");
|
||||
return Ok(None);
|
||||
}
|
||||
tracing::debug!(?meta, "Updating");
|
||||
tracing::debug!(pod = %name, "Updating");
|
||||
pod.meta = meta;
|
||||
pod
|
||||
}
|
||||
|
@ -452,11 +730,11 @@ impl PodIndex {
|
|||
Ok(Some(pod))
|
||||
}
|
||||
|
||||
fn reindex(&mut self, policy: &PolicyIndex) {
|
||||
fn reindex(&mut self, policy: &PolicyIndex, authns: &AuthenticationNsIndex) {
|
||||
let _span = info_span!("reindex", ns = %self.namespace).entered();
|
||||
for (name, pod) in self.by_name.iter_mut() {
|
||||
let _span = info_span!("pod", pod = %name).entered();
|
||||
pod.reindex_servers(policy);
|
||||
pod.reindex_servers(policy, authns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -465,9 +743,7 @@ impl PodIndex {
|
|||
|
||||
impl Pod {
|
||||
/// Determines the policies for ports on this pod.
|
||||
fn reindex_servers(&mut self, policy: &PolicyIndex) {
|
||||
tracing::debug!("Indexing servers for pod");
|
||||
|
||||
fn reindex_servers(&mut self, policy: &PolicyIndex, authentications: &AuthenticationNsIndex) {
|
||||
// Keep track of the ports that are already known in the pod so that, after applying server
|
||||
// matches, we can ensure remaining ports are set to the default policy.
|
||||
let mut unmatched_ports = self.port_servers.keys().copied().collect::<pod::PortSet>();
|
||||
|
@ -497,7 +773,7 @@ impl Pod {
|
|||
continue;
|
||||
}
|
||||
|
||||
let s = policy.inbound_server(srvname.clone(), server);
|
||||
let s = policy.inbound_server(srvname.clone(), server, authentications);
|
||||
self.update_server(port, srvname, s);
|
||||
|
||||
matched_ports.insert(port, srvname.clone());
|
||||
|
@ -519,6 +795,7 @@ impl Pod {
|
|||
fn update_server(&mut self, port: u16, name: &str, server: InboundServer) {
|
||||
match self.port_servers.entry(port) {
|
||||
Entry::Vacant(entry) => {
|
||||
tracing::trace!(port = %port, server = %name, "Creating server");
|
||||
let (tx, rx) = watch::channel(server);
|
||||
entry.insert(PodPortServer {
|
||||
name: Some(name.to_string()),
|
||||
|
@ -533,6 +810,7 @@ impl Pod {
|
|||
// Avoid sending redundant updates.
|
||||
if ps.name.as_deref() == Some(name) && *ps.rx.borrow() == server {
|
||||
tracing::trace!(port = %port, server = %name, "Skipped redundant server update");
|
||||
tracing::trace!(?server);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -542,6 +820,7 @@ impl Pod {
|
|||
// make the opportunistic choice to assume the cluster is
|
||||
// configured coherently so we take the update. The admission
|
||||
// controller should prevent conflicts.
|
||||
tracing::trace!(port = %port, server = %name, "Updating server");
|
||||
ps.name = Some(name.to_string());
|
||||
ps.tx.send(server).expect("a receiver is held by the index");
|
||||
}
|
||||
|
@ -553,9 +832,9 @@ impl Pod {
|
|||
/// Updates a pod-port to use the given named server.
|
||||
fn set_default_server(&mut self, port: u16, config: &ClusterInfo) {
|
||||
let server = Self::default_inbound_server(port, &self.meta.settings, config);
|
||||
tracing::debug!(%port, server = %config.default_policy, "Setting default server");
|
||||
match self.port_servers.entry(port) {
|
||||
Entry::Vacant(entry) => {
|
||||
tracing::debug!(%port, server = %config.default_policy, "Creating default server");
|
||||
let (tx, rx) = watch::channel(server);
|
||||
entry.insert(PodPortServer { name: None, tx, rx });
|
||||
}
|
||||
|
@ -565,9 +844,11 @@ impl Pod {
|
|||
|
||||
// Avoid sending redundant updates.
|
||||
if *ps.rx.borrow() == server {
|
||||
tracing::trace!(%port, server = %config.default_policy, "Default server already set");
|
||||
return;
|
||||
}
|
||||
|
||||
tracing::debug!(%port, server = %config.default_policy, "Setting default server");
|
||||
ps.name = None;
|
||||
ps.tx.send(server).expect("a receiver is held by the index");
|
||||
}
|
||||
|
@ -642,10 +923,13 @@ impl Pod {
|
|||
let networks = if cluster_only {
|
||||
config.networks.iter().copied().map(Into::into).collect()
|
||||
} else {
|
||||
vec![Ipv4Net::default().into(), Ipv6Net::default().into()]
|
||||
vec![
|
||||
"0.0.0.0/0".parse::<IpNet>().unwrap().into(),
|
||||
"::/0".parse::<IpNet>().unwrap().into(),
|
||||
]
|
||||
};
|
||||
authorizations.insert(
|
||||
format!("default:{}", policy),
|
||||
AuthorizationRef::Default(policy.to_string()),
|
||||
ClientAuthorization {
|
||||
authentication,
|
||||
networks,
|
||||
|
@ -653,9 +937,8 @@ impl Pod {
|
|||
);
|
||||
};
|
||||
|
||||
tracing::trace!(port, ?settings, %policy, ?protocol, ?authorizations, "default server");
|
||||
InboundServer {
|
||||
name: format!("default:{}", policy),
|
||||
reference: ServerRef::Default(policy.to_string()),
|
||||
protocol,
|
||||
authorizations,
|
||||
}
|
||||
|
@ -708,10 +991,32 @@ impl PolicyIndex {
|
|||
true
|
||||
}
|
||||
|
||||
fn inbound_server(&self, name: String, server: &server::Server) -> InboundServer {
|
||||
let authorizations = self.client_authzs(&name, server);
|
||||
fn update_authz_policy(&mut self, name: String, spec: authorization_policy::Spec) -> bool {
|
||||
match self.authorization_policies.entry(name) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(spec);
|
||||
}
|
||||
Entry::Occupied(entry) => {
|
||||
let ap = entry.into_mut();
|
||||
if *ap == spec {
|
||||
return false;
|
||||
}
|
||||
*ap = spec;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn inbound_server(
|
||||
&self,
|
||||
name: String,
|
||||
server: &server::Server,
|
||||
authentications: &AuthenticationNsIndex,
|
||||
) -> InboundServer {
|
||||
tracing::trace!(%name, ?server, "Creating inbound server");
|
||||
let authorizations = self.client_authzs(&name, server, authentications);
|
||||
InboundServer {
|
||||
name,
|
||||
reference: ServerRef::Server(name),
|
||||
authorizations,
|
||||
protocol: server.protocol.clone(),
|
||||
}
|
||||
|
@ -721,16 +1026,215 @@ impl PolicyIndex {
|
|||
&self,
|
||||
server_name: &str,
|
||||
server: &server::Server,
|
||||
) -> HashMap<String, ClientAuthorization> {
|
||||
self.server_authorizations
|
||||
.iter()
|
||||
.filter_map(|(name, saz)| {
|
||||
authentications: &AuthenticationNsIndex,
|
||||
) -> HashMap<AuthorizationRef, ClientAuthorization> {
|
||||
let mut authzs = HashMap::default();
|
||||
for (name, saz) in self.server_authorizations.iter() {
|
||||
if saz.server_selector.selects(server_name, &server.labels) {
|
||||
Some((name.to_string(), saz.authz.clone()))
|
||||
} else {
|
||||
None
|
||||
authzs.insert(
|
||||
AuthorizationRef::ServerAuthorization(name.to_string()),
|
||||
saz.authz.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (name, spec) in self.authorization_policies.iter() {
|
||||
match spec.target.server() {
|
||||
Some(target) if target != server_name => {
|
||||
tracing::trace!(
|
||||
ns = %self.namespace,
|
||||
authorizationpolicy = %name,
|
||||
server = %server_name,
|
||||
%target,
|
||||
"AuthorizationPolicy does not target server",
|
||||
);
|
||||
continue;
|
||||
}
|
||||
None => continue,
|
||||
Some(_) => {}
|
||||
}
|
||||
|
||||
tracing::trace!(
|
||||
ns = %self.namespace,
|
||||
authorizationpolicy = %name,
|
||||
server = %server_name,
|
||||
"AuthorizationPolicy targets server",
|
||||
);
|
||||
tracing::trace!(authns = ?spec.authentications);
|
||||
|
||||
let authz = match self.policy_client_authz(spec, authentications) {
|
||||
Ok(authz) => authz,
|
||||
Err(error) => {
|
||||
tracing::info!(
|
||||
server = %server_name,
|
||||
authorizationpolicy = %name,
|
||||
%error,
|
||||
"Illegal AuthorizationPolicy; ignoring",
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let reference = AuthorizationRef::AuthorizationPolicy(name.to_string());
|
||||
authzs.insert(reference, authz);
|
||||
}
|
||||
|
||||
authzs
|
||||
}
|
||||
|
||||
fn policy_client_authz(
|
||||
&self,
|
||||
spec: &authorization_policy::Spec,
|
||||
all_authentications: &AuthenticationNsIndex,
|
||||
) -> Result<ClientAuthorization> {
|
||||
use authorization_policy::AuthenticationTarget;
|
||||
|
||||
let mut identities = None;
|
||||
for tgt in spec.authentications.iter() {
|
||||
if let AuthenticationTarget::MeshTLS {
|
||||
ref namespace,
|
||||
ref name,
|
||||
} = tgt
|
||||
{
|
||||
let namespace = namespace.as_deref().unwrap_or(&self.namespace);
|
||||
tracing::trace!(ns = %namespace, %name, "Finding MeshTLSAuthentication");
|
||||
if let Some(ns) = all_authentications.by_ns.get(namespace) {
|
||||
if let Some(authn) = ns.meshtls.get(name) {
|
||||
tracing::trace!(ns = %namespace, %name, ids = ?authn.matches, "Found MeshTLSAuthentication");
|
||||
// There can only be a single required MeshTLSAuthentication. This is
|
||||
// enforced by the admission controller.
|
||||
if identities.is_some() {
|
||||
bail!("policy must not include multiple MeshTLSAuthentications");
|
||||
}
|
||||
let ids = authn.matches.clone();
|
||||
identities = Some(ids);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
bail!(
|
||||
"could not find MeshTLSAuthentication {} in namespace {}",
|
||||
name,
|
||||
namespace
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let mut networks = None;
|
||||
for tgt in spec.authentications.iter() {
|
||||
if let AuthenticationTarget::Network {
|
||||
ref namespace,
|
||||
ref name,
|
||||
} = tgt
|
||||
{
|
||||
let namespace = namespace.as_deref().unwrap_or(&self.namespace);
|
||||
tracing::trace!(ns = %namespace, %name, "Finding NetworkAuthentication");
|
||||
if let Some(ns) = all_authentications.by_ns.get(namespace) {
|
||||
if let Some(authn) = ns.network.get(name).as_ref() {
|
||||
tracing::trace!(ns = %namespace, %name, nets = ?authn.matches, "Found NetworkAuthentication");
|
||||
// There can only be a single required NetworkAuthentication. This is
|
||||
// enforced by the admission controller.
|
||||
if networks.is_some() {
|
||||
bail!("policy must not include multiple NetworkAuthentications");
|
||||
}
|
||||
let nets = authn.matches.clone();
|
||||
networks = Some(nets);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
bail!(
|
||||
"could not find NetworkAuthentication {} in namespace {}",
|
||||
name,
|
||||
namespace
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ClientAuthorization {
|
||||
// If MTLS identities are configured, use them. Otherwise, do not require
|
||||
// authentication.
|
||||
authentication: identities
|
||||
.map(ClientAuthentication::TlsAuthenticated)
|
||||
.unwrap_or(ClientAuthentication::Unauthenticated),
|
||||
|
||||
// If networks are configured, use them. Otherwise, this applies to all networks.
|
||||
networks: networks.unwrap_or_else(|| {
|
||||
vec![
|
||||
NetworkMatch {
|
||||
net: Ipv4Net::default().into(),
|
||||
except: vec![],
|
||||
},
|
||||
NetworkMatch {
|
||||
net: Ipv6Net::default().into(),
|
||||
except: vec![],
|
||||
},
|
||||
]
|
||||
}),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
// === impl AuthenticationNsIndex ===
|
||||
|
||||
impl AuthenticationNsIndex {
|
||||
fn update_meshtls(
|
||||
&mut self,
|
||||
namespace: String,
|
||||
name: String,
|
||||
spec: meshtls_authentication::Spec,
|
||||
) -> bool {
|
||||
match self.by_ns.entry(namespace).or_default().meshtls.entry(name) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(spec);
|
||||
}
|
||||
Entry::Occupied(mut entry) => {
|
||||
if *entry.get() == spec {
|
||||
return false;
|
||||
}
|
||||
entry.insert(spec);
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn update_network(
|
||||
&mut self,
|
||||
namespace: String,
|
||||
name: String,
|
||||
spec: network_authentication::Spec,
|
||||
) -> bool {
|
||||
match self.by_ns.entry(namespace).or_default().network.entry(name) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(spec);
|
||||
}
|
||||
Entry::Occupied(mut entry) => {
|
||||
if *entry.get() == spec {
|
||||
return false;
|
||||
}
|
||||
entry.insert(spec);
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// === impl AuthenticationIndex ===
|
||||
|
||||
impl AuthenticationIndex {
|
||||
#[inline]
|
||||
fn is_empty(&self) -> bool {
|
||||
self.meshtls.is_empty() && self.network.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
// === imp NsUpdate ===
|
||||
|
||||
impl<T> Default for NsUpdate<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
added: vec![],
|
||||
removed: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,8 +23,11 @@
|
|||
#![deny(warnings, rust_2018_idioms)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
mod authorization_policy;
|
||||
mod defaults;
|
||||
mod index;
|
||||
mod meshtls_authentication;
|
||||
mod network_authentication;
|
||||
mod pod;
|
||||
mod server;
|
||||
mod server_authorization;
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
use crate::ClusterInfo;
|
||||
use anyhow::Result;
|
||||
use linkerd_policy_controller_core::IdentityMatch;
|
||||
use linkerd_policy_controller_k8s_api::{
|
||||
policy::MeshTLSAuthentication, ResourceExt, ServiceAccount,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) struct Spec {
|
||||
pub matches: Vec<IdentityMatch>,
|
||||
}
|
||||
|
||||
impl Spec {
|
||||
pub(crate) fn try_from_resource(
|
||||
ma: MeshTLSAuthentication,
|
||||
cluster: &ClusterInfo,
|
||||
) -> anyhow::Result<Self> {
|
||||
let namespace = ma
|
||||
.namespace()
|
||||
.expect("MeshTLSAuthentication must have a namespace");
|
||||
|
||||
let identities = ma.spec.identities.into_iter().flatten().map(|s| {
|
||||
Ok(s.parse::<IdentityMatch>()
|
||||
.expect("identity match parsing is infallible"))
|
||||
});
|
||||
|
||||
let identity_refs = ma.spec.identity_refs.into_iter().flatten().map(|tgt| {
|
||||
if tgt.targets_kind::<ServiceAccount>() {
|
||||
let ns = tgt.namespace.as_deref().unwrap_or(&namespace);
|
||||
let id = cluster.service_account_identity(ns, &tgt.name);
|
||||
Ok(IdentityMatch::Exact(id))
|
||||
} else {
|
||||
anyhow::bail!("unsupported target type: {:?}", tgt.canonical_kind())
|
||||
}
|
||||
});
|
||||
|
||||
let matches = identities
|
||||
.chain(identity_refs)
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
if matches.is_empty() {
|
||||
anyhow::bail!("No identities configured");
|
||||
}
|
||||
|
||||
Ok(Spec { matches })
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
use linkerd_policy_controller_core::NetworkMatch;
|
||||
use linkerd_policy_controller_k8s_api::policy::NetworkAuthenticationSpec;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub(crate) struct Spec {
|
||||
pub matches: Vec<NetworkMatch>,
|
||||
}
|
||||
|
||||
impl TryFrom<NetworkAuthenticationSpec> for Spec {
|
||||
type Error = anyhow::Error;
|
||||
|
||||
fn try_from(spec: NetworkAuthenticationSpec) -> anyhow::Result<Self> {
|
||||
let matches = spec
|
||||
.networks
|
||||
.into_iter()
|
||||
.map(|n| NetworkMatch {
|
||||
net: n.cidr.into(),
|
||||
except: n.except.into_iter().flatten().map(Into::into).collect(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if matches.is_empty() {
|
||||
anyhow::bail!("No networks configured");
|
||||
}
|
||||
|
||||
Ok(Spec { matches })
|
||||
}
|
||||
}
|
|
@ -1,13 +1,21 @@
|
|||
mod annotation;
|
||||
mod authorization_policy;
|
||||
mod server_authorization;
|
||||
|
||||
use crate::{defaults::DefaultPolicy, index::*, server_authorization::ServerSelector, ClusterInfo};
|
||||
use ahash::AHashMap as HashMap;
|
||||
use kubert::index::IndexNamespacedResource;
|
||||
use linkerd_policy_controller_core::{
|
||||
ClientAuthentication, ClientAuthorization, IdentityMatch, InboundServer, IpNet, Ipv4Net,
|
||||
Ipv6Net, NetworkMatch, ProxyProtocol,
|
||||
AuthorizationRef, ClientAuthentication, ClientAuthorization, IdentityMatch, InboundServer,
|
||||
IpNet, Ipv4Net, Ipv6Net, NetworkMatch, ProxyProtocol, ServerRef,
|
||||
};
|
||||
use linkerd_policy_controller_k8s_api::{
|
||||
self as k8s, api::core::v1::ContainerPort, policy::server::Port, ResourceExt,
|
||||
self as k8s,
|
||||
api::core::v1::ContainerPort,
|
||||
policy::{server::Port, LocalTargetRef, NamespacedTargetRef},
|
||||
ResourceExt,
|
||||
};
|
||||
use maplit::*;
|
||||
use tokio::time;
|
||||
|
||||
#[test]
|
||||
|
@ -19,329 +27,14 @@ fn pod_must_exist_for_lookup() {
|
|||
.expect_err("pod-0.ns-0 must not exist");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn links_named_server_port() {
|
||||
let test = TestConfig::default();
|
||||
|
||||
let mut pod = mk_pod(
|
||||
"ns-0",
|
||||
"pod-0",
|
||||
Some((
|
||||
"container-0",
|
||||
Some(ContainerPort {
|
||||
name: Some("admin-http".to_string()),
|
||||
container_port: 8080,
|
||||
protocol: Some("TCP".to_string()),
|
||||
..ContainerPort::default()
|
||||
}),
|
||||
)),
|
||||
);
|
||||
pod.labels_mut()
|
||||
.insert("app".to_string(), "app-0".to_string());
|
||||
test.index.write().apply(pod);
|
||||
|
||||
let mut rx = test
|
||||
.index
|
||||
.write()
|
||||
.pod_server_rx("ns-0", "pod-0", 8080)
|
||||
.expect("pod-0.ns-0 should exist");
|
||||
assert_eq!(*rx.borrow_and_update(), test.default_server());
|
||||
|
||||
test.index.write().apply(mk_server(
|
||||
"ns-0",
|
||||
"srv-admin-http",
|
||||
Port::Name("admin-http".to_string()),
|
||||
None,
|
||||
Some(("app", "app-0")),
|
||||
Some(k8s::policy::server::ProxyProtocol::Http1),
|
||||
));
|
||||
assert!(rx.has_changed().unwrap());
|
||||
assert_eq!(
|
||||
*rx.borrow_and_update(),
|
||||
InboundServer {
|
||||
name: "srv-admin-http".to_string(),
|
||||
authorizations: Default::default(),
|
||||
protocol: ProxyProtocol::Http1,
|
||||
},
|
||||
);
|
||||
struct TestConfig {
|
||||
index: SharedIndex,
|
||||
detect_timeout: time::Duration,
|
||||
default_policy: DefaultPolicy,
|
||||
cluster: ClusterInfo,
|
||||
_tracing: tracing::subscriber::DefaultGuard,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn links_unnamed_server_port() {
|
||||
let test = TestConfig::default();
|
||||
|
||||
let mut pod = mk_pod("ns-0", "pod-0", Some(("container-0", None)));
|
||||
pod.labels_mut()
|
||||
.insert("app".to_string(), "app-0".to_string());
|
||||
test.index.write().apply(pod);
|
||||
|
||||
let mut rx = test
|
||||
.index
|
||||
.write()
|
||||
.pod_server_rx("ns-0", "pod-0", 8080)
|
||||
.expect("pod-0.ns-0 should exist");
|
||||
assert_eq!(*rx.borrow_and_update(), test.default_server());
|
||||
|
||||
test.index.write().apply(mk_server(
|
||||
"ns-0",
|
||||
"srv-8080",
|
||||
Port::Number(8080),
|
||||
None,
|
||||
Some(("app", "app-0")),
|
||||
Some(k8s::policy::server::ProxyProtocol::Http1),
|
||||
));
|
||||
assert!(rx.has_changed().unwrap());
|
||||
assert_eq!(
|
||||
*rx.borrow_and_update(),
|
||||
InboundServer {
|
||||
name: "srv-8080".to_string(),
|
||||
authorizations: Default::default(),
|
||||
protocol: ProxyProtocol::Http1,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn links_server_authz_by_name() {
|
||||
link_server_authz(ServerSelector::Name("srv-8080".to_string()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn links_server_authz_by_label() {
|
||||
link_server_authz(ServerSelector::Selector(
|
||||
Some(("app", "app-0")).into_iter().collect(),
|
||||
));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn link_server_authz(selector: ServerSelector) {
|
||||
let test = TestConfig::default();
|
||||
|
||||
let mut pod = mk_pod("ns-0", "pod-0", Some(("container-0", None)));
|
||||
pod.labels_mut()
|
||||
.insert("app".to_string(), "app-0".to_string());
|
||||
test.index.write().apply(pod);
|
||||
|
||||
let mut rx = test
|
||||
.index
|
||||
.write()
|
||||
.pod_server_rx("ns-0", "pod-0", 8080)
|
||||
.expect("pod-0.ns-0 should exist");
|
||||
assert_eq!(*rx.borrow_and_update(), test.default_server());
|
||||
|
||||
test.index.write().apply(mk_server(
|
||||
"ns-0",
|
||||
"srv-8080",
|
||||
Port::Number(8080),
|
||||
Some(("app", "app-0")),
|
||||
Some(("app", "app-0")),
|
||||
Some(k8s::policy::server::ProxyProtocol::Http1),
|
||||
));
|
||||
assert!(rx.has_changed().unwrap());
|
||||
assert_eq!(
|
||||
*rx.borrow_and_update(),
|
||||
InboundServer {
|
||||
name: "srv-8080".to_string(),
|
||||
authorizations: Default::default(),
|
||||
protocol: ProxyProtocol::Http1,
|
||||
},
|
||||
);
|
||||
test.index.write().apply(mk_server_authz(
|
||||
"ns-0",
|
||||
"authz-foo",
|
||||
selector,
|
||||
k8s::policy::server_authorization::Client {
|
||||
networks: Some(vec![k8s::policy::server_authorization::Network {
|
||||
cidr: "10.0.0.0/8".parse().unwrap(),
|
||||
except: None,
|
||||
}]),
|
||||
unauthenticated: false,
|
||||
mesh_tls: Some(k8s::policy::server_authorization::MeshTls {
|
||||
identities: Some(vec!["foo.bar".to_string()]),
|
||||
..Default::default()
|
||||
}),
|
||||
},
|
||||
));
|
||||
assert!(rx.has_changed().unwrap());
|
||||
assert_eq!(rx.borrow().name, "srv-8080");
|
||||
assert_eq!(rx.borrow().protocol, ProxyProtocol::Http1,);
|
||||
assert!(rx.borrow().authorizations.contains_key("authz-foo"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn server_update_deselects_pod() {
|
||||
let test = TestConfig::default();
|
||||
|
||||
test.index.write().reset(
|
||||
vec![mk_pod("ns-0", "pod-0", Some(("container-0", None)))],
|
||||
Default::default(),
|
||||
);
|
||||
|
||||
let mut srv = mk_server(
|
||||
"ns-0",
|
||||
"srv-0",
|
||||
Port::Number(2222),
|
||||
None,
|
||||
None,
|
||||
Some(k8s::policy::server::ProxyProtocol::Http2),
|
||||
);
|
||||
test.index
|
||||
.write()
|
||||
.reset(vec![srv.clone()], Default::default());
|
||||
|
||||
// The default policy applies for all ports.
|
||||
let mut rx = test
|
||||
.index
|
||||
.write()
|
||||
.pod_server_rx("ns-0", "pod-0", 2222)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
*rx.borrow_and_update(),
|
||||
InboundServer {
|
||||
name: "srv-0".into(),
|
||||
protocol: ProxyProtocol::Http2,
|
||||
authorizations: Default::default(),
|
||||
}
|
||||
);
|
||||
|
||||
test.index.write().apply({
|
||||
srv.spec.pod_selector = Some(("label", "value")).into_iter().collect();
|
||||
srv
|
||||
});
|
||||
assert!(rx.has_changed().unwrap());
|
||||
assert_eq!(*rx.borrow(), test.default_server());
|
||||
}
|
||||
|
||||
/// Tests that pod servers are configured with defaults based on the
|
||||
/// workload-defined `DefaultPolicy` policy.
|
||||
///
|
||||
/// Iterates through each default policy and validates that it produces expected
|
||||
/// configurations.
|
||||
#[test]
|
||||
fn default_policy_annotated() {
|
||||
for default in &DEFAULTS {
|
||||
let test = TestConfig::from_default_policy(match *default {
|
||||
// Invert default to ensure override applies.
|
||||
DefaultPolicy::Deny => DefaultPolicy::Allow {
|
||||
authenticated_only: false,
|
||||
cluster_only: false,
|
||||
},
|
||||
_ => DefaultPolicy::Deny,
|
||||
});
|
||||
|
||||
// Initially create the pod without an annotation and check that it gets
|
||||
// the global default.
|
||||
let mut pod = mk_pod("ns-0", "pod-0", Some(("container-0", None)));
|
||||
test.index
|
||||
.write()
|
||||
.reset(vec![pod.clone()], Default::default());
|
||||
|
||||
let mut rx = test
|
||||
.index
|
||||
.write()
|
||||
.pod_server_rx("ns-0", "pod-0", 2222)
|
||||
.expect("pod-0.ns-0 should exist");
|
||||
assert_eq!(
|
||||
rx.borrow_and_update().name,
|
||||
format!("default:{}", test.default_policy)
|
||||
);
|
||||
|
||||
// Update the annotation on the pod and check that the watch is updated
|
||||
// with the new default.
|
||||
pod.annotations_mut().insert(
|
||||
"config.linkerd.io/default-inbound-policy".into(),
|
||||
default.to_string(),
|
||||
);
|
||||
test.index.write().apply(pod);
|
||||
assert!(rx.has_changed().unwrap());
|
||||
assert_eq!(rx.borrow().name, format!("default:{}", default));
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests that an invalid workload annotation is ignored in favor of the global
|
||||
/// default.
|
||||
#[tokio::test]
|
||||
async fn default_policy_annotated_invalid() {
|
||||
let test = TestConfig::default();
|
||||
|
||||
let mut p = mk_pod("ns-0", "pod-0", Some(("container-0", None)));
|
||||
p.annotations_mut().insert(
|
||||
"config.linkerd.io/default-inbound-policy".into(),
|
||||
"bogus".into(),
|
||||
);
|
||||
test.index.write().reset(vec![p], Default::default());
|
||||
|
||||
// Lookup port 2222 -> default config.
|
||||
let rx = test
|
||||
.index
|
||||
.write()
|
||||
.pod_server_rx("ns-0", "pod-0", 2222)
|
||||
.expect("pod must exist in lookups");
|
||||
assert_eq!(*rx.borrow(), test.default_server());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn opaque_annotated() {
|
||||
for default in &DEFAULTS {
|
||||
let test = TestConfig::from_default_policy(*default);
|
||||
|
||||
let mut p = mk_pod("ns-0", "pod-0", Some(("container-0", None)));
|
||||
p.annotations_mut()
|
||||
.insert("config.linkerd.io/opaque-ports".into(), "2222".into());
|
||||
test.index.write().reset(vec![p], Default::default());
|
||||
|
||||
let mut server = test.default_server();
|
||||
server.protocol = ProxyProtocol::Opaque;
|
||||
|
||||
let rx = test
|
||||
.index
|
||||
.write()
|
||||
.pod_server_rx("ns-0", "pod-0", 2222)
|
||||
.expect("pod-0.ns-0 should exist");
|
||||
assert_eq!(*rx.borrow(), server);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authenticated_annotated() {
|
||||
for default in &DEFAULTS {
|
||||
let test = TestConfig::from_default_policy(*default);
|
||||
|
||||
let mut p = mk_pod("ns-0", "pod-0", Some(("container-0", None)));
|
||||
p.annotations_mut().insert(
|
||||
"config.linkerd.io/proxy-require-identity-inbound-ports".into(),
|
||||
"2222".into(),
|
||||
);
|
||||
test.index.write().reset(vec![p], Default::default());
|
||||
|
||||
let config = {
|
||||
let policy = match *default {
|
||||
DefaultPolicy::Allow { cluster_only, .. } => DefaultPolicy::Allow {
|
||||
cluster_only,
|
||||
authenticated_only: true,
|
||||
},
|
||||
DefaultPolicy::Deny => DefaultPolicy::Deny,
|
||||
};
|
||||
InboundServer {
|
||||
name: format!("default:{}", policy),
|
||||
authorizations: mk_default_policy(policy, test.cluster.networks),
|
||||
protocol: ProxyProtocol::Detect {
|
||||
timeout: test.detect_timeout,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
let rx = test
|
||||
.index
|
||||
.write()
|
||||
.pod_server_rx("ns-0", "pod-0", 2222)
|
||||
.expect("pod-0.ns-0 should exist");
|
||||
assert_eq!(*rx.borrow(), config);
|
||||
}
|
||||
}
|
||||
|
||||
// === Helpers ===
|
||||
|
||||
const DEFAULTS: [DefaultPolicy; 5] = [
|
||||
DefaultPolicy::Deny,
|
||||
DefaultPolicy::Allow {
|
||||
|
@ -363,21 +56,21 @@ const DEFAULTS: [DefaultPolicy; 5] = [
|
|||
];
|
||||
|
||||
fn mk_pod(
|
||||
ns: impl Into<String>,
|
||||
name: impl Into<String>,
|
||||
containers: impl IntoIterator<Item = (impl Into<String>, impl IntoIterator<Item = ContainerPort>)>,
|
||||
ns: impl ToString,
|
||||
name: impl ToString,
|
||||
containers: impl IntoIterator<Item = (impl ToString, impl IntoIterator<Item = ContainerPort>)>,
|
||||
) -> k8s::Pod {
|
||||
k8s::Pod {
|
||||
metadata: k8s::ObjectMeta {
|
||||
namespace: Some(ns.into()),
|
||||
name: Some(name.into()),
|
||||
namespace: Some(ns.to_string()),
|
||||
name: Some(name.to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: Some(k8s::api::core::v1::PodSpec {
|
||||
containers: containers
|
||||
.into_iter()
|
||||
.map(|(name, ports)| k8s::api::core::v1::Container {
|
||||
name: name.into(),
|
||||
name: name.to_string(),
|
||||
ports: Some(ports.into_iter().collect()),
|
||||
..Default::default()
|
||||
})
|
||||
|
@ -389,8 +82,8 @@ fn mk_pod(
|
|||
}
|
||||
|
||||
fn mk_server(
|
||||
ns: impl Into<String>,
|
||||
name: impl Into<String>,
|
||||
ns: impl ToString,
|
||||
name: impl ToString,
|
||||
port: Port,
|
||||
srv_labels: impl IntoIterator<Item = (&'static str, &'static str)>,
|
||||
pod_labels: impl IntoIterator<Item = (&'static str, &'static str)>,
|
||||
|
@ -398,8 +91,8 @@ fn mk_server(
|
|||
) -> k8s::policy::Server {
|
||||
k8s::policy::Server {
|
||||
metadata: k8s::ObjectMeta {
|
||||
namespace: Some(ns.into()),
|
||||
name: Some(name.into()),
|
||||
namespace: Some(ns.to_string()),
|
||||
name: Some(name.to_string()),
|
||||
labels: Some(
|
||||
srv_labels
|
||||
.into_iter()
|
||||
|
@ -416,38 +109,10 @@ fn mk_server(
|
|||
}
|
||||
}
|
||||
|
||||
fn mk_server_authz(
|
||||
ns: impl Into<String>,
|
||||
name: impl Into<String>,
|
||||
selector: ServerSelector,
|
||||
client: k8s::policy::server_authorization::Client,
|
||||
) -> k8s::policy::ServerAuthorization {
|
||||
k8s::policy::ServerAuthorization {
|
||||
metadata: k8s::ObjectMeta {
|
||||
namespace: Some(ns.into()),
|
||||
name: Some(name.into()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: k8s::policy::ServerAuthorizationSpec {
|
||||
server: match selector {
|
||||
ServerSelector::Name(n) => k8s::policy::server_authorization::Server {
|
||||
name: Some(n),
|
||||
selector: None,
|
||||
},
|
||||
ServerSelector::Selector(s) => k8s::policy::server_authorization::Server {
|
||||
selector: Some(s),
|
||||
name: None,
|
||||
},
|
||||
},
|
||||
client,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn mk_default_policy(
|
||||
da: DefaultPolicy,
|
||||
cluster_nets: Vec<IpNet>,
|
||||
) -> HashMap<String, ClientAuthorization> {
|
||||
) -> HashMap<AuthorizationRef, ClientAuthorization> {
|
||||
let all_nets = vec![Ipv4Net::default().into(), Ipv6Net::default().into()];
|
||||
|
||||
let cluster_nets = cluster_nets.into_iter().map(NetworkMatch::from).collect();
|
||||
|
@ -460,7 +125,7 @@ fn mk_default_policy(
|
|||
authenticated_only: true,
|
||||
cluster_only: false,
|
||||
} => Some((
|
||||
"default:all-authenticated".into(),
|
||||
AuthorizationRef::Default("all-authenticated".to_string()),
|
||||
ClientAuthorization {
|
||||
authentication: authed,
|
||||
networks: all_nets,
|
||||
|
@ -470,7 +135,7 @@ fn mk_default_policy(
|
|||
authenticated_only: false,
|
||||
cluster_only: false,
|
||||
} => Some((
|
||||
"default:all-unauthenticated".into(),
|
||||
AuthorizationRef::Default("all-unauthenticated".to_string()),
|
||||
ClientAuthorization {
|
||||
authentication: ClientAuthentication::Unauthenticated,
|
||||
networks: all_nets,
|
||||
|
@ -480,7 +145,7 @@ fn mk_default_policy(
|
|||
authenticated_only: true,
|
||||
cluster_only: true,
|
||||
} => Some((
|
||||
"default:cluster-authenticated".into(),
|
||||
AuthorizationRef::Default("cluster-authenticated".to_string()),
|
||||
ClientAuthorization {
|
||||
authentication: authed,
|
||||
networks: cluster_nets,
|
||||
|
@ -490,7 +155,7 @@ fn mk_default_policy(
|
|||
authenticated_only: false,
|
||||
cluster_only: true,
|
||||
} => Some((
|
||||
"default:cluster-unauthenticated".into(),
|
||||
AuthorizationRef::Default("cluster-unauthenticated".to_string()),
|
||||
ClientAuthorization {
|
||||
authentication: ClientAuthentication::Unauthenticated,
|
||||
networks: cluster_nets,
|
||||
|
@ -501,14 +166,6 @@ fn mk_default_policy(
|
|||
.collect()
|
||||
}
|
||||
|
||||
struct TestConfig {
|
||||
index: SharedIndex,
|
||||
detect_timeout: time::Duration,
|
||||
default_policy: DefaultPolicy,
|
||||
cluster: ClusterInfo,
|
||||
_tracing: tracing::subscriber::DefaultGuard,
|
||||
}
|
||||
|
||||
impl TestConfig {
|
||||
fn from_default_policy(default_policy: DefaultPolicy) -> Self {
|
||||
let _tracing = Self::init_tracing();
|
||||
|
@ -533,7 +190,7 @@ impl TestConfig {
|
|||
|
||||
fn default_server(&self) -> InboundServer {
|
||||
InboundServer {
|
||||
name: format!("default:{}", self.default_policy),
|
||||
reference: ServerRef::Default(self.default_policy.to_string()),
|
||||
authorizations: mk_default_policy(self.default_policy, self.cluster.networks.clone()),
|
||||
protocol: ProxyProtocol::Detect {
|
||||
timeout: self.detect_timeout,
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
use super::*;
|
||||
|
||||
/// Tests that pod servers are configured with defaults based on the
|
||||
/// workload-defined `DefaultPolicy` policy.
|
||||
///
|
||||
/// Iterates through each default policy and validates that it produces expected
|
||||
/// configurations.
|
||||
#[test]
|
||||
fn default_policy_annotated() {
|
||||
for default in &DEFAULTS {
|
||||
let test = TestConfig::from_default_policy(match *default {
|
||||
// Invert default to ensure override applies.
|
||||
DefaultPolicy::Deny => DefaultPolicy::Allow {
|
||||
authenticated_only: false,
|
||||
cluster_only: false,
|
||||
},
|
||||
_ => DefaultPolicy::Deny,
|
||||
});
|
||||
|
||||
// Initially create the pod without an annotation and check that it gets
|
||||
// the global default.
|
||||
let mut pod = mk_pod("ns-0", "pod-0", Some(("container-0", None)));
|
||||
test.index
|
||||
.write()
|
||||
.reset(vec![pod.clone()], Default::default());
|
||||
|
||||
let mut rx = test
|
||||
.index
|
||||
.write()
|
||||
.pod_server_rx("ns-0", "pod-0", 2222)
|
||||
.expect("pod-0.ns-0 should exist");
|
||||
assert_eq!(
|
||||
rx.borrow_and_update().reference,
|
||||
ServerRef::Default(test.default_policy.to_string()),
|
||||
);
|
||||
|
||||
// Update the annotation on the pod and check that the watch is updated
|
||||
// with the new default.
|
||||
pod.annotations_mut().insert(
|
||||
"config.linkerd.io/default-inbound-policy".into(),
|
||||
default.to_string(),
|
||||
);
|
||||
test.index.write().apply(pod);
|
||||
assert!(rx.has_changed().unwrap());
|
||||
assert_eq!(
|
||||
rx.borrow().reference,
|
||||
ServerRef::Default(default.to_string())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests that an invalid workload annotation is ignored in favor of the global
|
||||
/// default.
|
||||
#[tokio::test]
|
||||
async fn default_policy_annotated_invalid() {
|
||||
let test = TestConfig::default();
|
||||
|
||||
let mut p = mk_pod("ns-0", "pod-0", Some(("container-0", None)));
|
||||
p.annotations_mut().insert(
|
||||
"config.linkerd.io/default-inbound-policy".into(),
|
||||
"bogus".into(),
|
||||
);
|
||||
test.index.write().reset(vec![p], Default::default());
|
||||
|
||||
// Lookup port 2222 -> default config.
|
||||
let rx = test
|
||||
.index
|
||||
.write()
|
||||
.pod_server_rx("ns-0", "pod-0", 2222)
|
||||
.expect("pod must exist in lookups");
|
||||
assert_eq!(*rx.borrow(), test.default_server());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn opaque_annotated() {
|
||||
for default in &DEFAULTS {
|
||||
let test = TestConfig::from_default_policy(*default);
|
||||
|
||||
let mut p = mk_pod("ns-0", "pod-0", Some(("container-0", None)));
|
||||
p.annotations_mut()
|
||||
.insert("config.linkerd.io/opaque-ports".into(), "2222".into());
|
||||
test.index.write().reset(vec![p], Default::default());
|
||||
|
||||
let mut server = test.default_server();
|
||||
server.protocol = ProxyProtocol::Opaque;
|
||||
|
||||
let rx = test
|
||||
.index
|
||||
.write()
|
||||
.pod_server_rx("ns-0", "pod-0", 2222)
|
||||
.expect("pod-0.ns-0 should exist");
|
||||
assert_eq!(*rx.borrow(), server);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn authenticated_annotated() {
|
||||
for default in &DEFAULTS {
|
||||
let test = TestConfig::from_default_policy(*default);
|
||||
|
||||
let mut p = mk_pod("ns-0", "pod-0", Some(("container-0", None)));
|
||||
p.annotations_mut().insert(
|
||||
"config.linkerd.io/proxy-require-identity-inbound-ports".into(),
|
||||
"2222".into(),
|
||||
);
|
||||
test.index.write().reset(vec![p], Default::default());
|
||||
|
||||
let config = {
|
||||
let policy = match *default {
|
||||
DefaultPolicy::Allow { cluster_only, .. } => DefaultPolicy::Allow {
|
||||
cluster_only,
|
||||
authenticated_only: true,
|
||||
},
|
||||
DefaultPolicy::Deny => DefaultPolicy::Deny,
|
||||
};
|
||||
InboundServer {
|
||||
reference: ServerRef::Default(policy.to_string()),
|
||||
authorizations: mk_default_policy(policy, test.cluster.networks),
|
||||
protocol: ProxyProtocol::Detect {
|
||||
timeout: test.detect_timeout,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
let rx = test
|
||||
.index
|
||||
.write()
|
||||
.pod_server_rx("ns-0", "pod-0", 2222)
|
||||
.expect("pod-0.ns-0 should exist");
|
||||
assert_eq!(*rx.borrow(), config);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn links_authorization_policy_with_mtls_name() {
|
||||
let test = TestConfig::default();
|
||||
|
||||
let mut pod = mk_pod("ns-0", "pod-0", Some(("container-0", None)));
|
||||
pod.labels_mut()
|
||||
.insert("app".to_string(), "app-0".to_string());
|
||||
test.index.write().apply(pod);
|
||||
|
||||
let mut rx = test
|
||||
.index
|
||||
.write()
|
||||
.pod_server_rx("ns-0", "pod-0", 8080)
|
||||
.expect("pod-0.ns-0 should exist");
|
||||
assert_eq!(*rx.borrow_and_update(), test.default_server());
|
||||
|
||||
test.index.write().apply(mk_server(
|
||||
"ns-0",
|
||||
"srv-8080",
|
||||
Port::Number(8080),
|
||||
None,
|
||||
Some(("app", "app-0")),
|
||||
Some(k8s::policy::server::ProxyProtocol::Http1),
|
||||
));
|
||||
assert!(rx.has_changed().unwrap());
|
||||
assert_eq!(
|
||||
*rx.borrow_and_update(),
|
||||
InboundServer {
|
||||
reference: ServerRef::Server("srv-8080".to_string()),
|
||||
authorizations: Default::default(),
|
||||
protocol: ProxyProtocol::Http1,
|
||||
},
|
||||
);
|
||||
|
||||
let authz = ClientAuthorization {
|
||||
networks: vec!["10.0.0.0/8".parse::<IpNet>().unwrap().into()],
|
||||
authentication: ClientAuthentication::TlsAuthenticated(vec![IdentityMatch::Exact(
|
||||
"foo.bar".to_string(),
|
||||
)]),
|
||||
};
|
||||
test.index.write().apply(mk_authorization_policy(
|
||||
"ns-0",
|
||||
"authz-foo",
|
||||
"srv-8080",
|
||||
vec![
|
||||
NamespacedTargetRef {
|
||||
group: Some("policy.linkerd.io".to_string()),
|
||||
kind: "NetworkAuthentication".to_string(),
|
||||
name: "net-foo".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
NamespacedTargetRef {
|
||||
group: Some("policy.linkerd.io".to_string()),
|
||||
kind: "MeshTLSAuthentication".to_string(),
|
||||
namespace: Some("ns-1".to_string()),
|
||||
name: "mtls-bar".to_string(),
|
||||
},
|
||||
],
|
||||
));
|
||||
test.index.write().apply(mk_network_authentication(
|
||||
"ns-0".to_string(),
|
||||
"net-foo".to_string(),
|
||||
vec![k8s::policy::network_authentication::Network {
|
||||
cidr: "10.0.0.0/8".parse().unwrap(),
|
||||
except: None,
|
||||
}],
|
||||
));
|
||||
test.index.write().apply(mk_meshtls_authentication(
|
||||
"ns-1",
|
||||
"mtls-bar",
|
||||
Some("foo.bar".to_string()),
|
||||
None,
|
||||
));
|
||||
assert!(rx.has_changed().unwrap());
|
||||
assert_eq!(
|
||||
*rx.borrow(),
|
||||
InboundServer {
|
||||
reference: ServerRef::Server("srv-8080".to_string()),
|
||||
authorizations: hashmap!(
|
||||
AuthorizationRef::AuthorizationPolicy("authz-foo".to_string()) => authz
|
||||
)
|
||||
.into_iter()
|
||||
.collect(),
|
||||
protocol: ProxyProtocol::Http1,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn mk_authorization_policy(
|
||||
ns: impl ToString,
|
||||
name: impl ToString,
|
||||
server: impl ToString,
|
||||
authns: impl IntoIterator<Item = NamespacedTargetRef>,
|
||||
) -> k8s::policy::AuthorizationPolicy {
|
||||
k8s::policy::AuthorizationPolicy {
|
||||
metadata: k8s::ObjectMeta {
|
||||
namespace: Some(ns.to_string()),
|
||||
name: Some(name.to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: k8s::policy::AuthorizationPolicySpec {
|
||||
target_ref: LocalTargetRef {
|
||||
group: Some("policy.linkerd.io".to_string()),
|
||||
kind: "Server".to_string(),
|
||||
name: server.to_string(),
|
||||
},
|
||||
required_authentication_refs: authns.into_iter().collect(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn mk_meshtls_authentication(
|
||||
ns: impl ToString,
|
||||
name: impl ToString,
|
||||
identities: impl IntoIterator<Item = String>,
|
||||
refs: impl IntoIterator<Item = NamespacedTargetRef>,
|
||||
) -> k8s::policy::MeshTLSAuthentication {
|
||||
let identities = identities.into_iter().collect::<Vec<_>>();
|
||||
let identity_refs = refs.into_iter().collect::<Vec<_>>();
|
||||
k8s::policy::MeshTLSAuthentication {
|
||||
metadata: k8s::ObjectMeta {
|
||||
namespace: Some(ns.to_string()),
|
||||
name: Some(name.to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: k8s::policy::MeshTLSAuthenticationSpec {
|
||||
identities: if identities.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(identities)
|
||||
},
|
||||
identity_refs: if identity_refs.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(identity_refs)
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn mk_network_authentication(
|
||||
ns: impl ToString,
|
||||
name: impl ToString,
|
||||
networks: impl IntoIterator<Item = k8s::policy::network_authentication::Network>,
|
||||
) -> k8s::policy::NetworkAuthentication {
|
||||
k8s::policy::NetworkAuthentication {
|
||||
metadata: k8s::ObjectMeta {
|
||||
namespace: Some(ns.to_string()),
|
||||
name: Some(name.to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: k8s::policy::NetworkAuthenticationSpec {
|
||||
networks: networks.into_iter().collect(),
|
||||
},
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn links_named_server_port() {
|
||||
let test = TestConfig::default();
|
||||
|
||||
let mut pod = mk_pod(
|
||||
"ns-0",
|
||||
"pod-0",
|
||||
Some((
|
||||
"container-0",
|
||||
Some(ContainerPort {
|
||||
name: Some("admin-http".to_string()),
|
||||
container_port: 8080,
|
||||
protocol: Some("TCP".to_string()),
|
||||
..ContainerPort::default()
|
||||
}),
|
||||
)),
|
||||
);
|
||||
pod.labels_mut()
|
||||
.insert("app".to_string(), "app-0".to_string());
|
||||
test.index.write().apply(pod);
|
||||
|
||||
let mut rx = test
|
||||
.index
|
||||
.write()
|
||||
.pod_server_rx("ns-0", "pod-0", 8080)
|
||||
.expect("pod-0.ns-0 should exist");
|
||||
assert_eq!(*rx.borrow_and_update(), test.default_server());
|
||||
|
||||
test.index.write().apply(mk_server(
|
||||
"ns-0",
|
||||
"srv-admin-http",
|
||||
Port::Name("admin-http".to_string()),
|
||||
None,
|
||||
Some(("app", "app-0")),
|
||||
Some(k8s::policy::server::ProxyProtocol::Http1),
|
||||
));
|
||||
assert!(rx.has_changed().unwrap());
|
||||
assert_eq!(
|
||||
*rx.borrow_and_update(),
|
||||
InboundServer {
|
||||
reference: ServerRef::Server("srv-admin-http".to_string()),
|
||||
authorizations: Default::default(),
|
||||
protocol: ProxyProtocol::Http1,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn links_unnamed_server_port() {
|
||||
let test = TestConfig::default();
|
||||
|
||||
let mut pod = mk_pod("ns-0", "pod-0", Some(("container-0", None)));
|
||||
pod.labels_mut()
|
||||
.insert("app".to_string(), "app-0".to_string());
|
||||
test.index.write().apply(pod);
|
||||
|
||||
let mut rx = test
|
||||
.index
|
||||
.write()
|
||||
.pod_server_rx("ns-0", "pod-0", 8080)
|
||||
.expect("pod-0.ns-0 should exist");
|
||||
assert_eq!(*rx.borrow_and_update(), test.default_server());
|
||||
|
||||
test.index.write().apply(mk_server(
|
||||
"ns-0",
|
||||
"srv-8080",
|
||||
Port::Number(8080),
|
||||
None,
|
||||
Some(("app", "app-0")),
|
||||
Some(k8s::policy::server::ProxyProtocol::Http1),
|
||||
));
|
||||
assert!(rx.has_changed().unwrap());
|
||||
assert_eq!(
|
||||
*rx.borrow_and_update(),
|
||||
InboundServer {
|
||||
reference: ServerRef::Server("srv-8080".to_string()),
|
||||
authorizations: Default::default(),
|
||||
protocol: ProxyProtocol::Http1,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn server_update_deselects_pod() {
|
||||
let test = TestConfig::default();
|
||||
|
||||
test.index.write().reset(
|
||||
vec![mk_pod("ns-0", "pod-0", Some(("container-0", None)))],
|
||||
Default::default(),
|
||||
);
|
||||
|
||||
let mut srv = mk_server(
|
||||
"ns-0",
|
||||
"srv-0",
|
||||
Port::Number(2222),
|
||||
None,
|
||||
None,
|
||||
Some(k8s::policy::server::ProxyProtocol::Http2),
|
||||
);
|
||||
test.index
|
||||
.write()
|
||||
.reset(vec![srv.clone()], Default::default());
|
||||
|
||||
// The default policy applies for all ports.
|
||||
let mut rx = test
|
||||
.index
|
||||
.write()
|
||||
.pod_server_rx("ns-0", "pod-0", 2222)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
*rx.borrow_and_update(),
|
||||
InboundServer {
|
||||
reference: ServerRef::Server("srv-0".to_string()),
|
||||
protocol: ProxyProtocol::Http2,
|
||||
authorizations: Default::default(),
|
||||
}
|
||||
);
|
||||
|
||||
test.index.write().apply({
|
||||
srv.spec.pod_selector = Some(("label", "value")).into_iter().collect();
|
||||
srv
|
||||
});
|
||||
assert!(rx.has_changed().unwrap());
|
||||
assert_eq!(*rx.borrow(), test.default_server());
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn links_server_authz_by_name() {
|
||||
link_server_authz(ServerSelector::Name("srv-8080".to_string()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn links_server_authz_by_label() {
|
||||
link_server_authz(ServerSelector::Selector(
|
||||
Some(("app", "app-0")).into_iter().collect(),
|
||||
));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn link_server_authz(selector: ServerSelector) {
|
||||
let test = TestConfig::default();
|
||||
|
||||
let mut pod = mk_pod("ns-0", "pod-0", Some(("container-0", None)));
|
||||
pod.labels_mut()
|
||||
.insert("app".to_string(), "app-0".to_string());
|
||||
test.index.write().apply(pod);
|
||||
|
||||
let mut rx = test
|
||||
.index
|
||||
.write()
|
||||
.pod_server_rx("ns-0", "pod-0", 8080)
|
||||
.expect("pod-0.ns-0 should exist");
|
||||
assert_eq!(*rx.borrow_and_update(), test.default_server());
|
||||
|
||||
test.index.write().apply(mk_server(
|
||||
"ns-0",
|
||||
"srv-8080",
|
||||
Port::Number(8080),
|
||||
Some(("app", "app-0")),
|
||||
Some(("app", "app-0")),
|
||||
Some(k8s::policy::server::ProxyProtocol::Http1),
|
||||
));
|
||||
assert!(rx.has_changed().unwrap());
|
||||
assert_eq!(
|
||||
*rx.borrow_and_update(),
|
||||
InboundServer {
|
||||
reference: ServerRef::Server("srv-8080".to_string()),
|
||||
authorizations: Default::default(),
|
||||
protocol: ProxyProtocol::Http1,
|
||||
},
|
||||
);
|
||||
test.index.write().apply(mk_server_authz(
|
||||
"ns-0",
|
||||
"authz-foo",
|
||||
selector,
|
||||
k8s::policy::server_authorization::Client {
|
||||
networks: Some(vec![k8s::policy::server_authorization::Network {
|
||||
cidr: "10.0.0.0/8".parse().unwrap(),
|
||||
except: None,
|
||||
}]),
|
||||
unauthenticated: false,
|
||||
mesh_tls: Some(k8s::policy::server_authorization::MeshTls {
|
||||
identities: Some(vec!["foo.bar".to_string()]),
|
||||
..Default::default()
|
||||
}),
|
||||
},
|
||||
));
|
||||
assert!(rx.has_changed().unwrap());
|
||||
assert_eq!(
|
||||
rx.borrow().reference,
|
||||
ServerRef::Server("srv-8080".to_string())
|
||||
);
|
||||
assert_eq!(rx.borrow().protocol, ProxyProtocol::Http1,);
|
||||
assert!(rx
|
||||
.borrow()
|
||||
.authorizations
|
||||
.contains_key(&AuthorizationRef::ServerAuthorization(
|
||||
"authz-foo".to_string()
|
||||
)));
|
||||
}
|
||||
|
||||
fn mk_server_authz(
|
||||
ns: impl ToString,
|
||||
name: impl ToString,
|
||||
selector: ServerSelector,
|
||||
client: k8s::policy::server_authorization::Client,
|
||||
) -> k8s::policy::ServerAuthorization {
|
||||
k8s::policy::ServerAuthorization {
|
||||
metadata: k8s::ObjectMeta {
|
||||
namespace: Some(ns.to_string()),
|
||||
name: Some(name.to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: k8s::policy::ServerAuthorizationSpec {
|
||||
server: match selector {
|
||||
ServerSelector::Name(n) => k8s::policy::server_authorization::Server {
|
||||
name: Some(n),
|
||||
selector: None,
|
||||
},
|
||||
ServerSelector::Selector(s) => k8s::policy::server_authorization::Server {
|
||||
selector: Some(s),
|
||||
name: None,
|
||||
},
|
||||
},
|
||||
client,
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,10 +1,15 @@
|
|||
use crate::k8s::{
|
||||
labels,
|
||||
policy::{Server, ServerAuthorization, ServerAuthorizationSpec, ServerSpec},
|
||||
policy::{
|
||||
AuthorizationPolicy, AuthorizationPolicySpec, MeshTLSAuthentication,
|
||||
MeshTLSAuthenticationSpec, NetworkAuthentication, NetworkAuthenticationSpec, Server,
|
||||
ServerAuthorization, ServerAuthorizationSpec, ServerSpec,
|
||||
},
|
||||
};
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use futures::future;
|
||||
use hyper::{body::Buf, http, Body, Request, Response};
|
||||
use k8s_openapi::api::core::v1::ServiceAccount;
|
||||
use kube::{core::DynamicObject, Resource, ResourceExt};
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::task;
|
||||
|
@ -91,6 +96,18 @@ impl Admission {
|
|||
}
|
||||
|
||||
async fn admit(self, req: AdmissionRequest) -> AdmissionResponse {
|
||||
if is_kind::<AuthorizationPolicy>(&req) {
|
||||
return self.admit_spec::<AuthorizationPolicySpec>(req).await;
|
||||
}
|
||||
|
||||
if is_kind::<MeshTLSAuthentication>(&req) {
|
||||
return self.admit_spec::<MeshTLSAuthenticationSpec>(req).await;
|
||||
}
|
||||
|
||||
if is_kind::<NetworkAuthentication>(&req) {
|
||||
return self.admit_spec::<NetworkAuthenticationSpec>(req).await;
|
||||
}
|
||||
|
||||
if is_kind::<Server>(&req) {
|
||||
return self.admit_spec::<ServerSpec>(req).await;
|
||||
};
|
||||
|
@ -170,6 +187,71 @@ fn parse_spec<T: DeserializeOwned>(req: AdmissionRequest) -> Result<(String, Str
|
|||
Ok((ns, name, spec))
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Validate<AuthorizationPolicySpec> for Admission {
|
||||
async fn validate(self, _ns: &str, _name: &str, spec: AuthorizationPolicySpec) -> Result<()> {
|
||||
// TODO support namespace references?
|
||||
if !spec.target_ref.targets_kind::<Server>() {
|
||||
bail!(
|
||||
"invalid targetRef kind: {}",
|
||||
spec.target_ref.canonical_kind()
|
||||
);
|
||||
}
|
||||
|
||||
let mtls_authns_count = spec
|
||||
.required_authentication_refs
|
||||
.iter()
|
||||
.filter(|authn| authn.targets_kind::<MeshTLSAuthentication>())
|
||||
.count();
|
||||
if mtls_authns_count > 1 {
|
||||
bail!("only a single MeshTLSAuthentication may be set");
|
||||
}
|
||||
|
||||
let net_authns_count = spec
|
||||
.required_authentication_refs
|
||||
.iter()
|
||||
.filter(|authn| authn.targets_kind::<NetworkAuthentication>())
|
||||
.count();
|
||||
if net_authns_count > 1 {
|
||||
bail!("only a single NetworkAuthentication may be set");
|
||||
}
|
||||
|
||||
if mtls_authns_count + net_authns_count < spec.required_authentication_refs.len() {
|
||||
let kinds = spec
|
||||
.required_authentication_refs
|
||||
.iter()
|
||||
.filter(|authn| {
|
||||
!authn.targets_kind::<MeshTLSAuthentication>()
|
||||
&& !authn.targets_kind::<NetworkAuthentication>()
|
||||
})
|
||||
.map(|authn| authn.canonical_kind())
|
||||
.collect::<Vec<_>>();
|
||||
bail!("unsupported authentication kind(s): {}", kinds.join(", "));
|
||||
}
|
||||
|
||||
if mtls_authns_count + net_authns_count == 0 {
|
||||
bail!("at least one authentication reference must be set");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Validate<MeshTLSAuthenticationSpec> for Admission {
|
||||
async fn validate(self, _ns: &str, _name: &str, spec: MeshTLSAuthenticationSpec) -> Result<()> {
|
||||
// The CRD validates identity strings, but does not validate identity references.
|
||||
|
||||
for id in spec.identity_refs.iter().flatten() {
|
||||
if !id.targets_kind::<ServiceAccount>() {
|
||||
bail!("invalid identity target kind: {}", id.canonical_kind());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Validate<ServerSpec> for Admission {
|
||||
/// Checks that `spec` doesn't select the same pod/ports as other existing Servers
|
||||
|
@ -212,6 +294,35 @@ impl Admission {
|
|||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Validate<NetworkAuthenticationSpec> for Admission {
|
||||
async fn validate(self, _ns: &str, _name: &str, spec: NetworkAuthenticationSpec) -> Result<()> {
|
||||
if spec.networks.is_empty() {
|
||||
bail!("at least one network must be specified");
|
||||
}
|
||||
for net in spec.networks.into_iter() {
|
||||
for except in net.except.into_iter().flatten() {
|
||||
if except.contains(&net.cidr) {
|
||||
bail!(
|
||||
"cidr '{}' is completely negated by exception '{}'",
|
||||
net.cidr,
|
||||
except
|
||||
);
|
||||
}
|
||||
if !net.cidr.contains(&except) {
|
||||
bail!(
|
||||
"cidr '{}' does not include exception '{}'",
|
||||
net.cidr,
|
||||
except
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl Validate<ServerAuthorizationSpec> for Admission {
|
||||
async fn validate(self, _ns: &str, _name: &str, spec: ServerAuthorizationSpec) -> Result<()> {
|
||||
|
|
|
@ -125,6 +125,27 @@ async fn main() -> Result<()> {
|
|||
.instrument(info_span!("serverauthorizations")),
|
||||
);
|
||||
|
||||
let authz_policies =
|
||||
runtime.watch_all::<k8s::policy::AuthorizationPolicy>(ListParams::default());
|
||||
tokio::spawn(
|
||||
kubert::index::namespaced(index.clone(), authz_policies)
|
||||
.instrument(info_span!("authorizationpolicies")),
|
||||
);
|
||||
|
||||
let mtls_authns =
|
||||
runtime.watch_all::<k8s::policy::MeshTLSAuthentication>(ListParams::default());
|
||||
tokio::spawn(
|
||||
kubert::index::namespaced(index.clone(), mtls_authns)
|
||||
.instrument(info_span!("meshtlsauthentications")),
|
||||
);
|
||||
|
||||
let network_authns =
|
||||
runtime.watch_all::<k8s::policy::NetworkAuthentication>(ListParams::default());
|
||||
tokio::spawn(
|
||||
kubert::index::namespaced(index.clone(), network_authns)
|
||||
.instrument(info_span!("networkauthentications")),
|
||||
);
|
||||
|
||||
// Run the gRPC server, serving results by looking up against the index handle.
|
||||
tokio::spawn(grpc(
|
||||
grpc_addr,
|
||||
|
|
|
@ -155,7 +155,7 @@ impl Runner {
|
|||
args: Some(
|
||||
vec![
|
||||
"wait",
|
||||
"--timeout=60s",
|
||||
"--timeout=120s",
|
||||
"--for=delete",
|
||||
"--namespace",
|
||||
ns,
|
||||
|
@ -219,13 +219,22 @@ impl Running {
|
|||
&self.name,
|
||||
|obj: Option<&k8s::Pod>| -> bool { obj.and_then(get_exit_code).is_some() },
|
||||
);
|
||||
match time::timeout(time::Duration::from_secs(60), finished).await {
|
||||
match time::timeout(time::Duration::from_secs(120), finished).await {
|
||||
Ok(Ok(())) => {}
|
||||
Ok(Err(error)) => panic!("Failed to wait for exit code: {}: {}", self.name, error),
|
||||
Err(_timeout) => panic!("Timeout waiting for exit code: {}", self.name),
|
||||
};
|
||||
|
||||
let curl_pod = api.get(&self.name).await.expect("pod must exist");
|
||||
get_exit_code(&curl_pod).expect("curl pod must have an exit code")
|
||||
let ex = get_exit_code(&curl_pod).expect("curl pod must have an exit code");
|
||||
|
||||
if let Err(error) = api
|
||||
.delete(&self.name, &kube::api::DeleteParams::background())
|
||||
.await
|
||||
{
|
||||
tracing::trace!(%error, name = %self.name, "Failed to delete pod");
|
||||
}
|
||||
|
||||
ex
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,10 +14,10 @@ macro_rules! assert_is_default_all_unauthenticated {
|
|||
($config:expr) => {
|
||||
assert_eq!(
|
||||
$config.labels,
|
||||
Some((
|
||||
"name".to_string(),
|
||||
"default:all-unauthenticated".to_string()
|
||||
))
|
||||
vec![
|
||||
("kind".to_string(), "default".to_string()),
|
||||
("name".to_string(), "all-unauthenticated".to_string()),
|
||||
]
|
||||
.into_iter()
|
||||
.collect()
|
||||
);
|
||||
|
|
|
@ -0,0 +1,212 @@
|
|||
use linkerd_policy_controller_k8s_api::{
|
||||
self as api,
|
||||
policy::{AuthorizationPolicy, AuthorizationPolicySpec, LocalTargetRef, NamespacedTargetRef},
|
||||
};
|
||||
use linkerd_policy_test::admission;
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn accepts_valid() {
|
||||
admission::accepts(|ns| AuthorizationPolicy {
|
||||
metadata: api::ObjectMeta {
|
||||
namespace: Some(ns),
|
||||
name: Some("test".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: AuthorizationPolicySpec {
|
||||
target_ref: LocalTargetRef {
|
||||
group: Some("policy.linkerd.io".to_string()),
|
||||
kind: "Server".to_string(),
|
||||
name: "api".to_string(),
|
||||
},
|
||||
required_authentication_refs: vec![
|
||||
NamespacedTargetRef {
|
||||
group: Some("policy.linkerd.io".to_string()),
|
||||
kind: "MeshTLSAuthentication".to_string(),
|
||||
name: "mtls-clients".to_string(),
|
||||
..Default::default()
|
||||
},
|
||||
NamespacedTargetRef {
|
||||
group: Some("policy.linkerd.io".to_string()),
|
||||
kind: "NetworkAuthentication".to_string(),
|
||||
name: "cluster-nets".to_string(),
|
||||
namespace: Some("linkerd".to_string()),
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn accepts_valid_with_only_meshtls() {
|
||||
admission::accepts(|ns| AuthorizationPolicy {
|
||||
metadata: api::ObjectMeta {
|
||||
namespace: Some(ns),
|
||||
name: Some("test".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: AuthorizationPolicySpec {
|
||||
target_ref: LocalTargetRef {
|
||||
group: Some("policy.linkerd.io".to_string()),
|
||||
kind: "Server".to_string(),
|
||||
name: "api".to_string(),
|
||||
},
|
||||
required_authentication_refs: vec![NamespacedTargetRef {
|
||||
group: Some("policy.linkerd.io".to_string()),
|
||||
kind: "MeshTLSAuthentication".to_string(),
|
||||
name: "mtls-clients".to_string(),
|
||||
..Default::default()
|
||||
}],
|
||||
},
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn accepts_valid_with_only_network() {
|
||||
admission::accepts(|ns| AuthorizationPolicy {
|
||||
metadata: api::ObjectMeta {
|
||||
namespace: Some(ns),
|
||||
name: Some("test".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: AuthorizationPolicySpec {
|
||||
target_ref: LocalTargetRef {
|
||||
group: Some("policy.linkerd.io".to_string()),
|
||||
kind: "Server".to_string(),
|
||||
name: "api".to_string(),
|
||||
},
|
||||
required_authentication_refs: vec![NamespacedTargetRef {
|
||||
group: Some("policy.linkerd.io".to_string()),
|
||||
kind: "NetworkAuthentication".to_string(),
|
||||
name: "cluster-nets".to_string(),
|
||||
namespace: Some("linkerd".to_string()),
|
||||
}],
|
||||
},
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn rejects_empty() {
|
||||
admission::rejects(|ns| AuthorizationPolicy {
|
||||
metadata: api::ObjectMeta {
|
||||
namespace: Some(ns),
|
||||
name: Some("test".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: AuthorizationPolicySpec::default(),
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn rejects_empty_required_authentications() {
|
||||
admission::rejects(|ns| AuthorizationPolicy {
|
||||
metadata: api::ObjectMeta {
|
||||
namespace: Some(ns),
|
||||
name: Some("test".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: AuthorizationPolicySpec {
|
||||
target_ref: LocalTargetRef {
|
||||
group: Some("policy.linkerd.io".to_string()),
|
||||
kind: "Server".to_string(),
|
||||
name: "deny".to_string(),
|
||||
},
|
||||
required_authentication_refs: vec![],
|
||||
},
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn rejects_target_ref_deployment() {
|
||||
admission::rejects(|ns| AuthorizationPolicy {
|
||||
metadata: api::ObjectMeta {
|
||||
namespace: Some(ns),
|
||||
name: Some("test".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: AuthorizationPolicySpec {
|
||||
target_ref: LocalTargetRef {
|
||||
group: Some("apps".to_string()),
|
||||
kind: "Deployment".to_string(),
|
||||
name: "someapp".to_string(),
|
||||
},
|
||||
required_authentication_refs: vec![NamespacedTargetRef {
|
||||
group: Some("policy.linkerd.io".to_string()),
|
||||
kind: "NetworkAuthentication".to_string(),
|
||||
namespace: Some("linkerd".to_string()),
|
||||
name: "cluster-nets".to_string(),
|
||||
}],
|
||||
},
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn rejects_duplicate_mtls_authns() {
|
||||
admission::rejects(|ns| AuthorizationPolicy {
|
||||
metadata: api::ObjectMeta {
|
||||
namespace: Some(ns),
|
||||
name: Some("test".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: AuthorizationPolicySpec {
|
||||
target_ref: LocalTargetRef {
|
||||
group: Some("policy.linkerd.io".to_string()),
|
||||
kind: "Server".to_string(),
|
||||
name: "some-srv".to_string(),
|
||||
},
|
||||
required_authentication_refs: vec![
|
||||
NamespacedTargetRef {
|
||||
group: Some("policy.linkerd.io".to_string()),
|
||||
kind: "MeshTLSAuthentication".to_string(),
|
||||
namespace: Some("some-ns".to_string()),
|
||||
name: "some-ids".to_string(),
|
||||
},
|
||||
NamespacedTargetRef {
|
||||
group: Some("policy.linkerd.io".to_string()),
|
||||
kind: "MeshTLSAuthentication".to_string(),
|
||||
namespace: Some("other-ns".to_string()),
|
||||
name: "other-ids".to_string(),
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn rejects_duplicate_network_authns() {
|
||||
admission::rejects(|ns| AuthorizationPolicy {
|
||||
metadata: api::ObjectMeta {
|
||||
namespace: Some(ns),
|
||||
name: Some("test".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: AuthorizationPolicySpec {
|
||||
target_ref: LocalTargetRef {
|
||||
group: Some("policy.linkerd.io".to_string()),
|
||||
kind: "Server".to_string(),
|
||||
name: "some-srv".to_string(),
|
||||
},
|
||||
required_authentication_refs: vec![
|
||||
NamespacedTargetRef {
|
||||
group: Some("policy.linkerd.io".to_string()),
|
||||
kind: "NetworkAuthentication".to_string(),
|
||||
namespace: Some("some-ns".to_string()),
|
||||
name: "some-nets".to_string(),
|
||||
},
|
||||
NamespacedTargetRef {
|
||||
group: Some("policy.linkerd.io".to_string()),
|
||||
kind: "NetworkAuthentication".to_string(),
|
||||
namespace: Some("other-ns".to_string()),
|
||||
name: "other-nets".to_string(),
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
.await;
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
use linkerd_policy_controller_k8s_api::{
|
||||
self as api,
|
||||
policy::{MeshTLSAuthentication, MeshTLSAuthenticationSpec, NamespacedTargetRef},
|
||||
};
|
||||
use linkerd_policy_test::admission;
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn accepts_valid_ref() {
|
||||
admission::accepts(|ns| MeshTLSAuthentication {
|
||||
metadata: api::ObjectMeta {
|
||||
namespace: Some(ns),
|
||||
name: Some("test".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: MeshTLSAuthenticationSpec {
|
||||
identity_refs: Some(vec![NamespacedTargetRef {
|
||||
kind: "ServiceAccount".to_string(),
|
||||
name: "default".to_string(),
|
||||
..Default::default()
|
||||
}]),
|
||||
..Default::default()
|
||||
},
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn accepts_strings() {
|
||||
admission::accepts(|ns| MeshTLSAuthentication {
|
||||
metadata: api::ObjectMeta {
|
||||
namespace: Some(ns),
|
||||
name: Some("test".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: MeshTLSAuthenticationSpec {
|
||||
identities: Some(vec!["example.id".to_string()]),
|
||||
..Default::default()
|
||||
},
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn rejects_empty() {
|
||||
admission::rejects(|ns| MeshTLSAuthentication {
|
||||
metadata: api::ObjectMeta {
|
||||
namespace: Some(ns),
|
||||
name: Some("test".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: MeshTLSAuthenticationSpec::default(),
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn rejects_both_refs_and_strings() {
|
||||
admission::rejects(|ns| MeshTLSAuthentication {
|
||||
metadata: api::ObjectMeta {
|
||||
namespace: Some(ns),
|
||||
name: Some("test".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: MeshTLSAuthenticationSpec {
|
||||
identities: Some(vec!["example.id".to_string()]),
|
||||
identity_refs: Some(vec![NamespacedTargetRef {
|
||||
kind: "ServiceAccount".to_string(),
|
||||
name: "default".to_string(),
|
||||
..Default::default()
|
||||
}]),
|
||||
},
|
||||
})
|
||||
.await;
|
||||
}
|
|
@ -0,0 +1,142 @@
|
|||
use linkerd_policy_controller_k8s_api::{
|
||||
self as api,
|
||||
policy::network_authentication::{Network, NetworkAuthentication, NetworkAuthenticationSpec},
|
||||
};
|
||||
use linkerd_policy_test::admission;
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn accepts_valid() {
|
||||
admission::accepts(|ns| NetworkAuthentication {
|
||||
metadata: api::ObjectMeta {
|
||||
namespace: Some(ns),
|
||||
name: Some("test".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: NetworkAuthenticationSpec {
|
||||
networks: vec![
|
||||
Network {
|
||||
cidr: "10.1.0.0/24".parse().unwrap(),
|
||||
except: None,
|
||||
},
|
||||
Network {
|
||||
cidr: "10.1.1.0/24".parse().unwrap(),
|
||||
except: Some(vec!["10.1.1.0/28".parse().unwrap()]),
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn accepts_ip_except() {
|
||||
admission::accepts(|ns| NetworkAuthentication {
|
||||
metadata: api::ObjectMeta {
|
||||
namespace: Some(ns),
|
||||
name: Some("test".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: NetworkAuthenticationSpec {
|
||||
networks: vec![Network {
|
||||
cidr: "10.1.0.0/16".parse().unwrap(),
|
||||
except: Some(vec!["10.1.1.1".parse().unwrap()]),
|
||||
}],
|
||||
},
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn rejects_except_whole_cidr() {
|
||||
admission::rejects(|ns| NetworkAuthentication {
|
||||
metadata: api::ObjectMeta {
|
||||
namespace: Some(ns),
|
||||
name: Some("test".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: NetworkAuthenticationSpec {
|
||||
networks: vec![Network {
|
||||
cidr: "10.1.1.0/24".parse().unwrap(),
|
||||
except: Some(vec!["10.1.0.0/16".parse().unwrap()]),
|
||||
}],
|
||||
},
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn rejects_except_not_in_cidr() {
|
||||
admission::rejects(|ns| NetworkAuthentication {
|
||||
metadata: api::ObjectMeta {
|
||||
namespace: Some(ns),
|
||||
name: Some("test".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: NetworkAuthenticationSpec {
|
||||
networks: vec![Network {
|
||||
cidr: "10.1.1.0/24".parse().unwrap(),
|
||||
except: Some(vec!["10.1.2.0/24".parse().unwrap()]),
|
||||
}],
|
||||
},
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn rejects_invalid_cidr() {
|
||||
// Duplicate the CRD with relaxed validation so we can send an invalid CIDR value.
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
Default,
|
||||
kube::CustomResource,
|
||||
serde::Deserialize,
|
||||
serde::Serialize,
|
||||
schemars::JsonSchema,
|
||||
)]
|
||||
#[kube(
|
||||
group = "policy.linkerd.io",
|
||||
version = "v1alpha1",
|
||||
kind = "NetworkAuthentication",
|
||||
namespaced
|
||||
)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct NetworkAuthenticationSpec {
|
||||
pub networks: Vec<Network>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize, schemars::JsonSchema)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Network {
|
||||
pub cidr: String,
|
||||
pub except: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
admission::rejects(|ns| NetworkAuthentication {
|
||||
metadata: api::ObjectMeta {
|
||||
namespace: Some(ns),
|
||||
name: Some("test".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: NetworkAuthenticationSpec {
|
||||
networks: vec![Network {
|
||||
cidr: "10.1.0.0/16".to_string(),
|
||||
except: Some(vec!["bogus".to_string()]),
|
||||
}],
|
||||
},
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn rejects_empty() {
|
||||
admission::rejects(|ns| NetworkAuthentication {
|
||||
metadata: api::ObjectMeta {
|
||||
namespace: Some(ns),
|
||||
name: Some("test".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: NetworkAuthenticationSpec { networks: vec![] },
|
||||
})
|
||||
.await;
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
use futures::prelude::*;
|
||||
use kube::ResourceExt;
|
||||
use linkerd_policy_controller_core::{Ipv4Net, Ipv6Net};
|
||||
use linkerd_policy_controller_k8s_api as k8s;
|
||||
use linkerd_policy_test::{
|
||||
assert_is_default_all_unauthenticated, assert_protocol_detect, create, create_ready_pod, grpc,
|
||||
|
@ -55,7 +56,7 @@ async fn server_with_server_authorization() {
|
|||
assert_eq!(config.authorizations, vec![]);
|
||||
assert_eq!(
|
||||
config.labels,
|
||||
convert_args!(hashmap!("name" => "linkerd-admin")),
|
||||
convert_args!(hashmap!("kind" => "server", "name" => "linkerd-admin")),
|
||||
);
|
||||
|
||||
// Create a server authorizaation that refers to the `linkerd-admin`
|
||||
|
@ -99,6 +100,7 @@ async fn server_with_server_authorization() {
|
|||
assert_eq!(
|
||||
config.authorizations.first().unwrap().labels,
|
||||
convert_args!(hashmap!(
|
||||
"kind" => "serverauthorization",
|
||||
"name" => "all-admin",
|
||||
)),
|
||||
);
|
||||
|
@ -118,7 +120,7 @@ async fn server_with_server_authorization() {
|
|||
);
|
||||
assert_eq!(
|
||||
config.labels,
|
||||
convert_args!(hashmap!("name" => server.name()))
|
||||
convert_args!(hashmap!("kind" => "server", "name" => server.name()))
|
||||
);
|
||||
|
||||
// Delete the `Server` and ensure that the update reverts to the
|
||||
|
@ -139,6 +141,142 @@ async fn server_with_server_authorization() {
|
|||
.await;
|
||||
}
|
||||
|
||||
/// Creates a pod, watches its policy, and updates policy resources that impact
|
||||
/// the watch.
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn server_with_authorization_policy() {
|
||||
with_temp_ns(|client, ns| async move {
|
||||
// Create a pod that does nothing. It's injected with a proxy, so we can
|
||||
// attach policies to its admin server.
|
||||
let pod = create_ready_pod(&client, mk_pause(&ns, "pause")).await;
|
||||
tracing::trace!(?pod);
|
||||
|
||||
// Port-forward to the control plane and start watching the pod's admin
|
||||
// server's policy and ensure that the first update uses the default
|
||||
// policy.
|
||||
let mut policy_api = grpc::PolicyClient::port_forwarded(&client).await;
|
||||
let mut rx = policy_api
|
||||
.watch_port(&ns, &pod.name(), 4191)
|
||||
.await
|
||||
.expect("failed to establish watch");
|
||||
let config = rx
|
||||
.next()
|
||||
.await
|
||||
.expect("watch must not fail")
|
||||
.expect("watch must return an initial config");
|
||||
tracing::trace!(?config);
|
||||
assert_is_default_all_unauthenticated!(config);
|
||||
assert_protocol_detect!(config);
|
||||
|
||||
// Create a server that selects the pod's proxy admin server and ensure
|
||||
// that the update now uses this server, which has no authorizations
|
||||
let server = create(&client, mk_admin_server(&ns, "linkerd-admin")).await;
|
||||
let config = rx
|
||||
.next()
|
||||
.await
|
||||
.expect("watch must not fail")
|
||||
.expect("watch must return an updated config");
|
||||
tracing::trace!(?config);
|
||||
assert_eq!(
|
||||
config.protocol,
|
||||
Some(grpc::inbound::ProxyProtocol {
|
||||
kind: Some(grpc::inbound::proxy_protocol::Kind::Http1(
|
||||
grpc::inbound::proxy_protocol::Http1::default()
|
||||
)),
|
||||
}),
|
||||
);
|
||||
assert_eq!(config.authorizations, vec![]);
|
||||
assert_eq!(
|
||||
config.labels,
|
||||
convert_args!(hashmap!("kind" => "server", "name" => server.name()))
|
||||
);
|
||||
|
||||
let all_nets = create(
|
||||
&client,
|
||||
k8s::policy::NetworkAuthentication {
|
||||
metadata: kube::api::ObjectMeta {
|
||||
namespace: Some(ns.clone()),
|
||||
name: Some("all-admin".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: k8s::policy::NetworkAuthenticationSpec {
|
||||
networks: vec![
|
||||
k8s::policy::network_authentication::Network {
|
||||
cidr: Ipv4Net::default().into(),
|
||||
except: None,
|
||||
},
|
||||
k8s::policy::network_authentication::Network {
|
||||
cidr: Ipv6Net::default().into(),
|
||||
except: None,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
let authz_policy = create(
|
||||
&client,
|
||||
k8s::policy::AuthorizationPolicy {
|
||||
metadata: kube::api::ObjectMeta {
|
||||
namespace: Some(ns.clone()),
|
||||
name: Some("all-admin".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: k8s::policy::AuthorizationPolicySpec {
|
||||
target_ref: k8s::policy::LocalTargetRef::from_resource(&server),
|
||||
required_authentication_refs: vec![
|
||||
k8s::policy::NamespacedTargetRef::from_resource(&all_nets),
|
||||
],
|
||||
},
|
||||
},
|
||||
)
|
||||
.await;
|
||||
|
||||
let config = time::timeout(time::Duration::from_secs(10), rx.next())
|
||||
.await
|
||||
.expect("watch must update within 10s")
|
||||
.expect("watch must not fail")
|
||||
.expect("watch must return an updated config");
|
||||
tracing::trace!(?config);
|
||||
assert_eq!(
|
||||
config.protocol,
|
||||
Some(grpc::inbound::ProxyProtocol {
|
||||
kind: Some(grpc::inbound::proxy_protocol::Kind::Http1(
|
||||
grpc::inbound::proxy_protocol::Http1::default()
|
||||
)),
|
||||
}),
|
||||
);
|
||||
assert_eq!(config.authorizations.len(), 1);
|
||||
assert_eq!(
|
||||
config.authorizations.first().unwrap().labels,
|
||||
convert_args!(hashmap!(
|
||||
"kind" => "authorizationpolicy",
|
||||
"name" => authz_policy.name(),
|
||||
))
|
||||
);
|
||||
assert_eq!(
|
||||
*config
|
||||
.authorizations
|
||||
.first()
|
||||
.unwrap()
|
||||
.authentication
|
||||
.as_ref()
|
||||
.unwrap(),
|
||||
grpc::inbound::Authn {
|
||||
permit: Some(grpc::inbound::authn::Permit::Unauthenticated(
|
||||
grpc::inbound::authn::PermitUnauthenticated {}
|
||||
)),
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
config.labels,
|
||||
convert_args!(hashmap!("kind" => "server", "name" => server.name()))
|
||||
);
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
fn mk_pause(ns: &str, name: &str) -> k8s::Pod {
|
||||
k8s::Pod {
|
||||
metadata: k8s::ObjectMeta {
|
||||
|
|
|
@ -0,0 +1,375 @@
|
|||
use linkerd_policy_controller_k8s_api::{
|
||||
self as k8s,
|
||||
policy::{LocalTargetRef, NamespacedTargetRef},
|
||||
};
|
||||
use linkerd_policy_test::{create, create_ready_pod, curl, nginx, with_temp_ns, LinkerdInject};
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn meshtls() {
|
||||
with_temp_ns(|client, ns| async move {
|
||||
// First create all of the policies we'll need so that the nginx pod
|
||||
// starts up with the correct policy (to prevent races).
|
||||
//
|
||||
// The policy requires that all connections are authenticated with MeshTLS.
|
||||
let (srv, all_mtls) = tokio::join!(
|
||||
create(&client, nginx::server(&ns)),
|
||||
create(&client, all_authenticated(&ns))
|
||||
);
|
||||
create(
|
||||
&client,
|
||||
authz_policy(
|
||||
&ns,
|
||||
"nginx",
|
||||
&srv,
|
||||
Some(NamespacedTargetRef::from_resource(&all_mtls)),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Create the nginx pod and wait for it to be ready.
|
||||
tokio::join!(
|
||||
create(&client, nginx::service(&ns)),
|
||||
create_ready_pod(&client, nginx::pod(&ns))
|
||||
);
|
||||
|
||||
let curl = curl::Runner::init(&client, &ns).await;
|
||||
let (injected, uninjected) = tokio::join!(
|
||||
curl.run("curl-injected", "http://nginx", LinkerdInject::Enabled),
|
||||
curl.run("curl-uninjected", "http://nginx", LinkerdInject::Disabled),
|
||||
);
|
||||
let (injected_status, uninjected_status) =
|
||||
tokio::join!(injected.exit_code(), uninjected.exit_code());
|
||||
assert_eq!(
|
||||
injected_status, 0,
|
||||
"uninjected curl must fail to contact nginx"
|
||||
);
|
||||
assert_ne!(uninjected_status, 0, "injected curl must contact nginx");
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn network() {
|
||||
// In order to test the network policy, we need to create the client pod
|
||||
// before creating the authorization policy. To avoid races, we do this by
|
||||
// creating a `curl-lock` configmap that prevents curl from actually being
|
||||
// executed. Once nginx is running with the correct policy, the configmap is
|
||||
// deleted to unblock the curl pods.
|
||||
with_temp_ns(|client, ns| async move {
|
||||
let curl = curl::Runner::init(&client, &ns).await;
|
||||
curl.create_lock().await;
|
||||
|
||||
// Create a curl pod and wait for it to get an IP.
|
||||
let blessed = curl
|
||||
.run("curl-blessed", "http://nginx", LinkerdInject::Disabled)
|
||||
.await;
|
||||
let blessed_ip = blessed.ip().await;
|
||||
tracing::debug!(curl.blessed.ip = %blessed_ip);
|
||||
|
||||
// Once we know the IP of the (blocked) pod, create an nginx
|
||||
// authorization policy that permits connections from this pod.
|
||||
let (srv, allow_ips) = tokio::join!(
|
||||
create(&client, nginx::server(&ns)),
|
||||
create(&client, allow_ips(&ns, Some(blessed_ip)))
|
||||
);
|
||||
create(
|
||||
&client,
|
||||
authz_policy(
|
||||
&ns,
|
||||
"nginx",
|
||||
&srv,
|
||||
Some(NamespacedTargetRef::from_resource(&allow_ips)),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Start nginx with the policy.
|
||||
tokio::join!(
|
||||
create(&client, nginx::service(&ns)),
|
||||
create_ready_pod(&client, nginx::pod(&ns))
|
||||
);
|
||||
|
||||
// Once the nginx pod is ready, delete the `curl-lock` configmap to
|
||||
// unblock curl from running.
|
||||
curl.delete_lock().await;
|
||||
|
||||
// The blessed pod should be able to connect to the nginx pod.
|
||||
let status = blessed.exit_code().await;
|
||||
assert_eq!(status, 0, "blessed curl pod must succeed");
|
||||
|
||||
// Create another curl pod that is not included in the authorization. It
|
||||
// should fail to connect to the nginx pod.
|
||||
let status = curl
|
||||
.run("curl-cursed", "http://nginx", LinkerdInject::Disabled)
|
||||
.await
|
||||
.exit_code()
|
||||
.await;
|
||||
assert_ne!(status, 0, "cursed curl pod must fail");
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn both() {
|
||||
// In order to test the network policy, we need to create the client pod
|
||||
// before creating the authorization policy. To avoid races, we do this by
|
||||
// creating a `curl-lock` configmap that prevents curl from actually being
|
||||
// executed. Once nginx is running with the correct policy, the configmap is
|
||||
// deleted to unblock the curl pods.
|
||||
with_temp_ns(|client, ns| async move {
|
||||
let curl = curl::Runner::init(&client, &ns).await;
|
||||
curl.create_lock().await;
|
||||
|
||||
let (blessed_injected, blessed_uninjected) = tokio::join!(
|
||||
curl.run(
|
||||
"curl-blessed-injected",
|
||||
"http://nginx",
|
||||
LinkerdInject::Enabled,
|
||||
),
|
||||
curl.run(
|
||||
"curl-blessed-uninjected",
|
||||
"http://nginx",
|
||||
LinkerdInject::Disabled,
|
||||
)
|
||||
);
|
||||
let (blessed_injected_ip, blessed_uninjected_ip) =
|
||||
tokio::join!(blessed_injected.ip(), blessed_uninjected.ip(),);
|
||||
tracing::debug!(curl.blessed.injected.ip = ?blessed_injected_ip);
|
||||
tracing::debug!(curl.blessed.uninjected.ip = ?blessed_uninjected_ip);
|
||||
|
||||
// Once we know the IP of the (blocked) pod, create an nginx
|
||||
// authorization policy that permits connections from this pod.
|
||||
let (srv, allow_ips, all_mtls) = tokio::join!(
|
||||
create(&client, nginx::server(&ns)),
|
||||
create(
|
||||
&client,
|
||||
allow_ips(&ns, vec![blessed_injected_ip, blessed_uninjected_ip]),
|
||||
),
|
||||
create(&client, all_authenticated(&ns))
|
||||
);
|
||||
create(
|
||||
&client,
|
||||
authz_policy(
|
||||
&ns,
|
||||
"nginx",
|
||||
&srv,
|
||||
vec![
|
||||
NamespacedTargetRef::from_resource(&allow_ips),
|
||||
NamespacedTargetRef::from_resource(&all_mtls),
|
||||
],
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Start nginx with the policy.
|
||||
tokio::join!(
|
||||
create(&client, nginx::service(&ns)),
|
||||
create_ready_pod(&client, nginx::pod(&ns))
|
||||
);
|
||||
|
||||
// Once the nginx pod is ready, delete the `curl-lock` configmap to
|
||||
// unblock curl from running.
|
||||
curl.delete_lock().await;
|
||||
tracing::info!("unblocked curl");
|
||||
|
||||
let (blessed_injected_status, blessed_uninjected_status) =
|
||||
tokio::join!(blessed_injected.exit_code(), blessed_uninjected.exit_code());
|
||||
// The blessed and injected pod should be able to connect to the nginx pod.
|
||||
assert_eq!(
|
||||
blessed_injected_status, 0,
|
||||
"blessed injected curl pod must succeed"
|
||||
);
|
||||
// The blessed and uninjected pod should NOT be able to connect to the nginx pod.
|
||||
assert_ne!(
|
||||
blessed_uninjected_status, 0,
|
||||
"blessed uninjected curl pod must NOT succeed"
|
||||
);
|
||||
|
||||
let (cursed_injected, cursed_uninjected) = tokio::join!(
|
||||
curl.run(
|
||||
"curl-cursed-injected",
|
||||
"http://nginx",
|
||||
LinkerdInject::Enabled,
|
||||
),
|
||||
curl.run(
|
||||
"curl-cursed-uninjected",
|
||||
"http://nginx",
|
||||
LinkerdInject::Disabled,
|
||||
)
|
||||
);
|
||||
let (cursed_injected_status, cursed_uninjected_status) =
|
||||
tokio::join!(cursed_injected.exit_code(), cursed_uninjected.exit_code(),);
|
||||
assert_ne!(
|
||||
cursed_injected_status, 0,
|
||||
"cursed injected curl pod must fail"
|
||||
);
|
||||
assert_ne!(
|
||||
cursed_uninjected_status, 0,
|
||||
"cursed uninjected curl pod must fail"
|
||||
);
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn either() {
|
||||
// In order to test the network policy, we need to create the client pod
|
||||
// before creating the authorization policy. To avoid races, we do this by
|
||||
// creating a `curl-lock` configmap that prevents curl from actually being
|
||||
// executed. Once nginx is running with the correct policy, the configmap is
|
||||
// deleted to unblock the curl pods.
|
||||
with_temp_ns(|client, ns| async move {
|
||||
let curl = curl::Runner::init(&client, &ns).await;
|
||||
curl.create_lock().await;
|
||||
|
||||
let (blessed_injected, blessed_uninjected) = tokio::join!(
|
||||
curl.run(
|
||||
"curl-blessed-injected",
|
||||
"http://nginx",
|
||||
LinkerdInject::Enabled,
|
||||
),
|
||||
curl.run(
|
||||
"curl-blessed-uninjected",
|
||||
"http://nginx",
|
||||
LinkerdInject::Disabled,
|
||||
)
|
||||
);
|
||||
let (blessed_injected_ip, blessed_uninjected_ip) =
|
||||
tokio::join!(blessed_injected.ip(), blessed_uninjected.ip());
|
||||
tracing::debug!(curl.blessed.injected.ip = ?blessed_injected_ip);
|
||||
tracing::debug!(curl.blessed.uninjected.ip = ?blessed_uninjected_ip);
|
||||
|
||||
// Once we know the IP of the (blocked) pod, create an nginx
|
||||
// authorization policy that permits connections from this pod.
|
||||
let (srv, allow_ips, all_mtls) = tokio::join!(
|
||||
create(&client, nginx::server(&ns)),
|
||||
create(&client, allow_ips(&ns, vec![blessed_uninjected_ip])),
|
||||
create(&client, all_authenticated(&ns))
|
||||
);
|
||||
tokio::join!(
|
||||
create(
|
||||
&client,
|
||||
authz_policy(
|
||||
&ns,
|
||||
"nginx-from-ip",
|
||||
&srv,
|
||||
vec![NamespacedTargetRef::from_resource(&allow_ips)],
|
||||
),
|
||||
),
|
||||
create(
|
||||
&client,
|
||||
authz_policy(
|
||||
&ns,
|
||||
"nginx-from-id",
|
||||
&srv,
|
||||
vec![NamespacedTargetRef::from_resource(&all_mtls)],
|
||||
),
|
||||
)
|
||||
);
|
||||
|
||||
// Start nginx with the policy.
|
||||
tokio::join!(
|
||||
create(&client, nginx::service(&ns)),
|
||||
create_ready_pod(&client, nginx::pod(&ns)),
|
||||
);
|
||||
|
||||
// Once the nginx pod is ready, delete the `curl-lock` configmap to
|
||||
// unblock curl from running.
|
||||
curl.delete_lock().await;
|
||||
tracing::info!("unblocking curl");
|
||||
|
||||
let (blessed_injected_status, blessed_uninjected_status) =
|
||||
tokio::join!(blessed_injected.exit_code(), blessed_uninjected.exit_code());
|
||||
// The blessed and injected pod should be able to connect to the nginx pod.
|
||||
assert_eq!(
|
||||
blessed_injected_status, 0,
|
||||
"blessed injected curl pod must succeed"
|
||||
);
|
||||
// The blessed and uninjected pod should NOT be able to connect to the nginx pod.
|
||||
assert_eq!(
|
||||
blessed_uninjected_status, 0,
|
||||
"blessed uninjected curl pod must succeed"
|
||||
);
|
||||
|
||||
let (cursed_injected, cursed_uninjected) = tokio::join!(
|
||||
curl.run(
|
||||
"curl-cursed-injected",
|
||||
"http://nginx",
|
||||
LinkerdInject::Enabled,
|
||||
),
|
||||
curl.run(
|
||||
"curl-cursed-uninjected",
|
||||
"http://nginx",
|
||||
LinkerdInject::Disabled,
|
||||
),
|
||||
);
|
||||
let (cursed_injected_status, cursed_uninjected_status) =
|
||||
tokio::join!(cursed_injected.exit_code(), cursed_uninjected.exit_code());
|
||||
assert_eq!(
|
||||
cursed_injected_status, 0,
|
||||
"cursed injected curl pod must succeed"
|
||||
);
|
||||
assert_ne!(
|
||||
cursed_uninjected_status, 0,
|
||||
"cursed uninjected curl pod must fail"
|
||||
);
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
// === helpers ===
|
||||
|
||||
fn authz_policy(
|
||||
ns: &str,
|
||||
name: &str,
|
||||
target: &k8s::policy::Server,
|
||||
authns: impl IntoIterator<Item = NamespacedTargetRef>,
|
||||
) -> k8s::policy::AuthorizationPolicy {
|
||||
k8s::policy::AuthorizationPolicy {
|
||||
metadata: k8s::ObjectMeta {
|
||||
namespace: Some(ns.to_string()),
|
||||
name: Some(name.to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: k8s::policy::AuthorizationPolicySpec {
|
||||
target_ref: LocalTargetRef::from_resource(target),
|
||||
required_authentication_refs: authns.into_iter().collect(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn all_authenticated(ns: &str) -> k8s::policy::MeshTLSAuthentication {
|
||||
k8s::policy::MeshTLSAuthentication {
|
||||
metadata: k8s::ObjectMeta {
|
||||
namespace: Some(ns.to_string()),
|
||||
name: Some("all-authenticated".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: k8s::policy::MeshTLSAuthenticationSpec {
|
||||
identity_refs: None,
|
||||
identities: Some(vec!["*".to_string()]),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn allow_ips(
|
||||
ns: &str,
|
||||
ips: impl IntoIterator<Item = std::net::IpAddr>,
|
||||
) -> k8s::policy::NetworkAuthentication {
|
||||
k8s::policy::NetworkAuthentication {
|
||||
metadata: k8s::ObjectMeta {
|
||||
namespace: Some(ns.to_string()),
|
||||
name: Some("allow-pod".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
spec: k8s::policy::NetworkAuthenticationSpec {
|
||||
networks: ips
|
||||
.into_iter()
|
||||
.map(|ip| k8s::policy::Network {
|
||||
cidr: ip.into(),
|
||||
except: None,
|
||||
})
|
||||
.collect(),
|
||||
},
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue