mirror of
https://github.com/gravitational/teleport.git
synced 2026-10-11 22:49:54 +00:00
* unset XAUTHORITY env var for the networking command When Teleport passes almost all the environment variables to the child processes that are born of a reexec. This is an issue when XAUTHORITY is set as it will be passed to the networking process, causing `xauth` to use a different file than the one that the SSH session process will use. This is because the SSH session process only inherits a small subset of the environment variables from the parent process, and XAUTHORITY is not one of them. This causes X11 forwarding to fail. * add Remove method to SafeEnv
186 lines
5.5 KiB
Go
186 lines
5.5 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 envutils
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"log/slog"
|
|
"os"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/gravitational/trace"
|
|
)
|
|
|
|
// MaxEnvironmentFileLines is the maximum number of lines in a environment file.
|
|
const MaxEnvironmentFileLines = 1000
|
|
|
|
func ReadEnvironment(ctx context.Context, r io.Reader) ([]string, error) {
|
|
var lineno int
|
|
env := &SafeEnv{}
|
|
|
|
scanner := bufio.NewScanner(r)
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
|
|
// follow the lead of OpenSSH and don't allow more than 1,000 environment variables
|
|
// https://github.com/openssh/openssh-portable/blob/master/session.c#L873-L874
|
|
lineno = lineno + 1
|
|
if lineno > MaxEnvironmentFileLines {
|
|
slog.WarnContext(ctx, "Too many lines in environment file, limiting how many are consumed",
|
|
"lines_consumed", MaxEnvironmentFileLines,
|
|
)
|
|
return *env, nil
|
|
}
|
|
|
|
// empty lines or lines that start with # are ignored
|
|
if line == "" || line[0] == '#' {
|
|
continue
|
|
}
|
|
|
|
// split on first =, if not found, log it and continue
|
|
idx := strings.Index(line, "=")
|
|
if idx == -1 {
|
|
slog.DebugContext(ctx, "Bad line while reading environment file: no = separator found", "line_number", lineno)
|
|
continue
|
|
}
|
|
|
|
// split key and value and make sure that key has a name
|
|
key := line[:idx]
|
|
value := line[idx+1:]
|
|
if strings.TrimSpace(key) == "" {
|
|
slog.DebugContext(ctx, "Bad line while reading environment file: key without name", "line_number", lineno)
|
|
continue
|
|
}
|
|
|
|
// key is added trusted within this context, but should be "AddFullUnique" when combined with any other values
|
|
env.AddTrusted(key, value)
|
|
}
|
|
|
|
if err := scanner.Err(); err != nil {
|
|
slog.ErrorContext(ctx, "Unable to read environment file", "error", err)
|
|
return []string{}, trace.Wrap(err, "reading environment file")
|
|
}
|
|
|
|
return *env, nil
|
|
}
|
|
|
|
var unsafeEnvironmentPrefixes = []string{
|
|
// Linux
|
|
// Covering cases from LD (man ld.so) to prevent injection like LD_PRELOAD
|
|
"LD_",
|
|
// macOS
|
|
// Covering cases from DYLD (man dyld) to prevent injection like DYLD_LIBRARY_PATH
|
|
"DYLD_",
|
|
}
|
|
|
|
// SafeEnv allows you to build a system environment while avoiding potentially dangerous environment conditions. In
|
|
// addition, SafeEnv will ignore any values added if the key already exists. This allows earlier inserts to take
|
|
// priority and ensure there is no conflicting values.
|
|
type SafeEnv []string
|
|
|
|
// AddTrusted will add the key and value to the environment if it's a safe value to forward on for fork / exec. This
|
|
// will not check for duplicates.
|
|
func (e *SafeEnv) AddTrusted(k, v string) {
|
|
e.add(false, k, v)
|
|
}
|
|
|
|
// AddUnique will add the key and value to the environment if it's a safe value to forward on for fork / exec. If the
|
|
// key already exists (case-insensitive) it will be ignored.
|
|
func (e *SafeEnv) AddUnique(k, v string) {
|
|
e.add(true, k, v)
|
|
}
|
|
|
|
func (e *SafeEnv) add(preventDuplicates bool, k, v string) {
|
|
k = strings.TrimSpace(k)
|
|
v = strings.TrimSpace(v)
|
|
if e.isUnsafeKey(preventDuplicates, k) {
|
|
return
|
|
}
|
|
|
|
*e = append(*e, fmt.Sprintf("%s=%s", k, v))
|
|
}
|
|
|
|
// AddFullTrusted adds an exact value, in the KEY=VALUE format. This should only be used if they values are already
|
|
// combined. When the values are separate the [Add] function is generally preferred. This will not check for
|
|
// duplicates.
|
|
func (e *SafeEnv) AddFullTrusted(fullValues ...string) {
|
|
e.addFull(false, fullValues)
|
|
}
|
|
|
|
// AddFullUnique adds an exact value, in the KEY=VALUE format. This should only be used if they values are already
|
|
// combined. When the values are separate the [Add] function is generally preferred. If any keys already exists
|
|
// (case-insensitive) they will be ignored.
|
|
func (e *SafeEnv) AddFullUnique(fullValues ...string) {
|
|
e.addFull(true, fullValues)
|
|
}
|
|
|
|
func (e *SafeEnv) addFull(preventDuplicates bool, fullValues []string) {
|
|
for _, kv := range fullValues {
|
|
kv = strings.TrimSpace(kv)
|
|
|
|
key := strings.SplitN(kv, "=", 2)[0]
|
|
if e.isUnsafeKey(preventDuplicates, key) {
|
|
continue
|
|
}
|
|
|
|
*e = append(*e, kv)
|
|
}
|
|
}
|
|
|
|
func (e *SafeEnv) isUnsafeKey(preventDuplicates bool, key string) bool {
|
|
if key == "" || key == "=" {
|
|
return false
|
|
}
|
|
|
|
upperKey := strings.ToUpper(key)
|
|
for _, prefix := range unsafeEnvironmentPrefixes {
|
|
if strings.HasPrefix(upperKey, prefix) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
if preventDuplicates {
|
|
prefix := upperKey + "="
|
|
for _, kv := range *e {
|
|
if strings.HasPrefix(strings.ToUpper(kv), prefix) {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// AddExecEnvironment will add safe values from [os.Environ], ignoring any duplicates that may have already been added.
|
|
func (e *SafeEnv) AddExecEnvironment() {
|
|
e.addFull(true, os.Environ())
|
|
}
|
|
|
|
// Remove deletes the key and value from the environment if it exists.
|
|
func (e *SafeEnv) Remove(key string) {
|
|
keyPrefix := key + "="
|
|
*e = slices.DeleteFunc(*e, func(s string) bool {
|
|
return strings.HasPrefix(s, keyPrefix)
|
|
})
|
|
}
|