538 lines
16 KiB
Go
538 lines
16 KiB
Go
package infisical
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
api "github.com/infisical/go-sdk/packages/api/auth"
|
|
"github.com/infisical/go-sdk/packages/models"
|
|
"github.com/infisical/go-sdk/packages/util"
|
|
"github.com/oracle/oci-go-sdk/v65/common"
|
|
|
|
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
|
|
)
|
|
|
|
type KubernetesAuthLoginOptions struct {
|
|
IdentityID string
|
|
ServiceAccountTokenPath string
|
|
}
|
|
|
|
type AuthInterface interface {
|
|
SetAccessToken(accessToken string)
|
|
GetAccessToken() string
|
|
GetOrganizationSlug() string
|
|
// When set, this will scope the login session to the specified sub-organization the machine identity has access to. If left empty, the session defaults to the organization where the machine identity was created in.
|
|
WithOrganizationSlug(organizationSlug string) AuthInterface
|
|
// When set, the next AzureAuthLogin call will request a token for the specified User-Assigned Managed Identity (UAMI) by its client ID. Leave unset for System-Assigned Managed Identity.
|
|
WithAzureClientID(clientID string) AuthInterface
|
|
UniversalAuthLogin(clientID string, clientSecret string) (credential MachineIdentityCredential, err error)
|
|
JwtAuthLogin(identityID string, jwt string) (credential MachineIdentityCredential, err error)
|
|
KubernetesAuthLogin(identityID string, serviceAccountTokenPath string) (credential MachineIdentityCredential, err error)
|
|
KubernetesRawServiceAccountTokenLogin(identityID string, serviceAccountToken string) (credential MachineIdentityCredential, err error)
|
|
AzureAuthLogin(identityID string, resource string) (credential MachineIdentityCredential, err error)
|
|
GcpIdTokenAuthLogin(identityID string) (credential MachineIdentityCredential, err error)
|
|
GcpIamAuthLogin(identityID string, serviceAccountKeyFilePath string) (credential MachineIdentityCredential, err error)
|
|
AwsIamAuthLogin(identityId string) (credential MachineIdentityCredential, err error)
|
|
OidcAuthLogin(identityId string, jwt string) (credential MachineIdentityCredential, err error)
|
|
OciAuthLogin(options OciAuthLoginOptions) (credential MachineIdentityCredential, err error)
|
|
LdapAuthLogin(identityID string, username string, password string) (credential MachineIdentityCredential, err error)
|
|
RevokeAccessToken() error
|
|
}
|
|
|
|
type Auth struct {
|
|
client *InfisicalClient
|
|
organizationSlug string
|
|
azureClientID string
|
|
}
|
|
|
|
func (a *Auth) SetAccessToken(accessToken string) {
|
|
a.client.setPlainAccessToken(accessToken)
|
|
}
|
|
|
|
func (a *Auth) GetOrganizationSlug() string {
|
|
return a.organizationSlug
|
|
}
|
|
|
|
func (a *Auth) WithOrganizationSlug(organizationSlug string) AuthInterface {
|
|
a.organizationSlug = organizationSlug
|
|
return a
|
|
}
|
|
|
|
func (a *Auth) WithAzureClientID(clientID string) AuthInterface {
|
|
a.azureClientID = clientID
|
|
return a
|
|
}
|
|
|
|
func (a *Auth) GetAccessToken() string {
|
|
// case: user has set an access token manually, so we get it directly from the credential
|
|
if a.client.authMethod == util.ACCESS_TOKEN {
|
|
if parsedCreds, ok := a.client.credential.(models.AccessTokenCredential); ok {
|
|
return parsedCreds.AccessToken
|
|
}
|
|
return ""
|
|
}
|
|
return a.client.tokenDetails.AccessToken
|
|
}
|
|
|
|
func (a *Auth) RevokeAccessToken() error {
|
|
if a.client.tokenDetails.AccessToken == "" {
|
|
return errors.New("sdk client is not authenticated, cannot revoke access token")
|
|
}
|
|
|
|
_, err := api.CallRevokeAccessToken(a.client.httpClient, api.RevokeAccessTokenRequest{
|
|
AccessToken: a.client.tokenDetails.AccessToken,
|
|
})
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
a.client.clearAccessToken()
|
|
|
|
return nil
|
|
}
|
|
func (a *Auth) UniversalAuthLogin(clientID string, clientSecret string) (credential MachineIdentityCredential, err error) {
|
|
|
|
if clientID == "" {
|
|
clientID = os.Getenv(util.INFISICAL_UNIVERSAL_AUTH_CLIENT_ID_ENV_NAME)
|
|
}
|
|
if clientSecret == "" {
|
|
clientSecret = os.Getenv(util.INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET_ENV_NAME)
|
|
}
|
|
organizationSlug := a.organizationSlug
|
|
if organizationSlug == "" {
|
|
organizationSlug = os.Getenv(util.INFISICAL_AUTH_ORGANIZATION_SLUG_ENV_NAME)
|
|
}
|
|
|
|
credential, err = api.CallUniversalAuthLogin(a.client.httpClient, api.UniversalAuthLoginRequest{
|
|
ClientID: clientID,
|
|
ClientSecret: clientSecret,
|
|
OrganizationSlug: organizationSlug,
|
|
})
|
|
|
|
if err != nil {
|
|
return MachineIdentityCredential{}, err
|
|
}
|
|
|
|
a.client.setAccessToken(
|
|
credential,
|
|
models.UniversalAuthCredential{ClientID: clientID, ClientSecret: clientSecret},
|
|
util.UNIVERSAL_AUTH,
|
|
)
|
|
return credential, nil
|
|
|
|
}
|
|
|
|
func (a *Auth) KubernetesAuthLogin(identityID string, serviceAccountTokenPath string) (credential MachineIdentityCredential, err error) {
|
|
|
|
if serviceAccountTokenPath == "" {
|
|
serviceAccountTokenPath = os.Getenv(util.DEFAULT_KUBERNETES_SERVICE_ACCOUNT_TOKEN_PATH)
|
|
}
|
|
if identityID == "" {
|
|
identityID = os.Getenv(util.INFISICAL_KUBERNETES_IDENTITY_ID_ENV_NAME)
|
|
}
|
|
organizationSlug := a.organizationSlug
|
|
if organizationSlug == "" {
|
|
organizationSlug = os.Getenv(util.INFISICAL_AUTH_ORGANIZATION_SLUG_ENV_NAME)
|
|
}
|
|
|
|
serviceAccountToken, serviceAccountTokenErr := util.GetKubernetesServiceAccountToken(serviceAccountTokenPath)
|
|
|
|
if serviceAccountTokenErr != nil {
|
|
return MachineIdentityCredential{}, serviceAccountTokenErr
|
|
}
|
|
|
|
credential, err = api.CallKubernetesAuthLogin(a.client.httpClient, api.KubernetesAuthLoginRequest{
|
|
IdentityID: identityID,
|
|
JWT: serviceAccountToken,
|
|
OrganizationSlug: organizationSlug,
|
|
})
|
|
|
|
if err != nil {
|
|
return MachineIdentityCredential{}, err
|
|
}
|
|
|
|
a.client.setAccessToken(
|
|
credential,
|
|
models.KubernetesCredential{IdentityID: identityID, ServiceAccountToken: serviceAccountToken},
|
|
util.KUBERNETES,
|
|
)
|
|
|
|
return credential, nil
|
|
|
|
}
|
|
|
|
func (a *Auth) KubernetesRawServiceAccountTokenLogin(identityID string, serviceAccountToken string) (credential MachineIdentityCredential, err error) {
|
|
|
|
if identityID == "" {
|
|
identityID = os.Getenv(util.INFISICAL_KUBERNETES_IDENTITY_ID_ENV_NAME)
|
|
}
|
|
organizationSlug := a.organizationSlug
|
|
if organizationSlug == "" {
|
|
organizationSlug = os.Getenv(util.INFISICAL_AUTH_ORGANIZATION_SLUG_ENV_NAME)
|
|
}
|
|
|
|
credential, err = api.CallKubernetesAuthLogin(a.client.httpClient, api.KubernetesAuthLoginRequest{
|
|
IdentityID: identityID,
|
|
JWT: serviceAccountToken,
|
|
OrganizationSlug: organizationSlug,
|
|
})
|
|
|
|
if err != nil {
|
|
return MachineIdentityCredential{}, err
|
|
}
|
|
|
|
a.client.setAccessToken(
|
|
credential,
|
|
models.KubernetesCredential{IdentityID: identityID, ServiceAccountToken: serviceAccountToken},
|
|
util.KUBERNETES,
|
|
)
|
|
return credential, nil
|
|
}
|
|
|
|
func (a *Auth) AzureAuthLogin(identityID string, resource string) (credential MachineIdentityCredential, err error) {
|
|
if identityID == "" {
|
|
identityID = os.Getenv(util.INFISICAL_AZURE_AUTH_IDENTITY_ID_ENV_NAME)
|
|
}
|
|
clientID := a.azureClientID
|
|
if clientID == "" {
|
|
clientID = os.Getenv(util.INFISICAL_AZURE_AUTH_CLIENT_ID_ENV_NAME)
|
|
}
|
|
organizationSlug := a.organizationSlug
|
|
if organizationSlug == "" {
|
|
organizationSlug = os.Getenv(util.INFISICAL_AUTH_ORGANIZATION_SLUG_ENV_NAME)
|
|
}
|
|
|
|
jwt, jwtError := util.GetAzureMetadataToken(a.client.httpClient, resource, clientID)
|
|
|
|
if jwtError != nil {
|
|
return MachineIdentityCredential{}, jwtError
|
|
}
|
|
|
|
credential, err = api.CallAzureAuthLogin(a.client.httpClient, api.AzureAuthLoginRequest{
|
|
IdentityID: identityID,
|
|
JWT: jwt,
|
|
OrganizationSlug: organizationSlug,
|
|
})
|
|
|
|
if err != nil {
|
|
return MachineIdentityCredential{}, err
|
|
}
|
|
|
|
a.client.setAccessToken(
|
|
credential,
|
|
models.AzureCredential{IdentityID: identityID, Resource: resource, ClientID: clientID},
|
|
util.AZURE,
|
|
)
|
|
return credential, nil
|
|
}
|
|
|
|
func (a *Auth) GcpIdTokenAuthLogin(identityID string) (credential MachineIdentityCredential, err error) {
|
|
if identityID == "" {
|
|
identityID = os.Getenv(util.INFISICAL_GCP_AUTH_IDENTITY_ID_ENV_NAME)
|
|
}
|
|
organizationSlug := a.organizationSlug
|
|
if organizationSlug == "" {
|
|
organizationSlug = os.Getenv(util.INFISICAL_AUTH_ORGANIZATION_SLUG_ENV_NAME)
|
|
}
|
|
|
|
jwt, jwtError := util.GetGCPMetadataToken(a.client.httpClient, identityID)
|
|
|
|
if jwtError != nil {
|
|
return MachineIdentityCredential{}, jwtError
|
|
}
|
|
|
|
credential, err = api.CallGCPAuthLogin(a.client.httpClient, api.GCPAuthLoginRequest{
|
|
IdentityID: identityID,
|
|
JWT: jwt,
|
|
OrganizationSlug: organizationSlug,
|
|
})
|
|
|
|
if err != nil {
|
|
return MachineIdentityCredential{}, err
|
|
}
|
|
|
|
a.client.setAccessToken(
|
|
credential,
|
|
models.GCPIDTokenCredential{IdentityID: identityID},
|
|
util.GCP_ID_TOKEN,
|
|
)
|
|
return credential, nil
|
|
}
|
|
|
|
func (a *Auth) GcpIamAuthLogin(identityID string, serviceAccountKeyFilePath string) (credential MachineIdentityCredential, err error) {
|
|
if identityID == "" {
|
|
identityID = os.Getenv(util.INFISICAL_GCP_AUTH_IDENTITY_ID_ENV_NAME)
|
|
}
|
|
if serviceAccountKeyFilePath == "" {
|
|
serviceAccountKeyFilePath = os.Getenv(util.INFISICAL_GCP_IAM_SERVICE_ACCOUNT_KEY_FILE_PATH_ENV_NAME)
|
|
}
|
|
organizationSlug := a.organizationSlug
|
|
if organizationSlug == "" {
|
|
organizationSlug = os.Getenv(util.INFISICAL_AUTH_ORGANIZATION_SLUG_ENV_NAME)
|
|
}
|
|
|
|
jwt, jwtError := util.GetGCPIamServiceAccountToken(identityID, serviceAccountKeyFilePath)
|
|
|
|
if jwtError != nil {
|
|
return MachineIdentityCredential{}, jwtError
|
|
}
|
|
|
|
credential, err = api.CallGCPAuthLogin(a.client.httpClient, api.GCPAuthLoginRequest{
|
|
IdentityID: identityID,
|
|
JWT: jwt,
|
|
OrganizationSlug: organizationSlug,
|
|
})
|
|
|
|
if err != nil {
|
|
return MachineIdentityCredential{}, err
|
|
}
|
|
|
|
a.client.setAccessToken(
|
|
credential,
|
|
models.GCPIAMCredential{IdentityID: identityID, ServiceAccountKeyFilePath: serviceAccountKeyFilePath},
|
|
util.GCP_IAM,
|
|
)
|
|
return credential, nil
|
|
}
|
|
|
|
func (a *Auth) AwsIamAuthLogin(identityId string) (credential MachineIdentityCredential, err error) {
|
|
|
|
if identityId == "" {
|
|
identityId = os.Getenv(util.INFISICAL_AWS_IAM_AUTH_IDENTITY_ID_ENV_NAME)
|
|
}
|
|
organizationSlug := a.organizationSlug
|
|
if organizationSlug == "" {
|
|
organizationSlug = os.Getenv(util.INFISICAL_AUTH_ORGANIZATION_SLUG_ENV_NAME)
|
|
}
|
|
|
|
awsCredentials, awsRegion, err := util.RetrieveAwsCredentials()
|
|
if err != nil {
|
|
return MachineIdentityCredential{}, err
|
|
}
|
|
|
|
// Prepare request for signing
|
|
iamRequestURL := fmt.Sprintf("https://sts.%s.amazonaws.com/", awsRegion)
|
|
iamRequestBody := "Action=GetCallerIdentity&Version=2011-06-15"
|
|
|
|
req, err := http.NewRequest(http.MethodPost, iamRequestURL, strings.NewReader(iamRequestBody))
|
|
if err != nil {
|
|
return MachineIdentityCredential{}, fmt.Errorf("error creating HTTP request: %v", err)
|
|
}
|
|
|
|
currentTime := time.Now().UTC()
|
|
req.Header.Add("X-Amz-Date", currentTime.Format("20060102T150405Z"))
|
|
|
|
hashGenerator := sha256.New()
|
|
hashGenerator.Write([]byte(iamRequestBody))
|
|
payloadHash := fmt.Sprintf("%x", hashGenerator.Sum(nil))
|
|
|
|
signer := v4.NewSigner()
|
|
err = signer.SignHTTP(context.TODO(), awsCredentials, req, payloadHash, "sts", awsRegion, time.Now())
|
|
|
|
if err != nil {
|
|
return MachineIdentityCredential{}, fmt.Errorf("error signing request: %v", err)
|
|
}
|
|
|
|
realHeaders := make(map[string]string)
|
|
for name, values := range req.Header {
|
|
if strings.ToLower(name) == "content-length" {
|
|
continue
|
|
}
|
|
realHeaders[name] = values[0]
|
|
}
|
|
realHeaders["Host"] = fmt.Sprintf("sts.%s.amazonaws.com", awsRegion)
|
|
realHeaders["Content-Type"] = "application/x-www-form-urlencoded; charset=utf-8"
|
|
realHeaders["Content-Length"] = fmt.Sprintf("%d", len(iamRequestBody))
|
|
|
|
// convert the headers to a json marshalled string
|
|
jsonStringHeaders, err := json.Marshal(realHeaders)
|
|
|
|
if err != nil {
|
|
return MachineIdentityCredential{}, fmt.Errorf("error marshalling headers: %v", err)
|
|
}
|
|
|
|
credential, tokenErr := api.CallAWSIamAuthLogin(a.client.httpClient, api.AwsIamAuthLoginRequest{
|
|
HTTPRequestMethod: req.Method,
|
|
// Encoding is intended, we decode it on severside, and I know everything happening on the server is being done correctly. So it's something broken in this code somewhere.
|
|
IamRequestBody: base64.StdEncoding.EncodeToString([]byte(iamRequestBody)),
|
|
IamRequestHeaders: base64.StdEncoding.EncodeToString(jsonStringHeaders),
|
|
IdentityId: identityId,
|
|
OrganizationSlug: organizationSlug,
|
|
})
|
|
|
|
if tokenErr != nil {
|
|
return MachineIdentityCredential{}, tokenErr
|
|
}
|
|
|
|
a.client.setAccessToken(
|
|
credential,
|
|
models.AWSIAMCredential{IdentityID: identityId},
|
|
util.AWS_IAM,
|
|
)
|
|
return credential, nil
|
|
}
|
|
|
|
func (a *Auth) OidcAuthLogin(identityId string, jwt string) (credential MachineIdentityCredential, err error) {
|
|
if identityId == "" {
|
|
identityId = os.Getenv(util.INFISICAL_OIDC_AUTH_IDENTITY_ID_ENV_NAME)
|
|
}
|
|
organizationSlug := a.organizationSlug
|
|
if organizationSlug == "" {
|
|
organizationSlug = os.Getenv(util.INFISICAL_AUTH_ORGANIZATION_SLUG_ENV_NAME)
|
|
}
|
|
|
|
credential, err = api.CallOidcAuthLogin(a.client.httpClient, api.OidcAuthLoginRequest{
|
|
IdentityID: identityId,
|
|
JWT: jwt,
|
|
OrganizationSlug: organizationSlug,
|
|
})
|
|
|
|
if err != nil {
|
|
return MachineIdentityCredential{}, err
|
|
}
|
|
|
|
a.client.setAccessToken(
|
|
credential,
|
|
models.OIDCCredential{IdentityID: identityId},
|
|
util.OIDC_AUTH,
|
|
)
|
|
return credential, nil
|
|
|
|
}
|
|
|
|
func (a *Auth) JwtAuthLogin(identityID string, jwt string) (credential MachineIdentityCredential, err error) {
|
|
organizationSlug := a.organizationSlug
|
|
if organizationSlug == "" {
|
|
organizationSlug = os.Getenv(util.INFISICAL_AUTH_ORGANIZATION_SLUG_ENV_NAME)
|
|
}
|
|
|
|
credential, err = api.CallJwtAuthLogin(a.client.httpClient, api.JwtAuthLoginRequest{
|
|
IdentityID: identityID,
|
|
JWT: jwt,
|
|
OrganizationSlug: organizationSlug,
|
|
})
|
|
|
|
if err != nil {
|
|
return MachineIdentityCredential{}, err
|
|
}
|
|
|
|
a.client.setAccessToken(
|
|
credential,
|
|
models.JWTCredential{IdentityID: identityID, JWT: jwt},
|
|
util.JWT_AUTH,
|
|
)
|
|
return credential, nil
|
|
}
|
|
|
|
func (a *Auth) OciAuthLogin(options OciAuthLoginOptions) (credential MachineIdentityCredential, err error) {
|
|
|
|
if options.IdentityID == "" {
|
|
options.IdentityID = os.Getenv(util.INFISICAL_OCI_AUTH_IDENTITY_ID_ENV_NAME)
|
|
}
|
|
organizationSlug := a.organizationSlug
|
|
if organizationSlug == "" {
|
|
organizationSlug = os.Getenv(util.INFISICAL_AUTH_ORGANIZATION_SLUG_ENV_NAME)
|
|
}
|
|
|
|
provider := common.NewRawConfigurationProvider(
|
|
options.TenancyID,
|
|
options.UserID,
|
|
options.Region,
|
|
options.Fingerprint,
|
|
options.PrivateKey,
|
|
options.Passphrase,
|
|
)
|
|
|
|
requestURL := fmt.Sprintf("https://identity.%s.oraclecloud.com/20160918/users/%s", options.Region, options.UserID)
|
|
|
|
req, err := http.NewRequest("GET", requestURL, nil)
|
|
if err != nil {
|
|
return MachineIdentityCredential{}, fmt.Errorf("OciAuthLogin: failed to create request: %w", err)
|
|
}
|
|
|
|
req.Header.Set("host", fmt.Sprintf("identity.%s.oraclecloud.com", options.Region))
|
|
req.Header.Set("Date", time.Now().UTC().Format(http.TimeFormat))
|
|
|
|
signer := common.DefaultRequestSigner(provider)
|
|
err = signer.Sign(req)
|
|
if err != nil {
|
|
return MachineIdentityCredential{}, fmt.Errorf("OciAuthLogin: failed to sign request: %w", err)
|
|
}
|
|
|
|
headersMap := make(map[string]string)
|
|
for name, values := range req.Header {
|
|
if len(values) > 0 {
|
|
// Convert header names to lowercase to match OCI signature expectations
|
|
lowerName := strings.ToLower(name)
|
|
headersMap[lowerName] = values[0]
|
|
}
|
|
}
|
|
|
|
credential, err = api.CallOciAuthLogin(a.client.httpClient, api.OciAuthLoginRequest{
|
|
IdentityID: options.IdentityID,
|
|
UserOcid: options.UserID,
|
|
Headers: headersMap,
|
|
OrganizationSlug: organizationSlug,
|
|
})
|
|
|
|
if err != nil {
|
|
return MachineIdentityCredential{}, err
|
|
}
|
|
|
|
a.client.setAccessToken(
|
|
credential,
|
|
models.OCICredential{
|
|
IdentityID: options.IdentityID,
|
|
PrivateKey: options.PrivateKey,
|
|
Fingerprint: options.Fingerprint,
|
|
UserID: options.UserID,
|
|
TenancyID: options.TenancyID,
|
|
Region: options.Region,
|
|
Passphrase: options.Passphrase,
|
|
},
|
|
util.OCI_AUTH,
|
|
)
|
|
return credential, nil
|
|
}
|
|
|
|
func (a *Auth) LdapAuthLogin(identityID string, username string, password string) (credential MachineIdentityCredential, err error) {
|
|
if identityID == "" {
|
|
identityID = os.Getenv(util.INFISICAL_LDAP_AUTH_IDENTITY_ID_ENV_NAME)
|
|
}
|
|
organizationSlug := a.organizationSlug
|
|
if organizationSlug == "" {
|
|
organizationSlug = os.Getenv(util.INFISICAL_AUTH_ORGANIZATION_SLUG_ENV_NAME)
|
|
}
|
|
|
|
credential, err = api.CallLdapAuthLogin(a.client.httpClient, api.LdapAuthLoginRequest{
|
|
IdentityID: identityID,
|
|
Username: username,
|
|
Password: password,
|
|
OrganizationSlug: organizationSlug,
|
|
})
|
|
|
|
if err != nil {
|
|
return MachineIdentityCredential{}, err
|
|
}
|
|
|
|
a.client.setAccessToken(
|
|
credential,
|
|
models.LDAPCredential{IdentityID: identityID, Username: username, Password: password},
|
|
util.LDAP_AUTH,
|
|
)
|
|
|
|
return credential, nil
|
|
}
|
|
|
|
func NewAuth(client *InfisicalClient) AuthInterface {
|
|
return &Auth{client: client}
|
|
}
|