diff --git a/.mise.toml b/.mise.toml index ed096cc..48b74f1 100644 --- a/.mise.toml +++ b/.mise.toml @@ -31,12 +31,12 @@ run = ["git-cliff --config .cliff.toml --output CHANGELOG.md"] alias = "format" description = "Format code" run = [ - "go mod tidy", - "gofmt -s -w .", - "goimports -w .", - "shfmt -w .", - "bun node_modules/.bin/prettier --write '**/*.{json,yaml,yml,md,jsonschema.json}'", - "buf format -w", + "go mod tidy", + "gofmt -s -w .", + "goimports -w .", + "shfmt -w .", + "bun node_modules/.bin/prettier --write '**/*.{json,yaml,yml,md,jsonschema.json}'", + "buf format -w", ] [tasks.lint] diff --git a/cmd/klefkictl/klefkictl_new.go b/cmd/klefkictl/klefkictl_new.go index f32b55e..518b5a8 100644 --- a/cmd/klefkictl/klefkictl_new.go +++ b/cmd/klefkictl/klefkictl_new.go @@ -27,6 +27,7 @@ import ( // newNewCommand creates a new [cobra.Command] func newNewCommand() *cobra.Command { + // TODO(jaredallard): Support setting the name of the machine. return &cobra.Command{ Use: "new", Short: "Create a new machine", diff --git a/cmd/klefkictl/klefkictl_requests.go b/cmd/klefkictl/klefkictl_requests.go index fb7ce41..cf2f2eb 100644 --- a/cmd/klefkictl/klefkictl_requests.go +++ b/cmd/klefkictl/klefkictl_requests.go @@ -21,8 +21,8 @@ import ( "bytes" "crypto/ed25519" "fmt" - "io" "os" + "text/tabwriter" pbgrpcv1 "git.rgst.io/homelab/klefki/internal/server/grpc/generated/go/rgst/klefki/v1" @@ -39,25 +39,13 @@ func newRequestsCommand() *cobra.Command { Use: "requests", Short: "Make requests to a klefki server", } - cmd.AddCommand(newGetKeyRequestCommand()) + cmd.AddCommand( + newGetKeyRequestCommand(), + newListSessionsCommand(), + ) return cmd } -// 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} -} - // newGetKeyRequestCommand creates a getkeyrequest [cobra.Command] func newGetKeyRequestCommand() *cobra.Command { cmd := &cobra.Command{ @@ -98,27 +86,71 @@ func newGetKeyRequestCommand() *cobra.Command { client := pbgrpcv1.NewKlefkiServiceClient(conn) - req := &pbgrpcv1.GetKeyRequest{} + req := &pbgrpcv1.CreateSessionRequest{} req.SetMachineId(machineID) req.SetNonce("FIXME") req.SetSignature(ed25519.Sign(pk, []byte(req.GetNonce()))) - resp, err := client.GetKey(cmd.Context(), req) + resp, err := client.CreateSession(cmd.Context(), req) if err != nil { return fmt.Errorf("failed to get key from server: %w", err) } - dec, err := sign.NewDecryptor(bytes.NewReader(resp.GetKey())) + encSessionID := resp.GetEncSessionId() + dec, err := sign.NewDecryptor(bytes.NewReader(encSessionID)) if err != nil { return fmt.Errorf("failed to create decryptor: %w", err) } if err := dec.SetPrivateKey(spk, nil); err != nil { return fmt.Errorf("failed to set private key on decryptor: %w", err) } - return dec.Decrypt(os.Stdout) + var buf bytes.Buffer + if err := dec.Decrypt(&buf); err != nil { + return fmt.Errorf("failed to decrypt session ID: %w", err) + } + + sessionID := buf.String() + fmt.Println(sessionID) + return nil }, } flags := cmd.Flags() flags.String("priv-key", "", "path to private key") return cmd } + +// newListSessionsCommand creates a listsessions [cobra.Command] +func newListSessionsCommand() *cobra.Command { + return &cobra.Command{ + Use: "listsessions", + Short: "Return a list of all machines waiting for a key to be provided", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + // TODO(jaredallard): Make a client + conn, err := grpc.NewClient("127.0.0.1:5300", grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + return fmt.Errorf("failed to connect to klefki server: %w", err) + } + defer conn.Close() + + client := pbgrpcv1.NewKlefkiServiceClient(conn) + resp, err := client.ListSessions(cmd.Context(), &pbgrpcv1.ListSessionsRequest{}) + if err != nil { + return fmt.Errorf("failed to get key from server: %w", err) + } + + machines := resp.GetMachines() + if len(machines) == 0 { + fmt.Println("No results found") + return nil + } + + tw := tabwriter.NewWriter(os.Stdout, 2, 2, 2, ' ', 0) + fmt.Fprint(tw, "FINGERPRINT\n") + for _, m := range machines { + fmt.Fprintf(tw, "%s\n", m.GetId()) + } + return tw.Flush() + }, + } +} diff --git a/docs/DESIGN.md b/docs/DESIGN.md index 8ef143a..52aa293 100644 --- a/docs/DESIGN.md +++ b/docs/DESIGN.md @@ -29,13 +29,18 @@ disk. ### Endpoints -- `GetKey() string` - Creates a new session for the authenticated - machine, waits for `SubmitKey` to be called, then returns the - plain-text pass-phrase. +- `CreateSession() string` - Creates a new session for the machineID and + returns it. +- `GetKey(sessionID string) string` - If connected to a client through + `SubmitKey`, returns the key, otherwise waits for a period of time + then the caller should retry the endpoint (polling). - `ListSessions() []MachineID` - Returns a list of machine IDs waiting for a key to be provided. -- `SubmitKey(key string, machineID string)` - Finds the active sessions - for the provided `machineID` and submits the key to it. +- `SubmitKey(key []byte, machineID string)` - If a session is present + for the provided `machindID`, then the key is stored in memory on the + server side and provided when `GetKey` is next called by the machine. + Note that `key` is expected to be encrypted to the `machineID`'s + public key, which is obtained through `ListSessions` beforehand. ### Security diff --git a/go.mod b/go.mod index 3a677f1..61509c1 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.24 require ( entgo.io/ent v0.14.3 git.rgst.io/homelab/sigtool/v3 v3.2.3-jaredallard.2 + github.com/google/uuid v1.6.0 github.com/ncruces/go-sqlite3 v0.24.0 github.com/spf13/cobra v1.9.1 google.golang.org/grpc v1.70.0 @@ -19,7 +20,6 @@ require ( github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a // indirect github.com/go-openapi/inflect v0.21.0 // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/hcl/v2 v2.23.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect diff --git a/internal/db/ent/machine.go b/internal/db/ent/machine.go index e48dc97..f625f55 100644 --- a/internal/db/ent/machine.go +++ b/internal/db/ent/machine.go @@ -17,6 +17,8 @@ type Machine struct { // ID of the ent. // Fingerprint of the public key ID string `json:"id,omitempty"` + // User friendly name of this machine (e.g., hostname) + Name string `json:"name,omitempty"` // Public key of the machine PublicKey []byte `json:"public_key,omitempty"` // When this machine was added in UTC @@ -31,7 +33,7 @@ func (*Machine) scanValues(columns []string) ([]any, error) { switch columns[i] { case machine.FieldPublicKey: values[i] = new([]byte) - case machine.FieldID, machine.FieldCreatedAt: + case machine.FieldID, machine.FieldName, machine.FieldCreatedAt: values[i] = new(sql.NullString) default: values[i] = new(sql.UnknownType) @@ -54,6 +56,12 @@ func (m *Machine) assignValues(columns []string, values []any) error { } else if value.Valid { m.ID = value.String } + case machine.FieldName: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field name", values[i]) + } else if value.Valid { + m.Name = value.String + } case machine.FieldPublicKey: if value, ok := values[i].(*[]byte); !ok { return fmt.Errorf("unexpected type %T for field public_key", values[i]) @@ -102,6 +110,9 @@ func (m *Machine) String() string { var builder strings.Builder builder.WriteString("Machine(") builder.WriteString(fmt.Sprintf("id=%v, ", m.ID)) + builder.WriteString("name=") + builder.WriteString(m.Name) + builder.WriteString(", ") builder.WriteString("public_key=") builder.WriteString(fmt.Sprintf("%v", m.PublicKey)) builder.WriteString(", ") diff --git a/internal/db/ent/machine/machine.go b/internal/db/ent/machine/machine.go index bf4b802..fc9bdcc 100644 --- a/internal/db/ent/machine/machine.go +++ b/internal/db/ent/machine/machine.go @@ -11,6 +11,8 @@ const ( Label = "machine" // FieldID holds the string denoting the id field in the database. FieldID = "id" + // FieldName holds the string denoting the name field in the database. + FieldName = "name" // FieldPublicKey holds the string denoting the public_key field in the database. FieldPublicKey = "public_key" // FieldCreatedAt holds the string denoting the created_at field in the database. @@ -22,6 +24,7 @@ const ( // Columns holds all SQL columns for machine fields. var Columns = []string{ FieldID, + FieldName, FieldPublicKey, FieldCreatedAt, } @@ -49,6 +52,11 @@ func ByID(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldID, opts...).ToFunc() } +// ByName orders the results by the name field. +func ByName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldName, opts...).ToFunc() +} + // ByCreatedAt orders the results by the created_at field. func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() diff --git a/internal/db/ent/machine/where.go b/internal/db/ent/machine/where.go index da07094..f95257f 100644 --- a/internal/db/ent/machine/where.go +++ b/internal/db/ent/machine/where.go @@ -62,6 +62,11 @@ func IDContainsFold(id string) predicate.Machine { return predicate.Machine(sql.FieldContainsFold(FieldID, id)) } +// Name applies equality check predicate on the "name" field. It's identical to NameEQ. +func Name(v string) predicate.Machine { + return predicate.Machine(sql.FieldEQ(FieldName, v)) +} + // PublicKey applies equality check predicate on the "public_key" field. It's identical to PublicKeyEQ. func PublicKey(v []byte) predicate.Machine { return predicate.Machine(sql.FieldEQ(FieldPublicKey, v)) @@ -72,6 +77,71 @@ func CreatedAt(v string) predicate.Machine { return predicate.Machine(sql.FieldEQ(FieldCreatedAt, v)) } +// NameEQ applies the EQ predicate on the "name" field. +func NameEQ(v string) predicate.Machine { + return predicate.Machine(sql.FieldEQ(FieldName, v)) +} + +// NameNEQ applies the NEQ predicate on the "name" field. +func NameNEQ(v string) predicate.Machine { + return predicate.Machine(sql.FieldNEQ(FieldName, v)) +} + +// NameIn applies the In predicate on the "name" field. +func NameIn(vs ...string) predicate.Machine { + return predicate.Machine(sql.FieldIn(FieldName, vs...)) +} + +// NameNotIn applies the NotIn predicate on the "name" field. +func NameNotIn(vs ...string) predicate.Machine { + return predicate.Machine(sql.FieldNotIn(FieldName, vs...)) +} + +// NameGT applies the GT predicate on the "name" field. +func NameGT(v string) predicate.Machine { + return predicate.Machine(sql.FieldGT(FieldName, v)) +} + +// NameGTE applies the GTE predicate on the "name" field. +func NameGTE(v string) predicate.Machine { + return predicate.Machine(sql.FieldGTE(FieldName, v)) +} + +// NameLT applies the LT predicate on the "name" field. +func NameLT(v string) predicate.Machine { + return predicate.Machine(sql.FieldLT(FieldName, v)) +} + +// NameLTE applies the LTE predicate on the "name" field. +func NameLTE(v string) predicate.Machine { + return predicate.Machine(sql.FieldLTE(FieldName, v)) +} + +// NameContains applies the Contains predicate on the "name" field. +func NameContains(v string) predicate.Machine { + return predicate.Machine(sql.FieldContains(FieldName, v)) +} + +// NameHasPrefix applies the HasPrefix predicate on the "name" field. +func NameHasPrefix(v string) predicate.Machine { + return predicate.Machine(sql.FieldHasPrefix(FieldName, v)) +} + +// NameHasSuffix applies the HasSuffix predicate on the "name" field. +func NameHasSuffix(v string) predicate.Machine { + return predicate.Machine(sql.FieldHasSuffix(FieldName, v)) +} + +// NameEqualFold applies the EqualFold predicate on the "name" field. +func NameEqualFold(v string) predicate.Machine { + return predicate.Machine(sql.FieldEqualFold(FieldName, v)) +} + +// NameContainsFold applies the ContainsFold predicate on the "name" field. +func NameContainsFold(v string) predicate.Machine { + return predicate.Machine(sql.FieldContainsFold(FieldName, v)) +} + // PublicKeyEQ applies the EQ predicate on the "public_key" field. func PublicKeyEQ(v []byte) predicate.Machine { return predicate.Machine(sql.FieldEQ(FieldPublicKey, v)) diff --git a/internal/db/ent/machine_create.go b/internal/db/ent/machine_create.go index dc89799..d9c73c3 100644 --- a/internal/db/ent/machine_create.go +++ b/internal/db/ent/machine_create.go @@ -19,6 +19,12 @@ type MachineCreate struct { hooks []Hook } +// SetName sets the "name" field. +func (mc *MachineCreate) SetName(s string) *MachineCreate { + mc.mutation.SetName(s) + return mc +} + // SetPublicKey sets the "public_key" field. func (mc *MachineCreate) SetPublicKey(b []byte) *MachineCreate { mc.mutation.SetPublicKey(b) @@ -88,6 +94,9 @@ func (mc *MachineCreate) defaults() { // check runs all checks and user-defined validators on the builder. func (mc *MachineCreate) check() error { + if _, ok := mc.mutation.Name(); !ok { + return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "Machine.name"`)} + } if _, ok := mc.mutation.PublicKey(); !ok { return &ValidationError{Name: "public_key", err: errors.New(`ent: missing required field "Machine.public_key"`)} } @@ -129,6 +138,10 @@ func (mc *MachineCreate) createSpec() (*Machine, *sqlgraph.CreateSpec) { _node.ID = id _spec.ID.Value = id } + if value, ok := mc.mutation.Name(); ok { + _spec.SetField(machine.FieldName, field.TypeString, value) + _node.Name = value + } if value, ok := mc.mutation.PublicKey(); ok { _spec.SetField(machine.FieldPublicKey, field.TypeBytes, value) _node.PublicKey = value diff --git a/internal/db/ent/machine_query.go b/internal/db/ent/machine_query.go index 1637396..6f8b9cd 100644 --- a/internal/db/ent/machine_query.go +++ b/internal/db/ent/machine_query.go @@ -262,12 +262,12 @@ func (mq *MachineQuery) Clone() *MachineQuery { // Example: // // var v []struct { -// PublicKey []byte `json:"public_key,omitempty"` +// Name string `json:"name,omitempty"` // Count int `json:"count,omitempty"` // } // // client.Machine.Query(). -// GroupBy(machine.FieldPublicKey). +// GroupBy(machine.FieldName). // Aggregate(ent.Count()). // Scan(ctx, &v) func (mq *MachineQuery) GroupBy(field string, fields ...string) *MachineGroupBy { @@ -285,11 +285,11 @@ func (mq *MachineQuery) GroupBy(field string, fields ...string) *MachineGroupBy // Example: // // var v []struct { -// PublicKey []byte `json:"public_key,omitempty"` +// Name string `json:"name,omitempty"` // } // // client.Machine.Query(). -// Select(machine.FieldPublicKey). +// Select(machine.FieldName). // Scan(ctx, &v) func (mq *MachineQuery) Select(fields ...string) *MachineSelect { mq.ctx.Fields = append(mq.ctx.Fields, fields...) diff --git a/internal/db/ent/machine_update.go b/internal/db/ent/machine_update.go index 3ddb6af..e904d66 100644 --- a/internal/db/ent/machine_update.go +++ b/internal/db/ent/machine_update.go @@ -27,6 +27,20 @@ func (mu *MachineUpdate) Where(ps ...predicate.Machine) *MachineUpdate { return mu } +// SetName sets the "name" field. +func (mu *MachineUpdate) SetName(s string) *MachineUpdate { + mu.mutation.SetName(s) + return mu +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (mu *MachineUpdate) SetNillableName(s *string) *MachineUpdate { + if s != nil { + mu.SetName(*s) + } + return mu +} + // SetPublicKey sets the "public_key" field. func (mu *MachineUpdate) SetPublicKey(b []byte) *MachineUpdate { mu.mutation.SetPublicKey(b) @@ -88,6 +102,9 @@ func (mu *MachineUpdate) sqlSave(ctx context.Context) (n int, err error) { } } } + if value, ok := mu.mutation.Name(); ok { + _spec.SetField(machine.FieldName, field.TypeString, value) + } if value, ok := mu.mutation.PublicKey(); ok { _spec.SetField(machine.FieldPublicKey, field.TypeBytes, value) } @@ -114,6 +131,20 @@ type MachineUpdateOne struct { mutation *MachineMutation } +// SetName sets the "name" field. +func (muo *MachineUpdateOne) SetName(s string) *MachineUpdateOne { + muo.mutation.SetName(s) + return muo +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (muo *MachineUpdateOne) SetNillableName(s *string) *MachineUpdateOne { + if s != nil { + muo.SetName(*s) + } + return muo +} + // SetPublicKey sets the "public_key" field. func (muo *MachineUpdateOne) SetPublicKey(b []byte) *MachineUpdateOne { muo.mutation.SetPublicKey(b) @@ -205,6 +236,9 @@ func (muo *MachineUpdateOne) sqlSave(ctx context.Context) (_node *Machine, err e } } } + if value, ok := muo.mutation.Name(); ok { + _spec.SetField(machine.FieldName, field.TypeString, value) + } if value, ok := muo.mutation.PublicKey(); ok { _spec.SetField(machine.FieldPublicKey, field.TypeBytes, value) } diff --git a/internal/db/ent/migrate/schema.go b/internal/db/ent/migrate/schema.go index 22da516..7548ae9 100644 --- a/internal/db/ent/migrate/schema.go +++ b/internal/db/ent/migrate/schema.go @@ -11,8 +11,9 @@ var ( // MachinesColumns holds the columns for the "machines" table. MachinesColumns = []*schema.Column{ {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-01T18:10:35Z"}, + {Name: "created_at", Type: field.TypeString, Default: "2025-03-01T18:59:51Z"}, } // MachinesTable holds the schema information for the "machines" table. MachinesTable = &schema.Table{ diff --git a/internal/db/ent/mutation.go b/internal/db/ent/mutation.go index c4da0fe..fff2842 100644 --- a/internal/db/ent/mutation.go +++ b/internal/db/ent/mutation.go @@ -32,6 +32,7 @@ type MachineMutation struct { op Op typ string id *string + name *string public_key *[]byte created_at *string clearedFields map[string]struct{} @@ -144,6 +145,42 @@ func (m *MachineMutation) IDs(ctx context.Context) ([]string, error) { } } +// SetName sets the "name" field. +func (m *MachineMutation) SetName(s string) { + m.name = &s +} + +// Name returns the value of the "name" field in the mutation. +func (m *MachineMutation) Name() (r string, exists bool) { + v := m.name + if v == nil { + return + } + return *v, true +} + +// OldName returns the old "name" field's value of the Machine entity. +// If the Machine object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *MachineMutation) OldName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldName is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldName requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldName: %w", err) + } + return oldValue.Name, nil +} + +// ResetName resets all changes to the "name" field. +func (m *MachineMutation) ResetName() { + m.name = nil +} + // SetPublicKey sets the "public_key" field. func (m *MachineMutation) SetPublicKey(b []byte) { m.public_key = &b @@ -250,7 +287,10 @@ func (m *MachineMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *MachineMutation) Fields() []string { - fields := make([]string, 0, 2) + fields := make([]string, 0, 3) + if m.name != nil { + fields = append(fields, machine.FieldName) + } if m.public_key != nil { fields = append(fields, machine.FieldPublicKey) } @@ -265,6 +305,8 @@ func (m *MachineMutation) Fields() []string { // schema. func (m *MachineMutation) Field(name string) (ent.Value, bool) { switch name { + case machine.FieldName: + return m.Name() case machine.FieldPublicKey: return m.PublicKey() case machine.FieldCreatedAt: @@ -278,6 +320,8 @@ func (m *MachineMutation) Field(name string) (ent.Value, bool) { // database failed. func (m *MachineMutation) OldField(ctx context.Context, name string) (ent.Value, error) { switch name { + case machine.FieldName: + return m.OldName(ctx) case machine.FieldPublicKey: return m.OldPublicKey(ctx) case machine.FieldCreatedAt: @@ -291,6 +335,13 @@ func (m *MachineMutation) OldField(ctx context.Context, name string) (ent.Value, // type. func (m *MachineMutation) SetField(name string, value ent.Value) error { switch name { + case machine.FieldName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetName(v) + return nil case machine.FieldPublicKey: v, ok := value.([]byte) if !ok { @@ -354,6 +405,9 @@ func (m *MachineMutation) ClearField(name string) error { // It returns an error if the field is not defined in the schema. func (m *MachineMutation) ResetField(name string) error { switch name { + case machine.FieldName: + m.ResetName() + return nil case machine.FieldPublicKey: m.ResetPublicKey() return nil diff --git a/internal/db/ent/runtime.go b/internal/db/ent/runtime.go index 56d9347..9bb0f5f 100644 --- a/internal/db/ent/runtime.go +++ b/internal/db/ent/runtime.go @@ -14,7 +14,7 @@ func init() { machineFields := schema.Machine{}.Fields() _ = machineFields // machineDescCreatedAt is the schema descriptor for created_at field. - machineDescCreatedAt := machineFields[2].Descriptor() + machineDescCreatedAt := machineFields[3].Descriptor() // machine.DefaultCreatedAt holds the default value on creation for the created_at field. machine.DefaultCreatedAt = machineDescCreatedAt.Default.(string) } diff --git a/internal/db/ent/schema/machine.go b/internal/db/ent/schema/machine.go index be4f241..24b40af 100644 --- a/internal/db/ent/schema/machine.go +++ b/internal/db/ent/schema/machine.go @@ -33,6 +33,7 @@ type Machine struct { func (Machine) Fields() []ent.Field { return []ent.Field{ field.String("id").Comment("Fingerprint of the public key"), + field.String("name").Comment("User friendly name of this machine (e.g., hostname)").Unique(), field.Bytes("public_key").Comment("Public key of the machine"), field.String("created_at").Comment("When this machine was added in UTC").Default(time.Now().UTC().Format(time.RFC3339)), } diff --git a/internal/machines/machine.go b/internal/machines/machine.go index 0b4e12f..45812d2 100644 --- a/internal/machines/machine.go +++ b/internal/machines/machine.go @@ -27,6 +27,7 @@ import ( "sync" "git.rgst.io/homelab/klefki/internal/db/ent" + pbgrpcv1 "git.rgst.io/homelab/klefki/internal/server/grpc/generated/go/rgst/klefki/v1" ) // Fingerprint returns a fingerprint of the provided key. @@ -154,3 +155,11 @@ func Verify(pubKey ed25519.PublicKey, sig []byte, nonce string) error { return fmt.Errorf("invalid signature") } + +// GRPCMachine converts a [ent.Machine] into a [pbgrpcv1.Machine]. +func GRPCMachine(m *ent.Machine) *pbgrpcv1.Machine { + return (&pbgrpcv1.Machine_builder{ + Id: &m.ID, + PublicKey: m.PublicKey, + }).Build() +} diff --git a/internal/server/auth.go b/internal/server/auth.go deleted file mode 100644 index 922b57a..0000000 --- a/internal/server/auth.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (C) 2025 klefki contributors -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as -// published by the Free Software Foundation, either version 3 of the -// License, or (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -// -// SPDX-License-Identifier: AGPL-3.0 - -package server - -import ( - "context" - "crypto/ed25519" - - pbgrpcv1 "git.rgst.io/homelab/klefki/internal/server/grpc/generated/go/rgst/klefki/v1" -) - -// AuthChallenge is an authentication challenge. -type AuthChallenge struct { - // MachineID is the ID of the machine that sent this request - // (fingerprint). - MachineID string `json:"machine_id"` - - // Signature is an ED25519 signature of the provided message. - Signature []byte `json:"signature"` - - // Nonce is a randomly generated string that corresponds to the - // provided signature. - Nonce string `json:"nonce"` -} - -// ValidateAuth determines if the auth presented is valid or not. -func (s *Server) ValidateAuth(ctx context.Context, req *pbgrpcv1.GetKeyRequest) bool { - // Get the public key for this node. - m, err := s.db.Machine.Get(ctx, req.GetMachineId()) - if err != nil { - return false - } - - return ed25519.Verify(m.PublicKey, []byte(req.GetNonce()), req.GetSignature()) -} 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 b8112b3..baea617 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,12 +7,11 @@ 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 ( @@ -24,9 +23,7 @@ const ( type GetKeyRequest struct { state protoimpl.MessageState `protogen:"opaque.v1"` - xxx_hidden_MachineId *string `protobuf:"bytes,1,opt,name=machine_id,json=machineId"` - xxx_hidden_Signature []byte `protobuf:"bytes,2,opt,name=signature"` - xxx_hidden_Nonce *string `protobuf:"bytes,3,opt,name=nonce"` + xxx_hidden_SessionId *string `protobuf:"bytes,1,opt,name=session_id,json=sessionId"` XXX_raceDetectHookData protoimpl.RaceDetectHookData XXX_presence [1]uint32 unknownFields protoimpl.UnknownFields @@ -58,117 +55,53 @@ func (x *GetKeyRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -func (x *GetKeyRequest) GetMachineId() string { +func (x *GetKeyRequest) GetSessionId() string { if x != nil { - if x.xxx_hidden_MachineId != nil { - return *x.xxx_hidden_MachineId + if x.xxx_hidden_SessionId != nil { + return *x.xxx_hidden_SessionId } return "" } return "" } -func (x *GetKeyRequest) GetSignature() []byte { - if x != nil { - return x.xxx_hidden_Signature - } - return nil +func (x *GetKeyRequest) SetSessionId(v string) { + x.xxx_hidden_SessionId = &v + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 1) } -func (x *GetKeyRequest) GetNonce() string { - if x != nil { - if x.xxx_hidden_Nonce != nil { - return *x.xxx_hidden_Nonce - } - return "" - } - return "" -} - -func (x *GetKeyRequest) SetMachineId(v string) { - x.xxx_hidden_MachineId = &v - protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 3) -} - -func (x *GetKeyRequest) SetSignature(v []byte) { - if v == nil { - v = []byte{} - } - x.xxx_hidden_Signature = v - protoimpl.X.SetPresent(&(x.XXX_presence[0]), 1, 3) -} - -func (x *GetKeyRequest) SetNonce(v string) { - x.xxx_hidden_Nonce = &v - protoimpl.X.SetPresent(&(x.XXX_presence[0]), 2, 3) -} - -func (x *GetKeyRequest) HasMachineId() bool { +func (x *GetKeyRequest) HasSessionId() bool { if x == nil { return false } return protoimpl.X.Present(&(x.XXX_presence[0]), 0) } -func (x *GetKeyRequest) HasSignature() bool { - if x == nil { - return false - } - return protoimpl.X.Present(&(x.XXX_presence[0]), 1) -} - -func (x *GetKeyRequest) HasNonce() bool { - if x == nil { - return false - } - return protoimpl.X.Present(&(x.XXX_presence[0]), 2) -} - -func (x *GetKeyRequest) ClearMachineId() { +func (x *GetKeyRequest) ClearSessionId() { protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0) - x.xxx_hidden_MachineId = nil -} - -func (x *GetKeyRequest) ClearSignature() { - protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 1) - x.xxx_hidden_Signature = nil -} - -func (x *GetKeyRequest) ClearNonce() { - protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 2) - x.xxx_hidden_Nonce = nil + x.xxx_hidden_SessionId = nil } type GetKeyRequest_builder struct { _ [0]func() // Prevents comparability and use of unkeyed literals for the builder. - MachineId *string - Signature []byte - Nonce *string + SessionId *string } func (b0 GetKeyRequest_builder) Build() *GetKeyRequest { m0 := &GetKeyRequest{} b, x := &b0, m0 _, _ = b, x - if b.MachineId != nil { - protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 3) - x.xxx_hidden_MachineId = b.MachineId - } - if b.Signature != nil { - protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 1, 3) - x.xxx_hidden_Signature = b.Signature - } - if b.Nonce != nil { - protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 2, 3) - x.xxx_hidden_Nonce = b.Nonce + if b.SessionId != nil { + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 1) + x.xxx_hidden_SessionId = b.SessionId } return m0 } type GetKeyResponse struct { state protoimpl.MessageState `protogen:"opaque.v1"` - xxx_hidden_Key []byte `protobuf:"bytes,1,opt,name=key"` + xxx_hidden_EncKey []byte `protobuf:"bytes,1,opt,name=enc_key,json=encKey"` XXX_raceDetectHookData protoimpl.RaceDetectHookData XXX_presence [1]uint32 unknownFields protoimpl.UnknownFields @@ -200,50 +133,639 @@ func (x *GetKeyResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -func (x *GetKeyResponse) GetKey() []byte { +func (x *GetKeyResponse) GetEncKey() []byte { if x != nil { - return x.xxx_hidden_Key + return x.xxx_hidden_EncKey } return nil } -func (x *GetKeyResponse) SetKey(v []byte) { +func (x *GetKeyResponse) SetEncKey(v []byte) { if v == nil { v = []byte{} } - x.xxx_hidden_Key = v + x.xxx_hidden_EncKey = v protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 1) } -func (x *GetKeyResponse) HasKey() bool { +func (x *GetKeyResponse) HasEncKey() bool { if x == nil { return false } return protoimpl.X.Present(&(x.XXX_presence[0]), 0) } -func (x *GetKeyResponse) ClearKey() { +func (x *GetKeyResponse) ClearEncKey() { protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0) - x.xxx_hidden_Key = nil + x.xxx_hidden_EncKey = nil } type GetKeyResponse_builder struct { _ [0]func() // Prevents comparability and use of unkeyed literals for the builder. - Key []byte + EncKey []byte } func (b0 GetKeyResponse_builder) Build() *GetKeyResponse { m0 := &GetKeyResponse{} b, x := &b0, m0 _, _ = b, x - if b.Key != nil { + if b.EncKey != nil { protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 1) - x.xxx_hidden_Key = b.Key + x.xxx_hidden_EncKey = b.EncKey } return m0 } +type CreateSessionRequest struct { + state protoimpl.MessageState `protogen:"opaque.v1"` + xxx_hidden_MachineId *string `protobuf:"bytes,1,opt,name=machine_id,json=machineId"` + xxx_hidden_Signature []byte `protobuf:"bytes,2,opt,name=signature"` + xxx_hidden_Nonce *string `protobuf:"bytes,3,opt,name=nonce"` + XXX_raceDetectHookData protoimpl.RaceDetectHookData + XXX_presence [1]uint32 + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateSessionRequest) Reset() { + *x = CreateSessionRequest{} + mi := &file_rgst_klefki_v1_kelfki_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateSessionRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateSessionRequest) ProtoMessage() {} + +func (x *CreateSessionRequest) ProtoReflect() protoreflect.Message { + mi := &file_rgst_klefki_v1_kelfki_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +func (x *CreateSessionRequest) GetMachineId() string { + if x != nil { + if x.xxx_hidden_MachineId != nil { + return *x.xxx_hidden_MachineId + } + return "" + } + return "" +} + +func (x *CreateSessionRequest) GetSignature() []byte { + if x != nil { + return x.xxx_hidden_Signature + } + return nil +} + +func (x *CreateSessionRequest) GetNonce() string { + if x != nil { + if x.xxx_hidden_Nonce != nil { + return *x.xxx_hidden_Nonce + } + return "" + } + return "" +} + +func (x *CreateSessionRequest) SetMachineId(v string) { + x.xxx_hidden_MachineId = &v + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 3) +} + +func (x *CreateSessionRequest) SetSignature(v []byte) { + if v == nil { + v = []byte{} + } + x.xxx_hidden_Signature = v + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 1, 3) +} + +func (x *CreateSessionRequest) SetNonce(v string) { + x.xxx_hidden_Nonce = &v + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 2, 3) +} + +func (x *CreateSessionRequest) HasMachineId() bool { + if x == nil { + return false + } + return protoimpl.X.Present(&(x.XXX_presence[0]), 0) +} + +func (x *CreateSessionRequest) HasSignature() bool { + if x == nil { + return false + } + return protoimpl.X.Present(&(x.XXX_presence[0]), 1) +} + +func (x *CreateSessionRequest) HasNonce() bool { + if x == nil { + return false + } + return protoimpl.X.Present(&(x.XXX_presence[0]), 2) +} + +func (x *CreateSessionRequest) ClearMachineId() { + protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0) + x.xxx_hidden_MachineId = nil +} + +func (x *CreateSessionRequest) ClearSignature() { + protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 1) + x.xxx_hidden_Signature = nil +} + +func (x *CreateSessionRequest) ClearNonce() { + protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 2) + x.xxx_hidden_Nonce = nil +} + +type CreateSessionRequest_builder struct { + _ [0]func() // Prevents comparability and use of unkeyed literals for the builder. + + MachineId *string + Signature []byte + Nonce *string +} + +func (b0 CreateSessionRequest_builder) Build() *CreateSessionRequest { + m0 := &CreateSessionRequest{} + b, x := &b0, m0 + _, _ = b, x + if b.MachineId != nil { + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 3) + x.xxx_hidden_MachineId = b.MachineId + } + if b.Signature != nil { + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 1, 3) + x.xxx_hidden_Signature = b.Signature + } + if b.Nonce != nil { + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 2, 3) + x.xxx_hidden_Nonce = b.Nonce + } + return m0 +} + +type CreateSessionResponse struct { + state protoimpl.MessageState `protogen:"opaque.v1"` + xxx_hidden_EncSessionId []byte `protobuf:"bytes,1,opt,name=enc_session_id,json=encSessionId"` + XXX_raceDetectHookData protoimpl.RaceDetectHookData + XXX_presence [1]uint32 + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateSessionResponse) Reset() { + *x = CreateSessionResponse{} + mi := &file_rgst_klefki_v1_kelfki_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateSessionResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateSessionResponse) ProtoMessage() {} + +func (x *CreateSessionResponse) ProtoReflect() protoreflect.Message { + mi := &file_rgst_klefki_v1_kelfki_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +func (x *CreateSessionResponse) GetEncSessionId() []byte { + if x != nil { + return x.xxx_hidden_EncSessionId + } + return nil +} + +func (x *CreateSessionResponse) SetEncSessionId(v []byte) { + if v == nil { + v = []byte{} + } + x.xxx_hidden_EncSessionId = v + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 1) +} + +func (x *CreateSessionResponse) HasEncSessionId() bool { + if x == nil { + return false + } + return protoimpl.X.Present(&(x.XXX_presence[0]), 0) +} + +func (x *CreateSessionResponse) ClearEncSessionId() { + protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0) + x.xxx_hidden_EncSessionId = nil +} + +type CreateSessionResponse_builder struct { + _ [0]func() // Prevents comparability and use of unkeyed literals for the builder. + + EncSessionId []byte +} + +func (b0 CreateSessionResponse_builder) Build() *CreateSessionResponse { + m0 := &CreateSessionResponse{} + b, x := &b0, m0 + _, _ = b, x + if b.EncSessionId != nil { + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 1) + x.xxx_hidden_EncSessionId = b.EncSessionId + } + return m0 +} + +type ListSessionsRequest struct { + state protoimpl.MessageState `protogen:"opaque.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListSessionsRequest) Reset() { + *x = ListSessionsRequest{} + mi := &file_rgst_klefki_v1_kelfki_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListSessionsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListSessionsRequest) ProtoMessage() {} + +func (x *ListSessionsRequest) ProtoReflect() protoreflect.Message { + mi := &file_rgst_klefki_v1_kelfki_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +type ListSessionsRequest_builder struct { + _ [0]func() // Prevents comparability and use of unkeyed literals for the builder. + +} + +func (b0 ListSessionsRequest_builder) Build() *ListSessionsRequest { + m0 := &ListSessionsRequest{} + b, x := &b0, m0 + _, _ = b, x + return m0 +} + +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_raceDetectHookData protoimpl.RaceDetectHookData + XXX_presence [1]uint32 + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Machine) Reset() { + *x = Machine{} + mi := &file_rgst_klefki_v1_kelfki_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Machine) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Machine) ProtoMessage() {} + +func (x *Machine) ProtoReflect() protoreflect.Message { + mi := &file_rgst_klefki_v1_kelfki_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +func (x *Machine) GetId() string { + if x != nil { + if x.xxx_hidden_Id != nil { + return *x.xxx_hidden_Id + } + return "" + } + return "" +} + +func (x *Machine) GetPublicKey() []byte { + if x != nil { + return x.xxx_hidden_PublicKey + } + return nil +} + +func (x *Machine) SetId(v string) { + x.xxx_hidden_Id = &v + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 2) +} + +func (x *Machine) SetPublicKey(v []byte) { + if v == nil { + v = []byte{} + } + x.xxx_hidden_PublicKey = v + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 1, 2) +} + +func (x *Machine) HasId() bool { + if x == nil { + return false + } + return protoimpl.X.Present(&(x.XXX_presence[0]), 0) +} + +func (x *Machine) HasPublicKey() bool { + if x == nil { + return false + } + return protoimpl.X.Present(&(x.XXX_presence[0]), 1) +} + +func (x *Machine) ClearId() { + protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0) + x.xxx_hidden_Id = nil +} + +func (x *Machine) ClearPublicKey() { + protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 1) + x.xxx_hidden_PublicKey = nil +} + +type Machine_builder struct { + _ [0]func() // Prevents comparability and use of unkeyed literals for the builder. + + Id *string + PublicKey []byte +} + +func (b0 Machine_builder) Build() *Machine { + m0 := &Machine{} + b, x := &b0, m0 + _, _ = b, x + if b.Id != nil { + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 2) + x.xxx_hidden_Id = b.Id + } + if b.PublicKey != nil { + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 1, 2) + x.xxx_hidden_PublicKey = b.PublicKey + } + return m0 +} + +type ListSessionsResponse struct { + state protoimpl.MessageState `protogen:"opaque.v1"` + xxx_hidden_Machines *[]*Machine `protobuf:"bytes,1,rep,name=machines"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListSessionsResponse) Reset() { + *x = ListSessionsResponse{} + mi := &file_rgst_klefki_v1_kelfki_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListSessionsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListSessionsResponse) ProtoMessage() {} + +func (x *ListSessionsResponse) ProtoReflect() protoreflect.Message { + mi := &file_rgst_klefki_v1_kelfki_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +func (x *ListSessionsResponse) GetMachines() []*Machine { + if x != nil { + if x.xxx_hidden_Machines != nil { + return *x.xxx_hidden_Machines + } + } + return nil +} + +func (x *ListSessionsResponse) SetMachines(v []*Machine) { + x.xxx_hidden_Machines = &v +} + +type ListSessionsResponse_builder struct { + _ [0]func() // Prevents comparability and use of unkeyed literals for the builder. + + Machines []*Machine +} + +func (b0 ListSessionsResponse_builder) Build() *ListSessionsResponse { + m0 := &ListSessionsResponse{} + b, x := &b0, m0 + _, _ = b, x + x.xxx_hidden_Machines = &b.Machines + return m0 +} + +type SubmitKeyRequest struct { + state protoimpl.MessageState `protogen:"opaque.v1"` + xxx_hidden_MachineId *string `protobuf:"bytes,1,opt,name=machine_id,json=machineId"` + xxx_hidden_EncKey []byte `protobuf:"bytes,2,opt,name=enc_key,json=encKey"` + XXX_raceDetectHookData protoimpl.RaceDetectHookData + XXX_presence [1]uint32 + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SubmitKeyRequest) Reset() { + *x = SubmitKeyRequest{} + mi := &file_rgst_klefki_v1_kelfki_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SubmitKeyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitKeyRequest) ProtoMessage() {} + +func (x *SubmitKeyRequest) ProtoReflect() protoreflect.Message { + mi := &file_rgst_klefki_v1_kelfki_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +func (x *SubmitKeyRequest) GetMachineId() string { + if x != nil { + if x.xxx_hidden_MachineId != nil { + return *x.xxx_hidden_MachineId + } + return "" + } + return "" +} + +func (x *SubmitKeyRequest) GetEncKey() []byte { + if x != nil { + return x.xxx_hidden_EncKey + } + return nil +} + +func (x *SubmitKeyRequest) SetMachineId(v string) { + x.xxx_hidden_MachineId = &v + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 0, 2) +} + +func (x *SubmitKeyRequest) SetEncKey(v []byte) { + if v == nil { + v = []byte{} + } + x.xxx_hidden_EncKey = v + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 1, 2) +} + +func (x *SubmitKeyRequest) HasMachineId() bool { + if x == nil { + return false + } + return protoimpl.X.Present(&(x.XXX_presence[0]), 0) +} + +func (x *SubmitKeyRequest) HasEncKey() bool { + if x == nil { + return false + } + return protoimpl.X.Present(&(x.XXX_presence[0]), 1) +} + +func (x *SubmitKeyRequest) ClearMachineId() { + protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 0) + x.xxx_hidden_MachineId = nil +} + +func (x *SubmitKeyRequest) ClearEncKey() { + protoimpl.X.ClearPresent(&(x.XXX_presence[0]), 1) + x.xxx_hidden_EncKey = nil +} + +type SubmitKeyRequest_builder struct { + _ [0]func() // Prevents comparability and use of unkeyed literals for the builder. + + MachineId *string + EncKey []byte +} + +func (b0 SubmitKeyRequest_builder) Build() *SubmitKeyRequest { + m0 := &SubmitKeyRequest{} + b, x := &b0, m0 + _, _ = b, x + if b.MachineId != nil { + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 0, 2) + x.xxx_hidden_MachineId = b.MachineId + } + if b.EncKey != nil { + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 1, 2) + x.xxx_hidden_EncKey = b.EncKey + } + return m0 +} + +type SubmitKeyResponse struct { + state protoimpl.MessageState `protogen:"opaque.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SubmitKeyResponse) Reset() { + *x = SubmitKeyResponse{} + mi := &file_rgst_klefki_v1_kelfki_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SubmitKeyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SubmitKeyResponse) ProtoMessage() {} + +func (x *SubmitKeyResponse) ProtoReflect() protoreflect.Message { + mi := &file_rgst_klefki_v1_kelfki_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +type SubmitKeyResponse_builder struct { + _ [0]func() // Prevents comparability and use of unkeyed literals for the builder. + +} + +func (b0 SubmitKeyResponse_builder) Build() *SubmitKeyResponse { + m0 := &SubmitKeyResponse{} + b, x := &b0, m0 + _, _ = b, x + return m0 +} + var File_rgst_klefki_v1_kelfki_proto protoreflect.FileDescriptor var file_rgst_klefki_v1_kelfki_proto_rawDesc = string([]byte{ @@ -252,40 +774,95 @@ var file_rgst_klefki_v1_kelfki_proto_rawDesc = string([]byte{ 0x67, 0x73, 0x74, 0x2e, 0x6b, 0x6c, 0x65, 0x66, 0x6b, 0x69, 0x2e, 0x76, 0x31, 0x1a, 0x21, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x67, 0x6f, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x22, 0x62, 0x0a, 0x0d, 0x47, 0x65, 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, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x14, - 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6e, - 0x6f, 0x6e, 0x63, 0x65, 0x22, 0x22, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x32, 0x58, 0x0a, 0x0d, 0x4b, 0x6c, 0x65, 0x66, - 0x6b, 0x69, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 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, 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, 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, + 0x22, 0x2e, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, + 0x22, 0x29, 0x0a, 0x0e, 0x47, 0x65, 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, 0x69, 0x0a, 0x14, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 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, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, + 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x22, 0x3d, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x24, 0x0a, 0x0e, 0x65, 0x6e, 0x63, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x65, 0x6e, 0x63, 0x53, 0x65, 0x73, 0x73, + 0x69, 0x6f, 0x6e, 0x49, 0x64, 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, + 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, 0xe3, 0x02, 0x0a, 0x0d, 0x4b, 0x6c, 0x65, 0x66, 0x6b, 0x69, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x5c, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x24, 0x2e, 0x72, 0x67, 0x73, 0x74, 0x2e, 0x6b, + 0x6c, 0x65, 0x66, 0x6b, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, + 0x72, 0x67, 0x73, 0x74, 0x2e, 0x6b, 0x6c, 0x65, 0x66, 0x6b, 0x69, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 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, 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, 2) +var file_rgst_klefki_v1_kelfki_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_rgst_klefki_v1_kelfki_proto_goTypes = []any{ - (*GetKeyRequest)(nil), // 0: rgst.klefki.v1.GetKeyRequest - (*GetKeyResponse)(nil), // 1: rgst.klefki.v1.GetKeyResponse + (*GetKeyRequest)(nil), // 0: rgst.klefki.v1.GetKeyRequest + (*GetKeyResponse)(nil), // 1: rgst.klefki.v1.GetKeyResponse + (*CreateSessionRequest)(nil), // 2: rgst.klefki.v1.CreateSessionRequest + (*CreateSessionResponse)(nil), // 3: rgst.klefki.v1.CreateSessionResponse + (*ListSessionsRequest)(nil), // 4: rgst.klefki.v1.ListSessionsRequest + (*Machine)(nil), // 5: rgst.klefki.v1.Machine + (*ListSessionsResponse)(nil), // 6: rgst.klefki.v1.ListSessionsResponse + (*SubmitKeyRequest)(nil), // 7: rgst.klefki.v1.SubmitKeyRequest + (*SubmitKeyResponse)(nil), // 8: rgst.klefki.v1.SubmitKeyResponse } var file_rgst_klefki_v1_kelfki_proto_depIdxs = []int32{ - 0, // 0: rgst.klefki.v1.KlefkiService.GetKey:input_type -> rgst.klefki.v1.GetKeyRequest - 1, // 1: rgst.klefki.v1.KlefkiService.GetKey:output_type -> rgst.klefki.v1.GetKeyResponse - 1, // [1:2] is the sub-list for method output_type - 0, // [0:1] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name + 5, // 0: rgst.klefki.v1.ListSessionsResponse.machines:type_name -> rgst.klefki.v1.Machine + 2, // 1: rgst.klefki.v1.KlefkiService.CreateSession:input_type -> rgst.klefki.v1.CreateSessionRequest + 0, // 2: rgst.klefki.v1.KlefkiService.GetKey:input_type -> rgst.klefki.v1.GetKeyRequest + 4, // 3: rgst.klefki.v1.KlefkiService.ListSessions:input_type -> rgst.klefki.v1.ListSessionsRequest + 7, // 4: rgst.klefki.v1.KlefkiService.SubmitKey:input_type -> rgst.klefki.v1.SubmitKeyRequest + 3, // 5: rgst.klefki.v1.KlefkiService.CreateSession:output_type -> rgst.klefki.v1.CreateSessionResponse + 1, // 6: rgst.klefki.v1.KlefkiService.GetKey:output_type -> rgst.klefki.v1.GetKeyResponse + 6, // 7: rgst.klefki.v1.KlefkiService.ListSessions:output_type -> rgst.klefki.v1.ListSessionsResponse + 8, // 8: rgst.klefki.v1.KlefkiService.SubmitKey:output_type -> rgst.klefki.v1.SubmitKeyResponse + 5, // [5:9] is the sub-list for method output_type + 1, // [1:5] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name } func init() { file_rgst_klefki_v1_kelfki_proto_init() } @@ -299,7 +876,7 @@ func file_rgst_klefki_v1_kelfki_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_rgst_klefki_v1_kelfki_proto_rawDesc), len(file_rgst_klefki_v1_kelfki_proto_rawDesc)), NumEnums: 0, - NumMessages: 2, + NumMessages: 9, NumExtensions: 0, NumServices: 1, }, 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 5514b65..647099c 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,7 +8,6 @@ package v1 import ( context "context" - grpc "google.golang.org/grpc" codes "google.golang.org/grpc/codes" status "google.golang.org/grpc/status" @@ -20,14 +19,20 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - KlefkiService_GetKey_FullMethodName = "/rgst.klefki.v1.KlefkiService/GetKey" + KlefkiService_CreateSession_FullMethodName = "/rgst.klefki.v1.KlefkiService/CreateSession" + KlefkiService_GetKey_FullMethodName = "/rgst.klefki.v1.KlefkiService/GetKey" + KlefkiService_ListSessions_FullMethodName = "/rgst.klefki.v1.KlefkiService/ListSessions" + KlefkiService_SubmitKey_FullMethodName = "/rgst.klefki.v1.KlefkiService/SubmitKey" ) // KlefkiServiceClient is the client API for KlefkiService service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. type KlefkiServiceClient interface { + CreateSession(ctx context.Context, in *CreateSessionRequest, opts ...grpc.CallOption) (*CreateSessionResponse, error) GetKey(ctx context.Context, in *GetKeyRequest, opts ...grpc.CallOption) (*GetKeyResponse, error) + ListSessions(ctx context.Context, in *ListSessionsRequest, opts ...grpc.CallOption) (*ListSessionsResponse, error) + SubmitKey(ctx context.Context, in *SubmitKeyRequest, opts ...grpc.CallOption) (*SubmitKeyResponse, error) } type klefkiServiceClient struct { @@ -38,6 +43,16 @@ func NewKlefkiServiceClient(cc grpc.ClientConnInterface) KlefkiServiceClient { return &klefkiServiceClient{cc} } +func (c *klefkiServiceClient) CreateSession(ctx context.Context, in *CreateSessionRequest, opts ...grpc.CallOption) (*CreateSessionResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreateSessionResponse) + err := c.cc.Invoke(ctx, KlefkiService_CreateSession_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *klefkiServiceClient) GetKey(ctx context.Context, in *GetKeyRequest, opts ...grpc.CallOption) (*GetKeyResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(GetKeyResponse) @@ -48,11 +63,34 @@ func (c *klefkiServiceClient) GetKey(ctx context.Context, in *GetKeyRequest, opt return out, nil } +func (c *klefkiServiceClient) ListSessions(ctx context.Context, in *ListSessionsRequest, opts ...grpc.CallOption) (*ListSessionsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListSessionsResponse) + err := c.cc.Invoke(ctx, KlefkiService_ListSessions_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *klefkiServiceClient) SubmitKey(ctx context.Context, in *SubmitKeyRequest, opts ...grpc.CallOption) (*SubmitKeyResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SubmitKeyResponse) + err := c.cc.Invoke(ctx, KlefkiService_SubmitKey_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // KlefkiServiceServer is the server API for KlefkiService service. // All implementations must embed UnimplementedKlefkiServiceServer // for forward compatibility. type KlefkiServiceServer interface { + CreateSession(context.Context, *CreateSessionRequest) (*CreateSessionResponse, error) GetKey(context.Context, *GetKeyRequest) (*GetKeyResponse, error) + ListSessions(context.Context, *ListSessionsRequest) (*ListSessionsResponse, error) + SubmitKey(context.Context, *SubmitKeyRequest) (*SubmitKeyResponse, error) mustEmbedUnimplementedKlefkiServiceServer() } @@ -63,9 +101,18 @@ type KlefkiServiceServer interface { // pointer dereference when methods are called. type UnimplementedKlefkiServiceServer struct{} +func (UnimplementedKlefkiServiceServer) CreateSession(context.Context, *CreateSessionRequest) (*CreateSessionResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateSession not implemented") +} func (UnimplementedKlefkiServiceServer) GetKey(context.Context, *GetKeyRequest) (*GetKeyResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetKey not implemented") } +func (UnimplementedKlefkiServiceServer) ListSessions(context.Context, *ListSessionsRequest) (*ListSessionsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method ListSessions not implemented") +} +func (UnimplementedKlefkiServiceServer) SubmitKey(context.Context, *SubmitKeyRequest) (*SubmitKeyResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SubmitKey not implemented") +} func (UnimplementedKlefkiServiceServer) mustEmbedUnimplementedKlefkiServiceServer() {} func (UnimplementedKlefkiServiceServer) testEmbeddedByValue() {} @@ -87,6 +134,24 @@ func RegisterKlefkiServiceServer(s grpc.ServiceRegistrar, srv KlefkiServiceServe s.RegisterService(&KlefkiService_ServiceDesc, srv) } +func _KlefkiService_CreateSession_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateSessionRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(KlefkiServiceServer).CreateSession(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: KlefkiService_CreateSession_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(KlefkiServiceServer).CreateSession(ctx, req.(*CreateSessionRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _KlefkiService_GetKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(GetKeyRequest) if err := dec(in); err != nil { @@ -105,6 +170,42 @@ func _KlefkiService_GetKey_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } +func _KlefkiService_ListSessions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListSessionsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(KlefkiServiceServer).ListSessions(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: KlefkiService_ListSessions_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(KlefkiServiceServer).ListSessions(ctx, req.(*ListSessionsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _KlefkiService_SubmitKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(SubmitKeyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(KlefkiServiceServer).SubmitKey(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: KlefkiService_SubmitKey_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(KlefkiServiceServer).SubmitKey(ctx, req.(*SubmitKeyRequest)) + } + return interceptor(ctx, in, info, handler) +} + // KlefkiService_ServiceDesc is the grpc.ServiceDesc for KlefkiService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -112,10 +213,22 @@ var KlefkiService_ServiceDesc = grpc.ServiceDesc{ ServiceName: "rgst.klefki.v1.KlefkiService", HandlerType: (*KlefkiServiceServer)(nil), Methods: []grpc.MethodDesc{ + { + MethodName: "CreateSession", + Handler: _KlefkiService_CreateSession_Handler, + }, { MethodName: "GetKey", Handler: _KlefkiService_GetKey_Handler, }, + { + MethodName: "ListSessions", + Handler: _KlefkiService_ListSessions_Handler, + }, + { + MethodName: "SubmitKey", + Handler: _KlefkiService_SubmitKey_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "rgst/klefki/v1/kelfki.proto", diff --git a/internal/server/grpc/proto/rgst/klefki/v1/kelfki.proto b/internal/server/grpc/proto/rgst/klefki/v1/kelfki.proto index 9a996f5..a4b4960 100644 --- a/internal/server/grpc/proto/rgst/klefki/v1/kelfki.proto +++ b/internal/server/grpc/proto/rgst/klefki/v1/kelfki.proto @@ -8,15 +8,44 @@ option features.(pb.go).api_level = API_OPAQUE; option go_package = "git.rgst.io/internal/grpc/generated/go/rgst/klefki/v1"; message GetKeyRequest { + string session_id = 1; +} + +message GetKeyResponse { + bytes enc_key = 1; +} + +message CreateSessionRequest { string machine_id = 1; bytes signature = 2; string nonce = 3; } -message GetKeyResponse { - bytes key = 1; +message CreateSessionResponse { + bytes enc_session_id = 1; } -service KlefkiService { - rpc GetKey(GetKeyRequest) returns (GetKeyResponse); +message ListSessionsRequest {} + +message Machine { + string id = 1; + bytes public_key = 2; +} + +message ListSessionsResponse { + repeated Machine machines = 1; +} + +message SubmitKeyRequest { + string machine_id = 1; + bytes enc_key = 2; +} + +message SubmitKeyResponse {} + +service KlefkiService { + rpc CreateSession(CreateSessionRequest) returns (CreateSessionResponse); + rpc GetKey(GetKeyRequest) returns (GetKeyResponse); + rpc ListSessions(ListSessionsRequest) returns (ListSessionsResponse); + rpc SubmitKey(SubmitKeyRequest) returns (SubmitKeyResponse); } diff --git a/internal/server/server.go b/internal/server/server.go index ea3cc52..f418889 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -25,12 +25,15 @@ import ( "io" "net" "strings" + "sync" + "time" "git.rgst.io/homelab/klefki/internal/db" "git.rgst.io/homelab/klefki/internal/db/ent" "git.rgst.io/homelab/klefki/internal/machines" pbgrpcv1 "git.rgst.io/homelab/klefki/internal/server/grpc/generated/go/rgst/klefki/v1" "git.rgst.io/homelab/sigtool/v3/sign" + "github.com/google/uuid" "google.golang.org/grpc" "google.golang.org/grpc/reflection" ) @@ -50,15 +53,35 @@ func newNopWriteCloser(w io.Writer) *nopWriteCloser { return &nopWriteCloser{w} } +type Session struct { + // CreatedAt is when the provided session was created. Should be in + // UTC. + CreatedAt time.Time + + // ID is the session ID, this is used as an authenticate gate. + ID uuid.UUID + + // EncKey is the encrypted provided by SubmitKey. If not set, no key + // has been provided. + EncKey []byte +} + // Server is a Klefki gRPC server type Server struct { gs *grpc.Server db *ent.Client + + // ses is a machine_id -> Session map + ses map[string]*Session + sesMu sync.Mutex + pbgrpcv1.UnimplementedKlefkiServiceServer } // Run starts the server func (s *Server) Run(ctx context.Context) error { + s.ses = make(map[string]*Session) + var err error s.db, err = db.New(ctx) if err != nil { @@ -78,9 +101,9 @@ func (s *Server) Run(ctx context.Context) error { return s.gs.Serve(lis) } -// GetKey implements the GetKey request -func (s *Server) GetKey(ctx context.Context, req *pbgrpcv1.GetKeyRequest) (*pbgrpcv1.GetKeyResponse, error) { - resp := &pbgrpcv1.GetKeyResponse{} +// CreateSession implements the CreateSession RPC +func (s *Server) CreateSession(ctx context.Context, req *pbgrpcv1.CreateSessionRequest) (*pbgrpcv1.CreateSessionResponse, error) { + resp := &pbgrpcv1.CreateSessionResponse{} nonce := req.GetNonce() sig := req.GetSignature() @@ -108,13 +131,39 @@ 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) } - // TODO(jaredallard): Wait for input here. + sessionID := uuid.New() var buf bytes.Buffer - if err := enc.Encrypt(strings.NewReader("hello world"), newNopWriteCloser(&buf)); err != nil { + if err := enc.Encrypt(strings.NewReader(sessionID.String()), newNopWriteCloser(&buf)); err != nil { return nil, fmt.Errorf("failed to encrypt passphrase: %w", err) } + resp.SetEncSessionId(buf.Bytes()) - resp.SetKey(buf.Bytes()) + s.sesMu.Lock() + defer s.sesMu.Unlock() + + s.ses[machine.ID] = &Session{ + CreatedAt: time.Now().UTC(), + ID: sessionID, + } + + return resp, nil +} + +// ListSessions implements the ListSessions RPC. +func (s *Server) ListSessions(ctx context.Context, _ *pbgrpcv1.ListSessionsRequest) (*pbgrpcv1.ListSessionsResponse, error) { + resp := &pbgrpcv1.ListSessionsResponse{} + + grpcMachines := make([]*pbgrpcv1.Machine, 0, len(s.ses)) + for machineID := range s.ses { + machine, err := s.db.Machine.Get(ctx, machineID) + if err != nil { + return nil, fmt.Errorf("failed to get machine %q: %w", machineID, err) + } + + grpcMachines = append(grpcMachines, machines.GRPCMachine(machine)) + } + + resp.SetMachines(grpcMachines) return resp, nil }