github.com/nats-io/nsc/v2@v2.8.7-0.20240307184528-efd7023c6896/cmd/upgradejwt.go (about) 1 /* 2 * Copyright 2018-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 "archive/zip" 20 "errors" 21 "fmt" 22 "os" 23 "path/filepath" 24 "strings" 25 26 "github.com/nats-io/nkeys" 27 28 "github.com/nats-io/cliprompts/v2" 29 30 "github.com/nats-io/nsc/v2/cmd/store" 31 "github.com/spf13/cobra" 32 ) 33 34 func backup(file string, dir string) error { 35 fp, err := os.Create(file) 36 if err != nil { 37 return err 38 } 39 defer fp.Close() 40 w := zip.NewWriter(fp) 41 defer w.Close() 42 return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 43 if err != nil { 44 return err 45 } 46 if info.IsDir() { 47 return nil 48 } 49 wrtr, err := w.Create(strings.TrimPrefix(path, dir)) 50 if err != nil { 51 return err 52 } 53 dat, err := os.ReadFile(path) 54 if err != nil { 55 return err 56 } 57 _, err = wrtr.Write(dat) 58 if err != nil { 59 return err 60 } 61 return nil 62 }) 63 } 64 65 const upgradeQuestion = `Did you upgrade all nats-server and do you want to convert all non managed 66 operator / account / user jwt to V2? 67 Once converted you need to re-distribute the operator jwt wherever used. 68 This includes, but is not limited to: 69 ALL nats-server, which need to be restarted (one by one is ok) 70 ALL dependent nsc stores in managed mode 71 ` 72 73 func upgradeOperator(cmd *cobra.Command, s *store.Store, rep *store.Report) { 74 op, err := s.ReadOperatorClaim() 75 if err != nil { 76 rep.AddError(`Could not find Operator, please check your environment using "nsc env"`) 77 return 78 } 79 opName := op.Name 80 // check for Version > 2 happens in root 81 if op.Version == 2 { 82 rep.AddOK("Operator %s is already upgraded to version %d. No change was applied", opName, op.Version) 83 return 84 } 85 if s.IsManaged() { 86 cmd.Print(cliprompts.WrapString(80, upgradeQuestion)) 87 rep.AddError(`No change was made! Your store is in managed mode and was set up using: 88 "nsc add operator --force --url <file or url>"" 89 You need to contact "%s", obtain a V2 jwt and re issue the above command. 90 `, opName) 91 return 92 } 93 // obtain private identity key needed to recode the operator jwt 94 var opKp nkeys.KeyPair 95 if ctx, err := s.GetContext(); err != nil { 96 rep.AddError("Loading Operator Context %s failed: %v", opName, err) 97 } else { 98 opKp, err = ctx.KeyStore.GetKeyPair(op.Subject) 99 if opKp == nil { 100 errString := "not found" 101 if err != nil { 102 errString = fmt.Sprintf("failed with error: %v", err) 103 } 104 rep.AddError(`Identity Key for Operator %s %s. 105 This key needs to be present in order to rewrite the Operator to be v2. 106 If you intentionally removed it, you need to restore it for this command to work.`, opName, errString) 107 } 108 } 109 if opKp == nil { 110 return 111 } 112 if conv, _ := cliprompts.Confirm(cliprompts.WrapString(80, `It is advisable to create a backup of the store. 113 Do you want to create a backup in the form of a zip file now?`), true); conv { 114 dir, _ := os.Getwd() 115 zipFileDefault := filepath.Join(dir, fmt.Sprintf("%s-jwtV1-upgrade-backup.zip", opName)) 116 backupFile, err := cliprompts.Prompt("zip file name:", zipFileDefault) 117 if err != nil { 118 rep.AddError("Error obtaining file name") 119 return 120 } 121 if err = backup(backupFile, s.Dir); err != nil { 122 rep.AddError("Error creating backup zip file") 123 return 124 } 125 cmd.Print(cliprompts.WrapSprintf(80, `Backup file "%s" created. 126 This backup does `+cliprompts.Bold("not contain private nkeys")+`! 127 `+cliprompts.Bold("If you need to restore this state")+`: 128 Delete the directory "%s" 129 Extract the backup 130 Move the created directory in place of the deleted one 131 Downgrade nsc using: "nsc update -version 0.5.0" 132 `, backupFile, s.Dir)) 133 } 134 cmd.Print(cliprompts.WrapString(80, 135 `In order to use jwt V2, `+cliprompts.Bold("you must upgrade all nats-server")+` prior to usage! 136 If you are `+cliprompts.Bold("not ready")+` to switch over to jwt V2, downgrade nsc using: 137 "nsc update -version 0.5.0" 138 `)) 139 if conv, _ := cliprompts.Confirm(cliprompts.WrapString(80, upgradeQuestion), false); !conv { 140 rep.AddOK("Declined to convert at this time. Rerun command when ready.") 141 return 142 } 143 if newJwt, err := op.Encode(opKp); err != nil { 144 rep.AddError("Re-Encoding Operator jwt %s failed: %v", opName, err) 145 } else if _, err = s.StoreClaim([]byte(newJwt)); err != nil { 146 rep.AddError("Storing Re-Encoded Operator jwt %s failed: %v", opName, err) 147 } else { 148 rep.AddOK("Converted Operator: %s", opName) 149 } 150 } 151 152 func createUpgradeJwtCommand() *cobra.Command { 153 var cmd = &cobra.Command{ 154 Example: "nsc upgrade-jwt", 155 Use: "upgrade-jwt", 156 Short: "Update jwt w", 157 Args: MaxArgs(0), 158 Hidden: true, 159 RunE: func(cmd *cobra.Command, args []string) error { 160 config := GetConfig() 161 if config.StoreRoot == "" { 162 return errors.New("no store set - `env --store <dir>`") 163 } 164 rep := store.Report{Label: "Upgrade operator jwt to v2"} 165 s, err := GetStore() 166 if err != nil { 167 return err 168 } 169 upgradeOperator(cmd, s, &rep) 170 cmd.Print(rep.Message()) 171 return nil 172 }, 173 } 174 return cmd 175 } 176 177 func init() { 178 GetRootCmd().AddCommand(createUpgradeJwtCommand()) 179 }