forked from aws/aws-lambda-runtime-interface-emulator
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathuser.go
More file actions
135 lines (125 loc) · 4.18 KB
/
Copy pathuser.go
File metadata and controls
135 lines (125 loc) · 4.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// User utilities to create UNIX users and drop root privileges
package main
import (
"fmt"
"os"
"os/user"
"strconv"
"strings"
"syscall"
log "github.com/sirupsen/logrus"
)
// AddUser adds a UNIX user (e.g., sbx_user1051) to the passwd and shadow files if not already present
// The actual default values are based on inspecting the AWS Lambda runtime in us-east-1
// /etc/group is empty and /etc/gshadow is not accessible in AWS
// The home directory does not exist in AWS Lambda
func AddUser(user string, uid int, gid int) {
// passwd file format: https://www.cyberciti.biz/faq/understanding-etcpasswd-file-format/
passwdFile := "/etc/passwd"
passwdEntry := fmt.Sprintf("%[1]s:x:%[2]v:%[3]v::/home/%[1]s:/sbin/nologin", user, uid, gid)
if !doesFileContainEntry(passwdFile, passwdEntry) {
addEntry(passwdFile, passwdEntry)
}
// shadow file format: https://www.cyberciti.biz/faq/understanding-etcshadow-file/
shadowFile := "/etc/shadow"
shadowEntry := fmt.Sprintf("%s:*:18313:0:99999:7:::", user)
if !doesFileContainEntry(shadowFile, shadowEntry) {
addEntry(shadowFile, shadowEntry)
}
}
// doesFileContainEntry returns true if the entry string exists in the given file
func doesFileContainEntry(file string, entry string) bool {
data, err := os.ReadFile(file)
if err != nil {
log.Warnln("Could not read file:", file, err)
return false
}
text := string(data)
return strings.Contains(text, entry)
}
// addEntry appends an entry string to the given file
func addEntry(file string, entry string) error {
f, err := os.OpenFile(file,
os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
log.Errorln("Error opening file:", file, err)
return err
}
defer f.Close()
if _, err := f.WriteString(entry); err != nil {
log.Errorln("Error appending entry to file:", file, err)
return err
}
return nil
}
// IsRootUser returns true if the current process is root and false otherwise.
func IsRootUser() bool {
return os.Getuid() == 0
}
// UserLogger returns a context logger with user fields.
func UserLogger() *log.Entry {
// Skip user lookup at debug level
if !log.IsLevelEnabled(log.DebugLevel) {
return log.WithFields(log.Fields{})
}
uid := os.Getuid()
uidString := strconv.Itoa(uid)
userObject, err := user.LookupId(uidString)
if err != nil {
log.Warnln("Could not look up user by uid:", uid, err)
}
return log.WithFields(log.Fields{
"username": userObject.Username,
"uid": uid,
"euid": os.Geteuid(),
"gid": os.Getgid(),
})
}
// EnsureHome sets HOME=/tmp if the current process has no /etc/passwd entry.
// UnsetLsEnvs strips HOME for AWS parity, which is fine in the normal
// root-start flow where AddUser has written a passwd entry. But when the
// container is launched with --user=1000:1000, AddUser is never called and
// Node's os.homedir() / AWS SDK config loading fail with ENOENT.
func EnsureHome() {
if _, err := user.Current(); err != nil {
if setErr := os.Setenv("HOME", "/tmp"); setErr != nil {
log.Warnln("Could not set HOME=/tmp for non-passwd user:", setErr)
} else {
log.Debugln("No /etc/passwd entry for current UID; HOME set to /tmp")
}
}
}
// DropPrivileges switches to another UNIX user by dropping root privileges
// Initially based on https://stackoverflow.com/a/75545491/6875981
func DropPrivileges(userToSwitchTo string) error {
// Lookup user and group IDs for the user we want to switch to.
userInfo, err := user.Lookup(userToSwitchTo)
if err != nil {
log.Errorln("Error looking up user:", userToSwitchTo, err)
return err
}
// Convert group ID and user ID from string to int.
gid, err := strconv.Atoi(userInfo.Gid)
if err != nil {
log.Errorln("Error converting gid:", userInfo.Gid, err)
return err
}
uid, err := strconv.Atoi(userInfo.Uid)
if err != nil {
log.Errorln("Error converting uid:", userInfo.Uid, err)
return err
}
// Limitation: Debugger gets stuck when stepping over these syscalls!
// No breakpoints beyond this point are hit.
// Set group ID (real and effective).
if err = syscall.Setgid(gid); err != nil {
log.Errorln("Failed to set group ID:", err)
return err
}
// Set user ID (real and effective).
if err = syscall.Setuid(uid); err != nil {
log.Errorln("Failed to set user ID:", err)
return err
}
return nil
}