最近遇到Linux分配服务器账号的问题,本来以为就是简单的useradd几下就完事了,结果各种问题接踵而来,搞得我头都大了。趁着这个机会,我想跟大家聊聊Linux用户账号管理这块的经验,希望能帮到正在看这篇文章的你。
上个月我们有台生产服务器出了问题,原因竟然是一个离职员工的账号还在系统里,而且这个账号的密码被人猜到了。虽然这个账号权限不高,但是攻击者通过它做跳板,差点把我们整个内网都给渗透了。
这件事让我意识到,用户管理真的不是小事。很多运维同学可能觉得创建个用户删除个用户很简单,但是真正要做好用户管理,里面的门道可多着呢。
说到创建用户,大部分人第一反应就是useradd命令。没错,这个命令确实是最基础的,但是你知道它背后到底做了什么吗?
我记得刚开始做运维那会儿,领导让我给新同事创建账号,我就简单地执行了:
useradd zhangsan
结果新同事第二天找我说登录不了,我一查才发现密码都没设置。然后赶紧passwd一下,以为这下总行了吧。
过了几天又有问题,说是家目录权限不对,文件创建不了。我去看了一眼,好家伙,家目录确实创建了,但是里面空空如也,连基本的.bashrc都没有。
后来我才明白,useradd这个命令虽然简单,但是参数可多了去了。比如-m参数会自动创建家目录并复制骨架文件,-s参数可以指定默认shell,-G参数可以指定附加组。
现在我创建用户一般都这样:
useradd -m -s /bin/bash -G wheel zhangsan
这样创建出来的用户就比较完整了,有家目录,有合适的shell,还加入了wheel组(在CentOS系统里wheel组的用户可以使用sudo)。
不过说实话,每次都敲这么长的命令也挺烦的。后来我干脆写了个小脚本,把常用的参数都包装起来,用起来就方便多了。
用户创建好了,接下来就是密码问题。这个真的是让我头疼了很久的事情。
以前我们公司对密码要求不严格,很多同事都喜欢用简单密码,什么123456、password之类的。直到有一次安全检查,发现我们系统里有一大堆弱密码,差点被上级部门通报批评。
从那以后,我开始认真研究密码策略这块。Linux系统可以通过PAM(Pluggable Authentication Modules)来控制密码复杂度。
在/etc/pam.d/passwd文件里可以配置密码规则,比如:
password requisite pam_pwquality.so retry=3 minlen=8 difok=3 ucredit=-1 lcredit=-1 dcredit=-1
这行配置的意思是密码最少8位,至少包含一个大写字母、一个小写字母和一个数字。
但是光有技术手段还不够,更重要的是要让用户养成好习惯。我现在给新用户创建账号的时候,都会专门发个邮件说明密码要求,还会推荐一些密码管理工具。
另外,定期的密码过期策略也很重要。可以用chage命令来设置:
chage -M 90 zhangsan
这样用户zhangsan的密码90天后就会过期,必须更换新密码。
说到用户管理,就不得不提用户组。刚开始我对用户组的理解很浅显,觉得就是把用户归个类而已。后来才发现,用户组是Linux权限管理的核心。
我们公司有开发组、测试组、运维组等等,不同组的人需要访问不同的资源。比如开发组需要访问代码仓库,测试组需要访问测试环境,运维组需要访问生产环境。
通过合理的用户组设计,可以大大简化权限管理。比如我创建了一个webdev组,把所有web开发人员都加进去,然后把网站目录的组所有者设置为webdev,这样组内的所有人都可以修改网站文件了。
groupadd webdev
usermod -a -G webdev zhangsan
usermod -a -G webdev lisi
chgrp -R webdev /var/www/html
chmod -R g+w /var/www/html
不过用户组管理也有个坑,就是用户可能同时属于多个组。有时候权限问题排查起来特别麻烦,你得用groups命令看看用户到底属于哪些组,然后一个一个检查权限。
说到用户管理,就不得不提Linux系统是怎么存储用户信息的。刚开始做运维那会儿,我对这块理解很浅,只知道用useradd创建用户,但是不知道用户信息到底存在哪里。直到有一次系统出问题,用户登录不了,我才开始深入研究这些配置文件。
/etc/passwd这个文件可以说是用户管理的核心,所有用户的基本信息都存在这里。虽然名字叫passwd,但是现在密码已经不存在这个文件里了(出于安全考虑),主要存储的是用户的基本属性。
我们来看看这个文件的格式,每一行代表一个用户,字段之间用冒号分隔:
root:x:0:0:root:/root:/bin/bash
zhangsan:x:1001:1001:Zhang San:/home/zhangsan:/bin/bash
nginx:x:998:996:Nginx web server:/var/lib/nginx:/sbin/nologin
这七个字段分别是:
我记得有一次,一个同事手贱直接编辑了/etc/passwd文件,结果把某一行的冒号给删了一个,导致这个用户登录不了。后来我们排查了半天才发现是这个问题。所以说,这个文件虽然可以直接编辑,但是最好还是用vipw命令,它会检查语法错误。
UID这个字段有个规律,一般0是root用户,1-999是系统用户,1000以上是普通用户。不过这个规律在不同的Linux发行版里可能有差异。
以前密码是直接存在/etc/passwd里的,但是这个文件所有用户都可以读取,安全性很差。后来就把密码单独存到/etc/shadow文件里,这个文件只有root用户能读取。
shadow文件的格式比passwd复杂一些:
root:$6$randomsalt$hashedpassword:18500:0:99999:7:::
zhangsan:$6$anothersalt$anotherhashedpassword:18501:0:90:7:30::
这九个字段分别是:
密码字段里的表示使用SHA-512加密算法,是MD5,是SHA-256。现在一般都用SHA-512了,安全性更高。
我之前遇到过一个有趣的问题,有个用户说密码明明是对的但是登录不了。我检查shadow文件发现密码字段是!!,这表示账号被锁定了。后来才知道是因为密码过期了,系统自动锁定的。用passwd -u命令解锁就好了。
用户组信息存储在/etc/group文件里,格式相对简单:
root:x:0:
wheel:x:10:zhangsan,lisi
developers:x:1001:zhangsan,wangwu
四个字段分别是:
需要注意的是,这里的组成员列表只包含以这个组为附加组的用户,如果某个组是用户的主组,用户名不会出现在这里。
我刚开始的时候经常被这个搞混,明明用户属于某个组,但是在group文件里看不到。后来才明白,要看用户的主组得去passwd文件里看GID字段。
跟用户密码一样,组密码也有对应的shadow文件。不过现在组密码用得很少,这个文件主要是为了兼容性保留的。
root:::
wheel:::zhangsan,lisi
developers:!::zhangsan,wangwu
四个字段是:
说实话,我工作这么多年,还没遇到过需要设置组密码的场景。大部分时候这个文件都是空的或者用!占位。
说到用户管理,就不得不提等保(等级保护)要求。我们去年过了等保三级,在用户管理这块踩了不少坑,分享一些实践经验。
等保要求每个用户都要有唯一的身份标识,不能共享账号。这个看起来简单,但是实际执行起来有很多细节。
比如我们以前有个公共的test账号,大家都用来测试。等保检查的时候被指出问题,后来只能给每个需要测试的人都创建独立的测试账号。
还有就是离职人员的账号处理,等保要求离职当天就要禁用账号,不能拖延。我现在都是收到HR的离职通知后,立即执行:
usermod -L username # 锁定账号
usermod -s /sbin/nologin username # 禁用shell
chage -E 0 username # 设置账号过期
等保强调权限最小化,用户只能拥有完成工作所必需的最小权限。这个原则说起来容易,做起来挺难的。
我的做法是先梳理每个岗位的职责,然后设计对应的权限模板。比如:
通过用户组和sudo规则来实现精细化权限控制:
# 开发组只能重启开发环境服务
%developers ALL=(ALL) /bin/systemctl restart dev-*
# 测试组可以查看生产环境状态
%testers ALL=(ALL) /bin/systemctl status *, /bin/ps aux, /bin/netstat -tlnp
等保对密码有严格要求,至少8位,包含大小写字母、数字和特殊字符,定期更换。
我通过PAM模块来强制执行密码策略:
# /etc/pam.d/common-password
password requisite pam_pwquality.so retry=3 minlen=8 difok=3 ucredit=-1 lcredit=-1 dcredit=-1 ocredit=-1
密码周期管理通过chage命令:
# 设置密码90天过期,过期前7天开始警告
chage -M 90 -W 7 username
我还写了个脚本定期检查即将过期的密码,提前通知用户:
#!/bin/bash
# 检查30天内即将过期的密码
for user in $(cut -d: -f1 /etc/passwd); do
expire_date=$(chage -l $user | grep "Password expires" | cut -d: -f2)
if [[ "$expire_date" != *"never"* ]]; then
# 计算剩余天数,如果小于30天就发送提醒
# 这里省略具体的日期计算逻辑
fi
done
等保要求对连续登录失败进行限制,防止暴力破解。可以通过PAM的pam_tally2模块实现:
# /etc/pam.d/common-auth
auth required pam_tally2.so deny=5 unlock_time=300 even_deny_root
这个配置表示连续失败5次后锁定账号5分钟,包括root用户。
不过要注意,这个设置可能会影响正常使用。我就遇到过有同事因为输错密码被锁定,然后找我解锁:
pam_tally2 --user=username --reset
等保要求对用户的关键操作进行审计记录。Linux的auditd服务可以满足这个需求:
# 监控用户管理相关的系统调用
auditctl -w /etc/passwd -p wa -k user_management
auditctl -w /etc/shadow -p wa -k user_management
auditctl -w /etc/group -p wa -k user_management
auditctl -w /etc/
gshadow -p wa -k user_management
# 监控sudo命令执行
auditctl -a always,exit -F arch=b64 -S execve -F path=/usr/bin/sudo -k sudo_commands
# 监控用户切换
auditctl -a always,exit -F arch=b64 -S execve -F path=/usr/bin/su -k user_switch
还要记录用户的登录登出:
# /etc/rsyslog.conf
auth,authpriv.* /var/log/auth.log
我现在每天都会检查这些日志,看看有没有异常操作。虽然工作量大了一些,但是安全性确实提升了不少。
等保对特权用户(比如root、管理员)有特殊要求,需要严格控制数量,定期审查权限。
我的做法是尽量不直接使用root账号,而是通过sudo来提权。每个需要管理权限的人都有独立的账号,通过sudo配置来控制具体能执行哪些命令。
# /etc/sudoers
# 系统管理员组可以执行所有命令
%sysadmin ALL=(ALL) ALL
# 数据库管理员只能管理数据库相关服务
%dbadmin ALL=(ALL) /bin/systemctl * mysql*, /bin/systemctl * postgresql*
# 网络管理员只能管理网络相关配置
%netadmin ALL=(ALL) /sbin/iptables, /sbin/ip, /bin/systemctl * network*
而且我会定期(每个月)审查sudo权限,看看是否有不合理的配置。有时候临时给某个人开了权限,事后忘记收回,这种情况还挺常见的。
等保要求建立完整的账号生命周期管理流程,从申请、审批、创建、使用、变更到注销,每个环节都要有记录。
我现在用的流程是这样的:
每个步骤都有邮件通知和记录,确保有据可查。
虽然流程复杂了一些,但是确实规范了很多。以前经常有人直接找我要账号,现在都得走正规流程,减少了很多随意性。
等保对远程访问也有要求,需要采用加密传输,禁用不安全的协议。
SSH配置我现在都是这样的:
# /etc/ssh/sshd_config
Protocol 2
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
X11Forwarding no
AllowUsers zhangsan lisi wangwu
DenyUsers guest test
只允许指定用户通过密钥认证登录,禁用密码认证和root直接登录。
对于一些敏感系统,我还会限制登录的源IP:
# 只允许从办公网段登录
AllowUsers zhangsan@192.168.1.0/24 lisi@192.168.1.0/24
等保要求对用户会话进行管理,包括超时自动退出、并发会话限制等。
会话超时可以通过环境变量设置:
# /etc/profile
export TMOUT=1800 # 30分钟无操作自动退出
并发会话限制通过PAM模块:
# /etc/security/limits.conf
* hard maxlogins 3 # 每个用户最多3个并发会话
不过这个设置要小心,有时候用户开了多个终端窗口,可能会超过限制。我一般会根据实际使用情况调整。
等保要求定期进行安全检查,我现在每周都会跑一遍安全检查脚本:
#!/bin/bash
echo "=== 用户安全检查报告 ==="
echo "检查时间: $(date)"
echo
# 检查空密码账号
echo "空密码账号:"
awk -F: '($2 == "") {print $1}' /etc/shadow
# 检查UID为0的账号
echo "UID为0的账号:"
awk -F: '($3 == 0) {print $1}' /etc/passwd
# 检查长时间未登录的账号
echo "90天未登录的账号:"
lastlog -b 90 | grep -v "Never logged in" | grep -v "Username"
# 检查sudo权限
echo "具有sudo权限的用户:"
grep -v '^#' /etc/sudoers | grep -v '^$'
# 检查密码即将过期的账号
echo "30天内密码过期的账号:"
for user in $(cut -d: -f1 /etc/passwd); do
chage -l $user 2>/dev/null | grep "Password expires" | grep -v "never" | head -1
done
这个脚本会生成一份安全检查报告,我会仔细看每一项,发现问题及时处理。
等保还要求制定应急响应预案,当发现安全事件时能够快速响应。
我准备了一些应急处理脚本,比如发现异常登录时快速锁定账号:
#!/bin/bash
# 应急锁定用户账号
if [ $# -ne 1 ]; then
echo "Usage: $0 <username>"
exit 1
fi
username=$1
echo "锁定用户: $username"
usermod -L $username
pkill -u $username # 踢出当前会话
echo "用户 $username 已被锁定"
还有批量检查可疑活动的脚本:
#!/bin/bash
# 检查可疑登录活动
echo "最近24小时的登录记录:"
last -s yesterday
echo "登录失败记录:"
grep "Failed password" /var/log/auth.log | tail -20
echo "sudo使用记录:"
grep "sudo:" /var/log/auth.log | tail -20
在生产环境中,普通用户肯定不能直接用root账号,但是有时候又需要执行一些需要管理员权限的操作。这时候sudo就派上用场了。
sudo的配置文件是/etc/sudoers,但是千万不要直接编辑这个文件,一定要用visudo命令。我就见过有同事直接vim编辑sudoers文件,结果语法错误导致所有人都sudo不了,最后只能重启到单用户模式去修复。
sudo的配置其实挺灵活的,可以精确控制用户能执行哪些命令。比如:
zhangsan ALL=(ALL) /bin/systemctl restart nginx, /bin/systemctl status nginx
这样zhangsan就只能重启和查看nginx服务状态,不能执行其他管理员命令。
不过说实话,sudo配置写起来挺复杂的,特别是涉及到命令参数的时候。我一般都是先在测试环境试验好了,确认没问题再部署到生产环境。
随着公司规模扩大,手工管理用户账号越来越不现实。特别是新员工入职、老员工离职比较频繁的时候,一个一个处理真的很累。
我现在都是用脚本来批量处理用户账号。比如新员工入职,HR会给我一个Excel表格,我就写个脚本读取表格内容,批量创建用户账号。
#!/bin/bash
while IFS=',' read -r username fullname department email
do
useradd -m -s /bin/bash -c "$fullname" "$username"
echo "$username:TempPass123!" | chpasswd
chage -d 0 "$username" # 强制首次登录修改密码
echo "Created user: $username"
done < users.csv
这个脚本可以从CSV文件读取用户信息,批量创建账号,并设置临时密码,强制用户首次登录时修改密码。
员工离职的时候也是类似,我会先锁定账号,过一段时间确认没问题了再删除:
usermod -L zhangsan # 锁定账号
# 一个月后确认无问题
userdel -r zhangsan # 删除账号和家目录
除了普通用户,Linux系统里还有很多系统服务账号,比如apache、mysql、nginx等等。这些账号一般不需要登录shell,主要是用来运行特定的服务进程。
创建服务账号的时候要注意几个点:
useradd -r -s /sbin/nologin -d /var/lib/nginx nginx
这样创建的nginx用户就是一个纯粹的系统服务账号,不能登录,也不会在普通用户列表里显示。
我之前遇到过一个问题,有个开发同学不知道从哪里看到的教程,创建服务账号的时候没有用-r参数,结果这个账号的UID是1001,比普通用户的UID还大。虽然功能上没问题,但是看起来就很别扭,而且容易造成混淆。
说到用户管理,还有一个容易被忽略的问题就是磁盘配额。我就遇到过有用户在服务器上下载大文件,结果把整个磁盘都撑满了,导致系统无法正常运行。
Linux可以通过quota来限制用户的磁盘使用量。不过配置起来稍微有点复杂,需要先在文件系统上启用quota支持。
在/etc/fstab里给需要限制的分区加上usrquota选项:
/dev/sda1 /home ext4 defaults,usrquota 1 2
然后重新挂载分区,初始化quota数据库:
mount -o remount /home
quotacheck -cum /home
quotaon /home
接下来就可以给用户设置配额了:
edquota zhangsan
这个命令会打开一个编辑器,可以设置用户的软限制和硬限制。软限制是警告阈值,硬限制是绝对不能超过的上限。
不过说实话,quota配置起来确实有点麻烦,而且现在很多环境都用Docker或者虚拟机了,磁盘配额的需求没有以前那么强烈。但是在一些共享服务器环境中,还是很有用的。
用户管理的安全性真的很重要,我见过太多因为用户管理不当导致的安全事故。
首先是SSH登录配置,我现在都会禁用root直接SSH登录,强制使用密钥认证:
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
密钥认证虽然配置起来麻烦一点,但是安全性确实高很多。我一般会要求用户生成至少2048位的RSA密钥,或者直接用更安全的Ed25519算法。
另外就是定期清理无用账号。我写了个脚本,每个月检查一次长时间未登录的账号,发邮件提醒相关负责人确认是否还需要。
#!/bin/bash
# 检查90天未登录的用户
lastlog -b 90 | grep -v "Never logged in" | grep -v "Username" | while read line
do
username=$(echo $line | awk '{print $1}')
echo "User $username has not logged in for 90+ days"
# 这里可以加上发邮件的逻辑
done
还有一个容易被忽略的安全问题就是用户的.ssh目录权限。如果权限设置不当,可能会被其他用户读取私钥文件。正确的权限设置应该是:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/id_rsa
创建用户账号只是第一步,让用户用得舒服才是关键。我现在给新用户创建账号的时候,都会顺便配置一下基本的环境。
比如在用户的.bashrc里加上一些常用的别名:
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'
alias grep='grep --color=auto'
还会设置一些环境变量,比如EDITOR、LANG等等。这些看起来是小事,但是能大大提升用户体验。
有时候我还会根据用户的工作性质,预装一些常用的工具。比如开发人员可能需要git、vim、tmux等等,运维人员可能需要htop、iotop、netstat等等。
有时候需要把用户从一台服务器迁移到另一台服务器,这时候就要小心处理了,特别是用户的数据和配置文件。
我一般的做法是先在目标服务器上创建相同的用户账号,然后用rsync同步家目录:
rsync -avz --progress /home/zhangsan/ newserver:/home/zhangsan/
不过要注意的是,用户的UID和GID在两台服务器上可能不一样,同步完成后需要修正文件所有者:
chown -R zhangsan:zhangsan /home/zhangsan
另外,用户的crontab任务也要记得迁移:
crontab -l -u zhangsan > zhangsan.cron
# 在新服务器上
crontab -u zhangsan zhangsan.cron
随着云计算和DevOps的发展,现在有很多自动化工具可以帮助我们管理用户账号。比如Ansible、Puppet、Chef等等。
我现在用Ansible比较多,可以写个playbook来标准化用户创建流程:
- name: Create user accounts
user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
shell: /bin/bash
create_home: yes
with_items:
- { name: zhangsan, groups: developers }
- { name: lisi, groups: testers }
这样可以确保所有服务器上的用户配置都是一致的,而且出错的概率也大大降低。
不过自动化工具虽然好用,但是也不能完全依赖。基本的手工操作技能还是要掌握的,万一自动化工具出问题了,还得靠手工来救急。
用户管理不是一次性的工作,需要持续的监控和维护。我现在会监控一些关键指标,比如:
可以用一些开源的监控工具,比如Nagios、Zabbix等等,也可以自己写脚本配合cron来实现简单的监控。
比如监控登录失败次数:
#!/bin/bash
failed_count=$(grep "Failed password" /var/log/auth.log | wc -l)
if [ $failed_count -gt 100 ]; then
echo "Too many failed login attempts: $failed_count" | mail -s "Security Alert" admin@company.com
fi
用户管理看起来简单,但是要做好真的不容易。从我这几年的经验来看,最重要的是要建立规范的流程,不能图省事随意操作。
技术方面,要熟练掌握useradd、usermod、userdel等基本命令,了解PAM、sudo、quota等高级功能。更重要的是要有安全意识,定期审查用户权限,及时清理无用账号。
另外就是要善用自动化工具,提高管理效率。但是基本功还是要扎实,手工操作的技能不能丢。
最后想说的是,用户管理不只是技术问题,更是管理问题。要跟HR、安全、业务部门等多方协调,建立完善的用户生命周期管理流程。
希望我的这些经验能对大家有所帮助。用户管理这块的坑确实不少,但是只要用心去做,还是能做好的。如果你觉得这篇文章对你有帮助,欢迎点赞转发,让更多的运维同学看到。也欢迎关注@运维躬行录,我会持续分享更多实用的运维经验和技巧。