packer-cn/builder/oracle/oci/client/transport.go

117 lines
2.7 KiB
Go

package oci
import (
"bytes"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
"fmt"
"io"
"net/http"
"strconv"
"strings"
"time"
)
type nopCloser struct {
io.Reader
}
func (nopCloser) Close() error {
return nil
}
// Transport adds OCI signature authentication to each outgoing request.
type Transport struct {
transport http.RoundTripper
config *Config
}
// NewTransport creates a new Transport to add OCI signature authentication
// to each outgoing request.
func NewTransport(transport http.RoundTripper, config *Config) *Transport {
return &Transport{
transport: transport,
config: config,
}
}
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
var buf *bytes.Buffer
if req.Body != nil {
buf = new(bytes.Buffer)
buf.ReadFrom(req.Body)
req.Body = nopCloser{buf}
}
if req.Header.Get("date") == "" {
req.Header.Set("date", time.Now().UTC().Format(http.TimeFormat))
}
if req.Header.Get("content-type") == "" {
req.Header.Set("content-type", "application/json")
}
if req.Header.Get("accept") == "" {
req.Header.Set("accept", "application/json")
}
if req.Header.Get("host") == "" {
req.Header.Set("host", req.URL.Host)
}
var signheaders []string
if (req.Method == "PUT" || req.Method == "POST") && buf != nil {
signheaders = []string{"(request-target)", "host", "date",
"content-length", "content-type", "x-content-sha256"}
if req.Header.Get("content-length") == "" {
req.Header.Set("content-length", strconv.Itoa(buf.Len()))
}
hasher := sha256.New()
hasher.Write(buf.Bytes())
hash := hasher.Sum(nil)
req.Header.Set("x-content-sha256", base64.StdEncoding.EncodeToString(hash))
} else {
signheaders = []string{"date", "host", "(request-target)"}
}
var signbuffer bytes.Buffer
for idx, header := range signheaders {
signbuffer.WriteString(header)
signbuffer.WriteString(": ")
if header == "(request-target)" {
signbuffer.WriteString(strings.ToLower(req.Method))
signbuffer.WriteString(" ")
signbuffer.WriteString(req.URL.RequestURI())
} else {
signbuffer.WriteString(req.Header.Get(header))
}
if idx < len(signheaders)-1 {
signbuffer.WriteString("\n")
}
}
h := sha256.New()
h.Write(signbuffer.Bytes())
digest := h.Sum(nil)
signature, err := rsa.SignPKCS1v15(rand.Reader, t.config.Key, crypto.SHA256, digest)
if err != nil {
return nil, err
}
authHeader := fmt.Sprintf("Signature headers=\"%s\","+
"keyId=\"%s/%s/%s\","+
"algorithm=\"rsa-sha256\","+
"signature=\"%s\","+
"version=\"1\"",
strings.Join(signheaders, " "),
t.config.Tenancy, t.config.User, t.config.Fingerprint,
base64.StdEncoding.EncodeToString(signature))
req.Header.Add("Authorization", authHeader)
return t.transport.RoundTrip(req)
}