77 lines
1.5 KiB
Go
77 lines
1.5 KiB
Go
package secret
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"sync"
|
|
)
|
|
|
|
const numSecretBytes = 32
|
|
|
|
type sec struct {
|
|
path string
|
|
bytes []byte
|
|
sync.RWMutex
|
|
}
|
|
|
|
var (
|
|
theSecret = &sec{}
|
|
)
|
|
|
|
func SetPath(path string) {
|
|
theSecret.Lock()
|
|
defer theSecret.Unlock()
|
|
theSecret.path = path
|
|
theSecret.bytes = nil
|
|
}
|
|
|
|
// Lazy access to the HMAC secret key. We must be lazy because if the key
|
|
// is not already there, it will be generated by gitlab-rails, and
|
|
// gitlab-rails is slow.
|
|
func Bytes() ([]byte, error) {
|
|
if bytes := getBytes(); bytes != nil {
|
|
return copyBytes(bytes), nil
|
|
}
|
|
|
|
return setBytes()
|
|
}
|
|
|
|
func getBytes() []byte {
|
|
theSecret.RLock()
|
|
defer theSecret.RUnlock()
|
|
return theSecret.bytes
|
|
}
|
|
|
|
func copyBytes(bytes []byte) []byte {
|
|
out := make([]byte, len(bytes))
|
|
copy(out, bytes)
|
|
return out
|
|
}
|
|
|
|
func setBytes() ([]byte, error) {
|
|
theSecret.Lock()
|
|
defer theSecret.Unlock()
|
|
|
|
if theSecret.bytes != nil {
|
|
return theSecret.bytes, nil
|
|
}
|
|
|
|
base64Bytes, err := ioutil.ReadFile(theSecret.path)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("secret.setBytes: read %q: %v", theSecret.path, err)
|
|
}
|
|
|
|
secretBytes := make([]byte, base64.StdEncoding.DecodedLen(len(base64Bytes)))
|
|
n, err := base64.StdEncoding.Decode(secretBytes, base64Bytes)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("secret.setBytes: decode secret: %v", err)
|
|
}
|
|
|
|
if n != numSecretBytes {
|
|
return nil, fmt.Errorf("secret.setBytes: expected %d secretBytes in %s, found %d", numSecretBytes, theSecret.path, n)
|
|
}
|
|
|
|
theSecret.bytes = secretBytes
|
|
return copyBytes(theSecret.bytes), nil
|
|
}
|