From 6f876b2a172ace0ae37b7bc82e54504f34267389 Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Tue, 30 May 2023 03:43:09 -0400 Subject: [PATCH] Migrated wrench command --- cmd/wrench/main.go | 230 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 230 insertions(+) create mode 100644 cmd/wrench/main.go diff --git a/cmd/wrench/main.go b/cmd/wrench/main.go new file mode 100644 index 0000000..c283ff7 --- /dev/null +++ b/cmd/wrench/main.go @@ -0,0 +1,230 @@ +package main + +import "os" +import "fmt" +import "flag" +import "strconv" +import "os/exec" +import "os/user" +import "path/filepath" +import "golang.org/x/crypto/bcrypt" + +func printErr (format string, values ...any) { + fmt.Fprintf(os.Stderr, os.Args[0] + ": " + format, values...) +} + +func serviceUser (service string) string { + return "hn-" + service +} + +func tryCommand (cmd *exec.Cmd, failReason string) { + output, err := cmd.CombinedOutput() + if err != nil { + printErr("%s: %s", failReason, string(output)) + os.Exit(1) + } +} + +func ownOne (path string, uid, gid int) { + file, err := os.Stat(path) + if err != nil { + printErr("could not stat %s: %v", path, err) + return + } + + err = os.Chown(path, uid, gid) + if err != nil { + printErr("could not change ownership of %s: %v", path, err) + return + } + + if file.IsDir() { + err = os.Chmod(path, 0770) + } else { + err = os.Chmod(path, 0660) + } + if err != nil { + printErr("could not change mode of %s: %v", path, err) + return + } +} + +func main () { + user, err := user.Current() + if err != nil { + printErr("could not get username %v", err) + os.Exit(1) + } + + // define commands + hashCommand := flag.NewFlagSet("hash", flag.ExitOnError) + hashCost := hashCommand.Uint("cost", uint(bcrypt.DefaultCost), "Cost of the hash") + hashText := hashCommand.String("k", "", "Text content of the key") + + addUserCommand := flag.NewFlagSet("adduser", flag.ExitOnError) + addUserService := addUserCommand.String ("s", "router", + "Service to add a user for") + + delUserCommand := flag.NewFlagSet("deluser", flag.ExitOnError) + delUserService := delUserCommand.String ("s", "router", + "Service to delete the user for") + + authCommand := flag.NewFlagSet("auth", flag.ExitOnError) + authService := authCommand.String ("s", "router", + "Service to authorize the user to access") + authUser := authCommand.String ("u", user.Username, + "User to be given access") + + ownCommand := flag.NewFlagSet("own", flag.ExitOnError) + ownService := ownCommand.String ("s", "router", + "Service to give ownership of the file to") + ownFile := ownCommand.String ("f", ".", + "File to take ownership of") + ownRecursive := ownCommand.Bool ("r", false, + "Whether or not to recurse into sub-directories") + + // execute correct command + switch os.Args[1] { + case "hash": + execHash(int(*hashCost), *hashText) + case "adduser": + execAdduser(*addUserService) + case "deluser": + execDeluser(*delUserService) + case "auth": + execAuth(*authService, *authUser) + case "own": + execOwn(*ownService, *ownFile, *ownRecursive) + } +} + +func execHash (cost int, key string) { + if cost < bcrypt.MinCost { + printErr("cost is too low, must be at least %v", bcrypt.MinCost) + os.Exit(1) + } + if cost > bcrypt.MaxCost { + printErr("cost is too hight, can be at most %v", bcrypt.MaxCost) + os.Exit(1) + } + + hash, err := bcrypt.GenerateFromPassword([]byte(key), cost) + if err != nil { + printErr("could not hash key: %v", err) + os.Exit(1) + } + + fmt.Println(string(hash)) +} + +func execAdduser (service string) { + fullName := serviceUser(service) + + // BUSYBOX + adduser, err := exec.LookPath("adduser") + if err == nil { + addgroup, _ := exec.LookPath("addgroup") + tryCommand (exec.Command(addgroup, fullName, "-S"), + "could not add group") + tryCommand (exec.Command(adduser, fullName, "-SHDG", fullName), + "could not add user") + return + } + + // GNU + useradd, err := exec.LookPath("useradd") + if err == nil { + tryCommand (exec.Command ( + useradd, fullName, "-rUM", + "--shell", "/sbin/nologin"), "could not add user") + return + } + + printErr("could not add user: no command adduser or useradd") + os.Exit(1) +} + +func execDeluser (service string) { + fullName := serviceUser(service) + + // BUSYBOX + deluser, err := exec.LookPath("deluser") + if err == nil { + tryCommand (exec.Command(deluser, fullName, "--remove-home"), + "could not delete user") + return + } + + // GNU + userdel, err := exec.LookPath("userdel") + if err == nil { + tryCommand (exec.Command(userdel, fullName, "-r"), + "could not delete user") + groupdel, _ := exec.LookPath("groupdel") + tryCommand (exec.Command(groupdel, fullName), + "could not delete group") + return + } + + printErr("could not delete user: no command deluser or userdel") + os.Exit(1) +} + +func execAuth (service, user string) { + fullName := serviceUser(service) + + adduser, err := exec.LookPath("adduser") + if err == nil { + tryCommand (exec.Command(adduser, user, fullName), + "could not add user to group " + fullName) + return + } + + // GNU + useradd, err := exec.LookPath("usermod") + if err == nil { + tryCommand (exec.Command(useradd, "-a", "-g", fullName, user), + "could not add user to group " + fullName) + return + } + + printErr("could not auth user: no command adduser or usermod") + os.Exit(1) +} + +func execOwn (service, file string, recurse bool) { + fullName := serviceUser(service) + + userInfo, err := user.Lookup(fullName) + uid, _ := strconv.Atoi(userInfo.Uid) + gid, _ := strconv.Atoi(userInfo.Gid) + + if err != nil { + printErr("could not get user info: %v", err) + os.Exit(1) + } + + if !recurse { + ownOne(file, uid, gid) + return + } + + err = filepath.Walk(file, func( + filePath string, + file os.FileInfo, + err error, + ) error { + if err != nil { + printErr("could not traverse filesystem: %v", err) + return nil + } + + ownOne(filePath, uid, gid) + return nil + }) + + if err != nil { + printErr("could not traverse filesystem: %v", err) + os.Exit(1) + } +}