@@ -35,6 +35,7 @@ import (
35
35
regopclient "github.com/openshift/cluster-image-registry-operator/pkg/client"
36
36
"github.com/openshift/cluster-image-registry-operator/pkg/defaults"
37
37
"github.com/openshift/cluster-image-registry-operator/pkg/envvar"
38
+ "github.com/openshift/cluster-image-registry-operator/pkg/storage/azure/azureclient"
38
39
"github.com/openshift/cluster-image-registry-operator/pkg/storage/util"
39
40
)
40
41
@@ -173,6 +174,10 @@ func (d *driver) createStorageAccount(storageAccountsClient storage.AccountsClie
173
174
klog .Infof ("attempt to create azure storage account %s (resourceGroup=%q, location=%q)..." , accountName , resourceGroupName , location )
174
175
175
176
kind := storage .StorageV2
177
+ // NOTE: looks like this legacy version of the storage library does not support
178
+ // disabling public network access at all... either we update the storage
179
+ // account after creation using the new sdk, or we use the new sdk to create it
180
+ // outside of azure stack hub, and the old sdk for azure stack hub.
176
181
params := & storage.AccountPropertiesCreateParameters {
177
182
EnableHTTPSTrafficOnly : to .BoolPtr (true ),
178
183
AllowBlobPublicAccess : to .BoolPtr (false ),
@@ -540,10 +545,89 @@ func (d *driver) StorageChanged(cr *imageregistryv1.Config) bool {
540
545
return ! reflect .DeepEqual (cr .Status .Storage .Azure , cr .Spec .Storage .Azure )
541
546
}
542
547
548
+ func (d * driver ) assurePrivateAccount (cfg * Azure , infra * configv1.Infrastructure , tagset map [string ]* string , accountName string ) (string , error ) {
549
+ if d .Config .NetworkAccess == nil || d .Config .NetworkAccess .Type == imageregistryv1 .AzureNetworkAccessTypeExternal {
550
+ // user did not request private storage account setup - skip.
551
+ return "" , nil
552
+ }
553
+ if d .Config .NetworkAccess .Internal .VNetName == "" || d .Config .NetworkAccess .Internal .SubnetName == "" {
554
+ return "" , fmt .Errorf ("both vnetName and subnetName are required to setup a private storage account" )
555
+ }
556
+
557
+ var privateEndpointName string
558
+ if d .Config .NetworkAccess .Internal != nil && d .Config .NetworkAccess .Internal .PrivateEndpointName != "" {
559
+ privateEndpointName = d .Config .NetworkAccess .Internal .PrivateEndpointName
560
+ } else {
561
+ privateEndpointName = generateAccountName (infra .Status .InfrastructureName )
562
+ }
563
+
564
+ environment , err := getEnvironmentByName (d .Config .CloudName )
565
+ if err != nil {
566
+ return "" , err
567
+ }
568
+
569
+ azclient , err := azureclient .New (& azureclient.Options {
570
+ Environment : environment ,
571
+ TenantID : cfg .TenantID ,
572
+ ClientID : cfg .ClientID ,
573
+ ClientSecret : cfg .ClientSecret ,
574
+ FederatedTokenFile : cfg .FederatedTokenFile ,
575
+ SubscriptionID : cfg .SubscriptionID ,
576
+ ResourceGroupName : cfg .ResourceGroup ,
577
+ TagSet : tagset ,
578
+ })
579
+ if err != nil {
580
+ return "" , err
581
+ }
582
+
583
+ // the last step in this function is to disable public network for the
584
+ // storage account - if we already did that, then none of the steps
585
+ // below need to be executed.
586
+ if azclient .IsStorageAccountPrivate (d .Context , accountName ) {
587
+ return privateEndpointName , nil
588
+ }
589
+
590
+ klog .V (3 ).Infof ("configuring private endpoint %q for storage account..." , privateEndpointName )
591
+ pe , err := azclient .CreatePrivateEndpoint (
592
+ d .Context ,
593
+ & azureclient.PrivateEndpointCreateOptions {
594
+ Location : cfg .Region ,
595
+ VNetName : d .Config .NetworkAccess .Internal .VNetName ,
596
+ SubnetName : d .Config .NetworkAccess .Internal .SubnetName ,
597
+ PrivateEndpointName : privateEndpointName ,
598
+ StorageAccountName : accountName ,
599
+ },
600
+ )
601
+ if err != nil {
602
+ return "" , err
603
+ }
604
+ klog .V (3 ).Info ("private endpoint configured" )
605
+
606
+ klog .V (3 ).Info ("configuring private DNS..." )
607
+ if err := azclient .ConfigurePrivateDNS (
608
+ d .Context , pe , d .Config .NetworkAccess .Internal .VNetName , accountName ,
609
+ ); err != nil {
610
+ return privateEndpointName , err
611
+ }
612
+ klog .V (3 ).Info ("private DNS configured" )
613
+
614
+ klog .V (3 ).Infof ("disabling public network access for storage account %q..." , accountName )
615
+ if err := azclient .UpdateStorageAccountNetworkAccess (d .Context , accountName , false ); err != nil {
616
+ return privateEndpointName , err
617
+ }
618
+
619
+ klog .Infof (
620
+ "storage account %q is now served by private endpoint %q. public network access is disabled" ,
621
+ accountName , privateEndpointName ,
622
+ )
623
+
624
+ return privateEndpointName , nil
625
+ }
626
+
543
627
// assureStorageAccount makes sure there is a storage account in place and apply any provided tags.
544
628
// If no storage account name is provided it attempts to generate one. Returns the account name
545
629
// (either the one provided or the one generated), if the account was created or was already there and an error.
546
- func (d * driver ) assureStorageAccount (cfg * Azure , infra * configv1.Infrastructure ) (string , bool , error ) {
630
+ func (d * driver ) assureStorageAccount (cfg * Azure , infra * configv1.Infrastructure , tagset map [ string ] * string ) (string , bool , error ) {
547
631
environment , err := getEnvironmentByName (d .Config .CloudName )
548
632
if err != nil {
549
633
return "" , false , err
@@ -571,26 +655,6 @@ func (d *driver) assureStorageAccount(cfg *Azure, infra *configv1.Infrastructure
571
655
return "" , false , fmt .Errorf ("create storage account failed, name not available" )
572
656
}
573
657
574
- // Tag the storage account with the openshiftClusterID
575
- // along with any user defined tags from the cluster configuration
576
- klog .V (2 ).Info ("setting azure storage account tags" )
577
-
578
- tagset := map [string ]* string {
579
- fmt .Sprintf ("kubernetes.io_cluster.%s" , infra .Status .InfrastructureName ): to .StringPtr ("owned" ),
580
- }
581
-
582
- // at this stage we are not keeping user tags in sync. as per enhancement proposal
583
- // we only set user provided tags when we created the bucket.
584
- hasAzureStatus := infra .Status .PlatformStatus != nil && infra .Status .PlatformStatus .Azure != nil && infra .Status .PlatformStatus .Azure .ResourceTags != nil
585
- if hasAzureStatus {
586
- klog .V (5 ).Infof ("user has provided %d tags" , len (infra .Status .PlatformStatus .Azure .ResourceTags ))
587
- for _ , tag := range infra .Status .PlatformStatus .Azure .ResourceTags {
588
- klog .V (5 ).Infof ("user has provided storage account tag: %s: %s" , tag .Key , tag .Value )
589
- tagset [tag .Key ] = to .StringPtr (tag .Value )
590
- }
591
- }
592
- klog .V (5 ).Infof ("tagging storage account with tags: %+v" , tagset )
593
-
594
658
// regardless if the storage account name was provided by the user or we generated it,
595
659
// if it is available, we do attempt to create it.
596
660
var storageAccountCreated bool
@@ -743,7 +807,26 @@ func (d *driver) CreateStorage(cr *imageregistryv1.Config) error {
743
807
}
744
808
}
745
809
746
- storageAccountName , storageAccountCreated , err := d .assureStorageAccount (cfg , infra )
810
+ // Tag the storage account with the openshiftClusterID
811
+ // along with any user defined tags from the cluster configuration
812
+ klog .V (2 ).Info ("setting azure storage account tags" )
813
+ tagset := map [string ]* string {
814
+ fmt .Sprintf ("kubernetes.io_cluster.%s" , infra .Status .InfrastructureName ): to .StringPtr ("owned" ),
815
+ }
816
+
817
+ // at this stage we are not keeping user tags in sync. as per enhancement proposal
818
+ // we only set user provided tags when we created the bucket.
819
+ hasAzureStatus := infra .Status .PlatformStatus != nil && infra .Status .PlatformStatus .Azure != nil && infra .Status .PlatformStatus .Azure .ResourceTags != nil
820
+ if hasAzureStatus {
821
+ klog .V (5 ).Infof ("user has provided %d tags" , len (infra .Status .PlatformStatus .Azure .ResourceTags ))
822
+ for _ , tag := range infra .Status .PlatformStatus .Azure .ResourceTags {
823
+ klog .V (5 ).Infof ("user has provided storage account tag: %s: %s" , tag .Key , tag .Value )
824
+ tagset [tag .Key ] = to .StringPtr (tag .Value )
825
+ }
826
+ }
827
+ klog .V (5 ).Infof ("tagging storage account with tags: %+v" , tagset )
828
+
829
+ storageAccountName , storageAccountCreated , err := d .assureStorageAccount (cfg , infra , tagset )
747
830
if err != nil {
748
831
util .UpdateCondition (
749
832
cr ,
@@ -769,6 +852,26 @@ func (d *driver) CreateStorage(cr *imageregistryv1.Config) error {
769
852
}
770
853
d .Config .Container = containerName
771
854
855
+ privateEndpointName , err := d .assurePrivateAccount (cfg , infra , tagset , storageAccountName )
856
+ if err != nil {
857
+ util .UpdateCondition (
858
+ cr ,
859
+ defaults .StorageExists ,
860
+ operatorapiv1 .ConditionUnknown ,
861
+ storageExistsReasonAzureError ,
862
+ fmt .Sprintf ("Unable to process private endpoint: %s" , err ),
863
+ )
864
+ return err
865
+ }
866
+ if privateEndpointName != "" {
867
+ // in most cases, a call to d.assurePrivateAccount will not result
868
+ // in the creation of a private endpoint. this will only happen
869
+ // when users explicitly request so by setting
870
+ // VNetName and SubnetName
871
+ // in the registry config. only then we set the private endpoint name.
872
+ d .Config .NetworkAccess .Internal .PrivateEndpointName = privateEndpointName
873
+ }
874
+
772
875
// We only set the storage management if it is not already set.
773
876
if cr .Spec .Storage .ManagementState == "" {
774
877
if storageAccountCreated || containerCreated {
@@ -821,6 +924,56 @@ func (d *driver) RemoveStorage(cr *imageregistryv1.Config) (retry bool, err erro
821
924
return false , err
822
925
}
823
926
927
+ if d .Config .NetworkAccess != nil && d .Config .NetworkAccess .Internal != nil && d .Config .NetworkAccess .Internal .PrivateEndpointName != "" {
928
+ azclient , err := azureclient .New (& azureclient.Options {
929
+ Environment : environment ,
930
+ TenantID : cfg .TenantID ,
931
+ ClientID : cfg .ClientID ,
932
+ ClientSecret : cfg .ClientSecret ,
933
+ FederatedTokenFile : cfg .FederatedTokenFile ,
934
+ SubscriptionID : cfg .SubscriptionID ,
935
+ ResourceGroupName : cfg .ResourceGroup ,
936
+ })
937
+ if err != nil {
938
+ util .UpdateCondition (
939
+ cr ,
940
+ defaults .StorageExists ,
941
+ operatorapiv1 .ConditionUnknown ,
942
+ storageExistsReasonAzureError ,
943
+ fmt .Sprintf ("Unable to get azure client: %s" , err ),
944
+ )
945
+ return false , err
946
+ }
947
+ if err := azclient .DestroyPrivateDNS (
948
+ d .Context ,
949
+ d .Config .NetworkAccess .Internal .PrivateEndpointName ,
950
+ d .Config .NetworkAccess .Internal .VNetName ,
951
+ d .Config .AccountName ,
952
+ ); err != nil {
953
+ util .UpdateCondition (
954
+ cr ,
955
+ defaults .StorageExists ,
956
+ operatorapiv1 .ConditionUnknown ,
957
+ storageExistsReasonAzureError ,
958
+ fmt .Sprintf ("Unable to destroy private dns: %q" , err ),
959
+ )
960
+ return false , err
961
+ }
962
+ if err := azclient .DeletePrivateEndpoint (
963
+ d .Context , d .Config .NetworkAccess .Internal .PrivateEndpointName ,
964
+ ); err != nil {
965
+ util .UpdateCondition (
966
+ cr ,
967
+ defaults .StorageExists ,
968
+ operatorapiv1 .ConditionUnknown ,
969
+ storageExistsReasonAzureError ,
970
+ fmt .Sprintf ("Unable to delete private endpoint: %q" , err ),
971
+ )
972
+ return false , err
973
+ }
974
+ d .Config .NetworkAccess = nil
975
+ }
976
+
824
977
if d .Config .Container != "" {
825
978
key , err := d .getAccountPrimaryKey (storageAccountsClient , cfg .ResourceGroup , d .Config .AccountName )
826
979
if _ , ok := err .(* errDoesNotExist ); ok {
0 commit comments