Repository: kumahq/kuma
Pull request #1072 (2020-10-14 10:14:05)

feat(kuma-cp) exclude pods that matches labels from injection
### Summary

This PR introduces the capability to exclude individual pods from injection that match the given list of tags.

Use case:
On OpenShift when you use DeploymentConfig instead of Deployment, there is an additional Pod that does not match any Service, which is a requirement for Kuma. Now by default, we are excluding OpenShift Deployer Pods from Kuma injection.

Also, I added more logging to the injector

### Documentation

- [ ] Documented in config. We may need a separate site in docs for sidecar injection.



Patch:
diff --git a/api/mesh/v1alpha1/mesh.pb.go b/api/mesh/v1alpha1/mesh.pb.go
index 06168d5c7..b2f55a868 100644
--- a/api/mesh/v1alpha1/mesh.pb.go
+++ b/api/mesh/v1alpha1/mesh.pb.go
@@ -39,10 +39,12 @@ type Mesh struct {
 	// Additionally, it is also possible to further customize this configuration
 	// for each dataplane individually using Dataplane resource.
 	// +optional
-	Metrics              *Metrics `protobuf:"bytes,4,opt,name=metrics,proto3" json:"metrics,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	Metrics *Metrics `protobuf:"bytes,4,opt,name=metrics,proto3" json:"metrics,omitempty"`
+	// Networking settings of the mesh
+	Networking           *Networking `protobuf:"bytes,5,opt,name=networking,proto3" json:"networking,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
+	XXX_unrecognized     []byte      `json:"-"`
+	XXX_sizecache        int32       `json:"-"`
 }
 
 func (m *Mesh) Reset()         { *m = Mesh{} }
@@ -98,6 +100,13 @@ func (m *Mesh) GetMetrics() *Metrics {
 	return nil
 }
 
+func (m *Mesh) GetNetworking() *Networking {
+	if m != nil {
+		return m.Networking
+	}
+	return nil
+}
+
 // mTLS settings of a Mesh.
 type Mesh_Mtls struct {
 	// Name of the enabled backend
@@ -303,6 +312,88 @@ func (m *CertificateAuthorityBackend_DpCert_Rotation) GetExpiration() string {
 	return ""
 }
 
+// Networking defines the networking configuration of the mesh
+type Networking struct {
+	// Outbound settings
+	Outbound             *Networking_Outbound `protobuf:"bytes,1,opt,name=outbound,proto3" json:"outbound,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}             `json:"-"`
+	XXX_unrecognized     []byte               `json:"-"`
+	XXX_sizecache        int32                `json:"-"`
+}
+
+func (m *Networking) Reset()         { *m = Networking{} }
+func (m *Networking) String() string { return proto.CompactTextString(m) }
+func (*Networking) ProtoMessage()    {}
+func (*Networking) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ae9b3cd8c92bbf6a, []int{2}
+}
+
+func (m *Networking) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Networking.Unmarshal(m, b)
+}
+func (m *Networking) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Networking.Marshal(b, m, deterministic)
+}
+func (m *Networking) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Networking.Merge(m, src)
+}
+func (m *Networking) XXX_Size() int {
+	return xxx_messageInfo_Networking.Size(m)
+}
+func (m *Networking) XXX_DiscardUnknown() {
+	xxx_messageInfo_Networking.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Networking proto.InternalMessageInfo
+
+func (m *Networking) GetOutbound() *Networking_Outbound {
+	if m != nil {
+		return m.Outbound
+	}
+	return nil
+}
+
+// Outbound describes the common mesh outbound settings
+type Networking_Outbound struct {
+	// Control the passthrough cluster
+	Passthrough          *wrappers.BoolValue `protobuf:"bytes,1,opt,name=passthrough,proto3" json:"passthrough,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}            `json:"-"`
+	XXX_unrecognized     []byte              `json:"-"`
+	XXX_sizecache        int32               `json:"-"`
+}
+
+func (m *Networking_Outbound) Reset()         { *m = Networking_Outbound{} }
+func (m *Networking_Outbound) String() string { return proto.CompactTextString(m) }
+func (*Networking_Outbound) ProtoMessage()    {}
+func (*Networking_Outbound) Descriptor() ([]byte, []int) {
+	return fileDescriptor_ae9b3cd8c92bbf6a, []int{2, 0}
+}
+
+func (m *Networking_Outbound) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Networking_Outbound.Unmarshal(m, b)
+}
+func (m *Networking_Outbound) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Networking_Outbound.Marshal(b, m, deterministic)
+}
+func (m *Networking_Outbound) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Networking_Outbound.Merge(m, src)
+}
+func (m *Networking_Outbound) XXX_Size() int {
+	return xxx_messageInfo_Networking_Outbound.Size(m)
+}
+func (m *Networking_Outbound) XXX_DiscardUnknown() {
+	xxx_messageInfo_Networking_Outbound.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Networking_Outbound proto.InternalMessageInfo
+
+func (m *Networking_Outbound) GetPassthrough() *wrappers.BoolValue {
+	if m != nil {
+		return m.Passthrough
+	}
+	return nil
+}
+
 // Tracing defines tracing configuration of the mesh.
 type Tracing struct {
 	// Name of the default backend
@@ -318,7 +409,7 @@ func (m *Tracing) Reset()         { *m = Tracing{} }
 func (m *Tracing) String() string { return proto.CompactTextString(m) }
 func (*Tracing) ProtoMessage()    {}
 func (*Tracing) Descriptor() ([]byte, []int) {
-	return fileDescriptor_ae9b3cd8c92bbf6a, []int{2}
+	return fileDescriptor_ae9b3cd8c92bbf6a, []int{3}
 }
 
 func (m *Tracing) XXX_Unmarshal(b []byte) error {
@@ -375,7 +466,7 @@ func (m *TracingBackend) Reset()         { *m = TracingBackend{} }
 func (m *TracingBackend) String() string { return proto.CompactTextString(m) }
 func (*TracingBackend) ProtoMessage()    {}
 func (*TracingBackend) Descriptor() ([]byte, []int) {
-	return fileDescriptor_ae9b3cd8c92bbf6a, []int{3}
+	return fileDescriptor_ae9b3cd8c92bbf6a, []int{4}
 }
 
 func (m *TracingBackend) XXX_Unmarshal(b []byte) error {
@@ -442,7 +533,7 @@ func (m *ZipkinTracingBackendConfig) Reset()         { *m = ZipkinTracingBackend
 func (m *ZipkinTracingBackendConfig) String() string { return proto.CompactTextString(m) }
 func (*ZipkinTracingBackendConfig) ProtoMessage()    {}
 func (*ZipkinTracingBackendConfig) Descriptor() ([]byte, []int) {
-	return fileDescriptor_ae9b3cd8c92bbf6a, []int{4}
+	return fileDescriptor_ae9b3cd8c92bbf6a, []int{5}
 }
 
 func (m *ZipkinTracingBackendConfig) XXX_Unmarshal(b []byte) error {
@@ -498,7 +589,7 @@ func (m *Logging) Reset()         { *m = Logging{} }
 func (m *Logging) String() string { return proto.CompactTextString(m) }
 func (*Logging) ProtoMessage()    {}
 func (*Logging) Descriptor() ([]byte, []int) {
-	return fileDescriptor_ae9b3cd8c92bbf6a, []int{5}
+	return fileDescriptor_ae9b3cd8c92bbf6a, []int{6}
 }
 
 func (m *Logging) XXX_Unmarshal(b []byte) error {
@@ -555,7 +646,7 @@ func (m *LoggingBackend) Reset()         { *m = LoggingBackend{} }
 func (m *LoggingBackend) String() string { return proto.CompactTextString(m) }
 func (*LoggingBackend) ProtoMessage()    {}
 func (*LoggingBackend) Descriptor() ([]byte, []int) {
-	return fileDescriptor_ae9b3cd8c92bbf6a, []int{6}
+	return fileDescriptor_ae9b3cd8c92bbf6a, []int{7}
 }
 
 func (m *LoggingBackend) XXX_Unmarshal(b []byte) error {
@@ -617,7 +708,7 @@ func (m *FileLoggingBackendConfig) Reset()         { *m = FileLoggingBackendConf
 func (m *FileLoggingBackendConfig) String() string { return proto.CompactTextString(m) }
 func (*FileLoggingBackendConfig) ProtoMessage()    {}
 func (*FileLoggingBackendConfig) Descriptor() ([]byte, []int) {
-	return fileDescriptor_ae9b3cd8c92bbf6a, []int{7}
+	return fileDescriptor_ae9b3cd8c92bbf6a, []int{8}
 }
 
 func (m *FileLoggingBackendConfig) XXX_Unmarshal(b []byte) error {
@@ -658,7 +749,7 @@ func (m *TcpLoggingBackendConfig) Reset()         { *m = TcpLoggingBackendConfig
 func (m *TcpLoggingBackendConfig) String() string { return proto.CompactTextString(m) }
 func (*TcpLoggingBackendConfig) ProtoMessage()    {}
 func (*TcpLoggingBackendConfig) Descriptor() ([]byte, []int) {
-	return fileDescriptor_ae9b3cd8c92bbf6a, []int{8}
+	return fileDescriptor_ae9b3cd8c92bbf6a, []int{9}
 }
 
 func (m *TcpLoggingBackendConfig) XXX_Unmarshal(b []byte) error {
@@ -692,6 +783,8 @@ func init() {
 	proto.RegisterType((*CertificateAuthorityBackend)(nil), "kuma.mesh.v1alpha1.CertificateAuthorityBackend")
 	proto.RegisterType((*CertificateAuthorityBackend_DpCert)(nil), "kuma.mesh.v1alpha1.CertificateAuthorityBackend.DpCert")
 	proto.RegisterType((*CertificateAuthorityBackend_DpCert_Rotation)(nil), "kuma.mesh.v1alpha1.CertificateAuthorityBackend.DpCert.Rotation")
+	proto.RegisterType((*Networking)(nil), "kuma.mesh.v1alpha1.Networking")
+	proto.RegisterType((*Networking_Outbound)(nil), "kuma.mesh.v1alpha1.Networking.Outbound")
 	proto.RegisterType((*Tracing)(nil), "kuma.mesh.v1alpha1.Tracing")
 	proto.RegisterType((*TracingBackend)(nil), "kuma.mesh.v1alpha1.TracingBackend")
 	proto.RegisterType((*ZipkinTracingBackendConfig)(nil), "kuma.mesh.v1alpha1.ZipkinTracingBackendConfig")
@@ -704,42 +797,47 @@ func init() {
 func init() { proto.RegisterFile("mesh/v1alpha1/mesh.proto", fileDescriptor_ae9b3cd8c92bbf6a) }
 
 var fileDescriptor_ae9b3cd8c92bbf6a = []byte{
-	// 590 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x53, 0x51, 0x6b, 0xd4, 0x40,
-	0x10, 0xe6, 0xee, 0xc2, 0xf5, 0x3a, 0xc5, 0x22, 0xfb, 0x60, 0x43, 0x5a, 0x4b, 0x09, 0x22, 0x45,
-	0x21, 0xc7, 0xb5, 0x28, 0x7d, 0x52, 0x6c, 0x8b, 0x20, 0x5a, 0x1f, 0xd6, 0xd2, 0x87, 0xfa, 0xb4,
-	0x49, 0x36, 0xb9, 0xa5, 0x9b, 0xec, 0xba, 0xd9, 0xa8, 0x45, 0xfc, 0x11, 0xfe, 0x02, 0xff, 0x95,
-	0x2f, 0xfe, 0x19, 0xd9, 0xcd, 0xe6, 0xda, 0xdc, 0x5d, 0x0b, 0x55, 0xdf, 0x66, 0x67, 0xbe, 0x6f,
-	0xf2, 0x65, 0xbe, 0x19, 0xf0, 0x0b, 0x5a, 0x4d, 0xc7, 0x9f, 0x27, 0x84, 0xcb, 0x29, 0x99, 0x8c,
-	0xcd, 0x2b, 0x92, 0x4a, 0x68, 0x81, 0xd0, 0x45, 0x5d, 0x90, 0xc8, 0x26, 0xda, 0x72, 0xb0, 0x39,
-	0x8f, 0xd6, 0x8a, 0x25, 0x55, 0x43, 0x08, 0xb6, 0x73, 0x21, 0x72, 0x4e, 0xc7, 0xf6, 0x15, 0xd7,
-	0xd9, 0xf8, 0x8b, 0x22, 0x52, 0x52, 0xd5, 0xd6, 0xb7, 0xe6, 0xeb, 0x95, 0x56, 0x75, 0xa2, 0x9b,
-	0x6a, 0xf8, 0xbb, 0x0f, 0xde, 0x09, 0xad, 0xa6, 0x68, 0x02, 0x5e, 0xa1, 0x79, 0xe5, 0xf7, 0x76,
-	0x7a, 0xbb, 0x6b, 0x7b, 0x0f, 0xa3, 0x45, 0x19, 0x91, 0xc1, 0x45, 0x27, 0x9a, 0x57, 0xd8, 0x42,
-	0xd1, 0x33, 0x58, 0xd1, 0x8a, 0x24, 0xac, 0xcc, 0xfd, 0xbe, 0x65, 0x6d, 0x2e, 0x63, 0x9d, 0x36,
-	0x10, 0xdc, 0x62, 0x0d, 0x8d, 0x8b, 0x3c, 0x37, 0xb4, 0xc1, 0xcd, 0xb4, 0x77, 0x0d, 0x04, 0xb7,
-	0x58, 0x43, 0x73, 0x3f, 0xee, 0x7b, 0x37, 0xd3, 0x4e, 0x1a, 0x08, 0x6e, 0xb1, 0xc1, 0x37, 0xf0,
-	0x8c, 0x64, 0xf4, 0x18, 0xd6, 0x69, 0x49, 0x62, 0x4e, 0xd3, 0x43, 0x92, 0x5c, 0xd0, 0x32, 0xb5,
-	0x7f, 0xba, 0x8a, 0xe7, 0xb2, 0xe8, 0x2d, 0x8c, 0xe2, 0x26, 0xac, 0xfc, 0xfe, 0xce, 0x60, 0x77,
-	0x6d, 0x6f, 0xbc, 0xec, 0x3b, 0x47, 0x54, 0x69, 0x96, 0xb1, 0x84, 0x68, 0xfa, 0xaa, 0xd6, 0x53,
-	0xa1, 0x98, 0xbe, 0x74, 0x2d, 0xf0, 0xac, 0x41, 0xf8, 0xab, 0x0f, 0x9b, 0xb7, 0x20, 0x11, 0x02,
-	0xaf, 0x24, 0x05, 0x75, 0x52, 0x6c, 0x6c, 0x72, 0xfa, 0x52, 0x52, 0x3b, 0xd2, 0x55, 0x6c, 0x63,
-	0xf4, 0x1e, 0x86, 0xa9, 0x34, 0x8d, 0xdc, 0xc4, 0x9e, 0xdf, 0x51, 0x52, 0x74, 0x6c, 0xd9, 0xd8,
-	0x75, 0x41, 0x4f, 0xc1, 0x4b, 0x44, 0x99, 0xb9, 0x41, 0x6e, 0x44, 0xcd, 0x8a, 0x44, 0xed, 0x8a,
-	0x44, 0x1f, 0xec, 0x8a, 0x60, 0x0b, 0x0a, 0x7e, 0xf4, 0x60, 0xd8, 0xf0, 0xd1, 0x47, 0x18, 0x29,
-	0xa1, 0x89, 0x66, 0xa2, 0x74, 0x8b, 0xf2, 0xf2, 0xef, 0x94, 0x44, 0xd8, 0xb5, 0xc1, 0xb3, 0x86,
-	0xc1, 0x13, 0x18, 0xb5, 0x59, 0xb4, 0x0d, 0x40, 0xbf, 0x4a, 0xa6, 0xae, 0x3e, 0xb5, 0x8a, 0xaf,
-	0x65, 0xc2, 0x4f, 0xb0, 0xe2, 0xf6, 0xca, 0x18, 0x9b, 0xd2, 0x8c, 0xd4, 0x5c, 0xcf, 0x19, 0xdb,
-	0xcd, 0xa2, 0x17, 0x0b, 0xc6, 0x86, 0xb7, 0xac, 0xeb, 0xa2, 0x97, 0x3f, 0x7b, 0xb0, 0xde, 0x2d,
-	0x2e, 0xb5, 0xef, 0x00, 0x46, 0x15, 0x29, 0x24, 0xbf, 0xba, 0x8a, 0xad, 0x85, 0xf1, 0x1e, 0x8b,
-	0x3a, 0xe6, 0xf4, 0x8c, 0xf0, 0x9a, 0xe2, 0x19, 0x7a, 0x66, 0xfc, 0xe0, 0x9a, 0xf1, 0x77, 0x31,
-	0x2a, 0xd4, 0x10, 0x9c, 0x33, 0x79, 0xc1, 0xca, 0xae, 0xcc, 0x23, 0x51, 0x66, 0x2c, 0x47, 0xf7,
-	0x61, 0x50, 0x2b, 0xee, 0xb4, 0x9a, 0x10, 0x3d, 0x82, 0x7b, 0xe6, 0x26, 0xe9, 0x9b, 0x74, 0xb2,
-	0x77, 0x10, 0x33, 0x6d, 0xf5, 0x8e, 0x70, 0x37, 0x69, 0xac, 0x20, 0x92, 0x9d, 0x51, 0x55, 0x19,
-	0x2b, 0x1a, 0x71, 0xd7, 0x32, 0xc6, 0x0a, 0x77, 0xab, 0xff, 0xdb, 0x0a, 0xd7, 0x76, 0xd1, 0x8a,
-	0xef, 0xb0, 0xde, 0xad, 0x2d, 0x75, 0xe2, 0x01, 0x0c, 0x33, 0xa1, 0x0a, 0xa2, 0xdd, 0x29, 0xb9,
-	0xd7, 0xbf, 0xcf, 0x39, 0x02, 0xff, 0x35, 0xe3, 0xb4, 0x2b, 0xc1, 0x4d, 0x19, 0x81, 0x27, 0x89,
-	0x9e, 0xb6, 0x42, 0x4c, 0x1c, 0xee, 0xc3, 0xc6, 0x69, 0x22, 0x97, 0xc2, 0x7d, 0x58, 0x21, 0x69,
-	0xaa, 0x68, 0x55, 0x39, 0x46, 0xfb, 0x3c, 0x84, 0xf3, 0x51, 0x3b, 0x88, 0x78, 0x68, 0x75, 0xec,
-	0xff, 0x09, 0x00, 0x00, 0xff, 0xff, 0x9a, 0x04, 0xb7, 0x7f, 0x36, 0x06, 0x00, 0x00,
+	// 672 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0x4d, 0x6f, 0xd4, 0x3a,
+	0x14, 0xd5, 0x74, 0xf2, 0xa6, 0xe9, 0x1d, 0xbd, 0xea, 0xc9, 0x8b, 0xd7, 0x28, 0xed, 0xab, 0xaa,
+	0xe8, 0x09, 0x2a, 0x90, 0x32, 0x9a, 0xa9, 0x40, 0x5d, 0xa0, 0x22, 0xda, 0x0a, 0x81, 0xa0, 0x45,
+	0x32, 0x55, 0x17, 0x65, 0xe5, 0x24, 0x9e, 0x24, 0x9a, 0x4c, 0x6c, 0x6c, 0x87, 0x52, 0x21, 0x7e,
+	0x04, 0xeb, 0x2e, 0xf8, 0x57, 0xfc, 0x1e, 0x64, 0xc7, 0x99, 0xce, 0x57, 0x2b, 0x15, 0xd8, 0x5d,
+	0xdf, 0x7b, 0xce, 0xcd, 0xf1, 0xbd, 0xc7, 0x01, 0x6f, 0x4c, 0x65, 0xd6, 0xfb, 0xd4, 0x27, 0x05,
+	0xcf, 0x48, 0xbf, 0xa7, 0x4f, 0x21, 0x17, 0x4c, 0x31, 0x84, 0x46, 0xd5, 0x98, 0x84, 0x26, 0xd1,
+	0x94, 0xfd, 0xcd, 0x79, 0xb4, 0x12, 0x79, 0x2c, 0x6b, 0x82, 0xbf, 0x9d, 0x32, 0x96, 0x16, 0xb4,
+	0x67, 0x4e, 0x51, 0x35, 0xec, 0x5d, 0x0a, 0xc2, 0x39, 0x15, 0x4d, 0x7d, 0x6b, 0xbe, 0x2e, 0x95,
+	0xa8, 0x62, 0x55, 0x57, 0x83, 0xeb, 0x36, 0x38, 0x27, 0x54, 0x66, 0xa8, 0x0f, 0xce, 0x58, 0x15,
+	0xd2, 0x6b, 0xed, 0xb4, 0x76, 0xbb, 0x83, 0xff, 0xc2, 0x45, 0x19, 0xa1, 0xc6, 0x85, 0x27, 0xaa,
+	0x90, 0xd8, 0x40, 0xd1, 0x13, 0x58, 0x55, 0x82, 0xc4, 0x79, 0x99, 0x7a, 0x2b, 0x86, 0xb5, 0xb9,
+	0x8c, 0x75, 0x56, 0x43, 0x70, 0x83, 0xd5, 0xb4, 0x82, 0xa5, 0xa9, 0xa6, 0xb5, 0x6f, 0xa7, 0xbd,
+	0xad, 0x21, 0xb8, 0xc1, 0x6a, 0x9a, 0xbd, 0xb8, 0xe7, 0xdc, 0x4e, 0x3b, 0xa9, 0x21, 0xb8, 0xc1,
+	0xa2, 0x03, 0x80, 0x92, 0xaa, 0x4b, 0x26, 0x46, 0xfa, 0x83, 0x7f, 0x19, 0xe6, 0xf6, 0x32, 0xe6,
+	0xe9, 0x04, 0x85, 0xa7, 0x18, 0xfe, 0x17, 0x70, 0xf4, 0x95, 0xd1, 0x03, 0x58, 0xa7, 0x25, 0x89,
+	0x0a, 0x9a, 0x1c, 0x92, 0x78, 0x44, 0xcb, 0xc4, 0x4c, 0x6a, 0x0d, 0xcf, 0x65, 0xd1, 0x1b, 0x70,
+	0xa3, 0x3a, 0x94, 0xde, 0xca, 0x4e, 0x7b, 0xb7, 0x3b, 0xe8, 0x2d, 0xfb, 0xda, 0x11, 0x15, 0x2a,
+	0x1f, 0xe6, 0x31, 0x51, 0xf4, 0x45, 0xa5, 0x32, 0x26, 0x72, 0x75, 0x65, 0x5b, 0xe0, 0x49, 0x83,
+	0xe0, 0xc7, 0x0a, 0x6c, 0xde, 0x81, 0x44, 0x08, 0x9c, 0x92, 0x8c, 0xa9, 0x95, 0x62, 0x62, 0x9d,
+	0x53, 0x57, 0x9c, 0x9a, 0x95, 0xac, 0x61, 0x13, 0xa3, 0x53, 0xe8, 0x24, 0x5c, 0x37, 0xb2, 0x13,
+	0x7f, 0x7a, 0x4f, 0x49, 0xe1, 0xb1, 0x61, 0x63, 0xdb, 0x05, 0x3d, 0x06, 0x27, 0x66, 0xe5, 0xd0,
+	0x2e, 0x62, 0x23, 0xac, 0x2d, 0x16, 0x36, 0x16, 0x0b, 0xdf, 0x1b, 0x8b, 0x61, 0x03, 0xf2, 0xbf,
+	0xb5, 0xa0, 0x53, 0xf3, 0xd1, 0x07, 0x70, 0x05, 0x53, 0x44, 0xe5, 0xac, 0xb4, 0x46, 0x7b, 0xfe,
+	0x6b, 0x4a, 0x42, 0x6c, 0xdb, 0xe0, 0x49, 0x43, 0xff, 0x11, 0xb8, 0x4d, 0x16, 0x6d, 0x03, 0xd0,
+	0xcf, 0x3c, 0x17, 0x37, 0x9f, 0x5a, 0xc3, 0x53, 0x99, 0xe0, 0xba, 0x05, 0x70, 0xb3, 0x70, 0x74,
+	0x04, 0x2e, 0xab, 0x54, 0xc4, 0x2a, 0xbb, 0xd6, 0xee, 0xe0, 0xe1, 0xdd, 0x16, 0x09, 0xdf, 0x59,
+	0x38, 0x9e, 0x10, 0xfd, 0x57, 0xe0, 0x36, 0x59, 0xf4, 0x0c, 0xba, 0x9c, 0x48, 0xa9, 0x32, 0xc1,
+	0xaa, 0x34, 0xb3, 0x3d, 0xfd, 0x85, 0x39, 0x1d, 0x32, 0x56, 0x9c, 0x93, 0xa2, 0xa2, 0x78, 0x1a,
+	0x1e, 0x7c, 0x84, 0x55, 0xfb, 0x6a, 0xb4, 0xed, 0x12, 0x3a, 0x24, 0x55, 0xa1, 0xe6, 0x6c, 0x37,
+	0x9b, 0x45, 0x07, 0x0b, 0xb6, 0x0b, 0xee, 0x78, 0x8c, 0x8b, 0x4e, 0xfb, 0xde, 0x82, 0xf5, 0xd9,
+	0xe2, 0x52, 0x73, 0xed, 0x83, 0x2b, 0xc9, 0x98, 0x17, 0x37, 0x6f, 0x7e, 0x6b, 0xe1, 0x52, 0xc7,
+	0xac, 0x8a, 0x0a, 0x5a, 0x5f, 0x6b, 0x82, 0x9e, 0xd8, 0xb2, 0x3d, 0x65, 0xcb, 0xfb, 0xd8, 0x28,
+	0x50, 0xe0, 0x5f, 0xe4, 0x7c, 0x94, 0x97, 0xb3, 0x32, 0x8f, 0x58, 0x39, 0xcc, 0x53, 0xf4, 0x0f,
+	0xb4, 0x2b, 0x51, 0x58, 0xad, 0x3a, 0x44, 0xff, 0xc3, 0xdf, 0xfa, 0x8f, 0x43, 0x5f, 0x27, 0xfd,
+	0xc1, 0x7e, 0x94, 0x2b, 0xa3, 0xd7, 0xc5, 0xb3, 0x49, 0x6d, 0x14, 0xc2, 0xf3, 0x73, 0x2a, 0xa4,
+	0x36, 0x4a, 0x2d, 0x6e, 0x2a, 0xa3, 0x57, 0x61, 0xff, 0x44, 0x7f, 0x7a, 0x15, 0xb6, 0xed, 0xe2,
+	0x2a, 0xbe, 0xc2, 0xfa, 0x6c, 0x6d, 0xe9, 0x26, 0xfe, 0x85, 0xce, 0x90, 0x89, 0x31, 0x51, 0xf6,
+	0xa1, 0xdb, 0xd3, 0xef, 0xcf, 0x39, 0x04, 0xef, 0x65, 0x5e, 0xd0, 0x59, 0x09, 0x76, 0xca, 0x08,
+	0x1c, 0x4e, 0x54, 0xd6, 0x08, 0xd1, 0x71, 0xb0, 0x07, 0x1b, 0x67, 0x31, 0x5f, 0x0a, 0xf7, 0x60,
+	0x95, 0x24, 0x89, 0xa0, 0x52, 0x5a, 0x46, 0x73, 0x3c, 0x84, 0x0b, 0xb7, 0x19, 0x44, 0xd4, 0x31,
+	0x3a, 0xf6, 0x7e, 0x06, 0x00, 0x00, 0xff, 0xff, 0x18, 0x6a, 0x56, 0x78, 0x14, 0x07, 0x00, 0x00,
 }
diff --git a/api/mesh/v1alpha1/mesh.pb.validate.go b/api/mesh/v1alpha1/mesh.pb.validate.go
index 83b5f66fa..e1351b4a0 100644
--- a/api/mesh/v1alpha1/mesh.pb.validate.go
+++ b/api/mesh/v1alpha1/mesh.pb.validate.go
@@ -83,6 +83,16 @@ func (m *Mesh) Validate() error {
 		}
 	}
 
+	if v, ok := interface{}(m.GetNetworking()).(interface{ Validate() error }); ok {
+		if err := v.Validate(); err != nil {
+			return MeshValidationError{
+				field:  "Networking",
+				reason: "embedded message failed validation",
+				cause:  err,
+			}
+		}
+	}
+
 	return nil
 }
 
@@ -232,6 +242,80 @@ var _ interface {
 	ErrorName() string
 } = CertificateAuthorityBackendValidationError{}
 
+// Validate checks the field values on Networking with the rules defined in the
+// proto definition for this message. If any rules are violated, an error is returned.
+func (m *Networking) Validate() error {
+	if m == nil {
+		return nil
+	}
+
+	if v, ok := interface{}(m.GetOutbound()).(interface{ Validate() error }); ok {
+		if err := v.Validate(); err != nil {
+			return NetworkingValidationError{
+				field:  "Outbound",
+				reason: "embedded message failed validation",
+				cause:  err,
+			}
+		}
+	}
+
+	return nil
+}
+
+// NetworkingValidationError is the validation error returned by
+// Networking.Validate if the designated constraints aren't met.
+type NetworkingValidationError struct {
+	field  string
+	reason string
+	cause  error
+	key    bool
+}
+
+// Field function returns field value.
+func (e NetworkingValidationError) Field() string { return e.field }
+
+// Reason function returns reason value.
+func (e NetworkingValidationError) Reason() string { return e.reason }
+
+// Cause function returns cause value.
+func (e NetworkingValidationError) Cause() error { return e.cause }
+
+// Key function returns key value.
+func (e NetworkingValidationError) Key() bool { return e.key }
+
+// ErrorName returns error name.
+func (e NetworkingValidationError) ErrorName() string { return "NetworkingValidationError" }
+
+// Error satisfies the builtin error interface
+func (e NetworkingValidationError) Error() string {
+	cause := ""
+	if e.cause != nil {
+		cause = fmt.Sprintf(" | caused by: %v", e.cause)
+	}
+
+	key := ""
+	if e.key {
+		key = "key for "
+	}
+
+	return fmt.Sprintf(
+		"invalid %sNetworking.%s: %s%s",
+		key,
+		e.field,
+		e.reason,
+		cause)
+}
+
+var _ error = NetworkingValidationError{}
+
+var _ interface {
+	Field() string
+	Reason() string
+	Key() bool
+	Cause() error
+	ErrorName() string
+} = NetworkingValidationError{}
+
 // Validate checks the field values on Tracing with the rules defined in the
 // proto definition for this message. If any rules are violated, an error is returned.
 func (m *Tracing) Validate() error {
@@ -1003,3 +1087,80 @@ var _ interface {
 	Cause() error
 	ErrorName() string
 } = CertificateAuthorityBackend_DpCert_RotationValidationError{}
+
+// Validate checks the field values on Networking_Outbound with the rules
+// defined in the proto definition for this message. If any rules are
+// violated, an error is returned.
+func (m *Networking_Outbound) Validate() error {
+	if m == nil {
+		return nil
+	}
+
+	if v, ok := interface{}(m.GetPassthrough()).(interface{ Validate() error }); ok {
+		if err := v.Validate(); err != nil {
+			return Networking_OutboundValidationError{
+				field:  "Passthrough",
+				reason: "embedded message failed validation",
+				cause:  err,
+			}
+		}
+	}
+
+	return nil
+}
+
+// Networking_OutboundValidationError is the validation error returned by
+// Networking_Outbound.Validate if the designated constraints aren't met.
+type Networking_OutboundValidationError struct {
+	field  string
+	reason string
+	cause  error
+	key    bool
+}
+
+// Field function returns field value.
+func (e Networking_OutboundValidationError) Field() string { return e.field }
+
+// Reason function returns reason value.
+func (e Networking_OutboundValidationError) Reason() string { return e.reason }
+
+// Cause function returns cause value.
+func (e Networking_OutboundValidationError) Cause() error { return e.cause }
+
+// Key function returns key value.
+func (e Networking_OutboundValidationError) Key() bool { return e.key }
+
+// ErrorName returns error name.
+func (e Networking_OutboundValidationError) ErrorName() string {
+	return "Networking_OutboundValidationError"
+}
+
+// Error satisfies the builtin error interface
+func (e Networking_OutboundValidationError) Error() string {
+	cause := ""
+	if e.cause != nil {
+		cause = fmt.Sprintf(" | caused by: %v", e.cause)
+	}
+
+	key := ""
+	if e.key {
+		key = "key for "
+	}
+
+	return fmt.Sprintf(
+		"invalid %sNetworking_Outbound.%s: %s%s",
+		key,
+		e.field,
+		e.reason,
+		cause)
+}
+
+var _ error = Networking_OutboundValidationError{}
+
+var _ interface {
+	Field() string
+	Reason() string
+	Key() bool
+	Cause() error
+	ErrorName() string
+} = Networking_OutboundValidationError{}
diff --git a/api/mesh/v1alpha1/mesh.proto b/api/mesh/v1alpha1/mesh.proto
index f6fbeaebc..043d06f30 100644
--- a/api/mesh/v1alpha1/mesh.proto
+++ b/api/mesh/v1alpha1/mesh.proto
@@ -40,6 +40,9 @@ message Mesh {
   // for each dataplane individually using Dataplane resource.
   // +optional
   Metrics metrics = 4;
+
+  // Networking settings of the mesh
+  Networking networking = 5;
 }
 
 // CertificateAuthorityBackend defines Certificate Authority backend
@@ -70,6 +73,19 @@ message CertificateAuthorityBackend {
   google.protobuf.Struct conf = 4;
 }
 
+// Networking defines the networking configuration of the mesh
+message Networking {
+
+  // Outbound describes the common mesh outbound settings
+  message Outbound {
+    // Control the passthrough cluster
+    google.protobuf.BoolValue passthrough = 1;
+  }
+
+  // Outbound settings
+  Outbound outbound = 1;
+}
+
 // Tracing defines tracing configuration of the mesh.
 message Tracing {
 
diff --git a/api/mesh/v1alpha1/mesh_helpers.go b/api/mesh/v1alpha1/mesh_helpers.go
new file mode 100644
index 000000000..e6e412b1d
--- /dev/null
+++ b/api/mesh/v1alpha1/mesh_helpers.go
@@ -0,0 +1,9 @@
+package v1alpha1
+
+func (m *Mesh) IsPassthrough() bool {
+	passthrough := m.GetNetworking().GetOutbound().GetPassthrough()
+	if passthrough == nil {
+		return true
+	}
+	return passthrough.GetValue()
+}
diff --git a/pkg/config/app/kuma-cp/kuma-cp.defaults.yaml b/pkg/config/app/kuma-cp/kuma-cp.defaults.yaml
index 6bce18d94..cceab2800 100644
--- a/pkg/config/app/kuma-cp/kuma-cp.defaults.yaml
+++ b/pkg/config/app/kuma-cp/kuma-cp.defaults.yaml
@@ -164,6 +164,12 @@ runtime:
     injector:
       # if true runs kuma-cp in CNI compatible mode
       cniEnabled: false # ENV: KUMA_RUNTIME_KUBERNETES_INJECTOR_CNI_ENABLED
+      # list of exceptions for Kuma injection
+      exceptions:
+        # a map of labels for exception. If pod matches label with given value Kuma won't be injected. Specify '*' to match any value.
+        labels:
+          openshift.io/build.name: "*"
+          openshift.io/deployer-pod-for.name: "*"
       # VirtualProbesEnabled enables automatic converting HttpGet probes to virtual. Virtual probe
       #	serves on sub-path of insecure port 'virtualProbesPort',
       #	i.e :8080/health/readiness -> :9000/8080/health/readiness where 9000 is virtualProbesPort
diff --git a/pkg/config/plugins/runtime/k8s/config.go b/pkg/config/plugins/runtime/k8s/config.go
index 8c4214dfd..219ca2beb 100644
--- a/pkg/config/plugins/runtime/k8s/config.go
+++ b/pkg/config/plugins/runtime/k8s/config.go
@@ -59,6 +59,13 @@ func DefaultKubernetesRuntimeConfig() *KubernetesRuntimeConfig {
 				ExcludeInboundPorts:  []uint32{},
 				ExcludeOutboundPorts: []uint32{},
 			},
+			Exceptions: Exceptions{
+				Labels: map[string]string{
+					// when using DeploymentConfig instead of Deployment, OpenShift will create an extra deployer Pod for which we don't want to inject Kuma
+					"openshift.io/build.name":            "*",
+					"openshift.io/deployer-pod-for.name": "*",
+				},
+			},
 		},
 	}
 }
@@ -99,6 +106,14 @@ type Injector struct {
 	VirtualProbesPort uint32 `yaml:"virtualProbesPort" envconfig:"kuma_runtime_kubernetes_virtual_probes_enabled"`
 	// Cnfiguration for a traffic that is intercepted by sidecar
 	SidecarTraffic SidecarTraffic `yaml:"sidecarTraffic"`
+	// Exceptions defines list of exceptions for Kuma injection
+	Exceptions Exceptions `yaml:"exceptions"`
+}
+
+// Exceptions defines list of exceptions for Kuma injection
+type Exceptions struct {
+	// Labels is a map of labels for exception. If pod matches label with given value Kuma won't be injected. Specify '*' to match any value.
+	Labels map[string]string `yaml:"labels" envconfig:"kuma_runtime_kubernetes_exceptions_labels"`
 }
 
 type SidecarTraffic struct {
diff --git a/pkg/plugins/runtime/k8s/webhooks/injector/injector.go b/pkg/plugins/runtime/k8s/webhooks/injector/injector.go
index 9a53be013..ae58460c7 100644
--- a/pkg/plugins/runtime/k8s/webhooks/injector/injector.go
+++ b/pkg/plugins/runtime/k8s/webhooks/injector/injector.go
@@ -8,6 +8,7 @@ import (
 	"github.com/pkg/errors"
 
 	runtime_k8s "github.com/kumahq/kuma/pkg/config/plugins/runtime/k8s"
+	"github.com/kumahq/kuma/pkg/core"
 	core_model "github.com/kumahq/kuma/pkg/core/resources/model"
 
 	mesh_core "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
@@ -31,6 +32,8 @@ const (
 	serviceAccountTokenMountPath = "/var/run/secrets/kubernetes.io/serviceaccount"
 )
 
+var log = core.Log.WithName("injector")
+
 func New(cfg runtime_k8s.Injector, controlPlaneUrl string, client kube_client.Client) *KumaInjector {
 	return &KumaInjector{
 		cfg:             cfg,
@@ -52,11 +55,13 @@ func (i *KumaInjector) InjectKuma(pod *kube_core.Pod) error {
 	if err != nil {
 		return errors.Wrap(err, "could not retrieve namespace for pod")
 	}
-	if inject, err := needInject(pod, ns); err != nil {
+	if inject, err := i.needInject(pod, ns); err != nil {
 		return err
 	} else if !inject {
+		log.V(1).Info("skip injecting Kuma", "name", pod.Name, "namespace", pod.Namespace)
 		return nil
 	}
+	log.Info("injecting Kuma", "name", pod.Name, "namespace", pod.Namespace)
 	// sidecar container
 	if pod.Spec.Containers == nil {
 		pod.Spec.Containers = []kube_core.Container{}
@@ -95,12 +100,20 @@ func (i *KumaInjector) InjectKuma(pod *kube_core.Pod) error {
 	return nil
 }
 
-func needInject(pod *kube_core.Pod, ns *kube_core.Namespace) (bool, error) {
+func (i *KumaInjector) needInject(pod *kube_core.Pod, ns *kube_core.Namespace) (bool, error) {
+	log.WithValues("name", pod.Name, "namespace", pod.Namespace)
+	if i.isInjectionException(pod) {
+		log.V(1).Info("pod fulfills exception requirements")
+		return false, nil
+	}
 	enabled, exist, err := metadata.Annotations(pod.Annotations).GetEnabled(metadata.KumaSidecarInjectionAnnotation)
 	if err != nil {
 		return false, err
 	}
 	if exist {
+		if !enabled {
+			log.V(1).Info("pod has kuma.io/sidecar-injection: disabled annotation")
+		}
 		return enabled, nil
 	}
 	enabled, exist, err = metadata.Annotations(ns.Annotations).GetEnabled(metadata.KumaSidecarInjectionAnnotation)
@@ -108,11 +121,24 @@ func needInject(pod *kube_core.Pod, ns *kube_core.Namespace) (bool, error) {
 		return false, err
 	}
 	if exist {
+		if !enabled {
+			log.V(1).Info("namespace has kuma.io/sidecar-injection: disabled annotation")
+		}
 		return enabled, nil
 	}
 	return false, nil
 }
 
+func (i *KumaInjector) isInjectionException(pod *kube_core.Pod) bool {
+	for key, value := range i.cfg.Exceptions.Labels {
+		podValue, exist := pod.Labels[key]
+		if exist && (value == "*" || value == podValue) {
+			return true
+		}
+	}
+	return false
+}
+
 func meshName(pod *kube_core.Pod, ns *kube_core.Namespace) string {
 	if mesh, exist := metadata.Annotations(pod.Annotations).GetString(metadata.KumaMeshAnnotation); exist {
 		return mesh
diff --git a/pkg/plugins/runtime/k8s/webhooks/injector/probes.go b/pkg/plugins/runtime/k8s/webhooks/injector/probes.go
index a0a44e3d9..69d6c8716 100644
--- a/pkg/plugins/runtime/k8s/webhooks/injector/probes.go
+++ b/pkg/plugins/runtime/k8s/webhooks/injector/probes.go
@@ -10,7 +10,9 @@ import (
 )
 
 func (i *KumaInjector) overrideHTTPProbes(pod *kube_core.Pod) error {
+	log.WithValues("name", pod.Name, "namespace", pod.Namespace)
 	if ok, err := needVirtualProbes(pod, i.cfg); err != nil || !ok {
+		log.V(1).Info("skip adding virtual probes")
 		return err
 	}
 
@@ -21,11 +23,13 @@ func (i *KumaInjector) overrideHTTPProbes(pod *kube_core.Pod) error {
 
 	for _, c := range pod.Spec.Containers {
 		if c.LivenessProbe != nil && c.LivenessProbe.HTTPGet != nil {
+			log.V(1).Info("overriding liveness probe", "container", c.Name)
 			if err := overrideHTTPProbe(c.LivenessProbe, port); err != nil {
 				return err
 			}
 		}
 		if c.ReadinessProbe != nil && c.ReadinessProbe.HTTPGet != nil {
+			log.V(1).Info("overriding readiness probe", "container", c.Name)
 			if err := overrideHTTPProbe(c.ReadinessProbe, port); err != nil {
 				return err
 			}
diff --git a/pkg/xds/generator/transparent_proxy_generator.go b/pkg/xds/generator/transparent_proxy_generator.go
index 99a951c0c..099f8ae9b 100644
--- a/pkg/xds/generator/transparent_proxy_generator.go
+++ b/pkg/xds/generator/transparent_proxy_generator.go
@@ -1,6 +1,7 @@
 package generator
 
 import (
+	envoy_api_v2 "github.com/envoyproxy/go-control-plane/envoy/api/v2"
 	"github.com/pkg/errors"
 
 	mesh_core "github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
@@ -12,7 +13,11 @@ import (
 )
 
 // OriginTransparent is a marker to indicate by which ProxyGenerator resources were generated.
-const OriginTransparent = "transparent"
+const (
+	OriginTransparent = "transparent"
+	outboundName      = "outbound:passthrough"
+	inboundName       = "inbound:passthrough"
+)
 
 type TransparentProxyGenerator struct {
 }
@@ -25,8 +30,21 @@ func (_ TransparentProxyGenerator) Generate(ctx xds_context.Context, proxy *mode
 	}
 	sourceService := proxy.Dataplane.Spec.GetIdentifyingService()
 	meshName := ctx.Mesh.Resource.GetMeta().GetName()
-	const outboundName = "outbound:passthrough"
-	listener, err := envoy_listeners.NewListenerBuilder().
+
+	var outboundPassThroughCluster *envoy_api_v2.Cluster = nil
+	var outboundListener *envoy_api_v2.Listener = nil
+	var err error
+
+	if ctx.Mesh.Resource.Spec.IsPassthrough() {
+		outboundPassThroughCluster, err = envoy_clusters.NewClusterBuilder().
+			Configure(envoy_clusters.PassThroughCluster(outboundName)).
+			Build()
+		if err != nil {
+			return nil, errors.Wrapf(err, "could not generate outbound cluster: %s", outboundName)
+		}
+	}
+
+	outboundListener, err = envoy_listeners.NewListenerBuilder().
 		Configure(envoy_listeners.OutboundListener(outboundName, "0.0.0.0", redirectPortOutbound)).
 		Configure(envoy_listeners.FilterChain(envoy_listeners.NewFilterChainBuilder().
 			Configure(envoy_listeners.TcpProxy(outboundName, envoy_common.ClusterSubset{ClusterName: outboundName})).
@@ -36,15 +54,9 @@ func (_ TransparentProxyGenerator) Generate(ctx xds_context.Context, proxy *mode
 	if err != nil {
 		return nil, errors.Wrapf(err, "could not generate listener: %s", outboundName)
 	}
-	cluster, err := envoy_clusters.NewClusterBuilder().
-		Configure(envoy_clusters.PassThroughCluster(outboundName)).
-		Build()
-	if err != nil {
-		return nil, errors.Wrapf(err, "could not generate cluster: %s", outboundName)
-	}
 
 	redirectPortInbound := proxy.Dataplane.Spec.Networking.GetTransparentProxying().GetRedirectPortInbound()
-	const inboundName = "inbound:passthrough"
+
 	inboundPassThroughCluster, err := envoy_clusters.NewClusterBuilder().
 		Configure(envoy_clusters.PassThroughCluster(inboundName)).
 		Configure(envoy_clusters.UpstreamBindConfig("127.0.0.6", 0)).
@@ -64,15 +76,18 @@ func (_ TransparentProxyGenerator) Generate(ctx xds_context.Context, proxy *mode
 	}
 
 	resources.Add(&model.Resource{
-		Name:     listener.Name,
+		Name:     outboundListener.Name,
 		Origin:   OriginTransparent,
-		Resource: listener,
-	})
-	resources.Add(&model.Resource{
-		Name:     cluster.Name,
-		Origin:   OriginTransparent,
-		Resource: cluster,
+		Resource: outboundListener,
 	})
+
+	if ctx.Mesh.Resource.Spec.IsPassthrough() {
+		resources.Add(&model.Resource{
+			Name:     outboundPassThroughCluster.Name,
+			Origin:   OriginTransparent,
+			Resource: outboundPassThroughCluster,
+		})
+	}
 	resources.Add(&model.Resource{
 		Name:     inboundListener.Name,
 		Origin:   OriginTransparent,


Test patch:
diff --git a/pkg/api-server/config_ws_test.go b/pkg/api-server/config_ws_test.go
index a56b676a3..dc576ec9d 100644
--- a/pkg/api-server/config_ws_test.go
+++ b/pkg/api-server/config_ws_test.go
@@ -202,7 +202,13 @@ var _ = Describe("Config WS", func() {
                   "excludeOutboundPorts": []
                 },
                 "virtualProbesEnabled": true,
-                "virtualProbesPort": 9000
+                "virtualProbesPort": 9000,
+                "exceptions": {
+                  "labels": {
+                    "openshift.io/build.name": "*",
+                    "openshift.io/deployer-pod-for.name": "*"
+                  }
+                }
               }
             },
             "universal": {
diff --git a/pkg/config/plugins/runtime/k8s/testdata/default-config.golden.yaml b/pkg/config/plugins/runtime/k8s/testdata/default-config.golden.yaml
index 16222c559..869217cbb 100644
--- a/pkg/config/plugins/runtime/k8s/testdata/default-config.golden.yaml
+++ b/pkg/config/plugins/runtime/k8s/testdata/default-config.golden.yaml
@@ -38,3 +38,7 @@ injector:
   sidecarTraffic:
     excludeInboundPorts: []
     excludeOutboundPorts: []
+  exceptions:
+    labels:
+      openshift.io/build.name: "*"
+      openshift.io/deployer-pod-for.name: "*"
diff --git a/pkg/plugins/runtime/k8s/webhooks/injector/injector_test.go b/pkg/plugins/runtime/k8s/webhooks/injector/injector_test.go
index ec8592723..19c301b27 100644
--- a/pkg/plugins/runtime/k8s/webhooks/injector/injector_test.go
+++ b/pkg/plugins/runtime/k8s/webhooks/injector/injector_test.go
@@ -420,5 +420,22 @@ var _ = Describe("Injector", func() {
                   kuma.io/sidecar-injection: enabled`,
 			cfgFile: "inject.config-ports.yaml",
 		}),
+		Entry("20. skip injection for label exception", testCase{
+			num: "20",
+			mesh: `
+              apiVersion: kuma.io/v1alpha1
+              kind: Mesh
+              metadata:
+                name: default
+              spec: {}`,
+			namespace: `
+              apiVersion: v1
+              kind: Namespace
+              metadata:
+                name: default
+                annotations:
+                  kuma.io/sidecar-injection: enabled`,
+			cfgFile: "inject.config.yaml",
+		}),
 	)
 })
diff --git a/pkg/plugins/runtime/k8s/webhooks/injector/testdata/inject.20.golden.yaml b/pkg/plugins/runtime/k8s/webhooks/injector/testdata/inject.20.golden.yaml
new file mode 100644
index 000000000..c98b65e09
--- /dev/null
+++ b/pkg/plugins/runtime/k8s/webhooks/injector/testdata/inject.20.golden.yaml
@@ -0,0 +1,22 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  creationTimestamp: null
+  labels:
+    run: busybox
+    openshift.io/deployer-pod-for.name: "1234"
+  name: busybox
+spec:
+  containers:
+  - image: busybox
+    name: busybox
+    resources: {}
+    volumeMounts:
+    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
+      name: default-token-w7dxf
+      readOnly: true
+  volumes:
+  - name: default-token-w7dxf
+    secret:
+      secretName: default-token-w7dxf
+status: {}
diff --git a/pkg/plugins/runtime/k8s/webhooks/injector/testdata/inject.20.input.yaml b/pkg/plugins/runtime/k8s/webhooks/injector/testdata/inject.20.input.yaml
new file mode 100644
index 000000000..9d3177edd
--- /dev/null
+++ b/pkg/plugins/runtime/k8s/webhooks/injector/testdata/inject.20.input.yaml
@@ -0,0 +1,20 @@
+apiVersion: v1
+kind: Pod
+metadata:
+  name: busybox
+  labels:
+    run: busybox
+    openshift.io/deployer-pod-for.name: "1234"
+spec:
+  volumes:
+  - name: default-token-w7dxf
+    secret:
+      secretName: default-token-w7dxf
+  containers:
+  - name: busybox
+    image: busybox
+    resources: {}
+    volumeMounts:
+    - name: default-token-w7dxf
+      readOnly: true
+      mountPath: "/var/run/secrets/kubernetes.io/serviceaccount"
diff --git a/pkg/plugins/runtime/k8s/webhooks/injector/testdata/inject.config.yaml b/pkg/plugins/runtime/k8s/webhooks/injector/testdata/inject.config.yaml
index 639d5e479..80c21e1dd 100644
--- a/pkg/plugins/runtime/k8s/webhooks/injector/testdata/inject.config.yaml
+++ b/pkg/plugins/runtime/k8s/webhooks/injector/testdata/inject.config.yaml
@@ -33,3 +33,6 @@ initContainer:
   image: kuma/kuma-init:latest
 virtualProbesEnabled: true
 virtualProbesPort: 9000
+exceptions:
+  labels:
+    "openshift.io/deployer-pod-for.name": "*"
diff --git a/test/e2e/externalservices_kubernetes_test.go b/test/e2e/externalservices_kubernetes_test.go
index 5b9598c9f..d16c081a5 100644
--- a/test/e2e/externalservices_kubernetes_test.go
+++ b/test/e2e/externalservices_kubernetes_test.go
@@ -3,11 +3,13 @@ package e2e_test
 import (
 	"fmt"
 
-	"github.com/gruntwork-io/terratest/modules/k8s"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
+	"github.com/gruntwork-io/terratest/modules/k8s"
+	"github.com/gruntwork-io/terratest/modules/retry"
 	. "github.com/onsi/ginkgo"
 	. "github.com/onsi/gomega"
+	v1 "k8s.io/api/core/v1"
 
 	"github.com/kumahq/kuma/pkg/config/core"
 
@@ -28,6 +30,9 @@ spec:
     backends:
       - name: ca-1
         type: builtin
+  networking:
+    outbound:
+      passthrough: %s
 `
 
 	trafficPermissionAll := `
@@ -99,6 +104,7 @@ metadata:
 	}
 
 	var cluster Cluster
+	var clientPod *v1.Pod
 
 	BeforeEach(func() {
 		clusters, err := NewK8sClusters(
@@ -125,7 +131,7 @@ metadata:
 			Setup(cluster)
 		Expect(err).ToNot(HaveOccurred())
 
-		err = YamlK8s(meshDefaulMtlsOn)(cluster)
+		err = YamlK8s(fmt.Sprintf(meshDefaulMtlsOn, "false"))(cluster)
 		Expect(err).ToNot(HaveOccurred())
 
 		err = YamlK8s(trafficPermissionAll)(cluster)
@@ -148,6 +154,18 @@ metadata:
 			externalServiceAddress,
 			"true"))(cluster)
 		Expect(err).ToNot(HaveOccurred())
+
+		pods, err := k8s.ListPodsE(
+			cluster.GetTesting(),
+			cluster.GetKubectlOptions(TestNamespace),
+			metav1.ListOptions{
+				LabelSelector: fmt.Sprintf("app=%s", "demo-client"),
+			},
+		)
+		Expect(err).ToNot(HaveOccurred())
+		Expect(pods).To(HaveLen(1))
+
+		clientPod = &pods[0]
 	})
 
 	AfterEach(func() {
@@ -165,22 +183,10 @@ metadata:
 		cluster.(*K8sCluster).WaitNamespaceDelete(TestNamespace)
 	})
 
-	It("Should route to external-service", func() {
+	It("should route to external-service", func() {
 		err := YamlK8s(fmt.Sprintf(trafficRoute, es1))(cluster)
 		Expect(err).ToNot(HaveOccurred())
 
-		pods, err := k8s.ListPodsE(
-			cluster.GetTesting(),
-			cluster.GetKubectlOptions(TestNamespace),
-			metav1.ListOptions{
-				LabelSelector: fmt.Sprintf("app=%s", "demo-client"),
-			},
-		)
-		Expect(err).ToNot(HaveOccurred())
-		Expect(pods).To(HaveLen(1))
-
-		clientPod := pods[0]
-
 		stdout, stderr, err := cluster.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
 			"curl", "-v", "-m", "3", "--fail", "http://external-service.mesh")
 		Expect(err).ToNot(HaveOccurred())
@@ -188,22 +194,10 @@ metadata:
 		Expect(stdout).ToNot(ContainSubstring("HTTPS"))
 	})
 
-	It("Should route to external-service over tls", func() {
+	It("should route to external-service over tls", func() {
 		err := YamlK8s(fmt.Sprintf(trafficRoute, es2))(cluster)
 		Expect(err).ToNot(HaveOccurred())
 
-		pods, err := k8s.ListPodsE(
-			cluster.GetTesting(),
-			cluster.GetKubectlOptions(TestNamespace),
-			metav1.ListOptions{
-				LabelSelector: fmt.Sprintf("app=%s", "demo-client"),
-			},
-		)
-		Expect(err).ToNot(HaveOccurred())
-		Expect(pods).To(HaveLen(1))
-
-		clientPod := pods[0]
-
 		stdout, stderr, err := cluster.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
 			"curl", "-v", "-m", "3", "--fail", "http://external-service.mesh")
 		Expect(err).ToNot(HaveOccurred())
@@ -211,4 +205,32 @@ metadata:
 		Expect(stdout).To(ContainSubstring("HTTPS"))
 	})
 
+	It("should disable passthrough", func() {
+		// given Mesh with passthrough enabled
+		err := YamlK8s(fmt.Sprintf(meshDefaulMtlsOn, "true"))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		// then communication outside of the Mesh works
+		_, stderr, err := cluster.ExecWithRetries(TestNamespace, clientPod.GetName(), "demo-client",
+			"curl", "-v", "-m", "3", "--fail", "http://externalservice-http-server.externalservice-namespace")
+		Expect(err).ToNot(HaveOccurred())
+		Expect(stderr).To(ContainSubstring("HTTP/1.1 200 OK"))
+
+		// when passthrough is disabled on the Mesh
+		err = YamlK8s(fmt.Sprintf(meshDefaulMtlsOn, "false"))(cluster)
+		Expect(err).ToNot(HaveOccurred())
+
+		// then accessing the external service is no longer possible
+		_, err = retry.DoWithRetryE(cluster.GetTesting(), "passthrough access to service", 5, DefaultTimeout, func() (string, error) {
+			_, _, err := cluster.Exec("", "", "demo-client",
+				"curl", "-v", "-m", "3", "--fail", "http://externalservice-http-server.externalservice-namespace")
+			if err != nil {
+				return "", err
+			}
+
+			return "", nil
+		})
+		Expect(err).To(HaveOccurred())
+	})
+
 })
diff --git a/test/e2e/externalservices_universal_test.go b/test/e2e/externalservices_universal_test.go
index 367e478d4..553f40934 100644
--- a/test/e2e/externalservices_universal_test.go
+++ b/test/e2e/externalservices_universal_test.go
@@ -129,7 +129,7 @@ networking:
 		Expect(err).ToNot(HaveOccurred())
 	})
 
-	It("Should route to external-service", func() {
+	It("should route to external-service", func() {
 		err := YamlUniversal(fmt.Sprintf(trafficRoute, es1))(cluster)
 		Expect(err).ToNot(HaveOccurred())
 
@@ -140,7 +140,7 @@ networking:
 		Expect(stdout).ToNot(ContainSubstring("HTTPS"))
 	})
 
-	It("Should route to external-service over tls", func() {
+	It("should route to external-service over tls", func() {
 		err := YamlUniversal(fmt.Sprintf(trafficRoute, es2))(cluster)
 		Expect(err).ToNot(HaveOccurred())