@@ -33,7 +33,6 @@ import (
3333 "go.uber.org/zap"
3434 "golang.org/x/sync/errgroup"
3535 admissionv1 "k8s.io/api/admission/v1"
36- corelisters "k8s.io/client-go/listers/core/v1"
3736 "knative.dev/pkg/logging"
3837 "knative.dev/pkg/network"
3938 "knative.dev/pkg/system"
@@ -50,6 +49,7 @@ type Options struct {
5049 // server key/cert are used to serve the webhook and the CA cert
5150 // is provided to k8s apiserver during admission controller
5251 // registration.
52+ // If no SecretName is provided, then the webhook serves without TLS.
5353 SecretName string
5454
5555 // Port where the webhook is served. Per k8s admission
@@ -87,8 +87,10 @@ type Webhook struct {
8787 // before shutting down.
8888 gracePeriod time.Duration
8989
90- mux http.ServeMux
91- secretlister corelisters.SecretLister
90+ mux http.ServeMux
91+
92+ // The TLS configuration to use for serving (or nil for non-TLS)
93+ tlsConfig * tls.Config
9294}
9395
9496// New constructs a Webhook
@@ -104,13 +106,6 @@ func New(
104106 }
105107 }()
106108
107- // Injection is too aggressive for this case because by simply linking this
108- // library we force consumers to have secret access. If we require that one
109- // of the admission controllers' informers *also* require the secret
110- // informer, then we can fetch the shared informer factory here and produce
111- // a new secret informer from it.
112- secretInformer := kubeinformerfactory .Get (ctx ).Core ().V1 ().Secrets ()
113-
114109 opts := GetOptions (ctx )
115110 if opts == nil {
116111 return nil , errors .New ("context must have Options specified" )
@@ -128,11 +123,51 @@ func New(
128123 syncCtx , cancel := context .WithCancel (context .Background ())
129124
130125 webhook = & Webhook {
131- Options : * opts ,
132- secretlister : secretInformer .Lister (),
133- Logger : logger ,
134- synced : cancel ,
135- gracePeriod : network .DefaultDrainTimeout ,
126+ Options : * opts ,
127+ Logger : logger ,
128+ synced : cancel ,
129+ gracePeriod : network .DefaultDrainTimeout ,
130+ }
131+
132+ if opts .SecretName != "" {
133+ // Injection is too aggressive for this case because by simply linking this
134+ // library we force consumers to have secret access. If we require that one
135+ // of the admission controllers' informers *also* require the secret
136+ // informer, then we can fetch the shared informer factory here and produce
137+ // a new secret informer from it.
138+ secretInformer := kubeinformerfactory .Get (ctx ).Core ().V1 ().Secrets ()
139+
140+ webhook .tlsConfig = & tls.Config {
141+ MinVersion : tls .VersionTLS12 ,
142+
143+ // If we return (nil, error) the client sees - 'tls: internal error"
144+ // If we return (nil, nil) the client sees - 'tls: no certificates configured'
145+ //
146+ // We'll return (nil, nil) when we don't find a certificate
147+ GetCertificate : func (* tls.ClientHelloInfo ) (* tls.Certificate , error ) {
148+ secret , err := secretInformer .Lister ().Secrets (system .Namespace ()).Get (opts .SecretName )
149+ if err != nil {
150+ logger .Errorw ("failed to fetch secret" , zap .Error (err ))
151+ return nil , nil
152+ }
153+
154+ serverKey , ok := secret .Data [certresources .ServerKey ]
155+ if ! ok {
156+ logger .Warn ("server key missing" )
157+ return nil , nil
158+ }
159+ serverCert , ok := secret .Data [certresources .ServerCert ]
160+ if ! ok {
161+ logger .Warn ("server cert missing" )
162+ return nil , nil
163+ }
164+ cert , err := tls .X509KeyPair (serverCert , serverKey )
165+ if err != nil {
166+ return nil , err
167+ }
168+ return & cert , nil
169+ },
170+ }
136171 }
137172
138173 webhook .mux .HandleFunc ("/" , func (w http.ResponseWriter , r * http.Request ) {
@@ -176,46 +211,23 @@ func (wh *Webhook) Run(stop <-chan struct{}) error {
176211 }
177212
178213 server := & http.Server {
179- Handler : drainer ,
180- Addr : fmt .Sprint (":" , wh .Options .Port ),
181- TLSConfig : & tls.Config {
182- MinVersion : tls .VersionTLS12 ,
183-
184- // If we return (nil, error) the client sees - 'tls: internal error"
185- // If we return (nil, nil) the client sees - 'tls: no certificates configured'
186- //
187- // We'll return (nil, nil) when we don't find a certificate
188- GetCertificate : func (* tls.ClientHelloInfo ) (* tls.Certificate , error ) {
189- secret , err := wh .secretlister .Secrets (system .Namespace ()).Get (wh .Options .SecretName )
190- if err != nil {
191- logger .Errorw ("failed to fetch secret" , zap .Error (err ))
192- return nil , nil
193- }
194-
195- serverKey , ok := secret .Data [certresources .ServerKey ]
196- if ! ok {
197- logger .Warn ("server key missing" )
198- return nil , nil
199- }
200- serverCert , ok := secret .Data [certresources .ServerCert ]
201- if ! ok {
202- logger .Warn ("server cert missing" )
203- return nil , nil
204- }
205- cert , err := tls .X509KeyPair (serverCert , serverKey )
206- if err != nil {
207- return nil , err
208- }
209- return & cert , nil
210- },
211- },
214+ Handler : drainer ,
215+ Addr : fmt .Sprint (":" , wh .Options .Port ),
216+ TLSConfig : wh .tlsConfig ,
212217 }
213218
214219 eg , ctx := errgroup .WithContext (ctx )
215220 eg .Go (func () error {
216- if err := server .ListenAndServeTLS ("" , "" ); err != nil && ! errors .Is (err , http .ErrServerClosed ) {
217- logger .Errorw ("ListenAndServeTLS for admission webhook returned error" , zap .Error (err ))
218- return err
221+ if server .TLSConfig != nil {
222+ if err := server .ListenAndServeTLS ("" , "" ); err != nil && ! errors .Is (err , http .ErrServerClosed ) {
223+ logger .Errorw ("ListenAndServeTLS for admission webhook returned error" , zap .Error (err ))
224+ return err
225+ }
226+ } else {
227+ if err := server .ListenAndServe (); err != nil && ! errors .Is (err , http .ErrServerClosed ) {
228+ logger .Errorw ("ListenAndServe for admission webhook returned error" , zap .Error (err ))
229+ return err
230+ }
219231 }
220232 return nil
221233 })
0 commit comments