357 lines
11 KiB
Go
357 lines
11 KiB
Go
package edge_apis
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/tls"
|
|
"crypto/x509"
|
|
"github.com/go-openapi/runtime"
|
|
"github.com/go-openapi/strfmt"
|
|
"github.com/openziti/edge-api/rest_model"
|
|
"github.com/openziti/identity"
|
|
"github.com/openziti/sdk-golang/ziti/edge/network"
|
|
"github.com/openziti/sdk-golang/ziti/sdkinfo"
|
|
"net/http"
|
|
)
|
|
|
|
// Credentials represents the minimal information needed across all authentication mechanisms to authenticate an identity
|
|
// to an OpenZiti network.
|
|
type Credentials interface {
|
|
// Payload constructs the objects that represent the JSON authentication payload for this set of credentials.
|
|
Payload() *rest_model.Authenticate
|
|
|
|
// TlsCerts returns zero or more tls.Certificates used for client authentication.
|
|
TlsCerts() []tls.Certificate
|
|
|
|
// GetCaPool returns the CA pool that this credential was configured to trust.
|
|
GetCaPool() *x509.CertPool
|
|
|
|
// Method returns the authentication necessary to complete an authentication request.
|
|
Method() string
|
|
|
|
// AddAuthHeader adds a header for all authentication requests.
|
|
AddAuthHeader(key, value string)
|
|
|
|
// AddRequestHeader adds a header for all requests after authentication
|
|
AddRequestHeader(key, value string)
|
|
|
|
// AddJWT adds additional JWTs to the credentials. Used to satisfy secondary authentication/MFA requirements. The
|
|
// provided token should be the base64 encoded version of the token.
|
|
AddJWT(string)
|
|
|
|
// ClientAuthInfoWriter is used to pass a Credentials instance to the openapi runtime to authenticate outgoing
|
|
//requests.
|
|
runtime.ClientAuthInfoWriter
|
|
|
|
// GetRequestHeaders returns a set of headers to use after authentication during normal HTTP operations
|
|
GetRequestHeaders() http.Header
|
|
}
|
|
|
|
// IdentityProvider is a sentinel interface used to determine whether the backing Credentials instance can provide
|
|
// an Identity that can provide a certificate and private key used to initiate mTLS connections.
|
|
type IdentityProvider interface {
|
|
GetIdentity() identity.Identity
|
|
}
|
|
|
|
// toTlsCerts converts an array of certificates into a single tls.Certificate. Index zero is assumed to be the leaf
|
|
// certificate and all subsequent certificates to be the support certificate chain that should be sent to servers.
|
|
// At least one certificate must be provided.
|
|
func toTlsCerts(certs []*x509.Certificate, key crypto.PrivateKey) tls.Certificate {
|
|
tlsCert := tls.Certificate{
|
|
PrivateKey: key,
|
|
Leaf: certs[0],
|
|
}
|
|
for _, cert := range certs {
|
|
tlsCert.Certificate = append(tlsCert.Certificate, cert.Raw)
|
|
}
|
|
|
|
return tlsCert
|
|
}
|
|
|
|
// getClientAuthInfoOp returns a one-off runtime.ClientOperation used to authenticate single requests without altering
|
|
// the authentication operation of the entire client runtime.
|
|
func getClientAuthInfoOp(credentials Credentials, client *http.Client) func(*runtime.ClientOperation) {
|
|
return func(operation *runtime.ClientOperation) {
|
|
operation.AuthInfo = credentials
|
|
|
|
certs := credentials.TlsCerts()
|
|
|
|
if len(certs) != 0 {
|
|
operation.Client = client
|
|
if transport, ok := operation.Client.Transport.(*http.Transport); ok {
|
|
transport.TLSClientConfig.Certificates = certs
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// BaseCredentials is a shared struct of information all Credentials implementations require.
|
|
type BaseCredentials struct {
|
|
// ConfigTypes is used to set the configuration types for services during authentication
|
|
ConfigTypes []string
|
|
|
|
// AuthHeaders is a map of strings to string arrays of headers to send with auth requests.
|
|
AuthHeaders http.Header
|
|
|
|
// RequestHeaders is a map of string to string arrays of headers to send on non-authentication requests.
|
|
RequestHeaders http.Header
|
|
|
|
// EnvInfo is provided during authentication to set environmental information about the client.
|
|
EnvInfo *rest_model.EnvInfo
|
|
|
|
// SdkInfo is provided during authentication to set SDK information about the client.
|
|
SdkInfo *rest_model.SdkInfo
|
|
|
|
// CaPool will override the client's default certificate pool if set to a non-nil value.
|
|
CaPool *x509.CertPool
|
|
}
|
|
|
|
// Payload will produce the object used to construct the body of an authentication requests. The base version
|
|
// sets shared information available in BaseCredentials.
|
|
func (c *BaseCredentials) Payload() *rest_model.Authenticate {
|
|
envInfo, sdkInfo := sdkinfo.GetSdkInfo()
|
|
|
|
if c.EnvInfo != nil {
|
|
envInfo = c.EnvInfo
|
|
}
|
|
|
|
if c.SdkInfo != nil {
|
|
sdkInfo = c.SdkInfo
|
|
}
|
|
|
|
return &rest_model.Authenticate{
|
|
ConfigTypes: c.ConfigTypes,
|
|
EnvInfo: envInfo,
|
|
SdkInfo: sdkInfo,
|
|
}
|
|
}
|
|
|
|
// GetCaPool provides a base implementation to return the certificate pool of a Credentials instance.
|
|
func (c *BaseCredentials) GetCaPool() *x509.CertPool {
|
|
return c.CaPool
|
|
}
|
|
|
|
// AddAuthHeader provides a base implementation to add a header to authentication requests.
|
|
func (c *BaseCredentials) AddAuthHeader(key, value string) {
|
|
if c.AuthHeaders == nil {
|
|
c.AuthHeaders = http.Header{}
|
|
}
|
|
c.AuthHeaders.Add(key, value)
|
|
}
|
|
|
|
// AddRequestHeader provides a base implementation to add a header to all requests after authentication.
|
|
func (c *BaseCredentials) AddRequestHeader(key, value string) {
|
|
if c.RequestHeaders == nil {
|
|
c.RequestHeaders = http.Header{}
|
|
}
|
|
|
|
c.RequestHeaders.Add(key, value)
|
|
}
|
|
|
|
// AddJWT adds additional JWTs to the credentials. Used to satisfy secondary authentication/MFA requirements. The
|
|
// provided token should be the base64 encoded version of the token. Convenience function for AddHeader.
|
|
func (c *BaseCredentials) AddJWT(token string) {
|
|
c.AddAuthHeader("Authorization", "Bearer "+token)
|
|
c.AddRequestHeader("Authorization", "Bearer "+token)
|
|
}
|
|
|
|
// AuthenticateRequest provides a base implementation to authenticate an outgoing request. This is provided here
|
|
// for authentication methods such as `cert` which do not have to provide any more request level information.
|
|
func (c *BaseCredentials) AuthenticateRequest(request runtime.ClientRequest, _ strfmt.Registry) error {
|
|
var errors []error
|
|
|
|
for hName, hVals := range c.AuthHeaders {
|
|
for _, hVal := range hVals {
|
|
err := request.SetHeaderParam(hName, hVal)
|
|
if err != nil {
|
|
errors = append(errors, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(errors) > 0 {
|
|
return network.MultipleErrors(errors)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ProcessRequest proves a base implemmentation mutate runtime.ClientRequests as they are sent out after
|
|
// authentication. Useful for adding headers.
|
|
func (c *BaseCredentials) ProcessRequest(request runtime.ClientRequest, _ strfmt.Registry) error {
|
|
var errors []error
|
|
|
|
for hName, hVals := range c.RequestHeaders {
|
|
for _, hVal := range hVals {
|
|
err := request.SetHeaderParam(hName, hVal)
|
|
if err != nil {
|
|
errors = append(errors, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
if len(errors) > 0 {
|
|
return network.MultipleErrors(errors)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetRequestHeaders returns headers that should be sent on requests post authentication.
|
|
func (c *BaseCredentials) GetRequestHeaders() http.Header {
|
|
return c.RequestHeaders
|
|
}
|
|
|
|
// TlsCerts provides a base implementation of returning the tls.Certificate array that will be used to setup
|
|
// mTLS connections. This is provided here for authentication methods that do not initially require mTLS (e.g. JWTs).
|
|
func (c *BaseCredentials) TlsCerts() []tls.Certificate {
|
|
return nil
|
|
}
|
|
|
|
var _ Credentials = &CertCredentials{}
|
|
|
|
// CertCredentials represents authentication using certificates that are not from an Identity configuration file.
|
|
type CertCredentials struct {
|
|
BaseCredentials
|
|
Certs []*x509.Certificate
|
|
Key crypto.PrivateKey
|
|
}
|
|
|
|
// NewCertCredentials creates Credentials instance based upon an array of certificates. At least one certificate must
|
|
// be provided and the certificate at index zero is assumed to be the leaf client certificate that pairs with the
|
|
// provided private key. All other certificates are assumed to support the leaf client certificate as a chain.
|
|
func NewCertCredentials(certs []*x509.Certificate, key crypto.PrivateKey) *CertCredentials {
|
|
return &CertCredentials{
|
|
BaseCredentials: BaseCredentials{},
|
|
Certs: certs,
|
|
Key: key,
|
|
}
|
|
}
|
|
|
|
func (c *CertCredentials) Method() string {
|
|
return "cert"
|
|
}
|
|
|
|
func (c *CertCredentials) TlsCerts() []tls.Certificate {
|
|
return []tls.Certificate{toTlsCerts(c.Certs, c.Key)}
|
|
}
|
|
|
|
func (c *CertCredentials) GetIdentity() identity.Identity {
|
|
return identity.NewClientTokenIdentityWithPool(c.Certs, c.Key, c.GetCaPool())
|
|
}
|
|
|
|
var _ Credentials = &IdentityCredentials{}
|
|
|
|
type IdentityCredentials struct {
|
|
BaseCredentials
|
|
Identity identity.Identity
|
|
}
|
|
|
|
// NewIdentityCredentials creates a Credentials instance based upon and Identity.
|
|
func NewIdentityCredentials(identity identity.Identity) *IdentityCredentials {
|
|
return &IdentityCredentials{
|
|
BaseCredentials: BaseCredentials{},
|
|
Identity: identity,
|
|
}
|
|
}
|
|
|
|
// NewIdentityCredentialsFromConfig creates a Credentials instance based upon and Identity configuration.
|
|
func NewIdentityCredentialsFromConfig(config identity.Config) *IdentityCredentials {
|
|
return &IdentityCredentials{
|
|
BaseCredentials: BaseCredentials{},
|
|
Identity: &identity.LazyIdentity{Config: &config},
|
|
}
|
|
}
|
|
|
|
func (c *IdentityCredentials) GetIdentity() identity.Identity {
|
|
return c.Identity
|
|
}
|
|
|
|
func (c *IdentityCredentials) Method() string {
|
|
return "cert"
|
|
}
|
|
|
|
func (c *IdentityCredentials) GetCaPool() *x509.CertPool {
|
|
return c.Identity.CA()
|
|
}
|
|
|
|
func (c *IdentityCredentials) TlsCerts() []tls.Certificate {
|
|
tlsCert := c.Identity.Cert()
|
|
|
|
if tlsCert != nil {
|
|
return []tls.Certificate{*tlsCert}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *IdentityCredentials) AuthenticateRequest(request runtime.ClientRequest, reg strfmt.Registry) error {
|
|
return c.BaseCredentials.AuthenticateRequest(request, reg)
|
|
}
|
|
|
|
var _ Credentials = &JwtCredentials{}
|
|
|
|
type JwtCredentials struct {
|
|
BaseCredentials
|
|
JWT string
|
|
SendOnEveryRequest bool
|
|
}
|
|
|
|
// NewJwtCredentials creates a Credentials instance based on a JWT obtained from an outside system.
|
|
func NewJwtCredentials(jwt string) *JwtCredentials {
|
|
return &JwtCredentials{
|
|
BaseCredentials: BaseCredentials{},
|
|
JWT: jwt,
|
|
}
|
|
}
|
|
|
|
func (c *JwtCredentials) Method() string {
|
|
return "ext-jwt"
|
|
}
|
|
|
|
func (c *JwtCredentials) AuthenticateRequest(request runtime.ClientRequest, reg strfmt.Registry) error {
|
|
var errors []error
|
|
|
|
err := c.BaseCredentials.AuthenticateRequest(request, reg)
|
|
if err != nil {
|
|
errors = append(errors, err)
|
|
}
|
|
err = request.SetHeaderParam("Authorization", "Bearer "+c.JWT)
|
|
if err != nil {
|
|
errors = append(errors, err)
|
|
}
|
|
if len(errors) > 0 {
|
|
return network.MultipleErrors(errors)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var _ Credentials = &UpdbCredentials{}
|
|
|
|
type UpdbCredentials struct {
|
|
BaseCredentials
|
|
Username string
|
|
Password string
|
|
}
|
|
|
|
func (c *UpdbCredentials) Method() string {
|
|
return "password"
|
|
}
|
|
|
|
// NewUpdbCredentials creates a Credentials instance based on a username/passwords combination.
|
|
func NewUpdbCredentials(username string, password string) *UpdbCredentials {
|
|
return &UpdbCredentials{
|
|
BaseCredentials: BaseCredentials{},
|
|
Username: username,
|
|
Password: password,
|
|
}
|
|
}
|
|
|
|
func (c *UpdbCredentials) Payload() *rest_model.Authenticate {
|
|
payload := c.BaseCredentials.Payload()
|
|
payload.Username = rest_model.Username(c.Username)
|
|
payload.Password = rest_model.Password(c.Password)
|
|
|
|
return payload
|
|
}
|
|
|
|
func (c *UpdbCredentials) AuthenticateRequest(request runtime.ClientRequest, reg strfmt.Registry) error {
|
|
return c.BaseCredentials.AuthenticateRequest(request, reg)
|
|
}
|