Alerting: Linking external images securely - Azure Blob (#1) (#56598)

pull/57980/head
Petr Stupka 3 years ago committed by GitHub
parent 3188af9be3
commit e99f75f0ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      conf/defaults.ini
  2. 1
      conf/sample.ini
  3. 4
      docs/sources/setup-grafana/configure-grafana/_index.md
  4. 6
      go.mod
  5. 4
      go.sum
  6. 45
      pkg/components/imguploader/azureblobuploader.go
  7. 4
      pkg/components/imguploader/imguploader.go
  8. 3
      pkg/components/imguploader/imguploader_test.go

@ -1109,6 +1109,7 @@ signed_url_expiration =
account_name = account_name =
account_key = account_key =
container_name = container_name =
sas_token_expiration_days =
[external_image_storage.local] [external_image_storage.local]
# does not require any configuration # does not require any configuration

@ -1074,6 +1074,7 @@
;account_name = ;account_name =
;account_key = ;account_key =
;container_name = ;container_name =
;sas_token_expiration_days =
[external_image_storage.local] [external_image_storage.local]
# does not require any configuration # does not require any configuration

@ -1747,6 +1747,10 @@ Storage account key
Container name where to store "Blob" images with random names. Creating the blob container beforehand is required. Only public containers are supported. Container name where to store "Blob" images with random names. Creating the blob container beforehand is required. Only public containers are supported.
### sas_token_expiration_days
Number of days for SAS token validity. If specified SAS token will be attached to image URL. Allow storing images in private containers.
<hr> <hr>
## [external_image_storage.local] ## [external_image_storage.local]

@ -44,7 +44,7 @@ require (
github.com/go-sql-driver/mysql v1.6.0 github.com/go-sql-driver/mysql v1.6.0
github.com/go-stack/stack v1.8.1 github.com/go-stack/stack v1.8.1
github.com/gobwas/glob v0.2.3 github.com/gobwas/glob v0.2.3
github.com/gofrs/uuid v4.3.0+incompatible github.com/gofrs/uuid v4.3.0+incompatible // indirect
github.com/gogo/protobuf v1.3.2 github.com/gogo/protobuf v1.3.2
github.com/golang/mock v1.6.0 github.com/golang/mock v1.6.0
github.com/golang/snappy v0.0.4 github.com/golang/snappy v0.0.4
@ -240,6 +240,7 @@ require (
cloud.google.com/go/kms v1.4.0 cloud.google.com/go/kms v1.4.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.13.2 github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.13.2
github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.4.0 github.com/Azure/azure-sdk-for-go/sdk/keyvault/azkeys v0.4.0
github.com/Azure/azure-storage-blob-go v0.15.0
github.com/Azure/go-autorest/autorest/adal v0.9.20 github.com/Azure/go-autorest/autorest/adal v0.9.20
github.com/armon/go-radix v1.0.0 github.com/armon/go-radix v1.0.0
github.com/blugelabs/bluge v0.1.9 github.com/blugelabs/bluge v0.1.9
@ -261,11 +262,11 @@ require (
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.7.0
gocloud.dev v0.25.0 gocloud.dev v0.25.0
gotest.tools v2.2.0+incompatible
) )
require ( require (
cloud.google.com/go v0.100.2 // indirect cloud.google.com/go v0.100.2 // indirect
github.com/Azure/azure-pipeline-go v0.2.3 // indirect
github.com/armon/go-metrics v0.3.10 // indirect github.com/armon/go-metrics v0.3.10 // indirect
github.com/bmatcuk/doublestar v1.1.1 // indirect github.com/bmatcuk/doublestar v1.1.1 // indirect
github.com/buildkite/yaml v2.1.0+incompatible // indirect github.com/buildkite/yaml v2.1.0+incompatible // indirect
@ -279,6 +280,7 @@ require (
github.com/hashicorp/memberlist v0.4.0 // indirect github.com/hashicorp/memberlist v0.4.0 // indirect
github.com/kr/text v0.2.0 // indirect github.com/kr/text v0.2.0 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-ieproxy v0.0.3 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/rivo/uniseg v0.2.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect

@ -96,6 +96,7 @@ github.com/Azure/azure-amqp-common-go/v3 v3.2.1/go.mod h1:O6X1iYHP7s2x7NjUKsXVhk
github.com/Azure/azure-amqp-common-go/v3 v3.2.2/go.mod h1:O6X1iYHP7s2x7NjUKsXVhkwWrQhxrd+d8/3rRadj4CI= github.com/Azure/azure-amqp-common-go/v3 v3.2.2/go.mod h1:O6X1iYHP7s2x7NjUKsXVhkwWrQhxrd+d8/3rRadj4CI=
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U=
github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k= github.com/Azure/azure-pipeline-go v0.2.3/go.mod h1:x841ezTBIMG6O3lAcl8ATHnsOPVl2bqk7S3ta6S6u4k=
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go v23.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v23.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
@ -135,6 +136,8 @@ github.com/Azure/azure-service-bus-go v0.11.5/go.mod h1:MI6ge2CuQWBVq+ly456MY7Xq
github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0= github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0=
github.com/Azure/azure-storage-blob-go v0.13.0/go.mod h1:pA9kNqtjUeQF2zOSu4s//nUdBD+e64lEuc4sVnuOfNs= github.com/Azure/azure-storage-blob-go v0.13.0/go.mod h1:pA9kNqtjUeQF2zOSu4s//nUdBD+e64lEuc4sVnuOfNs=
github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck= github.com/Azure/azure-storage-blob-go v0.14.0/go.mod h1:SMqIBi+SuiQH32bvyjngEewEeXoPfKMgWlBDaYf6fck=
github.com/Azure/azure-storage-blob-go v0.15.0 h1:rXtgp8tN1p29GvpGgfJetavIG0V7OgcSXPpwp3tx6qk=
github.com/Azure/azure-storage-blob-go v0.15.0/go.mod h1:vbjsVbX0dlxnRc4FFMPsS9BsJWPcne7GB7onqlPvz58=
github.com/Azure/go-amqp v0.16.0/go.mod h1:9YJ3RhxRT1gquYnzpZO1vcYMMpAdJT+QEg6fwmw9Zlg= github.com/Azure/go-amqp v0.16.0/go.mod h1:9YJ3RhxRT1gquYnzpZO1vcYMMpAdJT+QEg6fwmw9Zlg=
github.com/Azure/go-amqp v0.16.4/go.mod h1:9YJ3RhxRT1gquYnzpZO1vcYMMpAdJT+QEg6fwmw9Zlg= github.com/Azure/go-amqp v0.16.4/go.mod h1:9YJ3RhxRT1gquYnzpZO1vcYMMpAdJT+QEg6fwmw9Zlg=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
@ -1805,6 +1808,7 @@ github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HN
github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc= github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-ieproxy v0.0.0-20191113090002-7c0f6868bffe/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= github.com/mattn/go-ieproxy v0.0.0-20191113090002-7c0f6868bffe/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E= github.com/mattn/go-ieproxy v0.0.1/go.mod h1:pYabZ6IHcRpFh7vIaLfK7rdcWgFEb3SFJ6/gNWuh88E=
github.com/mattn/go-ieproxy v0.0.3 h1:YkaHmK1CzE5C4O7A3hv3TCbfNDPSCf0RKZFX+VhBeYk=
github.com/mattn/go-ieproxy v0.0.3/go.mod h1:6ZpRmhBaYuBX1U2za+9rC9iCGLsSp2tftelZne7CPko= github.com/mattn/go-ieproxy v0.0.3/go.mod h1:6ZpRmhBaYuBX1U2za+9rC9iCGLsSp2tftelZne7CPko=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=

@ -19,6 +19,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/Azure/azure-storage-blob-go/azblob"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/util" "github.com/grafana/grafana/pkg/util"
) )
@ -27,14 +28,16 @@ type AzureBlobUploader struct {
account_name string account_name string
account_key string account_key string
container_name string container_name string
sas_token_expiration_days int
log log.Logger log log.Logger
} }
func NewAzureBlobUploader(account_name string, account_key string, container_name string) *AzureBlobUploader { func NewAzureBlobUploader(account_name string, account_key string, container_name string, sas_token_expiration_days int) *AzureBlobUploader {
return &AzureBlobUploader{ return &AzureBlobUploader{
account_name: account_name, account_name: account_name,
account_key: account_key, account_key: account_key,
container_name: container_name, container_name: container_name,
sas_token_expiration_days: sas_token_expiration_days,
log: log.New("azureBlobUploader"), log: log.New("azureBlobUploader"),
} }
} }
@ -91,9 +94,49 @@ func (az *AzureBlobUploader) Upload(ctx context.Context, imageDiskPath string) (
} }
url := fmt.Sprintf("https://%s.blob.core.windows.net/%s/%s", az.account_name, az.container_name, randomFileName) url := fmt.Sprintf("https://%s.blob.core.windows.net/%s/%s", az.account_name, az.container_name, randomFileName)
if az.sas_token_expiration_days > 0 {
url, err = blob.GetBlobSasUrl(ctx, az.container_name, randomFileName, az.sas_token_expiration_days)
if err != nil {
return "", err
}
}
return url, nil return url, nil
} }
// SignWithSharedKey uses an account's SharedKeyCredential to sign this signature values to produce the proper SAS query parameters.
func (c *StorageClient) GetBlobSasUrl(ctx context.Context, containerName, blobName string, sasTokenExpiration int) (string, error) {
if c.Auth == nil {
return "", fmt.Errorf("cannot sign SAS query without Shared Key Credential")
}
// create source blob SAS url
credential, err := azblob.NewSharedKeyCredential(c.Auth.Account, c.Auth.Key)
if err != nil {
return "", err
}
// Set the desired SAS signature values and sign them with the shared key credentials to get the SAS query parameters.
sasQueryParams, err := azblob.BlobSASSignatureValues{
Protocol: azblob.SASProtocolHTTPS, // Users MUST use HTTPS (not HTTP)
ExpiryTime: time.Now().UTC().AddDate(0, 0, sasTokenExpiration), // Expiration time
ContainerName: containerName,
BlobName: blobName,
Permissions: azblob.BlobSASPermissions{Add: false, Read: true, Write: false}.String(), // Read only permissions
}.NewSASQueryParameters(credential)
if err != nil {
return "", err
}
// Create the URL of the resource you wish to access and append the SAS query parameters.
// Since this is a blob SAS, the URL is to the Azure storage blob.
qp := sasQueryParams.Encode()
blobSasUrl := fmt.Sprintf("https://%s.blob.core.windows.net/%s/%s?%s", c.Auth.Account, containerName, blobName, qp)
// Return Blob SAS token URL
return blobSasUrl, nil
}
// --- AZURE LIBRARY // --- AZURE LIBRARY
type Error struct { type Error struct {
Code int Code int

@ -110,8 +110,10 @@ func NewImageUploader() (ImageUploader, error) {
account_name := azureBlobSec.Key("account_name").MustString("") account_name := azureBlobSec.Key("account_name").MustString("")
account_key := azureBlobSec.Key("account_key").MustString("") account_key := azureBlobSec.Key("account_key").MustString("")
container_name := azureBlobSec.Key("container_name").MustString("") container_name := azureBlobSec.Key("container_name").MustString("")
sas_token_expiration_days := azureBlobSec.Key("sas_token_expiration_days").MustInt(-1)
return NewAzureBlobUploader(account_name, account_key, container_name, sas_token_expiration_days), nil
return NewAzureBlobUploader(account_name, account_key, container_name), nil
case "local": case "local":
return NewLocalImageUploader() return NewLocalImageUploader()
} }

@ -154,6 +154,8 @@ func TestImageUploaderFactory(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
_, err = azureBlobSec.NewKey("container_name", "container_name") _, err = azureBlobSec.NewKey("container_name", "container_name")
require.NoError(t, err) require.NoError(t, err)
_, err = azureBlobSec.NewKey("sas_token_expiration_days", "sas_token_expiration_days")
require.NoError(t, err)
uploader, err := NewImageUploader() uploader, err := NewImageUploader()
require.NoError(t, err) require.NoError(t, err)
@ -163,6 +165,7 @@ func TestImageUploaderFactory(t *testing.T) {
require.Equal(t, "account_name", original.account_name) require.Equal(t, "account_name", original.account_name)
require.Equal(t, "account_key", original.account_key) require.Equal(t, "account_key", original.account_key)
require.Equal(t, "container_name", original.container_name) require.Equal(t, "container_name", original.container_name)
require.Equal(t, -1, original.sas_token_expiration_days)
}) })
}) })

Loading…
Cancel
Save