From 05a68df896c01498ba1c77b4622ac5573dbc688a Mon Sep 17 00:00:00 2001 From: ston1th Date: Tue, 21 Mar 2023 01:00:13 +0100 Subject: [PATCH] cleanup --- Makefile | 2 +- README.md | 40 +++++++++++++++++++++ cmd/keyctl/main.go | 39 ++++++++++---------- pkg/api/server.go | 54 ++++++++++++++-------------- pkg/api/types/approver.go | 20 +++++------ pkg/api/types/context.go | 7 ++-- pkg/api/types/{types.go => route.go} | 0 pkg/api/v1/schema/schema.go | 38 ++++++++------------ pkg/api/v1/server/handler.go | 9 ----- pkg/api/v1/server/routes.go | 48 ++++++++++++------------- pkg/cli/cli.go | 13 ++++--- pkg/core/encoding.go | 14 ++++---- pkg/core/key.go | 1 - pkg/core/type.go | 10 +++--- pkg/key/key.go | 2 -- 15 files changed, 160 insertions(+), 137 deletions(-) rename pkg/api/types/{types.go => route.go} (100%) diff --git a/Makefile b/Makefile index 4c8bd9f..42a3f0d 100644 --- a/Makefile +++ b/Makefile @@ -37,7 +37,7 @@ govet: $(CC) vet ./... misspell: - $(GOPATH)/bin/misspell cmd/* pkg/* Makefile README.md sri.sh + $(GOPATH)/bin/misspell cmd/* pkg/* Makefile README.md staticcheck: $(GOPATH)/bin/staticcheck ./... diff --git a/README.md b/README.md index 03cfade..da507f7 100644 --- a/README.md +++ b/README.md @@ -1 +1,41 @@ # KeyCtl - deliver secret keys with manual confirmation + +## Server + +``` +./keyctl server +``` + +## Client + +### Admin Access + +Admin access is only allowed via the Unix Domain Socket connection. + +``` +export KEYCTL_ENDPOINT=unix:///var/keyctl/keyctl.sock +``` + +``` +./keyctl ls +``` + +### Get Secret + +The API to retrieve a secret can be called from any source IP. + +``` +export KEYCTL_ENDPOINT=http://127.0.0.1:7070 +``` + +``` +./keyctl get -id aabbccddeeff... +``` + +## Usage + +``` +./keyctl --help + +./keyctl server --help +``` diff --git a/cmd/keyctl/main.go b/cmd/keyctl/main.go index 7178a4b..f449823 100644 --- a/cmd/keyctl/main.go +++ b/cmd/keyctl/main.go @@ -34,13 +34,13 @@ func usage() { fmt.Printf(`keyctl %s: supported subcommands: keyctl server - starts a keyctl server - keyctl new - generates a new key keyctl get - retrieve a key from the server - keyctl ls - list configured keys - keyctl req - list current key requests - keyctl approve - approve a key request - keyctl reject - reject a key request - keyctl del - delete a key + keyctl new - generates a new key (admin) + keyctl ls - list configured keys (admin) + keyctl req - list current key requests (admin) + keyctl approve - approve a key request (admin) + keyctl reject - reject a key request (admin) + keyctl del - delete a key (admin) keyctl help - show this usage `, version) os.Exit(1) @@ -55,27 +55,30 @@ func main() { case "new", "get", "ls", "req", "approve", "reject", "del": cli.Run(arg) // does not return case "server": - fs := flag.NewFlagSet("", flag.ExitOnError) - klog.InitFlags(fs) - fs.StringVar(&listen, "listen", ":7070", "listen ip:port") - fs.StringVar(&socket, "socket", "keyctl.sock", "listen unix domain socket") - fs.StringVar(&path, "path", "/var/keyctl", "db storage dir") - fs.Parse(os.Args[2:]) + server() // does not return default: - usage() + usage() // does not return } +} + +func server() { + fs := flag.NewFlagSet("", flag.ExitOnError) + klog.InitFlags(fs) + fs.StringVar(&listen, "listen", ":7070", "listen ip:port") + fs.StringVar(&socket, "socket", "keyctl.sock", "listen unix domain socket") + fs.StringVar(&path, "path", "/var/keyctl", "db storage dir") + fs.Parse(os.Args[2:]) log = klogr.New().WithName("main") log.Info("starting keyctl", "version", version) - srv, err := api.NewServer(klogr.New().WithName("srv"), listen, filepath.Join(path, socket)) - if err != nil { - klog.Fatalf("init failed: %s", err) - } dbstore, err := db.New(klogr.New().WithName("db"), path) if err != nil { klog.Fatalf("init failed: %s", err) } - srv.Start(dbstore) + srv, err := api.NewServer(klogr.New().WithName("srv"), listen, filepath.Join(path, socket), dbstore) + if err != nil { + klog.Fatalf("init failed: %s", err) + } sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) diff --git a/pkg/api/server.go b/pkg/api/server.go index c34aaa5..8effb60 100644 --- a/pkg/api/server.go +++ b/pkg/api/server.go @@ -34,21 +34,30 @@ type notFoundHandler struct { } func (nf *notFoundHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - types.NewContext(w, r, nf.s.Data, nf.s.log).Err(types.ErrInvalidAPIRoute) + types.NewContext(w, r, nil, nf.s.log).Err(types.ErrInvalidAPIRoute) } // NewHTTPServer returns a new HTTPServer -func NewServer(log logr.Logger, listen, socket string) (*Server, error) { +func NewServer(log logr.Logger, listen, socket string, db *db.DB) (*Server, error) { + m := mux.NewRouter() s := &Server{ - mux: mux.NewRouter(), - log: log, - Data: &types.ContextData{Approver: types.NewApprover()}, + mux: m, + log: log, + Data: &types.ContextData{ + Approver: types.NewApprover(), + DB: db, + }, + srv: &http.Server{ + Handler: m, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + }, } - l, err := net.Listen("tcp", listen) + list, err := net.Listen("tcp", listen) if err != nil { return nil, err } - s.listen = l + s.listen = list sock, err := unixListener(socket) if err != nil { @@ -56,29 +65,11 @@ func NewServer(log logr.Logger, listen, socket string) (*Server, error) { } s.socket = sock - s.mux.NotFoundHandler = ¬FoundHandler{s} + m.NotFoundHandler = ¬FoundHandler{s} for _, v := range serverv1.Routes { - s.mux.HandleFunc(v.Path, s.contextWrapper(v.Handler)).Methods(v.Methods...) + m.HandleFunc(v.Path, s.contextWrapper(v.Handler)).Methods(v.Methods...) } - return s, nil -} -func unixListener(socket string) (sock net.Listener, err error) { - sock, err = net.Listen("unix", socket) - if err != nil { - return - } - err = os.Chmod(socket, 0660) - return -} - -func (s *Server) Start(db *db.DB) { - s.srv = &http.Server{ - Handler: s.mux, - ReadTimeout: 10 * time.Second, - WriteTimeout: 10 * time.Second, - } - s.Data.DB = db go func() { err := s.srv.Serve(s.listen) if err != nil && err != http.ErrServerClosed { @@ -91,6 +82,15 @@ func (s *Server) Start(db *db.DB) { s.log.Error(err, "") } }() + return s, nil +} + +func unixListener(socket string) (sock net.Listener, err error) { + sock, err = net.Listen("unix", socket) + if err != nil { + return + } + err = os.Chmod(socket, 0660) return } diff --git a/pkg/api/types/approver.go b/pkg/api/types/approver.go index 84ca101..76f98c2 100644 --- a/pkg/api/types/approver.go +++ b/pkg/api/types/approver.go @@ -4,7 +4,6 @@ package types import ( "sync" - "time" "git.giftfish.de/ston1th/keyctl/pkg/key" "golang.org/x/exp/maps" @@ -17,7 +16,8 @@ type ApprovalMap map[string]Approval type Approver struct { // protects m mu sync.RWMutex - m ApprovalMap + + m ApprovalMap } func NewApprover() *Approver { @@ -39,10 +39,9 @@ func (a *Approver) New(id, name, ip string) (aid string, err error) { return } a.m[aid] = Approval{ - ID: id, - Name: name, - IP: ip, - Created: time.Now().Unix(), + ID: id, + Name: name, + IP: ip, } return } @@ -77,11 +76,10 @@ func (a *Approver) Update(aid string, s Status) { } type Approval struct { - ID string - Name string - IP string - Created int64 - Status Status + ID string + Name string + IP string + Status Status } type Status int diff --git a/pkg/api/types/context.go b/pkg/api/types/context.go index d263227..62a2aff 100644 --- a/pkg/api/types/context.go +++ b/pkg/api/types/context.go @@ -15,8 +15,8 @@ import ( ) type ContextData struct { - DB *db.DB Approver *Approver + DB *db.DB } func NewContext(w http.ResponseWriter, r *http.Request, data *ContextData, log logr.Logger) *Context { @@ -84,9 +84,8 @@ func (c *Context) ClientIP() (host string) { } // Var returns the given url variable -func (c *Context) Var(name string) (ret string) { - ret, _ = mux.Vars(c.Request)[name] - return +func (c *Context) Var(name string) string { + return mux.Vars(c.Request)[name] } func (c *Context) log() { diff --git a/pkg/api/types/types.go b/pkg/api/types/route.go similarity index 100% rename from pkg/api/types/types.go rename to pkg/api/types/route.go diff --git a/pkg/api/v1/schema/schema.go b/pkg/api/v1/schema/schema.go index 732cb4e..15360a8 100644 --- a/pkg/api/v1/schema/schema.go +++ b/pkg/api/v1/schema/schema.go @@ -6,7 +6,6 @@ import ( "encoding/hex" "errors" "fmt" - "time" "git.giftfish.de/ston1th/keyctl/pkg/api/types" "git.giftfish.de/ston1th/keyctl/pkg/core" @@ -59,7 +58,6 @@ func ApproveURL(id, aid string) string { type Key struct { ID string `json:"id"` Name string `json:"name"` - Created int64 `json:"created"` Size int `json:"size"` Encoding core.Encoding `json:"encoding"` Type core.Type `json:"type"` @@ -92,15 +90,14 @@ func (Keys) Less(a, b Key) bool { return a.Name < b.Name } func (k Keys) Sort() { slices.SortStableFunc(k, k.Less) } func (k Keys) Len() int { return len(k) } func (k Keys) Fields() string { - return "ID\t Name\t Created\t Size\t Encoding\t Type" + return "ID\t Name\t Size\t Encoding\t Type" } func (k Keys) Values() []string { vals := make([]string, len(k)) for i, v := range k { - vals[i] = fmt.Sprintf("%s\t %s\t %s\t %d\t %s\t %s", + vals[i] = fmt.Sprintf("%s\t %s\t %d\t %s\t %s", v.ID, v.Name, - timeFmt(v.Created), v.Size, v.Encoding, v.Type, @@ -118,12 +115,11 @@ func NewKeys(keys core.Keys) (k Keys) { } type Approval struct { - ID string `json:"id"` - AID string `json:"aid"` - Name string `json:"name"` - IP string `json:"ip"` - Created int64 `json:"created"` - Status types.Status `json:"status"` + ID string `json:"id"` + AID string `json:"aid"` + Name string `json:"name"` + IP string `json:"ip"` + Status types.Status `json:"status"` } type Approvals []Approval @@ -132,17 +128,16 @@ func (Approvals) Less(a, b Approval) bool { return a.Name < b.Name } func (a Approvals) Sort() { slices.SortStableFunc(a, a.Less) } func (a Approvals) Len() int { return len(a) } func (a Approvals) Fields() string { - return "ID\t Approval ID\t Name\t Status\t Created\t IP" + return "ID\t Approval ID\t Name\t Status\t IP" } func (a Approvals) Values() []string { vals := make([]string, len(a)) for i, v := range a { - vals[i] = fmt.Sprintf("%s\t %s\t %s\t %s\t %s\t %s", + vals[i] = fmt.Sprintf("%s\t %s\t %s\t %s\t %s", v.ID, v.AID, v.Name, v.Status, - timeFmt(v.Created), v.IP, ) } @@ -152,18 +147,13 @@ func (a Approvals) Values() []string { func NewApprovals(m types.ApprovalMap) (a Approvals) { for k, v := range m { a = append(a, Approval{ - ID: v.ID, - AID: k, - Name: v.Name, - IP: v.IP, - Created: v.Created, - Status: v.Status, + ID: v.ID, + AID: k, + Name: v.Name, + IP: v.IP, + Status: v.Status, }) } a.Sort() return } - -func timeFmt(t int64) string { - return time.Unix(t, 0).Format(time.RFC3339) -} diff --git a/pkg/api/v1/server/handler.go b/pkg/api/v1/server/handler.go index cf758ef..95f84e5 100644 --- a/pkg/api/v1/server/handler.go +++ b/pkg/api/v1/server/handler.go @@ -18,15 +18,6 @@ func local(h types.CtxHandler) types.CtxHandler { h(ctx) return } - //addr, err := netip.ParseAddr(ip) - //if err != nil { - // ctx.Err(types.ErrForbidden) - // return - //} - //if addr.IsLoopback() { - // h(ctx) - // return - //} ctx.Err(types.ErrForbidden) } } diff --git a/pkg/api/v1/server/routes.go b/pkg/api/v1/server/routes.go index 18b05d2..d0c785f 100644 --- a/pkg/api/v1/server/routes.go +++ b/pkg/api/v1/server/routes.go @@ -17,43 +17,43 @@ const ( var Routes = []types.Route{ { - healthz, - healthzHandler, - []string{"GET"}, + Path: healthz, + Handler: healthzHandler, + Methods: []string{"GET"}, }, { - schema.KeyPath, - local(keyListHandler), - []string{"GET"}, + Path: schema.KeyPath, + Handler: local(keyListHandler), + Methods: []string{"GET"}, }, { - schema.KeyPath, - local(newHandler), - []string{"POST"}, + Path: schema.KeyPath, + Handler: local(newHandler), + Methods: []string{"POST"}, }, { - keyID, - keyHandler, - []string{"GET"}, + Path: keyID, + Handler: keyHandler, + Methods: []string{"GET"}, }, { - keyReqID, - keyHandler, - []string{"GET"}, + Path: keyReqID, + Handler: keyHandler, + Methods: []string{"GET"}, }, { - keyID, - local(keyHandler), - []string{"DELETE"}, + Path: keyID, + Handler: local(keyHandler), + Methods: []string{"DELETE"}, }, { - schema.ReqPath, - local(reqListHandler), - []string{"GET"}, + Path: schema.ReqPath, + Handler: local(reqListHandler), + Methods: []string{"GET"}, }, { - reqID, - local(reqHandler), - []string{"PUT", "DELETE"}, + Path: reqID, + Handler: local(reqHandler), + Methods: []string{"PUT", "DELETE"}, }, } diff --git a/pkg/cli/cli.go b/pkg/cli/cli.go index dbf109d..10dbf4a 100644 --- a/pkg/cli/cli.go +++ b/pkg/cli/cli.go @@ -25,7 +25,6 @@ func exit(err error) { } func Run(arg string) { - var err error ctx := context.Background() switch arg { case "new": @@ -58,6 +57,9 @@ func Run(arg string) { } c := client() err = c.DeleteKey(ctx, id) + if err != nil { + exit(err) + } case "approve": id, err := idFlags("approval id") if err != nil { @@ -65,6 +67,9 @@ func Run(arg string) { } c := client() err = c.AcceptApproval(ctx, id) + if err != nil { + exit(err) + } case "reject": id, err := idFlags("approval id") if err != nil { @@ -72,6 +77,9 @@ func Run(arg string) { } c := client() err = c.RejectApproval(ctx, id) + if err != nil { + exit(err) + } case "get": id, share, json, err := getFlags() if err != nil { @@ -114,9 +122,6 @@ func Run(arg string) { } print(approvals) } - if err != nil { - exit(err) - } os.Exit(0) } diff --git a/pkg/core/encoding.go b/pkg/core/encoding.go index 4e2df70..d0ae123 100644 --- a/pkg/core/encoding.go +++ b/pkg/core/encoding.go @@ -12,6 +12,13 @@ var b32raw = base32.StdEncoding.WithPadding(base32.NoPadding) type Encoding int +const ( + Hex Encoding = iota + Base32 + Base64 + Base64URL +) + func (e Encoding) String() string { switch e { case Base32: @@ -59,10 +66,3 @@ func EncodingFromString(e string) Encoding { } return Hex } - -const ( - Hex Encoding = iota - Base32 - Base64 - Base64URL -) diff --git a/pkg/core/key.go b/pkg/core/key.go index 2da47e7..e1c352c 100644 --- a/pkg/core/key.go +++ b/pkg/core/key.go @@ -17,7 +17,6 @@ var NameRe = regexp.MustCompile(nameReReverse) type Key struct { ID string Name string - Created int64 Size int Encoding Encoding Type Type diff --git a/pkg/core/type.go b/pkg/core/type.go index ee8eb00..441f397 100644 --- a/pkg/core/type.go +++ b/pkg/core/type.go @@ -10,6 +10,11 @@ import ( type Type int +const ( + Plain Type = iota + Shamir +) + func (t Type) String() string { switch t { case Shamir: @@ -48,8 +53,3 @@ func TypeFromString(t string) Type { } return Plain } - -const ( - Plain Type = iota - Shamir -) diff --git a/pkg/key/key.go b/pkg/key/key.go index 07ac991..77b4b3d 100644 --- a/pkg/key/key.go +++ b/pkg/key/key.go @@ -6,7 +6,6 @@ import ( "crypto/rand" "encoding/hex" "io" - "time" "git.giftfish.de/ston1th/keyctl/pkg/core" ) @@ -46,7 +45,6 @@ func Generate(name string, size int, enc core.Encoding, t core.Type) (kstore, k Size: size, Encoding: enc, Type: t, - Created: time.Now().Unix(), } k = kstore if t == core.Shamir {