Files

49 lines
1.5 KiB
Go

// Package setuptoken provides a one-time setup token used to protect the
// public initialization endpoints (admin account creation and backup restore)
// on an uninitialized Portainer instance.
package setuptoken
import (
"crypto/rand"
"crypto/subtle"
"encoding/hex"
"errors"
"net/http"
httperror "github.com/portainer/portainer/pkg/libhttp/error"
)
// HeaderName is the HTTP header that carries the setup token.
const HeaderName = "X-Setup-Token"
// tokenByteLength is the number of random bytes before hex-encoding (256 bits).
const tokenByteLength = 32
var errInvalidSetupToken = errors.New("invalid or missing setup token")
// Generate returns a cryptographically random, hex-encoded setup token.
func Generate() (string, error) {
b := make([]byte, tokenByteLength)
if _, err := rand.Read(b); err != nil {
return "", err
}
return hex.EncodeToString(b), nil
}
// Validate checks that the request carries the expected setup token in the
// X-Setup-Token header. When expected is empty the gate is disabled and the
// request is always allowed. Comparison is constant-time.
func Validate(r *http.Request, expected string) *httperror.HandlerError {
if expected == "" {
return nil
}
provided := r.Header.Get(HeaderName)
if subtle.ConstantTimeCompare([]byte(provided), []byte(expected)) != 1 {
return httperror.Forbidden("Invalid or missing setup token. Provide the X-Setup-Token header with the token printed in the server logs at startup.", errInvalidSetupToken)
}
return nil
}