pomerium/internal/autocert/storage.go
Caleb Doxsey 8d61575ada
autocert: add support for storage in gcs (#3794)
* autocert: add support for storage in s3

* go mod tidy

* skip on mac

* autocert: add support for storage in gcs
2022-12-09 08:22:32 -07:00

99 lines
2.8 KiB
Go

package autocert
import (
"context"
"errors"
"fmt"
"regexp"
"strings"
"cloud.google.com/go/storage"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/caddyserver/certmagic"
)
var (
errUnknownStorageProvider = errors.New("unknown storage provider")
s3virtualRE = regexp.MustCompile(`^([-a-zA-Z0-9]+)\.s3\.([-a-zA-Z0-9]+)\.amazonaws\.com(/.*)?$`)
s3hostRE = regexp.MustCompile(`^([^/]+)/([^/]+)(/.*)?$`)
gcsRE = regexp.MustCompile(`^([^/]+)(/.*)?$`)
)
// GetCertMagicStorage gets the certmagic storage provider based on the destination.
func GetCertMagicStorage(ctx context.Context, dst string) (certmagic.Storage, error) {
idx := strings.Index(dst, "://")
if idx == -1 {
return &certmagic.FileStorage{Path: dst}, nil
}
scheme := dst[:idx]
switch scheme {
case "gs":
bucket := ""
prefix := ""
if match := gcsRE.FindStringSubmatch(dst[idx+3:]); len(match) == 3 {
bucket = match[1]
prefix = strings.TrimPrefix(match[2], "/")
} else {
return nil, fmt.Errorf("autocert: invalid gcs storage location")
}
if prefix != "" && !strings.HasSuffix(prefix, "/") {
prefix += "/"
}
client, err := storage.NewClient(ctx)
if err != nil {
return nil, fmt.Errorf("autocert: error creating gcs storage client: %w", err)
}
return newGCSStorage(client, bucket, prefix), nil
case "s3":
bucket := ""
prefix := ""
var options []func(*config.LoadOptions) error
if match := s3virtualRE.FindStringSubmatch(dst[idx+3:]); len(match) == 4 {
// s3://{bucket}.s3.{region}.amazonaws.com/{prefix}
bucket = match[1]
prefix = strings.TrimPrefix(match[3], "/")
options = append(options, config.WithRegion(match[2]))
} else if match := s3hostRE.FindStringSubmatch(dst[idx+3:]); len(match) == 4 {
// s3://{host}/{bucket-name}/{prefix}
host := match[1]
bucket = match[2]
prefix = strings.TrimPrefix(match[3][1:], "/")
options = append(options,
config.WithRegion("us-east-1"),
config.WithEndpointResolver(aws.EndpointResolverFunc(func(service, region string) (aws.Endpoint, error) {
return aws.Endpoint{
PartitionID: "aws",
URL: "http://" + host,
SigningRegion: "us-east-1",
HostnameImmutable: true,
}, nil
})))
} else {
return nil, fmt.Errorf("autocert: invalid s3 storage location")
}
if prefix != "" && !strings.HasSuffix(prefix, "/") {
prefix += "/"
}
cfg, err := config.LoadDefaultConfig(ctx, options...)
if err != nil {
return nil, fmt.Errorf("autocert: error creating aws config: %w", err)
}
client := s3.NewFromConfig(cfg)
return newS3Storage(client, bucket, prefix), nil
}
return nil, errUnknownStorageProvider
}