Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release-4.17] OCPBUGS-44861: Use Client Cert Auth for ARO HCP deployments #1161

Open
wants to merge 3 commits into
base: release-4.17
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ require (
github.com/IBM/platform-services-go-sdk v0.55.0
github.com/aws/aws-sdk-go v1.50.35
github.com/davecgh/go-spew v1.1.1
github.com/fsnotify/fsnotify v1.7.0
github.com/ghodss/yaml v1.0.0
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/google/go-cmp v0.6.0
Expand Down Expand Up @@ -80,7 +81,6 @@ require (
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/errors v0.20.3 // indirect
Expand Down
67 changes: 67 additions & 0 deletions pkg/filewatcher/filewatcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package filewatcher

import (
"os"
"path/filepath"
"sync"

"github.com/fsnotify/fsnotify"
"k8s.io/klog/v2"
)

var watchCertificateFileOnce sync.Once

// 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 {
var err error

// This starts only one occurrence of the file watcher, which watches the file, fileToWatch.
watchCertificateFileOnce.Do(func() {
klog.Infof("Starting the file change watcher on file, %s", fileToWatch)

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

// Start the file watcher to monitor file changes
go func() {
err = checkForFileChanges(fileToWatch)
}()
})
return err
}

// 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)) {
klog.Infof("file, %s, was modified, exiting...", event.Name)
os.Exit(0)
}
case err, ok := <-watcher.Errors:
if ok {
klog.Errorf("file watcher error: %v", err)
}
}
}
}()

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

return nil
}
33 changes: 27 additions & 6 deletions pkg/storage/azure/azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import (
regopclient "github.com/openshift/cluster-image-registry-operator/pkg/client"
"github.com/openshift/cluster-image-registry-operator/pkg/defaults"
"github.com/openshift/cluster-image-registry-operator/pkg/envvar"
"github.com/openshift/cluster-image-registry-operator/pkg/filewatcher"
"github.com/openshift/cluster-image-registry-operator/pkg/storage/azure/azureclient"
"github.com/openshift/cluster-image-registry-operator/pkg/storage/util"
)
Expand Down Expand Up @@ -370,17 +371,37 @@ func (d *driver) storageAccountsClient(cfg *Azure, environment autorestazure.Env
cred azcore.TokenCredential
err error
)
// MSI Override for ARO HCP
msi := os.Getenv("AZURE_MSI_AUTHENTICATION")
if msi == "true" {
options := azidentity.ManagedIdentityCredentialOptions{
// Managed Identity Override for ARO HCP
managedIdentityClientID := os.Getenv("ARO_HCP_MI_CLIENT_ID")
if managedIdentityClientID != "" {
klog.V(2).Info("Using client certification Azure authentication for ARO HCP")
options := &azidentity.ClientCertificateCredentialOptions{
ClientOptions: azcore.ClientOptions{
Cloud: cloudConfig,
},
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 storage.AccountsClient{}, fmt.Errorf(`failed to read certificate file "%s": %v`, certPath, err)
}

certs, key, err := azidentity.ParseCertificates(certData, []byte{})
if err != nil {
return storage.AccountsClient{}, fmt.Errorf(`failed to parse certificate data "%s": %v`, certPath, err)
}

// Watch the certificate for changes; if the certificate changes, the pod will be restarted
err = filewatcher.WatchFileForChanges(certPath)
if err != nil {
return storage.AccountsClient{}, err
}

cred, err = azidentity.NewClientCertificateCredential(tenantID, managedIdentityClientID, certs, key, options)
if err != nil {
return storage.AccountsClient{}, err
}
Expand Down
29 changes: 26 additions & 3 deletions pkg/storage/azure/azureclient/azureclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
"github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/container"
autorestazure "github.com/Azure/go-autorest/autorest/azure"
"github.com/Azure/go-autorest/autorest/to"
"github.com/openshift/cluster-image-registry-operator/pkg/filewatcher"
"k8s.io/klog/v2"
)

const (
Expand Down Expand Up @@ -105,13 +107,34 @@ func (c *Client) getCreds() (azcore.TokenCredential, error) {
// Managed Identity Override for ARO HCP
managedIdentityClientID := os.Getenv("ARO_HCP_MI_CLIENT_ID")
if managedIdentityClientID != "" {
options := azidentity.ManagedIdentityCredentialOptions{
klog.V(2).Info("Using client certification Azure authentication for ARO HCP")
options := &azidentity.ClientCertificateCredentialOptions{
ClientOptions: azcore.ClientOptions{
Cloud: c.clientOpts.Cloud,
},
ID: azidentity.ClientID(managedIdentityClientID),
SendCertificateChain: true,
}
creds, 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 "%s": %v`, certPath, err)
}

certs, key, err := azidentity.ParseCertificates(certData, []byte{})
if err != nil {
return nil, fmt.Errorf(`failed to parse certificate data "%s": %v`, certPath, err)
}

// Watch the certificate for changes; if the certificate changes, the pod will be restarted
err = filewatcher.WatchFileForChanges(certPath)
if err != nil {
return nil, err
}

creds, err = azidentity.NewClientCertificateCredential(tenantID, managedIdentityClientID, certs, key, options)
if err != nil {
return nil, err
}
Expand Down