Skip to content

Commit

Permalink
v0.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Robert Usher committed Dec 11, 2023
0 parents commit 10525e5
Show file tree
Hide file tree
Showing 10 changed files with 306 additions and 0 deletions.
39 changes: 39 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Build and Release

on:
workflow_run:
workflows:
- Create Tag
types:
- completed

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version-file: go.mod
- name: Version
run: echo "::set-output name=version::$(cat VERSION)"
id: version
- name: Build artifacts
run: make build
- name: Create release
uses: softprops/action-gh-release@v1
if: ${{ github.event.workflow_run.conclusion == 'success' }}
with:
draft: false
files: |
./dist/darwin_amd64/presign
./dist/darwin_arm64/presign
./dist/linux_amd64/presign
./dist/windows_amd64/presign.exe
generate_release_notes: true
name: ${{ steps.version.outputs.version }}
prerelease: false
tag_name: v${{ steps.version.outputs.version }}
23 changes: 23 additions & 0 deletions .github/workflows/tag.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Create Tag

on:
push:
branches:
- main

jobs:
create_tag:
name: Create and Push Tag
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Version
run: echo "::set-output name=version::$(cat VERSION)"
id: version
- name: Tag
run: |
git config user.name github-actions
git config user.email [email protected]
git tag -a v${{ steps.version.outputs.version }} -m "${{ steps.version.outputs.version }}"
git push origin v${{ steps.version.outputs.version }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist/
16 changes: 16 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
build: pkg-linux pkg-macos-intel pkg-macos-arm pkg-windows

pkg-linux:
GOOS=linux GOARCH=amd64 go build -o dist/linux_amd64/presign

pkg-macos-intel:
GOOS=darwin GOARCH=amd64 go build -o dist/darwin_amd64/presign

pkg-macos-arm:
GOOS=darwin GOARCH=arm64 go build -o dist/darwin_arm64/presign

pkg-windows:
GOOS=windows GOARCH=amd64 go build -o dist/windows_amd64/presign.exe

clean:
rm -rf dist
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# aws-presigner

A simple command-line tool to create AWS S3 presigned URLs. The main motivation to create this was
because creating PUT URLs is not possible via the console or AWS CLI tool.

## Usage

To use, download the executable file and run like this:

```sh
./presign -b rusher-test -k some-file.txt -m put
```

If you get a permission error, you made need to update the file permissions

```sh
chmod +x presign
```

## Switching environments

This tool loads the default AWS config, so it relies on whichever AWS CLI profile is currently set in your enviroment. To use a different environment without globally changing your aws profile, you can prepend the command to set the `AWS_PROFILE` environment variable for the execution

```sh
AWS_PROFILE=qa ./presign -b rusher-test -k some-file.txt -m put
```
1 change: 1 addition & 0 deletions VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.1.0
27 changes: 27 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module github.com/stori-rusher/aws-presigner

go 1.21.4

require (
github.com/aws/aws-sdk-go-v2 v1.24.0
github.com/aws/aws-sdk-go-v2/config v1.26.0
github.com/aws/aws-sdk-go-v2/service/s3 v1.47.4
)

require (
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.16.11 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.18.4 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.26.4 // indirect
github.com/aws/smithy-go v1.19.0 // indirect
)
38 changes: 38 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
github.com/aws/aws-sdk-go-v2 v1.24.0 h1:890+mqQ+hTpNuw0gGP6/4akolQkSToDJgHfQE7AwGuk=
github.com/aws/aws-sdk-go-v2 v1.24.0/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 h1:OCs21ST2LrepDfD3lwlQiOqIGp6JiEUqG84GzTDoyJs=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4/go.mod h1:usURWEKSNNAcAZuzRn/9ZYPT8aZQkR7xcCtunK/LkJo=
github.com/aws/aws-sdk-go-v2/config v1.26.0 h1:uItWWbD/FmHPGSa6GJFyZJD/RPakVjS0fmoq1vccjNw=
github.com/aws/aws-sdk-go-v2/config v1.26.0/go.mod h1:8Rf77VTcX9MMkoMIsCnuwmef+Y1bs2Zhvw9IXHdD/Po=
github.com/aws/aws-sdk-go-v2/credentials v1.16.11 h1:Gcut3tJSU7F/C5W/NnFimqnJqljF58rmaw7QlbigN3U=
github.com/aws/aws-sdk-go-v2/credentials v1.16.11/go.mod h1:CysUbSCfqvEbEQTd9Ubg2RrJy2EFM+AUHJOqqj0guTo=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 h1:w98BT5w+ao1/r5sUuiH6JkVzjowOKeOJRHERyy1vh58=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10/go.mod h1:K2WGI7vUvkIv1HoNbfBA1bvIZ+9kL3YVmWxeKuLQsiw=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9 h1:v+HbZaCGmOwnTTVS86Fleq0vPzOd7tnJGbFhP0stNLs=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9/go.mod h1:Xjqy+Nyj7VDLBtCMkQYOw1QYfAEZCVLrfI0ezve8wd4=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9 h1:N94sVhRACtXyVcjXxrwK1SKFIJrA9pOJ5yu2eSHnmls=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9/go.mod h1:hqamLz7g1/4EJP+GH5NBhcUMLjW+gKLQabgyz6/7WAU=
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 h1:uR9lXYjdPX0xY+NhvaJ4dD8rpSRz5VY81ccIIoNG+lw=
github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9 h1:ugD6qzjYtB7zM5PN/ZIeaAIyefPaD82G8+SJopgvUpw=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9/go.mod h1:YD0aYBWCrPENpHolhKw2XDlTIWae2GKXT1T4o6N6hiM=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4 h1:/b31bi3YVNlkzkBrm9LfpaKoaYZUxIAj4sHfOTmLfqw=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.4/go.mod h1:2aGXHFmbInwgP9ZfpmdIfOELL79zhdNYNmReK8qDfdQ=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9 h1:/90OR2XbSYfXucBMJ4U14wrjlfleq/0SB6dZDPncgmo=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.9/go.mod h1:dN/Of9/fNZet7UrQQ6kTDo/VSwKPIq94vjlU16bRARc=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9 h1:Nf2sHxjMJR8CSImIVCONRi4g0Su3J+TSTbS7G0pUeMU=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.9/go.mod h1:idky4TER38YIjr2cADF1/ugFMKvZV7p//pVeV5LZbF0=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9 h1:iEAeF6YC3l4FzlJPP9H3Ko1TXpdjdqWffxXjp8SY6uk=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.9/go.mod h1:kjsXoK23q9Z/tLBrckZLLyvjhZoS+AGrzqzUfEClvMM=
github.com/aws/aws-sdk-go-v2/service/s3 v1.47.4 h1:iEkLh6fe2ATtH5PGynlJ1SdnbZuZgoWLdvSedjwmqKk=
github.com/aws/aws-sdk-go-v2/service/s3 v1.47.4/go.mod h1:vADO6Jn+Rq4nDtfwNjhgR84qkZwiC6FqCaXdw/kYwjA=
github.com/aws/aws-sdk-go-v2/service/sso v1.18.4 h1:2UVO4N/polvKeP+yCA8TLEmidEKxmNTeVpsZnj/bbgA=
github.com/aws/aws-sdk-go-v2/service/sso v1.18.4/go.mod h1:CaFfXLYL376jgbP7VKC96uFcU8Rlavak0UlAwk1Dlhc=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.4 h1:3JXkQ1F5n73qTpSPas6AQ8/6HFksgnB24JlNPLt3SlM=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.4/go.mod h1:W+nd4wWDVkSUIox9bacmkBP5NMFQeTJ/xqNabpzSR38=
github.com/aws/aws-sdk-go-v2/service/sts v1.26.4 h1:gaRFldXhoT36jVMfQ+AjAYwSfjO5LMgy1u0ObcKFhhc=
github.com/aws/aws-sdk-go-v2/service/sts v1.26.4/go.mod h1:XX5gh4CB7wAs4KhcF46G6C8a2i7eupU19dcAAE+EydU=
github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM=
github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
82 changes: 82 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package main

import (
"context"
_ "embed"
"flag"
"fmt"
"log"
"os"
"strings"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
)

//go:embed VERSION
var version string

func main() {
ctx := context.Background()

var (
b, k, m string
d int64
v bool
)
flag.StringVar(&b, "b", "", "bucket")
flag.Int64Var(&d, "d", 0, "duration in seconds (max 604,800)")
flag.StringVar(&k, "k", "", "key")
flag.StringVar(&m, "m", "", "method (get|put)")
flag.BoolVar(&v, "v", false, "version")

flag.Usage = func() {
fmt.Fprintf(flag.CommandLine.Output(), "Usage of %s:\n", os.Args[0])
flag.PrintDefaults()
fmt.Printf("\nVersion:\n %s\n", version)
}

flag.Parse()

if v {
fmt.Printf("v%s\n", version)
os.Exit(0)
}

awsCFG, err := config.LoadDefaultConfig(ctx)
if err != nil {
log.Fatalf("unable to load S3 config, %v", err)
}

s3Client := s3.NewFromConfig(awsCFG)

loc, err := s3Client.GetBucketLocation(ctx, &s3.GetBucketLocationInput{Bucket: aws.String(b)})
if err != nil {
log.Fatalf("unable to get bucket location: %v\n", err)
}

p := NewS3Presigner(s3.NewPresignClient(s3Client), string(loc.LocationConstraint))

if d < 1 || d > 604_800 {
log.Fatalf("duration must be between 1 and 604,800 seconds (7 days)")
}

var obj *v4.PresignedHTTPRequest
switch strings.ToLower(m) {
case "get":
obj, err = p.GetObject(ctx, b, k, time.Duration(d)*time.Second)
case "put":
obj, err = p.PutObject(ctx, b, k, time.Duration(d)*time.Second)
default:
log.Fatalf("invalid method: %v", m)
}

if err != nil {
log.Fatalf("unable to presign request: %v\n", err)
}

fmt.Println(obj.URL)
}
53 changes: 53 additions & 0 deletions presigner.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package main

import (
"context"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
v4 "github.com/aws/aws-sdk-go-v2/aws/signer/v4"
"github.com/aws/aws-sdk-go-v2/service/s3"
)

type Presigner interface {
PresignGetObject(context.Context, *s3.GetObjectInput, ...func(*s3.PresignOptions)) (*v4.PresignedHTTPRequest, error)
PresignPutObject(context.Context, *s3.PutObjectInput, ...func(*s3.PresignOptions)) (*v4.PresignedHTTPRequest, error)
}

// S3Presigner encapsulates the Amazon Simple Storage Service (Amazon S3) presign actions
// used in the examples.
// It contains PresignClient, a client that is used to presign requests to Amazon S3.
// Presigned requests contain temporary credentials and can be made from any HTTP client.
type S3Presigner struct {
client *s3.PresignClient
region string
}

func NewS3Presigner(client *s3.PresignClient, region string) S3Presigner {
return S3Presigner{client, region}
}

// GetObject makes a presigned request that can be used to get an object from a bucket.
// The presigned request is valid for the specified number of seconds.
func (p S3Presigner) GetObject(ctx context.Context, bucket, key string, exp time.Duration) (*v4.PresignedHTTPRequest, error) {
input := &s3.GetObjectInput{Bucket: aws.String(bucket), Key: aws.String(key)}

return p.client.PresignGetObject(ctx, input, s3.WithPresignExpires(exp), withRegion(p.region))
}

// PutObject makes a presigned request that can be used to put an object in a bucket.
// The presigned request is valid for the specified number of seconds.
func (p S3Presigner) PutObject(ctx context.Context, bucket, key string, exp time.Duration) (*v4.PresignedHTTPRequest, error) {
input := &s3.PutObjectInput{Bucket: aws.String(bucket), Key: aws.String(key)}

return p.client.PresignPutObject(ctx, input, s3.WithPresignExpires(exp), withRegion(p.region))
}

// withRegion is a helper function that adds the region to the PresignOptions.
func withRegion(region string) func(*s3.PresignOptions) {
return func(o *s3.PresignOptions) {
o.ClientOptions = append(o.ClientOptions,
func(o *s3.Options) { o.Region = region },
)
}
}

0 comments on commit 10525e5

Please sign in to comment.