server: custom redirect status code and expiration
This commit is contained in:
parent
61986da0b1
commit
d2fd7057a4
6 changed files with 71 additions and 21 deletions
|
|
@ -12,3 +12,6 @@ AWS_SECRET_KEY=example-minio-secret
|
|||
# accessible S3 gateway
|
||||
OBS_REDIRECT_SECURE=false
|
||||
OBS_HOST_REDIRECT=127.0.0.1:9000
|
||||
|
||||
# OBS_REDIRECT_CODE=307 # temporary redirect
|
||||
# OBS_URL_EXPIRY=48h # 2 days
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1 +1,2 @@
|
|||
.env
|
||||
obs-access-signer
|
||||
|
|
@ -10,6 +10,8 @@ There's an example of using it with Varnish Cache, which you can see [here](dock
|
|||
|
||||
Some S3-compatible gateways might not support ACL endpoints but they support presigned access. Currently, the behavior of `obs-access-signer` is similar to `public-read` ACL where clients can access objects anonymously and redirect them (permanently) to presigned URL with `Expires` set to the max signed value of `int64` which has roughly 250yrs lifetime since UNIX time started.
|
||||
|
||||
Update: starting at v0.0.3, obs-access-signer support custom redirection status code and expiry.
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
|
|
|
|||
35
main.go
35
main.go
|
|
@ -4,6 +4,7 @@ import (
|
|||
"flag"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
_ "github.com/joho/godotenv/autoload"
|
||||
"go.uber.org/zap"
|
||||
|
|
@ -13,7 +14,6 @@ import (
|
|||
var (
|
||||
httpAddr string
|
||||
logLevel string
|
||||
// obsSignedUrlExpiry time.Duration
|
||||
zapLogLevel zapcore.Level
|
||||
postFlagParse = []func(){}
|
||||
)
|
||||
|
|
@ -22,10 +22,10 @@ func init() {
|
|||
var err error
|
||||
_ = err
|
||||
|
||||
// app
|
||||
// -- app
|
||||
flag.StringVar(&httpAddr, "addr", os.Getenv("HTTP_ADDR"), "Server address")
|
||||
|
||||
// log
|
||||
// -- log
|
||||
flag.StringVar(&logLevel, "log-level", os.Getenv("LOG_LEVEL"), "Log level")
|
||||
qpostFlagParse(func() {
|
||||
err := zapLogLevel.UnmarshalText([]byte(logLevel))
|
||||
|
|
@ -34,7 +34,7 @@ func init() {
|
|||
}
|
||||
})
|
||||
|
||||
// OBS
|
||||
// -- OBS
|
||||
flag.StringVar(&defaultObsOpts.Endpoint, "obs-endpoint", os.Getenv("OBS_ENDPOINT"), "OBS host")
|
||||
flag.StringVar(&defaultObsOpts.Region, "obs-region", os.Getenv("OBS_REGION"), "OBS region")
|
||||
flag.BoolVar(&defaultObsOpts.Secure, "obs-secure", ok1(strconv.ParseBool(os.Getenv("OBS_SECURE"))), "OBS secure transport")
|
||||
|
|
@ -43,12 +43,25 @@ func init() {
|
|||
flag.BoolVar(&defaultObsOpts.RedirectSecure, "obs-redirect-secure", ok1(strconv.ParseBool(os.Getenv("OBS_REDIRECT_SECURE"))), "OBS redirect secure transport")
|
||||
flag.StringVar(&defaultObsOpts.HostRedirect, "obs-host-redirect", os.Getenv("OBS_HOST_REDIRECT"), "OBS host redirect")
|
||||
|
||||
// obsSignedUrlExpiry, err = time.ParseDuration(os.Getenv("OBS_SIGNED_URL_EXPIRY"))
|
||||
// if err != nil {
|
||||
// // max signed value
|
||||
// obsSignedUrlExpiry = time.Duration(^uint64(0) / 2)
|
||||
// }
|
||||
// flag.DurationVar(&obsSignedUrlExpiry, "obs-signed-url-expiry", obsSignedUrlExpiry, "OBS ")
|
||||
// redirect http code
|
||||
var obsRedirectCode = int64(defaultObsOpts.RedirectCode)
|
||||
if obsRedirectCodeStr := os.Getenv("OBS_REDIRECT_CODE"); obsRedirectCodeStr != "" {
|
||||
obsRedirectCode, err = strconv.ParseInt(obsRedirectCodeStr, 10, 64)
|
||||
if err != nil {
|
||||
obsRedirectCode = int64(defaultObsOpts.RedirectCode)
|
||||
}
|
||||
}
|
||||
flag.IntVar(&defaultObsOpts.RedirectCode, "obs-redirect-code", int(obsRedirectCode), "OBS redirect http code")
|
||||
|
||||
// url expiry
|
||||
var obsUrlExpiry = defaultObsOpts.URLExpiry
|
||||
if obsUrlExpiryStr := os.Getenv("OBS_URL_EXPIRY"); obsUrlExpiryStr != "" {
|
||||
if obsUrlExpiry, err = time.ParseDuration(obsUrlExpiryStr); err != nil {
|
||||
obsUrlExpiry = defaultObsOpts.URLExpiry
|
||||
}
|
||||
}
|
||||
flag.DurationVar(&defaultObsOpts.URLExpiry, "obs-url-expiry", obsUrlExpiry, "OBS url expiry")
|
||||
|
||||
}
|
||||
|
||||
func qpostFlagParse(f func()) {
|
||||
|
|
@ -77,6 +90,8 @@ func main() {
|
|||
"obs_endpoint", defaultObsOpts.Endpoint,
|
||||
"obs_redirect_secure", defaultObsOpts.RedirectSecure,
|
||||
"obs_host_redirect", defaultObsOpts.HostRedirect,
|
||||
"obs_redirect_code", defaultObsOpts.RedirectCode,
|
||||
"obs_url_expiry", defaultObsOpts.URLExpiry.String(),
|
||||
)
|
||||
|
||||
client := unwrap1(newObsClient(defaultObsOpts))
|
||||
|
|
|
|||
10
obs.go
10
obs.go
|
|
@ -6,6 +6,7 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/minio/minio-go/v7"
|
||||
|
|
@ -21,10 +22,17 @@ type obsOptions struct {
|
|||
BucketName string
|
||||
|
||||
RedirectSecure bool
|
||||
RedirectCode int // HTTP redirect status code
|
||||
URLExpiry time.Duration
|
||||
HostRedirect string
|
||||
}
|
||||
|
||||
var defaultObsOpts obsOptions
|
||||
const maxURLExpiry = time.Duration(int64(^uint64(0) / 2))
|
||||
|
||||
var defaultObsOpts = obsOptions{
|
||||
URLExpiry: maxURLExpiry,
|
||||
RedirectCode: http.StatusMovedPermanently, // 301
|
||||
}
|
||||
|
||||
func newObsClient(opts obsOptions) (*minio.Client, error) {
|
||||
client, err := minio.New(opts.Endpoint, &minio.Options{
|
||||
|
|
|
|||
31
server.go
31
server.go
|
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
|
@ -24,8 +25,6 @@ type serverOptions struct {
|
|||
Logger *zap.Logger
|
||||
OBS *obsOptions
|
||||
|
||||
ObjectExpiry time.Duration
|
||||
|
||||
S3 *minio.Client
|
||||
}
|
||||
|
||||
|
|
@ -99,11 +98,12 @@ func (s *server) handle(ctx *fasthttp.RequestCtx) {
|
|||
}
|
||||
|
||||
// compose initial request
|
||||
expireSeconds := int64(s.opts.OBS.URLExpiry / time.Second)
|
||||
req, err := newRequest(s.opts.S3, ctx, http.MethodGet, requestMetadata{
|
||||
presignURL: true,
|
||||
bucketName: bucketName,
|
||||
objectName: objectName,
|
||||
expires: 1, // to trigger presigned generator
|
||||
expires: expireSeconds, // to trigger presigned generator
|
||||
queryValues: url.Values{},
|
||||
})
|
||||
if err != nil {
|
||||
|
|
@ -120,8 +120,28 @@ func (s *server) handle(ctx *fasthttp.RequestCtx) {
|
|||
return
|
||||
}
|
||||
|
||||
var statusCode = s.opts.OBS.RedirectCode
|
||||
|
||||
// custom "expiry"
|
||||
var exp string
|
||||
if expiry := s.opts.OBS.URLExpiry; expiry == maxURLExpiry || expiry <= 0 {
|
||||
// clear given params, set max signed value for expire, and re-presign.
|
||||
exp := strconv.FormatInt(int64(^uint64(0)/2), 10) // ~250years
|
||||
exp = strconv.FormatInt(int64(^uint64(0)/2), 10) // ~250years
|
||||
} else {
|
||||
// we can't allow a permanent redirect here since we already have
|
||||
// expiry set, the redirected url needs to be updated.
|
||||
if statusCode == http.StatusMovedPermanently || (statusCode < 300 || statusCode > 399) {
|
||||
statusCode = http.StatusTemporaryRedirect
|
||||
}
|
||||
|
||||
expireAt := time.Now().UTC().Add(s.opts.OBS.URLExpiry)
|
||||
exp = strconv.FormatInt(int64(expireAt.Unix()), 10)
|
||||
// set object cache lifetime
|
||||
if statusCode == http.StatusTemporaryRedirect {
|
||||
ctx.Response.Header.Set("Cache-Control", fmt.Sprintf("max-age=%d", expireSeconds))
|
||||
ctx.Response.Header.Set("Expires", expireAt.Format("Mon, 02 Jan 2006 15:04:05 GMT"))
|
||||
}
|
||||
}
|
||||
req.Header.Set("Expires", exp)
|
||||
req.URL.RawQuery = ""
|
||||
req = signer.PreSignV2(*req, value.AccessKeyID, value.SecretAccessKey, 0, isVirtualHostStyle)
|
||||
|
|
@ -130,6 +150,7 @@ func (s *server) handle(ctx *fasthttp.RequestCtx) {
|
|||
query := req.URL.Query()
|
||||
query.Set("Expires", exp)
|
||||
req.URL.RawQuery = s3utils.QueryEncode(query)
|
||||
|
||||
if s.opts.OBS.RedirectSecure {
|
||||
req.URL.Scheme = "https"
|
||||
} else {
|
||||
|
|
@ -140,7 +161,7 @@ func (s *server) handle(ctx *fasthttp.RequestCtx) {
|
|||
req.URL.Host = hostRedirect
|
||||
}
|
||||
|
||||
ctx.Redirect(req.URL.String(), http.StatusMovedPermanently)
|
||||
ctx.Redirect(req.URL.String(), statusCode)
|
||||
}
|
||||
|
||||
func (s *server) Run() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue