123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452 |
- package keyring
- import (
- "encoding/base64"
- "encoding/json"
- "errors"
- "fmt"
- "log"
- "net/url"
- "git.aetherial.dev/aeth/yosai/pkg/config"
- daemonproto "git.aetherial.dev/aeth/yosai/pkg/daemon-proto"
- "git.aetherial.dev/aeth/yosai/pkg/keytags"
- )
- type KeyType string
- func AssertKeytype(v string) KeyType {
- switch v {
- case "ssh":
- return SSH_KEY
- case "api_key":
- return API_KEY
- case "bearer_auth":
- return BEARER_AUTH
- case "basic_auth":
- return BASIC_AUTH
- case "login_password":
- return LOGIN_CRED
- case "wireguard":
- return WIREGUARD
- default:
- log.Fatal("Pivotal system component has failed, 'Keyring' was passed an invalid keytype: ", v)
- return SSH_KEY
- }
- }
- const (
- SSH_KEY KeyType = "ssh"
- API_KEY KeyType = "api_key"
- BEARER_AUTH KeyType = "bearer_auth"
- CLIENT_CREDENTIALS KeyType = "client_credentials"
- BASIC_AUTH KeyType = "basic_auth"
- LOGIN_CRED KeyType = "login_password"
- WIREGUARD KeyType = "wireguard"
- )
- type WireguardKeypair struct {
- PrivateKey string
- PublicKey string
- Username string
- }
- func (w WireguardKeypair) GetPublic() string {
- return w.PublicKey
- }
- func (w WireguardKeypair) GetSecret() string {
- return w.PrivateKey
- }
- func (w WireguardKeypair) Prepare() string {
- return ""
- }
- func (w WireguardKeypair) GetType() KeyType {
- return WIREGUARD
- }
- func (w WireguardKeypair) Owner() config.Username {
- return config.ValidateUsername(w.Username)
- }
- type VpsRootUser struct {
- Password string
- Pubkey string
- }
- type BearerAuth struct {
- Username config.Username
- Secret string // Likely would be the API key for the API
- }
- /*
- Format a bearer auth payload according to RFC 6750
- */
- func (b BearerAuth) Prepare() string {
- return fmt.Sprintf("Bearer %s", b.Secret)
- }
- /*
- Return the 'public' identifier, which for bearer auth the closest is going to be the auth type
- */
- func (b BearerAuth) GetPublic() string {
- return "Bearer"
- }
- /*
- Return the private data for this auth type
- */
- func (b BearerAuth) GetSecret() string {
- return b.Secret
- }
- func (b BearerAuth) Owner() config.Username {
- return b.Username
- }
- func (b BearerAuth) GetType() KeyType {
- return BEARER_AUTH
- }
- type BasicAuth struct {
- Username string // Username for basic auth
- Password string // subsequent password for basic auth
- }
- /*
- Encode a basic auth payload according to RFC 7617
- */
- func (b BasicAuth) Prepare() string {
- encodedcreds := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", b.Username, b.Password)))
- return fmt.Sprintf("Basic %s", encodedcreds)
- }
- /*
- Return the 'public' identifier, which for basic auth it will return the username
- */
- func (b BasicAuth) GetPublic() string {
- return b.Password
- }
- /*
- Return the private data for this auth type
- */
- func (b BasicAuth) GetSecret() string {
- return b.Password
- }
- func (b BasicAuth) GetType() KeyType {
- return BASIC_AUTH
- }
- type ClientCredentials struct {
- ClientId string // Client ID for the API
- ClientSecret string // Client Secret for the API
- }
- /*
- Encode a client credentials type payload and return the payload string according to RFC 6749
- */
- func (c ClientCredentials) Prepare() string {
- credQuery := url.Values{}
- credQuery.Add("grant_type", "client_credentials")
- credQuery.Add("client_id", c.ClientId)
- credQuery.Add("client_secret", c.ClientSecret)
- return credQuery.Encode()
- }
- // return the client ID
- func (c ClientCredentials) GetPublic() string {
- return c.ClientId
- }
- // Return the Client Secret
- func (c ClientCredentials) GetSecret() string {
- return c.ClientSecret
- }
- func (c ClientCredentials) GetType() KeyType {
- return CLIENT_CREDENTIALS
- }
- type SshKey struct {
- Username config.Username
- PublicKey string
- PrivateKey string
- }
- func (s SshKey) GetPublic() string {
- return s.PublicKey
- }
- func (s SshKey) GetSecret() string {
- return s.PrivateKey
- }
- func (s SshKey) Prepare() string {
- return s.PrivateKey
- }
- func (s SshKey) Owner() config.Username {
- return s.Username
- }
- func (s SshKey) GetType() KeyType {
- return SSH_KEY
- }
- type ApiKeyRing struct {
- Rungs []DaemonKeyRing
- Keys map[string]Key // hashmap with the keys in the keyring. Protected with getters and setters
- Config *config.Configuration
- KeyTagger keytags.Keytagger
- }
- /*
- Retrieve a keyring from the daemon keyring
- :param name: the name of the keyring. e.g., 'LINODE', or 'OPENWRT'
- */
- func (a *ApiKeyRing) GetKey(name string) (Key, error) {
- var key Key
- key, ok := a.Keys[name]
- if ok {
- return key, nil
- }
- if len(a.Rungs) > 0 {
- for i := range a.Rungs {
- key, err := a.Rungs[i].GetKey(name)
- if err != nil {
- if errors.Is(err, KeyNotFound) {
- a.Log("Key: ", name, "not found.", err.Error())
- continue
- }
- if errors.Is(err, KeyRingError) {
- a.Log("Error with the actual keyring while getting key:", name, err.Error(), "from: ", a.Rungs[i].Source())
- return key, err
- }
- a.Log("Unhandled exception getting key: ", name, err.Error())
- log.Fatal("Ungraceful shutdown. unhandled error within keyring: ", err)
- }
- if key.GetPublic() == "" || key.GetSecret() == "" {
- a.Log("null key: ", name, "returned.")
- continue
- }
- a.Log("Key:", name, "successfully added to the daemon keyring.")
- a.AddKey(name, key)
- return key, nil
- }
- }
- return key, KeyNotFound
- }
- /*
- Add a key to the daemon keyring
- :param name: name to give the key, used when indexing
- :param key: the Key struct to add to the keyring
- */
- func (a *ApiKeyRing) AddKey(name string, key Key) error {
- _, ok := a.Keys[name]
- if ok {
- return KeyExists
- }
- a.Keys[name] = key
- return nil
- }
- /*
- Remove a key from the daemon keyring
- :param name: the name that the key was given when adding to the keyring
- */
- func (a *ApiKeyRing) RemoveKey(name string) error {
- _, err := a.GetKey(name)
- if err != nil {
- return err
- }
- delete(a.Keys, name)
- return nil
- }
- // Return the resource name for logging purposes
- func (a *ApiKeyRing) Source() string {
- return "Base API Keyring"
- }
- /*
- Create a new daemon keyring. Passing additional implementers of the DaemonKeyRing will
- allow the GetKey() method on the toplevel keyring to search all subsequent keyrings for a match.
- */
- func NewKeyRing(cfg *config.Configuration, keytagger keytags.Keytagger) *ApiKeyRing {
- return &ApiKeyRing{
- Keys: map[string]Key{},
- Rungs: []DaemonKeyRing{},
- Config: cfg,
- KeyTagger: keytagger,
- }
- }
- func (a *ApiKeyRing) Log(msg ...string) {
- keyMsg := []string{
- "ApiKeyRing:",
- }
- keyMsg = append(keyMsg, msg...)
- a.Config.Log(keyMsg...)
- }
- type KeyringRequest struct {
- Public string `json:"public"`
- Secret string `json:"secret"`
- Type string `json:"type"`
- Name string `json:"name"`
- }
- /*
- Wrapping the show keyring function in a route friendly interface
- :param msg: a message to be decoded from the daemon socket
- */
- func (a *ApiKeyRing) ShowKeyringHandler(msg daemonproto.SockMessage) daemonproto.SockMessage {
- var req KeyringRequest
- err := json.Unmarshal(msg.Body, &req)
- if err != nil {
- return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_FAILED, []byte(err.Error()))
- }
- switch req.Name {
- case "all":
- b, err := json.Marshal(a.Keys)
- if err != nil {
- return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_FAILED, []byte(err.Error()))
- }
- return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_OK, b)
- default:
- key, err := a.GetKey(req.Name)
- if err != nil {
- return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_FAILED, []byte(err.Error()))
- }
- b, _ := json.Marshal(key)
- return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_OK, b)
- }
- }
- /*
- Wrapping the reload keyring function in a route friendly interface
- :param msg: a message to be decoded from the daemon socket
- */
- func (a *ApiKeyRing) ReloadKeyringHandler(msg daemonproto.SockMessage) daemonproto.SockMessage {
- protectedKeys := a.KeyTagger.ProtectedKeys()
- keynames := []string{}
- for keyname := range a.Keys {
- _, ok := protectedKeys[keyname]
- if ok {
- continue
- }
- keynames = append(keynames, keyname)
- }
- for i := range keynames {
- delete(a.Keys, keynames[i])
- }
- a.Log("Keyring depleted, keys in keyring: ", fmt.Sprint(len(a.Keys)))
- a.Log("Keys to retrieve: ", fmt.Sprint(keynames))
- for i := range keynames {
- _, err := a.GetKey(keynames[i])
- if err != nil {
- a.Log("Keyring reload error, Error getting key: ", keynames[i], err.Error())
- }
- }
- return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_OK, []byte("Keyring successfully reloaded."))
- }
- /*
- Wrapping the bootstrap keyring function in a route friendly interface
- :param msg: a message to be decoded from the daemon socket
- */
- func (a *ApiKeyRing) BootstrapKeyringHandler(msg daemonproto.SockMessage) daemonproto.SockMessage {
- err := a.Bootstrap()
- if err != nil {
- return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_FAILED, []byte(err.Error()))
- }
- return *daemonproto.NewSockMessage(daemonproto.MsgResponse, daemonproto.REQUEST_OK, []byte("Keyring successfully bootstrapped."))
- }
- /*
- Bootstrap the keyring
- */
- func (a *ApiKeyRing) Bootstrap() error {
- allkeytags := a.KeyTagger.AllKeys()
- for i := range allkeytags {
- kn := allkeytags[i]
- _, err := a.GetKey(kn)
- if err != nil {
- return &KeyringBootstrapError{Msg: "Key with keytag: " + kn + " was not found on any of the daemon Keyring rungs."}
- }
- }
- return nil
- }
- type KeyringRouter struct {
- routes map[daemonproto.Method]func(daemonproto.SockMessage) daemonproto.SockMessage
- }
- func (k *KeyringRouter) Register(method daemonproto.Method, callable func(daemonproto.SockMessage) daemonproto.SockMessage) {
- k.routes[method] = callable
- }
- func (k *KeyringRouter) Routes() map[daemonproto.Method]func(daemonproto.SockMessage) daemonproto.SockMessage {
- return k.routes
- }
- func NewKeyRingRouter() *KeyringRouter {
- return &KeyringRouter{routes: map[daemonproto.Method]func(daemonproto.SockMessage) daemonproto.SockMessage{}}
- }
- /*
- ######################
- ##### INTERFACES #####
- ######################
- */
- type DaemonKeyRing interface {
- GetKey(string) (Key, error) // retrieve a key by its tag on the keyring
- AddKey(string, Key) error // Add a key to your keyring
- RemoveKey(string) error // Remove a key from the keyring
- Source() string // Return the name of the resource being called, i.e. 'Semaphone Keystore', or 'Hashicorp Vault'
- }
- type Key interface {
- // This function is supposed to return the payload for the given key type according to its RFC
- // i.e. if the 'type' is Bearer, then it returns a string with 'Bearer tokencode123456xyz'
- Prepare() string
- GetPublic() string // Get the public identifier of the key, i.e. the username, or client id, etc.
- GetSecret() string // Get the private/secret data, i.e. the password, API key, client secret, etc
- Owner() config.Username // returns the owning username
- GetType() KeyType // Returns the type of key. I.e. API_KEY, SSH_KEY, BASIC_AUTH, etc
- }
- /*
- ######################
- ##### ERRORS #####
- ######################
- */
- var (
- KeyNotFound = errors.New("Key not found.")
- KeyExists = errors.New("Key exists.")
- KeyRingError = errors.New("Unexpected error from child keyrung")
- )
- type KeyringBootstrapError struct {
- Msg string
- }
- func (k *KeyringBootstrapError) Error() string {
- return fmt.Sprintf("There was a fatal error bootstrapping the keyring: %s", k.Msg)
- }
|