github.com/kbehouse/nsc@v0.0.6/cmd/reissueoperator.go (about)

     1  /*
     2   * Copyright 2020-2020 The NATS Authors
     3   * Licensed under the Apache License, Version 2.0 (the "License");
     4   * you may not use this file except in compliance with the License.
     5   * You may obtain a copy of the License at
     6   *
     7   * http://www.apache.org/licenses/LICENSE-2.0
     8   *
     9   * Unless required by applicable law or agreed to in writing, software
    10   * distributed under the License is distributed on an "AS IS" BASIS,
    11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12   * See the License for the specific language governing permissions and
    13   * limitations under the License.
    14   */
    15  
    16  package cmd
    17  
    18  import (
    19  	"fmt"
    20  
    21  	"github.com/kbehouse/nsc/cmd/store"
    22  	"github.com/nats-io/nkeys"
    23  	"github.com/spf13/cobra"
    24  )
    25  
    26  func createReIssueOperatorCmd() *cobra.Command {
    27  	var params reIssueOperator
    28  	cmd := &cobra.Command{
    29  		Use:          "operator",
    30  		Short:        "Re-issues the operator with a new identity and re-signs affected accounts",
    31  		Example:      `nsc reissue operator`,
    32  		Args:         MaxArgs(0),
    33  		SilenceUsage: false,
    34  		RunE: func(cmd *cobra.Command, args []string) error {
    35  			if err := RunMaybeStorelessAction(cmd, args, &params); err != nil {
    36  				return err
    37  			}
    38  			return nil
    39  		},
    40  	}
    41  	cmd.Flags().BoolVarP(&params.turnIntoSigningKey, "convert-to-signing-key", "", false,
    42  		"turn operator identity key into signing key (avoids account re-signing)")
    43  	cmd.Flags().StringVarP(&params.name, "name", "n", "", "operator name")
    44  	return cmd
    45  }
    46  
    47  // removeCmd represents the resign command
    48  var reIssue = &cobra.Command{
    49  	Use:   "reissue",
    50  	Short: "Re-issue objects with a new identity key",
    51  }
    52  
    53  func init() {
    54  	reIssue.AddCommand(createReIssueOperatorCmd())
    55  	GetRootCmd().AddCommand(reIssue)
    56  }
    57  
    58  type reIssueOperator struct {
    59  	turnIntoSigningKey bool
    60  	name               string
    61  }
    62  
    63  func (p *reIssueOperator) PreInteractive(ctx ActionCtx) error {
    64  	return nil
    65  }
    66  
    67  func (p *reIssueOperator) Load(ctx ActionCtx) error {
    68  	return nil
    69  }
    70  
    71  func (p *reIssueOperator) PostInteractive(ctx ActionCtx) error {
    72  	return nil
    73  }
    74  
    75  func (p *reIssueOperator) SetDefaults(ctx ActionCtx) error {
    76  	return nil
    77  }
    78  
    79  func (p *reIssueOperator) Validate(ctx ActionCtx) error {
    80  	store := ctx.StoreCtx().Store
    81  	if p.name != "" {
    82  		var err error
    83  		store, err = GetStoreForOperator(p.name)
    84  		if err != nil {
    85  			return err
    86  		}
    87  	}
    88  	if store.IsManaged() {
    89  		return fmt.Errorf("resign is only supported in non managed stores")
    90  	}
    91  	if op, err := store.ReadOperatorClaim(); err != nil {
    92  		return err
    93  	} else if op.StrictSigningKeyUsage && !p.turnIntoSigningKey && len(op.SigningKeys) == 0 {
    94  		return fmt.Errorf("resign of strict operator is only supported with signing keys present")
    95  	}
    96  	return nil
    97  }
    98  
    99  func (p *reIssueOperator) Run(ctx ActionCtx) (store.Status, error) {
   100  	var err error
   101  	r := store.NewDetailedReport(true)
   102  	s := ctx.StoreCtx().Store
   103  	if p.name != "" {
   104  		if s, err = GetStoreForOperator(p.name); err != nil {
   105  			r.AddError("failed to load operator %s: %v", p.name, err)
   106  			return r, err
   107  		}
   108  	}
   109  	op, err := s.ReadOperatorClaim()
   110  	if err != nil {
   111  		r.AddError("failed to obtain operator: %v", err)
   112  		return r, err
   113  	}
   114  	opKp, err := nkeys.CreateOperator()
   115  	if err != nil {
   116  		r.AddError("failed to generate new operator identity: %v", err)
   117  		return r, err
   118  	}
   119  	accountSigningKey := opKp
   120  	if op.StrictSigningKeyUsage && !p.turnIntoSigningKey {
   121  		sp := SignerParams{kind: []nkeys.PrefixByte{nkeys.PrefixByteOperator}}
   122  		err = sp.Resolve(ctx)
   123  		if err != nil || sp.signerKP == nil {
   124  			r.AddError("failed to obtain signing key: %v", err)
   125  			return r, err
   126  		}
   127  		accountSigningKey = sp.signerKP
   128  	}
   129  	if _, err := ctx.StoreCtx().KeyStore.Store(opKp); err != nil {
   130  		r.AddError("failed to store new operator identity: %v", err)
   131  		return r, err
   132  	}
   133  	opPub, err := opKp.PublicKey()
   134  	if err != nil {
   135  		r.AddError("failed to obtain public key from new identity: %v", err)
   136  		return r, err
   137  	}
   138  	oldPubKey := op.Subject
   139  	if p.turnIntoSigningKey {
   140  		op.SigningKeys.Add(oldPubKey)
   141  	}
   142  	op.Subject = opPub
   143  	if opJWT, err := op.Encode(opKp); err != nil {
   144  		r.AddError("failed to encode new operator jwt: %v", err)
   145  		return r, err
   146  	} else if err := s.StoreRaw([]byte(opJWT)); err != nil {
   147  		r.AddError("failed to store new operator jwt: %v", err)
   148  		return r, err
   149  	}
   150  	r.AddOK("operator %q successfully changed identity to: %s", op.Name, opPub)
   151  	if p.turnIntoSigningKey {
   152  		r.AddOK("old operator key %q turned into signing key", opPub)
   153  		return r, nil
   154  	}
   155  	accounts, err := s.ListSubContainers(store.Accounts)
   156  	if err != nil {
   157  		r.AddError("failed to obtain accounts: %v", err)
   158  	}
   159  	for _, acName := range accounts {
   160  		claim, err := s.ReadAccountClaim(acName)
   161  		if err != nil {
   162  			r.AddError("failed reading account %s: %v", acName, err)
   163  		}
   164  		if claim.Issuer != oldPubKey {
   165  			r.AddOK("account %q already signed properly", acName)
   166  			continue
   167  		}
   168  		if accJWT, err := claim.Encode(accountSigningKey); err != nil {
   169  			r.AddError("account %q error during encoding: %v", acName, err)
   170  		} else if err := s.StoreRaw([]byte(accJWT)); err != nil {
   171  			r.AddError("failed to store new account jwt for account %q: %v", acName, err)
   172  		}
   173  		r.AddOK("account %q re-signed", acName)
   174  	}
   175  	return r, nil
   176  }