Replace occurences of . and ~ with _ when creating sudoers files. (#14300)

This commit is contained in:
Alex McGrath 2022-07-12 09:22:38 +00:00 committed by GitHub
parent 0deb57f343
commit 59063b1078
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 59 additions and 9 deletions

View file

@ -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))
})
}

View file

@ -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)

View file

@ -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,

View file

@ -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)
}
}