Skip to content

Commit

Permalink
Update tls connection with new design
Browse files Browse the repository at this point in the history
  • Loading branch information
savitaashture committed Nov 18, 2020
1 parent 02d77e4 commit 7e97b68
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 47 deletions.
20 changes: 11 additions & 9 deletions examples/eventlistener-tls-connection/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,17 +76,19 @@ This request will be processed by the owner of the root key to generate the cert
serviceAccountName: tekton-triggers-github-sa
containers:
- env:
- name: TLS_SECRET_NAME
value: "tls-secret-key" # Name of the secret which is created above (Mandatory env for TLS connection)
- name: TLS_CERT_NAME
value: "tls.crt" # Name of the cert file (Optional, If not provided tls.crt used)
- name: TLS_KEY_NAME
value: "tls.key" # Name of the key file (Optional, If not provided tls.key used)
- name: TLS_CERT
valueFrom:
secretKeyRef:
name: tls-key-secret
key: tls.crt
- name: TLS_KEY
valueFrom:
secretKeyRef:
name: tls-key-secret
key: tls.key
```
There are **RESERVED** env to provide `HTTPS` connection to triggers eventlistener
* `TLS_SECRET_NAME` is **mandatory** env key where user specify created secret name.
* `TLS_CERT_NAME` and `TLS_KEY_NAME` are **optional** env where user specify name of the cert and key respectively,
If not provided triggers assume names as `tls.crt` and `tls.key` by default.
* `TLS_CERT` and `TLS_KEY` are **reserved** env where user specify file name for the cert and key respectively using `secretKeyRef` env type.

1. Test by sending the sample payload.

Expand Down
81 changes: 80 additions & 1 deletion pkg/apis/triggers/v1alpha1/event_listener_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,18 @@ import (
"fmt"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/validation"
"knative.dev/pkg/apis"
)

var (
reservedEnvVars = sets.NewString(
"TLS_CERT",
"TLS_KEY",
)
)

// Validate EventListener.
func (e *EventListener) Validate(ctx context.Context) *apis.FieldError {
return e.Spec.validate(ctx)
Expand All @@ -43,7 +51,7 @@ func (s *EventListenerSpec) validate(ctx context.Context) (errs *apis.FieldError
errs = errs.Also(trigger.validate(ctx).ViaField(fmt.Sprintf("spec.triggers[%d]", i)))
}
if s.Resources.KubernetesResource != nil {
errs = errs.Also(validateKubernetesObject(s.Resources.KubernetesResource))
errs = errs.Also(validateKubernetesObject(s.Resources.KubernetesResource).ViaField("spec.resources.kubernetesResource"))
}
return errs
}
Expand All @@ -59,11 +67,82 @@ func validateKubernetesObject(orig *KubernetesResource) (errs *apis.FieldError)
if len(orig.Template.Spec.Containers) == 1 {
errs = errs.Also(apis.CheckDisallowedFields(orig.Template.Spec.Containers[0],
*containerFieldMask(&orig.Template.Spec.Containers[0])).ViaField("spec.template.spec.containers[0]"))
// validate env
errs = errs.Also(validateEnv(orig.Template.Spec.Containers[0].Env).ViaField("spec.template.spec.containers[0].env"))
}

return errs
}

func validateEnv(envVars []corev1.EnvVar) (errs *apis.FieldError) {
var (
count = 0
envValue string
)
for i, env := range envVars {
errs = errs.Also(validateEnvVar(env).ViaIndex(i))
if reservedEnvVars.Has(env.Name) {
count++
envValue = env.Name
}
}
// This is to make sure both TLS_CERT and TLS_KEY is set for tls connection
if count == 1 {
errs = errs.Also(&apis.FieldError{
Message: fmt.Sprintf("Expected env's are TLS_CERT and TLS_KEY, but got only one env %s", envValue),
})
}
return errs
}

func validateEnvVar(env corev1.EnvVar) (errs *apis.FieldError) {
errs = errs.Also(apis.CheckDisallowedFields(env, *envVarMask(&env)))

return errs.Also(validateEnvValueFrom(env.ValueFrom).ViaField("valueFrom"))
}

func validateEnvValueFrom(source *corev1.EnvVarSource) *apis.FieldError {
if source == nil {
return nil
}
return apis.CheckDisallowedFields(*source, *envVarSourceMask(source))
}

// envVarSourceMask performs a _shallow_ copy of the Kubernetes EnvVarSource object to a new
// Kubernetes EnvVarSource object bringing over only the fields allowed in the Triggers EventListener API.
func envVarSourceMask(in *corev1.EnvVarSource) *corev1.EnvVarSource {
if in == nil {
return nil
}
out := new(corev1.EnvVarSource)
// Allowed fields
out.SecretKeyRef = in.SecretKeyRef

// Disallowed fields
out.ConfigMapKeyRef = nil
out.FieldRef = nil
out.ResourceFieldRef = nil

return out
}

// envVarMask performs a _shallow_ copy of the Kubernetes EnvVar object to a new
// Kubernetes EnvVar object bringing over only the fields allowed in the Triggers EventListener API.
func envVarMask(in *corev1.EnvVar) *corev1.EnvVar {
if in == nil {
return nil
}
out := new(corev1.EnvVar)
// Allowed fields
out.Name = in.Name
out.ValueFrom = in.ValueFrom

// Disallowed fields
out.Value = ""

return out
}

func containerFieldMask(in *corev1.Container) *corev1.Container {
out := new(corev1.Container)
out.Resources = in.Resources
Expand Down
60 changes: 57 additions & 3 deletions pkg/apis/triggers/v1alpha1/event_listener_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,21 @@ func Test_EventListenerValidate(t *testing.T) {
ServiceAccountName: "k8sresource",
Containers: []corev1.Container{{
Env: []corev1.EnvVar{{
Name: "TLS_SECRET_NAME",
Value: "tls-secret-key",
Name: "TLS_CERT",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{Name: "secret-name"},
Key: "tls.crt",
},
},
}, {
Name: "TLS_KEY",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{Name: "secret-name"},
Key: "tls.key",
},
},
}},
}},
},
Expand Down Expand Up @@ -563,7 +576,7 @@ func TestEventListenerValidate_error(t *testing.T) {
)),
)),
}, {
name: "user specifies an unsupported container field",
name: "user specifies an unsupported container fields",
el: bldr.EventListener("name", "namespace",
bldr.EventListenerSpec(
bldr.EventListenerTrigger("tt", "v1alpha1",
Expand All @@ -576,6 +589,47 @@ func TestEventListenerValidate_error(t *testing.T) {
Spec: corev1.PodSpec{
Containers: []corev1.Container{{
Name: "containername",
Env: []corev1.EnvVar{{
Name: "key",
Value: "value",
}, {
Name: "key1",
ValueFrom: &corev1.EnvVarSource{
ConfigMapKeyRef: &corev1.ConfigMapKeySelector{
Key: "key",
},
},
}},
}},
},
},
}),
)),
)),
}, {
name: "user specifies an invalid env for TLS connection",
el: bldr.EventListener("name", "namespace",
bldr.EventListenerSpec(
bldr.EventListenerTrigger("tt", "v1alpha1",
bldr.EventListenerTriggerBinding("tb", "TriggerBinding", "v1alpha1"),
),
bldr.EventListenerResources(
bldr.EventListenerKubernetesResources(
bldr.EventListenerPodSpec(duckv1.WithPodSpec{
Template: duckv1.PodSpecable{
Spec: corev1.PodSpec{
Containers: []corev1.Container{{
Env: []corev1.EnvVar{{
Name: "TLS_CERT",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "secret-name",
},
Key: "tls.key",
},
},
}},
}},
},
},
Expand Down
48 changes: 26 additions & 22 deletions pkg/reconciler/v1alpha1/eventlistener/eventlistener.go
Original file line number Diff line number Diff line change
Expand Up @@ -418,19 +418,19 @@ func getDeployment(el *v1alpha1.EventListener) *appsv1.Deployment {
}}

container := getContainer(el)
for i := range container.Env {
if container.Env[i].Name == "TLS_SECRET_NAME" {
for _, v := range container.Env {
// If TLS related env are set then mount secret volume which will be used while starting the eventlistener.
if v.Name == "TLS_CERT" {
vol = append(vol, corev1.Volume{
Name: "https-connection",
VolumeSource: corev1.VolumeSource{
Secret: &corev1.SecretVolumeSource{
SecretName: container.Env[i].Value,
SecretName: v.ValueFrom.SecretKeyRef.Name,
},
},
})
}
}

if el.Spec.Resources.KubernetesResource != nil {
if len(el.Spec.Resources.KubernetesResource.Template.Spec.Tolerations) != 0 {
tolerations = el.Spec.Resources.KubernetesResource.Template.Spec.Tolerations
Expand Down Expand Up @@ -471,11 +471,8 @@ func getDeployment(el *v1alpha1.EventListener) *appsv1.Deployment {

func getContainer(el *v1alpha1.EventListener) corev1.Container {
var (
// Default location fot certificate.
elCert = "/etc/triggers/tls/tls.crt"
// Default location fot key.
elKey = "/etc/triggers/tls/tls.key"
resources corev1.ResourceRequirements
elCert, elKey string
resources corev1.ResourceRequirements
)

vMount := []corev1.VolumeMount{{
Expand All @@ -492,36 +489,39 @@ func getContainer(el *v1alpha1.EventListener) corev1.Container {
},
}}

certEnv := map[string]string{}
certEnv := map[string]*corev1.EnvVarSource{}
if el.Spec.Resources.KubernetesResource != nil {
if len(el.Spec.Resources.KubernetesResource.Template.Spec.Containers) != 0 {
resources = el.Spec.Resources.KubernetesResource.Template.Spec.Containers[0].Resources
for i, e := range el.Spec.Resources.KubernetesResource.Template.Spec.Containers[0].Env {
env = append(env, e)
certEnv[el.Spec.Resources.KubernetesResource.Template.Spec.Containers[0].Env[i].Name] =
el.Spec.Resources.KubernetesResource.Template.Spec.Containers[0].Env[i].Value
el.Spec.Resources.KubernetesResource.Template.Spec.Containers[0].Env[i].ValueFrom
}
}
}

var scheme corev1.URIScheme
if _, ok := certEnv["TLS_SECRET_NAME"]; ok {
if v, ok := certEnv["TLS_CERT_NAME"]; ok {
elCert = "/etc/triggers/tls/" + v
}
if v, ok := certEnv["TLS_KEY_NAME"]; ok {
elKey = "/etc/triggers/tls/" + v
}
if v, ok := certEnv["TLS_CERT"]; ok {
elCert = "/etc/triggers/tls/" + v.SecretKeyRef.Key
} else {
elCert = ""
}
if v, ok := certEnv["TLS_KEY"]; ok {
elKey = "/etc/triggers/tls/" + v.SecretKeyRef.Key
} else {
elKey = ""
}

if elCert != "" && elKey != "" {
*ElPort = 8443
scheme = corev1.URISchemeHTTPS
vMount = append(vMount, corev1.VolumeMount{
Name: "https-connection",
ReadOnly: true,
MountPath: "/etc/triggers/tls",
})
*ElPort = 8443
scheme = corev1.URISchemeHTTPS
} else {
elCert = ""
elKey = ""
*ElPort = 8080
scheme = corev1.URISchemeHTTP
}
Expand Down Expand Up @@ -560,6 +560,10 @@ func getContainer(el *v1alpha1.EventListener) corev1.Container {
"-el-name", el.Name,
"-el-namespace", el.Namespace,
"-port", strconv.Itoa(*ElPort),
"-readtimeout", strconv.FormatInt(*ELReadTimeOut, 10),
"-writetimeout", strconv.FormatInt(*ELWriteTimeOut, 10),
"-idletimeout", strconv.FormatInt(*ELIdleTimeOut, 10),
"-timeouthandler", strconv.FormatInt(*ELTimeOutHandler, 10),
"-tls-cert", elCert,
"-tls-key", elKey,
},
Expand Down
44 changes: 36 additions & 8 deletions pkg/reconciler/v1alpha1/eventlistener/eventlistener_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -303,11 +303,25 @@ var withTLSConfig = func(d *appsv1.Deployment) {
},
},
}, {
Name: "TLS_SECRET_NAME",
Value: "tls-secret-key",
Name: "TLS_CERT",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "tls-secret-key",
},
Key: "tls.crt",
},
},
}, {
Name: "TLS_CERT_NAME",
Value: "tls.pem",
Name: "TLS_KEY",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "tls-secret-key",
},
Key: "tls.key",
},
},
}},
}}
d.Spec.Template.Spec.Volumes = []corev1.Volume{{
Expand Down Expand Up @@ -490,11 +504,25 @@ func TestReconcile(t *testing.T) {
Spec: corev1.PodSpec{
Containers: []corev1.Container{{
Env: []corev1.EnvVar{{
Name: "TLS_SECRET_NAME",
Value: "tls-secret-key",
Name: "TLS_CERT",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "tls-secret-key",
},
Key: "tls.crt",
},
},
}, {
Name: "TLS_CERT_NAME",
Value: "tls.pem",
Name: "TLS_KEY",
ValueFrom: &corev1.EnvVarSource{
SecretKeyRef: &corev1.SecretKeySelector{
LocalObjectReference: corev1.LocalObjectReference{
Name: "tls-secret-key",
},
Key: "tls.key",
},
},
}},
}},
},
Expand Down
Loading

0 comments on commit 7e97b68

Please sign in to comment.