2023-07-29 10:59:24 +00:00
|
|
|
// Package s3 implements an HTTP provider for solving the HTTP-01 challenge using AWS S3.
|
2023-07-27 10:42:48 +00:00
|
|
|
package s3
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
2024-02-08 02:16:48 +00:00
|
|
|
"errors"
|
2023-07-27 10:42:48 +00:00
|
|
|
"fmt"
|
|
|
|
"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/go-acme/lego/v4/challenge/http01"
|
|
|
|
)
|
|
|
|
|
|
|
|
// HTTPProvider implements ChallengeProvider for `http-01` challenge.
|
|
|
|
type HTTPProvider struct {
|
|
|
|
bucket string
|
|
|
|
client *s3.Client
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewHTTPProvider returns a HTTPProvider instance with a configured s3 bucket and aws session.
|
|
|
|
// Credentials must be passed in the environment variables.
|
|
|
|
func NewHTTPProvider(bucket string) (*HTTPProvider, error) {
|
|
|
|
if bucket == "" {
|
2024-02-08 02:16:48 +00:00
|
|
|
return nil, errors.New("s3: bucket name missing")
|
2023-07-27 10:42:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
cfg, err := config.LoadDefaultConfig(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("s3: unable to create AWS config: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
client := s3.NewFromConfig(cfg)
|
|
|
|
|
|
|
|
return &HTTPProvider{
|
|
|
|
bucket: bucket,
|
|
|
|
client: client,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Present makes the token available at `HTTP01ChallengePath(token)` by creating a file in the given s3 bucket.
|
|
|
|
func (s *HTTPProvider) Present(domain, token, keyAuth string) error {
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
params := &s3.PutObjectInput{
|
|
|
|
ACL: "public-read",
|
|
|
|
Bucket: aws.String(s.bucket),
|
|
|
|
Key: aws.String(strings.Trim(http01.ChallengePath(token), "/")),
|
|
|
|
Body: bytes.NewReader([]byte(keyAuth)),
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := s.client.PutObject(ctx, params)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("s3: failed to upload token to s3: %w", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// CleanUp removes the file created for the challenge.
|
|
|
|
func (s *HTTPProvider) CleanUp(domain, token, keyAuth string) error {
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
params := &s3.DeleteObjectInput{
|
|
|
|
Bucket: aws.String(s.bucket),
|
|
|
|
Key: aws.String(strings.Trim(http01.ChallengePath(token), "/")),
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := s.client.DeleteObject(ctx, params)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("s3: could not remove file in s3 bucket after HTTP challenge: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|