...

Source file src/github.com/datawire/ambassador/v2/cmd/kat-server/services/grpc-auth-v3.go

Documentation: github.com/datawire/ambassador/v2/cmd/kat-server/services

     1  package services
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"encoding/json"
     7  	"fmt"
     8  	"net"
     9  	"net/http"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"google.golang.org/genproto/googleapis/rpc/code"
    14  	"google.golang.org/genproto/googleapis/rpc/status"
    15  	"google.golang.org/grpc"
    16  	"google.golang.org/protobuf/types/known/wrapperspb"
    17  
    18  	core "github.com/datawire/ambassador/v2/pkg/api/envoy/config/core/v3"
    19  	pb "github.com/datawire/ambassador/v2/pkg/api/envoy/service/auth/v3"
    20  	envoy_type "github.com/datawire/ambassador/v2/pkg/api/envoy/type/v3"
    21  	"github.com/datawire/dlib/dlog"
    22  )
    23  
    24  // GRPCAUTHV3 server object (all fields are required).
    25  type GRPCAUTHV3 struct {
    26  	Port            int16
    27  	Backend         string
    28  	SecurePort      int16
    29  	SecureBackend   string
    30  	Cert            string
    31  	Key             string
    32  	ProtocolVersion string
    33  }
    34  
    35  // Start initializes the HTTP server.
    36  func (g *GRPCAUTHV3) Start(ctx context.Context) <-chan bool {
    37  	dlog.Printf(ctx, "GRPCAUTHV3: %s listening on %d/%d", g.Backend, g.Port, g.SecurePort)
    38  
    39  	exited := make(chan bool)
    40  	proto := "tcp"
    41  
    42  	go func() {
    43  		port := fmt.Sprintf(":%v", g.Port)
    44  
    45  		ln, err := net.Listen(proto, port)
    46  		if err != nil {
    47  			dlog.Error(ctx, err)
    48  			panic(err) // TODO: do something better
    49  		}
    50  
    51  		s := grpc.NewServer()
    52  		dlog.Printf(ctx, "registering v3 service")
    53  		pb.RegisterAuthorizationServer(s, g)
    54  		if err := s.Serve(ln); err != nil {
    55  			panic(err) // TODO: do something better
    56  		}
    57  
    58  		defer ln.Close()
    59  		close(exited)
    60  	}()
    61  
    62  	go func() {
    63  		cer, err := tls.LoadX509KeyPair(g.Cert, g.Key)
    64  		if err != nil {
    65  			dlog.Error(ctx, err)
    66  			panic(err) // TODO: do something better
    67  		}
    68  
    69  		config := &tls.Config{Certificates: []tls.Certificate{cer}}
    70  		port := fmt.Sprintf(":%v", g.SecurePort)
    71  		ln, err := tls.Listen(proto, port, config)
    72  		if err != nil {
    73  			dlog.Error(ctx, err)
    74  			panic(err) // TODO: do something better
    75  		}
    76  
    77  		s := grpc.NewServer()
    78  		dlog.Printf(ctx, "registering v2 service")
    79  		pb.RegisterAuthorizationServer(s, g)
    80  		if err := s.Serve(ln); err != nil {
    81  			panic(err) // TODO: do something better
    82  		}
    83  
    84  		defer ln.Close()
    85  		close(exited)
    86  	}()
    87  
    88  	dlog.Print(ctx, "starting gRPC authorization service")
    89  	return exited
    90  }
    91  
    92  // Check checks the request object.
    93  func (g *GRPCAUTHV3) Check(ctx context.Context, r *pb.CheckRequest) (*pb.CheckResponse, error) {
    94  	rs := &ResponseV3{}
    95  
    96  	rheader := r.GetAttributes().GetRequest().GetHttp().GetHeaders()
    97  	rbody := r.GetAttributes().GetRequest().GetHttp().GetBody()
    98  	if len(rbody) > 0 {
    99  		rheader["body"] = rbody
   100  	}
   101  
   102  	// Sets requested HTTP status.
   103  	rs.SetStatus(ctx, rheader["requested-status"])
   104  
   105  	rs.AddHeader(false, "x-grpc-service-protocol-version", g.ProtocolVersion)
   106  
   107  	// Sets requested headers.
   108  	for _, key := range strings.Split(rheader["requested-header"], ",") {
   109  		if val := rheader[key]; len(val) > 0 {
   110  			rs.AddHeader(false, key, val)
   111  		}
   112  	}
   113  
   114  	// Append requested headers.
   115  	for _, token := range strings.Split(rheader["x-grpc-auth-append"], ";") {
   116  		header := strings.Split(strings.TrimSpace(token), "=")
   117  		if len(header) > 1 {
   118  			dlog.Printf(ctx, "appending header %s : %s", header[0], header[1])
   119  			rs.AddHeader(true, header[0], header[1])
   120  		}
   121  	}
   122  
   123  	// Sets requested Cookies.
   124  	for _, v := range strings.Split(rheader["requested-cookie"], ",") {
   125  		val := strings.Trim(v, " ")
   126  		rs.AddHeader(false, "Set-Cookie", fmt.Sprintf("%s=%s", val, val))
   127  	}
   128  
   129  	// Sets requested location.
   130  	if len(rheader["requested-location"]) > 0 {
   131  		rs.AddHeader(false, "Location", rheader["requested-location"])
   132  	}
   133  
   134  	// Parses request headers.
   135  	headers := make(map[string]interface{})
   136  	for k, v := range rheader {
   137  		headers[k] = strings.Split(v, ",")
   138  	}
   139  
   140  	// Parses request URL.
   141  	url := make(map[string]interface{})
   142  	url["fragment"] = r.GetAttributes().GetRequest().GetHttp().GetFragment()
   143  	url["host"] = r.GetAttributes().GetRequest().GetHttp().GetHost()
   144  	url["path"] = r.GetAttributes().GetRequest().GetHttp().GetPath()
   145  	url["query"] = r.GetAttributes().GetRequest().GetHttp().GetQuery()
   146  	url["scheme"] = r.GetAttributes().GetRequest().GetHttp().GetScheme()
   147  
   148  	// Parses TLS info.
   149  	tls := make(map[string]interface{})
   150  	tls["enabled"] = false
   151  
   152  	// Sets request portion of the results body.
   153  	request := make(map[string]interface{})
   154  	request["url"] = url
   155  	request["method"] = r.GetAttributes().GetRequest().GetHttp().GetMethod()
   156  	request["headers"] = headers
   157  	request["host"] = r.GetAttributes().GetRequest().GetHttp().GetHost()
   158  	request["tls"] = tls
   159  
   160  	// Sets results body.
   161  	results := make(map[string]interface{})
   162  	results["backend"] = g.Backend
   163  	results["status"] = rs.GetStatus()
   164  	if len(request) > 0 {
   165  		results["request"] = request
   166  	}
   167  	if rs.GetHTTPHeaderMap() != nil {
   168  		results["headers"] = *rs.GetHTTPHeaderMap()
   169  	}
   170  	body, err := json.MarshalIndent(results, "", "  ")
   171  	if err != nil {
   172  		body = []byte(fmt.Sprintf("Error: %v", err))
   173  	}
   174  
   175  	// Sets response body.
   176  	dlog.Printf(ctx, "setting response body: %s", string(body))
   177  	rs.SetBody(string(body))
   178  
   179  	return rs.GetResponse(), nil
   180  }
   181  
   182  // ResponseV3 constructs an authorization response object.
   183  type ResponseV3 struct {
   184  	headers []*core.HeaderValueOption
   185  	body    string
   186  	status  uint32
   187  }
   188  
   189  // AddHeader adds a header to the response. When append param is true, Envoy will
   190  // append the value to an existent request header instead of overriding it.
   191  func (r *ResponseV3) AddHeader(a bool, k, v string) {
   192  	val := &core.HeaderValueOption{
   193  		Header: &core.HeaderValue{
   194  			Key:   k,
   195  			Value: v,
   196  		},
   197  		Append: &wrapperspb.BoolValue{Value: a},
   198  	}
   199  	r.headers = append(r.headers, val)
   200  }
   201  
   202  // GetHTTPHeaderMap returns HTTP header mapping of the response header-options.
   203  func (r *ResponseV3) GetHTTPHeaderMap() *http.Header {
   204  	h := &http.Header{}
   205  	for _, v := range r.headers {
   206  		h.Add(v.Header.Key, v.Header.Value)
   207  	}
   208  	return h
   209  }
   210  
   211  // SetBody sets the authorization response message body.
   212  func (r *ResponseV3) SetBody(s string) {
   213  	r.body = s
   214  }
   215  
   216  // SetStatus sets the authorization response HTTP status code.
   217  func (r *ResponseV3) SetStatus(ctx context.Context, s string) {
   218  	if len(s) == 0 {
   219  		s = "200"
   220  	}
   221  	if val, err := strconv.Atoi(s); err == nil {
   222  		r.status = uint32(val)
   223  		r.AddHeader(false, "status", s)
   224  		dlog.Printf(ctx, "setting HTTP status %v", r.status)
   225  	} else {
   226  		r.status = uint32(500)
   227  		r.AddHeader(false, "status", "500")
   228  		dlog.Printf(ctx, "error setting HTTP status. Cannot parse string %s: %v.", s, err)
   229  	}
   230  }
   231  
   232  // GetStatus returns the authorization response HTTP status code.
   233  func (r *ResponseV3) GetStatus() uint32 {
   234  	return r.status
   235  }
   236  
   237  // GetResponse returns the gRPC authorization response object.
   238  func (r *ResponseV3) GetResponse() *pb.CheckResponse {
   239  	rs := &pb.CheckResponse{}
   240  	switch {
   241  	// Ok respose.
   242  	case r.status == http.StatusOK || r.status == 0:
   243  		rs.Status = &status.Status{Code: int32(code.Code_OK)}
   244  		rs.HttpResponse = &pb.CheckResponse_OkResponse{
   245  			OkResponse: &pb.OkHttpResponse{
   246  				Headers: r.headers,
   247  			},
   248  		}
   249  
   250  	// Denied response.
   251  	default:
   252  		rs.Status = &status.Status{Code: int32(code.Code_UNAUTHENTICATED)}
   253  		rs.HttpResponse = &pb.CheckResponse_DeniedResponse{
   254  			DeniedResponse: &pb.DeniedHttpResponse{
   255  				Status: &envoy_type.HttpStatus{
   256  					Code: envoy_type.StatusCode(r.status),
   257  				},
   258  				Headers: r.headers,
   259  				Body:    r.body,
   260  			},
   261  		}
   262  	}
   263  
   264  	return rs
   265  }
   266  

View as plain text