mirror of
https://github.com/gravitational/teleport
synced 2024-10-19 08:43:58 +00:00
Replace occurences of . and ~ with _ when creating sudoers files. (#14300)
This commit is contained in:
parent
0deb57f343
commit
59063b1078
|
@ -25,6 +25,7 @@ import (
|
|||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/cloudflare/cfssl/log"
|
||||
|
@ -51,8 +52,11 @@ func requireRoot(t *testing.T) {
|
|||
|
||||
func TestRootHostUsersBackend(t *testing.T) {
|
||||
requireRoot(t)
|
||||
|
||||
backend := srv.HostUsersProvisioningBackend{}
|
||||
sudoersTestDir := t.TempDir()
|
||||
backend := srv.HostUsersProvisioningBackend{
|
||||
SudoersPath: sudoersTestDir,
|
||||
HostUUID: "hostuuid",
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
// cleanup users if they got left behind due to a failing test
|
||||
host.UserDel(testuser)
|
||||
|
@ -121,6 +125,13 @@ func TestRootHostUsersBackend(t *testing.T) {
|
|||
invalidSudoersEntry := []byte("yipee i broke sudo!!!!")
|
||||
err = backend.CheckSudoers(invalidSudoersEntry)
|
||||
require.EqualError(t, err, "parse error in stdin near line 1\n\n\tvisudo: invalid sudoers file")
|
||||
// test sudoers entry containing . or ~
|
||||
require.NoError(t, backend.WriteSudoersFile("user.name", validSudoersEntry))
|
||||
_, err = os.Stat(filepath.Join(sudoersTestDir, "teleport-hostuuid-user_name"))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, backend.RemoveSudoersFile("user.name"))
|
||||
_, err = os.Stat(filepath.Join(sudoersTestDir, "teleport-hostuuid-user_name"))
|
||||
require.True(t, os.IsNotExist(err))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"os/user"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -132,6 +133,15 @@ type HostUserManagement struct {
|
|||
|
||||
var _ HostUsers = &HostUserManagement{}
|
||||
|
||||
var sudoersSanitizationMatcher = regexp.MustCompile(`\.|~`)
|
||||
|
||||
// sanitizeSudoersName replaces occurrences of `.` and `~` with
|
||||
// underscores as `sudo` will not read files including these
|
||||
// characters
|
||||
func sanitizeSudoersName(username string) string {
|
||||
return sudoersSanitizationMatcher.ReplaceAllString(username, "_")
|
||||
}
|
||||
|
||||
// CreateUser creates a temporary Teleport user in the TeleportServiceGroup
|
||||
func (u *HostUserManagement) CreateUser(name string, ui *services.HostUsersInfo) (*user.User, io.Closer, error) {
|
||||
tempUser, err := u.backend.Lookup(name)
|
||||
|
|
|
@ -29,15 +29,17 @@ import (
|
|||
|
||||
// HostUsersProvisioningBackend is used to implement HostUsersBackend
|
||||
type HostUsersProvisioningBackend struct {
|
||||
sudoersPath string
|
||||
hostUUID string
|
||||
// SudoersPath is the path to write sudoers files to.
|
||||
SudoersPath string
|
||||
// HostUUID is the UUID of the running host
|
||||
HostUUID string
|
||||
}
|
||||
|
||||
// newHostUsersBackend initializes a new OS specific HostUsersBackend
|
||||
func newHostUsersBackend(uuid string) (HostUsersBackend, error) {
|
||||
return &HostUsersProvisioningBackend{
|
||||
sudoersPath: "/etc/sudoers.d",
|
||||
hostUUID: uuid,
|
||||
SudoersPath: "/etc/sudoers.d",
|
||||
HostUUID: uuid,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -115,8 +117,9 @@ func (u *HostUsersProvisioningBackend) WriteSudoersFile(username string, content
|
|||
if err := u.CheckSudoers(contents); err != nil {
|
||||
return trace.Wrap(err)
|
||||
}
|
||||
sudoersFilePath := filepath.Join(u.sudoersPath, fmt.Sprintf("teleport-%s-%s", u.hostUUID, username))
|
||||
tmpSudoers, err := writeSudoersFile(u.sudoersPath, username, contents)
|
||||
username = sanitizeSudoersName(username)
|
||||
sudoersFilePath := filepath.Join(u.SudoersPath, fmt.Sprintf("teleport-%s-%s", u.HostUUID, username))
|
||||
tmpSudoers, err := writeSudoersFile(u.SudoersPath, username, contents)
|
||||
if err != nil {
|
||||
if tmpSudoers != "" {
|
||||
rmErr := os.Remove(tmpSudoers)
|
||||
|
@ -131,7 +134,8 @@ func (u *HostUsersProvisioningBackend) WriteSudoersFile(username string, content
|
|||
|
||||
// RemoveSudoersFile deletes a user's sudoers file.
|
||||
func (u *HostUsersProvisioningBackend) RemoveSudoersFile(username string) error {
|
||||
sudoersFilePath := filepath.Join(u.sudoersPath, fmt.Sprintf("teleport-%s-%s", u.hostUUID, username))
|
||||
username = sanitizeSudoersName(username)
|
||||
sudoersFilePath := filepath.Join(u.SudoersPath, fmt.Sprintf("teleport-%s-%s", u.HostUUID, username))
|
||||
if _, err := os.Stat(sudoersFilePath); os.IsNotExist(err) {
|
||||
log.Debugf("User %q, did not have sudoers file as it did not exist at path %q",
|
||||
username,
|
||||
|
|
|
@ -266,3 +266,28 @@ func TestUserMgmt_DeleteAllTeleportSystemUsers(t *testing.T) {
|
|||
// teleport-system group doesnt exist, DeleteAllUsers will return nil, instead of erroring
|
||||
require.NoError(t, users.DeleteAllUsers())
|
||||
}
|
||||
|
||||
func TestSudoersSanitization(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tc := range []struct {
|
||||
user string
|
||||
userExpected string
|
||||
}{
|
||||
{
|
||||
user: "testuser",
|
||||
userExpected: "testuser",
|
||||
},
|
||||
{
|
||||
user: "test.user",
|
||||
userExpected: "test_user",
|
||||
},
|
||||
{
|
||||
user: "test.us~er",
|
||||
userExpected: "test_us_er",
|
||||
},
|
||||
} {
|
||||
actual := sanitizeSudoersName(tc.user)
|
||||
require.Equal(t, tc.userExpected, actual)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue