-
Notifications
You must be signed in to change notification settings - Fork 505
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* certExtractor filter * remove unused import * Apply suggestions from code review Co-authored-by: Bomin Zhang <[email protected]> * Update pkg/filter/certextractor/spec.go Co-authored-by: Bomin Zhang <[email protected]> * revert changes in HTTPContext * make fmt * remove DefaultHeaderKey * add filter to registry Co-authored-by: Bomin Zhang <[email protected]>
- Loading branch information
Showing
4 changed files
with
406 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
/* | ||
* Copyright (c) 2017, MegaEase | ||
* All rights reserved. | ||
* | ||
* 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 | ||
* | ||
* http://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. | ||
*/ | ||
|
||
package certextractor | ||
|
||
import ( | ||
"crypto/x509/pkix" | ||
"fmt" | ||
|
||
httpcontext "github.com/megaease/easegress/pkg/context" | ||
"github.com/megaease/easegress/pkg/object/httppipeline" | ||
) | ||
|
||
const ( | ||
// Kind is the kind of CertExtractor. | ||
Kind = "CertExtractor" | ||
) | ||
|
||
var results = []string{} | ||
|
||
func init() { | ||
httppipeline.Register(&CertExtractor{}) | ||
} | ||
|
||
type ( | ||
// CertExtractor extracts given field from TLS certificates and sets it to request headers. | ||
CertExtractor struct { | ||
filterSpec *httppipeline.FilterSpec | ||
spec *Spec | ||
|
||
headerKey string | ||
} | ||
|
||
// Spec describes the CertExtractor. | ||
Spec struct { | ||
CertIndex int16 `yaml:"certIndex" jsonschema:"required"` | ||
Target string `yaml:"target" jsonschema:"required,enum=subject,enum=issuer"` | ||
// Different field options listed here https://pkg.go.dev/crypto/x509/pkix#Name | ||
Field string `yaml:"field" jsonschema:"required,enum=Country,enum=Organization,enum=OrganizationalUnit,enum=Locality,enum=Province,enum=StreetAddress,enum=PostalCode,enum=SerialNumber,enum=CommonName"` | ||
HeaderKey string `yaml:"headerKey" jsonschema:"required"` | ||
} | ||
) | ||
|
||
// Validate is dummy as yaml rules already validate Spec. | ||
func (spec *Spec) Validate() error { return nil } | ||
|
||
// Kind returns the kind of CertExtractor. | ||
func (ce *CertExtractor) Kind() string { | ||
return Kind | ||
} | ||
|
||
// DefaultSpec returns the default spec of CertExtractor. | ||
func (ce *CertExtractor) DefaultSpec() interface{} { | ||
return &Spec{} | ||
} | ||
|
||
// Description returns the description of CertExtractor. | ||
func (ce *CertExtractor) Description() string { | ||
return "CertExtractor extracts given field from TLS certificates and sets it to request headers." | ||
} | ||
|
||
// Results returns the results of CertExtractor. | ||
func (ce *CertExtractor) Results() []string { | ||
return results | ||
} | ||
|
||
// Init initializes CertExtractor. | ||
func (ce *CertExtractor) Init(filterSpec *httppipeline.FilterSpec) { | ||
ce.filterSpec, ce.spec = filterSpec, filterSpec.FilterSpec().(*Spec) | ||
|
||
ce.headerKey = fmt.Sprintf("tls-%s-%s", ce.spec.Target, ce.spec.Field) | ||
if ce.spec.HeaderKey != "" { | ||
ce.headerKey = ce.spec.HeaderKey | ||
} | ||
} | ||
|
||
// Inherit inherits previous generation of CertExtractor. | ||
func (ce *CertExtractor) Inherit(filterSpec *httppipeline.FilterSpec, previousGeneration httppipeline.Filter) { | ||
previousGeneration.Close() | ||
ce.Init(filterSpec) | ||
} | ||
|
||
// Close closes CertExtractor. | ||
func (ce *CertExtractor) Close() {} | ||
|
||
// Handle retrieves header values and sets request headers. | ||
func (ce *CertExtractor) Handle(ctx httpcontext.HTTPContext) string { | ||
result := ce.handle(ctx) | ||
return ctx.CallNextHandler(result) | ||
} | ||
|
||
// CertExtractor extracts given field from TLS certificates and sets it to request headers. | ||
func (ce *CertExtractor) handle(ctx httpcontext.HTTPContext) string { | ||
r := ctx.Request() | ||
connectionState := r.Std().TLS | ||
if connectionState == nil { | ||
return "" | ||
} | ||
|
||
certs := connectionState.PeerCertificates | ||
if certs == nil || len(certs) < 1 { | ||
return "" | ||
} | ||
|
||
n := int16(len(certs)) | ||
// positive ce.spec.CertIndex from the beginning, negative from the end | ||
relativeIndex := ce.spec.CertIndex % n | ||
index := (n + relativeIndex) % n | ||
cert := certs[index] | ||
|
||
var target pkix.Name | ||
if ce.spec.Target == "subject" { | ||
target = cert.Subject | ||
} else { | ||
target = cert.Issuer | ||
} | ||
|
||
var result []string | ||
switch ce.spec.Field { | ||
case "Country": | ||
result = target.Country | ||
case "Organization": | ||
result = target.Organization | ||
case "OrganizationalUnit": | ||
result = target.OrganizationalUnit | ||
case "Locality": | ||
result = target.Locality | ||
case "Province": | ||
result = target.Province | ||
case "StreetAddress": | ||
result = target.StreetAddress | ||
case "PostalCode": | ||
result = target.PostalCode | ||
case "SerialNumber": | ||
result = append(result, target.SerialNumber) | ||
case "CommonName": | ||
result = append(result, target.CommonName) | ||
} | ||
for _, res := range result { | ||
if res != "" { | ||
r.Header().Add(ce.headerKey, res) | ||
} | ||
} | ||
return "" | ||
} | ||
|
||
// Status returns status. | ||
func (ce *CertExtractor) Status() interface{} { return nil } |
Oops, something went wrong.