Skip to content

Commit e99d0f1

Browse files
jkhelilcursoragent
andcommitted
feat(tektonpipeline): add NetworkPolicy for proxy-webhook
Reuse the NetworkPolicy foundation built for TektonTrigger to scope ingress/egress for the proxy-webhook workload TektonPipeline deploys into the operand namespace. Adds spec.networkPolicy to TektonPipeline, propagated from TektonConfig, and reconciles it via a new custom InstallerSet, mirroring the TektonTrigger pattern. Related to SRVKP-12050. Signed-off-by: Jawed khelil <jkhelil@redhat.com> Assisted-by: Claude Sonnet 5 (via Cursor) Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 8855b54 commit e99d0f1

11 files changed

Lines changed: 186 additions & 8 deletions

File tree

charts/tekton-operator/templates/kubernetes-crds.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2829,6 +2829,24 @@ spec:
28292829
type: string
28302830
metrics.taskrun.level:
28312831
type: string
2832+
networkPolicy:
2833+
description: |-
2834+
NetworkPolicy configures NetworkPolicy creation for the proxy-webhook
2835+
workload deployed by TektonPipeline.
2836+
properties:
2837+
disabled:
2838+
description: |-
2839+
Disabled disables all NetworkPolicy creation for this component.
2840+
Existing policies are removed on the next reconcile.
2841+
type: boolean
2842+
policies:
2843+
description: |-
2844+
Policies merges with the operator's default NetworkPolicies by name.
2845+
A key matching a default policy name replaces that default entirely.
2846+
A key not matching any default is added alongside the defaults.
2847+
If nil or empty, all operator defaults are applied unchanged.
2848+
x-kubernetes-preserve-unknown-fields: true
2849+
type: object
28322850
options:
28332851
description: options holds additions fields and these fields will
28342852
be updated on the manifests

config/base/generated-crds/operator.tekton.dev_tektonpipelines.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,24 @@ spec:
196196
type: string
197197
metrics.taskrun.level:
198198
type: string
199+
networkPolicy:
200+
description: |-
201+
NetworkPolicy configures NetworkPolicy creation for the proxy-webhook
202+
workload deployed by TektonPipeline.
203+
properties:
204+
disabled:
205+
description: |-
206+
Disabled disables all NetworkPolicy creation for this component.
207+
Existing policies are removed on the next reconcile.
208+
type: boolean
209+
policies:
210+
description: |-
211+
Policies merges with the operator's default NetworkPolicies by name.
212+
A key matching a default policy name replaces that default entirely.
213+
A key not matching any default is added alongside the defaults.
214+
If nil or empty, all operator defaults are applied unchanged.
215+
x-kubernetes-preserve-unknown-fields: true
216+
type: object
199217
options:
200218
description: options holds additions fields and these fields will
201219
be updated on the manifests

docs/NetworkPolicy.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ weight: 15
77
# NetworkPolicy
88

99
The operator can manage [NetworkPolicy][np] resources for Tekton component workloads.
10-
Currently only TektonTrigger is supported; other components will be added later.
10+
Currently TektonTrigger and TektonPipeline (proxy-webhook only) are supported;
11+
other components will be added later.
1112

1213
Configuration is available via `TektonConfig`:
1314

@@ -30,8 +31,9 @@ spec:
3031
- port: 9000
3132
```
3233
33-
The `networkPolicy` field is propagated from `TektonConfig` to `TektonTrigger`.
34-
Users can also configure it directly on the `TektonTrigger` CR.
34+
The `networkPolicy` field is propagated from `TektonConfig` to `TektonTrigger` and
35+
`TektonPipeline`. Users can also configure it directly on the `TektonTrigger` or
36+
`TektonPipeline` CR.
3537

3638
## Default Policies
3739

@@ -53,6 +55,17 @@ to the operand namespace (e.g. `tekton-pipelines` or `openshift-pipelines`):
5355
| | egress | UDP+TCP/53 or 5353 | DNS resolver pods |
5456
| | egress | TCP/443 or 6443 | API server |
5557
| | egress | TCP/80, 443 | Any (external APIs e.g. GitHub) |
58+
| `tekton-proxy-webhook-default-deny` | deny all | — | All pods with `name: tekton-operator` (proxy-webhook) in the Pipeline target namespace |
59+
| `proxy-webhook` | ingress | TCP/8443 | Any (admission webhook) |
60+
| | egress | UDP+TCP/53 or 5353 | DNS resolver pods |
61+
| | egress | TCP/443 or 6443 | API server |
62+
63+
The `proxy-webhook` policies apply to the TektonPipeline target namespace (e.g.
64+
`tekton-pipelines` or `openshift-pipelines`), where the operator deploys the
65+
proxy-webhook Deployment. They do not cover the operator's own namespace
66+
(`tekton-operator` / `openshift-operators`), which ships fixed, non-configurable
67+
NetworkPolicies as part of the operator's own install manifests/bundle (see
68+
[Operator's own namespace](#operators-own-namespace) below).
5669

5770
### Platform differences
5871

pkg/apis/operator/v1alpha1/tektonconfig_types.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,9 @@ type TektonConfigSpec struct {
135135
// +optional
136136
TargetNamespaceMetadata *NamespaceMetadata `json:"targetNamespaceMetadata,omitempty"`
137137
// NetworkPolicy configures NetworkPolicy resources for the operand namespace.
138-
// This field is propagated to TektonTrigger, which is the only component with
139-
// NetworkPolicy reconciliation implemented. Other components (Pipeline, Chains,
140-
// Results, Dashboard) do not yet act on this field.
138+
// This field is propagated to TektonTrigger and TektonPipeline, which are the
139+
// only components with NetworkPolicy reconciliation implemented. Other
140+
// components (Chains, Results, Dashboard) do not yet act on this field.
141141
// +optional
142142
NetworkPolicy NetworkPolicyConfig `json:"networkPolicy,omitempty"`
143143
}

pkg/apis/operator/v1alpha1/tektonpipeline_types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ type TektonPipelineSpec struct {
5656
// Config holds the configuration for resources created by TektonPipeline
5757
// +optional
5858
Config Config `json:"config,omitempty"`
59+
// NetworkPolicy configures NetworkPolicy creation for the proxy-webhook
60+
// workload deployed by TektonPipeline.
61+
// +optional
62+
NetworkPolicy NetworkPolicyConfig `json:"networkPolicy,omitempty"`
5963
}
6064

6165
// TektonPipelineStatus defines the observed state of TektonPipeline

pkg/apis/operator/v1alpha1/zz_generated.deepcopy.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/reconciler/kubernetes/tektonpipeline/controller.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
tektonPipelineInformer "github.com/tektoncd/operator/pkg/client/injection/informers/operator/v1alpha1/tektonpipeline"
2626
tektonPipelineReconciler "github.com/tektoncd/operator/pkg/client/injection/reconciler/operator/v1alpha1/tektonpipeline"
2727
"github.com/tektoncd/operator/pkg/reconciler/common"
28+
"github.com/tektoncd/operator/pkg/reconciler/common/networkpolicy"
2829
"github.com/tektoncd/operator/pkg/reconciler/kubernetes/tektoninstallerset/client"
2930
"k8s.io/client-go/tools/cache"
3031
kubeclient "knative.dev/pkg/client/injection/kube/client"
@@ -62,12 +63,18 @@ func NewExtendedController(generator common.ExtensionGenerator) injection.Contro
6263
}
6364
tisClient := operatorclient.Get(ctx).OperatorV1alpha1().TektonInstallerSets()
6465

66+
params := networkpolicy.KubernetesPlatformDefaults()
67+
if v1alpha1.IsOpenShiftPlatform() {
68+
params = networkpolicy.OpenShiftPlatformDefaults()
69+
}
70+
6571
c := &Reconciler{
6672
kubeClientSet: kubeclient.Get(ctx),
6773
extension: generator(ctx),
6874
manifest: manifest,
6975
pipelineVersion: pipelineVer,
7076
installerSetClient: client.NewInstallerSetClient(tisClient, operatorVer, pipelineVer, v1alpha1.KindTektonPipeline, metrics),
77+
platformParams: params,
7178
}
7279
impl := tektonPipelineReconciler.NewImpl(ctx, c)
7380

pkg/reconciler/kubernetes/tektonpipeline/finalize.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ func (r *Reconciler) FinalizeKind(ctx context.Context, original *v1alpha1.Tekton
4545
return err
4646
}
4747

48+
if err := r.installerSetClient.CleanupCustomSet(ctx, "pipeline-network-policies"); err != nil {
49+
logger.Error("failed to cleanup pipeline network policies installerset: ", err)
50+
return err
51+
}
52+
4853
if err := r.extension.Finalize(ctx, original); err != nil {
4954
logger.Error("Failed to finalize platform resources: ", err)
5055
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
Copyright 2026 The Tekton Authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package tektonpipeline
18+
19+
import (
20+
"context"
21+
22+
mf "github.com/manifestival/manifestival"
23+
"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
24+
"github.com/tektoncd/operator/pkg/reconciler/common/networkpolicy"
25+
"github.com/tektoncd/operator/pkg/reconciler/kubernetes/tektoninstallerset/client"
26+
networkingv1 "k8s.io/api/networking/v1"
27+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+
"k8s.io/apimachinery/pkg/util/intstr"
29+
)
30+
31+
// proxyWebhookPodSelector matches the proxy-webhook Deployment/Service selector
32+
// (name: tekton-operator) shipped in cmd/{kubernetes,openshift}/operator/kodata/webhook/webhook.yaml.
33+
// This label is the same value used by the main operator Deployment in the operator's own
34+
// namespace (see tektoncd/operator#3227) — harmless here because the proxy-webhook runs in the
35+
// operand namespace (e.g. tekton-pipelines / openshift-pipelines), never the operator namespace.
36+
var proxyWebhookPodSelector = metav1.LabelSelector{
37+
MatchLabels: map[string]string{"name": "tekton-operator"},
38+
}
39+
40+
func pipelineDefaultPolicies(params networkpolicy.PlatformParams) []networkingv1.NetworkPolicy {
41+
webhookPort := intstr.FromInt32(8443)
42+
43+
return []networkingv1.NetworkPolicy{
44+
{
45+
ObjectMeta: metav1.ObjectMeta{Name: "proxy-webhook"},
46+
Spec: networkingv1.NetworkPolicySpec{
47+
PodSelector: proxyWebhookPodSelector,
48+
PolicyTypes: []networkingv1.PolicyType{networkingv1.PolicyTypeIngress, networkingv1.PolicyTypeEgress},
49+
Ingress: []networkingv1.NetworkPolicyIngressRule{
50+
// cidr="" → permissive; restrict via spec.networkPolicy.policies if needed.
51+
networkpolicy.WebhookIngressRule("", webhookPort),
52+
},
53+
Egress: []networkingv1.NetworkPolicyEgressRule{
54+
networkpolicy.DNSEgressRule(params),
55+
networkpolicy.APIServerEgressRule(params),
56+
},
57+
},
58+
},
59+
}
60+
}
61+
62+
// defaultDenyPolicy returns the scoped default-deny for the proxy-webhook pod.
63+
func defaultDenyPolicy() networkingv1.NetworkPolicy {
64+
return networkpolicy.DefaultDenyPolicy("tekton-proxy-webhook-default-deny", proxyWebhookPodSelector)
65+
}
66+
67+
func (r *Reconciler) reconcileNetworkPolicies(ctx context.Context, tp *v1alpha1.TektonPipeline) error {
68+
if tp.Spec.NetworkPolicy.Disabled {
69+
return r.installerSetClient.CleanupCustomSet(ctx, "pipeline-network-policies")
70+
}
71+
defaults := append(
72+
[]networkingv1.NetworkPolicy{defaultDenyPolicy()},
73+
pipelineDefaultPolicies(r.platformParams)...,
74+
)
75+
manifest, err := networkpolicy.Generate(
76+
tp.Spec.NetworkPolicy,
77+
tp.Spec.GetTargetNamespace(),
78+
defaults,
79+
)
80+
if err != nil {
81+
return err
82+
}
83+
return r.installerSetClient.CustomSet(ctx, tp, "pipeline-network-policies", &manifest, passthroughTransform, nil)
84+
}
85+
86+
// passthroughTransform is a no-op FilterAndTransform used for pre-built manifests
87+
// where namespace injection is already handled by Generate.
88+
func passthroughTransform(_ context.Context, m *mf.Manifest, _ v1alpha1.TektonComponent) (*mf.Manifest, error) {
89+
return m, nil
90+
}
91+
92+
// Ensure passthroughTransform satisfies the FilterAndTransform type.
93+
var _ client.FilterAndTransform = passthroughTransform

pkg/reconciler/kubernetes/tektonpipeline/reconcile.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
2525
tektonpipelinereconciler "github.com/tektoncd/operator/pkg/client/injection/reconciler/operator/v1alpha1/tektonpipeline"
2626
"github.com/tektoncd/operator/pkg/reconciler/common"
27+
"github.com/tektoncd/operator/pkg/reconciler/common/networkpolicy"
2728
"github.com/tektoncd/operator/pkg/reconciler/kubernetes/tektoninstallerset/client"
2829
"k8s.io/client-go/kubernetes"
2930
"knative.dev/pkg/apis"
@@ -48,6 +49,8 @@ type Reconciler struct {
4849
kubeClientSet kubernetes.Interface
4950
// version of pipelines which we are installing
5051
pipelineVersion string
52+
// platformParams holds platform-specific values for building NetworkPolicy rules
53+
platformParams networkpolicy.PlatformParams
5154
}
5255

5356
// Check that our Reconciler implements controller.Reconciler
@@ -154,6 +157,16 @@ func (r *Reconciler) ReconcileKind(ctx context.Context, tp *v1alpha1.TektonPipel
154157
}
155158
logger.Debug("Main manifest applied successfully")
156159

160+
if err := r.reconcileNetworkPolicies(ctx, tp); err != nil {
161+
if err == v1alpha1.REQUEUE_EVENT_AFTER {
162+
return err
163+
}
164+
msg := fmt.Sprintf("NetworkPolicy reconciliation failed: %s", err.Error())
165+
logger.Errorw("NetworkPolicy reconciliation failed", "error", err)
166+
tp.Status.MarkInstallerSetNotReady(msg)
167+
return nil
168+
}
169+
157170
logger.Debug("Executing post-reconciliation")
158171
if err := r.extension.PostReconcile(ctx, tp); err != nil {
159172
msg := fmt.Sprintf("PostReconciliation failed: %s", err.Error())

0 commit comments

Comments
 (0)