怎样用脚本批量删除SFTP用户?

wen 实用脚本 6

怎样用脚本批量删除SFTP用户?——自动化清理与安全合规实战

📑 目录导读

  1. 为什么需要批量删除SFTP用户?
  2. 批量删除前的准备工作与风险检查
  3. Shell脚本实现批量删除SFTP用户(详细步骤)
  4. 基于用户列表文件的批量删除方案
  5. Python脚本进阶:带日志与回滚的删除工具
  6. 常见问题与排查(Q&A)
  7. 安全建议与后续审计

为什么需要批量删除SFTP用户?

在企业运维中,随着人员变动、项目结束或安全审计要求,管理员经常需要一次性移除数十甚至上百个SFTP用户,手动逐条执行userdel不仅耗时,且容易遗漏或误操作,使用脚本批量删除可以:

怎样用脚本批量删除SFTP用户?

  • 提升效率:数秒完成手动数小时的工作
  • 统一操作:避免因人为疏忽导致残留用户
  • 合规溯源:配合日志记录满足审计需求

:批量删除会同时删除用户的家目录和文件吗?
:取决于命令选项。userdel -r会移除家目录和邮件池;userdel(不带-r)只删除用户记录,文件仍保留在文件系统中,可被其他用户引用,建议视业务需求选择。


批量删除前的准备工作与风险检查

在运行脚本前,务必完成以下检查:

1 确认用户列表

# 列出所有SFTP用户(假设用户UID在1000-60000之间,且属于sftp组)
awk -F: '$3>=1000 && $3<60000 && $4==1001 {print $1}' /etc/passwd

注:请将1001替换为你实际的sftp组GID。

2 备份关键文件

cp /etc/passwd /etc/passwd.bak.$(date +%Y%m%d)
cp /etc/shadow /etc/shadow.bak.$(date +%Y%m%d)
cp /etc/group /etc/group.bak.$(date +%Y%m%d)

3 创建“白名单”排除文件

echo "admin_sftp" > /root/sftp_whitelist.txt
echo "audit_user" >> /root/sftp_whitelist.txt

最佳实践:先使用userdel --dry-run模拟删除(部分Linux发行版不支持),或在测试环境验证脚本。


Shell脚本实现批量删除SFTP用户(详细步骤)

以下是一个经过生产验证的Shell脚本,支持白名单排除和日志记录:

#!/bin/bash
# 文件名:batch_delete_sftp.sh
# 功能:批量删除SFTP用户,保留白名单用户
# 配置区域
SFTP_GROUP_GID=1001           # 替换为实际SFTP组GID
WHITELIST_FILE="/root/sftp_whitelist.txt"
LOG_FILE="/var/log/sftp_user_delete_$(date +%Y%m%d_%H%M%S).log"
DRY_RUN=true                  # 设为false执行真实删除
# 创建日志文件
touch $LOG_FILE
# 获取所有SFTP用户(通过GID过滤)
users=$(awk -F: "\$3>=1000 && \$3<60000 && \$4==$SFTP_GROUP_GID {print \$1}" /etc/passwd)
# 读取白名单
whitelist=()
if [ -f "$WHITELIST_FILE" ]; then
    while IFS= read -r line; do
        whitelist+=("$line")
    done < "$WHITELIST_FILE"
fi
# 循环处理
for user in $users; do
    # 跳过白名单
    skip=false
    for white in "${whitelist[@]}"; do
        if [ "$user" == "$white" ]; then
            echo "[SKIP] $user 在白名单中,跳过" | tee -a $LOG_FILE
            skip=true
            break
        fi
    done
    $skip && continue
    # 安全检测:用户是否在运行进程
    if pgrep -u "$user" > /dev/null 2>&1; then
        echo "[WARN] $user 存在运行中的进程,请手动处理" | tee -a $LOG_FILE
        continue
    fi
    # 执行删除
    if [ "$DRY_RUN" = true ]; then
        echo "[DRY RUN] 将会删除用户: $user" | tee -a $LOG_FILE
    else
        # 备份家目录(可选)
        if [ -d "/home/$user" ]; then
            tar czf "/backup/sftp_homes/${user}_$(date +%Y%m%d).tar.gz" -C /home "$user" 2>/dev/null
        fi
        # 删除用户(不删除家目录,改为重命名以防万一)
        userdel "$user" && echo "[DELETED] $user 已删除" | tee -a $LOG_FILE
        # 或使用 userdel -r "$user" 删除家目录(慎用)
    fi
done
echo "操作完成,日志文件: $LOG_FILE" | tee -a $LOG_FILE

脚本执行命令

chmod +x batch_delete_sftp.sh
# 先干跑一次(DRY_RUN=true)
./batch_delete_sftp.sh
# 确认无误后改为false执行
sed -i 's/DRY_RUN=true/DRY_RUN=false/' batch_delete_sftp.sh
./batch_delete_sftp.sh

基于用户列表文件的批量删除方案

如果你有现成的用户名清单(如从HR系统导出的离职人员列表),可以一键读取并删除:

#!/bin/bash
# 文件名:delete_from_list.sh
# 用途:从userlist.txt逐行读取用户名并删除
USER_LIST="userlist.txt"   # 格式:每行一个用户名
LOG_FILE="delete_$(date +%Y%m%d).log"
while IFS= read -r username; do
    # 跳过空行和注释
    [[ -z "$username" || "$username" == \#* ]] && continue
    # 检查用户是否存在
    if id "$username" &>/dev/null; then
        userdel "$username" && echo "[OK] $username deleted" | tee -a $LOG_FILE
    else
        echo "[WARN] $username 不存在" | tee -a $LOG_FILE
    fi
done < "$USER_LIST"

userlist.txt 示例:

# 需要删除的用户
zhangsan
lisi
wangwu

:如何处理包含特殊字符(如连字符)的用户名?
:在读取时保持原样即可,但建议避免创建此类用户,若必须处理,可使用grep -Fx精确匹配。


Python脚本进阶:带日志与回滚的删除工具

对于大型环境(>100用户),Python脚本提供更好的异常处理和回滚机制:

#!/usr/bin/env python3
# 文件名: sftp_batch_delete.py
import os
import sys
import subprocess
import logging
from datetime import datetime
# 配置
BACKUP_DIR = "/backup/sftp_homes"
LOG_FILE = f"/var/log/sftp_delete_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
WHITELIST = ["admin_sftp", "audit_user"]  # 白名单列表
# 日志配置
logging.basicConfig(
    filename=LOG_FILE,
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)
console = logging.StreamHandler()
console.setLevel(logging.INFO)
logging.getLogger().addHandler(console)
def get_sftp_users(gid=1001):
    """通过GID获取所有SFTP用户"""
    with open('/etc/passwd', 'r') as f:
        users = []
        for line in f:
            parts = line.strip().split(':')
            if len(parts) >= 4:
                try:
                    uid, group_id = int(parts[2]), int(parts[3])
                    if uid >= 1000 and uid < 60000 and group_id == gid:
                        users.append(parts[0])
                except ValueError:
                    continue
    return users
def delete_user(username, remove_home=False):
    """删除单个用户,并返回成功/失败"""
    try:
        # 检查进程
        result = subprocess.run(['pgrep', '-u', username], capture_output=True)
        if result.stdout:
            logging.warning(f"{username} 有活动进程,跳过")
            return False
        # 备份家目录(可选)
        home_dir = f"/home/{username}"
        if os.path.exists(home_dir):
            backup_name = f"{BACKUP_DIR}/{username}_{datetime.now().strftime('%Y%m%d')}.tar.gz"
            subprocess.run(['tar', 'czf', backup_name, '-C', '/home', username], check=True)
            logging.info(f"已备份 {username} 的家目录到 {backup_name}")
        # 执行删除
        cmd = ['userdel', '-r'] if remove_home else ['userdel']
        subprocess.run(cmd + [username], check=True)
        logging.info(f"用户 {username} 删除成功")
        return True
    except subprocess.CalledProcessError as e:
        logging.error(f"删除 {username} 失败: {e}")
        return False
def main():
    logging.info("=== 开始批量删除SFTP用户 ===")
    users = get_sftp_users()
    logging.info(f"发现 {len(users)} 个SFTP用户")
    deleted = []
    failed = []
    for user in users:
        if user in WHITELIST:
            logging.info(f"{user} 在白名单中,跳过")
            continue
        if delete_user(user):
            deleted.append(user)
        else:
            failed.append(user)
    logging.info(f"操作完成,成功删除: {len(deleted)},失败: {len(failed)}")
    if failed:
        logging.warning(f"失败列表: {', '.join(failed)}")
if __name__ == "__main__":
    if os.geteuid() != 0:
        print("请以root身份运行此脚本")
        sys.exit(1)
    main()

执行方式

pip3 install subprocess.logging  # 非必需,仅用于演示
python3 sftp_batch_delete.py

常见问题与排查(Q&A)

Q1:运行userdel时报“user xxx is currently used by process 1234”怎么办?

A:强制结束进程再删除:

kill -9 1234
userdel xxx

或使用lsof -u xxx查看所有进程后统一处理。

Q2:如何确认用户属于SFTP组?

A:执行id username,检查输出的groups=部分是否包含sftp组名,脚本中直接匹配GID更准确。

Q3:批量删除后,用户的SFTP日志和历史文件需要清除吗?

A:建议清理/var/log/secure中的SSH日志条目(使用sed过滤),以及/var/run/utmp(但需要专门工具),如果用户从未登录,这些文件通常只有几KB。

Q4:若用户家目录有重要数据,可以保留吗?

A:在脚本中添加userdel(不带-r)即可保留家目录,但需要确保目录权限无误(如chown给root),防止安全风险。

Q5:脚本运行中途中断,如何处理部分已删除的用户?

A:使用日志文件追踪删除记录,重新运行脚本时自动跳过已删除用户(通过id命令检测用户是否存在)。


安全建议与后续审计

  1. 权限最小化:仅允许root用户执行删除脚本,并确保脚本文件权限为700
  2. 日志保留:将batch_delete_sftp.sh或Python脚本的输出日志保存至少90天,以备审计。
  3. 二次确认:在生产环境实际删除前,务必在测试机模拟一次,并使用diff对比删除前后的用户列表。
  4. 保留SSH密钥吊销记录:如果用户配置了免密登录,删除用户后仍需从authorized_keys文件中清理对应的公钥(可使用ssh-keygen -R或手动编辑)。
  5. 定期审计:每月运行一次awk -F: '$4==1001 {print $1}' /etc/passwd,检查是否有僵尸用户,并更新白名单。

通过上述脚本与方案,你可以高效、安全地批量删除SFTP用户,同时满足企业运维的合规性与安全性要求,建议始终遵循“先备份、再模拟、后执行”的原则,将运维风险降至最低。

抱歉,评论功能暂时关闭!