s2a_options.go raw

   1  /*
   2   *
   3   * Copyright 2021 Google LLC
   4   *
   5   * Licensed under the Apache License, Version 2.0 (the "License");
   6   * you may not use this file except in compliance with the License.
   7   * You may obtain a copy of the License at
   8   *
   9   *     https://www.apache.org/licenses/LICENSE-2.0
  10   *
  11   * Unless required by applicable law or agreed to in writing, software
  12   * distributed under the License is distributed on an "AS IS" BASIS,
  13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14   * See the License for the specific language governing permissions and
  15   * limitations under the License.
  16   *
  17   */
  18  
  19  package s2a
  20  
  21  import (
  22  	"crypto/tls"
  23  	"errors"
  24  	"sync"
  25  
  26  	"github.com/google/s2a-go/fallback"
  27  	"github.com/google/s2a-go/stream"
  28  	"google.golang.org/grpc/credentials"
  29  
  30  	s2av1pb "github.com/google/s2a-go/internal/proto/common_go_proto"
  31  	s2apb "github.com/google/s2a-go/internal/proto/v2/common_go_proto"
  32  )
  33  
  34  // Identity is the interface for S2A identities.
  35  type Identity interface {
  36  	// Name returns the name of the identity.
  37  	Name() string
  38  	Attributes() map[string]string
  39  }
  40  
  41  type UnspecifiedID struct {
  42  	Attr map[string]string
  43  }
  44  
  45  func (u *UnspecifiedID) Name() string { return "" }
  46  
  47  func (u *UnspecifiedID) Attributes() map[string]string {
  48  	return u.Attr
  49  }
  50  
  51  type spiffeID struct {
  52  	spiffeID string
  53  }
  54  
  55  func (s *spiffeID) Name() string { return s.spiffeID }
  56  
  57  func (spiffeID) Attributes() map[string]string { return nil }
  58  
  59  // NewSpiffeID creates a SPIFFE ID from id.
  60  func NewSpiffeID(id string) Identity { return &spiffeID{spiffeID: id} }
  61  
  62  type hostname struct {
  63  	hostname string
  64  }
  65  
  66  func (h *hostname) Name() string { return h.hostname }
  67  
  68  func (hostname) Attributes() map[string]string { return nil }
  69  
  70  // NewHostname creates a hostname from name.
  71  func NewHostname(name string) Identity { return &hostname{hostname: name} }
  72  
  73  type uid struct {
  74  	uid string
  75  }
  76  
  77  func (h *uid) Name() string { return h.uid }
  78  
  79  func (uid) Attributes() map[string]string { return nil }
  80  
  81  // NewUID creates a UID from name.
  82  func NewUID(name string) Identity { return &uid{uid: name} }
  83  
  84  // VerificationModeType specifies the mode that S2A must use to verify the peer
  85  // certificate chain.
  86  type VerificationModeType int
  87  
  88  // Three types of verification modes.
  89  const (
  90  	Unspecified VerificationModeType = iota
  91  	Spiffe
  92  	ConnectToGoogle
  93  	ReservedCustomVerificationMode3
  94  	ReservedCustomVerificationMode4
  95  	ReservedCustomVerificationMode5
  96  	ReservedCustomVerificationMode6
  97  )
  98  
  99  // ClientOptions contains the client-side options used to establish a secure
 100  // channel using the S2A handshaker service.
 101  type ClientOptions struct {
 102  	// TargetIdentities contains a list of allowed server identities. One of the
 103  	// target identities should match the peer identity in the handshake
 104  	// result; otherwise, the handshake fails.
 105  	TargetIdentities []Identity
 106  	// LocalIdentity is the local identity of the client application. If none is
 107  	// provided, then the S2A will choose the default identity, if one exists.
 108  	LocalIdentity Identity
 109  	// S2AAddress is the address of the S2A.
 110  	S2AAddress string
 111  	// Optional transport credentials.
 112  	// If set, this will be used for the gRPC connection to the S2A server.
 113  	TransportCreds credentials.TransportCredentials
 114  	// EnsureProcessSessionTickets waits for all session tickets to be sent to
 115  	// S2A before a process completes.
 116  	//
 117  	// This functionality is crucial for processes that complete very soon after
 118  	// using S2A to establish a TLS connection, but it can be ignored for longer
 119  	// lived processes.
 120  	//
 121  	// Usage example:
 122  	//   func main() {
 123  	//     var ensureProcessSessionTickets sync.WaitGroup
 124  	//     clientOpts := &s2a.ClientOptions{
 125  	//       EnsureProcessSessionTickets: &ensureProcessSessionTickets,
 126  	//       // Set other members.
 127  	//     }
 128  	//     creds, _ := s2a.NewClientCreds(clientOpts)
 129  	//     conn, _ := grpc.Dial(serverAddr, grpc.WithTransportCredentials(creds))
 130  	//     defer conn.Close()
 131  	//
 132  	//     // Make RPC call.
 133  	//
 134  	//     // The process terminates right after the RPC call ends.
 135  	//     // ensureProcessSessionTickets can be used to ensure resumption
 136  	//     // tickets are fully processed. If the process is long-lived, using
 137  	//     // ensureProcessSessionTickets is not necessary.
 138  	//     ensureProcessSessionTickets.Wait()
 139  	//   }
 140  	EnsureProcessSessionTickets *sync.WaitGroup
 141  	// If true, enables the use of legacy S2Av1.
 142  	EnableLegacyMode bool
 143  	// VerificationMode specifies the mode that S2A must use to verify the
 144  	// peer certificate chain.
 145  	VerificationMode VerificationModeType
 146  
 147  	// Optional fallback after dialing with S2A fails.
 148  	FallbackOpts *FallbackOptions
 149  
 150  	// Generates an S2AStream interface for talking to the S2A server.
 151  	getS2AStream stream.GetS2AStream
 152  
 153  	// Serialized user specified policy for server authorization.
 154  	serverAuthorizationPolicy []byte
 155  }
 156  
 157  // FallbackOptions prescribes the fallback logic that should be taken if the application fails to connect with S2A.
 158  type FallbackOptions struct {
 159  	// FallbackClientHandshakeFunc is used to specify fallback behavior when calling s2a.NewClientCreds().
 160  	// It will be called by ClientHandshake function, after handshake with S2A fails.
 161  	// s2a.NewClientCreds() ignores the other FallbackDialer field.
 162  	FallbackClientHandshakeFunc fallback.ClientHandshake
 163  
 164  	// FallbackDialer is used to specify fallback behavior when calling s2a.NewS2aDialTLSContextFunc().
 165  	// It passes in a custom fallback dialer and server address to use after dialing with S2A fails.
 166  	// s2a.NewS2aDialTLSContextFunc() ignores the other FallbackClientHandshakeFunc field.
 167  	FallbackDialer *FallbackDialer
 168  }
 169  
 170  // FallbackDialer contains a fallback tls.Dialer and a server address to connect to.
 171  type FallbackDialer struct {
 172  	// Dialer specifies a fallback tls.Dialer.
 173  	Dialer *tls.Dialer
 174  	// ServerAddr is used by Dialer to establish fallback connection.
 175  	ServerAddr string
 176  }
 177  
 178  // DefaultClientOptions returns the default client options.
 179  func DefaultClientOptions(s2aAddress string) *ClientOptions {
 180  	return &ClientOptions{
 181  		S2AAddress:       s2aAddress,
 182  		VerificationMode: ConnectToGoogle,
 183  	}
 184  }
 185  
 186  // ServerOptions contains the server-side options used to establish a secure
 187  // channel using the S2A handshaker service.
 188  type ServerOptions struct {
 189  	// LocalIdentities is the list of local identities that may be assumed by
 190  	// the server. If no local identity is specified, then the S2A chooses a
 191  	// default local identity, if one exists.
 192  	LocalIdentities []Identity
 193  	// S2AAddress is the address of the S2A.
 194  	S2AAddress string
 195  	// Optional transport credentials.
 196  	// If set, this will be used for the gRPC connection to the S2A server.
 197  	TransportCreds credentials.TransportCredentials
 198  	// If true, enables the use of legacy S2Av1.
 199  	EnableLegacyMode bool
 200  	// VerificationMode specifies the mode that S2A must use to verify the
 201  	// peer certificate chain.
 202  	VerificationMode VerificationModeType
 203  
 204  	// Generates an S2AStream interface for talking to the S2A server.
 205  	getS2AStream stream.GetS2AStream
 206  }
 207  
 208  // DefaultServerOptions returns the default server options.
 209  func DefaultServerOptions(s2aAddress string) *ServerOptions {
 210  	return &ServerOptions{
 211  		S2AAddress:       s2aAddress,
 212  		VerificationMode: ConnectToGoogle,
 213  	}
 214  }
 215  
 216  func toProtoIdentity(identity Identity) (*s2av1pb.Identity, error) {
 217  	if identity == nil {
 218  		return nil, nil
 219  	}
 220  	switch id := identity.(type) {
 221  	case *spiffeID:
 222  		return &s2av1pb.Identity{
 223  			IdentityOneof: &s2av1pb.Identity_SpiffeId{SpiffeId: id.Name()},
 224  			Attributes:    id.Attributes(),
 225  		}, nil
 226  	case *hostname:
 227  		return &s2av1pb.Identity{
 228  			IdentityOneof: &s2av1pb.Identity_Hostname{Hostname: id.Name()},
 229  			Attributes:    id.Attributes(),
 230  		}, nil
 231  	case *uid:
 232  		return &s2av1pb.Identity{
 233  			IdentityOneof: &s2av1pb.Identity_Uid{Uid: id.Name()},
 234  			Attributes:    id.Attributes(),
 235  		}, nil
 236  	case *UnspecifiedID:
 237  		return &s2av1pb.Identity{
 238  			Attributes: id.Attributes(),
 239  		}, nil
 240  	default:
 241  		return nil, errors.New("unrecognized identity type")
 242  	}
 243  }
 244  
 245  func toV2ProtoIdentity(identity Identity) (*s2apb.Identity, error) {
 246  	if identity == nil {
 247  		return nil, nil
 248  	}
 249  	switch id := identity.(type) {
 250  	case *spiffeID:
 251  		return &s2apb.Identity{
 252  			IdentityOneof: &s2apb.Identity_SpiffeId{SpiffeId: id.Name()},
 253  			Attributes:    id.Attributes(),
 254  		}, nil
 255  	case *hostname:
 256  		return &s2apb.Identity{
 257  			IdentityOneof: &s2apb.Identity_Hostname{Hostname: id.Name()},
 258  			Attributes:    id.Attributes(),
 259  		}, nil
 260  	case *uid:
 261  		return &s2apb.Identity{
 262  			IdentityOneof: &s2apb.Identity_Uid{Uid: id.Name()},
 263  			Attributes:    id.Attributes(),
 264  		}, nil
 265  	case *UnspecifiedID:
 266  		return &s2apb.Identity{
 267  			Attributes: id.Attributes(),
 268  		}, nil
 269  	default:
 270  		return nil, errors.New("unrecognized identity type")
 271  	}
 272  }
 273