diff --git a/cmd/headscale/cli/preauthkeys.go b/cmd/headscale/cli/preauthkeys.go index eb9d182c..c164610f 100644 --- a/cmd/headscale/cli/preauthkeys.go +++ b/cmd/headscale/cli/preauthkeys.go @@ -19,6 +19,7 @@ func init() { } preauthkeysCmd.AddCommand(listPreAuthKeys) preauthkeysCmd.AddCommand(createPreAuthKeyCmd) + preauthkeysCmd.AddCommand(expirePreAuthKeyCmd) createPreAuthKeyCmd.PersistentFlags().Bool("reusable", false, "Make the preauthkey reusable") createPreAuthKeyCmd.PersistentFlags().Bool("ephemeral", false, "Preauthkey for ephemeral nodes") createPreAuthKeyCmd.Flags().StringP("expiration", "e", "", "Human-readable expiration of the key (30m, 24h, 365d...)") @@ -119,3 +120,42 @@ var createPreAuthKeyCmd = &cobra.Command{ fmt.Printf("Key: %s\n", k.Key) }, } + +var expirePreAuthKeyCmd = &cobra.Command{ + Use: "expire", + Short: "Expire a preauthkey", + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return fmt.Errorf("missing parameters") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + n, err := cmd.Flags().GetString("namespace") + if err != nil { + log.Fatalf("Error getting namespace: %s", err) + } + o, _ := cmd.Flags().GetString("output") + + h, err := getHeadscaleApp() + if err != nil { + log.Fatalf("Error initializing: %s", err) + } + + k, err := h.GetPreAuthKey(n, args[0]) + if err != nil { + log.Fatalf("Error getting the key: %s", err) + } + + err = h.MarkExpirePreAuthKey(k) + if strings.HasPrefix(o, "json") { + JsonOutput(k, err, o) + return + } + if err != nil { + fmt.Println(err) + return + } + fmt.Println("Expired") + }, +} diff --git a/preauth_keys.go b/preauth_keys.go index 7cffceae..cc849fc0 100644 --- a/preauth_keys.go +++ b/preauth_keys.go @@ -67,6 +67,28 @@ func (h *Headscale) GetPreAuthKeys(namespaceName string) (*[]PreAuthKey, error) return &keys, nil } +// GetPreAuthKey returns a PreAuthKey for a given key +func (h *Headscale) GetPreAuthKey(namespace string, key string) (*PreAuthKey, error) { + pak, err := h.checkKeyValidity(key) + if err != nil { + return nil, err + } + + if pak.Namespace.Name != namespace { + return nil, errors.New("Namespace mismatch") + } + + return pak, nil +} + +// MarkExpirePreAuthKey marks a PreAuthKey as expired +func (h *Headscale) MarkExpirePreAuthKey(k *PreAuthKey) error { + if err := h.db.Model(&k).Update("Expiration", time.Now()).Error; err != nil { + return err + } + return nil +} + // checkKeyValidity does the heavy lifting for validation of the PreAuthKey coming from a node // If returns no error and a PreAuthKey, it can be used func (h *Headscale) checkKeyValidity(k string) (*PreAuthKey, error) { diff --git a/preauth_keys_test.go b/preauth_keys_test.go index 6f1369c5..37f2e4dd 100644 --- a/preauth_keys_test.go +++ b/preauth_keys_test.go @@ -163,3 +163,20 @@ func (*Suite) TestEphemeralKey(c *check.C) { _, err = h.GetMachine("test7", "testest") c.Assert(err, check.NotNil) } + +func (*Suite) TestExpirePreauthKey(c *check.C) { + n, err := h.CreateNamespace("test3") + c.Assert(err, check.IsNil) + + pak, err := h.CreatePreAuthKey(n.Name, true, false, nil) + c.Assert(err, check.IsNil) + c.Assert(pak.Expiration, check.IsNil) + + err = h.MarkExpirePreAuthKey(pak) + c.Assert(err, check.IsNil) + c.Assert(pak.Expiration, check.NotNil) + + p, err := h.checkKeyValidity(pak.Key) + c.Assert(err, check.Equals, errorAuthKeyExpired) + c.Assert(p, check.IsNil) +}