mirror of
https://github.com/gravitational/teleport.git
synced 2026-11-03 16:38:35 +08:00
161 lines
4.9 KiB
Go
161 lines
4.9 KiB
Go
/*
|
|
* Teleport
|
|
* Copyright (C) 2023 Gravitational, Inc.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package host
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"os/exec"
|
|
"strings"
|
|
|
|
"github.com/gravitational/trace"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// man GROUPADD(8), exit codes section
|
|
const GroupExistExit = 9
|
|
|
|
// man USERADD(8), exit codes section
|
|
const UserExistExit = 9
|
|
const UserLoggedInExit = 8
|
|
|
|
// GroupAdd creates a group on a host using `groupadd` optionally
|
|
// specifying the GID to create the group with.
|
|
func GroupAdd(groupname string, gid string) (exitCode int, err error) {
|
|
groupaddBin, err := exec.LookPath("groupadd")
|
|
if err != nil {
|
|
return -1, trace.Wrap(err, "cant find groupadd binary")
|
|
}
|
|
var args []string
|
|
if gid != "" {
|
|
args = append(args, "--gid", gid)
|
|
}
|
|
args = append(args, groupname)
|
|
|
|
cmd := exec.Command(groupaddBin, args...)
|
|
output, err := cmd.CombinedOutput()
|
|
log.Debugf("%s output: %s", cmd.Path, string(output))
|
|
if cmd.ProcessState.ExitCode() == GroupExistExit {
|
|
return cmd.ProcessState.ExitCode(), trace.AlreadyExists("group already exists")
|
|
}
|
|
return cmd.ProcessState.ExitCode(), trace.Wrap(err)
|
|
}
|
|
|
|
// UserAdd creates a user on a host using `useradd`
|
|
func UserAdd(username string, groups []string, home, uid, gid string) (exitCode int, err error) {
|
|
useraddBin, err := exec.LookPath("useradd")
|
|
if err != nil {
|
|
return -1, trace.Wrap(err, "cant find useradd binary")
|
|
}
|
|
|
|
if home == "" {
|
|
return -1, trace.BadParameter("home is a required parameter")
|
|
}
|
|
|
|
// useradd ---no-create-home (username) (groups)...
|
|
args := []string{"--no-create-home", "--home-dir", home, username}
|
|
if len(groups) != 0 {
|
|
args = append(args, "--groups", strings.Join(groups, ","))
|
|
}
|
|
if uid != "" {
|
|
args = append(args, "--uid", uid)
|
|
}
|
|
if gid != "" {
|
|
args = append(args, "--gid", gid)
|
|
}
|
|
|
|
cmd := exec.Command(useraddBin, args...)
|
|
output, err := cmd.CombinedOutput()
|
|
log.Debugf("%s output: %s", cmd.Path, string(output))
|
|
if cmd.ProcessState.ExitCode() == UserExistExit {
|
|
return cmd.ProcessState.ExitCode(), trace.AlreadyExists("user already exists")
|
|
}
|
|
return cmd.ProcessState.ExitCode(), trace.Wrap(err)
|
|
}
|
|
|
|
// AddUserToGroups adds a user to a list of specified groups on a host using `usermod`
|
|
func AddUserToGroups(username string, groups []string) (exitCode int, err error) {
|
|
usermodBin, err := exec.LookPath("usermod")
|
|
if err != nil {
|
|
return -1, trace.Wrap(err, "cant find usermod binary")
|
|
}
|
|
args := []string{"-aG"}
|
|
args = append(args, groups...)
|
|
args = append(args, username)
|
|
// usermod -aG (append groups) (username)
|
|
cmd := exec.Command(usermodBin, args...)
|
|
output, err := cmd.CombinedOutput()
|
|
log.Debugf("%s output: %s", cmd.Path, string(output))
|
|
return cmd.ProcessState.ExitCode(), trace.Wrap(err)
|
|
}
|
|
|
|
// UserDel deletes a user on a host using `userdel`.
|
|
func UserDel(username string) (exitCode int, err error) {
|
|
userdelBin, err := exec.LookPath("userdel")
|
|
if err != nil {
|
|
return -1, trace.Wrap(err, "cant find userdel binary")
|
|
}
|
|
// userdel --remove (remove home) username
|
|
cmd := exec.Command(userdelBin, "--remove", username)
|
|
output, err := cmd.CombinedOutput()
|
|
log.Debugf("%s output: %s", cmd.Path, string(output))
|
|
return cmd.ProcessState.ExitCode(), trace.Wrap(err)
|
|
}
|
|
|
|
func GetAllUsers() ([]string, int, error) {
|
|
getentBin, err := exec.LookPath("getent")
|
|
if err != nil {
|
|
return nil, -1, trace.Wrap(err, "cant find getent binary")
|
|
}
|
|
// getent passwd
|
|
cmd := exec.Command(getentBin, "passwd")
|
|
output, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
return nil, -1, trace.Wrap(err)
|
|
}
|
|
var users []string
|
|
for _, line := range bytes.Split(output, []byte("\n")) {
|
|
line := string(line)
|
|
passwdEnt := strings.SplitN(line, ":", 2)
|
|
if passwdEnt[0] != "" {
|
|
users = append(users, passwdEnt[0])
|
|
}
|
|
}
|
|
return users, -1, nil
|
|
}
|
|
|
|
var ErrInvalidSudoers = errors.New("visudo: invalid sudoers file")
|
|
|
|
// CheckSudoers tests a suders file using `visudo`. The contents
|
|
// are written to the process via stdin pipe.
|
|
func CheckSudoers(contents []byte) error {
|
|
visudoBin, err := exec.LookPath("visudo")
|
|
if err != nil {
|
|
return trace.Wrap(err, "cant find visudo binary")
|
|
}
|
|
cmd := exec.Command(visudoBin, "--check", "--file", "-")
|
|
|
|
cmd.Stdin = bytes.NewBuffer(contents)
|
|
output, err := cmd.Output()
|
|
if cmd.ProcessState.ExitCode() != 0 {
|
|
return trace.WrapWithMessage(ErrInvalidSudoers, string(output))
|
|
}
|
|
return trace.Wrap(err)
|
|
}
|