forked from TrueCloudLab/distribution
0e3018f2cf
Signed-off-by: gotgelf <gotgelf@gmail.com>
208 lines
6.7 KiB
Go
208 lines
6.7 KiB
Go
/*
|
|
*
|
|
* Copyright 2021 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* https://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*/
|
|
|
|
package s2a
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"errors"
|
|
"sync"
|
|
|
|
"github.com/google/s2a-go/fallback"
|
|
"github.com/google/s2a-go/stream"
|
|
|
|
s2apb "github.com/google/s2a-go/internal/proto/common_go_proto"
|
|
)
|
|
|
|
// Identity is the interface for S2A identities.
|
|
type Identity interface {
|
|
// Name returns the name of the identity.
|
|
Name() string
|
|
}
|
|
|
|
type spiffeID struct {
|
|
spiffeID string
|
|
}
|
|
|
|
func (s *spiffeID) Name() string { return s.spiffeID }
|
|
|
|
// NewSpiffeID creates a SPIFFE ID from id.
|
|
func NewSpiffeID(id string) Identity {
|
|
return &spiffeID{spiffeID: id}
|
|
}
|
|
|
|
type hostname struct {
|
|
hostname string
|
|
}
|
|
|
|
func (h *hostname) Name() string { return h.hostname }
|
|
|
|
// NewHostname creates a hostname from name.
|
|
func NewHostname(name string) Identity {
|
|
return &hostname{hostname: name}
|
|
}
|
|
|
|
type uid struct {
|
|
uid string
|
|
}
|
|
|
|
func (h *uid) Name() string { return h.uid }
|
|
|
|
// NewUID creates a UID from name.
|
|
func NewUID(name string) Identity {
|
|
return &uid{uid: name}
|
|
}
|
|
|
|
// VerificationModeType specifies the mode that S2A must use to verify the peer
|
|
// certificate chain.
|
|
type VerificationModeType int
|
|
|
|
// Three types of verification modes.
|
|
const (
|
|
Unspecified = iota
|
|
ConnectToGoogle
|
|
Spiffe
|
|
)
|
|
|
|
// ClientOptions contains the client-side options used to establish a secure
|
|
// channel using the S2A handshaker service.
|
|
type ClientOptions struct {
|
|
// TargetIdentities contains a list of allowed server identities. One of the
|
|
// target identities should match the peer identity in the handshake
|
|
// result; otherwise, the handshake fails.
|
|
TargetIdentities []Identity
|
|
// LocalIdentity is the local identity of the client application. If none is
|
|
// provided, then the S2A will choose the default identity, if one exists.
|
|
LocalIdentity Identity
|
|
// S2AAddress is the address of the S2A.
|
|
S2AAddress string
|
|
// EnsureProcessSessionTickets waits for all session tickets to be sent to
|
|
// S2A before a process completes.
|
|
//
|
|
// This functionality is crucial for processes that complete very soon after
|
|
// using S2A to establish a TLS connection, but it can be ignored for longer
|
|
// lived processes.
|
|
//
|
|
// Usage example:
|
|
// func main() {
|
|
// var ensureProcessSessionTickets sync.WaitGroup
|
|
// clientOpts := &s2a.ClientOptions{
|
|
// EnsureProcessSessionTickets: &ensureProcessSessionTickets,
|
|
// // Set other members.
|
|
// }
|
|
// creds, _ := s2a.NewClientCreds(clientOpts)
|
|
// conn, _ := grpc.Dial(serverAddr, grpc.WithTransportCredentials(creds))
|
|
// defer conn.Close()
|
|
//
|
|
// // Make RPC call.
|
|
//
|
|
// // The process terminates right after the RPC call ends.
|
|
// // ensureProcessSessionTickets can be used to ensure resumption
|
|
// // tickets are fully processed. If the process is long-lived, using
|
|
// // ensureProcessSessionTickets is not necessary.
|
|
// ensureProcessSessionTickets.Wait()
|
|
// }
|
|
EnsureProcessSessionTickets *sync.WaitGroup
|
|
// If true, enables the use of legacy S2Av1.
|
|
EnableLegacyMode bool
|
|
// VerificationMode specifies the mode that S2A must use to verify the
|
|
// peer certificate chain.
|
|
VerificationMode VerificationModeType
|
|
|
|
// Optional fallback after dialing with S2A fails.
|
|
FallbackOpts *FallbackOptions
|
|
|
|
// Generates an S2AStream interface for talking to the S2A server.
|
|
getS2AStream func(ctx context.Context, s2av2Address string) (stream.S2AStream, error)
|
|
|
|
// Serialized user specified policy for server authorization.
|
|
serverAuthorizationPolicy []byte
|
|
}
|
|
|
|
// FallbackOptions prescribes the fallback logic that should be taken if the application fails to connect with S2A.
|
|
type FallbackOptions struct {
|
|
// FallbackClientHandshakeFunc is used to specify fallback behavior when calling s2a.NewClientCreds().
|
|
// It will be called by ClientHandshake function, after handshake with S2A fails.
|
|
// s2a.NewClientCreds() ignores the other FallbackDialer field.
|
|
FallbackClientHandshakeFunc fallback.ClientHandshake
|
|
|
|
// FallbackDialer is used to specify fallback behavior when calling s2a.NewS2aDialTLSContextFunc().
|
|
// It passes in a custom fallback dialer and server address to use after dialing with S2A fails.
|
|
// s2a.NewS2aDialTLSContextFunc() ignores the other FallbackClientHandshakeFunc field.
|
|
FallbackDialer *FallbackDialer
|
|
}
|
|
|
|
// FallbackDialer contains a fallback tls.Dialer and a server address to connect to.
|
|
type FallbackDialer struct {
|
|
// Dialer specifies a fallback tls.Dialer.
|
|
Dialer *tls.Dialer
|
|
// ServerAddr is used by Dialer to establish fallback connection.
|
|
ServerAddr string
|
|
}
|
|
|
|
// DefaultClientOptions returns the default client options.
|
|
func DefaultClientOptions(s2aAddress string) *ClientOptions {
|
|
return &ClientOptions{
|
|
S2AAddress: s2aAddress,
|
|
VerificationMode: ConnectToGoogle,
|
|
}
|
|
}
|
|
|
|
// ServerOptions contains the server-side options used to establish a secure
|
|
// channel using the S2A handshaker service.
|
|
type ServerOptions struct {
|
|
// LocalIdentities is the list of local identities that may be assumed by
|
|
// the server. If no local identity is specified, then the S2A chooses a
|
|
// default local identity, if one exists.
|
|
LocalIdentities []Identity
|
|
// S2AAddress is the address of the S2A.
|
|
S2AAddress string
|
|
// If true, enables the use of legacy S2Av1.
|
|
EnableLegacyMode bool
|
|
// VerificationMode specifies the mode that S2A must use to verify the
|
|
// peer certificate chain.
|
|
VerificationMode VerificationModeType
|
|
|
|
// Generates an S2AStream interface for talking to the S2A server.
|
|
getS2AStream func(ctx context.Context, s2av2Address string) (stream.S2AStream, error)
|
|
}
|
|
|
|
// DefaultServerOptions returns the default server options.
|
|
func DefaultServerOptions(s2aAddress string) *ServerOptions {
|
|
return &ServerOptions{
|
|
S2AAddress: s2aAddress,
|
|
VerificationMode: ConnectToGoogle,
|
|
}
|
|
}
|
|
|
|
func toProtoIdentity(identity Identity) (*s2apb.Identity, error) {
|
|
if identity == nil {
|
|
return nil, nil
|
|
}
|
|
switch id := identity.(type) {
|
|
case *spiffeID:
|
|
return &s2apb.Identity{IdentityOneof: &s2apb.Identity_SpiffeId{SpiffeId: id.Name()}}, nil
|
|
case *hostname:
|
|
return &s2apb.Identity{IdentityOneof: &s2apb.Identity_Hostname{Hostname: id.Name()}}, nil
|
|
case *uid:
|
|
return &s2apb.Identity{IdentityOneof: &s2apb.Identity_Uid{Uid: id.Name()}}, nil
|
|
default:
|
|
return nil, errors.New("unrecognized identity type")
|
|
}
|
|
}
|