github.com/nats-io/nsc/v2@v2.8.7-0.20240307184528-efd7023c6896/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/nats-io/nkeys" 22 "github.com/nats-io/nsc/v2/cmd/store" 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, ¶ms); err != nil { 36 return err 37 } 38 return nil 39 }, 40 } 41 cmd.Flags().BoolVarP(¶ms.turnIntoSigningKey, "convert-to-signing-key", "", false, 42 "turn operator identity key into signing key (avoids account re-signing)") 43 cmd.Flags().StringVarP(¶ms.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 GetRootCmd().AddCommand(reIssue) 55 reIssue.AddCommand(createReIssueOperatorCmd()) 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", oldPubKey) 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 }