-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathazcertcache.go
138 lines (118 loc) · 4.21 KB
/
azcertcache.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// Package azcertcache implements an autocert.Cache to store certificate data within an Azure Blob Storage container
//
// See https://godoc.org/golang.org/x/crypto/acme/autocert
package azcertcache
import (
"bytes"
"context"
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"github.com/Azure/azure-storage-blob-go/azblob"
"golang.org/x/crypto/acme/autocert"
)
func isNotFound(err error) bool {
if err != nil {
if azError, ok := err.(azblob.ResponseError); ok {
return azError.Response().StatusCode == http.StatusNotFound
}
}
return false
}
// ErrEmptyContainerName is returned when given container name is empty
var ErrEmptyContainerName = errors.New("containerName must not be empty")
// Making sure that we're adhering to the autocert.Cache interface.
var _ autocert.Cache = (*Cache)(nil)
// Cache provides an Azure Blob Storage backend to the autocert cache.
type Cache struct {
containerURL azblob.ContainerURL
}
// New creates an cache instance that can be used with autocert.Cache.
// It returns any errors that could happen while connecting to Azure Blob Storage.
func New(accountName, accountKey, containerName string) (*Cache, error) {
return NewWithEndpoint(accountName, accountKey, containerName, fmt.Sprintf("https://%s.blob.core.windows.net", accountName))
}
// NewWithEndpoint creates an cache instance that can be used with autocert.Cache.
// Endpoint can be used to target a different environment that is not Azure
// It returns any errors that could happen while connecting to Azure Blob Storage.
func NewWithEndpoint(accountName, accountKey, containerName, endpointURL string) (*Cache, error) {
credential, err := azblob.NewSharedKeyCredential(accountName, accountKey)
if err != nil {
return nil, err
}
if strings.TrimSpace(containerName) == "" {
return nil, ErrEmptyContainerName
}
pipeline := azblob.NewPipeline(credential, azblob.PipelineOptions{})
endpoint, _ := url.Parse(endpointURL)
serviceURL := azblob.NewServiceURL(*endpoint, pipeline)
containerURL := serviceURL.NewContainerURL(containerName)
// Use GetAccountInfo to contact Azure endpoint and check the credential
// Returns "AuthenticationFailed" if credential is bad
_, err = containerURL.GetAccountInfo(context.Background())
if err != nil {
return nil, err
}
return &Cache{
containerURL: containerURL,
}, nil
}
// DeleteContainer based on current configured container
func (c *Cache) DeleteContainer(ctx context.Context) error {
_, err := c.containerURL.Delete(ctx, azblob.ContainerAccessConditions{})
if isNotFound(err) {
return nil
}
return err
}
// CreateContainer based on current configured container
func (c *Cache) CreateContainer(ctx context.Context) error {
_, err := c.containerURL.Create(ctx, azblob.Metadata{}, azblob.PublicAccessNone)
return err
}
// Get returns a certificate data for the specified key.
// If there's no such key, Get returns ErrCacheMiss.
func (c *Cache) Get(ctx context.Context, key string) ([]byte, error) {
blobURL := c.containerURL.NewBlockBlobURL(key)
get, err := blobURL.Download(ctx, 0, 0, azblob.BlobAccessConditions{}, false, azblob.ClientProvidedKeyOptions{})
if isNotFound(err) {
return nil, autocert.ErrCacheMiss
}
if err != nil {
return nil, err
}
reader := get.Body(azblob.RetryReaderOptions{})
defer reader.Close()
data := &bytes.Buffer{}
data.ReadFrom(reader)
return data.Bytes(), nil
}
// Put stores the data in the cache under the specified key.
func (c *Cache) Put(ctx context.Context, key string, data []byte) error {
blobURL := c.containerURL.NewBlockBlobURL(key)
_, err := blobURL.Upload(
ctx, bytes.NewReader(data),
azblob.BlobHTTPHeaders{
ContentType: "application/x-pem-file",
},
azblob.Metadata{},
azblob.BlobAccessConditions{},
azblob.DefaultAccessTier,
nil, // blobTagsMap
azblob.ClientProvidedKeyOptions{},
azblob.ImmutabilityPolicyOptions{},
)
return err
}
// Delete removes a certificate data from the cache under the specified key.
// If there's no such key in the cache, Delete returns nil.
func (c *Cache) Delete(ctx context.Context, key string) error {
blobURL := c.containerURL.NewBlockBlobURL(key)
_, err := blobURL.Delete(ctx, azblob.DeleteSnapshotsOptionNone, azblob.BlobAccessConditions{})
if isNotFound(err) {
return nil
}
return err
}