diff --git a/cmd/klefkictl/klefkictl_new.go b/cmd/klefkictl/klefkictl_new.go index 518b5a8..323c3ee 100644 --- a/cmd/klefkictl/klefkictl_new.go +++ b/cmd/klefkictl/klefkictl_new.go @@ -29,10 +29,12 @@ import ( func newNewCommand() *cobra.Command { // TODO(jaredallard): Support setting the name of the machine. return &cobra.Command{ - Use: "new", + Use: "new ", Short: "Create a new machine", - Args: cobra.NoArgs, - RunE: func(cmd *cobra.Command, _ []string) error { + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + name := args[0] // Checked by [cobra.ExactArgs] above. + db, err := db.New(cmd.Context()) if err != nil { return fmt.Errorf("failed to open DB: %w", err) @@ -54,7 +56,7 @@ func newNewCommand() *cobra.Command { return err } - if err := db.Machine.Create(). + if err := db.Machine.Create().SetName(name). SetID(fprint).SetPublicKey(m.PublicKey). Exec(cmd.Context()); err != nil { return fmt.Errorf("failed to write to DB: %w", err) diff --git a/cmd/klefkictl/klefkictl_requests.go b/cmd/klefkictl/klefkictl_requests.go index 64c29b2..3ebcc46 100644 --- a/cmd/klefkictl/klefkictl_requests.go +++ b/cmd/klefkictl/klefkictl_requests.go @@ -21,7 +21,9 @@ import ( "bytes" "crypto/ed25519" "fmt" + "io" "os" + "strings" "text/tabwriter" pbgrpcv1 "git.rgst.io/homelab/klefki/internal/server/grpc/generated/go/rgst/klefki/v1" @@ -33,6 +35,21 @@ import ( "github.com/spf13/cobra" ) +// nopWriteCloser is a no-op [io.WriteCloser] +type nopWriteCloser struct { + io.Writer +} + +// Close implements [io.Closer] +func (nwc nopWriteCloser) Close() error { + return nil +} + +// newNopWriteCloser creates a new nopWriteCloser +func newNopWriteCloser(w io.Writer) *nopWriteCloser { + return &nopWriteCloser{w} +} + // newRequestsCommand creates a requests [cobra.Command] func newRequestsCommand() *cobra.Command { cmd := &cobra.Command{ @@ -40,18 +57,19 @@ func newRequestsCommand() *cobra.Command { Short: "Make requests to a klefki server", } cmd.AddCommand( - newGetKeyRequestCommand(), + newGetKeyCommand(), newListSessionsCommand(), + newSubmitKeyCommand(), ) flags := cmd.Flags() flags.String("hostname", "127.0.0.1:5300", "hostname of the klefki server to connect to") return cmd } -// newGetKeyRequestCommand creates a getkeyrequest [cobra.Command] -func newGetKeyRequestCommand() *cobra.Command { +// newGetKeyCommand creates a getkeyrequest [cobra.Command] +func newGetKeyCommand() *cobra.Command { cmd := &cobra.Command{ - Use: "getkeyrequest", + Use: "getkey", Short: "Get the passphrase for the given machine", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { @@ -84,11 +102,17 @@ func newGetKeyRequestCommand() *cobra.Command { if err != nil { return err } - defer kcclose() //nolint:errcheck // Why: Best effort + defer kcclose() //nolint:errcheck // Why: Btiest effort + + tsResp, err := kc.GetTime(cmd.Context(), &pbgrpcv1.GetTimeRequest{}) + if err != nil { + return fmt.Errorf("failed to connect to server to get time: %w", err) + } req := &pbgrpcv1.GetKeyRequest{} req.SetMachineId(machineID) req.SetNonce(uuid.New().String()) + req.SetSignedAt(tsResp.GetTime()) req.SetSignature(ed25519.Sign(pk, []byte(req.GetNonce()))) resp, err := kc.GetKey(cmd.Context(), req) @@ -125,7 +149,7 @@ func newListSessionsCommand() *cobra.Command { Short: "Return a list of all machines waiting for a key to be provided", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, _ []string) error { - kc, kcclose, err := client.Dial(cmd.Flag("hostname").Value.String()) + kc, kcclose, err := client.Dial(cmd.Parent().Flag("hostname").Value.String()) if err != nil { return err } @@ -143,11 +167,74 @@ func newListSessionsCommand() *cobra.Command { } tw := tabwriter.NewWriter(os.Stdout, 2, 2, 2, ' ', 0) - fmt.Fprint(tw, "FINGERPRINT\n") + fmt.Fprint(tw, "FINGERPRINT\tLAST ASKED\n") for _, m := range machines { - fmt.Fprintf(tw, "%s\n", m.GetId()) + fmt.Fprintf(tw, "%s\t%s\n", m.GetId(), m.GetLastAsked()) } return tw.Flush() }, } } + +// newSubmitKeyCommand creates a submitekey [cobra.Command] +func newSubmitKeyCommand() *cobra.Command { + return &cobra.Command{ + Use: "submitkey ", + Short: "Submit a passphrase to a given machine by its ID", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + machineID := args[0] + // TODO(jaredallard): don't expect to be passed + passphrase := args[1] + + kc, kcclose, err := client.Dial(cmd.Parent().Flag("hostname").Value.String()) + if err != nil { + return err + } + defer kcclose() //nolint:errcheck // Why: Best effort + + resp, err := kc.ListSessions(cmd.Context(), &pbgrpcv1.ListSessionsRequest{}) + if err != nil { + return fmt.Errorf("failed to get key from server: %w", err) + } + + machines := resp.GetMachines() + + var machine *pbgrpcv1.Machine + for _, m := range machines { + if m.GetId() == machineID { + machine = m + break + } + } + if machine == nil { + return fmt.Errorf("no sessions found for %q", machineID) + } + + pubKey, err := sign.PublicKeyFromBytes(machine.GetPublicKey()) + if err != nil { + return fmt.Errorf("failed to convert machine's public key to encryption public key: %w", err) + } + + enc, err := sign.NewEncryptor(nil, 1024) + if err != nil { + return fmt.Errorf("failed to create decryptor: %w", err) + } + + if err := enc.AddRecipient(pubKey); err != nil { + return fmt.Errorf("failed to set private key on decryptor: %w", err) + } + + var buf bytes.Buffer + if err := enc.Encrypt(strings.NewReader(passphrase), newNopWriteCloser(&buf)); err != nil { + return fmt.Errorf("failed to decrypt session ID: %w", err) + } + + req := &pbgrpcv1.SubmitKeyRequest{} + req.SetEncKey(buf.Bytes()) + req.SetMachineId(machineID) + _, err = kc.SubmitKey(cmd.Context(), req) + return err + }, + } +} diff --git a/internal/db/ent/migrate/schema.go b/internal/db/ent/migrate/schema.go index 7940e4e..2629a75 100644 --- a/internal/db/ent/migrate/schema.go +++ b/internal/db/ent/migrate/schema.go @@ -13,7 +13,7 @@ var ( {Name: "id", Type: field.TypeString}, {Name: "name", Type: field.TypeString, Unique: true}, {Name: "public_key", Type: field.TypeBytes}, - {Name: "created_at", Type: field.TypeString, Default: "2025-03-01T23:41:53Z"}, + {Name: "created_at", Type: field.TypeString, Default: "2025-03-07T05:15:58Z"}, } // MachinesTable holds the schema information for the "machines" table. MachinesTable = &schema.Table{ diff --git a/internal/server/grpc/generated/go/rgst/klefki/v1/kelfki.pb.go b/internal/server/grpc/generated/go/rgst/klefki/v1/kelfki.pb.go index 90a5c45..1074596 100644 --- a/internal/server/grpc/generated/go/rgst/klefki/v1/kelfki.pb.go +++ b/internal/server/grpc/generated/go/rgst/klefki/v1/kelfki.pb.go @@ -7,11 +7,12 @@ package v1 import ( + reflect "reflect" + unsafe "unsafe" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" _ "google.golang.org/protobuf/types/gofeaturespb" - reflect "reflect" - unsafe "unsafe" ) const ( @@ -444,6 +445,7 @@ type Machine struct { state protoimpl.MessageState `protogen:"opaque.v1"` xxx_hidden_Id *string `protobuf:"bytes,1,opt,name=id"` xxx_hidden_PublicKey []byte `protobuf:"bytes,2,opt,name=public_key,json=publicKey"` + xxx_hidden_LastAsked *string `protobuf:"bytes,3,opt,name=last_asked,json=lastAsked"` XXX_raceDetectHookData protoimpl.RaceDetectHookData XXX_presence [1]uint32 unknownFields protoimpl.UnknownFields @@ -492,9 +494,19 @@ func (x *Machine) GetPublicKey() []byte { return nil } +func (x *Machine) GetLastAsked() string { + if x != nil { + if x.xxx_hidden_LastAsked != nil { + return *x.xxx_hidden_LastAsked + } + return "" + } + return "" +} + func (x *Machine) SetId(v string) { x.xxx_hidden_Id = &v - protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 2) + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 3) } func (x *Machine) SetPublicKey(v []byte) { @@ -502,7 +514,12 @@ func (x *Machine) SetPublicKey(v []byte) { v = []byte{} } x.xxx_hidden_PublicKey = v - protoimpl.X.SetPresent(&(x.XXX_presence[0]), 1, 2) + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 1, 3) +} + +func (x *Machine) SetLastAsked(v string) { + x.xxx_hidden_LastAsked = &v + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 2, 3) } func (x *Machine) HasId() bool { @@ -519,6 +536,13 @@ func (x *Machine) HasPublicKey() bool { return protoimpl.X.Present(&(x.XXX_presence[0]), 1) } +func (x *Machine) HasLastAsked() bool { + if x == nil { + return false + } + return protoimpl.X.Present(&(x.XXX_presence[0]), 2) +} + func (x *Machine) ClearId() { protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0) x.xxx_hidden_Id = nil @@ -529,11 +553,17 @@ func (x *Machine) ClearPublicKey() { x.xxx_hidden_PublicKey = nil } +func (x *Machine) ClearLastAsked() { + protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 2) + x.xxx_hidden_LastAsked = nil +} + type Machine_builder struct { _ [0]func() // Prevents comparability and use of unkeyed literals for the builder. Id *string PublicKey []byte + LastAsked *string } func (b0 Machine_builder) Build() *Machine { @@ -541,13 +571,17 @@ func (b0 Machine_builder) Build() *Machine { b, x := &b0, m0 _, _ = b, x if b.Id != nil { - protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 2) + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 3) x.xxx_hidden_Id = b.Id } if b.PublicKey != nil { - protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 1, 2) + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 1, 3) x.xxx_hidden_PublicKey = b.PublicKey } + if b.LastAsked != nil { + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 2, 3) + x.xxx_hidden_LastAsked = b.LastAsked + } return m0 } @@ -787,48 +821,50 @@ var file_rgst_klefki_v1_kelfki_proto_rawDesc = string([]byte{ 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x65, 0x6e, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x65, 0x6e, 0x63, 0x4b, 0x65, 0x79, 0x22, 0x15, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x38, 0x0a, 0x07, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x57, 0x0a, 0x07, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x70, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x22, 0x4b, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, - 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, - 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x72, 0x67, 0x73, 0x74, 0x2e, 0x6b, 0x6c, 0x65, 0x66, 0x6b, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, - 0x6e, 0x65, 0x73, 0x22, 0x4a, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4b, 0x65, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, - 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x61, 0x63, - 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x65, 0x6e, 0x63, 0x5f, 0x6b, 0x65, - 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x65, 0x6e, 0x63, 0x4b, 0x65, 0x79, 0x22, - 0x13, 0x0a, 0x11, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xd1, 0x02, 0x0a, 0x0d, 0x4b, 0x6c, 0x65, 0x66, 0x6b, 0x69, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x54, 0x69, 0x6d, - 0x65, 0x12, 0x1e, 0x2e, 0x72, 0x67, 0x73, 0x74, 0x2e, 0x6b, 0x6c, 0x65, 0x66, 0x6b, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1f, 0x2e, 0x72, 0x67, 0x73, 0x74, 0x2e, 0x6b, 0x6c, 0x65, 0x66, 0x6b, 0x69, 0x2e, - 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x47, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x2e, 0x72, - 0x67, 0x73, 0x74, 0x2e, 0x6b, 0x6c, 0x65, 0x66, 0x6b, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, - 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x72, 0x67, + 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, + 0x73, 0x6b, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6c, 0x61, 0x73, 0x74, + 0x41, 0x73, 0x6b, 0x65, 0x64, 0x22, 0x4b, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, + 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x72, 0x67, 0x73, 0x74, 0x2e, 0x6b, 0x6c, 0x65, 0x66, 0x6b, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, + 0x65, 0x73, 0x22, 0x4a, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, + 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, + 0x69, 0x6e, 0x65, 0x49, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x65, 0x6e, 0x63, 0x5f, 0x6b, 0x65, 0x79, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x65, 0x6e, 0x63, 0x4b, 0x65, 0x79, 0x22, 0x13, + 0x0a, 0x11, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x32, 0xd1, 0x02, 0x0a, 0x0d, 0x4b, 0x6c, 0x65, 0x66, 0x6b, 0x69, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4a, 0x0a, 0x07, 0x47, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, + 0x12, 0x1e, 0x2e, 0x72, 0x67, 0x73, 0x74, 0x2e, 0x6b, 0x6c, 0x65, 0x66, 0x6b, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x1f, 0x2e, 0x72, 0x67, 0x73, 0x74, 0x2e, 0x6b, 0x6c, 0x65, 0x66, 0x6b, 0x69, 0x2e, 0x76, + 0x31, 0x2e, 0x47, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x47, 0x0a, 0x06, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x1d, 0x2e, 0x72, 0x67, 0x73, 0x74, 0x2e, 0x6b, 0x6c, 0x65, 0x66, 0x6b, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, - 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x0c, 0x4c, - 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, 0x2e, 0x72, 0x67, - 0x73, 0x74, 0x2e, 0x6b, 0x6c, 0x65, 0x66, 0x6b, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, - 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x24, 0x2e, 0x72, 0x67, 0x73, 0x74, 0x2e, 0x6b, 0x6c, 0x65, 0x66, 0x6b, 0x69, 0x2e, 0x76, - 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, - 0x4b, 0x65, 0x79, 0x12, 0x20, 0x2e, 0x72, 0x67, 0x73, 0x74, 0x2e, 0x6b, 0x6c, 0x65, 0x66, 0x6b, - 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x72, 0x67, 0x73, 0x74, 0x2e, 0x6b, 0x6c, 0x65, - 0x66, 0x6b, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4b, 0x65, 0x79, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3f, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x2e, - 0x72, 0x67, 0x73, 0x74, 0x2e, 0x69, 0x6f, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2f, - 0x67, 0x6f, 0x2f, 0x72, 0x67, 0x73, 0x74, 0x2f, 0x6b, 0x6c, 0x65, 0x66, 0x6b, 0x69, 0x2f, 0x76, - 0x31, 0x92, 0x03, 0x05, 0xd2, 0x3e, 0x02, 0x10, 0x03, 0x62, 0x08, 0x65, 0x64, 0x69, 0x74, 0x69, - 0x6f, 0x6e, 0x73, 0x70, 0xe8, 0x07, + 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x72, 0x67, 0x73, + 0x74, 0x2e, 0x6b, 0x6c, 0x65, 0x66, 0x6b, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4b, + 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x59, 0x0a, 0x0c, 0x4c, 0x69, + 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x23, 0x2e, 0x72, 0x67, 0x73, + 0x74, 0x2e, 0x6b, 0x6c, 0x65, 0x66, 0x6b, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x24, 0x2e, 0x72, 0x67, 0x73, 0x74, 0x2e, 0x6b, 0x6c, 0x65, 0x66, 0x6b, 0x69, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4b, + 0x65, 0x79, 0x12, 0x20, 0x2e, 0x72, 0x67, 0x73, 0x74, 0x2e, 0x6b, 0x6c, 0x65, 0x66, 0x6b, 0x69, + 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x72, 0x67, 0x73, 0x74, 0x2e, 0x6b, 0x6c, 0x65, 0x66, + 0x6b, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x75, 0x62, 0x6d, 0x69, 0x74, 0x4b, 0x65, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x3f, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x2e, 0x72, + 0x67, 0x73, 0x74, 0x2e, 0x69, 0x6f, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, + 0x67, 0x72, 0x70, 0x63, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2f, 0x67, + 0x6f, 0x2f, 0x72, 0x67, 0x73, 0x74, 0x2f, 0x6b, 0x6c, 0x65, 0x66, 0x6b, 0x69, 0x2f, 0x76, 0x31, + 0x92, 0x03, 0x05, 0xd2, 0x3e, 0x02, 0x10, 0x03, 0x62, 0x08, 0x65, 0x64, 0x69, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x70, 0xe8, 0x07, }) var file_rgst_klefki_v1_kelfki_proto_msgTypes = make([]protoimpl.MessageInfo, 9) diff --git a/internal/server/grpc/generated/go/rgst/klefki/v1/kelfki_grpc.pb.go b/internal/server/grpc/generated/go/rgst/klefki/v1/kelfki_grpc.pb.go index bb96b0a..6009dea 100644 --- a/internal/server/grpc/generated/go/rgst/klefki/v1/kelfki_grpc.pb.go +++ b/internal/server/grpc/generated/go/rgst/klefki/v1/kelfki_grpc.pb.go @@ -8,6 +8,7 @@ package v1 import ( context "context" + grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" diff --git a/internal/server/grpc/proto/rgst/klefki/v1/kelfki.proto b/internal/server/grpc/proto/rgst/klefki/v1/kelfki.proto index 63dc9b7..fdd7e16 100644 --- a/internal/server/grpc/proto/rgst/klefki/v1/kelfki.proto +++ b/internal/server/grpc/proto/rgst/klefki/v1/kelfki.proto @@ -28,6 +28,7 @@ message ListSessionsRequest {} message Machine { string id = 1; bytes public_key = 2; + string last_asked = 3; } message ListSessionsResponse { diff --git a/internal/server/server.go b/internal/server/server.go index a8a056a..987b034 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -84,6 +84,8 @@ func (s *Server) Run(ctx context.Context) error { return fmt.Errorf("failed to open DB: %w", err) } + // TODO(jaredallard): Clean up expired sessions after X time period. + s.gs = grpc.NewServer() pbgrpcv1.RegisterKlefkiServiceServer(s.gs, s) reflection.Register(s.gs) @@ -100,10 +102,21 @@ func (s *Server) Run(ctx context.Context) error { // GetTime implements the GetTime RPC func (s *Server) GetTime(_ context.Context, req *pbgrpcv1.GetTimeRequest) (*pbgrpcv1.GetTimeResponse, error) { resp := &pbgrpcv1.GetTimeResponse{} - resp.SetTime(time.Now().UTC().Format(time.RFC3339Nano)) + resp.SetTime(time.Now().Format(time.RFC3339Nano)) return resp, nil } +// SubmitKey implements the SubmitKey RPC +func (s *Server) SubmitKey(ctx context.Context, req *pbgrpcv1.SubmitKeyRequest) (*pbgrpcv1.SubmitKeyResponse, error) { + machineID := req.GetMachineId() + if _, ok := s.ses[machineID]; !ok { + return nil, fmt.Errorf("failed to find machine ID %q", machineID) + } + + s.ses[machineID].EncKey = req.GetEncKey() + return &pbgrpcv1.SubmitKeyResponse{}, nil +} + // GetKey implements the GetKey RPC func (s *Server) GetKey(ctx context.Context, req *pbgrpcv1.GetKeyRequest) (*pbgrpcv1.GetKeyResponse, error) { resp := &pbgrpcv1.GetKeyResponse{} @@ -113,6 +126,7 @@ func (s *Server) GetKey(ctx context.Context, req *pbgrpcv1.GetKeyRequest) (*pbgr if err != nil || ts.IsZero() { return nil, fmt.Errorf("failed to parsed signed at %q: %w", req.GetSignedAt(), err) } + ts = ts.UTC() // Always operate with UTC time. sig := req.GetSignature() machine, err := s.db.Machine.Get(ctx, req.GetMachineId()) @@ -138,11 +152,21 @@ func (s *Server) GetKey(ctx context.Context, req *pbgrpcv1.GetKeyRequest) (*pbgr return nil, fmt.Errorf("failed to add instance public key to encryptor: %w", err) } + // Track the last time the machine asked for a key. This is what backs + // the sessions api + if _, ok := s.ses[machine.ID]; !ok { + s.ses[machine.ID] = &Session{} + } + s.ses[machine.ID].LastAsked = time.Now() + if len(s.ses[machine.ID].EncKey) == 0 { return nil, fmt.Errorf("key not available") } - resp.SetEncKey(s.ses[machine.ID].EncKey) + + // Reset the session + delete(s.ses, machine.ID) + return resp, nil } @@ -157,7 +181,10 @@ func (s *Server) ListSessions(ctx context.Context, _ *pbgrpcv1.ListSessionsReque return nil, fmt.Errorf("failed to get machine %q: %w", machineID, err) } - grpcMachines = append(grpcMachines, machines.GRPCMachine(machine)) + // If the machine asked recently, return it + gMachine := machines.GRPCMachine(machine) + gMachine.SetLastAsked(s.ses[machineID].LastAsked.Format(time.RFC3339Nano)) + grpcMachines = append(grpcMachines, gMachine) } resp.SetMachines(grpcMachines)