Adjusting for custom cloud
This commit is contained in:
Родитель
8987236793
Коммит
72ff98110f
|
@ -13,19 +13,46 @@ import (
|
|||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"internal/utils"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets"
|
||||
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential"
|
||||
gpkcs12 "software.sslmate.com/src/go-pkcs12"
|
||||
)
|
||||
|
||||
const (
|
||||
ERR_AUTHORIZER = 2
|
||||
ERR_INVALID_ARGUMENT = 3
|
||||
ERR_INVALID_URL = 4
|
||||
ERR_GET_AKV_CERT_SECRET = 5
|
||||
ERR_GET_PEM_PRIVATE_KEY = 6
|
||||
ERR_GET_PEM_CERTIFICATE = 7
|
||||
ERR_CREATE_PEM_FILE = 8
|
||||
ERR_X509_THUMBPRINT = 9
|
||||
ERR_OUTPUTFOLDER_NOT_FOUND = 10
|
||||
ERR_INVALID_AZURE_ENVIRONMENT = 11
|
||||
ERR_CREDENTIALS = 12
|
||||
ERR_INVALID_CREDENTIAL_ARGS = 13
|
||||
ERR_CLOUD_CONFIG_FILE_ONLY_FOR_CUSTOM_CLOUD = 180
|
||||
ERR_CLOUD_CONFIG_FILE_NOT_FOUND = 181
|
||||
ERR_CLOUD_CONFIG_FILE_REQUIRED_FOR_CUSTOM_CLOUD = 182
|
||||
)
|
||||
|
||||
var (
|
||||
Stdout = log.New(os.Stdout, "", log.LstdFlags)
|
||||
Stderr = log.New(os.Stderr, "", log.LstdFlags)
|
||||
)
|
||||
|
||||
// GetBlocksFromPEM - Gets decoded data block from PEM
|
||||
func GetBlocksFromPEM(data []byte, blocks []*pem.Block) []*pem.Block {
|
||||
block, rest := pem.Decode(data)
|
||||
|
@ -130,7 +157,7 @@ func GetPrivateKeyFromPEMBlocks(blocks interface{}) (privateKey interface{}, err
|
|||
}
|
||||
|
||||
//
|
||||
//!SECTION - Azcore SDK related functions
|
||||
// Azcore SDK related functions
|
||||
// Source: https://github.com/Azure/azure-workload-identity/tree/main/examples
|
||||
//
|
||||
|
||||
|
@ -197,8 +224,162 @@ func (c *clientAssertionCredential) getAssertion(context.Context) (string, error
|
|||
return c.assertion, nil
|
||||
}
|
||||
|
||||
func GetTokenCredentials(managedIdentityId string, useSystemManagedIdentity bool) (azcore.TokenCredential, error) {
|
||||
var cred azcore.TokenCredential
|
||||
var err error
|
||||
|
||||
tokenFilePath := os.Getenv("AZURE_FEDERATED_TOKEN_FILE")
|
||||
if tokenFilePath == "" {
|
||||
// Not running within a container with azwi webhook configured
|
||||
utils.ConsoleOutput("Obtaining credentials", Stdout)
|
||||
|
||||
if managedIdentityId == "" && !useSystemManagedIdentity {
|
||||
cred, err = azidentity.NewDefaultAzureCredential(nil)
|
||||
} else if useSystemManagedIdentity {
|
||||
cred, err = azidentity.NewManagedIdentityCredential(nil)
|
||||
} else if managedIdentityId != "" {
|
||||
opts := azidentity.ManagedIdentityCredentialOptions{}
|
||||
|
||||
if strings.Contains(managedIdentityId, "/") {
|
||||
opts = azidentity.ManagedIdentityCredentialOptions{
|
||||
ID: azidentity.ResourceID(managedIdentityId),
|
||||
}
|
||||
} else {
|
||||
opts = azidentity.ManagedIdentityCredentialOptions{
|
||||
ID: azidentity.ClientID(managedIdentityId),
|
||||
}
|
||||
}
|
||||
|
||||
cred, err = azidentity.NewManagedIdentityCredential(&opts)
|
||||
} else {
|
||||
return nil, fmt.Errorf("authentication method not supported")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("an error ocurred while obtaining : %v", err)
|
||||
}
|
||||
} else {
|
||||
|
||||
// NOTE: following block is based on azure workload identity sample:
|
||||
// https://github.dev/Azure/azure-workload-identity/blob/main/examples/msal-net/akvdotnet/TokenCredential.cs
|
||||
//
|
||||
|
||||
// Azure AD Workload Identity webhook will inject the following env vars
|
||||
// AZURE_CLIENT_ID with the clientID set in the service account annotation
|
||||
// AZURE_TENANT_ID with the tenantID set in the service account annotation. If not defined, then
|
||||
// the tenantID provided via azure-wi-webhook-config for the webhook will be used.
|
||||
// AZURE_FEDERATED_TOKEN_FILE is the service account token path
|
||||
// AZURE_AUTHORITY_HOST is the AAD authority hostname
|
||||
clientID := os.Getenv("AZURE_CLIENT_ID")
|
||||
tenantID := os.Getenv("AZURE_TENANT_ID")
|
||||
tokenFilePath := os.Getenv("AZURE_FEDERATED_TOKEN_FILE")
|
||||
authorityHost := os.Getenv("AZURE_AUTHORITY_HOST")
|
||||
|
||||
if clientID == "" {
|
||||
return nil, fmt.Errorf("an error ocurred: AZURE_CLIENT_ID environment variable is not set")
|
||||
}
|
||||
if tenantID == "" {
|
||||
return nil, fmt.Errorf("an error ocurred: AZURE_TENANT_ID environment variable is not set")
|
||||
}
|
||||
if authorityHost == "" {
|
||||
return nil, fmt.Errorf("an error ocurred: AZURE_AUTHORITY_HOST environment variable is not set")
|
||||
}
|
||||
|
||||
cred, err = NewClientAssertionCredential(tenantID, clientID, authorityHost, tokenFilePath, nil)
|
||||
if err != nil {
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> failed to create client assertion credential: %v\n", err), Stderr)
|
||||
return nil, fmt.Errorf("an error ocurred: AZURE_CLIENT_ID environment variable is not set")
|
||||
}
|
||||
}
|
||||
|
||||
return cred, nil
|
||||
}
|
||||
|
||||
func getCloudConfiguration(environment, cloudConfigFile string) (cloud.Configuration, error) {
|
||||
cloudConfig := cloud.Configuration{}
|
||||
|
||||
if environment == "AZUREUSGOVERNMENTCLOUD" {
|
||||
cloudConfig = cloud.AzureGovernment
|
||||
} else if environment == "AZURECHINACLOUD" {
|
||||
cloudConfig = cloud.AzureChina
|
||||
} else if environment == "CUSTOMCLOUD" {
|
||||
|
||||
// This is the mapping between values expected on cloud.Configuration
|
||||
// and the output of az cloud show -n AzureCloud -o json
|
||||
//
|
||||
// ActiveDirectoryAuthorityHost = endpoints.activeDirectory (e.g."https://login.microsoftonline.us")
|
||||
// Endpoint = endpoints.resourceManager (e.g. "https://management.usgovcloudapi.net")
|
||||
// Audience = endpoints.activeDirectoryResourceId (e.g. "https://management.core.usgovcloudapi.net")
|
||||
|
||||
if cloudConfigFile != "" {
|
||||
cloudInfo, err := utils.ImportCloudConfigJson(cloudConfigFile)
|
||||
if err != nil {
|
||||
return cloud.Configuration{}, fmt.Errorf("an error ocurred while importing cloud config information from json file: %v", err)
|
||||
}
|
||||
|
||||
cloudConfig = cloud.Configuration{
|
||||
ActiveDirectoryAuthorityHost: cloudInfo.Endpoints.ActiveDirectoryAuthorityHost,
|
||||
Services: map[cloud.ServiceName]cloud.ServiceConfiguration{
|
||||
cloud.ResourceManager: {
|
||||
Endpoint: cloudInfo.Endpoints.ResourceManagerEndpoint,
|
||||
Audience: cloudInfo.Endpoints.ResourceManagerEndpoint,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
cloudConfig = cloud.AzurePublic
|
||||
}
|
||||
|
||||
return cloudConfig, nil
|
||||
}
|
||||
|
||||
// GetCertsClient returns a certs client
|
||||
func GetCertsClient(keyVaultUrl, environment, cloudConfigFile string, cred azcore.TokenCredential) (azcertificates.Client, error) {
|
||||
cloudConfig, err := getCloudConfiguration(environment, cloudConfigFile)
|
||||
if err != nil {
|
||||
return azcertificates.Client{}, fmt.Errorf("failed to create cloudConfig object: %v\n", err)
|
||||
}
|
||||
|
||||
options := azcertificates.ClientOptions{
|
||||
ClientOptions: azcore.ClientOptions{
|
||||
Cloud: cloudConfig,
|
||||
},
|
||||
}
|
||||
|
||||
azcertsClient, err := azcertificates.NewClient(keyVaultUrl, cred, &options)
|
||||
if err != nil {
|
||||
return azcertificates.Client{}, fmt.Errorf("failed to create azcerts client: %v\n", err)
|
||||
}
|
||||
|
||||
return *azcertsClient, nil
|
||||
}
|
||||
|
||||
// GetSecretsClient returns an azsecrets.Client
|
||||
func GetSecretsClient(keyVaultUrl, environment, cloudConfigFile string, cred azcore.TokenCredential) (azsecrets.Client, error) {
|
||||
|
||||
cloudConfig, err := getCloudConfiguration(environment, cloudConfigFile)
|
||||
if err != nil {
|
||||
return azsecrets.Client{}, fmt.Errorf("failed to create cloudConfig object: %v\n", err)
|
||||
}
|
||||
|
||||
options := azsecrets.ClientOptions{
|
||||
ClientOptions: azcore.ClientOptions{
|
||||
Cloud: cloudConfig,
|
||||
},
|
||||
}
|
||||
|
||||
azsecretsClient, err := azsecrets.NewClient(keyVaultUrl, cred, &options)
|
||||
if err != nil {
|
||||
return azsecrets.Client{}, fmt.Errorf("failed to create azsecrets client: %v\n", err)
|
||||
}
|
||||
|
||||
return *azsecretsClient, nil
|
||||
}
|
||||
|
||||
//
|
||||
//!SECTION - Keyvault SDK related functions
|
||||
// Keyvault SDK related functions
|
||||
//
|
||||
|
||||
func getAKVCertificateBundle(cntx context.Context, client *azcertificates.Client, certURL url.URL) (azcertificates.CertificateBundle, error) {
|
||||
|
|
|
@ -25,3 +25,15 @@ type AzureBasicInfo struct {
|
|||
ResourceManagerEndpointURL *string
|
||||
ManagementEndpointURL *string
|
||||
}
|
||||
|
||||
// Endpoints object definition
|
||||
type Endpoints struct {
|
||||
ActiveDirectoryAuthorityHost string `json:"activeDirectory"`
|
||||
ResourceManagerEndpoint string `json:"resourceManager"`
|
||||
ResourceManagerAudience string `json:"activeDirectoryResourceId"`
|
||||
}
|
||||
|
||||
// CloudConfigInfo object definition, used to map the output of az cloud show -n <cloud name> -o json
|
||||
type CloudConfigInfo struct {
|
||||
Endpoints Endpoints `json:"endpoints"`
|
||||
}
|
||||
|
|
|
@ -12,11 +12,17 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"internal/models"
|
||||
)
|
||||
|
||||
var (
|
||||
stdout = log.New(os.Stdout, "", log.LstdFlags)
|
||||
stderr = log.New(os.Stderr, "", log.LstdFlags)
|
||||
)
|
||||
|
||||
// PrintHeader prints a header message
|
||||
func PrintHeader(header string) {
|
||||
fmt.Println(header)
|
||||
|
@ -60,3 +66,17 @@ func FindInSlice(slice []string, val string) (int, bool) {
|
|||
}
|
||||
return -1, false
|
||||
}
|
||||
|
||||
// ImportCloudConfigJson imports the cloud config json file and returns a struct
|
||||
func ImportCloudConfigJson(path string) (*models.CloudConfigInfo, error) {
|
||||
infoJSON, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
ConsoleOutput(fmt.Sprintf("failed to read file: %v", err), stderr)
|
||||
return &models.CloudConfigInfo{}, err
|
||||
}
|
||||
|
||||
// Converting json to struct
|
||||
var info models.CloudConfigInfo
|
||||
json.Unmarshal(infoJSON, &info)
|
||||
return &info, nil
|
||||
}
|
||||
|
|
|
@ -14,47 +14,25 @@ import (
|
|||
"encoding/hex"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"internal/corehelper"
|
||||
"internal/utils"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azcertificates"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/keyvault/azsecrets"
|
||||
)
|
||||
|
||||
const (
|
||||
ERR_AUTHORIZER = 2
|
||||
ERR_INVALID_ARGUMENT = 3
|
||||
ERR_INVALID_URL = 4
|
||||
ERR_GET_AKV_CERT_SECRET = 5
|
||||
ERR_GET_PEM_PRIVATE_KEY = 6
|
||||
ERR_GET_PEM_CERTIFICATE = 7
|
||||
ERR_CREATE_PEM_FILE = 8
|
||||
ERR_X509_THUMBPRINT = 9
|
||||
ERR_OUTPUTFOLDER_NOT_FOUND = 10
|
||||
ERR_INVALID_AZURE_ENVIRONMENT = 11
|
||||
ERR_CREDENTIALS = 12
|
||||
ERR_INVALID_CREDENTIAL_ARGS = 13
|
||||
)
|
||||
|
||||
var (
|
||||
validEnvironments = []string{"AZUREPUBLICCLOUD", "AZUREUSGOVERNMENTCLOUD", "AZUREGERMANCLOUD", "AZURECHINACLOUD"}
|
||||
validEnvironments = []string{"AZUREPUBLICCLOUD", "AZUREUSGOVERNMENTCLOUD", "AZURECHINACLOUD", "CUSTOMCLOUD"}
|
||||
certURL = flag.String("certurl", "", "certificate URL, e.g. \"https://mykeyvault.vault.azure.net/mycertificate\"")
|
||||
outputFolder = flag.String("outputfolder", "", "folder where PEM file with certificate and private key will be saved")
|
||||
environment = flag.String("environment", "AZUREPUBLICCLOUD", fmt.Sprintf("valid azure cloud environments: %v", validEnvironments))
|
||||
cmdlineversion = flag.Bool("version", false, "shows current tool version")
|
||||
managedIdentityId = flag.String("managed-identity-id", "", "uses user managed identities (accepts resource id or client id)")
|
||||
useSystemManagedIdentity = flag.Bool("use-system-managed-identity", false, "uses system managed identity")
|
||||
cloudConfigFile = flag.String("custom-cloudconfig-file", "", "passes a custom cloud configuration to the sdk for use with non-public azure clouds, only used for CUSTOMCLOUD environment")
|
||||
exitCode = 0
|
||||
version = "1.1.1"
|
||||
stdout = log.New(os.Stdout, "", log.LstdFlags)
|
||||
stderr = log.New(os.Stderr, "", log.LstdFlags)
|
||||
version = "1.5.0"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -66,8 +44,8 @@ func main() {
|
|||
flag.Parse()
|
||||
|
||||
if len(os.Args[1:]) < 1 {
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> invalid number of arguments, please execute %v -h or --help for more information", os.Args[0]), stderr)
|
||||
exitCode = ERR_INVALID_ARGUMENT
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> invalid number of arguments, please execute %v -h or --help for more information", os.Args[0]), corehelper.Stderr)
|
||||
exitCode = corehelper.ERR_INVALID_ARGUMENT
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -80,163 +58,121 @@ func main() {
|
|||
|
||||
// Checks if outputfolder exists
|
||||
if _, err := os.Stat(*outputFolder); err != nil {
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> output folder %v not found", *outputFolder), stderr)
|
||||
exitCode = ERR_OUTPUTFOLDER_NOT_FOUND
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> output folder %v not found", *outputFolder), corehelper.Stderr)
|
||||
exitCode = corehelper.ERR_OUTPUTFOLDER_NOT_FOUND
|
||||
return
|
||||
}
|
||||
|
||||
// Checks if valid cloud environment was passed
|
||||
_, found := utils.FindInSlice(validEnvironments, strings.ToUpper(*environment))
|
||||
if !found {
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> invalid azure environment (%v), valid environments are: %v", *environment, validEnvironments), stderr)
|
||||
exitCode = ERR_INVALID_AZURE_ENVIRONMENT
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> invalid azure environment (%v), valid environments are: %v", *environment, validEnvironments), corehelper.Stderr)
|
||||
exitCode = corehelper.ERR_INVALID_AZURE_ENVIRONMENT
|
||||
return
|
||||
}
|
||||
|
||||
// Checks if both user managed identity and system managed identities were set
|
||||
if *managedIdentityId != "" && *useSystemManagedIdentity {
|
||||
utils.ConsoleOutput("<error> invalid authentication options, user and system assigned managed identities arguments cannot be used at the same time", stderr)
|
||||
exitCode = ERR_INVALID_CREDENTIAL_ARGS
|
||||
utils.ConsoleOutput("<error> invalid authentication options, user and system assigned managed identities arguments cannot be used at the same time", corehelper.Stderr)
|
||||
exitCode = corehelper.ERR_INVALID_CREDENTIAL_ARGS
|
||||
return
|
||||
}
|
||||
|
||||
// Checks if custom cloud file is only passed when custom cloud option is used
|
||||
if strings.ToUpper(*environment) != "CUSTOMCLOUD" && *cloudConfigFile != "" {
|
||||
utils.ConsoleOutput("<error> cloud config file is only supported for custom cloud", corehelper.Stderr)
|
||||
exitCode = corehelper.ERR_CLOUD_CONFIG_FILE_ONLY_FOR_CUSTOM_CLOUD
|
||||
return
|
||||
}
|
||||
|
||||
// Checks if cloud config file is passed when using custom cloud
|
||||
if strings.ToUpper(*environment) == "CUSTOMCLOUD" && *cloudConfigFile == "" {
|
||||
utils.ConsoleOutput("<error> cloud config file is required for custom cloud", corehelper.Stderr)
|
||||
exitCode = corehelper.ERR_CLOUD_CONFIG_FILE_REQUIRED_FOR_CUSTOM_CLOUD
|
||||
return
|
||||
}
|
||||
|
||||
if strings.ToUpper(*environment) == "CUSTOMCLOUD" && *cloudConfigFile != "" {
|
||||
// Checks if custom cloud config file exists
|
||||
if _, err := os.Stat(*cloudConfigFile); os.IsNotExist(err) {
|
||||
utils.ConsoleOutput("<error> cloud config file not found", corehelper.Stderr)
|
||||
exitCode = corehelper.ERR_CLOUD_CONFIG_FILE_NOT_FOUND
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Creates URL object
|
||||
u, err := url.Parse(*certURL)
|
||||
if err != nil {
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> an error occurred parsing cert url: %v", err), stderr)
|
||||
exitCode = ERR_INVALID_URL
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> an error occurred parsing cert url: %v", err), corehelper.Stderr)
|
||||
exitCode = corehelper.ERR_INVALID_URL
|
||||
return
|
||||
}
|
||||
|
||||
keyVaultUrl := fmt.Sprintf("%v://%v/", u.Scheme, u.Hostname())
|
||||
|
||||
utils.PrintHeader(fmt.Sprintf("keyvaultcertdownloader - Downloads a certificate from Azure KeyVault saving as PEM file - v%v", version))
|
||||
|
||||
utils.ConsoleOutput(fmt.Sprintf("Output Folder: %v", *outputFolder), stdout)
|
||||
utils.ConsoleOutput(fmt.Sprintf("Using Certificate URL: %v", *certURL), stdout)
|
||||
utils.ConsoleOutput(fmt.Sprintf("Environment: %v", *environment), stdout)
|
||||
utils.ConsoleOutput(fmt.Sprintf("Output Folder: %v", *outputFolder), corehelper.Stdout)
|
||||
utils.ConsoleOutput(fmt.Sprintf("Using Certificate URL: %v", *certURL), corehelper.Stdout)
|
||||
utils.ConsoleOutput(fmt.Sprintf("Environment: %v", *environment), corehelper.Stdout)
|
||||
|
||||
utils.ConsoleOutput("Checking if this session needs to rely on AD Workload Identity webhook", stdout)
|
||||
var cred azcore.TokenCredential
|
||||
utils.ConsoleOutput("Checking if this session needs to rely on AD Workload Identity webhook", corehelper.Stdout)
|
||||
|
||||
tokenFilePath := os.Getenv("AZURE_FEDERATED_TOKEN_FILE")
|
||||
if tokenFilePath == "" {
|
||||
// Not running within a container with azwi webhook configured
|
||||
utils.ConsoleOutput("Obtaining credentials", stdout)
|
||||
|
||||
if *managedIdentityId == "" && !*useSystemManagedIdentity {
|
||||
cred, err = azidentity.NewDefaultAzureCredential(nil)
|
||||
} else if *useSystemManagedIdentity {
|
||||
cred, err = azidentity.NewManagedIdentityCredential(nil)
|
||||
} else if *managedIdentityId != "" {
|
||||
opts := azidentity.ManagedIdentityCredentialOptions{}
|
||||
|
||||
if strings.Contains(*managedIdentityId, "/") {
|
||||
opts = azidentity.ManagedIdentityCredentialOptions{
|
||||
ID: azidentity.ResourceID(*managedIdentityId),
|
||||
}
|
||||
} else {
|
||||
opts = azidentity.ManagedIdentityCredentialOptions{
|
||||
ID: azidentity.ClientID(*managedIdentityId),
|
||||
}
|
||||
}
|
||||
|
||||
cred, err = azidentity.NewManagedIdentityCredential(&opts)
|
||||
} else {
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> %v\n", err), stderr)
|
||||
exitCode = ERR_CREDENTIALS
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> %v\n", err), stderr)
|
||||
exitCode = ERR_CREDENTIALS
|
||||
return
|
||||
}
|
||||
} else {
|
||||
|
||||
// NOTE: following block is based on azure workload identity sample:
|
||||
// https://github.dev/Azure/azure-workload-identity/blob/main/examples/msal-net/akvdotnet/TokenCredential.cs
|
||||
//
|
||||
|
||||
// Azure AD Workload Identity webhook will inject the following env vars
|
||||
// AZURE_CLIENT_ID with the clientID set in the service account annotation
|
||||
// AZURE_TENANT_ID with the tenantID set in the service account annotation. If not defined, then
|
||||
// the tenantID provided via azure-wi-webhook-config for the webhook will be used.
|
||||
// AZURE_FEDERATED_TOKEN_FILE is the service account token path
|
||||
// AZURE_AUTHORITY_HOST is the AAD authority hostname
|
||||
clientID := os.Getenv("AZURE_CLIENT_ID")
|
||||
tenantID := os.Getenv("AZURE_TENANT_ID")
|
||||
tokenFilePath := os.Getenv("AZURE_FEDERATED_TOKEN_FILE")
|
||||
authorityHost := os.Getenv("AZURE_AUTHORITY_HOST")
|
||||
|
||||
if clientID == "" {
|
||||
utils.ConsoleOutput("AZURE_CLIENT_ID environment variable is not set", stderr)
|
||||
exitCode = ERR_CREDENTIALS
|
||||
return
|
||||
}
|
||||
if tenantID == "" {
|
||||
utils.ConsoleOutput("AZURE_TENANT_ID environment variable is not set", stderr)
|
||||
exitCode = ERR_CREDENTIALS
|
||||
return
|
||||
}
|
||||
if authorityHost == "" {
|
||||
utils.ConsoleOutput("AZURE_AUTHORITY_HOST environment variable is not set", stderr)
|
||||
exitCode = ERR_CREDENTIALS
|
||||
return
|
||||
}
|
||||
|
||||
cred, err = corehelper.NewClientAssertionCredential(tenantID, clientID, authorityHost, tokenFilePath, nil)
|
||||
if err != nil {
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> failed to create client assertion credential: %v\n", err), stderr)
|
||||
exitCode = ERR_CREDENTIALS
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
utils.ConsoleOutput("Creating clients", stdout)
|
||||
azsecretsClient, err := azsecrets.NewClient(keyVaultUrl, cred, nil)
|
||||
// Getting token credentials to be used by various clients
|
||||
cred, err := corehelper.GetTokenCredentials(*managedIdentityId, *useSystemManagedIdentity)
|
||||
if err != nil {
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> failed to create azsecrets client: %v\n", err), stderr)
|
||||
exitCode = ERR_CREDENTIALS
|
||||
return
|
||||
}
|
||||
azcertsClient, err := azcertificates.NewClient(keyVaultUrl, cred, nil)
|
||||
if err != nil {
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> failed to create azcertificates client: %v\n", err), stderr)
|
||||
exitCode = ERR_CREDENTIALS
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> failed to obtain token credentials: %v\n", err), corehelper.Stderr)
|
||||
exitCode = corehelper.ERR_CREDENTIALS
|
||||
return
|
||||
}
|
||||
|
||||
utils.ConsoleOutput("Getting certificate thumbprint", stdout)
|
||||
x509Thumbprint, err := corehelper.GetAKVCertThumbprint(cntx, azcertsClient, *u)
|
||||
// Creating clients
|
||||
utils.ConsoleOutput("Creating clients", corehelper.Stdout)
|
||||
azsecretsClient, err := corehelper.GetSecretsClient(keyVaultUrl, *environment, *cloudConfigFile, cred)
|
||||
if err != nil {
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> %v\n", err), stderr)
|
||||
exitCode = ERR_X509_THUMBPRINT
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> failed to create azsecrets client: %v\n", err), corehelper.Stderr)
|
||||
exitCode = corehelper.ERR_CREDENTIALS
|
||||
return
|
||||
}
|
||||
|
||||
azcertsClient, err := corehelper.GetCertsClient(keyVaultUrl, *environment, *cloudConfigFile, cred)
|
||||
if err != nil {
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> failed to create azcertificates client: %v\n", err), corehelper.Stderr)
|
||||
exitCode = corehelper.ERR_CREDENTIALS
|
||||
return
|
||||
}
|
||||
|
||||
utils.ConsoleOutput("Getting certificate thumbprint", corehelper.Stdout)
|
||||
x509Thumbprint, err := corehelper.GetAKVCertThumbprint(cntx, &azcertsClient, *u)
|
||||
if err != nil {
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> %v\n", err), corehelper.Stderr)
|
||||
exitCode = corehelper.ERR_X509_THUMBPRINT
|
||||
return
|
||||
}
|
||||
|
||||
hexThumbprint := strings.ToUpper(hex.EncodeToString([]byte(x509Thumbprint)))
|
||||
kvCertName := strings.Replace(u.Path, "/", "", 1)
|
||||
pemFileName := fmt.Sprintf("%v/%v-%v.PEM", *outputFolder, kvCertName, hexThumbprint)
|
||||
utils.ConsoleOutput(fmt.Sprintf("PEM filename: %v", pemFileName), stdout)
|
||||
utils.ConsoleOutput(fmt.Sprintf("PEM filename: %v", pemFileName), corehelper.Stdout)
|
||||
|
||||
// Checks if certificate needs to be downloaded
|
||||
if _, err := os.Stat(pemFileName); err == nil {
|
||||
utils.ConsoleOutput(fmt.Sprintf("Certificate %v in key vault is the same as the one already downloaded, exiting...", kvCertName), stdout)
|
||||
utils.ConsoleOutput(fmt.Sprintf("Certificate %v in key vault is the same as the one already downloaded, exiting...", kvCertName), corehelper.Stdout)
|
||||
exitCode = 0
|
||||
return
|
||||
}
|
||||
|
||||
// Get cert as secret
|
||||
utils.ConsoleOutput("Getting certificate as secret", stdout)
|
||||
certAKV, err := corehelper.GetAKVCertificate(cntx, azsecretsClient, *u)
|
||||
utils.ConsoleOutput("Getting certificate as secret", corehelper.Stdout)
|
||||
certAKV, err := corehelper.GetAKVCertificate(cntx, &azsecretsClient, *u)
|
||||
if err != nil {
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> unable to get certificate: %v\n", err), stderr)
|
||||
exitCode = ERR_GET_AKV_CERT_SECRET
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> unable to get certificate: %v\n", err), corehelper.Stderr)
|
||||
exitCode = corehelper.ERR_GET_AKV_CERT_SECRET
|
||||
return
|
||||
}
|
||||
|
||||
utils.ConsoleOutput(fmt.Sprintf("Cert format: %v", *certAKV.ContentType), stdout)
|
||||
utils.ConsoleOutput(fmt.Sprintf("Cert format: %v", *certAKV.ContentType), corehelper.Stdout)
|
||||
|
||||
// Getting PEM Blocks
|
||||
var blocks interface{}
|
||||
|
@ -248,22 +184,22 @@ func main() {
|
|||
|
||||
privateKey, err := corehelper.GetPrivateKeyFromPEMBlocks(blocks)
|
||||
if err != nil {
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> %v\n", err), stderr)
|
||||
exitCode = ERR_GET_PEM_PRIVATE_KEY
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> %v\n", err), corehelper.Stderr)
|
||||
exitCode = corehelper.ERR_GET_PEM_PRIVATE_KEY
|
||||
return
|
||||
}
|
||||
|
||||
certificate, err := corehelper.GetCertificateFromPEMBLocks(blocks)
|
||||
if err != nil {
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> %v\n", err), stderr)
|
||||
exitCode = ERR_GET_PEM_CERTIFICATE
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> %v\n", err), corehelper.Stderr)
|
||||
exitCode = corehelper.ERR_GET_PEM_CERTIFICATE
|
||||
return
|
||||
}
|
||||
|
||||
err = corehelper.WritePEMfile(pemFileName, certificate, privateKey)
|
||||
if err != nil {
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> %v\n", err), stderr)
|
||||
exitCode = ERR_CREATE_PEM_FILE
|
||||
utils.ConsoleOutput(fmt.Sprintf("<error> %v\n", err), corehelper.Stderr)
|
||||
exitCode = corehelper.ERR_CREATE_PEM_FILE
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -271,7 +207,7 @@ func main() {
|
|||
func exit(cntx context.Context, exitCode int) {
|
||||
if exitCode == 0 {
|
||||
if !*cmdlineversion {
|
||||
utils.ConsoleOutput("Execution successfully completed", stdout)
|
||||
utils.ConsoleOutput("Execution successfully completed", corehelper.Stdout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче