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