mirror of
https://github.com/cloudflare/cloudflared.git
synced 2025-05-11 22:56:34 +00:00

This adds a new verifier interface that can be attached to ingress.Rule. This would act as a middleware layer that gets executed at the start of proxy.ProxyHTTP. A jwt validator implementation for this verifier is also provided. The validator downloads the public key from the access teams endpoint and uses it to verify the JWT sent to cloudflared with the audtag (clientID) information provided in the config.
560 lines
14 KiB
Go
560 lines
14 KiB
Go
// Copyright 2015 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package jsonpb
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
"reflect"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
"google.golang.org/protobuf/encoding/protojson"
|
|
protoV2 "google.golang.org/protobuf/proto"
|
|
"google.golang.org/protobuf/reflect/protoreflect"
|
|
"google.golang.org/protobuf/reflect/protoregistry"
|
|
)
|
|
|
|
const wrapJSONMarshalV2 = false
|
|
|
|
// Marshaler is a configurable object for marshaling protocol buffer messages
|
|
// to the specified JSON representation.
|
|
type Marshaler struct {
|
|
// OrigName specifies whether to use the original protobuf name for fields.
|
|
OrigName bool
|
|
|
|
// EnumsAsInts specifies whether to render enum values as integers,
|
|
// as opposed to string values.
|
|
EnumsAsInts bool
|
|
|
|
// EmitDefaults specifies whether to render fields with zero values.
|
|
EmitDefaults bool
|
|
|
|
// Indent controls whether the output is compact or not.
|
|
// If empty, the output is compact JSON. Otherwise, every JSON object
|
|
// entry and JSON array value will be on its own line.
|
|
// Each line will be preceded by repeated copies of Indent, where the
|
|
// number of copies is the current indentation depth.
|
|
Indent string
|
|
|
|
// AnyResolver is used to resolve the google.protobuf.Any well-known type.
|
|
// If unset, the global registry is used by default.
|
|
AnyResolver AnyResolver
|
|
}
|
|
|
|
// JSONPBMarshaler is implemented by protobuf messages that customize the
|
|
// way they are marshaled to JSON. Messages that implement this should also
|
|
// implement JSONPBUnmarshaler so that the custom format can be parsed.
|
|
//
|
|
// The JSON marshaling must follow the proto to JSON specification:
|
|
// https://developers.google.com/protocol-buffers/docs/proto3#json
|
|
//
|
|
// Deprecated: Custom types should implement protobuf reflection instead.
|
|
type JSONPBMarshaler interface {
|
|
MarshalJSONPB(*Marshaler) ([]byte, error)
|
|
}
|
|
|
|
// Marshal serializes a protobuf message as JSON into w.
|
|
func (jm *Marshaler) Marshal(w io.Writer, m proto.Message) error {
|
|
b, err := jm.marshal(m)
|
|
if len(b) > 0 {
|
|
if _, err := w.Write(b); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
|
|
// MarshalToString serializes a protobuf message as JSON in string form.
|
|
func (jm *Marshaler) MarshalToString(m proto.Message) (string, error) {
|
|
b, err := jm.marshal(m)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(b), nil
|
|
}
|
|
|
|
func (jm *Marshaler) marshal(m proto.Message) ([]byte, error) {
|
|
v := reflect.ValueOf(m)
|
|
if m == nil || (v.Kind() == reflect.Ptr && v.IsNil()) {
|
|
return nil, errors.New("Marshal called with nil")
|
|
}
|
|
|
|
// Check for custom marshalers first since they may not properly
|
|
// implement protobuf reflection that the logic below relies on.
|
|
if jsm, ok := m.(JSONPBMarshaler); ok {
|
|
return jsm.MarshalJSONPB(jm)
|
|
}
|
|
|
|
if wrapJSONMarshalV2 {
|
|
opts := protojson.MarshalOptions{
|
|
UseProtoNames: jm.OrigName,
|
|
UseEnumNumbers: jm.EnumsAsInts,
|
|
EmitUnpopulated: jm.EmitDefaults,
|
|
Indent: jm.Indent,
|
|
}
|
|
if jm.AnyResolver != nil {
|
|
opts.Resolver = anyResolver{jm.AnyResolver}
|
|
}
|
|
return opts.Marshal(proto.MessageReflect(m).Interface())
|
|
} else {
|
|
// Check for unpopulated required fields first.
|
|
m2 := proto.MessageReflect(m)
|
|
if err := protoV2.CheckInitialized(m2.Interface()); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
w := jsonWriter{Marshaler: jm}
|
|
err := w.marshalMessage(m2, "", "")
|
|
return w.buf, err
|
|
}
|
|
}
|
|
|
|
type jsonWriter struct {
|
|
*Marshaler
|
|
buf []byte
|
|
}
|
|
|
|
func (w *jsonWriter) write(s string) {
|
|
w.buf = append(w.buf, s...)
|
|
}
|
|
|
|
func (w *jsonWriter) marshalMessage(m protoreflect.Message, indent, typeURL string) error {
|
|
if jsm, ok := proto.MessageV1(m.Interface()).(JSONPBMarshaler); ok {
|
|
b, err := jsm.MarshalJSONPB(w.Marshaler)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if typeURL != "" {
|
|
// we are marshaling this object to an Any type
|
|
var js map[string]*json.RawMessage
|
|
if err = json.Unmarshal(b, &js); err != nil {
|
|
return fmt.Errorf("type %T produced invalid JSON: %v", m.Interface(), err)
|
|
}
|
|
turl, err := json.Marshal(typeURL)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to marshal type URL %q to JSON: %v", typeURL, err)
|
|
}
|
|
js["@type"] = (*json.RawMessage)(&turl)
|
|
if b, err = json.Marshal(js); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
w.write(string(b))
|
|
return nil
|
|
}
|
|
|
|
md := m.Descriptor()
|
|
fds := md.Fields()
|
|
|
|
// Handle well-known types.
|
|
const secondInNanos = int64(time.Second / time.Nanosecond)
|
|
switch wellKnownType(md.FullName()) {
|
|
case "Any":
|
|
return w.marshalAny(m, indent)
|
|
case "BoolValue", "BytesValue", "StringValue",
|
|
"Int32Value", "UInt32Value", "FloatValue",
|
|
"Int64Value", "UInt64Value", "DoubleValue":
|
|
fd := fds.ByNumber(1)
|
|
return w.marshalValue(fd, m.Get(fd), indent)
|
|
case "Duration":
|
|
const maxSecondsInDuration = 315576000000
|
|
// "Generated output always contains 0, 3, 6, or 9 fractional digits,
|
|
// depending on required precision."
|
|
s := m.Get(fds.ByNumber(1)).Int()
|
|
ns := m.Get(fds.ByNumber(2)).Int()
|
|
if s < -maxSecondsInDuration || s > maxSecondsInDuration {
|
|
return fmt.Errorf("seconds out of range %v", s)
|
|
}
|
|
if ns <= -secondInNanos || ns >= secondInNanos {
|
|
return fmt.Errorf("ns out of range (%v, %v)", -secondInNanos, secondInNanos)
|
|
}
|
|
if (s > 0 && ns < 0) || (s < 0 && ns > 0) {
|
|
return errors.New("signs of seconds and nanos do not match")
|
|
}
|
|
var sign string
|
|
if s < 0 || ns < 0 {
|
|
sign, s, ns = "-", -1*s, -1*ns
|
|
}
|
|
x := fmt.Sprintf("%s%d.%09d", sign, s, ns)
|
|
x = strings.TrimSuffix(x, "000")
|
|
x = strings.TrimSuffix(x, "000")
|
|
x = strings.TrimSuffix(x, ".000")
|
|
w.write(fmt.Sprintf(`"%vs"`, x))
|
|
return nil
|
|
case "Timestamp":
|
|
// "RFC 3339, where generated output will always be Z-normalized
|
|
// and uses 0, 3, 6 or 9 fractional digits."
|
|
s := m.Get(fds.ByNumber(1)).Int()
|
|
ns := m.Get(fds.ByNumber(2)).Int()
|
|
if ns < 0 || ns >= secondInNanos {
|
|
return fmt.Errorf("ns out of range [0, %v)", secondInNanos)
|
|
}
|
|
t := time.Unix(s, ns).UTC()
|
|
// time.RFC3339Nano isn't exactly right (we need to get 3/6/9 fractional digits).
|
|
x := t.Format("2006-01-02T15:04:05.000000000")
|
|
x = strings.TrimSuffix(x, "000")
|
|
x = strings.TrimSuffix(x, "000")
|
|
x = strings.TrimSuffix(x, ".000")
|
|
w.write(fmt.Sprintf(`"%vZ"`, x))
|
|
return nil
|
|
case "Value":
|
|
// JSON value; which is a null, number, string, bool, object, or array.
|
|
od := md.Oneofs().Get(0)
|
|
fd := m.WhichOneof(od)
|
|
if fd == nil {
|
|
return errors.New("nil Value")
|
|
}
|
|
return w.marshalValue(fd, m.Get(fd), indent)
|
|
case "Struct", "ListValue":
|
|
// JSON object or array.
|
|
fd := fds.ByNumber(1)
|
|
return w.marshalValue(fd, m.Get(fd), indent)
|
|
}
|
|
|
|
w.write("{")
|
|
if w.Indent != "" {
|
|
w.write("\n")
|
|
}
|
|
|
|
firstField := true
|
|
if typeURL != "" {
|
|
if err := w.marshalTypeURL(indent, typeURL); err != nil {
|
|
return err
|
|
}
|
|
firstField = false
|
|
}
|
|
|
|
for i := 0; i < fds.Len(); {
|
|
fd := fds.Get(i)
|
|
if od := fd.ContainingOneof(); od != nil {
|
|
fd = m.WhichOneof(od)
|
|
i += od.Fields().Len()
|
|
if fd == nil {
|
|
continue
|
|
}
|
|
} else {
|
|
i++
|
|
}
|
|
|
|
v := m.Get(fd)
|
|
|
|
if !m.Has(fd) {
|
|
if !w.EmitDefaults || fd.ContainingOneof() != nil {
|
|
continue
|
|
}
|
|
if fd.Cardinality() != protoreflect.Repeated && (fd.Message() != nil || fd.Syntax() == protoreflect.Proto2) {
|
|
v = protoreflect.Value{} // use "null" for singular messages or proto2 scalars
|
|
}
|
|
}
|
|
|
|
if !firstField {
|
|
w.writeComma()
|
|
}
|
|
if err := w.marshalField(fd, v, indent); err != nil {
|
|
return err
|
|
}
|
|
firstField = false
|
|
}
|
|
|
|
// Handle proto2 extensions.
|
|
if md.ExtensionRanges().Len() > 0 {
|
|
// Collect a sorted list of all extension descriptor and values.
|
|
type ext struct {
|
|
desc protoreflect.FieldDescriptor
|
|
val protoreflect.Value
|
|
}
|
|
var exts []ext
|
|
m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
|
|
if fd.IsExtension() {
|
|
exts = append(exts, ext{fd, v})
|
|
}
|
|
return true
|
|
})
|
|
sort.Slice(exts, func(i, j int) bool {
|
|
return exts[i].desc.Number() < exts[j].desc.Number()
|
|
})
|
|
|
|
for _, ext := range exts {
|
|
if !firstField {
|
|
w.writeComma()
|
|
}
|
|
if err := w.marshalField(ext.desc, ext.val, indent); err != nil {
|
|
return err
|
|
}
|
|
firstField = false
|
|
}
|
|
}
|
|
|
|
if w.Indent != "" {
|
|
w.write("\n")
|
|
w.write(indent)
|
|
}
|
|
w.write("}")
|
|
return nil
|
|
}
|
|
|
|
func (w *jsonWriter) writeComma() {
|
|
if w.Indent != "" {
|
|
w.write(",\n")
|
|
} else {
|
|
w.write(",")
|
|
}
|
|
}
|
|
|
|
func (w *jsonWriter) marshalAny(m protoreflect.Message, indent string) error {
|
|
// "If the Any contains a value that has a special JSON mapping,
|
|
// it will be converted as follows: {"@type": xxx, "value": yyy}.
|
|
// Otherwise, the value will be converted into a JSON object,
|
|
// and the "@type" field will be inserted to indicate the actual data type."
|
|
md := m.Descriptor()
|
|
typeURL := m.Get(md.Fields().ByNumber(1)).String()
|
|
rawVal := m.Get(md.Fields().ByNumber(2)).Bytes()
|
|
|
|
var m2 protoreflect.Message
|
|
if w.AnyResolver != nil {
|
|
mi, err := w.AnyResolver.Resolve(typeURL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
m2 = proto.MessageReflect(mi)
|
|
} else {
|
|
mt, err := protoregistry.GlobalTypes.FindMessageByURL(typeURL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
m2 = mt.New()
|
|
}
|
|
|
|
if err := protoV2.Unmarshal(rawVal, m2.Interface()); err != nil {
|
|
return err
|
|
}
|
|
|
|
if wellKnownType(m2.Descriptor().FullName()) == "" {
|
|
return w.marshalMessage(m2, indent, typeURL)
|
|
}
|
|
|
|
w.write("{")
|
|
if w.Indent != "" {
|
|
w.write("\n")
|
|
}
|
|
if err := w.marshalTypeURL(indent, typeURL); err != nil {
|
|
return err
|
|
}
|
|
w.writeComma()
|
|
if w.Indent != "" {
|
|
w.write(indent)
|
|
w.write(w.Indent)
|
|
w.write(`"value": `)
|
|
} else {
|
|
w.write(`"value":`)
|
|
}
|
|
if err := w.marshalMessage(m2, indent+w.Indent, ""); err != nil {
|
|
return err
|
|
}
|
|
if w.Indent != "" {
|
|
w.write("\n")
|
|
w.write(indent)
|
|
}
|
|
w.write("}")
|
|
return nil
|
|
}
|
|
|
|
func (w *jsonWriter) marshalTypeURL(indent, typeURL string) error {
|
|
if w.Indent != "" {
|
|
w.write(indent)
|
|
w.write(w.Indent)
|
|
}
|
|
w.write(`"@type":`)
|
|
if w.Indent != "" {
|
|
w.write(" ")
|
|
}
|
|
b, err := json.Marshal(typeURL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
w.write(string(b))
|
|
return nil
|
|
}
|
|
|
|
// marshalField writes field description and value to the Writer.
|
|
func (w *jsonWriter) marshalField(fd protoreflect.FieldDescriptor, v protoreflect.Value, indent string) error {
|
|
if w.Indent != "" {
|
|
w.write(indent)
|
|
w.write(w.Indent)
|
|
}
|
|
w.write(`"`)
|
|
switch {
|
|
case fd.IsExtension():
|
|
// For message set, use the fname of the message as the extension name.
|
|
name := string(fd.FullName())
|
|
if isMessageSet(fd.ContainingMessage()) {
|
|
name = strings.TrimSuffix(name, ".message_set_extension")
|
|
}
|
|
|
|
w.write("[" + name + "]")
|
|
case w.OrigName:
|
|
name := string(fd.Name())
|
|
if fd.Kind() == protoreflect.GroupKind {
|
|
name = string(fd.Message().Name())
|
|
}
|
|
w.write(name)
|
|
default:
|
|
w.write(string(fd.JSONName()))
|
|
}
|
|
w.write(`":`)
|
|
if w.Indent != "" {
|
|
w.write(" ")
|
|
}
|
|
return w.marshalValue(fd, v, indent)
|
|
}
|
|
|
|
func (w *jsonWriter) marshalValue(fd protoreflect.FieldDescriptor, v protoreflect.Value, indent string) error {
|
|
switch {
|
|
case fd.IsList():
|
|
w.write("[")
|
|
comma := ""
|
|
lv := v.List()
|
|
for i := 0; i < lv.Len(); i++ {
|
|
w.write(comma)
|
|
if w.Indent != "" {
|
|
w.write("\n")
|
|
w.write(indent)
|
|
w.write(w.Indent)
|
|
w.write(w.Indent)
|
|
}
|
|
if err := w.marshalSingularValue(fd, lv.Get(i), indent+w.Indent); err != nil {
|
|
return err
|
|
}
|
|
comma = ","
|
|
}
|
|
if w.Indent != "" {
|
|
w.write("\n")
|
|
w.write(indent)
|
|
w.write(w.Indent)
|
|
}
|
|
w.write("]")
|
|
return nil
|
|
case fd.IsMap():
|
|
kfd := fd.MapKey()
|
|
vfd := fd.MapValue()
|
|
mv := v.Map()
|
|
|
|
// Collect a sorted list of all map keys and values.
|
|
type entry struct{ key, val protoreflect.Value }
|
|
var entries []entry
|
|
mv.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {
|
|
entries = append(entries, entry{k.Value(), v})
|
|
return true
|
|
})
|
|
sort.Slice(entries, func(i, j int) bool {
|
|
switch kfd.Kind() {
|
|
case protoreflect.BoolKind:
|
|
return !entries[i].key.Bool() && entries[j].key.Bool()
|
|
case protoreflect.Int32Kind, protoreflect.Sint32Kind, protoreflect.Sfixed32Kind, protoreflect.Int64Kind, protoreflect.Sint64Kind, protoreflect.Sfixed64Kind:
|
|
return entries[i].key.Int() < entries[j].key.Int()
|
|
case protoreflect.Uint32Kind, protoreflect.Fixed32Kind, protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
|
|
return entries[i].key.Uint() < entries[j].key.Uint()
|
|
case protoreflect.StringKind:
|
|
return entries[i].key.String() < entries[j].key.String()
|
|
default:
|
|
panic("invalid kind")
|
|
}
|
|
})
|
|
|
|
w.write(`{`)
|
|
comma := ""
|
|
for _, entry := range entries {
|
|
w.write(comma)
|
|
if w.Indent != "" {
|
|
w.write("\n")
|
|
w.write(indent)
|
|
w.write(w.Indent)
|
|
w.write(w.Indent)
|
|
}
|
|
|
|
s := fmt.Sprint(entry.key.Interface())
|
|
b, err := json.Marshal(s)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
w.write(string(b))
|
|
|
|
w.write(`:`)
|
|
if w.Indent != "" {
|
|
w.write(` `)
|
|
}
|
|
|
|
if err := w.marshalSingularValue(vfd, entry.val, indent+w.Indent); err != nil {
|
|
return err
|
|
}
|
|
comma = ","
|
|
}
|
|
if w.Indent != "" {
|
|
w.write("\n")
|
|
w.write(indent)
|
|
w.write(w.Indent)
|
|
}
|
|
w.write(`}`)
|
|
return nil
|
|
default:
|
|
return w.marshalSingularValue(fd, v, indent)
|
|
}
|
|
}
|
|
|
|
func (w *jsonWriter) marshalSingularValue(fd protoreflect.FieldDescriptor, v protoreflect.Value, indent string) error {
|
|
switch {
|
|
case !v.IsValid():
|
|
w.write("null")
|
|
return nil
|
|
case fd.Message() != nil:
|
|
return w.marshalMessage(v.Message(), indent+w.Indent, "")
|
|
case fd.Enum() != nil:
|
|
if fd.Enum().FullName() == "google.protobuf.NullValue" {
|
|
w.write("null")
|
|
return nil
|
|
}
|
|
|
|
vd := fd.Enum().Values().ByNumber(v.Enum())
|
|
if vd == nil || w.EnumsAsInts {
|
|
w.write(strconv.Itoa(int(v.Enum())))
|
|
} else {
|
|
w.write(`"` + string(vd.Name()) + `"`)
|
|
}
|
|
return nil
|
|
default:
|
|
switch v.Interface().(type) {
|
|
case float32, float64:
|
|
switch {
|
|
case math.IsInf(v.Float(), +1):
|
|
w.write(`"Infinity"`)
|
|
return nil
|
|
case math.IsInf(v.Float(), -1):
|
|
w.write(`"-Infinity"`)
|
|
return nil
|
|
case math.IsNaN(v.Float()):
|
|
w.write(`"NaN"`)
|
|
return nil
|
|
}
|
|
case int64, uint64:
|
|
w.write(fmt.Sprintf(`"%d"`, v.Interface()))
|
|
return nil
|
|
}
|
|
|
|
b, err := json.Marshal(v.Interface())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
w.write(string(b))
|
|
return nil
|
|
}
|
|
}
|