package autocert import ( "context" "errors" "fmt" "regexp" "strings" "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(`^([^/]+)/([^/]+)(/.*)?$`) ) // 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 "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 = 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] if strings.HasPrefix(match[3], "/") { prefix = 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 }))) } 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 &s3Storage{ client: client, bucket: bucket, prefix: prefix, }, nil } return nil, errUnknownStorageProvider }