Skip to content

Commit 4546bcd

Browse files
Retrieve SAS from a kv (#6)
* Retrieve SAS from a kv * Refresh token after 24hrs * Disable sharedkey auth * Ze's comments
1 parent 3a564dc commit 4546bcd

File tree

8 files changed

+158
-56
lines changed

8 files changed

+158
-56
lines changed

cmd/lhsm-plugin-az-core/archive.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ import (
1616
type ArchiveOptions struct {
1717
AccountName string
1818
ContainerName string
19+
ResourceSAS string
1920
BlobName string
2021
SourcePath string
21-
Credential *azblob.SharedKeyCredential
22+
Credential azblob.Credential
2223
Parallelism uint16
2324
BlockSize int64
2425
Pacer util.Pacer
@@ -34,7 +35,7 @@ func Archive(o ArchiveOptions) (int64, error) {
3435
p := util.NewPipeline(ctx, o.Credential, o.Pacer, azblob.PipelineOptions{})
3536

3637
//Get the blob URL
37-
cURL, _ := url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net/%s", o.AccountName, o.ContainerName))
38+
cURL, _ := url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net/%s%s", o.AccountName, o.ContainerName, o.ResourceSAS))
3839
containerURL := azblob.NewContainerURL(*cURL, p)
3940
blobName := path.Join(o.ExportPrefix, o.BlobName)
4041
blobURL := containerURL.NewBlockBlobURL(blobName)

cmd/lhsm-plugin-az-core/remove.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,27 @@ import (
66
"net/url"
77
"path"
88

9+
"github.com/Azure/azure-pipeline-go/pipeline"
910
"github.com/Azure/azure-storage-blob-go/azblob"
11+
"github.com/wastore/lemur/cmd/util"
1012
)
1113

1214
type RemoveOptions struct {
1315
AccountName string
1416
ContainerName string
17+
ResourceSAS string
1518
BlobName string
1619
ExportPrefix string
17-
Credential *azblob.SharedKeyCredential
20+
Credential azblob.Credential
1821
}
1922

2023
func Remove(o RemoveOptions) error {
2124
ctx := context.TODO()
2225
p := azblob.NewPipeline(o.Credential, azblob.PipelineOptions{})
2326
blobPath := path.Join(o.ContainerName, o.ExportPrefix, o.BlobName)
24-
u, _ := url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net/%s", o.AccountName, blobPath))
27+
u, _ := url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net/%s%s", o.AccountName, blobPath, o.ResourceSAS))
28+
29+
util.Log(pipeline.LogInfo, fmt.Sprintf("Removing %s.", u.String()))
2530

2631
// fetch the properties first so that we know how big the source blob is
2732
blobURL := azblob.NewBlobURL(*u, p)

cmd/lhsm-plugin-az-core/restore.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"os"
88
"path"
99

10+
"github.com/Azure/azure-pipeline-go/pipeline"
1011
"github.com/Azure/azure-storage-blob-go/azblob"
1112
"github.com/pkg/errors"
1213
"github.com/wastore/lemur/cmd/util"
@@ -15,9 +16,10 @@ import (
1516
type RestoreOptions struct {
1617
AccountName string
1718
ContainerName string
19+
ResourceSAS string
1820
BlobName string
1921
DestinationPath string
20-
Credential *azblob.SharedKeyCredential
22+
Credential azblob.Credential
2123
Parallelism uint16
2224
BlockSize int64
2325
ExportPrefix string
@@ -33,7 +35,9 @@ func Restore(o RestoreOptions) (int64, error) {
3335
p := util.NewPipeline(ctx, o.Credential, o.Pacer, azblob.PipelineOptions{})
3436
blobPath := path.Join(o.ContainerName, o.ExportPrefix, o.BlobName)
3537

36-
u, _ := url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net/%s", o.AccountName, blobPath))
38+
u, _ := url.Parse(fmt.Sprintf("https://%s.blob.core.windows.net/%s%s", o.AccountName, blobPath, o.ResourceSAS))
39+
40+
util.Log(pipeline.LogInfo, fmt.Sprintf("Restoring %s to %s.", u.String(), o.DestinationPath))
3741

3842
blobURL := azblob.NewBlobURL(*u, p)
3943
blobProp, err := blobURL.GetProperties(ctx, azblob.BlobAccessConditions{})

cmd/lhsm-plugin-az/main.go

Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -24,35 +24,38 @@ import (
2424

2525
type (
2626
archiveConfig struct {
27-
Name string `hcl:",key"`
28-
ID int
29-
AzStorageAccount string `hcl:"az_storage_account"`
30-
AzStorageKey string `hcl:"az_storage_key"`
31-
Endpoint string
32-
Region string
33-
Container string
34-
Prefix string
35-
UploadPartSize int64 `hcl:"upload_part_size"`
36-
NumThreads int `hcl:"num_threads"`
37-
Bandwidth int `hcl:"bandwidth"`
38-
MountRoot string `hcl:"mountroot"`
39-
ExportPrefix string `hcl:"exportprefix"`
40-
azCreds *azblob.SharedKeyCredential
27+
Name string `hcl:",key"`
28+
ID int
29+
AzStorageAccount string `hcl:"az_storage_account"`
30+
AzStorageKVName string `hcl:"az_kv_name"`
31+
AzStorageKVSecretName string `hcl:"az_kv_secret_name"`
32+
AzStorageSAS string
33+
Endpoint string
34+
Region string
35+
Container string
36+
Prefix string
37+
UploadPartSize int64 `hcl:"upload_part_size"`
38+
NumThreads int `hcl:"num_threads"`
39+
Bandwidth int `hcl:"bandwidth"`
40+
MountRoot string `hcl:"mountroot"`
41+
ExportPrefix string `hcl:"exportprefix"`
42+
azCreds azblob.Credential
4143
}
4244

4345
archiveSet []*archiveConfig
4446

4547
azConfig struct {
46-
NumThreads int `hcl:"num_threads"`
47-
AzStorageAccount string `hcl:"az_storage_account"`
48-
AzStorageKey string `hcl:"az_storage_key"`
49-
Endpoint string `hcl:"endpoint"`
50-
Region string `hcl:"region"`
51-
UploadPartSize int64 `hcl:"upload_part_size"`
52-
Archives archiveSet `hcl:"archive"`
53-
Bandwidth int `hcl:"bandwidth"`
54-
MountRoot string `hcl:"mountroot"`
55-
ExportPrefix string `hcl:"exportprefix"`
48+
NumThreads int `hcl:"num_threads"`
49+
AzStorageAccount string `hcl:"az_storage_account"`
50+
AzStorageKVName string `hcl:"az_kv_name"`
51+
AzStorageKVSecretName string `hcl:"az_kv_secret_name"`
52+
Endpoint string `hcl:"endpoint"`
53+
Region string `hcl:"region"`
54+
UploadPartSize int64 `hcl:"upload_part_size"`
55+
Archives archiveSet `hcl:"archive"`
56+
Bandwidth int `hcl:"bandwidth"`
57+
MountRoot string `hcl:"mountroot"`
58+
ExportPrefix string `hcl:"exportprefix"`
5659
}
5760
)
5861

@@ -94,8 +97,8 @@ func (a *archiveConfig) checkValid() error {
9497
}
9598

9699
func (a *archiveConfig) checkAzAccess() error {
97-
if _, err := azblob.NewSharedKeyCredential(a.AzStorageAccount, a.AzStorageKey); err != nil {
98-
return errors.Wrap(err, "No Az credentials found; cannot initialize data mover")
100+
if a.AzStorageKVName == "" || a.AzStorageKVSecretName == "" {
101+
return errors.New("No Az credentials found; cannot initialize data mover")
99102
}
100103

101104
/*
@@ -115,8 +118,12 @@ func (a *archiveConfig) mergeGlobals(g *azConfig) {
115118
a.AzStorageAccount = g.AzStorageAccount
116119
}
117120

118-
if a.AzStorageKey == "" {
119-
a.AzStorageKey = g.AzStorageKey
121+
if a.AzStorageKVName == "" {
122+
a.AzStorageKVName = g.AzStorageKVName
123+
}
124+
125+
if a.AzStorageKVSecretName == "" {
126+
a.AzStorageKVSecretName = g.AzStorageKVSecretName
120127
}
121128

122129
if a.Endpoint == "" {
@@ -135,11 +142,7 @@ func (a *archiveConfig) mergeGlobals(g *azConfig) {
135142
}
136143

137144
// If these were set on a per-archive basis, override the defaults.
138-
if a.AzStorageAccount != "" && a.AzStorageKey != "" {
139-
creds, _ := azblob.NewSharedKeyCredential(a.AzStorageAccount, a.AzStorageKey)
140-
a.azCreds = creds
141-
}
142-
145+
a.azCreds = azblob.NewAnonymousCredential()
143146
a.Bandwidth = g.Bandwidth
144147
a.MountRoot = g.MountRoot
145148
a.ExportPrefix = g.ExportPrefix
@@ -173,9 +176,14 @@ func (c *azConfig) Merge(other *azConfig) *azConfig {
173176
result.AzStorageAccount = other.AzStorageAccount
174177
}
175178

176-
result.AzStorageKey = c.AzStorageKey
177-
if other.AzStorageKey != "" {
178-
result.AzStorageKey = other.AzStorageKey
179+
result.AzStorageKVName = c.AzStorageKVName
180+
if other.AzStorageKVName != "" {
181+
result.AzStorageKVName = other.AzStorageKVName
182+
}
183+
184+
result.AzStorageKVSecretName = c.AzStorageKVSecretName
185+
if other.AzStorageKVSecretName != "" {
186+
result.AzStorageKVSecretName = other.AzStorageKVSecretName
179187
}
180188

181189
result.Archives = c.Archives
@@ -224,9 +232,8 @@ func init() {
224232
// }
225233
}
226234

227-
func getStorageSharedKeyCredential(ac *archiveConfig) *azblob.SharedKeyCredential {
228-
creds, _ := azblob.NewSharedKeyCredential(ac.AzStorageAccount, ac.AzStorageKey)
229-
return creds
235+
func getCredential(ac *archiveConfig) azblob.Credential {
236+
return ac.azCreds
230237
}
231238

232239
func getMergedConfig(plugin *dmplugin.Plugin) (*azConfig, error) {
@@ -292,7 +299,7 @@ func main() {
292299

293300
for _, ac := range cfg.Archives {
294301
plugin.AddMover(&dmplugin.Config{
295-
Mover: AzMover(ac, getStorageSharedKeyCredential(ac), uint32(ac.ID)),
302+
Mover: AzMover(ac, getCredential(ac), uint32(ac.ID)),
296303
NumThreads: cfg.NumThreads,
297304
ArchiveID: uint32(ac.ID),
298305
})

cmd/lhsm-plugin-az/mover.go

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,19 @@ import (
2424

2525
// Mover supports archiving/restoring data to/from Azure Storage
2626
type Mover struct {
27-
name string
28-
cred *azblob.SharedKeyCredential
29-
config *archiveConfig
27+
name string
28+
cred azblob.Credential
29+
nextCredRefreshTime time.Time
30+
config *archiveConfig
3031
}
3132

3233
// AzMover returns a new *Mover
33-
func AzMover(cfg *archiveConfig, creds *azblob.SharedKeyCredential, archiveID uint32) *Mover {
34+
func AzMover(cfg *archiveConfig, creds azblob.Credential, archiveID uint32) *Mover {
3435
return &Mover{
35-
name: fmt.Sprintf("az-%d", archiveID),
36-
cred: creds,
37-
config: cfg,
36+
name: fmt.Sprintf("az-%d", archiveID),
37+
cred: creds,
38+
nextCredRefreshTime: time.Now().Add(-1 * time.Second), // So that first action updates SAS
39+
config: cfg,
3840
}
3941
}
4042

@@ -48,7 +50,7 @@ func (m *Mover) destination(id string) string {
4850

4951
// Start signals the mover to begin any asynchronous processing (e.g. stats)
5052
func (m *Mover) Start() {
51-
util.InitJobLogger(pipeline.LogInfo)
53+
util.InitJobLogger(pipeline.LogDebug)
5254
util.Log(pipeline.LogDebug, fmt.Sprintf("%s started", m.name))
5355
debug.Printf("%s started", m.name)
5456
}
@@ -67,10 +69,27 @@ func (m *Mover) fileIDtoContainerPath(fileID string) (string, string, error) {
6769
path = m.destination(fileID)
6870
container = m.config.Container
6971
}
70-
util.Log(pipeline.LogDebug, fmt.Sprintf("Parsed %s -> %s / %s", fileID, container, path))
7172
return container, path, nil
7273
}
7374

75+
func (m *Mover) refreshCredential() {
76+
var err error
77+
if m.nextCredRefreshTime.After(time.Now()) {
78+
return
79+
}
80+
81+
m.cred = azblob.NewAnonymousCredential()
82+
m.config.AzStorageSAS, err = util.GetKVSecret(m.config.AzStorageKVName, m.config.AzStorageKVSecretName)
83+
84+
if err != nil {
85+
util.Log(pipeline.LogError, fmt.Sprintf("Failed to update SAS. Falling back to previous SAS.\n%s", err))
86+
return
87+
}
88+
89+
//Refresh successful, next refresh - at least 24 hrs later
90+
m.nextCredRefreshTime = time.Now().Add(24 * time.Hour)
91+
}
92+
7493
// Archive fulfills an HSM Archive request
7594
func (m *Mover) Archive(action dmplugin.Action) error {
7695
util.Log(pipeline.LogDebug, fmt.Sprintf("%s id:%d archive %s %s", m.name, action.ID(), action.PrimaryPath(), action.UUID()))
@@ -107,9 +126,12 @@ func (m *Mover) Archive(action dmplugin.Action) error {
107126
fileID := fnames[0]
108127
fileKey := m.destination(fileID)
109128

129+
m.refreshCredential()
130+
110131
total, err := core.Archive(core.ArchiveOptions{
111132
AccountName: m.config.AzStorageAccount,
112133
ContainerName: m.config.Container,
134+
ResourceSAS: m.config.AzStorageSAS,
113135
BlobName: fileKey,
114136
Credential: m.cred,
115137
SourcePath: action.PrimaryPath(),
@@ -161,9 +183,12 @@ func (m *Mover) Restore(action dmplugin.Action) error {
161183
return errors.Wrap(err, "fileIDtoContainerPath failed")
162184
}
163185

186+
m.refreshCredential()
187+
164188
contentLen, err := core.Restore(core.RestoreOptions{
165189
AccountName: m.config.AzStorageAccount,
166190
ContainerName: container,
191+
ResourceSAS: m.config.AzStorageSAS,
167192
BlobName: srcObj,
168193
Credential: m.cred,
169194
DestinationPath: action.WritePath(),
@@ -199,9 +224,12 @@ func (m *Mover) Remove(action dmplugin.Action) error {
199224
return errors.Wrap(err, "fileIDtoContainerPath failed")
200225
}
201226

227+
m.refreshCredential()
228+
202229
err = core.Remove(core.RemoveOptions{
203230
AccountName: m.config.AzStorageAccount,
204231
ContainerName: container,
232+
ResourceSAS: m.config.AzStorageSAS,
205233
BlobName: srcObj,
206234
ExportPrefix: m.config.ExportPrefix,
207235
Credential: m.cred,

cmd/util/misc.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import (
2525
"time"
2626

2727
"github.com/Azure/azure-pipeline-go/pipeline"
28+
kvauth "github.com/Azure/azure-sdk-for-go/services/keyvault/auth"
29+
"github.com/Azure/azure-sdk-for-go/services/keyvault/v7.0/keyvault"
2830
"github.com/Azure/azure-storage-blob-go/azblob"
2931
)
3032

@@ -57,3 +59,24 @@ func NewPipeline(ctx context.Context, c azblob.Credential, p Pacer, o azblob.Pip
5759

5860
return pipeline.NewPipeline(f, pipeline.Options{HTTPSender: o.HTTPSender, Log: o.Log})
5961
}
62+
63+
//GetKVSecret returns string secret by name 'kvSecretName' in keyvault 'kvName'
64+
//Uses MSI auth to login
65+
func GetKVSecret(kvName, kvSecretName string) (secret string, err error) {
66+
authorizer, err := kvauth.NewAuthorizerFromEnvironment()
67+
if err != nil {
68+
return "", err
69+
}
70+
71+
basicClient := keyvault.New()
72+
basicClient.Authorizer = authorizer
73+
74+
ctx, _ := context.WithTimeout(context.Background(), 3*time.Minute)
75+
secretResp, err := basicClient.GetSecret(ctx, "https://"+kvName+".vault.azure.net", kvSecretName, "")
76+
if err != nil {
77+
return "", err
78+
}
79+
80+
Log(pipeline.LogDebug, *secretResp.Value)
81+
return *secretResp.Value, nil
82+
}

go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ go 1.13
44

55
require (
66
github.com/Azure/azure-pipeline-go v0.2.3
7+
github.com/Azure/azure-sdk-for-go v48.2.0+incompatible
78
github.com/Azure/azure-storage-azcopy v10.0.2+incompatible
89
github.com/Azure/azure-storage-blob-go v0.10.1-0.20201027154117-4e8f2d48550b
9-
github.com/Azure/go-autorest/autorest v0.9.0 // indirect
10+
github.com/Azure/go-autorest/autorest/azure/auth v0.5.3 // indirect
11+
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
12+
github.com/Azure/go-autorest/autorest/validation v0.3.0 // indirect
1013
github.com/dustin/go-humanize v1.0.0
1114
github.com/fortytw2/leaktest v1.3.0
1215
github.com/go-delve/delve v1.5.0 // indirect

0 commit comments

Comments
 (0)