2021-03-10 15:24:13 -05:00
|
|
|
// Copyright 2013 The imageproxy authors.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
2019-03-27 16:51:22 -05:00
|
|
|
// The imageproxy-sign tool creates signature values for a provided URL and
|
|
|
|
// signing key.
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/hmac"
|
|
|
|
"crypto/sha256"
|
|
|
|
"encoding/base64"
|
2019-06-11 12:45:25 -05:00
|
|
|
"errors"
|
2019-03-27 16:51:22 -05:00
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"os"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"willnorris.com/go/imageproxy"
|
|
|
|
)
|
|
|
|
|
2019-06-11 12:45:25 -05:00
|
|
|
var signingKey = flag.String("key", "@/etc/imageproxy.key", "signing key, or file containing key prefixed with '@'")
|
2019-03-27 16:51:22 -05:00
|
|
|
var urlOnly = flag.Bool("url", false, "only sign the URL value, do not include options")
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
flag.Parse()
|
2019-06-11 12:45:25 -05:00
|
|
|
u := flag.Arg(0)
|
2019-03-27 16:51:22 -05:00
|
|
|
|
2019-06-11 12:45:25 -05:00
|
|
|
sig, err := sign(*signingKey, u, *urlOnly)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println(err)
|
2019-03-27 16:51:22 -05:00
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
2019-06-11 12:45:25 -05:00
|
|
|
fmt.Printf("url: %v\n", u)
|
|
|
|
fmt.Printf("signature: %v\n", base64.URLEncoding.EncodeToString(sig))
|
|
|
|
}
|
|
|
|
|
|
|
|
func sign(key string, s string, urlOnly bool) ([]byte, error) {
|
|
|
|
if s == "" {
|
|
|
|
return nil, errors.New("imageproxy-sign url [key]")
|
|
|
|
}
|
|
|
|
|
|
|
|
u := parseURL(s)
|
2019-03-27 16:51:22 -05:00
|
|
|
if u == nil {
|
2019-06-11 12:45:25 -05:00
|
|
|
return nil, fmt.Errorf("unable to parse URL: %v", s)
|
2019-03-27 16:51:22 -05:00
|
|
|
}
|
2019-06-11 12:45:25 -05:00
|
|
|
if urlOnly {
|
2019-03-27 16:51:22 -05:00
|
|
|
u.Fragment = ""
|
|
|
|
}
|
|
|
|
|
2019-06-11 12:45:25 -05:00
|
|
|
k, err := parseKey(key)
|
2019-03-27 16:51:22 -05:00
|
|
|
if err != nil {
|
2022-01-22 13:29:37 -05:00
|
|
|
return nil, fmt.Errorf("error parsing key: %w", err)
|
2019-03-27 16:51:22 -05:00
|
|
|
}
|
|
|
|
|
2020-09-10 02:30:25 -05:00
|
|
|
mac := hmac.New(sha256.New, k)
|
2020-06-19 20:08:35 -05:00
|
|
|
if _, err := mac.Write([]byte(u.String())); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-06-11 12:45:25 -05:00
|
|
|
return mac.Sum(nil), nil
|
2019-03-27 16:51:22 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func parseKey(s string) ([]byte, error) {
|
|
|
|
if strings.HasPrefix(s, "@") {
|
2023-01-31 23:30:38 -05:00
|
|
|
return os.ReadFile(s[1:])
|
2019-03-27 16:51:22 -05:00
|
|
|
}
|
|
|
|
return []byte(s), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// parseURL parses s as either an imageproxy request URL or a remote URL with
|
|
|
|
// options in the URL fragment. Any existing signature values are stripped,
|
|
|
|
// and the final remote URL returned with remaining options in the fragment.
|
|
|
|
func parseURL(s string) *url.URL {
|
|
|
|
u, err := url.Parse(s)
|
|
|
|
if s == "" || err != nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// first try to parse this as an imageproxy URL, containing
|
|
|
|
// transformation options and the remote URL embedded
|
|
|
|
if r, err := imageproxy.NewRequest(&http.Request{URL: u}, nil); err == nil {
|
|
|
|
r.Options.Signature = ""
|
|
|
|
r.URL.Fragment = r.Options.String()
|
|
|
|
return r.URL
|
|
|
|
}
|
|
|
|
|
|
|
|
// second, we assume that this is the remote URL itself. If a fragment
|
|
|
|
// is present, treat it as an option string.
|
|
|
|
opt := imageproxy.ParseOptions(u.Fragment)
|
|
|
|
opt.Signature = ""
|
|
|
|
u.Fragment = opt.String()
|
|
|
|
return u
|
|
|
|
}
|