@@ -17,7 +17,6 @@ limitations under the License.
1717package auth
1818
1919import (
20- "bytes"
2120 "context"
2221 "encoding/json"
2322 "fmt"
@@ -32,6 +31,7 @@ import (
3231 "go.opentelemetry.io/otel"
3332 corev1listers "k8s.io/client-go/listers/core/v1"
3433 "knative.dev/eventing/pkg/eventingtls"
34+ "knative.dev/eventing/pkg/utils"
3535 "knative.dev/pkg/configmap"
3636 "knative.dev/pkg/network"
3737 "knative.dev/pkg/observability/tracing"
@@ -136,6 +136,30 @@ func (v *Verifier) VerifyRequestFromSubject(ctx context.Context, features featur
136136 return nil
137137}
138138
139+ // VerifyRequestFromSubjectsWithFilters verifies AuthN and AuthZ in the request.
140+ // In the AuthZ part it checks if the request comes from the given allowedSubject.
141+ // On verification errors, it sets the responses HTTP status and returns an error.
142+ // This method is similar to VerifyRequestFromSubject() except that
143+ // VerifyRequestFromSubjectsWithFilters() allows to check based on a list of
144+ // subjects with filters.
145+ func (v * Verifier ) VerifyRequestFromSubjectsWithFilters (ctx context.Context , features feature.Flags , requiredOIDCAudience * string , allowedSubjectsWithFilters []SubjectsWithFilters , resourceNamespace string , req * http.Request , resp http.ResponseWriter ) error {
146+ if ! features .IsOIDCAuthentication () {
147+ return nil
148+ }
149+
150+ idToken , err := v .verifyAuthN (ctx , requiredOIDCAudience , req , resp )
151+ if err != nil {
152+ return fmt .Errorf ("authentication of request could not be verified: %w" , err )
153+ }
154+
155+ err = v .verifyAuthZBySubjectsWithFilters (ctx , features , idToken , resourceNamespace , allowedSubjectsWithFilters , req , resp )
156+ if err != nil {
157+ return fmt .Errorf ("authorization of request could not be verified: %w" , err )
158+ }
159+
160+ return nil
161+ }
162+
139163// verifyAuthN verifies if the incoming request contains a correct JWT token
140164func (v * Verifier ) verifyAuthN (ctx context.Context , audience * string , req * http.Request , resp http.ResponseWriter ) (* IDToken , error ) {
141165 token := GetJWTFromHeader (req .Header )
@@ -160,8 +184,20 @@ func (v *Verifier) verifyAuthN(ctx context.Context, audience *string, req *http.
160184
161185// verifyAuthZ verifies if the given idToken is allowed by the resources eventPolicyStatus
162186func (v * Verifier ) verifyAuthZ (ctx context.Context , features feature.Flags , idToken * IDToken , resourceNamespace string , policyRefs []duckv1.AppliedEventPolicyRef , req * http.Request , resp http.ResponseWriter ) error {
163- if len (policyRefs ) > 0 {
164- req , err := copyRequest (req )
187+ subjectsWithFiltersFromApplyingPolicies , err := SubjectWithFiltersFromPolicyRef (v .eventPolicyLister , resourceNamespace , policyRefs )
188+ if err != nil {
189+ resp .WriteHeader (http .StatusInternalServerError )
190+ return fmt .Errorf ("could not get subjects with filters from policy: %w" , err )
191+ }
192+
193+ return v .verifyAuthZBySubjectsWithFilters (ctx , features , idToken , resourceNamespace , subjectsWithFiltersFromApplyingPolicies , req , resp )
194+ }
195+
196+ // verifyAuthZBySubjectsWithFilters verifies if the given idToken is allowed by the resources eventPolicyStatus
197+ // it does the same as verifyAuthZ but taking a subjectWithFilters slice instead
198+ func (v * Verifier ) verifyAuthZBySubjectsWithFilters (ctx context.Context , features feature.Flags , idToken * IDToken , resourceNamespace string , subjectsWithFiltersFromApplyingPolicies []SubjectsWithFilters , req * http.Request , resp http.ResponseWriter ) error {
199+ if len (subjectsWithFiltersFromApplyingPolicies ) > 0 {
200+ req , err := utils .CopyRequest (req )
165201 if err != nil {
166202 resp .WriteHeader (http .StatusInternalServerError )
167203 return fmt .Errorf ("failed to copy request body: %w" , err )
@@ -176,38 +212,26 @@ func (v *Verifier) verifyAuthZ(ctx context.Context, features feature.Flags, idTo
176212 return fmt .Errorf ("failed to decode event from request: %w" , err )
177213 }
178214
179- subjectsWithFiltersFromApplyingPolicies := []subjectsWithFilters {}
180- for _ , p := range policyRefs {
181- policy , err := v .eventPolicyLister .EventPolicies (resourceNamespace ).Get (p .Name )
182- if err != nil {
183- resp .WriteHeader (http .StatusInternalServerError )
184- return fmt .Errorf ("failed to get eventPolicy: %w" , err )
185- }
186-
187- subjectsWithFiltersFromApplyingPolicies = append (subjectsWithFiltersFromApplyingPolicies , subjectsWithFilters {subjects : policy .Status .From , filters : policy .Spec .Filters })
188- }
189-
190215 if ! SubjectAndFiltersPass (ctx , idToken .Subject , subjectsWithFiltersFromApplyingPolicies , event , v .logger ) {
191216 resp .WriteHeader (http .StatusForbidden )
192217 return fmt .Errorf ("token is from subject %q, but only %#v are part of applying event policies" , idToken .Subject , subjectsWithFiltersFromApplyingPolicies )
193218 }
194219
195220 return nil
196- } else {
197- if features .IsAuthorizationDefaultModeDenyAll () {
198- resp .WriteHeader (http .StatusForbidden )
199- return fmt .Errorf ("no event policies apply for resource and %s is set to %s" , feature .AuthorizationDefaultMode , feature .AuthorizationDenyAll )
200-
201- } else if features .IsAuthorizationDefaultModeSameNamespace () {
202- if ! strings .HasPrefix (idToken .Subject , fmt .Sprintf ("%s:%s:" , kubernetesServiceAccountPrefix , resourceNamespace )) {
203- resp .WriteHeader (http .StatusForbidden )
204- return fmt .Errorf ("no policies apply for resource. %s is set to %s, but token is from subject %q, which is not part of %q namespace" , feature .AuthorizationDefaultMode , feature .AuthorizationDenyAll , idToken .Subject , resourceNamespace )
205- }
221+ }
206222
207- return nil
223+ if features .IsAuthorizationDefaultModeDenyAll () {
224+ resp .WriteHeader (http .StatusForbidden )
225+ return fmt .Errorf ("no event policies apply for resource and %s is set to %s" , feature .AuthorizationDefaultMode , feature .AuthorizationDenyAll )
226+ } else if features .IsAuthorizationDefaultModeSameNamespace () {
227+ if ! strings .HasPrefix (idToken .Subject , fmt .Sprintf ("%s:%s:" , kubernetesServiceAccountPrefix , resourceNamespace )) {
228+ resp .WriteHeader (http .StatusForbidden )
229+ return fmt .Errorf ("no policies apply for resource. %s is set to %s, but token is from subject %q, which is not part of %q namespace" , feature .AuthorizationDefaultMode , feature .AuthorizationDenyAll , idToken .Subject , resourceNamespace )
208230 }
209- // else: allow all
231+
232+ return nil
210233 }
234+ // else: allow all
211235
212236 return nil
213237}
@@ -335,35 +359,6 @@ func (v *Verifier) getKubernetesOIDCDiscovery(features feature.Flags, client *ht
335359 return openIdConfig , nil
336360}
337361
338- // copyRequest makes a copy of the http request which can be consumed as needed, leaving the original request
339- // able to be consumed as well.
340- func copyRequest (req * http.Request ) (* http.Request , error ) {
341- // check if we actually need to copy the body, otherwise we can return the original request
342- if req .Body == nil || req .Body == http .NoBody {
343- return req , nil
344- }
345-
346- var buf bytes.Buffer
347- if _ , err := buf .ReadFrom (req .Body ); err != nil {
348- return nil , fmt .Errorf ("failed to read request body while copying it: %w" , err )
349- }
350-
351- if err := req .Body .Close (); err != nil {
352- return nil , fmt .Errorf ("failed to close original request body ready while copying request: %w" , err )
353- }
354-
355- // set the original request body to be readable again
356- req .Body = io .NopCloser (& buf )
357-
358- // return a new request with a readable body and same headers as the original
359- // we don't need to set any other fields as cloudevents only uses the headers
360- // and body to construct the Message/Event.
361- return & http.Request {
362- Header : req .Header ,
363- Body : io .NopCloser (bytes .NewReader (buf .Bytes ())),
364- }, nil
365- }
366-
367362type openIDMetadata struct {
368363 Issuer string `json:"issuer"`
369364 JWKSURI string `json:"jwks_uri"`
@@ -372,7 +367,22 @@ type openIDMetadata struct {
372367 SigningAlgs []string `json:"id_token_signing_alg_values_supported"`
373368}
374369
375- type subjectsWithFilters struct {
376- filters []eventingv1.SubscriptionsAPIFilter
377- subjects []string
370+ func SubjectWithFiltersFromPolicyRef (eventPolicyLister listerseventingv1alpha1.EventPolicyLister , resourceNamespace string , policyRefs []duckv1.AppliedEventPolicyRef ) ([]SubjectsWithFilters , error ) {
371+ subjectsWithFiltersFromApplyingPolicies := make ([]SubjectsWithFilters , 0 , len (policyRefs ))
372+
373+ for _ , p := range policyRefs {
374+ policy , err := eventPolicyLister .EventPolicies (resourceNamespace ).Get (p .Name )
375+ if err != nil {
376+ return nil , fmt .Errorf ("failed to get eventPolicy: %w" , err )
377+ }
378+
379+ subjectsWithFiltersFromApplyingPolicies = append (subjectsWithFiltersFromApplyingPolicies , SubjectsWithFilters {Subjects : policy .Status .From , Filters : policy .Spec .Filters })
380+ }
381+
382+ return subjectsWithFiltersFromApplyingPolicies , nil
383+ }
384+
385+ type SubjectsWithFilters struct {
386+ Filters []eventingv1.SubscriptionsAPIFilter `json:"filters,omitempty"`
387+ Subjects []string `json:"subjects,omitempty"`
378388}
0 commit comments