diff --git a/.chloggen/inst-tls.yaml b/.chloggen/inst-tls.yaml new file mode 100755 index 0000000000..368bb318fe --- /dev/null +++ b/.chloggen/inst-tls.yaml @@ -0,0 +1,34 @@ +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. collector, target allocator, auto-instrumentation, opamp, github action) +component: auto-instrumentation + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add support for specifying exporter TLS certificates in auto-instrumentation. + +# One or more tracking issues related to the change +issues: [3338] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: | + Now Instrumentation CR supports specifying TLS certificates for exporter: + ```yaml + spec: + exporter: + endpoint: https://otel-collector:4317 + tls: + secretName: otel-tls-certs + configMapName: otel-ca-bundle + # otel-ca-bundle + ca: ca.crt + # present in otel-tls-certs + cert: tls.crt + # present in otel-tls-certs + key: tls.key + ``` + + * Propagating secrets across namespaces can be done with https://github.com/EmberStack/kubernetes-reflector or https://github.com/zakkg3/ClusterSecret + * Restarting workloads on certificate renewal can be done with https://github.com/stakater/Reloader or https://github.com/wave-k8s/wave diff --git a/apis/v1alpha1/instrumentation_types.go b/apis/v1alpha1/instrumentation_types.go index 2cccef7d6b..c76ab49d8b 100644 --- a/apis/v1alpha1/instrumentation_types.go +++ b/apis/v1alpha1/instrumentation_types.go @@ -97,8 +97,37 @@ type Resource struct { // Exporter defines OTLP exporter configuration. type Exporter struct { // Endpoint is address of the collector with OTLP endpoint. + // If the endpoint defines https:// scheme TLS has to be specified. // +optional Endpoint string `json:"endpoint,omitempty"` + + // TLS defines certificates for TLS. + // TLS needs to be enabled by specifying https:// scheme in the Endpoint. + TLS *TLS `json:"tls,omitempty"` +} + +// TLS defines TLS configuration for exporter. +type TLS struct { + // SecretName defines secret name that will be used to configure TLS on the exporter. + // It is user responsibility to create the secret in the namespace of the workload. + // The secret must contain client certificate (Cert) and private key (Key). + // The CA certificate might be defined in the secret or in the config map. + SecretName string `json:"secretName,omitempty"` + + // ConfigMapName defines configmap name with CA certificate. If it is not defined CA certificate will be + // used from the secret defined in SecretName. + ConfigMapName string `json:"configMapName,omitempty"` + + // CA defines the key of certificate (e.g. ca.crt) in the configmap map, secret or absolute path to a certificate. + // The absolute path can be used when certificate is already present on the workload filesystem e.g. + // /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt + CA string `json:"ca,omitempty"` + // Cert defines the key (e.g. tls.crt) of the client certificate in the secret or absolute path to a certificate. + // The absolute path can be used when certificate is already present on the workload filesystem. + Cert string `json:"cert,omitempty"` + // Key defines a key (e.g. tls.key) of the private key in the secret or absolute path to a certificate. + // The absolute path can be used when certificate is already present on the workload filesystem. + Key string `json:"key,omitempty"` } // Sampler defines sampling configuration. diff --git a/apis/v1alpha1/instrumentation_webhook.go b/apis/v1alpha1/instrumentation_webhook.go index 004992f795..3d896b0a10 100644 --- a/apis/v1alpha1/instrumentation_webhook.go +++ b/apis/v1alpha1/instrumentation_webhook.go @@ -236,9 +236,31 @@ func (w InstrumentationWebhook) validate(r *Instrumentation) (admission.Warnings default: return warnings, fmt.Errorf("spec.sampler.type is not valid: %s", r.Spec.Sampler.Type) } + + warnings = append(warnings, validateExporter(r.Spec.Exporter)...) + return warnings, nil } +func validateExporter(exporter Exporter) []string { + var warnings []string + if exporter.TLS != nil { + tls := exporter.TLS + if tls.Key != "" && tls.Cert == "" || tls.Cert != "" && tls.Key == "" { + warnings = append(warnings, "both exporter.tls.key and exporter.tls.cert mut be set") + } + + if !strings.HasPrefix(exporter.Endpoint, "https://") { + warnings = append(warnings, "exporter.tls is configured but exporter.endpoint is not enabling TLS with https://") + } + } + if strings.HasPrefix(exporter.Endpoint, "https://") && exporter.TLS == nil { + warnings = append(warnings, "exporter is using https:// but exporter.tls is unset") + } + + return warnings +} + func validateJaegerRemoteSamplerArgument(argument string) error { parts := strings.Split(argument, ",") diff --git a/apis/v1alpha1/instrumentation_webhook_test.go b/apis/v1alpha1/instrumentation_webhook_test.go index 81049cbc0c..9c6c1ae5c3 100644 --- a/apis/v1alpha1/instrumentation_webhook_test.go +++ b/apis/v1alpha1/instrumentation_webhook_test.go @@ -113,6 +113,94 @@ func TestInstrumentationValidatingWebhook(t *testing.T) { }, }, }, + { + name: "exporter: tls cert set but missing key", + inst: Instrumentation{ + Spec: InstrumentationSpec{ + Sampler: Sampler{ + Type: ParentBasedTraceIDRatio, + Argument: "0.99", + }, + Exporter: Exporter{ + Endpoint: "https://collector:4317", + TLS: &TLS{ + Cert: "cert", + }, + }, + }, + }, + warnings: []string{"both exporter.tls.key and exporter.tls.cert mut be set"}, + }, + { + name: "exporter: tls key set but missing cert", + inst: Instrumentation{ + Spec: InstrumentationSpec{ + Sampler: Sampler{ + Type: ParentBasedTraceIDRatio, + Argument: "0.99", + }, + Exporter: Exporter{ + Endpoint: "https://collector:4317", + TLS: &TLS{ + Key: "key", + }, + }, + }, + }, + warnings: []string{"both exporter.tls.key and exporter.tls.cert mut be set"}, + }, + { + name: "exporter: tls set but using http://", + inst: Instrumentation{ + Spec: InstrumentationSpec{ + Sampler: Sampler{ + Type: ParentBasedTraceIDRatio, + Argument: "0.99", + }, + Exporter: Exporter{ + Endpoint: "http://collector:4317", + TLS: &TLS{ + Key: "key", + Cert: "cert", + }, + }, + }, + }, + warnings: []string{"exporter.tls is configured but exporter.endpoint is not enabling TLS with https://"}, + }, + { + name: "exporter: exporter using http://, but the tls is nil", + inst: Instrumentation{ + Spec: InstrumentationSpec{ + Sampler: Sampler{ + Type: ParentBasedTraceIDRatio, + Argument: "0.99", + }, + Exporter: Exporter{ + Endpoint: "https://collector:4317", + }, + }, + }, + warnings: []string{"exporter is using https:// but exporter.tls is unset"}, + }, + { + name: "exporter no warning set", + inst: Instrumentation{ + Spec: InstrumentationSpec{ + Sampler: Sampler{ + Type: ParentBasedTraceIDRatio, + Argument: "0.99", + }, + Exporter: Exporter{ + Endpoint: "https://collector:4317", + TLS: &TLS{ + Key: "key", + Cert: "cert", + }, + }, + }, + }, + }, } for _, test := range tests { diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index 270c617e17..5bf6ffaf0a 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -171,6 +171,11 @@ func (in *DotNet) DeepCopy() *DotNet { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Exporter) DeepCopyInto(out *Exporter) { *out = *in + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = new(TLS) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Exporter. @@ -323,7 +328,7 @@ func (in *InstrumentationList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *InstrumentationSpec) DeepCopyInto(out *InstrumentationSpec) { *out = *in - out.Exporter = in.Exporter + in.Exporter.DeepCopyInto(&out.Exporter) in.Resource.DeepCopyInto(&out.Resource) if in.Propagators != nil { in, out := &in.Propagators, &out.Propagators @@ -1272,6 +1277,21 @@ func (in *ScaleSubresourceStatus) DeepCopy() *ScaleSubresourceStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TLS) DeepCopyInto(out *TLS) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLS. +func (in *TLS) DeepCopy() *TLS { + if in == nil { + return nil + } + out := new(TLS) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TargetAllocator) DeepCopyInto(out *TargetAllocator) { *out = *in diff --git a/bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml b/bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml index 0811d9a12b..e5b5bafdfc 100644 --- a/bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml +++ b/bundle/community/manifests/opentelemetry-operator.clusterserviceversion.yaml @@ -99,7 +99,7 @@ metadata: categories: Logging & Tracing,Monitoring certified: "false" containerImage: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator - createdAt: "2024-10-08T09:52:53Z" + createdAt: "2024-10-10T15:31:51Z" description: Provides the OpenTelemetry components, including the Collector operators.operatorframework.io/builder: operator-sdk-v1.29.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 @@ -284,7 +284,9 @@ spec: - "" resources: - namespaces + - secrets verbs: + - get - list - watch - apiGroups: diff --git a/bundle/community/manifests/opentelemetry.io_instrumentations.yaml b/bundle/community/manifests/opentelemetry.io_instrumentations.yaml index 76f050bf0d..4ff96eca41 100644 --- a/bundle/community/manifests/opentelemetry.io_instrumentations.yaml +++ b/bundle/community/manifests/opentelemetry.io_instrumentations.yaml @@ -409,6 +409,19 @@ spec: properties: endpoint: type: string + tls: + properties: + ca: + type: string + cert: + type: string + configMapName: + type: string + key: + type: string + secretName: + type: string + type: object type: object go: properties: diff --git a/bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml b/bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml index 24958408c7..bcab54329b 100644 --- a/bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml +++ b/bundle/openshift/manifests/opentelemetry-operator.clusterserviceversion.yaml @@ -99,7 +99,7 @@ metadata: categories: Logging & Tracing,Monitoring certified: "false" containerImage: ghcr.io/open-telemetry/opentelemetry-operator/opentelemetry-operator - createdAt: "2024-10-08T09:52:57Z" + createdAt: "2024-10-10T15:31:51Z" description: Provides the OpenTelemetry components, including the Collector operators.operatorframework.io/builder: operator-sdk-v1.29.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v3 @@ -284,7 +284,9 @@ spec: - "" resources: - namespaces + - secrets verbs: + - get - list - watch - apiGroups: diff --git a/bundle/openshift/manifests/opentelemetry.io_instrumentations.yaml b/bundle/openshift/manifests/opentelemetry.io_instrumentations.yaml index 76f050bf0d..4ff96eca41 100644 --- a/bundle/openshift/manifests/opentelemetry.io_instrumentations.yaml +++ b/bundle/openshift/manifests/opentelemetry.io_instrumentations.yaml @@ -409,6 +409,19 @@ spec: properties: endpoint: type: string + tls: + properties: + ca: + type: string + cert: + type: string + configMapName: + type: string + key: + type: string + secretName: + type: string + type: object type: object go: properties: diff --git a/config/crd/bases/opentelemetry.io_instrumentations.yaml b/config/crd/bases/opentelemetry.io_instrumentations.yaml index 19582f62c6..3065e245a1 100644 --- a/config/crd/bases/opentelemetry.io_instrumentations.yaml +++ b/config/crd/bases/opentelemetry.io_instrumentations.yaml @@ -407,6 +407,19 @@ spec: properties: endpoint: type: string + tls: + properties: + ca: + type: string + cert: + type: string + configMapName: + type: string + key: + type: string + secretName: + type: string + type: object type: object go: properties: diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 73632f89c8..0991eb4d38 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -30,7 +30,9 @@ rules: - "" resources: - namespaces + - secrets verbs: + - get - list - watch - apiGroups: diff --git a/docs/api.md b/docs/api.md index 24d16da3f4..71b3b7071a 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1625,7 +1625,80 @@ Exporter defines exporter configuration. endpoint string - Endpoint is address of the collector with OTLP endpoint.
+ Endpoint is address of the collector with OTLP endpoint. +If the endpoint defines https:// scheme TLS has to be specified.
+ + false + + tls + object + + TLS defines certificates for TLS. +TLS needs to be enabled by specifying https:// scheme in the Endpoint.
+ + false + + + + +### Instrumentation.spec.exporter.tls +[↩ Parent](#instrumentationspecexporter) + + + +TLS defines certificates for TLS. +TLS needs to be enabled by specifying https:// scheme in the Endpoint. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/internal/webhook/podmutation/webhookhandler.go b/internal/webhook/podmutation/webhookhandler.go index b4ad5fa7fc..a0704c5ad9 100644 --- a/internal/webhook/podmutation/webhookhandler.go +++ b/internal/webhook/podmutation/webhookhandler.go @@ -30,7 +30,7 @@ import ( ) // +kubebuilder:webhook:path=/mutate-v1-pod,mutating=true,failurePolicy=ignore,groups="",resources=pods,verbs=create,versions=v1,name=mpod.kb.io,sideEffects=none,admissionReviewVersions=v1 -// +kubebuilder:rbac:groups="",resources=namespaces,verbs=list;watch +// +kubebuilder:rbac:groups="",resources=namespaces;secrets,verbs=get;list;watch // +kubebuilder:rbac:groups=opentelemetry.io,resources=opentelemetrycollectors,verbs=get;list;watch // +kubebuilder:rbac:groups=opentelemetry.io,resources=instrumentations,verbs=get;list;watch // +kubebuilder:rbac:groups="apps",resources=replicasets,verbs=get;list;watch diff --git a/pkg/constants/env.go b/pkg/constants/env.go index 8dd4f65200..8bfcd667f4 100644 --- a/pkg/constants/env.go +++ b/pkg/constants/env.go @@ -15,12 +15,16 @@ package constants const ( - EnvOTELServiceName = "OTEL_SERVICE_NAME" - EnvOTELExporterOTLPEndpoint = "OTEL_EXPORTER_OTLP_ENDPOINT" - EnvOTELResourceAttrs = "OTEL_RESOURCE_ATTRIBUTES" - EnvOTELPropagators = "OTEL_PROPAGATORS" - EnvOTELTracesSampler = "OTEL_TRACES_SAMPLER" - EnvOTELTracesSamplerArg = "OTEL_TRACES_SAMPLER_ARG" + EnvOTELServiceName = "OTEL_SERVICE_NAME" + EnvOTELResourceAttrs = "OTEL_RESOURCE_ATTRIBUTES" + EnvOTELPropagators = "OTEL_PROPAGATORS" + EnvOTELTracesSampler = "OTEL_TRACES_SAMPLER" + EnvOTELTracesSamplerArg = "OTEL_TRACES_SAMPLER_ARG" + + EnvOTELExporterOTLPEndpoint = "OTEL_EXPORTER_OTLP_ENDPOINT" + EnvOTELExporterCertificate = "OTEL_EXPORTER_OTLP_CERTIFICATE" + EnvOTELExporterClientCertificate = "OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE" + EnvOTELExporterClientKey = "OTEL_EXPORTER_OTLP_CLIENT_KEY" InstrumentationPrefix = "instrumentation.opentelemetry.io/" AnnotationDefaultAutoInstrumentationJava = InstrumentationPrefix + "default-auto-instrumentation-java-image" diff --git a/pkg/instrumentation/exporter.go b/pkg/instrumentation/exporter.go new file mode 100644 index 0000000000..5598de24cf --- /dev/null +++ b/pkg/instrumentation/exporter.go @@ -0,0 +1,150 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package instrumentation + +import ( + "fmt" + "path/filepath" + + corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" + "github.com/open-telemetry/opentelemetry-operator/internal/naming" + "github.com/open-telemetry/opentelemetry-operator/pkg/constants" +) + +func configureExporter(exporter v1alpha1.Exporter, pod *corev1.Pod, container *corev1.Container) { + if exporter.Endpoint != "" { + if getIndexOfEnv(container.Env, constants.EnvOTELExporterOTLPEndpoint) == -1 { + container.Env = append(container.Env, corev1.EnvVar{ + Name: constants.EnvOTELExporterOTLPEndpoint, + Value: exporter.Endpoint, + }) + } + } + if exporter.TLS == nil { + return + } + // the name cannot be longer than 63 characters + secretVolumeName := naming.Truncate("otel-auto-secret-%s", 63, exporter.TLS.SecretName) + secretMountPath := fmt.Sprintf("/otel-auto-instrumentation-secret-%s", exporter.TLS.SecretName) + configMapVolumeName := naming.Truncate("otel-auto-configmap-%s", 63, exporter.TLS.ConfigMapName) + configMapMountPath := fmt.Sprintf("/otel-auto-instrumentation-configmap-%s", exporter.TLS.ConfigMapName) + + if exporter.TLS.CA != "" { + mountPath := secretMountPath + if exporter.TLS.ConfigMapName != "" { + mountPath = configMapMountPath + } + envVarVal := fmt.Sprintf("%s/%s", mountPath, exporter.TLS.CA) + if filepath.IsAbs(exporter.TLS.CA) { + envVarVal = exporter.TLS.CA + } + if getIndexOfEnv(container.Env, constants.EnvOTELExporterCertificate) == -1 { + container.Env = append(container.Env, corev1.EnvVar{ + Name: constants.EnvOTELExporterCertificate, + Value: envVarVal, + }) + } + } + if exporter.TLS.Cert != "" { + envVarVal := fmt.Sprintf("%s/%s", secretMountPath, exporter.TLS.Cert) + if filepath.IsAbs(exporter.TLS.Cert) { + envVarVal = exporter.TLS.Cert + } + if getIndexOfEnv(container.Env, constants.EnvOTELExporterClientCertificate) == -1 { + container.Env = append(container.Env, corev1.EnvVar{ + Name: constants.EnvOTELExporterClientCertificate, + Value: envVarVal, + }) + } + } + if exporter.TLS.Key != "" { + envVarVar := fmt.Sprintf("%s/%s", secretMountPath, exporter.TLS.Key) + if filepath.IsAbs(exporter.TLS.Key) { + envVarVar = exporter.TLS.Key + } + if getIndexOfEnv(container.Env, constants.EnvOTELExporterClientKey) == -1 { + container.Env = append(container.Env, corev1.EnvVar{ + Name: constants.EnvOTELExporterClientKey, + Value: envVarVar, + }) + } + } + + if exporter.TLS.SecretName != "" { + addVolume := true + for _, vol := range pod.Spec.Volumes { + if vol.Name == secretVolumeName { + addVolume = false + } + } + if addVolume { + pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{ + Name: secretVolumeName, + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: exporter.TLS.SecretName, + }, + }}) + } + addVolumeMount := true + for _, vol := range container.VolumeMounts { + if vol.Name == secretVolumeName { + addVolumeMount = false + } + } + if addVolumeMount { + container.VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{ + Name: secretVolumeName, + MountPath: secretMountPath, + ReadOnly: true, + }) + } + } + + if exporter.TLS.ConfigMapName != "" { + addVolume := true + for _, vol := range pod.Spec.Volumes { + if vol.Name == configMapVolumeName { + addVolume = false + } + } + if addVolume { + pod.Spec.Volumes = append(pod.Spec.Volumes, corev1.Volume{ + Name: configMapVolumeName, + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: exporter.TLS.ConfigMapName, + }, + }, + }}) + } + addVolumeMount := true + for _, vol := range container.VolumeMounts { + if vol.Name == configMapVolumeName { + addVolumeMount = false + } + } + if addVolumeMount { + container.VolumeMounts = append(container.VolumeMounts, corev1.VolumeMount{ + Name: configMapVolumeName, + MountPath: configMapMountPath, + ReadOnly: true, + }) + } + } +} diff --git a/pkg/instrumentation/exporter_test.go b/pkg/instrumentation/exporter_test.go new file mode 100644 index 0000000000..2fddf1264a --- /dev/null +++ b/pkg/instrumentation/exporter_test.go @@ -0,0 +1,209 @@ +// Copyright The OpenTelemetry Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package instrumentation + +import ( + "testing" + + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + + "github.com/open-telemetry/opentelemetry-operator/apis/v1alpha1" +) + +func TestExporter(t *testing.T) { + tests := []struct { + name string + exporter v1alpha1.Exporter + expected corev1.Pod + }{ + { + name: "ca, crt and key from secret", + exporter: v1alpha1.Exporter{ + Endpoint: "https://collector:4318", + TLS: &v1alpha1.TLS{ + SecretName: "my-certs", + CA: "ca.crt", + Cert: "cert.crt", + Key: "key.key", + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: "otel-auto-secret-my-certs", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "my-certs", + }, + }, + }, + }, + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + Name: "otel-auto-secret-my-certs", + ReadOnly: true, + MountPath: "/otel-auto-instrumentation-secret-my-certs", + }, + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "https://collector:4318", + }, + { + Name: "OTEL_EXPORTER_OTLP_CERTIFICATE", + Value: "/otel-auto-instrumentation-secret-my-certs/ca.crt", + }, + { + Name: "OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE", + Value: "/otel-auto-instrumentation-secret-my-certs/cert.crt", + }, + { + Name: "OTEL_EXPORTER_OTLP_CLIENT_KEY", + Value: "/otel-auto-instrumentation-secret-my-certs/key.key", + }, + }, + }, + }, + }, + }, + }, + { + name: "crt and key from secret and ca from configmap", + exporter: v1alpha1.Exporter{ + Endpoint: "https://collector:4318", + TLS: &v1alpha1.TLS{ + SecretName: "my-certs", + ConfigMapName: "ca-bundle", + CA: "ca.crt", + Cert: "cert.crt", + Key: "key.key", + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: "otel-auto-secret-my-certs", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "my-certs", + }, + }, + }, + { + Name: "otel-auto-configmap-ca-bundle", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "ca-bundle", + }, + }, + }, + }, + }, + Containers: []corev1.Container{ + { + VolumeMounts: []corev1.VolumeMount{ + { + Name: "otel-auto-secret-my-certs", + ReadOnly: true, + MountPath: "/otel-auto-instrumentation-secret-my-certs", + }, + { + Name: "otel-auto-configmap-ca-bundle", + ReadOnly: true, + MountPath: "/otel-auto-instrumentation-configmap-ca-bundle", + }, + }, + Env: []corev1.EnvVar{ + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "https://collector:4318", + }, + { + Name: "OTEL_EXPORTER_OTLP_CERTIFICATE", + Value: "/otel-auto-instrumentation-configmap-ca-bundle/ca.crt", + }, + { + Name: "OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE", + Value: "/otel-auto-instrumentation-secret-my-certs/cert.crt", + }, + { + Name: "OTEL_EXPORTER_OTLP_CLIENT_KEY", + Value: "/otel-auto-instrumentation-secret-my-certs/key.key", + }, + }, + }, + }, + }, + }, + }, + { + name: "ca, crt key absolute paths", + exporter: v1alpha1.Exporter{ + Endpoint: "https://collector:4318", + TLS: &v1alpha1.TLS{ + CA: "/ca.crt", + Cert: "/cert.crt", + Key: "/key.key", + }, + }, + expected: corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Env: []corev1.EnvVar{ + { + Name: "OTEL_EXPORTER_OTLP_ENDPOINT", + Value: "https://collector:4318", + }, + { + Name: "OTEL_EXPORTER_OTLP_CERTIFICATE", + Value: "/ca.crt", + }, + { + Name: "OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE", + Value: "/cert.crt", + }, + { + Name: "OTEL_EXPORTER_OTLP_CLIENT_KEY", + Value: "/key.key", + }, + }, + }, + }, + }, + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pod := corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {}, + }, + }, + } + configureExporter(test.exporter, &pod, &pod.Spec.Containers[0]) + assert.Equal(t, test.expected, pod) + }) + } +} diff --git a/pkg/instrumentation/podmutator.go b/pkg/instrumentation/podmutator.go index b1a2356d04..3c3a2f8e52 100644 --- a/pkg/instrumentation/podmutator.go +++ b/pkg/instrumentation/podmutator.go @@ -22,6 +22,7 @@ import ( "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/record" @@ -395,6 +396,11 @@ func (pm *instPodMutator) Mutate(ctx context.Context, ns corev1.Namespace, pod c return pod, err } + if err = pm.validateInstrumentations(ctx, insts, ns.Name); err != nil { + logger.Error(err, "failed to validate instrumentations") + return pod, err + } + // We retrieve the annotation for podname if pm.config.EnableMultiInstrumentation() { err = insts.setLanguageSpecificContainers(ns.ObjectMeta, pod.ObjectMeta) @@ -460,3 +466,55 @@ func (pm *instPodMutator) selectInstrumentationInstanceFromNamespace(ctx context return &otelInsts.Items[0], nil } } + +func (pm *instPodMutator) validateInstrumentations(ctx context.Context, inst languageInstrumentations, podNamespace string) error { + instrumentations := []struct { + instrumentation *v1alpha1.Instrumentation + }{ + {inst.Java.Instrumentation}, + {inst.Python.Instrumentation}, + {inst.NodeJS.Instrumentation}, + {inst.DotNet.Instrumentation}, + {inst.Go.Instrumentation}, + {inst.ApacheHttpd.Instrumentation}, + {inst.Nginx.Instrumentation}, + {inst.Sdk.Instrumentation}, + } + var errs []error + for _, i := range instrumentations { + if i.instrumentation != nil { + if err := pm.validateInstrumentation(ctx, i.instrumentation, podNamespace); err != nil { + errs = append(errs, err) + } + } + } + + if len(errs) > 0 { + return errors.Join(errs...) + } + return nil +} + +func (pm *instPodMutator) validateInstrumentation(ctx context.Context, inst *v1alpha1.Instrumentation, podNamespace string) error { + // Check if secret and configmap exists + // If they don't exist pod cannot start + var errs []error + if inst.Spec.Exporter.TLS != nil { + if inst.Spec.Exporter.TLS.SecretName != "" { + nsn := types.NamespacedName{Name: inst.Spec.Exporter.TLS.SecretName, Namespace: podNamespace} + if err := pm.Client.Get(ctx, nsn, &corev1.Secret{}); apierrors.IsNotFound(err) { + errs = append(errs, fmt.Errorf("secret %s with certificates does not exists: %w", nsn.String(), err)) + } + } + if inst.Spec.Exporter.TLS.ConfigMapName != "" { + nsn := types.NamespacedName{Name: inst.Spec.Exporter.TLS.ConfigMapName, Namespace: podNamespace} + if err := pm.Client.Get(ctx, nsn, &corev1.ConfigMap{}); apierrors.IsNotFound(err) { + errs = append(errs, fmt.Errorf("configmap %s with CA certificate does not exists: %w", nsn.String(), err)) + } + } + } + if len(errs) > 0 { + return errors.Join(errs...) + } + return nil +} diff --git a/pkg/instrumentation/podmutator_test.go b/pkg/instrumentation/podmutator_test.go index 2eddd045f3..400d44c22b 100644 --- a/pkg/instrumentation/podmutator_test.go +++ b/pkg/instrumentation/podmutator_test.go @@ -42,6 +42,8 @@ func TestMutatePod(t *testing.T) { expected corev1.Pod inst v1alpha1.Instrumentation ns corev1.Namespace + secret *corev1.Secret + configMap *corev1.ConfigMap setFeatureGates func(t *testing.T) config config.Config }{ @@ -52,6 +54,18 @@ func TestMutatePod(t *testing.T) { Name: "javaagent", }, }, + secret: &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-certs", + Namespace: "javaagent", + }, + }, + configMap: &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-ca-bundle", + Namespace: "javaagent", + }, + }, inst: v1alpha1.Instrumentation{ ObjectMeta: metav1.ObjectMeta{ Name: "example-inst", @@ -103,6 +117,13 @@ func TestMutatePod(t *testing.T) { }, Exporter: v1alpha1.Exporter{ Endpoint: "http://collector:12345", + TLS: &v1alpha1.TLS{ + SecretName: "my-certs", + ConfigMapName: "my-ca-bundle", + CA: "ca.crt", + Cert: "cert.crt", + Key: "key.key", + }, }, }, }, @@ -136,6 +157,24 @@ func TestMutatePod(t *testing.T) { }, }, }, + { + Name: "otel-auto-secret-my-certs", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "my-certs", + }, + }, + }, + { + Name: "otel-auto-configmap-my-ca-bundle", + VolumeSource: corev1.VolumeSource{ + ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "my-ca-bundle", + }, + }, + }, + }, }, InitContainers: []corev1.Container{ { @@ -212,6 +251,18 @@ func TestMutatePod(t *testing.T) { Name: "OTEL_SERVICE_NAME", Value: "app", }, + { + Name: "OTEL_EXPORTER_OTLP_CERTIFICATE", + Value: "/otel-auto-instrumentation-configmap-my-ca-bundle/ca.crt", + }, + { + Name: "OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE", + Value: "/otel-auto-instrumentation-secret-my-certs/cert.crt", + }, + { + Name: "OTEL_EXPORTER_OTLP_CLIENT_KEY", + Value: "/otel-auto-instrumentation-secret-my-certs/key.key", + }, { Name: "OTEL_RESOURCE_ATTRIBUTES_POD_NAME", ValueFrom: &corev1.EnvVarSource{ @@ -238,6 +289,16 @@ func TestMutatePod(t *testing.T) { Name: javaVolumeName, MountPath: javaInstrMountPath, }, + { + Name: "otel-auto-secret-my-certs", + ReadOnly: true, + MountPath: "/otel-auto-instrumentation-secret-my-certs", + }, + { + Name: "otel-auto-configmap-my-ca-bundle", + ReadOnly: true, + MountPath: "/otel-auto-instrumentation-configmap-my-ca-bundle", + }, }, }, }, @@ -4785,6 +4846,49 @@ func TestMutatePod(t *testing.T) { config.WithEnableNodeJSInstrumentation(false), ), }, + { + name: "secret and configmap does not exists", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "error-missing-secrets", + }, + }, + inst: v1alpha1.Instrumentation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "example-inst", + Namespace: "error-missing-secrets", + }, + Spec: v1alpha1.InstrumentationSpec{ + Exporter: v1alpha1.Exporter{ + Endpoint: "http://collector:12345", + TLS: &v1alpha1.TLS{ + SecretName: "my-certs", + ConfigMapName: "my-ca-bundle", + CA: "ca.crt", + Cert: "cert.crt", + Key: "key.key", + }, + }, + }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + annotationInjectJava: "true", + }, + Namespace: "error-missing-secrets", + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "app", + }, + }, + }, + }, + config: config.New(), + err: "secret error-missing-secrets/my-certs with certificates does not exists: secrets \"my-certs\" not found\nconfigmap error-missing-secrets/my-ca-bundle with CA certificate does not exists: configmaps \"my-ca-bundle\" not found", + }, } for _, test := range tests { @@ -4801,6 +4905,21 @@ func TestMutatePod(t *testing.T) { defer func() { _ = k8sClient.Delete(context.Background(), &test.ns) }() + if test.secret != nil { + err = k8sClient.Create(context.Background(), test.secret) + require.NoError(t, err) + defer func() { + _ = k8sClient.Delete(context.Background(), test.secret) + }() + } + if test.configMap != nil { + err = k8sClient.Create(context.Background(), test.configMap) + require.NoError(t, err) + defer func() { + _ = k8sClient.Delete(context.Background(), test.configMap) + }() + } + err = k8sClient.Create(context.Background(), &test.inst) require.NoError(t, err) diff --git a/pkg/instrumentation/sdk.go b/pkg/instrumentation/sdk.go index a9f7d9bbfd..0033f70566 100644 --- a/pkg/instrumentation/sdk.go +++ b/pkg/instrumentation/sdk.go @@ -304,15 +304,7 @@ func (i *sdkInjector) injectCommonSDKConfig(ctx context.Context, otelinst v1alph Value: chooseServiceName(pod, useLabelsForResourceAttributes, resourceMap, appIndex), }) } - if otelinst.Spec.Exporter.Endpoint != "" { - idx = getIndexOfEnv(container.Env, constants.EnvOTELExporterOTLPEndpoint) - if idx == -1 { - container.Env = append(container.Env, corev1.EnvVar{ - Name: constants.EnvOTELExporterOTLPEndpoint, - Value: otelinst.Spec.Endpoint, - }) - } - } + configureExporter(otelinst.Spec.Exporter, &pod, container) // Always retrieve the pod name from the Downward API. Ensure that the OTEL_RESOURCE_ATTRIBUTES_POD_NAME env exists. container.Env = append(container.Env, corev1.EnvVar{ diff --git a/tests/e2e-instrumentation/instrumentation-java-tls/.gitignore b/tests/e2e-instrumentation/instrumentation-java-tls/.gitignore new file mode 100644 index 0000000000..b8987f0ba0 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-java-tls/.gitignore @@ -0,0 +1,2 @@ +*.crt +*.key \ No newline at end of file diff --git a/tests/e2e-instrumentation/instrumentation-java-tls/00-install-collector.yaml b/tests/e2e-instrumentation/instrumentation-java-tls/00-install-collector.yaml new file mode 100644 index 0000000000..536c392481 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-java-tls/00-install-collector.yaml @@ -0,0 +1,43 @@ +apiVersion: opentelemetry.io/v1beta1 +kind: OpenTelemetryCollector +metadata: + name: simplest +spec: + volumeMounts: + - name: certs + mountPath: /certs + - name: certs-ca + mountPath: /certs-ca + volumes: + - name: certs + secret: + secretName: server-certs + - name: certs-ca + configMap: + name: ca + config: + receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + tls: + cert_file: /certs/tls.crt + key_file: /certs/tls.key + client_ca_file: /certs-ca/ca.crt + http: + endpoint: 0.0.0.0:4318 + tls: + cert_file: /certs/tls.crt + key_file: /certs/tls.key + client_ca_file: /certs-ca/ca.crt + processors: + + exporters: + debug: {} + + service: + pipelines: + traces: + receivers: [otlp] + exporters: [debug] diff --git a/tests/e2e-instrumentation/instrumentation-java-tls/00-install-instrumentation.yaml b/tests/e2e-instrumentation/instrumentation-java-tls/00-install-instrumentation.yaml new file mode 100644 index 0000000000..222a0584a8 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-java-tls/00-install-instrumentation.yaml @@ -0,0 +1,19 @@ +apiVersion: opentelemetry.io/v1alpha1 +kind: Instrumentation +metadata: + name: java +spec: + exporter: + endpoint: https://simplest-collector:4317 + tls: + secretName: client-certs + configMapName: ca + ca: ca.crt + cert: tls.crt + key: tls.key + propagators: + - tracecontext + - baggage + sampler: + type: parentbased_traceidratio + argument: "1" diff --git a/tests/e2e-instrumentation/instrumentation-java-tls/01-assert.yaml b/tests/e2e-instrumentation/instrumentation-java-tls/01-assert.yaml new file mode 100644 index 0000000000..7ddecadb47 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-java-tls/01-assert.yaml @@ -0,0 +1,70 @@ +apiVersion: v1 +kind: Pod +metadata: + annotations: + instrumentation.opentelemetry.io/inject-java: "true" + labels: + app: my-java +spec: + containers: + - env: + - name: OTEL_NODE_IP + valueFrom: + fieldRef: + fieldPath: status.hostIP + - name: OTEL_POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: JAVA_TOOL_OPTIONS + value: ' -javaagent:/otel-auto-instrumentation-java/javaagent.jar' + - name: OTEL_SERVICE_NAME + value: my-java + - name: OTEL_EXPORTER_OTLP_ENDPOINT + value: https://simplest-collector:4317 + - name: OTEL_EXPORTER_OTLP_CERTIFICATE + value: /otel-auto-instrumentation-configmap-ca/ca.crt + - name: OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE + value: /otel-auto-instrumentation-secret-client-certs/tls.crt + - name: OTEL_EXPORTER_OTLP_CLIENT_KEY + value: /otel-auto-instrumentation-secret-client-certs/tls.key + - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.name + - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: spec.nodeName + - name: OTEL_PROPAGATORS + value: tracecontext,baggage + - name: OTEL_TRACES_SAMPLER + value: parentbased_traceidratio + - name: OTEL_TRACES_SAMPLER_ARG + value: "1" + - name: OTEL_RESOURCE_ATTRIBUTES + name: myapp + volumeMounts: + - mountPath: /var/run/secrets/kubernetes.io/serviceaccount + readOnly: true + - mountPath: /otel-auto-instrumentation-java + name: opentelemetry-auto-instrumentation-java + - mountPath: /otel-auto-instrumentation-secret-client-certs + name: otel-auto-secret-client-certs + readOnly: true + - mountPath: /otel-auto-instrumentation-configmap-ca + name: otel-auto-configmap-ca + readOnly: true + initContainers: + - name: opentelemetry-auto-instrumentation-java +status: + containerStatuses: + - name: myapp + ready: true + started: true + initContainerStatuses: + - name: opentelemetry-auto-instrumentation-java + ready: true + phase: Running diff --git a/tests/e2e-instrumentation/instrumentation-java-tls/01-install-app.yaml b/tests/e2e-instrumentation/instrumentation-java-tls/01-install-app.yaml new file mode 100644 index 0000000000..0d16826e53 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-java-tls/01-install-app.yaml @@ -0,0 +1,27 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-java +spec: + selector: + matchLabels: + app: my-java + replicas: 1 + template: + metadata: + labels: + app: my-java + annotations: + instrumentation.opentelemetry.io/inject-java: "true" + spec: + securityContext: + runAsUser: 1000 + runAsGroup: 3000 + fsGroup: 3000 + containers: + - name: myapp + image: ghcr.io/open-telemetry/opentelemetry-operator/e2e-test-app-java:main + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: ["ALL"] diff --git a/tests/e2e-instrumentation/instrumentation-java-tls/ca.yaml b/tests/e2e-instrumentation/instrumentation-java-tls/ca.yaml new file mode 100644 index 0000000000..c078708fd6 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-java-tls/ca.yaml @@ -0,0 +1,30 @@ +apiVersion: v1 +data: + ca.crt: | + -----BEGIN CERTIFICATE----- + MIID3zCCAsegAwIBAgIUbgTamPDD9mF7SzjykOtjZ6eOJygwDQYJKoZIhvcNAQEL + BQAwfjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM + DU1vdW50YWluIFZpZXcxGjAYBgNVBAoMEVlvdXIgT3JnYW5pemF0aW9uMRIwEAYD + VQQLDAlZb3VyIFVuaXQxEjAQBgNVBAMMCWxvY2FsaG9zdDAgFw0yNDEwMTAxMjQw + MTFaGA8yMDUxMDMxMzEyNDAxMVowfjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNh + bGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxGjAYBgNVBAoMEVlvdXIg + T3JnYW5pemF0aW9uMRIwEAYDVQQLDAlZb3VyIFVuaXQxEjAQBgNVBAMMCWxvY2Fs + aG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMax8x9QrIB924Tn + J+GhOsvEU6DDTbntLS8rXy7ePeCrUgjh+E3ThzvdZFqqx8ffVmrDVd8SF9TabXWC + j4Bytyv1AxBN8+PviXjyDeF5qSYEzh9K9poJCnTPOXZcToEna0Q5Po41fFY/M5QL + 7YHBrlc4rJKd+CJmQ0bjUj1OjG0NBT2Xm0rU1o92+73CMb//ADd8XkqDunHMfILe + wyWDiTbXsgXuh62cdmQyAL98xH0ghSrGYM2KA/F9FvD51B2+CDs2YwET4IsRTAt+ + 9nLJpjrN7o+lofnhGWy88wPwlzJZeMP3oyna2iVlemXXYZeYXv2uRN6DCLUaamXT + sy2sawECAwEAAaNTMFEwHQYDVR0OBBYEFI7foDRaBz788AJJcAo0wC422LDUMB8G + A1UdIwQYMBaAFI7foDRaBz788AJJcAo0wC422LDUMA8GA1UdEwEB/wQFMAMBAf8w + DQYJKoZIhvcNAQELBQADggEBAIyVPNo2vsiRoqeJjaDCUSJFzop4ykdQOsOUMeJT + UqiJvH87unmEm50QgGOwsSxYPZkaPosxjnIFs9lVXixIcETtqbb8DT2AU9muDJ4o + 2p8tYBD/4jTN0I6waEpsubMwz+U4llxyfCG0UK3/6kpFwi8/723i8LwzynwkMiki + gtAPGmo1QwMFW/2w24l/+Uo4dhrq3GpuV2qBwyYc04z88abvAzRy/wIdw0IC4DiO + nNNN1SsjAeN+wp1dm0ohDm4z5d60O9CiTtggizzONJ8tln9SkyN6fCvpArgp9xxD + vChKkZiGSJlRoql1k8nRvHBaPZ9e3L8MEw7LgrkPSgleaNI= + -----END CERTIFICATE----- +kind: ConfigMap +metadata: + creationTimestamp: null + name: ca diff --git a/tests/e2e-instrumentation/instrumentation-java-tls/chainsaw-test.yaml b/tests/e2e-instrumentation/instrumentation-java-tls/chainsaw-test.yaml new file mode 100755 index 0000000000..f552743fa3 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-java-tls/chainsaw-test.yaml @@ -0,0 +1,46 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/kyverno/chainsaw/main/.schemas/json/test-chainsaw-v1alpha1.json +apiVersion: chainsaw.kyverno.io/v1alpha1 +kind: Test +metadata: + creationTimestamp: null + name: instrumentation-java-tls +spec: + steps: + - name: step-00 + try: + # In OpenShift, when a namespace is created, all necessary SCC annotations are automatically added. However, if a namespace is created using a resource file with only selected SCCs, the other auto-added SCCs are not included. Therefore, the UID-range and supplemental groups SCC annotations must be set after the namespace is created. + - command: + entrypoint: kubectl + args: + - annotate + - namespace + - ${NAMESPACE} + - openshift.io/sa.scc.uid-range=1000/1000 + - --overwrite + - command: + entrypoint: kubectl + args: + - annotate + - namespace + - ${NAMESPACE} + - openshift.io/sa.scc.supplemental-groups=3000/3000 + - --overwrite + - apply: + file: ca.yaml + - apply: + file: client-secret.yaml + - apply: + file: server-secret.yaml + - apply: + file: 00-install-collector.yaml + - apply: + file: 00-install-instrumentation.yaml + - name: step-01 + try: + - apply: + file: 01-install-app.yaml + - assert: + file: 01-assert.yaml + catch: + - podLogs: + selector: app=my-java \ No newline at end of file diff --git a/tests/e2e-instrumentation/instrumentation-java-tls/client-secret.yaml b/tests/e2e-instrumentation/instrumentation-java-tls/client-secret.yaml new file mode 100644 index 0000000000..d038b02d89 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-java-tls/client-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQ2VENDQXRHZ0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREIrTVFzd0NRWURWUVFHRXdKVlV6RVQKTUJFR0ExVUVDQXdLUTJGc2FXWnZjbTVwWVRFV01CUUdBMVVFQnd3TlRXOTFiblJoYVc0Z1ZtbGxkekVhTUJnRwpBMVVFQ2d3UldXOTFjaUJQY21kaGJtbDZZWFJwYjI0eEVqQVFCZ05WQkFzTUNWbHZkWElnVlc1cGRERVNNQkFHCkExVUVBd3dKYkc5allXeG9iM04wTUNBWERUSTBNVEF4TURFeU5EQXhNVm9ZRHpJd05URXdNekV6TVRJME1ERXgKV2pDQm1qRUxNQWtHQTFVRUJoTUNWVk14RXpBUkJnTlZCQWdNQ2tOaGJHbG1iM0p1YVdFeEZqQVVCZ05WQkFjTQpEVTF2ZFc1MFlXbHVJRlpwWlhjeEdqQVlCZ05WQkFvTUVWbHZkWElnVDNKbllXNXBlbUYwYVc5dU1SSXdFQVlEClZRUUxEQWxaYjNWeUlGVnVhWFF4R2pBWUJnTlZCQU1NRVhOMll5NWpiSFZ6ZEdWeUxteHZZMkZzTVJJd0VBWUQKVlFRRERBbHNiMk5oYkdodmMzUXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDeApNSzljZDgvb0tHL0FGaTZUS2tsYmNzdlNSS0dTQUkvU2NSNVJQU3FoVW5sQ1NITEhpdXU0TDhSS202RGhGekVaCngvVVppSlREU3BWTllKcHpzZ1B5YkxWU1NQL2k5SDVqWGc3NW5MbUNUNmtRYWV5NG5EUjBZMEp3ZkdaaDB0eWwKMnpMMUlZdnVCYkZQTjMwRVp0bE82WVN3eWtqYjMwcjl3eFhrS1lsMFlCcEJFbkpqYyt5SW44OW52amNtTHgvVApkd3JPTlNXc2QzdXJpdXJNMWFWQlFjd09tMG8rMG9aVFdJY2daa2hPM0cvV29uZHBnSVl2OWF6dWYrL2craWs1ClQwZXBQR3RJSUsrajNqN3lGb3lkMFVONmprb2hyclV1bUFZRmF5UkdkRjhxNzc5dXIxZ0hIcXozRkE0YVY3aEoKd2JWNTNLV0ZCb2hLZEtqQkdVcHBBZ01CQUFHalV6QlJNQjBHQTFVZERnUVdCQlFhK2NpYTZvaS9RenhtZm43RgpiQnhOWTF0bXpEQWZCZ05WSFNNRUdEQVdnQlNPMzZBMFdnYysvUEFDU1hBS05NQXVOdGl3MURBUEJnTlZIUk1CCkFmOEVCVEFEQVFIL01BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ0xvYTFIWnd5aGRQMyt4cXZYUzN5Z0FlbDAKMTdXWEpuM0FocndsajRxUFpvR2NrT0FYY1RoYm9ZaExITnV1ZVpPRkpIWFd4dU5lZDR2Y2xONEVocFdrWVRZTApodUtDN1IvVFVXVnAxMnh1SzNsdTBMZ3ZacUQ5bW05bTlXUW02eVJ5dmZCT0JBc3BLYXdtd0ljS0NLa3RCUnRpClRBaWxxNy9zOVZKRnFtSWNWWEg5bGtIaWNBMUROUjhMc2JSUWJtMUZOSCt4eGd1bTd6cURJbGZlZzhFMkg3WVIKVnRpNXZmZmRUUHVqZHlGaWZJbHR0cmw0MThwVDNVWEJ3UWFGRkVJSHlFdjlDamI5RFRMWDMrTDdlaWVYdXNrcgpxUkZzVjFIa3Arc1IwMTVNckxpUU9uOThjYUlKcG4yN3JOaU9GeVU2b3R3eTR0WGtmd0RTNlJLMkV5T24KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo= + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ3hNSzljZDgvb0tHL0EKRmk2VEtrbGJjc3ZTUktHU0FJL1NjUjVSUFNxaFVubENTSExIaXV1NEw4UkttNkRoRnpFWngvVVppSlREU3BWTgpZSnB6c2dQeWJMVlNTUC9pOUg1alhnNzVuTG1DVDZrUWFleTRuRFIwWTBKd2ZHWmgwdHlsMnpMMUlZdnVCYkZQCk4zMEVadGxPNllTd3lramIzMHI5d3hYa0tZbDBZQnBCRW5KamMreUluODludmpjbUx4L1Rkd3JPTlNXc2QzdXIKaXVyTTFhVkJRY3dPbTBvKzBvWlRXSWNnWmtoTzNHL1dvbmRwZ0lZdjlhenVmKy9nK2lrNVQwZXBQR3RJSUsragozajd5Rm95ZDBVTjZqa29ocnJVdW1BWUZheVJHZEY4cTc3OXVyMWdISHF6M0ZBNGFWN2hKd2JWNTNLV0ZCb2hLCmRLakJHVXBwQWdNQkFBRUNnZ0VBQ2ozZzZQbnc2R3BMN3FEKzcxa3luOGlqeTRNcTNwNzRob2FzejFwM0tCZDEKdEZVdWlKRDQvUzYwOXh0YlFoOXp6UVJ2NEVVSUtqM3U5dEpydUY1cUF0TEhRVVZyRmFjaHEyU0YvanRtNy9JTgpvNG45THVlSDB2d09IS1V5eXNVNWVwUmxYb3kzbUpvTXpMZDRSYTlITU1hU2VhQ0dTUlUxUndrd3hGSjZwRGExCml2am1neXZJRkp5L3RWMGxQSStUWHpnRzdzdkI1Y01CZDFVNjJXN2VkNDBDQWF6bmg1R09FZHZ1YmFaYmZmSWsKZ2huZ0N0ak5EU0p1Rk5ma1ZXdWNnTmErejVFK2dFYjZBdHJBMEhtZEtVR1V2UkRrNUhiVGZJR09ka0hzcnF5UApSSlk0WndFcEFxMWZhY2FxcWVwMXZsVU5vSmNNTVJhc29VOTZGU0lzelFLQmdRRFpCRFBUa0lQeFFNYXF1c3lDCmw5UkpRVy9OZGpuaDl3enRwOUhYWXNTcUEyU3VQQWVPMGdpWWdBOC9MZUpPTFhYelc0dWp2blhLcENveUNiQ3oKc0IvUm9MeTkrWENiODIzdDlKSTJvczZpbFhGbUo4S21OTUlQdExHVG82T1RJNlNseUNJMUFHRzdtdkVhVWY2SQpmanJaL25pY0c0Tlo5TU5rNVg0M2J5UjUzUUtCZ1FEUkJRU3VNWlNyVFFTMGpWbFZQaGxWdHRhV29vbTE5cFRjCmpjYS9vRTZ0Z2RRb2dUalVEZDlDRmtJZ1VxYmNpYlo0ZitnUmhHNHF4VkxwMDBxR0JLWFdYOXlBQTBtMmpNa3MKZkFDbTdZbFNvTElLNkY2M0FuWk9Kck5ETWYyejd1WWthWDBQRk4rTXZsLzRiQTFYMTFEcFRSdG4vL2QyLzBuMwpTeW1LWnVJWC9RS0JnRkh5TjB1OU4wVmpLMkdPdGVqZVFpZ0RVSjlwOUVOeVVXeHdRVm11andxUHkzWExieU1zCkJsam5pbHBXRGkxdEZ5djB0bzczUFcxdWZneDFBa2RueXl3U0lSTXZYS2xXeTN6ZUxGUDdPRUhHWXBLcmt1SEYKN0QyWUFySDRTYTBtK1dZc1kxWldOWkZzMlh3UjJDWmNYQWF6QTRJWEZZdGpWR0VHRTVvRkd1WDFBb0dBTTVXWAplQjRJWU5aYktPd1JkZllqYm9IM0o2bnBicHp5VkJReFRxMlRmVUtqUjNQTXdKakQxcDJEcUZKOWw4UHM0b1ErCms4UXBKQ2thczFaUCtBOUJsa3lHTUptZklZeFJRY2RBcWZISmlEamNkOUN0UDJFK0xUOWowbHVPRDFBUVFFQkEKZXU1ZDFYQk9ZeExYb0N3bGJjNTN5d3ppMTkxZE5jaTQ4YzArVTBrQ2dZRUF5eXdIYkcxOUd4dlNxZXNON2JCQgp6Yy8zYm1qczJocjMvYUxoQWUzMDZUbEdRUkg3Y3lCYzFpR2ZONTF0UTVqV01ZRHg1UndBMmNhUEwwcnl6REhmCjg0SktJeW1pVDB2ckFYRFN2bEorK21BVG1BQnNLcGpSVnpKWTJlVHFqNGQ1NUgyOUdudTVPVUtpMDY1Y2c5WEUKbVIrQ1o5Y1FqN212MDNVbW45MjVJWjQ9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K +kind: Secret +metadata: + creationTimestamp: null + name: client-certs +type: kubernetes.io/tls diff --git a/tests/e2e-instrumentation/instrumentation-java-tls/generate-certs.sh b/tests/e2e-instrumentation/instrumentation-java-tls/generate-certs.sh new file mode 100755 index 0000000000..d4070b03c9 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-java-tls/generate-certs.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +set -ex + +# CA key and cert +openssl req -new -nodes -x509 -days 9650 -keyout ca.key -out ca.crt -subj "/C=US/ST=California/L=Mountain View/O=Your Organization/OU=Your Unit/CN=localhost" +# Server, E.g. use NDS:*.default.svc.cluster.local for arbitrary collector name deployed in the default namespace +openssl req -new -nodes -x509 -CA ca.crt -CAkey ca.key -days 9650 -set_serial 01 -keyout server.key -out server.crt -subj "/C=US/ST=California/L=Mountain View/O=Your Organization/OU=Your Unit/CN=svc.cluster.local/CN=localhost" -addext "subjectAltName = DNS:simplest-collector,DNS:localhost" +# Client +openssl req -new -nodes -x509 -CA ca.crt -CAkey ca.key -days 9650 -set_serial 01 -keyout client.key -out client.crt -subj "/C=US/ST=California/L=Mountain View/O=Your Organization/OU=Your Unit/CN=svc.cluster.local/CN=localhost" + +kubectl create configmap ca --from-file=ca.crt=ca.crt -o yaml --dry-run=client > ca.yaml +kubectl create secret tls server-certs --cert=server.crt --key=server.key -o yaml --dry-run=client > server-secret.yaml +kubectl create secret tls client-certs --cert=client.crt --key=client.key -o yaml --dry-run=client > client-secret.yaml diff --git a/tests/e2e-instrumentation/instrumentation-java-tls/server-secret.yaml b/tests/e2e-instrumentation/instrumentation-java-tls/server-secret.yaml new file mode 100644 index 0000000000..63afbd2286 --- /dev/null +++ b/tests/e2e-instrumentation/instrumentation-java-tls/server-secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +data: + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVPVENDQXlHZ0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREIrTVFzd0NRWURWUVFHRXdKVlV6RVQKTUJFR0ExVUVDQXdLUTJGc2FXWnZjbTVwWVRFV01CUUdBMVVFQnd3TlRXOTFiblJoYVc0Z1ZtbGxkekVhTUJnRwpBMVVFQ2d3UldXOTFjaUJQY21kaGJtbDZZWFJwYjI0eEVqQVFCZ05WQkFzTUNWbHZkWElnVlc1cGRERVNNQkFHCkExVUVBd3dKYkc5allXeG9iM04wTUNBWERUSTBNVEF4TURFeU5EQXhNVm9ZRHpJd05URXdNekV6TVRJME1ERXgKV2pDQm1qRUxNQWtHQTFVRUJoTUNWVk14RXpBUkJnTlZCQWdNQ2tOaGJHbG1iM0p1YVdFeEZqQVVCZ05WQkFjTQpEVTF2ZFc1MFlXbHVJRlpwWlhjeEdqQVlCZ05WQkFvTUVWbHZkWElnVDNKbllXNXBlbUYwYVc5dU1SSXdFQVlEClZRUUxEQWxaYjNWeUlGVnVhWFF4R2pBWUJnTlZCQU1NRVhOMll5NWpiSFZ6ZEdWeUxteHZZMkZzTVJJd0VBWUQKVlFRRERBbHNiMk5oYkdodmMzUXdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDcgpGN0U5SmtScldZYmM1Wkh2VU5GM2NZNVFaL0FrWktiN1Exend3aGFhQmtqVldWUEJCeUFmakNzM3laU3RGN2RkClo1VUxKNzN1VmtSdEJuTFd5bUJqbTZMN2xHVms2c0VqSXdFQkFUUDVJSksxdjBONFliSExYM1RJMDZXdlNCM2gKbGpjRzZOY0d0djE3NGFnbU1xZ05Bc0lmYlhEcW0xWkZSQzhCa0IwK2Jhc1hLUzJJS3VuSlIweEl1eTlFL09IWgp4b3hLUFFyQjNMcFBJNDAzSThPR2h1alZiY2xvVzh3UEljaVFwOGdJNVU3UWRJWmwzVkszcTZFWkNTVVhiMGNMClEyNnQ3TDZiRmlWcmhrTm5DZGtOU1Fybm55V2p4cldTdmdLTVZWaUtOVU1XQ0pGRWk1MytlSllxZkkzRTBhYXAKNlNsQ3NUQlE2akJYd0Y5R01mRURBZ01CQUFHamdhSXdnWjh3SFFZRFZSME9CQllFRkhZMjV2bmRHZGdDSC9zUgpMREx3Wmc4Ry9reXpNQjhHQTFVZEl3UVlNQmFBRkk3Zm9EUmFCejc4OEFKSmNBbzB3QzQyMkxEVU1BOEdBMVVkCkV3RUIvd1FGTUFNQkFmOHdUQVlEVlIwUkJFVXdRNElTYzJsdGNHeGxjM1F0WTI5c2JHVmpkRzl5Z2lJcUxuUnkKWVdOcGJtY3RjM2x6ZEdWdExuTjJZeTVqYkhWemRHVnlMbXh2WTJGc2dnbHNiMk5oYkdodmMzUXdEUVlKS29aSQpodmNOQVFFTEJRQURnZ0VCQURVNWtCUnRHaTlYalh5TUZJWEdKYTZtNEJrSTFOK0ZqUUprbGVSbWFOaWljRzhwCngzcnNBLzVVZU8wR0pDOXIrMWpxYThtVjhtUzcrMVVNUWJwMTZFUlZOVDBHWnA1TXNFdERqUHBveWFtM1JOZ1YKT2QwMnhZUUNNK3NGMVdNWll3M05FMEszTkJRcncyY3hoKzg2U29GdDBMdDEyYlBmbkxGTzJSdmI3b09aaHB0aApLNjJxaUlOVXAveG85S3hEelVhT1R1SndsTFdmTWQzMS80NFNyZm1hVWpGMWhKRDJ2SHZGeHZhcVNvSmJNK3pkCm1lbGo4cFhkS1ZJaWFRQ3hQZ1E4bTRHUmhQWkFuTk1pVlhiVm9vR05pelFaeEt2UWVwWnpXRTRXNmNFZTJtTWoKYlYvNVBOc3l5Mk0wOWY0MjlvTGxtNEpjb3IyMWZFSGVNVG1PYytzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ3JGN0U5SmtScldZYmMKNVpIdlVORjNjWTVRWi9Ba1pLYjdRMXp3d2hhYUJralZXVlBCQnlBZmpDczN5WlN0RjdkZFo1VUxKNzN1VmtSdApCbkxXeW1Cam02TDdsR1ZrNnNFakl3RUJBVFA1SUpLMXYwTjRZYkhMWDNUSTA2V3ZTQjNobGpjRzZOY0d0djE3CjRhZ21NcWdOQXNJZmJYRHFtMVpGUkM4QmtCMCtiYXNYS1MySUt1bkpSMHhJdXk5RS9PSFp4b3hLUFFyQjNMcFAKSTQwM0k4T0dodWpWYmNsb1c4d1BJY2lRcDhnSTVVN1FkSVpsM1ZLM3E2RVpDU1VYYjBjTFEyNnQ3TDZiRmlWcgpoa05uQ2RrTlNRcm5ueVdqeHJXU3ZnS01WVmlLTlVNV0NKRkVpNTMrZUpZcWZJM0UwYWFwNlNsQ3NUQlE2akJYCndGOUdNZkVEQWdNQkFBRUNnZ0VBQWZ0d3lhQk10SVFoZzVYQzIrZUxvVVhvc1QxejYvVlo1d1BodVNsMW1laXIKU1QwTUZxT1Z4YjdvQ21MUFVsSFdNQkRtTlB5WjFGVUJhMkticjNEUUJCSVV0QWxhWDVqaU9TdDlYeGxVb3NNegp5cGg2MkZxeXUrZTd0Z2UwWGZ0elRMc05hRVBPNEp4dFdOVWgxcVJYdG1yZ1grQzU0aDdWSjFGSkU2L1dJblJJClF1ajEydytCTXVoUUcyRVM0cHpQQ2RtUGgvTkdSV1Q1K0NuamJzMkdjS1EvNXZIaXNnZVAyWEU2ZWRmbldCcFAKN0xNYjFCUGdLU09FTXZ2eUI2R2lMU2xwYWpKTmRtUHpmQmZURllWT05RTk5RYWJhRVFoNG9Gd2s1SkJKTlB5dQo1bCt2UlRpTEtZdk45YzB6Q1ZLa2NuZ0VyaXc0cTVCT3pPa3l1bGlNQVFLQmdRRHNzNkw2UVppNTRTOUh2ckRLCmxSMms4WlRoR1NSZ25uQ1Q2ZHVBcG50VEJpMWs3RUdGWFlWWGtIcEtpZEV5NFNKUmc4bXlSeTNjd1J3S2UzNVgKQVU5aURrajNjN3JLa041RmNleURqTzhXRVptdzZsb213aUtaczk0Y3FHY2tEWnEzSFNROStiMmNjZXFvc3QzSApOWE9ZUHBZdUZKUDAraFFEVFErSytXZUZBd0tCZ1FDNUNxNmJrSEFmQlp3OG5LeG5od3drV21JOTFjV3Fya21VCm5vamRPUkRxV1I1aWZDS05uQi9La0R4RHhGMDNRTjF5alpqcGZFOXdZRm42Vys4ZitPZXNBSGhaODRyZmhUTUgKTTd0VXZoNnNzcSt4QjhkWkJIeTRMd0prQTBPSFBqV2ZtaU96WllwczloUW5WRzAyRDBkdWZlWUNOd0hYN2hTRgptckp6RnJJa0FRS0JnUUNsclZiMk05UGl4MnVBbkVqQ2czMHNacXYrb3NxRGxtTFdKV291c2xpLzFDTVI4UXdyCmZUcElBQ2lZNDc0NkRyc21zMGdLTVNnNHpESUVaRXdhT2lDR1VkbGcydkJ6dU5MYmFOSlRnZUlYWUZwaktxWFAKV3pNOHdsbEZWZHBic2VvSklheXNkSkh6WHdrUTY2R3dQZ21iRnJPbnJWK2lxU2c0NTBkcHp3aFdZUUtCZ0JKNQpWWXRrZFQwem96Q045OHh5T0MwYzlQZjFjc0dpbXVnQ2wrbDJQQkVaaXFZTWZLcWtycXZia0ppM2J4TUlIOVBDCi9VUTZTL2dOTm81L1JUVnM5VHcvNDhRZlEzc2pZai9TMDE0WGlScDIwSUdkSkRMbjlzZXdzYzFvWWdLTG5IRHQKdzZpeWQ0cC9XdTIrU1JUL200TVZnTFF4NTdZMko4aGE5SHYzQlJ3QkFvR0FVMkNXRkJUWk1HL0NCZDhiOG5VeQpHblltNnhIMHpkeWdzK3JWakl5aEhBRXBTODZCaTZGSGh6TkNYRDFjSCtJWjAvYzR0QmYwSjhsVVRvOXpCV1YrClBJNjJOaEVMOW45WjMydTJGNzRDS0VTUi9EdUhTNXBTT2M3eXgvU3BOMjBpUkpodFl5aHA2YU9HMXQwL2NzRHAKVFpvOHlEdzl5cmVTdU9VSUFmM3JraG89Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K +kind: Secret +metadata: + creationTimestamp: null + name: server-certs +type: kubernetes.io/tls
NameTypeDescriptionRequired
castring + CA defines the key of certificate (e.g. ca.crt) in the configmap map, secret or absolute path to a certificate. +The absolute path can be used when certificate is already present on the workload filesystem e.g. +/var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt
+
false
certstring + Cert defines the key (e.g. tls.crt) of the client certificate in the secret or absolute path to a certificate. +The absolute path can be used when certificate is already present on the workload filesystem.
+
false
configMapNamestring + ConfigMapName defines configmap name with CA certificate. If it is not defined CA certificate will be +used from the secret defined in SecretName.
+
false
keystring + Key defines a key (e.g. tls.key) of the private key in the secret or absolute path to a certificate. +The absolute path can be used when certificate is already present on the workload filesystem.
+
false
secretNamestring + SecretName defines secret name that will be used to configure TLS on the exporter. +It is user responsibility to create the secret in the namespace of the workload. +The secret must contain client certificate (Cert) and private key (Key). +The CA certificate might be defined in the secret or in the config map.
false