s2a_options.go

  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
 19package s2a
 20
 21import (
 22	"context"
 23	"crypto/tls"
 24	"errors"
 25	"sync"
 26
 27	"github.com/google/s2a-go/fallback"
 28	"github.com/google/s2a-go/stream"
 29	"google.golang.org/grpc/credentials"
 30
 31	s2apbv1 "github.com/google/s2a-go/internal/proto/common_go_proto"
 32	s2apb "github.com/google/s2a-go/internal/proto/v2/common_go_proto"
 33)
 34
 35// Identity is the interface for S2A identities.
 36type Identity interface {
 37	// Name returns the name of the identity.
 38	Name() string
 39}
 40
 41type spiffeID struct {
 42	spiffeID string
 43}
 44
 45func (s *spiffeID) Name() string { return s.spiffeID }
 46
 47// NewSpiffeID creates a SPIFFE ID from id.
 48func NewSpiffeID(id string) Identity {
 49	return &spiffeID{spiffeID: id}
 50}
 51
 52type hostname struct {
 53	hostname string
 54}
 55
 56func (h *hostname) Name() string { return h.hostname }
 57
 58// NewHostname creates a hostname from name.
 59func NewHostname(name string) Identity {
 60	return &hostname{hostname: name}
 61}
 62
 63type uid struct {
 64	uid string
 65}
 66
 67func (h *uid) Name() string { return h.uid }
 68
 69// NewUID creates a UID from name.
 70func NewUID(name string) Identity {
 71	return &uid{uid: name}
 72}
 73
 74// VerificationModeType specifies the mode that S2A must use to verify the peer
 75// certificate chain.
 76type VerificationModeType int
 77
 78// Three types of verification modes.
 79const (
 80	Unspecified VerificationModeType = iota
 81	Spiffe
 82	ConnectToGoogle
 83	ReservedCustomVerificationMode3
 84	ReservedCustomVerificationMode4
 85	ReservedCustomVerificationMode5
 86)
 87
 88// ClientOptions contains the client-side options used to establish a secure
 89// channel using the S2A handshaker service.
 90type ClientOptions struct {
 91	// TargetIdentities contains a list of allowed server identities. One of the
 92	// target identities should match the peer identity in the handshake
 93	// result; otherwise, the handshake fails.
 94	TargetIdentities []Identity
 95	// LocalIdentity is the local identity of the client application. If none is
 96	// provided, then the S2A will choose the default identity, if one exists.
 97	LocalIdentity Identity
 98	// S2AAddress is the address of the S2A.
 99	S2AAddress string
100	// Optional transport credentials.
101	// If set, this will be used for the gRPC connection to the S2A server.
102	TransportCreds credentials.TransportCredentials
103	// EnsureProcessSessionTickets waits for all session tickets to be sent to
104	// S2A before a process completes.
105	//
106	// This functionality is crucial for processes that complete very soon after
107	// using S2A to establish a TLS connection, but it can be ignored for longer
108	// lived processes.
109	//
110	// Usage example:
111	//   func main() {
112	//     var ensureProcessSessionTickets sync.WaitGroup
113	//     clientOpts := &s2a.ClientOptions{
114	//       EnsureProcessSessionTickets: &ensureProcessSessionTickets,
115	//       // Set other members.
116	//     }
117	//     creds, _ := s2a.NewClientCreds(clientOpts)
118	//     conn, _ := grpc.Dial(serverAddr, grpc.WithTransportCredentials(creds))
119	//     defer conn.Close()
120	//
121	//     // Make RPC call.
122	//
123	//     // The process terminates right after the RPC call ends.
124	//     // ensureProcessSessionTickets can be used to ensure resumption
125	//     // tickets are fully processed. If the process is long-lived, using
126	//     // ensureProcessSessionTickets is not necessary.
127	//     ensureProcessSessionTickets.Wait()
128	//   }
129	EnsureProcessSessionTickets *sync.WaitGroup
130	// If true, enables the use of legacy S2Av1.
131	EnableLegacyMode bool
132	// VerificationMode specifies the mode that S2A must use to verify the
133	// peer certificate chain.
134	VerificationMode VerificationModeType
135
136	// Optional fallback after dialing with S2A fails.
137	FallbackOpts *FallbackOptions
138
139	// Generates an S2AStream interface for talking to the S2A server.
140	getS2AStream func(ctx context.Context, s2av2Address string) (stream.S2AStream, error)
141
142	// Serialized user specified policy for server authorization.
143	serverAuthorizationPolicy []byte
144}
145
146// FallbackOptions prescribes the fallback logic that should be taken if the application fails to connect with S2A.
147type FallbackOptions struct {
148	// FallbackClientHandshakeFunc is used to specify fallback behavior when calling s2a.NewClientCreds().
149	// It will be called by ClientHandshake function, after handshake with S2A fails.
150	// s2a.NewClientCreds() ignores the other FallbackDialer field.
151	FallbackClientHandshakeFunc fallback.ClientHandshake
152
153	// FallbackDialer is used to specify fallback behavior when calling s2a.NewS2aDialTLSContextFunc().
154	// It passes in a custom fallback dialer and server address to use after dialing with S2A fails.
155	// s2a.NewS2aDialTLSContextFunc() ignores the other FallbackClientHandshakeFunc field.
156	FallbackDialer *FallbackDialer
157}
158
159// FallbackDialer contains a fallback tls.Dialer and a server address to connect to.
160type FallbackDialer struct {
161	// Dialer specifies a fallback tls.Dialer.
162	Dialer *tls.Dialer
163	// ServerAddr is used by Dialer to establish fallback connection.
164	ServerAddr string
165}
166
167// DefaultClientOptions returns the default client options.
168func DefaultClientOptions(s2aAddress string) *ClientOptions {
169	return &ClientOptions{
170		S2AAddress:       s2aAddress,
171		VerificationMode: ConnectToGoogle,
172	}
173}
174
175// ServerOptions contains the server-side options used to establish a secure
176// channel using the S2A handshaker service.
177type ServerOptions struct {
178	// LocalIdentities is the list of local identities that may be assumed by
179	// the server. If no local identity is specified, then the S2A chooses a
180	// default local identity, if one exists.
181	LocalIdentities []Identity
182	// S2AAddress is the address of the S2A.
183	S2AAddress string
184	// Optional transport credentials.
185	// If set, this will be used for the gRPC connection to the S2A server.
186	TransportCreds credentials.TransportCredentials
187	// If true, enables the use of legacy S2Av1.
188	EnableLegacyMode bool
189	// VerificationMode specifies the mode that S2A must use to verify the
190	// peer certificate chain.
191	VerificationMode VerificationModeType
192
193	// Generates an S2AStream interface for talking to the S2A server.
194	getS2AStream func(ctx context.Context, s2av2Address string) (stream.S2AStream, error)
195}
196
197// DefaultServerOptions returns the default server options.
198func DefaultServerOptions(s2aAddress string) *ServerOptions {
199	return &ServerOptions{
200		S2AAddress:       s2aAddress,
201		VerificationMode: ConnectToGoogle,
202	}
203}
204
205func toProtoIdentity(identity Identity) (*s2apbv1.Identity, error) {
206	if identity == nil {
207		return nil, nil
208	}
209	switch id := identity.(type) {
210	case *spiffeID:
211		return &s2apbv1.Identity{IdentityOneof: &s2apbv1.Identity_SpiffeId{SpiffeId: id.Name()}}, nil
212	case *hostname:
213		return &s2apbv1.Identity{IdentityOneof: &s2apbv1.Identity_Hostname{Hostname: id.Name()}}, nil
214	case *uid:
215		return &s2apbv1.Identity{IdentityOneof: &s2apbv1.Identity_Uid{Uid: id.Name()}}, nil
216	default:
217		return nil, errors.New("unrecognized identity type")
218	}
219}
220
221func toV2ProtoIdentity(identity Identity) (*s2apb.Identity, error) {
222	if identity == nil {
223		return nil, nil
224	}
225	switch id := identity.(type) {
226	case *spiffeID:
227		return &s2apb.Identity{IdentityOneof: &s2apb.Identity_SpiffeId{SpiffeId: id.Name()}}, nil
228	case *hostname:
229		return &s2apb.Identity{IdentityOneof: &s2apb.Identity_Hostname{Hostname: id.Name()}}, nil
230	case *uid:
231		return &s2apb.Identity{IdentityOneof: &s2apb.Identity_Uid{Uid: id.Name()}}, nil
232	default:
233		return nil, errors.New("unrecognized identity type")
234	}
235}