named passphrases (+deletion by name)

This commit is contained in:
Mikaël Cluseau
2023-09-10 16:47:54 +02:00
parent 34afe03818
commit ee5629643c
11 changed files with 200 additions and 113 deletions

View File

@ -26,7 +26,7 @@ func authorizeToken(r *http.Request, token string) bool {
}
func forbidden(w http.ResponseWriter, r *http.Request) {
log.Printf("denied access to %s from %s", r.RequestURI, r.RemoteAddr)
log.Printf("denied access to %s from %s", r.URL.Path, r.RemoteAddr)
http.Error(w, "Forbidden", http.StatusForbidden)
}

View File

@ -52,12 +52,12 @@ func main() {
}
if autoUnlock != "" {
log.Printf("auto-unlocking the store")
err := unlockSecretStore([]byte(autoUnlock))
err := unlockSecretStore("test", []byte(autoUnlock))
if err.Any() {
log.Fatal(err)
}
log.Print("store auto-unlocked, admin token is ", adminToken)
log.Print("store auto-unlocked, token is ", adminToken)
}
os.Setenv("DLS_AUTO_UNLOCK", "")

View File

@ -63,7 +63,7 @@ var (
ErrInvalidPassphrase = httperr.NewStd(2, http.StatusBadRequest, "invalid passphrase")
)
func unlockSecretStore(passphrase []byte) (err httperr.Error) {
func unlockSecretStore(name string, passphrase []byte) (err httperr.Error) {
unlockMutex.Lock()
defer unlockMutex.Unlock()
@ -72,7 +72,7 @@ func unlockSecretStore(passphrase []byte) (err httperr.Error) {
}
if secStore.IsNew() {
err := secStore.Init(passphrase)
err := secStore.Init(name, passphrase)
if err != nil {
return httperr.Internal(err)
}

View File

@ -22,6 +22,7 @@ type State struct {
Store struct {
DownloadToken string
KeyNames []string
}
Clusters []ClusterState
@ -59,14 +60,21 @@ func init() {
func updateState() {
log.Print("updating state")
// store key names
keyNames := make([]string, 0, len(secStore.Keys))
for _, key := range secStore.Keys {
keyNames = append(keyNames, key.Name)
}
// config
cfg, err := readConfig()
if err != nil {
wState.Change(func(v *State) { v.HasConfig = false; v.Config = nil })
wState.Change(func(v *State) { v.HasConfig = false; v.Config = nil; v.Store.KeyNames = keyNames })
return
}
if secStore.IsNew() || !secStore.Unlocked() {
wState.Change(func(v *State) { v.HasConfig = false; v.Config = nil })
wState.Change(func(v *State) { v.HasConfig = false; v.Config = nil; v.Store.KeyNames = keyNames })
return
}
@ -122,7 +130,7 @@ func updateState() {
// done
wState.Change(func(v *State) {
v.HasConfig = true
//v.Config = cfg
v.Store.KeyNames = keyNames
v.Clusters = clusters
v.Hosts = hosts
})

View File

@ -14,15 +14,32 @@ import (
"m.cluseau.fr/go/httperr"
)
type NamedPassphrase struct {
Name string
Passphrase []byte
}
func wsUnlockStore(req *restful.Request, resp *restful.Response) {
var passphrase string
err := req.ReadEntity(&passphrase)
np := NamedPassphrase{}
err := req.ReadEntity(&np)
if err != nil {
resp.WriteError(http.StatusBadRequest, err)
return
}
if err := unlockSecretStore([]byte(passphrase)); err.Any() {
if secStore.IsNew() {
if len(np.Name) == 0 {
wsBadRequest(resp, "no name given")
return
}
}
if len(np.Passphrase) == 0 {
wsBadRequest(resp, "no passphrase given")
return
}
if err := unlockSecretStore(np.Name, np.Passphrase); err.Any() {
err.WriteJSON(resp.ResponseWriter)
return
}

View File

@ -1,24 +1,77 @@
package main
import (
"strconv"
"strings"
restful "github.com/emicklei/go-restful"
"novit.tech/direktil/local-server/secretstore"
)
func wsStoreAddKey(req *restful.Request, resp *restful.Response) {
var passphrase string
np := NamedPassphrase{}
err := req.ReadEntity(&passphrase)
err := req.ReadEntity(&np)
if err != nil {
wsBadRequest(resp, err.Error())
return
}
if len(passphrase) == 0 {
np.Name = strings.TrimSpace(np.Name)
if len(np.Name) == 0 {
wsBadRequest(resp, "no name given")
return
}
if len(np.Passphrase) == 0 {
wsBadRequest(resp, "no passphrase given")
return
}
secStore.AddKey([]byte(passphrase))
secStore.AddKey(np.Name, np.Passphrase)
defer updateState()
for _, k := range secStore.Keys {
if k.Name == np.Name {
wsBadRequest(resp, "there's already a passphrase named "+strconv.Quote(np.Name))
return
}
}
err = secStore.SaveTo(secKeysStorePath())
if err != nil {
wsError(resp, err)
return
}
}
func wsStoreDelKey(req *restful.Request, resp *restful.Response) {
name := ""
err := req.ReadEntity(&name)
if err != nil {
wsBadRequest(resp, err.Error())
return
}
newKeys := make([]secretstore.KeyEntry, 0, len(secStore.Keys))
for _, k := range secStore.Keys {
if k.Name == name {
continue
}
newKeys = append(newKeys, k)
}
if len(newKeys) == 0 {
wsBadRequest(resp, "can't remove the last key from the store")
return
}
secStore.Keys = newKeys
defer updateState()
err = secStore.SaveTo(secKeysStorePath())
if err != nil {
wsError(resp, err)

View File

@ -27,7 +27,7 @@ func registerWS(rest *restful.Container) {
Produces(mime.JSON).
Consumes(mime.JSON).
Route(ws.POST("/unlock-store").To(wsUnlockStore).
Reads("").
Reads(NamedPassphrase{}).
Writes("").
Doc("Try to unlock the store")).
Route(ws.GET("/store.tar").To(wsStoreDownload).
@ -54,8 +54,11 @@ func registerWS(rest *restful.Container) {
// - store management
ws.Route(ws.POST("/store/add-key").To(wsStoreAddKey).
Consumes(mime.JSON).Reads("").
Consumes(mime.JSON).Reads(NamedPassphrase{}).
Doc("Add an unlock key to the store"))
ws.Route(ws.POST("/store/delete-key").To(wsStoreDelKey).
Consumes(mime.JSON).Reads("").
Doc("Remove an unlock key to the store (by its name)"))
// - downloads
ws.Route(ws.POST("/authorize-download").To(wsAuthorizeDownload).