Linux教程Linux一键安装vsFTP墨颜丶2021-02-062026-09-01123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152#!/bin/bash# vsftpd 虚拟用户一键安装脚本(修复版)# 修复/改进内容:# 1. [核心修复] vsftpd >= 2.3.5(CentOS 7 的 3.0.2)自动写入 allow_writeable_chroot=YES,# 解决"认证成功但登录失败"(500 OOPS: refusing to run with writable root inside chroot())# CentOS 6 的 vsftpd 2.2.2 没有该检查也没有该选项,自动跳过,避免启动报 unknown option# 2. 删除重复的 pam_service_name,只保留 vsftpdftp.vu# 3. 固定被动模式端口段 40000-40010,并自动开放防火墙(firewalld / iptables)# 4. SELinux 为 Enforcing 时自动设置 ftp_full_access# 5. 映射用户 virtualftp 改为 /sbin/nologin,禁止登录系统# 6. 明文密码文件用完即删;密码库权限 600# 7. 支持多次运行:追加新用户、覆盖同名旧用户,不影响已有其他账号# (并自动迁移旧脚本留下的 /etc/vsftpd/accuser.txt)FTP_PORT=56118PASV_MIN=40000PASV_MAX=40010FTP_ROOT=/data/ftpread -p "创建用户 :" nameread -p "用户密码 :" passwd# ---------- 输入校验 ----------if [[ -z "$name" || -z "$passwd" ]]; then echo "错误:用户名和密码不能为空" >&2; exit 1fiif [[ ! "$name" =~ ^[A-Za-z0-9._-]+$ ]]; then echo "错误:用户名只能包含字母、数字、点、下划线、中划线" >&2; exit 1fi# ---------- 安装依赖 ----------yum -y install vsftpd opensslyum -y install db4-utils 2>/dev/null || yum -y install db4*command -v db_load >/dev/null 2>&1 || { echo "错误:db_load 不可用,请安装 db4-utils" >&2; exit 1; }# ---------- 判断 vsftpd 版本,决定是否需要 allow_writeable_chroot ----------VSVER=$(vsftpd -v 2>&1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)CHROOT_OPT=""if [ -n "$VSVER" ] && [ "$(printf '%s\n2.3.5\n' "$VSVER" | sort -V | head -1)" = "2.3.5" ]; then CHROOT_OPT="allow_writeable_chroot=YES"fi# ---------- 主配置 ----------cd /etc/vsftpd[ -f vsftpd.conf ] && cp vsftpd.conf vsftpd.conf.bakcat > vsftpd.conf <<EOFanonymous_enable=NOlocal_enable=YESwrite_enable=YESlocal_umask=022dirmessage_enable=YESxferlog_enable=YESconnect_from_port_20=YESxferlog_std_format=YESlisten=YESlisten_port=$FTP_PORTuserlist_enable=NOtcp_wrappers=YESaccept_timeout=1800connect_timeout=1800data_connection_timeout=1800idle_session_timeout=1800chroot_local_user=YES$CHROOT_OPTftpd_banner=Welcom to JG FTP servervirtual_use_local_privs=YESguest_enable=YESguest_username=virtualftppam_service_name=vsftpdftp.vuuser_config_dir=/etc/vsftpd/user_config_dirlocal_root=$FTP_ROOTpasv_enable=YESpasv_min_port=$PASV_MINpasv_max_port=$PASV_MAX# 如果服务器在 NAT 后面,取消下一行注释并填公网 IP:#pasv_address=1.2.3.4EOF# ---------- 系统映射用户(仅作权限映射,禁止登录) ----------id virtualftp &>/dev/null || useradd -M -s /sbin/nologin virtualftpmkdir -p /etc/vsftpd/user_config_dir "$FTP_ROOT"chown -R virtualftp:virtualftp "$FTP_ROOT"# ---------- PAM 验证模块 ----------PAM_SO=/lib64/security/pam_userdb.so[ -f "$PAM_SO" ] || PAM_SO=/lib/security/pam_userdb.socat > /etc/pam.d/vsftpdftp.vu <<EOFauth required $PAM_SO db=/etc/vsftpd/vsftpdloginaccount required $PAM_SO db=/etc/vsftpd/vsftpdloginEOF# ---------- 虚拟用户库(多用户、可重复运行) ----------VIRT=/etc/vsftpd/virtusers.txttouch "$VIRT"; chmod 600 "$VIRT"# 迁移旧脚本遗留的账号文件(保留 lijiacheng 等已有账号)if [ -s /etc/vsftpd/accuser.txt ] && [ ! -s "$VIRT" ]; then mv /etc/vsftpd/accuser.txt "$VIRT"fi# 删除同名旧记录(用户名一行 + 密码一行),再追加新记录awk -v u="$name" '$0==u{getline; next} {print}' "$VIRT" > "$VIRT.tmp" && mv "$VIRT.tmp" "$VIRT"printf '%s\n%s\n' "$name" "$passwd" >> "$VIRT"chmod 600 "$VIRT"db_load -T -t hash -f "$VIRT" /etc/vsftpd/vsftpdlogin.db \ || { echo "错误:db_load 生成密码库失败" >&2; exit 1; }chmod 600 /etc/vsftpd/vsftpdlogin.db# ---------- 用户独立配置 ----------cat > "/etc/vsftpd/user_config_dir/$name" <<EOFanon_world_readable_only=NOwrite_enable=YESanon_upload_enable=YESanon_other_write_enable=YESanon_mkdir_write_enable=YESlocal_root=$FTP_ROOTEOF# ---------- SELinux ----------if command -v getenforce >/dev/null 2>&1 && [ "$(getenforce 2>/dev/null)" = "Enforcing" ]; then setsebool -P ftp_full_access on 2>/dev/null \ || setsebool -P allow_ftpd_full_access on 2>/dev/null \ || echo "警告:SELinux 设置失败,请手动执行 setsebool -P ftp_full_access on"fi# ---------- 防火墙 ----------if systemctl is-active firewalld >/dev/null 2>&1; then firewall-cmd --permanent --add-port=${FTP_PORT}/tcp firewall-cmd --permanent --add-port=${PASV_MIN}-${PASV_MAX}/tcp firewall-cmd --reloadelif service iptables status >/dev/null 2>&1; then iptables -I INPUT -p tcp --dport ${FTP_PORT} -j ACCEPT iptables -I INPUT -p tcp --dport ${PASV_MIN}:${PASV_MAX} -j ACCEPT service iptables save >/dev/null 2>&1fi# ---------- 启动服务(CentOS 6 / 7 自适应) ----------if [ -d /run/systemd/system ] && command -v systemctl >/dev/null 2>&1; then systemctl restart vsftpd.service systemctl enable vsftpd.service >/dev/null 2>&1else service vsftpd restart chkconfig vsftpd onfiecho "vsFTP搭建成功,账号:密码:端口===$name:$passwd:$FTP_PORT"echo "被动端口段:$PASV_MIN-$PASV_MAX 数据目录:$FTP_ROOT"echo "自检命令:journalctl -u vsftpd | grep -i oops(无输出即正常)"