269 lines
6.6 KiB
Go
269 lines
6.6 KiB
Go
package main
|
|
|
|
import "os"
|
|
import "fmt"
|
|
import "flag"
|
|
import "strconv"
|
|
import "os/exec"
|
|
import "os/user"
|
|
import "hnakra/cli"
|
|
import "path/filepath"
|
|
import "golang.org/x/crypto/bcrypt"
|
|
|
|
func tryCommand (cmd *exec.Cmd, failReason string) {
|
|
output, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
cli.Sayf("%s: %s\n", failReason, string(output))
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func ownOne (path string, uid, gid int) {
|
|
file, err := os.Stat(path)
|
|
if err != nil {
|
|
cli.Sayf("could not stat %s: %v\n", path, err)
|
|
return
|
|
}
|
|
|
|
err = os.Chown(path, uid, gid)
|
|
if err != nil {
|
|
cli.Sayf("could not change ownership of %s: %v\n", path, err)
|
|
return
|
|
}
|
|
|
|
if file.IsDir() {
|
|
err = os.Chmod(path, 0770)
|
|
} else {
|
|
err = os.Chmod(path, 0660)
|
|
}
|
|
if err != nil {
|
|
cli.Sayf("could not change mode of %s: %v\n", path, err)
|
|
return
|
|
}
|
|
}
|
|
|
|
func main () {
|
|
user, err := user.Current()
|
|
if err != nil {
|
|
cli.Sayf("could not get username %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
flag.Usage = func () {
|
|
cli.Printf("Usage of %s:\n", os.Args[0])
|
|
cli.Printf(" hash\n")
|
|
cli.Printf(" Generate a bcrypt hash of a key\n")
|
|
cli.Printf(" adduser\n")
|
|
cli.Printf(" Add a system user to run a service as\n")
|
|
cli.Printf(" deluser\n")
|
|
cli.Printf(" Remove a user added with adduser\n")
|
|
cli.Printf(" auth\n")
|
|
cli.Printf(" Authorize a system user to access a service's files\n")
|
|
cli.Printf(" own\n")
|
|
cli.Printf(" Give ownership of a file to a service\n")
|
|
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")
|
|
delUserRmData := delUserCommand.Bool ("rmd", false,
|
|
"Whether to remove the service's data directory")
|
|
|
|
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")
|
|
|
|
flag.Parse()
|
|
|
|
// execute correct command
|
|
if len(os.Args) < 2 {
|
|
flag.Usage()
|
|
os.Exit(1)
|
|
}
|
|
subCommandArgs := os.Args[2:]
|
|
switch os.Args[1] {
|
|
case "hash":
|
|
hashCommand.Parse(subCommandArgs)
|
|
execHash(int(*hashCost), *hashText)
|
|
case "adduser":
|
|
addUserCommand.Parse(subCommandArgs)
|
|
execAdduser(*addUserService)
|
|
case "deluser":
|
|
delUserCommand.Parse(subCommandArgs)
|
|
execDeluser(*delUserService, *delUserRmData)
|
|
case "auth":
|
|
authCommand.Parse(subCommandArgs)
|
|
execAuth(*authService, *authUser)
|
|
case "own":
|
|
ownCommand.Parse(subCommandArgs)
|
|
execOwn(*ownService, *ownFile, *ownRecursive)
|
|
}
|
|
}
|
|
|
|
func execHash (cost int, key string) {
|
|
if key == "" {
|
|
cli.Sayf("please specify key text content\n")
|
|
os.Exit(1)
|
|
}
|
|
|
|
if cost < bcrypt.MinCost {
|
|
cli.Sayf("cost is too low, must be at least %v\n", bcrypt.MinCost)
|
|
os.Exit(1)
|
|
}
|
|
if cost > bcrypt.MaxCost {
|
|
cli.Sayf("cost is too hight, can be at most %v\n", bcrypt.MaxCost)
|
|
os.Exit(1)
|
|
}
|
|
|
|
hash, err := bcrypt.GenerateFromPassword([]byte(key), cost)
|
|
if err != nil {
|
|
cli.Sayf("could not hash key: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
fmt.Println(string(hash))
|
|
}
|
|
|
|
func execAdduser (service string) {
|
|
fullName := cli.ServiceUser(service)
|
|
dataDir := cli.ServiceDir(service)
|
|
|
|
if adduser, err := exec.LookPath("adduser"); err == nil {
|
|
// BUSYBOX
|
|
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")
|
|
} else if useradd, err := exec.LookPath("useradd"); err == nil {
|
|
// GNU
|
|
tryCommand (exec.Command (
|
|
useradd, fullName, "-rUM",
|
|
"--shell", "/sbin/nologin"), "could not add user")
|
|
} else {
|
|
cli.Sayf("could not add user: no command adduser or useradd\n")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// create data directory
|
|
uid, gid, err := cli.LookupUID(fullName)
|
|
if err != nil {
|
|
cli.Sayf("could not create data dir: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
err = cli.MkdirFor(dataDir, int(uid), int(gid))
|
|
if err != nil {
|
|
cli.Sayf("could not create data dir: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func execDeluser (service string, rmData bool) {
|
|
fullName := cli.ServiceUser(service)
|
|
dataDir := cli.ServiceDir(service)
|
|
|
|
if deluser, err := exec.LookPath("deluser"); err == nil {
|
|
// BUSYBOX
|
|
tryCommand (exec.Command(deluser, fullName, "--remove-home"),
|
|
"could not delete user")
|
|
} else if userdel, err := exec.LookPath("userdel"); err == nil {
|
|
// GNU
|
|
tryCommand (exec.Command(userdel, fullName, "-r"),
|
|
"could not delete user")
|
|
groupdel, _ := exec.LookPath("groupdel")
|
|
tryCommand (exec.Command(groupdel, fullName),
|
|
"could not delete group")
|
|
} else {
|
|
cli.Sayf("could not delete user: no command deluser or userdel\n")
|
|
os.Exit(1)
|
|
}
|
|
|
|
// delete data directory
|
|
if rmData {
|
|
err := os.RemoveAll(dataDir)
|
|
if err != nil {
|
|
cli.Sayf("could not delete data dir: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
}
|
|
|
|
func execAuth (service, user string) {
|
|
fullName := cli.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
|
|
}
|
|
|
|
cli.Sayf("could not auth user: no command adduser or usermod\n")
|
|
os.Exit(1)
|
|
}
|
|
|
|
func execOwn (service, file string, recurse bool) {
|
|
fullName := cli.ServiceUser(service)
|
|
|
|
userInfo, err := user.Lookup(fullName)
|
|
uid, _ := strconv.Atoi(userInfo.Uid)
|
|
gid, _ := strconv.Atoi(userInfo.Gid)
|
|
|
|
if err != nil {
|
|
cli.Sayf("could not get user info: %v\n", 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 {
|
|
cli.Sayf("could not traverse filesystem: %v\n", err)
|
|
return nil
|
|
}
|
|
|
|
ownOne(filePath, uid, gid)
|
|
return nil
|
|
})
|
|
|
|
if err != nil {
|
|
cli.Sayf("could not traverse filesystem: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
}
|