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