package utils import ( "crypto/sha256" "encoding/base64" "fmt" "os" "os/exec" "regexp" "slices" "strconv" "strings" "time" ) const ( NIS_YPCAT = "ypcat" SYS_PASSWD = "/etc/passwd" ) // LinuxSysUser linux系统用户信息 type LinuxSysUser struct { Name string Home string Shell string Uid int Gid int SSHkeyInfo map[string]SSHPubKeyInfo } func GetOneSysUser(name string) (*LinuxSysUser, error) { content, err := os.ReadFile(SYS_PASSWD) if err != nil { return nil, err } lines := strings.Split(strings.Trim(string(content), "\n"), "\n") for _, line := range lines { if strings.HasPrefix(line, name) { fields := strings.Split(line, ":") if len(fields) != 7 || fields[0] != name { continue } uid, err := strconv.Atoi(fields[2]) if err != nil { continue } if uid < 999 && uid != 0 { continue } u := LinuxSysUser{} u.Uid = uid gid, err := strconv.Atoi(fields[3]) if err != nil { continue } u.Gid = gid u.Name = fields[0] u.Shell = fields[6] u.Home = fields[5] s, err := ParseSSHAuthKey(u.Home + "/.ssh/authorized_keys") if err == nil { u.SSHkeyInfo = s } return &u, nil } } return nil, nil } func GetOneNisUser(name string) (*LinuxSysUser, error) { ypcat := FindCmd(NIS_YPCAT) if ypcat == nil { return nil, nil } content, err := exec.Command(NIS_YPCAT, "passwd").Output() if err != nil { return nil, err } lines := strings.Split(strings.Trim(string(content), "\n"), "\n") for _, line := range lines { if strings.HasPrefix(line, name) { fields := strings.Split(line, ":") if len(fields) != 7 || fields[0] != name { continue } uid, err := strconv.Atoi(fields[2]) if err != nil { continue } if uid < 999 && uid != 0 { continue } u := LinuxSysUser{} u.Uid = uid gid, err := strconv.Atoi(fields[3]) if err != nil { continue } u.Gid = gid u.Name = fields[0] u.Shell = fields[6] u.Home = fields[5] s, err := ParseSSHAuthKey(u.Home + "/.ssh/authorized_keys") if err == nil { u.SSHkeyInfo = s } return &u, nil } } return nil, nil } // GetSysUser 获取系统用户信息 func GetSysUser() (map[int]LinuxSysUser, error) { content, err := os.ReadFile(SYS_PASSWD) if err != nil { return nil, err } r := parsePasswd(string(content)) return r, nil } // GetNisUser 获取Nis用户信息 func GetNisUser() (map[int]LinuxSysUser, error) { ypcat := FindCmd(NIS_YPCAT) if ypcat == nil { return make(map[int]LinuxSysUser), nil } output, err := exec.Command(NIS_YPCAT, "passwd").Output() if err != nil { return nil, err } r := parsePasswd(string(output)) return r, nil } // GetClusUser 获取Clusconf用户信息 func GetClusUser() { // todo } type SSHPubKeyInfo struct { Type string Content string // base64格式的公钥 UserInfo string Sha256 string // 公钥经过sha256后,再base64,如果结尾是=,删除= } // ParseSSHAuthKey 解析ssh认证文件的内容 func ParseSSHAuthKey(path string) (map[string]SSHPubKeyInfo, error) { content, err := os.ReadFile(path) if err != nil { return nil, err } pub := string(content) lines := strings.Split(strings.Trim(pub, "\n"), "\n") result := make(map[string]SSHPubKeyInfo) for _, line := range lines { if len(line) == 0 { continue } fields := strings.Fields(line) if len(fields) != 3 { continue } if !strings.HasPrefix(fields[0], "ssh-") { continue } item := SSHPubKeyInfo{} item.Type, _ = strings.CutPrefix(fields[0], "ssh-") item.Content = fields[1] item.UserInfo = fields[2] key, err := base64.StdEncoding.DecodeString(item.Content) if err != nil { continue } b := sha256.Sum256(key) item.Sha256 = strings.Trim(base64.StdEncoding.EncodeToString(b[:]), "=") result[item.Sha256] = item } return result, nil } func parsePasswd(str string) map[int]LinuxSysUser { result := make(map[int]LinuxSysUser) pwd := strings.Trim(string(str), "\n") lines := strings.Split(pwd, "\n") for _, line := range lines { fields := strings.Split(line, ":") if len(fields) != 7 { continue } uid, err := strconv.Atoi(fields[2]) if err != nil { continue } if uid < 999 && uid != 0 { continue } u := LinuxSysUser{} u.Uid = uid gid, err := strconv.Atoi(fields[3]) if err != nil { continue } u.Gid = gid u.Name = fields[0] u.Shell = fields[6] u.Home = fields[5] s, err := ParseSSHAuthKey(u.Home + "/.ssh/authorized_keys") if err == nil { u.SSHkeyInfo = s } result[u.Uid] = u } return result } type OnlineUser struct { Name string `json:"name"` Type string `json:"type"` When time.Time `json:"loginTime"` Pids []int `json:"pids"` LoginFrom string `json:"loginForm"` } func (ou OnlineUser) String() string { return fmt.Sprintf("name:%s type:%s when:%s pids:%v login from:%s", ou.Name, ou.Type, ou.When.Format("2006-01-02 15:04"), ou.Pids, ou.LoginFrom) } func (ou OnlineUser) Sha256sum() [32]byte { return sha256.Sum256([]byte(ou.String())) } func (ou OnlineUser) PidString() string { if len(ou.Pids) == 0 { return "[]" } return fmt.Sprintf("%v", ou.Pids) } var ( ReOnLineUser = regexp.MustCompile(`^(?i)([a-zA-Z_0-9]*)\s+([a-zA-Z0-9/]*)\s+(\d{4}-\d{1,2}-\d{1,2} \d{2}:\d{2})\s+(?:old|\.|\d{2}:\d{2})\s+(\d*(?:,\d*)*)\s+\((.*)\)$`) // sshd远程登录的 ReOnLineUserTTY = regexp.MustCompile(`^(?i)([a-zA-Z_0-9]*)\s+(tty[0-9]*)\s+(\d{4}-\d{1,2}-\d{1,2} \d{2}:\d{2})\s+(?:old|\.|\d{2}:\d{2})\s+(\d*(?:,\d*)*)$`) // 通过控制台登录的 ReOnLineUserX = regexp.MustCompile(`^(?i)([a-zA-Z_0-9]*)\s+(:[0-9]*)\s+(\d{4}-\d{1,2}-\d{1,2} \d{2}:\d{2})\s+\?\s+(\d*(?:,\d*)*).*$`) // 通过图像界面 ) // GetOnlineUser func GetOnlineUser() ([]OnlineUser, error) { output, err := exec.Command("who", "-u").Output() if err != nil { return nil, err } lines := strings.Split(strings.Trim(string(output), "\n"), "\n") result := make([]OnlineUser, 0, 16) for _, line := range lines { if ReOnLineUser.MatchString(line) { m := ReOnLineUser.FindStringSubmatch(line) if len(m) != 6 { continue } u := OnlineUser{} u.Name = m[1] u.Type = m[2] t, err := time.Parse("2006-01-02 15:04", m[3]) if err != nil { return nil, err } u.When = t u.LoginFrom = m[5] pids := strings.Split(m[4], ",") u.Pids = make([]int, 0, len(pids)) for _, v := range pids { p, err := strconv.Atoi(v) if err != nil { return nil, err } u.Pids = append(u.Pids, p) } slices.Sort(u.Pids) result = append(result, u) } else if ReOnLineUserTTY.MatchString(line) { // todo } } return result, nil }