Skip to content

Commit

Permalink
Merge pull request #1151 from bryan-cox/HOSTEDCP-1872
Browse files Browse the repository at this point in the history
HOSTEDCP-2031: Use Client Certificate Authentication for ARO HCP deployments
  • Loading branch information
openshift-merge-bot[bot] authored Nov 15, 2024
2 parents 9305d20 + f8e2bcd commit e12e1ba
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 6 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require (
github.com/aws/aws-sdk-go v1.38.49
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
github.com/florianl/go-nfqueue v1.3.2
github.com/fsnotify/fsnotify v1.7.0
github.com/go-logr/logr v1.4.2
github.com/go-logr/zapr v1.3.0
github.com/google/go-cmp v0.6.0
Expand Down Expand Up @@ -70,7 +71,6 @@ require (
github.com/evanphx/json-patch v5.7.0+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-openapi/errors v0.21.0 // indirect
Expand Down
41 changes: 36 additions & 5 deletions pkg/dns/azure/client/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,23 @@ package client

import (
"errors"
"fmt"
"os"
"strings"
"sync"

"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/azidentity"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/azure"
"github.com/jongio/azidext/go/azidext"

"github.com/openshift/cluster-ingress-operator/pkg/util/filewatcher"
)

var watchCertificateFileOnce sync.Once

func getAuthorizerForResource(config Config) (autorest.Authorizer, error) {
var cloudConfig cloud.Configuration
switch config.Environment {
Expand Down Expand Up @@ -70,18 +76,43 @@ func getAuthorizerForResource(config Config) (autorest.Authorizer, error) {
}

var cred azcore.TokenCredential
// Managed Identity Override for ARO HCP
// Managed Identity Override for ARO HCP. In ARO HCP, we ignore the values provided for AZURE_TENANT_ID and
// AZURE_CLIENT_ID and use ARO_HCP_TENANT_ID and ARO_HCP_MI_CLIENT_ID instead.
managedIdentityClientID := os.Getenv("ARO_HCP_MI_CLIENT_ID")
if managedIdentityClientID != "" {
options := azidentity.ManagedIdentityCredentialOptions{
options := &azidentity.ClientCertificateCredentialOptions{
ClientOptions: azcore.ClientOptions{
Cloud: cloudConfig,
},
ID: azidentity.ClientID(managedIdentityClientID),
SendCertificateChain: true,
}

var err error
cred, err = azidentity.NewManagedIdentityCredential(&options)
tenantID := os.Getenv("ARO_HCP_TENANT_ID")
certPath := os.Getenv("ARO_HCP_CLIENT_CERTIFICATE_PATH")

certData, err := os.ReadFile(certPath)
if err != nil {
return nil, fmt.Errorf("failed to read certificate file %q: %w", certPath, err)
}

certs, key, err := azidentity.ParseCertificates(certData, nil)
if err != nil {
return nil, fmt.Errorf("failed to parse certificate data %q: %w", certPath, err)
}

// Watch the certificate for changes; if the certificate changes, the pod will be restarted.
// This starts only one occurrence of the file watcher, which watches the file, certPath.
var fileWatcherError error
watchCertificateFileOnce.Do(func() {
if err = filewatcher.WatchFileForChanges(certPath); err != nil {
fileWatcherError = err
}
})
if fileWatcherError != nil {
return nil, fmt.Errorf("failed to watch certificate file %q: %w", certPath, fileWatcherError)
}

cred, err = azidentity.NewClientCertificateCredential(tenantID, managedIdentityClientID, certs, key, options)
if err != nil {
return nil, err
}
Expand Down
65 changes: 65 additions & 0 deletions pkg/util/filewatcher/filewatcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package filewatcher

import (
"os"
"path/filepath"

"github.com/fsnotify/fsnotify"
logf "github.com/openshift/cluster-ingress-operator/pkg/log"
)

var log = logf.Logger.WithName("filewatcher")

// WatchFileForChanges watches the file, fileToWatch, for changes. If the file contents have changed, the pod this
// function is running on will be restarted.
func WatchFileForChanges(fileToWatch string) error {
log.Info("Starting the file change watcher")

// Update the file path to watch in case this is a symlink
fileToWatch, err := filepath.EvalSymlinks(fileToWatch)
if err != nil {
return err
}
log.Info("Watching file", "file", fileToWatch)

// Start the file watcher to monitor file changes
go func() {
if err := checkForFileChanges(fileToWatch); err != nil {
log.Error(err, "Error checking for file changes")
}
}()

return nil
}

// checkForFileChanges starts a new file watcher. If the file is changed, the pod running this function will exit.
func checkForFileChanges(path string) error {
watcher, err := fsnotify.NewWatcher()
if err != nil {
return err
}

go func() {
for {
select {
case event, ok := <-watcher.Events:
if ok && (event.Has(fsnotify.Write) || event.Has(fsnotify.Chmod) || event.Has(fsnotify.Remove)) {
log.Info("file was modified, exiting...", "file", event.Name)
os.Exit(0)
}
case err, ok := <-watcher.Errors:
if ok {
log.Error(err, "file watcher error")
} else {
log.Error(err, "failed to retrieve watcher error")
}
}
}
}()

if err = watcher.Add(path); err != nil {
return err
}

return nil
}

0 comments on commit e12e1ba

Please sign in to comment.