FFFF feat(destination): advertise identity for pods meshed by any control plane by gnarlex · Pull Request #1 · gnarlex/linkerd2 · GitHub
[go: up one dir, main page]

Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
8000
Diff view
Diff view
4 changes: 3 additions & 1 deletion charts/linkerd-control-plane/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,9 @@ proxy:
# endpoint; "cluster-authenticated" requires one only for endpoints within
# `clusterNetworks`; "all-unauthenticated" permits cleartext (today's
# behavior). One of: "all-unauthenticated", "all-authenticated",
# "cluster-authenticated"
# "cluster-authenticated". Note: for "all-authenticated"/"cluster-authenticated"
# to authenticate pods meshed by a different Linkerd control plane, all control
# planes in the cluster must share the same trust root.
# @default -- "all-unauthenticated"
defaultOutboundPolicy: "all-unauthenticated"
# -- Configures the outbound transport mode. Valid values are "transport-header" and "transparent"
Expand Down
5 changes: 1 addition & 4 deletions controller/api/destination/endpoint_profile_translator.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
type endpointProfileTranslator struct {
forceOpaqueTransport,
enableH2Upgrade bool
controllerNS string
identityTrustDomain string
defaultOpaquePorts map[uint32]struct{}

Expand Down Expand Up @@ -47,7 +46,6 @@ var endpointProfileUpdatesQueueOverflowCounter = promauto.NewCounter(
func newEndpointProfileTranslator(
forceOpaqueTransport bool,
enableH2Upgrade bool,
controllerNS,
identityTrustDomain string,
defaultOpaquePorts map[uint32]struct{},
meshedHTTP2ClientParams *pb.Http2ClientParams,
Expand All @@ -59,7 +57,6 @@ func newEndpointProfileTranslator(
return &endpointProfileTranslator{
forceOpaqueTransport: forceOpaqueTransport,
enableH2Upgrade: enableH2Upgrade,
controllerNS: controllerNS,
identityTrustDomain: identityTrustDomain,
defaultOpaquePorts: defaultOpaquePorts,

Expand Down Expand Up @@ -165,7 +162,7 @@ func (ept *endpointProfileTranslator) createEndpoint(address watcher.Address, op
weightedAddr, err = createWeightedAddrForExternalWorkload(address, ept.forceOpaqueTransport, opaquePorts, ept.meshedHttp2ClientParams)
} else {
weightedAddr, err = createWeightedAddr(address, opaquePorts,
ept.forceOpaqueTransport, ept.enableH2Upgrade, ept.identityTrustDomain, ept.controllerNS, ept.meshedHttp2ClientParams)
ept.forceOpaqueTransport, ept.enableH2Upgrade, ept.identityTrustDomain, ept.meshedHttp2ClientParams)
}
if err != nil {
return nil, err
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func TestEndpointProfileTranslator(t *testing.T) {
}
log := logging.WithField("test", t.Name())
translator := newEndpointProfileTranslator(
true, true, "cluster", "identity", make(map[uint32]struct{}), nil,
true, true, "identity", make(map[uint32]struct{}), nil,
mockGetProfileServer,
nil,
log,
Expand Down Expand Up @@ -124,7 +124,7 @@ func TestEndpointProfileTranslator(t *testing.T) {
endStream := make(chan struct{})
queueCapacity := DefaultStreamQueueCapacity
translator := newEndpointProfileTranslator(
true, true, "cluster", "identity", make(map[uint32]struct{}), nil,
true, true, "identity", make(map[uint32]struct{}), nil,
mockGetProfileServer,
endStream,
log,
Expand Down
20 changes: 10 additions & 10 deletions controller/api/destination/endpoint_translator.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ const (
// into Destination.Get messages.
type (
endpointTranslator struct {
controllerNS string
identityTrustDomain string
nodeTopologyZone string
defaultOpaquePorts map[uint32]struct{}
Expand Down Expand Up @@ -74,7 +73,6 @@ var updatesQueueOverflowCounter = promauto.NewCounterVec(
)

func newEndpointTranslator(
controllerNS string,
identityTrustDomain string,
forceOpaqueTransport,
enableH2Upgrade,
Expand Down Expand Up @@ -105,7 +103,6 @@ func newEndpointTranslator(
}

return &endpointTranslator{
controllerNS,
identityTrustDomain,
nodeTopologyZone,
defaultOpaquePorts,
Expand Down Expand Up @@ -207,7 +204,7 @@ func (et *endpointTranslator) sendClientAdd(set watcher.AddressSet) {
if address.Pod != nil {
opaquePorts = watcher.GetAnnotatedOpaquePorts(address.Pod, et.defaultOpaquePorts)
wa, err = createWeightedAddr(address, opaquePorts,
et.forceOpaqueTransport, et.enableH2Upgrade, et.identityTrustDomain, et.controllerNS, et.meshedHTTP2ClientParams)
et.forceOpaqueTransport, et.enableH2Upgrade, et.identityTrustDomain, et.meshedHTTP2ClientParams)
if err != nil {
et.log.Errorf("Failed to translate Pod endpoints to weighted addr: %s", err)
continue
Expand Down Expand Up @@ -404,7 +401,6 @@ func createWeightedAddr(
forceOpaqueTransport bool,
enableH2Upgrade bool,
identityTrustDomain string,
controllerNS string,
meshedHttp2 *pb.Http2ClientParams,
) (*pb.WeightedAddr, error) {
tcpAddr, err := toAddr(address)
Expand Down Expand Up @@ -477,13 +473,17 @@ func createWeightedAddr(
}
}

// If the pod is controlled by the same Linkerd control plane, then it can
// participate in identity with peers.
// If the pod is meshed by any Linkerd control plane, then it can
// participate in identity with peers. The identity SAN is derived from the
// pod's own control-plane-ns label, so this assumes all control planes in
// the cluster share a trust root/domain. This is what lets an outbound
// default policy of all-/cluster-authenticated require mTLS to pods that
// are meshed by a different control plane.
//
// TODO this should be relaxed to match a trust domain annotation so that
// multiple meshes can participate in identity if they share trust roots.
// TODO this should be relaxed further to match a trust domain annotation so
// that meshes with distinct trust roots can be trusted selectively.
if identityTrustDomain != "" &&
controllerNSLabel == controllerNS &&
controllerNSLabel != "" &&
!isSkippedInboundPort {

id := fmt.Sprintf("%s.%s.serviceaccount.identity.%s.%s", sa, ns, controllerNSLabel, identityTrustDomain)
Expand Down
68 changes: 68 additions & 0 deletions controller/api/destination/endpoint_translator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,49 @@ var (
OwnerName: "rc-name",
}

// podForeignCP is meshed by a different Linkerd control plane
// (control-plane-ns "other-linkerd") than the one running the destination
// controller. Under a shared trust root, identity must still be advertised
// for it, with a SAN derived from its own control-plane-ns label.
podForeignCP = watcher.Address{
IP: "1.1.1.9",
Port: 1,
Pod: &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "pod-foreign-cp",
Namespace: "ns",
Labels: map[string]string{
k8s.ControllerNSLabel: "other-linkerd",
k8s.ProxyDeploymentLabel: "deployment-name",
},
},
Spec: corev1.PodSpec{
ServiceAccountName: "serviceaccount-name",
Containers: []corev1.Container{
{
Name: k8s.ProxyContainerName,
Env: []corev1.EnvVar{
{
Name: envInboundListenAddr,
Value: "0.0.0.0:4143",
},
{
Name: envAdminListenAddr,
Value: "0.0.0.0:4191",
},
{
Name: envControlListenAddr,
Value: "0.0.0.0:4190",
},
},
},
},
},
},
OwnerKind: "replicationcontroller",
OwnerName: "rc-name",
}

pod2 = watcher.Address{
IP: "1.1.1.2",
Port: 2,
Expand Down Expand Up @@ -761,6 +804,31 @@ func TestEndpointTranslatorForPods(t *testing.T) {
}
})

t.Run("Sends TlsIdentity for pods meshed by a different control plane", func(t *testing.T) {
// The SAN is derived from the pod's own control-plane-ns label
// ("other-linkerd"), not the destination controller's namespace, so
// that a shared trust root can authenticate cross-control-plane peers.
expectedTLSIdentity := &pb.TlsIdentity_DnsLikeIdentity{
Name: "serviceaccount-name.ns.serviceaccount.identity.other-linkerd.trust.domain",
}

mockGetServer, translator := makeEndpointTranslator(t)
translator.Start()
defer translator.Stop()

translator.Add(mkAddressSetForPods(t, podForeignCP))

addrs := (<-mockGetServer.updatesReceived).GetAdd().GetAddrs()
if len(addrs) != 1 {
t.Fatalf("Expected [1] address returned, got %v", addrs)
}

actualTLSIdentity := addrs[0].GetTlsIdentity().GetDnsLikeIdentity()
if diff := deep.Equal(actualTLSIdentity, expectedTLSIdentity); diff != nil {
t.Fatalf("Expected TlsIdentity to be [%v] but was [%v]", expectedTLSIdentity, actualTLSIdentity)
}
})

t.Run("Sends Opaque ProtocolHint for opaque ports", func(t *testing.T) {
expectedProtocolHint := &pb.ProtocolHint{
Protocol: &pb.ProtocolHint_Opaque_{
Expand Down
2 changes: 0 additions & 2 deletions controller/api/destination/federated_service_watcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,6 @@ func (fs *federatedService) remoteDiscoverySubscribe(
}

translator, err := newEndpointTranslator(
fs.config.ControllerNS,
remoteConfig.TrustDomain,
fs.config.ForceOpaqueTransport,
fs.config.EnableH2Upgrade,
Expand Down Expand Up @@ -421,7 +420,6 @@ func (fs *federatedService) localDiscoverySubscribe(
localDiscovery string,
) {
translator, err := newEndpointTranslator(
fs.config.ControllerNS,
fs.config.IdentityTrustDomain,
fs.config.ForceOpaqueTransport,
fs.config.EnableH2Upgrade,
Expand Down
3 changes: 0 additions & 3 deletions controller/api/destination/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,6 @@ func (s *server) Get(dest *pb.GetDestination, stream pb.Destination_GetServer) e
return status.Errorf(codes.NotFound, "Remote cluster not found: %s", cluster)
}
translator, err := newEndpointTranslator(
s.config.ControllerNS,
remoteConfig.TrustDomain,
s.config.ForceOpaqueTransport,
s.config.EnableH2Upgrade,
Expand Down Expand Up @@ -253,7 +252,6 @@ func (s *server) Get(dest *pb.GetDestination, stream pb.Destination_GetServer) e
log.Debug("Local discovery service detected")
// Local discovery
translator, err := newEndpointTranslator(
s.config.ControllerNS,
s.config.IdentityTrustDomain,
s.config.ForceOpaqueTransport,
s.config.EnableH2Upgrade,
Expand Down Expand Up @@ -586,7 +584,6 @@ func (s *server) subscribeToEndpointProfile(
translator := newEndpointProfileTranslator(
s.config.ForceOpaqueTransport,
s.config.EnableH2Upgrade,
s.config.ControllerNS,
s.config.IdentityTrustDomain,
s.config.DefaultOpaquePorts,
s.config.MeshedHttp2ClientParams,
Expand Down
1 change: 0 additions & 1 deletion controller/api/destination/test_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -1180,7 +1180,6 @@ metadata:

mockGetServer := &mockDestinationGetServer{updatesReceived: make(chan *pb.Update, 50)}
translator, err := newEndpointTranslator(
"linkerd",
"trust.domain",
forceOpaqueTransport,
true, // enableH2Upgrade
Expand Down
0