Skip to content

Commit

Permalink
Add Namespaced Scope Interceptor
Browse files Browse the repository at this point in the history
Added Namespaced Scope Interceptor. A namespaced admin can create and
manage its interceptor without requiring elevated permissions. Fixes #1364
  • Loading branch information
khrm committed Oct 13, 2022
1 parent 811e571 commit 09c9932
Show file tree
Hide file tree
Showing 39 changed files with 2,916 additions and 29 deletions.
2 changes: 2 additions & 0 deletions cmd/controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/tektoncd/triggers/pkg/reconciler/clusterinterceptor"
elresources "github.com/tektoncd/triggers/pkg/reconciler/eventlistener/resources"
"github.com/tektoncd/triggers/pkg/reconciler/interceptor"

corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/labels"
Expand Down Expand Up @@ -95,5 +96,6 @@ func main() {
cfg,
eventlistener.NewController(c),
clusterinterceptor.NewController(),
interceptor.NewController(),
)
}
4 changes: 2 additions & 2 deletions config/200-clusterrole.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ rules:
resources: ["mutatingwebhookconfigurations", "validatingwebhookconfigurations"]
verbs: ["get", "list", "create", "update", "delete", "patch", "watch"]
- apiGroups: ["triggers.tekton.dev"]
resources: ["clustertriggerbindings", "clusterinterceptors", "eventlisteners", "triggerbindings", "triggertemplates", "triggers", "eventlisteners/finalizers"]
resources: ["clustertriggerbindings", "clusterinterceptors", "interceptors", "eventlisteners", "triggerbindings", "triggertemplates", "triggers", "eventlisteners/finalizers"]
verbs: ["get", "list", "create", "update", "delete", "patch", "watch"]
- apiGroups: ["triggers.tekton.dev"]
resources: ["clustertriggerbindings/status", "clusterinterceptors/status", "eventlisteners/status", "triggerbindings/status", "triggertemplates/status", "triggers/status"]
resources: ["clustertriggerbindings/status", "clusterinterceptors/status", "interceptors/status", "eventlisteners/status", "triggerbindings/status", "triggertemplates/status", "triggers/status"]
verbs: ["get", "list", "create", "update", "delete", "patch", "watch"]
# We uses leases for leaderelection
- apiGroups: ["coordination.k8s.io"]
Expand Down
54 changes: 54 additions & 0 deletions config/300-interceptor.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Copyright 2022 The Tekton 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
#
# https://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.

apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
name: interceptors.triggers.tekton.dev
labels:
app.kubernetes.io/instance: default
app.kubernetes.io/part-of: tekton-triggers
triggers.tekton.dev/release: "devel"
version: "devel"
spec:
group: triggers.tekton.dev
scope: Cluster
names:
kind: Interceptor
plural: interceptors
singular: interceptor
shortNames:
- ni
categories:
- tekton
- tekton-triggers
versions:
- name: v1alpha1
served: true
storage: true
schema:
openAPIV3Schema:
type: object
# One can use x-kubernetes-preserve-unknown-fields: true
# at the root of the schema (and inside any properties, additionalProperties)
# to get the traditional CRD behaviour that nothing is pruned, despite
# setting spec.preserveUnknownProperties: false.
#
# See https://kubernetes.io/blog/2019/06/20/crd-structural-schema/
# See issue: https://github.com/knative/serving/issues/912
x-kubernetes-preserve-unknown-fields: true
# Opt into the status subresource so metadata.generation
# starts to increment
subresources:
status: {}
164 changes: 160 additions & 4 deletions docs/triggers-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ string
<h3 id="triggers.tekton.dev/v1alpha1.ClientConfig">ClientConfig
</h3>
<p>
(<em>Appears on:</em><a href="#triggers.tekton.dev/v1alpha1.ClusterInterceptorSpec">ClusterInterceptorSpec</a>)
(<em>Appears on:</em><a href="#triggers.tekton.dev/v1alpha1.ClusterInterceptorSpec">ClusterInterceptorSpec</a>, <a href="#triggers.tekton.dev/v1alpha1.InterceptorSpec">InterceptorSpec</a>)
</p>
<div>
<p>ClientConfig describes how a client can communicate with the Interceptor</p>
Expand Down Expand Up @@ -1182,6 +1182,79 @@ SecretRef
</tr>
</tbody>
</table>
<h3 id="triggers.tekton.dev/v1alpha1.Interceptor">Interceptor
</h3>
<div>
<p>Interceptor describes a pluggable interceptor including configuration
such as the fields it accepts and its deployment address. The type is based on
the Validating/MutatingWebhookConfiguration types for configuring AdmissionWebhooks</p>
</div>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>metadata</code><br/>
<em>
<a href="https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#objectmeta-v1-meta">
Kubernetes meta/v1.ObjectMeta
</a>
</em>
</td>
<td>
<em>(Optional)</em>
Refer to the Kubernetes API documentation for the fields of the
<code>metadata</code> field.
</td>
</tr>
<tr>
<td>
<code>spec</code><br/>
<em>
<a href="#triggers.tekton.dev/v1alpha1.InterceptorSpec">
InterceptorSpec
</a>
</em>
</td>
<td>
<br/>
<br/>
<table>
<tr>
<td>
<code>clientConfig</code><br/>
<em>
<a href="#triggers.tekton.dev/v1alpha1.ClientConfig">
ClientConfig
</a>
</em>
</td>
<td>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<code>status</code><br/>
<em>
<a href="#triggers.tekton.dev/v1alpha1.InterceptorStatus">
InterceptorStatus
</a>
</em>
</td>
<td>
<em>(Optional)</em>
</td>
</tr>
</tbody>
</table>
<h3 id="triggers.tekton.dev/v1alpha1.InterceptorInterface">InterceptorInterface
</h3>
<div>
Expand All @@ -1204,6 +1277,9 @@ SecretRef
<tbody><tr><td><p>&#34;ClusterInterceptor&#34;</p></td>
<td><p>ClusterInterceptorKind indicates that Interceptor type has a cluster scope.</p>
</td>
</tr><tr><td><p>&#34;NamespacedInterceptor&#34;</p></td>
<td><p>NamespacedInterceptorKind indicated that interceptor has a namespaced scope</p>
</td>
</tr></tbody>
</table>
<h3 id="triggers.tekton.dev/v1alpha1.InterceptorParams">InterceptorParams
Expand Down Expand Up @@ -1427,6 +1503,85 @@ Status
</tr>
</tbody>
</table>
<h3 id="triggers.tekton.dev/v1alpha1.InterceptorSpec">InterceptorSpec
</h3>
<p>
(<em>Appears on:</em><a href="#triggers.tekton.dev/v1alpha1.Interceptor">Interceptor</a>)
</p>
<div>
<p>InterceptorSpec describes the Spec for an Interceptor</p>
</div>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>clientConfig</code><br/>
<em>
<a href="#triggers.tekton.dev/v1alpha1.ClientConfig">
ClientConfig
</a>
</em>
</td>
<td>
</td>
</tr>
</tbody>
</table>
<h3 id="triggers.tekton.dev/v1alpha1.InterceptorStatus">InterceptorStatus
</h3>
<p>
(<em>Appears on:</em><a href="#triggers.tekton.dev/v1alpha1.Interceptor">Interceptor</a>)
</p>
<div>
<p>InterceptorStatus holds the status of the Interceptor</p>
</div>
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>Status</code><br/>
<em>
<a href="https://pkg.go.dev/knative.dev/pkg/apis/duck/v1#Status">
knative.dev/pkg/apis/duck/v1.Status
</a>
</em>
</td>
<td>
<p>
(Members of <code>Status</code> are embedded into this type.)
</p>
</td>
</tr>
<tr>
<td>
<code>AddressStatus</code><br/>
<em>
<a href="https://pkg.go.dev/knative.dev/pkg/apis/duck/v1#AddressStatus">
knative.dev/pkg/apis/duck/v1.AddressStatus
</a>
</em>
</td>
<td>
<p>
(Members of <code>AddressStatus</code> are embedded into this type.)
</p>
<p>Interceptor is Addressable and exposes the URL where the Interceptor is running</p>
</td>
</tr>
</tbody>
</table>
<h3 id="triggers.tekton.dev/v1alpha1.KubernetesResource">KubernetesResource
</h3>
<p>
Expand Down Expand Up @@ -3612,6 +3767,9 @@ SecretRef
<tbody><tr><td><p>&#34;ClusterInterceptor&#34;</p></td>
<td><p>ClusterInterceptorKind indicates that Interceptor type has a cluster scope.</p>
</td>
</tr><tr><td><p>&#34;NamespacedInterceptor&#34;</p></td>
<td><p>NamespacedInterceptorKind indicates that Interceptor type has a namespace scope.</p>
</td>
</tr></tbody>
</table>
<h3 id="triggers.tekton.dev/v1beta1.InterceptorParams">InterceptorParams
Expand Down Expand Up @@ -3690,9 +3848,7 @@ InterceptorKind
</td>
<td>
<em>(Optional)</em>
<p>InterceptorKind indicates the kind of the Interceptor, namespaced or cluster scoped.
Currently only InterceptorKind is ClusterInterceptor, so the only valid value
is the default one</p>
<p>InterceptorKind indicates the kind of the Interceptor, namespaced or cluster scoped.</p>
</td>
</tr>
<tr>
Expand Down
42 changes: 35 additions & 7 deletions pkg/adapter/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"time"

clusterinterceptorsinformer "github.com/tektoncd/triggers/pkg/client/injection/informers/triggers/v1alpha1/clusterinterceptor"
interceptorsinformer "github.com/tektoncd/triggers/pkg/client/injection/informers/triggers/v1alpha1/interceptor"
clustertriggerbindingsinformer "github.com/tektoncd/triggers/pkg/client/injection/informers/triggers/v1beta1/clustertriggerbinding"
eventlistenerinformer "github.com/tektoncd/triggers/pkg/client/injection/informers/triggers/v1beta1/eventlistener"
triggersinformer "github.com/tektoncd/triggers/pkg/client/injection/informers/triggers/v1beta1/trigger"
Expand Down Expand Up @@ -108,7 +109,7 @@ func (s *sinker) getHTTPClient() (*http.Client, error) {

certPool := x509.NewCertPool()

err := s.getCertFromClusterInterceptor(certPool)
err := s.getCertFromInterceptor(certPool)
if err != nil {
return &http.Client{}, fmt.Errorf("Timed out waiting on CaBundle to available for clusterInterceptor: %v", err)
}
Expand All @@ -119,7 +120,7 @@ func (s *sinker) getHTTPClient() (*http.Client, error) {
go func() {
for {
<-ticker.C
if err := s.getCertFromClusterInterceptor(certPool); err != nil {
if err := s.getCertFromInterceptor(certPool); err != nil {
s.Logger.Fatalf("Timed out waiting on CaBundle to available for clusterInterceptor: %v", err)
}
}
Expand All @@ -141,7 +142,7 @@ func (s *sinker) getHTTPClient() (*http.Client, error) {
ExpectContinueTimeout: s.Args.ElHTTPClientExpectContinueTimeout * time.Second}}, nil
}

func (s *sinker) getCertFromClusterInterceptor(certPool *x509.CertPool) error {
func (s *sinker) getCertFromInterceptor(certPool *x509.CertPool) error {
var (
caCert []byte
count int
Expand All @@ -166,12 +167,38 @@ func (s *sinker) getCertFromClusterInterceptor(certPool *x509.CertPool) error {
}
}
}
if httpsCILen != 0 && httpsCILen == count {
return true, nil

if httpsCILen == 0 || httpsCILen != count {
return false, fmt.Errorf("empty caBundle in clusterInterceptor spec")
}
return false, fmt.Errorf("empty caBundle in clusterInterceptor spec")

httpsCILen = 0
count = 0

interceptorList, err := interceptorsinformer.Get(s.injCtx).Lister().Interceptors(s.Namespace).List(labels.NewSelector())
if err != nil {
return false, err
}

for i := range interceptorList {
if v, k := interceptorList[i].Labels["server/type"]; k && v == "https" {
httpsCILen++
if !bytes.Equal(interceptorList[i].Spec.ClientConfig.CaBundle, []byte{}) {
caCert = interceptorList[i].Spec.ClientConfig.CaBundle
if ok := certPool.AppendCertsFromPEM(caCert); !ok {
return false, fmt.Errorf("unable to parse cert from %s", caCert)
}
count++
}
}
}
if httpsCILen != count {
return false, fmt.Errorf("empty caBundle in interceptor spec")
}

return true, nil
}); err != nil {
return fmt.Errorf("Timed out waiting on CaBundle to available for clusterInterceptor: %v", err)
return fmt.Errorf("Timed out waiting on CaBundle to available for Interceptor: %v", err)
}
return nil
}
Expand Down Expand Up @@ -206,6 +233,7 @@ func (s *sinker) Start(ctx context.Context) error {
ClusterTriggerBindingLister: clustertriggerbindingsinformer.Get(s.injCtx).Lister(),
TriggerTemplateLister: triggertemplatesinformer.Get(s.injCtx).Lister(),
ClusterInterceptorLister: clusterinterceptorsinformer.Get(s.injCtx).Lister(),
InterceptorLister: interceptorsinformer.Get(s.injCtx).Lister(),
}

mux := http.NewServeMux()
Expand Down
Loading

0 comments on commit 09c9932

Please sign in to comment.