github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/services/upgrader/upgrader_linux.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package upgrader 5 6 import ( 7 "archive/tar" 8 "bytes" 9 "compress/gzip" 10 "fmt" 11 "io" 12 "io/ioutil" 13 "net/http" 14 "os" 15 "os/user" 16 "path" 17 "runtime" 18 "strconv" 19 "strings" 20 "sync/atomic" 21 "syscall" 22 23 "github.com/pkg/errors" 24 "golang.org/x/crypto/openpgp" 25 26 "github.com/masterhung0112/hk_server/v5/model" 27 "github.com/masterhung0112/hk_server/v5/shared/mlog" 28 ) 29 30 const mattermostBuildPublicKey = `-----BEGIN PGP PUBLIC KEY BLOCK----- 31 32 mQENBFjZQxwBCAC6kNn3zDlq/aY83M9V7MHVPoK2jnZ3BfH7sA+ibQXsijCkPSR4 33 5bCUJ9qVA4XKGK+cpO9vkolSNs10igCaaemaUZNB6ksu3gT737/SZcCAfRO+cLX7 34 Q2la+jwTvu1YeT/M5xDZ1KHTFxsGskeIenz2rZHeuZwBl9qep34QszWtRX40eRts 35 fl6WltLrepiExTp6NMZ50k+Em4JGM6CWBMo22ucy0jYjZXO5hEGb3o6NGiG+Dx2z 36 b2J78LksCKGsSrn0F1rLJeA933bFL4g9ozv9asBlzmpgG77ESg6YE1N/Rh7WDzVA 37 prIR0MuB5JjElASw5LDVxDV6RZsxEVQr7ETLABEBAAG0KU1hdHRlcm1vc3QgQnVp 38 bGQgPGRldi1vcHNAbWF0dGVybW9zdC5jb20+iQFUBBMBCAA+AhsDBQsJCAcCBhUI 39 CQoLAgQWAgMBAh4BAheAFiEEobMdRvDzoQsCzy1E+PLDF0R3SygFAl6HYr0FCQlw 40 hqEACgkQ+PLDF0R3SyheNQgAnkiT2vFMCtU5FmC16HVYXzDpYMtdCQPh/gmeEkiI 41 80rFRg/cn6f0BNnaTfDu6r6cepmhLNpDAowjQ7uBnv8fL2dzCydIGFv2r7FfmcOJ 42 zhEQ3zXPwP6mYlxPCCgxAozsLv9Yv41KGCHIlzYwkAazc0BhpAW/h8L3VGkE+b+g 43 x6lKVoufm4rKnT49Dgly6fVOxuR/BqZo87B5jksV3izLTHt5hiY8Pc5GW8WwO/tr 44 pNAw+6HRXq1Dr/JRz5PIOr5KP5tVLBed4IteZ1xaTRd4++07ZbiZjhXY8WKpVp3y 45 iN7Om24jQpxbJI9+KKJ3+yhcwhr8/PJ8ZVuhJo3BNv1PcQ== 46 =9Qk8 47 -----END PGP PUBLIC KEY BLOCK-----` 48 49 var upgradePercentage int64 50 var upgradeError error 51 var upgrading int32 52 53 type writeCounter struct { 54 total int64 55 readed int64 56 } 57 58 func (wc *writeCounter) Write(p []byte) (int, error) { 59 n := len(p) 60 wc.readed += int64(n) 61 percentage := (wc.readed * 100) / wc.total 62 if percentage == 0 { 63 upgradePercentage = 1 64 } else if percentage == 100 { 65 upgradePercentage = 99 66 } else { 67 upgradePercentage = percentage 68 } 69 return n, nil 70 } 71 72 func getCurrentVersionTgzUrl() string { 73 version := model.CurrentVersion 74 if strings.HasPrefix(model.BuildNumber, version+"-rc") { 75 version = model.BuildNumber 76 } 77 78 return "https://releases.mattermost.com/" + version + "/mattermost-" + version + "-linux-amd64.tar.gz" 79 } 80 81 func verifySignature(filename string, sigfilename string, publicKey string) error { 82 keyring, err := openpgp.ReadArmoredKeyRing(bytes.NewReader([]byte(publicKey))) 83 if err != nil { 84 mlog.Debug("Unable to load the public key to verify the file signature", mlog.Err(err)) 85 return NewInvalidSignature() 86 } 87 88 mattermost_tar, err := os.Open(filename) 89 if err != nil { 90 mlog.Debug("Unable to open the Mattermost .tar file to verify the file signature", mlog.Err(err)) 91 return NewInvalidSignature() 92 } 93 94 signature, err := os.Open(sigfilename) 95 if err != nil { 96 mlog.Debug("Unable to open the Mattermost .sig file verify the file signature", mlog.Err(err)) 97 return NewInvalidSignature() 98 } 99 100 _, err = openpgp.CheckDetachedSignature(keyring, mattermost_tar, signature) 101 if err != nil { 102 mlog.Debug("Unable to verify the Mattermost file signature", mlog.Err(err)) 103 return NewInvalidSignature() 104 } 105 return nil 106 } 107 108 func canIWriteTheExecutable() error { 109 executablePath, err := os.Executable() 110 if err != nil { 111 return errors.New("error getting the path of the executable") 112 } 113 executableInfo, err := os.Stat(path.Dir(executablePath)) 114 if err != nil { 115 return errors.New("error getting the executable info") 116 } 117 stat, ok := executableInfo.Sys().(*syscall.Stat_t) 118 if !ok { 119 return errors.New("error getting the executable info") 120 } 121 fileUID := int(stat.Uid) 122 fileUser, err := user.LookupId(strconv.Itoa(fileUID)) 123 if err != nil { 124 return errors.New("error getting the executable info") 125 } 126 127 mattermostUID := os.Getuid() 128 mattermostUser, err := user.LookupId(strconv.Itoa(mattermostUID)) 129 if err != nil { 130 return errors.New("error getting the executable info") 131 } 132 133 mode := executableInfo.Mode() 134 if fileUID != mattermostUID && mode&(1<<1) == 0 && mode&(1<<7) == 0 { 135 return NewInvalidPermissions("invalid-user-and-permission", path.Dir(executablePath), mattermostUser.Username, fileUser.Username) 136 } 137 138 if fileUID != mattermostUID && mode&(1<<1) == 0 && mode&(1<<7) != 0 { 139 return NewInvalidPermissions("invalid-user", path.Dir(executablePath), mattermostUser.Username, fileUser.Username) 140 } 141 142 if fileUID == mattermostUID && mode&(1<<7) == 0 { 143 return NewInvalidPermissions("invalid-permission", path.Dir(executablePath), mattermostUser.Username, fileUser.Username) 144 } 145 return nil 146 } 147 148 func canIUpgrade() error { 149 if runtime.GOARCH != "amd64" { 150 return NewInvalidArch() 151 } 152 if runtime.GOOS != "linux" { 153 return NewInvalidArch() 154 } 155 return canIWriteTheExecutable() 156 } 157 158 func CanIUpgradeToE0() error { 159 if err := canIUpgrade(); err != nil { 160 return errors.Wrap(err, "unable to upgrade from TE to E0") 161 } 162 if model.BuildEnterpriseReady == "true" { 163 mlog.Warn("Unable to upgrade from TE to E0. The server is already running E0.") 164 return errors.New("you cannot upgrade your server from TE to E0 because you are already running Mattermost Enterprise Edition") 165 } 166 return nil 167 } 168 169 func UpgradeToE0() error { 170 if !atomic.CompareAndSwapInt32(&upgrading, 0, 1) { 171 mlog.Warn("Trying to upgrade while another upgrade is running") 172 return errors.New("another upgrade is already running") 173 } 174 defer atomic.CompareAndSwapInt32(&upgrading, 1, 0) 175 176 upgradePercentage = 1 177 upgradeError = nil 178 179 executablePath, err := os.Executable() 180 if err != nil { 181 upgradePercentage = 0 182 upgradeError = errors.New("error getting the executable path") 183 mlog.Error("Unable to get the path of the Mattermost executable", mlog.Err(err)) 184 return err 185 } 186 187 filename, err := download(getCurrentVersionTgzUrl(), 1024*1024*300) 188 if err != nil { 189 if filename != "" { 190 os.Remove(filename) 191 } 192 upgradeError = fmt.Errorf("error downloading the new HungKnow server binary file (percentage: %d)", upgradePercentage) 193 mlog.Error("Unable to download the HungKnow server binary file", mlog.Int64("percentage", upgradePercentage), mlog.String("url", getCurrentVersionTgzUrl()), mlog.Err(err)) 194 upgradePercentage = 0 195 return err 196 } 197 defer os.Remove(filename) 198 sigfilename, err := download(getCurrentVersionTgzUrl()+".sig", 1024) 199 if err != nil { 200 if sigfilename != "" { 201 os.Remove(sigfilename) 202 } 203 upgradeError = errors.New("error downloading the signature file of the new server") 204 mlog.Error("Unable to download the signature file of the new HungKnow server ", mlog.String("url", getCurrentVersionTgzUrl()+".sig"), mlog.Err(err)) 205 upgradePercentage = 0 206 return err 207 } 208 defer os.Remove(sigfilename) 209 210 err = verifySignature(filename, sigfilename, mattermostBuildPublicKey) 211 if err != nil { 212 upgradePercentage = 0 213 upgradeError = errors.New("unable to verify the signature of the downloaded file") 214 mlog.Error("Unable to verify the signature of the downloaded file", mlog.Err(err)) 215 return err 216 } 217 218 err = extractBinary(executablePath, filename) 219 if err != nil { 220 upgradePercentage = 0 221 upgradeError = err 222 mlog.Error("Unable to extract the binary from the downloaded file", mlog.Err(err)) 223 return err 224 } 225 upgradePercentage = 100 226 return nil 227 } 228 229 func UpgradeToE0Status() (int64, error) { 230 return upgradePercentage, upgradeError 231 } 232 233 func download(url string, limit int64) (string, error) { 234 resp, err := http.Get(url) 235 if err != nil { 236 return "", err 237 } 238 defer resp.Body.Close() 239 240 out, err := ioutil.TempFile("", "*_mattermost.tar.gz") 241 if err != nil { 242 return "", err 243 } 244 defer out.Close() 245 246 counter := &writeCounter{total: resp.ContentLength} 247 _, err = io.Copy(out, io.TeeReader(&io.LimitedReader{R: resp.Body, N: limit}, counter)) 248 return out.Name(), err 249 } 250 251 func getFilePermissionsOrDefault(filename string, def os.FileMode) os.FileMode { 252 file, err := os.Open(filename) 253 if err != nil { 254 mlog.Warn("Unable to get the file permissions", mlog.String("filename", filename), mlog.Err(err)) 255 return def 256 } 257 defer file.Close() 258 259 fileStats, err := file.Stat() 260 if err != nil { 261 mlog.Warn("Unable to get the file permissions", mlog.String("filename", filename), mlog.Err(err)) 262 return def 263 } 264 return fileStats.Mode() 265 } 266 267 func extractBinary(executablePath string, filename string) error { 268 gzipStream, err := os.Open(filename) 269 if err != nil { 270 return err 271 } 272 273 uncompressedStream, err := gzip.NewReader(gzipStream) 274 if err != nil { 275 return err 276 } 277 278 tarReader := tar.NewReader(uncompressedStream) 279 280 for { 281 header, err := tarReader.Next() 282 283 if err == io.EOF { 284 return errors.New("unable to find the Mattermost binary in the downloaded version") 285 } 286 287 if err != nil { 288 return err 289 } 290 291 if header.Typeflag == tar.TypeReg && header.Name == "mattermost/bin/mattermost" { 292 permissions := getFilePermissionsOrDefault(executablePath, 0755) 293 tmpFile, err := ioutil.TempFile(path.Dir(executablePath), "*") 294 if err != nil { 295 return err 296 } 297 tmpFileName := tmpFile.Name() 298 os.Remove(tmpFileName) 299 err = os.Rename(executablePath, tmpFileName) 300 if err != nil { 301 return err 302 } 303 outFile, err := os.Create(executablePath) 304 if err != nil { 305 err2 := os.Rename(tmpFileName, executablePath) 306 if err2 != nil { 307 mlog.Critical("Unable to restore the backup of the executable file. Restore the executable file manually.") 308 return errors.Wrap(err2, "critical error: unable to upgrade the binary or restore the old binary version. Please restore it manually") 309 } 310 return err 311 } 312 defer outFile.Close() 313 if _, err = io.Copy(outFile, tarReader); err != nil { 314 err2 := os.Remove(executablePath) 315 if err2 != nil { 316 mlog.Critical("Unable to restore the backup of the executable file. Restore the executable file manually.") 317 return errors.Wrap(err2, "critical error: unable to upgrade the binary or restore the old binary version. Please restore it manually") 318 } 319 320 err2 = os.Rename(tmpFileName, executablePath) 321 if err2 != nil { 322 mlog.Critical("Unable to restore the backup of the executable file. Restore the executable file manually.") 323 return errors.Wrap(err2, "critical error: unable to upgrade the binary or restore the old binary version. Please restore it manually") 324 } 325 return err 326 } 327 err = os.Remove(tmpFileName) 328 if err != nil { 329 mlog.Warn("Unable to clean up the binary backup file.", mlog.Err(err)) 330 } 331 err = os.Chmod(executablePath, permissions) 332 if err != nil { 333 mlog.Warn("Unable to set the correct permissions for the file.", mlog.Err(err)) 334 } 335 break 336 } 337 } 338 return nil 339 }