From 9451d7b74e55d59d382436791a4116d14db5ce65 Mon Sep 17 00:00:00 2001 From: Leon Zhang Date: Sat, 11 Apr 2020 00:18:04 +0800 Subject: [PATCH 1/4] add HiddenPasswordFromCmdline function for security when user input password from cli, hidden the password from ps --- go/base/utils.go | 26 ++++++++++++++++++++++++++ go/cmd/gh-ost/main.go | 1 + 2 files changed, 27 insertions(+) diff --git a/go/base/utils.go b/go/base/utils.go index 99536f82e..59a5111d7 100644 --- a/go/base/utils.go +++ b/go/base/utils.go @@ -9,10 +9,12 @@ import ( "fmt" "os" "regexp" + "runtime" "strings" "time" gosql "database/sql" + "github.com/ErikDubbelboer/gspt" "github.com/github/gh-ost/go/mysql" "github.com/outbrain/golib/log" ) @@ -94,3 +96,27 @@ func ValidateConnection(db *gosql.DB, connectionConfig *mysql.ConnectionConfig, return "", fmt.Errorf("Unexpected database port reported: %+v / extra_port: %+v", port, extraPort) } } + +// HiddenPasswordFromCmdline hidden password from `ps` command, Linux just change /proc/{pid}/cmdline +func HiddenPasswordFromCmdline() { + // third party package `gspt` use cgo, not cross platform available + switch runtime.GOOS { + case "linux", "darwin": + default: + return + } + // replace password with `xxx` + var cmdline []string + argsLength := len(os.Args) + for i, arg := range os.Args { + switch arg { + case "--password", "-password", "-master-password", "--master-password": + if i+1 < argsLength { + os.Args[i+1] = "xxx" + } + } + cmdline = append(cmdline, arg) + } + // call third party function set process's `/proc/{pid}/cmdline` + gspt.SetProcTitle(strings.Join(cmdline, " ")) +} diff --git a/go/cmd/gh-ost/main.go b/go/cmd/gh-ost/main.go index a467f077c..4f36ce3a3 100644 --- a/go/cmd/gh-ost/main.go +++ b/go/cmd/gh-ost/main.go @@ -134,6 +134,7 @@ func main() { flag.CommandLine.SetOutput(os.Stdout) flag.Parse() + base.HiddenPasswordFromCmdline() if *checkFlag { return From ba096a9cf26471f3476dbae492257d1cb1dacbef Mon Sep 17 00:00:00 2001 From: Leon Zhang Date: Sat, 11 Apr 2020 00:30:28 +0800 Subject: [PATCH 2/4] add vendor gspt --- vendor/github.com/ErikDubbelboer/gspt | 1 + 1 file changed, 1 insertion(+) create mode 160000 vendor/github.com/ErikDubbelboer/gspt diff --git a/vendor/github.com/ErikDubbelboer/gspt b/vendor/github.com/ErikDubbelboer/gspt new file mode 160000 index 000000000..e68493906 --- /dev/null +++ b/vendor/github.com/ErikDubbelboer/gspt @@ -0,0 +1 @@ +Subproject commit e68493906b8382891943ddc9960cb9c6ecd1a1f0 From e3ae912d97f3bf1e956676e659cc8e0a502af99e Mon Sep 17 00:00:00 2001 From: Leon Zhang Date: Sat, 11 Apr 2020 00:35:19 +0800 Subject: [PATCH 3/4] update vendor gspt --- vendor/github.com/ErikDubbelboer/gspt | 1 - .../ErikDubbelboer/gspt/.travis.yml | 4 + vendor/github.com/ErikDubbelboer/gspt/LICENSE | 20 ++ .../github.com/ErikDubbelboer/gspt/README.md | 28 ++ .../ErikDubbelboer/gspt/example/example.go | 51 +++ vendor/github.com/ErikDubbelboer/gspt/gspt.go | 71 ++++ .../ErikDubbelboer/gspt/gspt_test.go | 61 ++++ .../ErikDubbelboer/gspt/setproctitle.h | 307 ++++++++++++++++++ 8 files changed, 542 insertions(+), 1 deletion(-) delete mode 160000 vendor/github.com/ErikDubbelboer/gspt create mode 100644 vendor/github.com/ErikDubbelboer/gspt/.travis.yml create mode 100644 vendor/github.com/ErikDubbelboer/gspt/LICENSE create mode 100644 vendor/github.com/ErikDubbelboer/gspt/README.md create mode 100644 vendor/github.com/ErikDubbelboer/gspt/example/example.go create mode 100644 vendor/github.com/ErikDubbelboer/gspt/gspt.go create mode 100644 vendor/github.com/ErikDubbelboer/gspt/gspt_test.go create mode 100644 vendor/github.com/ErikDubbelboer/gspt/setproctitle.h diff --git a/vendor/github.com/ErikDubbelboer/gspt b/vendor/github.com/ErikDubbelboer/gspt deleted file mode 160000 index e68493906..000000000 --- a/vendor/github.com/ErikDubbelboer/gspt +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e68493906b8382891943ddc9960cb9c6ecd1a1f0 diff --git a/vendor/github.com/ErikDubbelboer/gspt/.travis.yml b/vendor/github.com/ErikDubbelboer/gspt/.travis.yml new file mode 100644 index 000000000..8d61700ec --- /dev/null +++ b/vendor/github.com/ErikDubbelboer/gspt/.travis.yml @@ -0,0 +1,4 @@ +language: go +go: + - 1.1 + - tip diff --git a/vendor/github.com/ErikDubbelboer/gspt/LICENSE b/vendor/github.com/ErikDubbelboer/gspt/LICENSE new file mode 100644 index 000000000..c718359d3 --- /dev/null +++ b/vendor/github.com/ErikDubbelboer/gspt/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013 Erik Dubbelboer + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/ErikDubbelboer/gspt/README.md b/vendor/github.com/ErikDubbelboer/gspt/README.md new file mode 100644 index 000000000..6e3c6fefe --- /dev/null +++ b/vendor/github.com/ErikDubbelboer/gspt/README.md @@ -0,0 +1,28 @@ +gspt +==== + +`setproctitle()` package for Go. + +[![Build Status](https://travis-ci.org/erikdubbelboer/gspt.png?branch=master)](https://travis-ci.org/erikdubbelboer/gspt) + +-------------------------------- + +Installation +------------ + +Simple install the package to your [$GOPATH](https://github.com/golang/go/wiki/GOPATH) with the [go tool](http://golang.org/cmd/go/): +```bash +go get github.com/erikdubbelboer/gspt +``` +Make sure [Git is installed](http://git-scm.com/downloads) on your machine and in your system's `PATH`. + +Usage +----- + +```go +import "github.com/erikdubbelboer/gspt" + +gspt.SetProcTitle("some title") +``` + +Please check the [documentation](http://godoc.org/github.com/erikdubbelboer/gspt) for more details. diff --git a/vendor/github.com/ErikDubbelboer/gspt/example/example.go b/vendor/github.com/ErikDubbelboer/gspt/example/example.go new file mode 100644 index 000000000..44ea81914 --- /dev/null +++ b/vendor/github.com/ErikDubbelboer/gspt/example/example.go @@ -0,0 +1,51 @@ +package main + +import ( + "fmt" + "strings" + "time" + + "os" + "os/exec" + + "github.com/erikdubbelboer/gspt" +) + +func main() { + fmt.Println("HaveSetProcTitle:", gspt.HaveSetProcTitle) + fmt.Println("HaveSetProcTitleFast:", gspt.HaveSetProcTitleFast) + + gspt.SetProcTitle("some title") + + fmt.Println("The title has been set, 'ps a' now shows:") + + out, err := exec.Command("ps", "aux").Output() + if err != nil { + // Could not execute 'ps'. + return + } + lines := strings.Split(string(out), "\n") + fmt.Println(lines[0]) + for _, line := range lines { + if strings.Contains(line, "some title") { + fmt.Println(line) + } + } + + fmt.Println("----") + fmt.Println("os.Environ() should still contain things like:") + fmt.Println("PATH =", os.Getenv("PATH")) + + fmt.Println("----") + fmt.Println("os.Args should still be correct:") + + for i, a := range os.Args { + fmt.Println(i, ":", a) + } + + fmt.Println("----") + fmt.Println("You can now check the output of 'ps a' or 'top'.") + fmt.Println("Press Ctrl+C to kill this program.") + + time.Sleep(time.Minute * 5) +} diff --git a/vendor/github.com/ErikDubbelboer/gspt/gspt.go b/vendor/github.com/ErikDubbelboer/gspt/gspt.go new file mode 100644 index 000000000..5cfdeeb6b --- /dev/null +++ b/vendor/github.com/ErikDubbelboer/gspt/gspt.go @@ -0,0 +1,71 @@ +package gspt + +/* + +#include "setproctitle.h" + +*/ +import "C" + +import ( + "os" + "strings" + "unsafe" +) + +const ( + // These values must match the return values for spt_init1() used in C. + HaveNone = 0 + HaveNative = 1 + HaveReplacement = 2 +) + +var ( + HaveSetProcTitle int + HaveSetProcTitleFast int +) + +func init() { + HaveSetProcTitle = int(C.spt_init1()) + HaveSetProcTitleFast = int(C.spt_fast_init1()) + + if HaveSetProcTitle == HaveReplacement { + newArgs := make([]string, len(os.Args)) + for i, s := range os.Args { + // Use cgo to force go to make copies of the strings. + cs := C.CString(s) + newArgs[i] = C.GoString(cs) + C.free(unsafe.Pointer(cs)) + } + os.Args = newArgs + + env := os.Environ() + for _, kv := range env { + skv := strings.SplitN(kv, "=", 2) + os.Setenv(skv[0], skv[1]) + } + + argc := C.int(len(os.Args)) + arg0 := C.CString(os.Args[0]) + defer C.free(unsafe.Pointer(arg0)) + + C.spt_init2(argc, arg0) + + // Restore the original title. + SetProcTitle(os.Args[0]) + } +} + +func SetProcTitle(title string) { + cs := C.CString(title) + defer C.free(unsafe.Pointer(cs)) + + C.spt_setproctitle(cs) +} + +func SetProcTitleFast(title string) { + cs := C.CString(title) + defer C.free(unsafe.Pointer(cs)) + + C.spt_setproctitle_fast(cs) +} diff --git a/vendor/github.com/ErikDubbelboer/gspt/gspt_test.go b/vendor/github.com/ErikDubbelboer/gspt/gspt_test.go new file mode 100644 index 000000000..79503217f --- /dev/null +++ b/vendor/github.com/ErikDubbelboer/gspt/gspt_test.go @@ -0,0 +1,61 @@ +package gspt + +import ( + "bytes" + "strings" + "testing" + "time" + + "encoding/binary" + "encoding/hex" + + "crypto/md5" + "os/exec" +) + +func randomMD5() string { + str := md5.New() + random := new(bytes.Buffer) + + binary.Write(random, binary.LittleEndian, time.Now().UTC().UnixNano()) + + str.Write(random.Bytes()) + + return hex.EncodeToString(str.Sum(nil)) +} + +func TestSetProcTitle(t *testing.T) { + if HaveSetProcTitle == HaveNone { + t.SkipNow() + } + + title := randomMD5() + + SetProcTitle(title) + + out, err := exec.Command("/bin/ps", "ax").Output() + if err != nil { + // No ps available on this platform. + t.SkipNow() + } else if !strings.Contains(string(out), title) { + t.FailNow() + } +} + +func TestSetProcTitleFast(t *testing.T) { + if HaveSetProcTitleFast == HaveNone { + t.SkipNow() + } + + title := randomMD5() + + SetProcTitleFast(title) + + out, err := exec.Command("/bin/ps", "ax").Output() + if err != nil { + // No ps available on this platform. + t.SkipNow() + } else if !strings.Contains(string(out), title) { + t.FailNow() + } +} diff --git a/vendor/github.com/ErikDubbelboer/gspt/setproctitle.h b/vendor/github.com/ErikDubbelboer/gspt/setproctitle.h new file mode 100644 index 000000000..ea92890d6 --- /dev/null +++ b/vendor/github.com/ErikDubbelboer/gspt/setproctitle.h @@ -0,0 +1,307 @@ +/* ========================================================================== + * setproctitle.h - Linux/Darwin setproctitle. + * -------------------------------------------------------------------------- + * Copyright (C) 2010 William Ahern + * Copyright (C) 2013 Salvatore Sanfilippo + * Copyright (C) 2013 Stam He + * Copyright (C) 2013 Erik Dubbelboer + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to permit + * persons to whom the Software is furnished to do so, subject to the + * following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * ========================================================================== + */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include // NULL size_t +#include // va_list va_start va_end +#include // malloc(3) setenv(3) clearenv(3) setproctitle(3) getprogname(3) +#include // vsnprintf(3) snprintf(3) +#include // strlen(3) strdup(3) memset(3) memcpy(3) +#include /* program_invocation_name program_invocation_short_name */ +#include /* os versions */ +#include /* freebsd setproctitle(3) */ +#include /* freebsd setproctitle(3) */ + +#if !defined(HAVE_SETPROCTITLE) +#if (__NetBSD__ || __FreeBSD__ || __OpenBSD__) +#define HAVE_SETPROCTITLE 1 +#if (__FreeBSD__ && __FreeBSD_version > 1200000) +#define HAVE_SETPROCTITLE_FAST 1 +#else +#define HAVE_SETPROCTITLE_FAST 0 +#endif +#else +#define HAVE_SETPROCTITLE 0 +#define HAVE_SETPROCTITLE_FAST 0 +#endif +#endif + + +#if HAVE_SETPROCTITLE +#define HAVE_SETPROCTITLE_REPLACEMENT 0 +#elif (defined __linux || defined __APPLE__) +#define HAVE_SETPROCTITLE_REPLACEMENT 1 +#else +#define HAVE_SETPROCTITLE_REPLACEMENT 0 +#endif + + +#if HAVE_SETPROCTITLE_REPLACEMENT + +#ifndef SPT_MAXTITLE +#define SPT_MAXTITLE 255 +#endif + + +extern char **environ; + + +static struct { + // Original value. + const char *arg0; + + // First enviroment variable. + char* env0; + + // Title space available. + char* base; + char* end; + + // Pointer to original nul character within base. + char *nul; + + int reset; +} SPT; + + +#ifndef SPT_MIN +#define SPT_MIN(a, b) (((a) < (b))? (a) : (b)) +#endif + + +static inline size_t spt_min(size_t a, size_t b) { + return SPT_MIN(a, b); +} + +static char **spt_find_argv_from_env(int argc, char *arg0) { + int i; + char **buf = NULL; + char *ptr; + char *limit; + + if (!(buf = (char **)malloc((argc + 1) * sizeof(char *)))) { + return NULL; + } + buf[argc] = NULL; + + // Walk back from environ until you find argc-1 null-terminated strings. + // Don't look for argv[0] as it's probably not preceded by 0. + ptr = SPT.env0; + limit = ptr - 8192; // TODO: empiric limit: should use MAX_ARG + --ptr; + for (i = argc - 1; i >= 1; --i) { + if (*ptr) { + return NULL; + } + --ptr; + while (*ptr && ptr > limit) { --ptr; } + if (ptr <= limit) { + return NULL; + } + buf[i] = (ptr + 1); + } + + // The first arg has not a zero in front. But what we have is reliable + // enough (modulo its encoding). Check if it is exactly what found. + // + // The check is known to fail on OS X with locale C if there are + // non-ascii characters in the executable path. + ptr -= strlen(arg0); + + if (ptr <= limit) { + return NULL; + } + if (strcmp(ptr, arg0)) { + return NULL; + } + + buf[0] = ptr; + return buf; +} + + +static int spt_init1() { + // Store a pointer to the first environment variable since go + // will overwrite environment. + SPT.env0 = environ[0]; + + return 2; +} + +static int spt_fast_init1() { + return 0; +} + + +static void spt_init2(int argc, char *arg0) { + char **argv = spt_find_argv_from_env(argc, arg0); + char **envp = &SPT.env0; + char *base, *end, *nul, *tmp; + int i; + + if (!argv) { + return; + } + + if (!(base = argv[0])) + return; + + nul = &base[strlen(base)]; + end = nul + 1; + + for (i = 0; i < argc || (i >= argc && argv[i]); i++) { + if (!argv[i] || argv[i] < end) + continue; + + end = argv[i] + strlen(argv[i]) + 1; + } + + for (i = 0; envp[i]; i++) { + if (envp[i] < end) + continue; + + end = envp[i] + strlen(envp[i]) + 1; + } + + if (!(SPT.arg0 = strdup(argv[0]))) + return; + +#if __GLIBC__ + if (!(tmp = strdup(program_invocation_name))) + return; + + program_invocation_name = tmp; + + if (!(tmp = strdup(program_invocation_short_name))) + return; + + program_invocation_short_name = tmp; +#elif __APPLE__ + if (!(tmp = strdup(getprogname()))) + return; + + setprogname(tmp); +#endif + + memset(base, 0, end - base); + + SPT.nul = nul; + SPT.base = base; + SPT.end = end; + + return; +} + + +static void setproctitle(const char *fmt, ...) { + char buf[SPT_MAXTITLE + 1]; // Use buffer in case argv[0] is passed. + va_list ap; + char *nul; + int len; + + if (!SPT.base) + return; + + if (fmt) { + va_start(ap, fmt); + len = vsnprintf(buf, sizeof buf, fmt, ap); + va_end(ap); + } else { + len = snprintf(buf, sizeof buf, "%s", SPT.arg0); + } + + if (len <= 0) { + return; + } + + if (!SPT.reset) { + memset(SPT.base, 0, SPT.end - SPT.base); + SPT.reset = 1; + } else { + memset(SPT.base, 0, spt_min(sizeof buf, SPT.end - SPT.base)); + } + + len = spt_min(len, spt_min(sizeof buf, SPT.end - SPT.base) - 1); + memcpy(SPT.base, buf, len); + nul = &SPT.base[len]; + + if (nul < SPT.nul) { + memset(nul, ' ', SPT.nul - nul); + } else if (nul == SPT.nul && &nul[1] < SPT.end) { + *SPT.nul = ' '; + *++nul = '\0'; + } +} + + +#else // HAVE_SETPROCTITLE_REPLACEMENT + +static int spt_init1() { +#if HAVE_SETPROCTITLE + return 1; +#else + return 0; +#endif +} + +static int spt_fast_init1() { +#if HAVE_SETPROCTITLE_FAST + return 1; +#else + return 0; +#endif +} + +static void spt_init2(int argc, char *arg0) { + (void)argc; + (void)arg0; +} + +#endif // HAVE_SETPROCTITLE_REPLACEMENT + + + +static void spt_setproctitle(const char *title) { +#if HAVE_SETPROCTITLE || HAVE_SETPROCTITLE_REPLACEMENT + setproctitle("%s", title); +#else + (void)title; +#endif +} + +static void spt_setproctitle_fast(const char *title) { +#if HAVE_SETPROCTITLE_FAST + setproctitle_fast("%s", title); +#else + (void)title; +#endif +} + From 917896408ff71d53c940fd0a88c130a991f75ceb Mon Sep 17 00:00:00 2001 From: Leon Zhang Date: Sat, 11 Apr 2020 01:02:24 +0800 Subject: [PATCH 4/4] support --password=aaa, -password=aaa type HiddenPasswordFromCmdline --- go/base/utils.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/go/base/utils.go b/go/base/utils.go index 59a5111d7..aa1f5742d 100644 --- a/go/base/utils.go +++ b/go/base/utils.go @@ -110,10 +110,19 @@ func HiddenPasswordFromCmdline() { argsLength := len(os.Args) for i, arg := range os.Args { switch arg { + // -password aaa, --password aaa case "--password", "-password", "-master-password", "--master-password": if i+1 < argsLength { os.Args[i+1] = "xxx" } + default: + // -password=aaa, --password=aaa + switch strings.Split(arg, "=")[0] { + case "--password", "-password": + arg = "-password=xxx" + case "-master-password", "--master-password": + arg = "-master-password=xxx" + } } cmdline = append(cmdline, arg) }