自动化部署——安装并配置 vsftpd(2.3.4)+ OpenLDAP(2.4.37)+ pam_ldap 认证体系,实现基于 LDAP 认证的 FTP 服务的bash shell脚本

脚本核心功能总览

脚本通过不同执行参数(install_all/config_all/smart_all 等)实现模块化部署,整体流程:

  1. 密钥验证:执行任何核心操作前需输入正确密钥,否则直接退出;
  2. 基础依赖安装
    • 安装 BerkeleyDB(OpenLDAP 依赖)→ 安装 OpenLDAP;
    • 安装 vsftpd(2.3.4),自动检测并启用 TCP Wrappers/SSL 支持;
  3. pam_ldap 安装与配置
    • 生成大量随机隐藏目录,将 LDAP 认证配置文件隐藏在随机目录中;
    • 配置 pam_ldap 关联 OpenLDAP,实现 FTP 认证对接 LDAP 服务器;
  4. vsftpd 定制化配置:开启虚拟用户、LDAP 认证、被动模式等,绑定 PAM 认证模块;
  5. 服务管理:重启 vsftpd 服务,确保配置生效。
#!/bin/bash
##############################################################################
# 脚本名称: ftp_ldap_deploy.sh
# 脚本功能: 自动化安装配置vsftpd + openldap + pam_ldap,实现LDAP认证的FTP服务
# 版本信息: version:201401171318 since:201401161923
# 开发信息: Production tools are designed by rattlesnake® Automation Institute
# 开发人员: Design and R & D engineers: Mr. Zhang
# 使用说明: 
#   1. 安装全部组件: ./脚本名 install_all (需输入执行密钥)
#   2. 配置全部组件: ./脚本名 config_all (需输入执行密钥)
#   3. 安装+配置+重启: ./脚本名 smart_all (需输入执行密钥)
#   4. 重启FTP服务: ./脚本名 ftprestart 或 ftpstart
# 注意事项: 
#   1. 执行脚本需要root权限
#   2. 依赖内网地址172.16.0.123的软件包下载服务
#   3. 部分操作会修改系统文件,建议在测试环境验证后使用
##############################################################################

# 打印版本和开发信息
echo "version:201401171318 since:201401161923"
echo "Production tools are designed by rattlesnake® Automation Institute"
echo "Design and R & D engineers: Mr. Zhang"

# ======================== 核心函数定义 ========================

# 函数名: install_openldap
# 功能: 安装OpenLDAP(依赖BerkeleyDB)
# 步骤: 
#   1. 下载BerkeleyDB和OpenLDAP软件包并校验MD5
#   2. 编译安装BerkeleyDB
#   3. 编译安装OpenLDAP(关联BerkeleyDB)
#   4. 校验安装结果,失败则退出脚本
install_openldap() {
    # 切换到临时目录
    cd /tmp
    # 静默下载BerkeleyDB软件包(-N:只下更新的 -c:断点续传 -q:静默)
    wget -N -c -q http://172.16.0.123/tools/ywgj/db-4.5.20.tar.gz &>/dev/null
    
    # 校验文件是否存在且MD5值正确
    if [[ -f "db-4.5.20.tar.gz" ]] && [[ "$(md5sum db-4.5.20.tar.gz | awk '{print $1}')" == "b0f1c777708cb8e9d37fb47e7ed3312d" ]]; then
        echo -e "\e[1;4;5;42mdb-4.5.20.tar.gz软件包下载完整,文件校验和正确!\e[1;0;m"
    else
        echo -e "\e[1;4;5;41mdb-4.5.20.tar.gz软件包下载残缺,文件校验和错误!\e[1;0;m"
        exit 1
    fi

    # 静默下载OpenLDAP软件包
    wget -N -c -q http://172.16.0.123/tools/ywgj/openldap-2.4.37.tgz &>/dev/null
    # 校验OpenLDAP软件包
    if [[ -f "openldap-2.4.37.tgz" ]] && [[ "$(md5sum openldap-2.4.37.tgz | awk '{print $1}')" == "49f0e9a77ddd0d49f88bf7233a51efa8" ]]; then
        echo -e "\e[1;4;5;42mopenldap-2.4.37.tgz软件包下载完整,文件校验和正确!\e[1;0;m"
    else
        echo -e "\e[1;4;5;41mopenldap-2.4.37.tgz软件包下载残缺,文件校验和错误!\e[1;0;m"
        exit 1
    fi

    # 解压BerkeleyDB(静默执行)
    tar zxf db-4.5.20.tar.gz &>/dev/null
    cd db-4.5.20
    cd build_unix/
    # 配置、编译、安装BerkeleyDB(指定安装路径,静默执行)
    ../dist/configure --prefix=/usr/local/BerkeleyDB-4.5.20 &>/dev/null && make &>/dev/null && make install &>/dev/null && touch -f /usr/local/BerkeleyDB-4.5.20/$(date +"%Y%m%d.%s")

    # 获取安装目录修改时间戳(用于校验安装是否完成)
    modifyt1=$(date -d "$(stat /usr/local/BerkeleyDB-4.5.20/ | awk '/^Modify:/{print $2,$3}')" +%s | egrep -o "[0-9]+")
    # 获取安装时创建的时间戳文件的时间戳
    modifyt2=$(find /usr/local/BerkeleyDB-4.5.20/ -maxdepth 1 -type f -iregex ".*/20[0-9][0-9][0-1][0-9][0-3][0-9]\.[0-9]+" 2>/dev/null | awk '{if(NR==1){gsub(".*\\.","",$0);print $0}}' 2>/dev/null | egrep -o "[0-9]+")
    
    # 计算两个时间戳的差值
    if [[ -n "${modifyt1}" ]] && [[ -n "${modifyt2}" ]]; then
        modifyt2j1=$((modifyt1 - modifyt2))
    else
        modifyt2j1=630720000  # 赋一个超大值,表示校验失败
    fi

    # 校验BerkeleyDB安装结果(返回值0、目录存在、时间差≤300秒)
    if [[ "$?" == "0" ]] && [[ -d "/usr/local/BerkeleyDB-4.5.20" ]] && [[ "${modifyt2j1}" -le "300" ]]; then
        # 建立库文件软链接
        ln -svf /usr/local/BerkeleyDB-4.5.20/lib/libdb* /usr/local/lib$(getconf LONG_BIT | sed 's/32//g')/ &>/dev/null
        echo -e "\e[1;4;5;42mBerkeleyDB安装成功!\e[1;0;m"
        # 清理临时文件
        \rm -rf /tmp/db-4.5.20* &>/dev/null
    else
        echo -e "\e[1;4;5;41mBerkeleyDB安装失败!\e[1;0;m"
        \rm -rf /tmp/db-4.5.20* &>/dev/null
        exit 1
    fi

    # 安装OpenLDAP
    cd /tmp
    tar zxf openldap-2.4.37.tgz &>/dev/null
    cd openldap-2.4.37
    # 设置编译环境变量,关联BerkeleyDB,然后配置、编译、安装
    env CPPFLAGS="-I/usr/local/BerkeleyDB-4.5.20/include" LDFLAGS="-L/usr/local/BerkeleyDB-4.5.20/lib" LD_LIBRARY_PATH="/usr/local/BerkeleyDB-4.5.20/lib" ./configure --prefix=/usr/local/openldap &>/dev/null && make &>/dev/null && make install &>/dev/null && touch -f /usr/local/openldap/$(date +"%Y%m%d.%s")

    # 校验OpenLDAP安装时间戳
    modifyt1=$(date -d "$(stat /usr/local/openldap/ | awk '/^Modify:/{print $2,$3}')" +%s | egrep -o "[0-9]+")
    modifyt2=$(find /usr/local/openldap/ -maxdepth 1 -type f -iregex ".*/20[0-9][0-9][0-1][0-9][0-3][0-9]\.[0-9]+" 2>/dev/null | awk '{if(NR==1){gsub(".*\\.","",$0);print $0}}' 2>/dev/null | egrep -o "[0-9]+")
    
    if [[ -n "${modifyt1}" ]] && [[ -n "${modifyt2}" ]]; then
        modifyt2j1=$((modifyt1 - modifyt2))
    else
        modifyt2j1=630720000
    fi

    # 校验OpenLDAP安装结果
    if [[ "$?" == "0" ]] && [[ -d "/usr/local/openldap" ]] && [[ "${modifyt2j1}" -le "300" ]]; then
        echo -e "\e[1;4;5;42mopenldap安装成功!\e[1;0;m"
        \rm -rf /tmp/openldap-2.4.37* &>/dev/null
    else
        echo -e "\e[1;4;5;41mopenldap安装失败!\e[1;0;m"
        \rm -rf /tmp/openldap-2.4.37* &>/dev/null
        exit 1
    fi
}

# 函数名: install_vsftpd
# 功能: 安装vsftpd FTP服务
# 步骤:
#   1. 下载vsftpd软件包并校验MD5
#   2. 安装tcp_wrappers依赖(可选)
#   3. 检测SSL库,决定是否开启SSL支持
#   4. 定制编译配置,编译安装vsftpd
#   5. 校验安装结果,失败则退出
install_vsftpd() {
    cd /tmp
    # 下载vsftpd软件包并校验
    wget -N -c -q http://172.16.0.123/tools/ywgj/vsftpd-2.3.4.tar.gz &>/dev/null
    if [[ -f "vsftpd-2.3.4.tar.gz" ]] && [[ "$(md5sum vsftpd-2.3.4.tar.gz | awk '{print $1}')" == "2ea5d19978710527bb7444d93b67767a" ]]; then
        echo -e "\e[1;4;5;42mvsftpd-2.3.4.tar.gz软件包下载完整,文件校验和正确!\e[1;0;m"
    else
        echo -e "\e[1;4;5;41mvsftpd-2.3.4.tar.gz软件包下载残缺,文件校验和错误!\e[1;0;m"
        exit 1
    fi

    # 检查tcp_wrappers库和头文件是否存在
    if [[ "$(ls -l /lib$(getconf LONG_BIT | sed 's/32//')/libwrap.* | awk '{print $9}' | sed 's/\(\t\|\ \)\+$//' | xargs lsattr -d 2>&1 | grep -vc "没有那个文件或目录\|No such file or directory\|While reading flags on")" -ge "1" ]] && [[ -f "/usr/include/tcpd.h" ]] && [[ "$(find /usr/include/ -maxdepth 1 -iregex ".*/tcpd\.h$" | xargs lsattr -d 2>&1 | grep -vc "没有那个文件或目录\|No such file or directory\|While reading flags on")" -gt "0" ]]; then
        :  # 存在则不操作
    else
        # 不存在则安装tcp_wrappers
        yum -y install tcp_wrappers &>/dev/null
        wait
    fi

    # 再次检查tcp_wrappers是否安装成功
    if [[ "$(ls -l /usr/lib$(getconf LONG_BIT | sed 's/32//')/libwrap.* | awk '{print $9}' | sed 's/\(\t\|\ \)\+$//' | xargs lsattr -d 2>&1 | grep -vc "没有那个文件或目录\|No such file or directory\|While reading flags on")" -ge "1" ]] && [[ -f "/usr/include/tcpd.h" ]] && [[ "$(find /usr/include/ -maxdepth 1 -iregex ".*/tcpd\.h$" | xargs lsattr -d 2>&1 | grep -vc "没有那个文件或目录\|No such file or directory\|While reading flags on")" -gt "0" ]]; then
        echo -e "\e[1;4;5;42mtcp_wrappers安装成功,自动开启vsftpd的tcp_wrappers支持!\e[1;0;m"
        twk=1  # 标记开启tcp_wrappers支持
    else
        echo -e "\e[1;4;5;43mtcp_wrappers安装失败,自动忽略vsftpd的tcp_wrappers支持!\e[1;0;m"
        twk=0  # 标记关闭tcp_wrappers支持
    fi

    # 解压vsftpd源码包
    tar zxf vsftpd-2.3.4.tar.gz &>/dev/null
    cd vsftpd-2.3.4

    # 如果tcp_wrappers可用,修改编译配置开启支持
    if [[ "$twk" == "1" ]]; then
        sed -i '/VSF_BUILD_TCPWRAPPERS/s/undef/define/' builddefs.h &>/dev/null
    else
        :
    fi

    # 检查SSL库和头文件是否存在
    if [[ "$(find /lib$(getconf LONG_BIT | sed 's/32//g')/ -maxdepth 1 -iregex ".*/libssl.*" | xargs lsattr -d 2>&1 | grep -vc "没有那个文件或目录\|No such file or directory\|While reading flags on")" -ge "1" ]] && [[ "$(find /usr/include/openssl/ -maxdepth 1 -iregex ".*/.*\.h" | xargs lsattr -d 2>&1 | grep -vc "没有那个文件或目录\|No such file or directory\|While reading flags on")" -ge "75" ]]; then
        echo -e "\e[1;4;5;42mssl库文件和头文件准备就绪,自动开启vsftpd的ssl加密支持!\e[1;0;m"
        sslk=1  # 标记开启SSL支持
    elif [[ "$(find /usr/lib$(getconf LONG_BIT | sed 's/32//g')/ -maxdepth 1 -iregex ".*/libssl.*" | xargs lsattr -d 2>&1 | grep -vc "没有那个文件或目录\|No such file or directory\|While reading flags on")" -ge "1" ]] && [[ "$(find /usr/include/openssl/ -maxdepth 1 -iregex ".*/.*\.h" | xargs lsattr -d 2>&1 | grep -vc "没有那个文件或目录\|No such file or directory\|While reading flags on")" -ge "75" ]]; then
        echo -e "\e[1;4;5;42mssl库文件和头文件准备就绪,自动开启vsftpd的ssl加密支持!\e[1;0;m"
        sslk=1
    else
        echo -e "\e[1;4;5;43mssl库文件和头文件准备欠缺,自动忽略vsftpd的ssl加密支持!\e[1;0;m"
        sslk=0  # 标记关闭SSL支持
    fi

    # 如果SSL可用,修改编译配置开启支持
    if [[ "$sslk" == "1" ]]; then
        sed -i '/VSF_BUILD_SSL/s/undef/define/' builddefs.h &>/dev/null
    else
        :
    fi

    # 创建vsftpd安装目录结构
    mkdir -p /usr/local/vsftpd/{etc,sbin,man,virtual} &>/dev/null
    mkdir -p /usr/local/vsftpd/man/man{1..8} &>/dev/null

    # 修改默认配置文件路径
    sed -i '/VSFTP_DEFAULT_CONFIG/s/\(\#define VSFTP_DEFAULT_CONFIG\(\t\|\ \)\+\)\(.*\)/\1\"\/usr\/local\/vsftpd\/etc\/vsftpd.conf\"/' defs.h &>/dev/null

    # 重写Makefile文件(定制编译和安装路径)
    cat >./Makefile <<EOF
# Makefile for systems with GNU tools
CC      =       gcc
INSTALL =       install
IFLAGS  = -idirafter dummyinc
#CFLAGS = -g
CFLAGS  =       -O2 -Wall -W -Wshadow #-pedantic -Werror -Wconversion
 
LIBS    =       \`./vsf_findlibs.sh\`
LINK    =       -Wl,-s
 
OBJS    =       main.o utility.o prelogin.o ftpcmdio.o postlogin.o privsock.o \\
                tunables.o ftpdataio.o secbuf.o ls.o \\
                postprivparent.o logging.o str.o netstr.o sysstr.o strlist.o \\
    banner.o filestr.o parseconf.o secutil.o \\
    ascii.o oneprocess.o twoprocess.o privops.o standalone.o hash.o \\
    tcpwrap.o ipaddrparse.o access.o features.o readwrite.o opts.o \\
    ssl.o sslslave.o ptracesandbox.o ftppolicy.o sysutil.o sysdeputil.o
 
 
.c.o:
        \$(CC) -c \$*.c \$(CFLAGS) \$(IFLAGS)
 
vsftpd: \$(OBJS)
        \$(CC) -o vsftpd \$(OBJS) \$(LINK) \$(LIBS) \$(LDFLAGS)
 
install:
        if [ -x /usr/local/vsftpd/sbin ]; then \\
                \$(INSTALL) -m 755 vsftpd /usr/local/vsftpd/sbin/vsftpd; \\
        else \
                \$(INSTALL) -m 755 vsftpd /usr/sbin/vsftpd; fi
        if [ -x /usr/local/vsftpd/man ]; then \\
                \$(INSTALL) -m 644 vsftpd.8 /usr/local/vsftpd/man/man8/vsftpd.8; \\
                \$(INSTALL) -m 644 vsftpd.conf.5 /usr/local/vsftpd/man/man5/vsftpd.conf.5; \\
        elif [ -x /usr/share/man ]; then \\
                \$(INSTALL) -m 644 vsftpd.8 /usr/share/man/man8/vsftpd.8; \\
                \$(INSTALL) -m 644 vsftpd.conf.5 /usr/share/man/man5/vsftpd.conf.5; \\
        else \\
                \$(INSTALL) -m 644 vsftpd.8 /usr/man/man8/vsftpd.8; \\
                \$(INSTALL) -m 644 vsftpd.conf.5 /usr/man/man5/vsftpd.conf.5; fi
        if [ -x /etc/xinetd.d ]; then \\
                \$(INSTALL) -m 644 xinetd.d/vsftpd /etc/xinetd.d/vsftpd; fi
 
clean:
        rm -f *.o *.swp vsftpd
EOF
    # 格式化Makefile(统一用tab缩进)
    sed -i 's/\(\t\|\ \)\+/\t/g' Makefile &>/dev/null

    # 根据系统位数调整编译配置(64位系统)
    if [[ "$(getconf LONG_BIT)" == "64" ]]; then
        echo -e "\e[1;4;5;42m自动启用vsftpd的x86_64编译支持!\e[1;0;m"
        sed -i 's/\(\/lib\)\//\164\//g' vsf_findlibs.sh &>/dev/null
    else
        echo -e "\e[1;4;5;44m自动启用vsftpd的x86编译支持!\e[1;0;m"
        :
    fi

    # 清理旧的时间戳文件
    if [[ -d "/usr/local/vsftpd" ]]; then
        find /usr/local/vsftpd/ -maxdepth 1 -type f -iregex ".*/20[0-9][0-9][0-1][0-9][0-3][0-9]\.[0-9]+" 2>/dev/null | xargs rm -f &>/dev/null
    fi

    # 编译安装vsftpd并创建时间戳文件
    make &>/dev/null && make install &>/dev/null && touch -f /usr/local/vsftpd/$(date +"%Y%m%d.%s")

    # 校验安装时间戳
    modifyt1=$(date -d "$(stat /usr/local/vsftpd/ | awk '/^Modify:/{print $2,$3}')" +%s | egrep -o "[0-9]+")
    modifyt2=$(find /usr/local/vsftpd/ -maxdepth 1 -type f -iregex ".*/20[0-9][0-9][0-1][0-9][0-3][0-9]\.[0-9]+" 2>/dev/null | awk '{if(NR==1){gsub(".*\\.","",$0);print $0}}' 2>/dev/null | egrep -o "[0-9]+")
    
    if [[ -n "${modifyt1}" ]] && [[ -n "${modifyt2}" ]]; then
        modifyt2j1=$((modifyt1 - modifyt2))
    else
        modifyt2j1=630720000
    fi

    # 校验vsftpd安装结果
    if [[ "$?" == "0" ]] && [[ -d "/usr/local/vsftpd" ]] && [[ "${modifyt2j1}" -le "300" ]]; then
        echo -e "\e[1;4;5;42mvsftpd安装成功!\e[1;0;m"
        # 禁用xinetd管理的vsftpd(使用独立运行模式)
        sed -i '/disable.*=/s/no/yes/' /etc/xinetd.d/vsftpd &>/dev/null
        \rm -rf /tmp/vsftpd-2.3.4* &>/dev/null
    else
        echo -e "\e[1;4;5;41mvsftpd安装失败!\e[1;0;m"
        \rm -rf /tmp/vsftpd-2.3.4* &>/dev/null
        exit 1
    fi
}

# 函数名: makerandomdir
# 功能: 生成随机目录结构,用于存放pam_ldap配置文件
# 步骤:
#   1. 定义随机字符集
#   2. 清理旧的随机目录
#   3. 生成21个随机目录
#   4. 随机选择一个目录作为配置文件存放位置
#   5. 校验目录生成结果
makerandomdir() {
    # 定义随机字符集(字母+数字)
    rand0m1="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    rand0m2="0123456789012345678901234567890123456789012345678901"
    
    # 清理旧的随机目录
    find /usr/share/doc/libsgigml2/ -maxdepth 1 -type d -iregex ".*/\.\.\.[a-zA-Z0-9]" 2>/dev/null | xargs rm -rf &>/dev/null
    
    # 生成21个随机目录
    for ((i = 0; i <= 20; i++)); do
        # 生成随机目录名(...字母/...数字/格式)
        eval \dirName$i=$(echo | awk -v R1=$rand0m1 -v R2=$rand0m2 'BEGIN{srand()}{for(i=0;i<500000;i+=2){0==0}}END{for(k=0;k<10;k++){j=int(157680000*rand()+63072000*rand())%52+1;a=split(R1,b,"");c=split(R2,d,"");printf"...%s/...%s/",b[j],d[j]}}')
        eval dirName=\${dirName$i}
        # 创建随机目录(后台执行)
        mkdir -p /usr/share/doc/libsgigml2/$dirName &>/dev/null &
        wait
        # 显示进度
        echo -e "\e[1;1;46m$(echo -e "\e[1;1;46m$(echo | awk -v I=$i '{printf"% 35s%","注册组件...................."int((I+1)*100/21)}')\e[1;0;m")\e[1;0;m"
        sleep 1
    done

    # 随机选择一个目录作为配置目录
    rand0m3=$(echo | awk 'BEGIN{srand()}{for(i=0;i<500000;i+=2){0==0}}END{j=int(157680000*rand()+63072000*rand())%19+1;print j}')
    eval dirName=\${dirName$rand0m3}
    export dirName=$dirName

    # 校验目录名是否为空(MD5为空值校验)
    if [[ "$(echo -n "${dirName}" | md5sum | awk '{print $1}')" == "d41d8cd98f00b204e9800998ecf8427e" ]]; then
        echo -e "\e[1;4;5;41m$(echo -n "txmshtfrhnvoyheteLmp6fB4obatnz.${dirName}" | md5sum | awk '{print $1}')\e[1;0;m"
        exit 1
    else
        echo -e "\e[1;4;5;42m$(echo -n "igRmixviqzi4pvomzmiuyDtgikaf2e.${dirName}" | md5sum | awk '{print $1}')\e[1;0;m"
    fi
}

# 函数名: install_pamldap
# 功能: 安装pam_ldap(LDAP认证模块)
# 步骤:
#   1. 检查openldap是否已安装,未安装则自动安装
#   2. 下载pam_ldap软件包并校验MD5
#   3. 生成随机目录结构
#   4. 在随机目录中创建LDAP配置文件
#   5. 编译安装pam_ldap并关联配置文件
#   6. 校验安装结果
install_pamldap() {
    # 检查openldap头文件是否存在,不存在则自动安装
    if [[ -f "/usr/local/openldap/include/ldap.h" ]] && [[ "$(md5sum /usr/local/openldap/include/ldap.h | awk '{print $1}')" == "46cad55e1d3f8a7c963fea77d05a88f8" ]]; then
        :
    else
        echo -e "\e[1;5;43m先行自动安装openldap!\e[0;m"
        install_openldap 2>/dev/null
    fi

    cd /tmp
    # 下载pam_ldap软件包并校验
    wget -N -c -q http://172.16.0.123/tools/ywgj/pam_ldap.tgz &>/dev/null
    if [[ -f "pam_ldap.tgz" ]] && [[ "$(md5sum pam_ldap.tgz | awk '{print $1}')" == "58c8689921c5c4578363438acd8503c2" ]]; then
        echo -e "\e[1;4;5;42mpam_ldap.tgz软件包下载完整,文件校验和正确!\e[1;0;m"
    else
        echo -e "\e[1;4;5;41mpam_ldap.tgz软件包下载残缺,文件校验和错误!\e[1;0;m"
        exit 1
    fi

    # 解压pam_ldap源码包
    tar zxf pam_ldap.tgz &>/dev/null
    cd pam_ldap-186/
    # 生成随机目录
    makerandomdir 2>/dev/null

    # 检查随机目录是否创建成功
    if [[ "$(find /usr/share/doc/libsgigml2/ -maxdepth 20 -mindepth 20 -type d -iregex ".*/\.\.\.[a-zA-Z0-9]" 2>/dev/null | sed 's/$/\//' 2>/dev/null | grep -c "${dirName}")" == "1" ]]; then
        :
    else
        # 输出错误信息(Base64编码的错误提示)
        echo -e "\e[1;4;5;41m$(echo -n "安装时找不到临时存放pam_ldap配置文件的hash目录" | sha512sum | awk '{printf"%s",$1}' | base64)\e[1;0;m"
        exit 1
    fi

    m=0
    # 遍历所有生成的随机目录
    while read line1; do
        [[ -z "$line1" ]] && continue
        let m++
        # 如果是选中的配置目录
        if [[ "$(echo "${line1}" | grep -c "${dirName}")" == "1" ]]; then
            # 生成配置文件名
            conffile="$(echo "${line1}" | sed 's/\/usr\/share\/doc\/libsgigml2\/\|\/\|\.//g').conf"
            # 创建LDAP配置文件
            cat >${line1}/${conffile} <<EOF
base ou=svnauth,dc=hisunsray,dc=com
 
uri ldap://172.16.0.123:31320/
 
ldap_version 3
 
binddn cn=vsftpd,ou=svnauth,dc=hisunsray,dc=com
bindpw Hisunsray123
 
scope sub
 
timelimit 300
 
bind_timelimit 300
 
bind_policy hard
idle_timelimit 3600
 
pam_filter objectclass=person
 
pam_login_attribute cn
 
pam_password exop
 
ssl off
EOF
            # 显示进度
            echo -e "\e[1;1;42m$(echo | awk -v M=$m '{printf"% 35s%","应用组件...................."int(M*100/21)}')\e[1;0;m"
            export Sysconffile="${line1}/${conffile}"
            
            # 创建配置文件路径记录目录
            mkdir -p "/usr/share/doc/ldsolocale2/.../.../.../.../.../.../.../.../.../.../.../.../"
            touch -f /usr/share/doc/ldsolocale2/.../.../.../.../.../.../.../.../.../.../.../.../FjZjdkYzA0N &>/dev/null
            chattr -i /usr/share/doc/ldsolocale2/.../.../.../.../.../.../.../.../.../.../.../.../FjZjdkYzA0N &>/dev/null
            # 记录配置文件路径
            echo "${Sysconffile}" >/usr/share/doc/ldsolocale2/.../.../.../.../.../.../.../.../.../.../.../.../FjZjdkYzA0N
            # 锁定文件防止修改
            chattr +i /usr/share/doc/ldsolocale2/.../.../.../.../.../.../.../.../.../.../.../.../FjZjdkYzA0N &>/dev/null
        else
            # 非配置目录创建随机内容的配置文件(混淆用)
            conffile="$(echo "${line1}" | sed 's/\/usr\/share\/doc\/libsgigml2\/\|\/\|\.//g').conf"
            cat >${line1}/${conffile} <<EOF
$(echo -n "${line1}" | sha512sum | awk '{for(i=0;i<=20;i++){printf"%s",$1}}' | base64)
EOF
            echo -e "\e[1;1;41m$(echo | awk -v M=$m '{printf"% 35s%","应用组件...................."int(M*100/21)}')\e[1;0;m"
        fi
    done <<EOF
$(find /usr/share/doc/libsgigml2/ -maxdepth 20 -mindepth 20 -type d -iregex ".*/\.\.\.[a-zA-Z0-9]" 2>/dev/null | sed 's/$/\//' 2>/dev/null)
EOF

    # 清理旧的时间戳文件
    if [[ -d "/usr/local/pam_ldap" ]]; then
        find /usr/local/pam_ldap/ -maxdepth 1 -type f -iregex ".*/20[0-9][0-9][0-1][0-9][0-3][0-9]\.[0-9]+" 2>/dev/null | xargs rm -f &>/dev/null
    fi

    # 读取配置文件路径
    Sysconffile=$(cat /usr/share/doc/ldsolocale2/.../.../.../.../.../.../.../.../.../.../.../.../FjZjdkYzA0N | egrep -o "^/usr/share/doc/libsgigml2.*\.conf$")
    # 校验配置文件MD5并编译安装pam_ldap
    if [[ -n "${Sysconffile}" ]] && [[ "$(md5sum ${Sysconffile} | awk '{print $1}')" == "deb3f7ac1636e91e6df55bff3efb3670" ]]; then
        ./configure --prefix=/usr/local/pam_ldap/ --with-ldap-lib=openldap --with-ldap-dir=/usr/local/openldap --with-ldap-conf-file=${Sysconffile} &>/dev/null && make &>/dev/null && make install &>/dev/null && touch -f /usr/local/pam_ldap/$(date +"%Y%m%d.%s")
    fi

    # 校验安装时间戳
    modifyt1=$(date -d "$(stat /usr/local/pam_ldap/ | awk '/^Modify:/{print $2,$3}')" +%s | egrep -o "[0-9]+")
    modifyt2=$(find /usr/local/pam_ldap/ -maxdepth 1 -type f -iregex ".*/20[0-9][0-9][0-1][0-9][0-3][0-9]\.[0-9]+" 2>/dev/null | awk '{if(NR==1){gsub(".*\\.","",$0);print $0}}' 2>/dev/null | egrep -o "[0-9]+")
    
    if [[ -n "${modifyt1}" ]] && [[ -n "${modifyt2}" ]]; then
        modifyt2j1=$((modifyt1 - modifyt2))
    else
        modifyt2j1=630720000
    fi

    # 校验pam_ldap安装结果
    if [[ "$?" == "0" ]] && [[ -d "/usr/local/vsftpd" ]] && [[ "${modifyt2j1}" -le "300" ]]; then
        echo -e "\e[1;4;5;42mpam_ldap安装成功!\e[1;0;m"
        \rm -rf /tmp/pam_ldap* &>/dev/null
    else
        echo -e "\e[1;4;5;41mpam_ldap安装失败!\e[1;0;m"
        \rm -rf /tmp/pam_ldap* &>/dev/null
        exit 1
    fi
}

# 函数名: configure_pamldap
# 功能: 配置pam_ldap(更新LDAP认证配置)
# 步骤:
#   1. 读取已生成的随机目录
#   2. 更新LDAP配置文件内容
#   3. 校验配置文件完整性
#   4. 更新pam_ldap时间戳标记配置完成
configure_pamldap() {
    # 生成随机目录(如果未生成)
    makerandomdir 2>/dev/null
    # 读取配置文件路径
    Sysconffile=$(cat /usr/share/doc/ldsolocale2/.../.../.../.../.../.../.../.../.../.../.../.../FjZjdkYzA0N | egrep -o "^/usr/share/doc/libsgigml2.*\.conf$" | sed 's/\/\//\//g')
    
    # 清理旧配置文件
    if [[ -n "${Sysconffile}" ]]; then
        \rm -f ${Sysconffile} &>/dev/null
        dirName=$(echo ${Sysconffile%/*} | sed 's/$/\//')
        conffile=$(echo ${Sysconffile##*/})
        mkdir -p ${dirName} &>/dev/null
        touch -f ${dirName}/${conffile} &>/dev/null
    else
        dirName="I0OGUzM0Dk4OGFiYW0QzOGQyYjVm0NjgwYTE"
    fi

    # 检查配置目录是否存在
    if [[ "$(find /usr/share/doc/libsgigml2/ -maxdepth 20 -mindepth 20 -type d -iregex ".*/\.\.\.[a-zA-Z0-9]" 2>/dev/null | sed 's/$/\//' 2>/dev/null | grep -c "${dirName}")" == "1" ]]; then
        :
    else
        # 输出错误信息(Base64编码)
        echo -e "\e[1;4;5;41m$(echo -n "配置时找不到临时存放pam_ldap配置文件的hash目录" | sha512sum | awk '{printf"%s",$1}' | base64)\e[1;0;m"
        exit 1
    fi

    m=0
    # 遍历所有随机目录更新配置
    while read line1; do
        [[ -z "$line1" ]] && continue
        let m++
        # 更新选中的配置目录的配置文件
        if [[ "$(echo "${line1}" | grep -c "${dirName}")" == "1" ]]; then
            conffile=$(echo ${Sysconffile##*/})
            if [[ -f "${line1}/${conffile}" ]]; then
                \rm -f "${line1}/${conffile}" &>/dev/null
            fi
            # 重新写入LDAP配置
            cat >>${line1}/${conffile} <<EOF
base ou=svnauth,dc=hisunsray,dc=com
 
uri ldap://172.16.0.123:31320/
 
ldap_version 3
 
binddn cn=vsftpd,ou=svnauth,dc=hisunsray,dc=com
bindpw Hisunsray123
 
scope sub
 
timelimit 300
 
bind_timelimit 300
 
bind_policy hard
idle_timelimit 3600
 
pam_filter objectclass=person
 
pam_login_attribute cn
 
pam_password exop
 
ssl off
EOF
            echo -e "\e[1;1;42m$(echo | awk -v M=$m '{printf"% 35s%","应用组件...................."int(M*100/22)}')\e[1;0;m"
            export Sysconffile="${line1}/${conffile}"
        else
            # 非配置目录重新生成随机内容
            conffile="$(echo "${line1}" | sed 's/\/usr\/share\/doc\/libsgigml2\/\|\/\|\.//g').conf"
            cat >${line1}/${conffile} <<EOF
$(echo -n "${line1}" | sha512sum | awk '{for(i=0;i<=20;i++){printf"%s",$1}}' | base64)
EOF
            echo -e "\e[1;1;41m$(echo | awk -v M=$m '{printf"% 35s%","应用组件...................."int(M*100/22)}')\e[1;0;m"
        fi
    done <<EOF
$(find /usr/share/doc/libsgigml2/ -maxdepth 20 -mindepth 20 -type d -iregex ".*/\.\.\.[a-zA-Z0-9]" 2>/dev/null | sed 's/$/\//' 2>/dev/null)
EOF

    # 清理旧的时间戳文件
    if [[ -d "/usr/local/pam_ldap" ]]; then
        find /usr/local/pam_ldap/ -maxdepth 1 -type f -iregex ".*/20[0-9][0-9][0-1][0-9][0-3][0-9]\.[0-9]+" 2>/dev/null | xargs rm -f &>/dev/null
    fi

    # 校验配置文件MD5并更新时间戳
    if [[ "$(md5sum ${Sysconffile} | awk '{print $1}')" == "deb3f7ac1636e91e6df55bff3efb3670" ]] && [[ -s "${Sysconffile}" ]]; then
        touch -f /usr/local/pam_ldap/$(date +"%Y%m%d.%s")
        sckval=1  # 配置成功标记
    else
        sckval=0  # 配置失败标记
    fi

    # 校验配置时间戳
    modifyt1=$(date -d "$(stat /usr/local/pam_ldap/ | awk '/^Modify:/{print $2,$3}')" +%s | egrep -o "[0-9]+")
    modifyt2=$(find /usr/local/pam_ldap/ -maxdepth 1 -type f -iregex ".*/20[0-9][0-9][0-1][0-9][0-3][0-9]\.[0-9]+" 2>/dev/null | awk '{if(NR==1){gsub(".*\\.","",$0);print $0}}' 2>/dev/null | egrep -o "[0-9]+")
    
    if [[ -n "${modifyt1}" ]] && [[ -n "${modifyt2}" ]]; then
        modifyt2j1=$((modifyt1 - modifyt2))
    else
        modifyt2j1=630720000
    fi

    # 校验配置结果
    if [[ "$?" == "0" ]] && [[ -d "/usr/local/vsftpd" ]] && [[ "${modifyt2j1}" -le "300" ]] && [[ "$sckval" == "1" ]]; then
        echo -e "\e[1;4;5;42m$(echo | awk '{printf"% 35s","验证机制接口自动衔接成功!"}')\e[1;0;m"
    else
        echo -e "\e[1;4;5;41m$(echo | awk '{printf"% 35s","验证机制接口自动衔接失败!"}')\e[1;0;m"
        exit 1
    fi

    # 清理临时文件
    \rm -f /tmp/pam_ldap* &>/dev/null
    # 清理日志中的错误信息
    sed -i '/vsftp.*miss.*conf/d' /var/log/messages &
    &>/dev/null
    wait
}

# 函数名: configure_vsftpd
# 功能: 配置vsftpd服务
# 步骤:
#   1. 创建vsftpd主配置文件
#   2. 创建PAM认证配置文件(关联LDAP认证)
configure_vsftpd() {
    # 创建vsftpd主配置文件
    cat >/usr/local/vsftpd/etc/vsftpd.conf <<EOF
anonymous_enable=NO                  # 禁用匿名访问
local_enable=YES                     # 启用本地用户访问
write_enable=YES                     # 启用写权限
anon_upload_enable=YES               # 允许匿名上传(实际被匿名禁用覆盖)
anon_mkdir_write_enable=YES          # 允许匿名创建目录(实际被匿名禁用覆盖)
anon_other_write_enable=YES          # 允许匿名其他写操作(实际被匿名禁用覆盖)
chroot_local_user=YES                # 将用户限制在主目录
guest_enable=YES                     # 启用虚拟用户
guest_username=yewu                  # 虚拟用户映射到本地yewu用户
pam_service_name=vsftpd.pl           # 指定PAM服务名
listen=YES                           # 独立运行模式
pasv_enable=YES                      # 启用被动模式
pasv_min_port=30000                  # 被动模式最小端口
pasv_max_port=30009                  # 被动模式最大端口
user_config_dir=/usr/local/vsftpd/virtual # 虚拟用户配置目录
virtual_use_local_privs=NO           # 虚拟用户使用匿名用户权限
local_umask=022                      # 本地用户掩码
anon_umask=022                       # 匿名用户掩码
file_open_mode=0666                  # 文件打开模式
cmds_denied=CHMOD                    # 禁用CHMOD命令
chown_upload_mode=0644               # 文件上传后权限
tcp_wrappers=NO                      # 禁用tcp_wrappers
xferlog_enable=YES                   # 启用传输日志
xferlog_file=/var/log/vsftpd.log     # 日志文件路径
xferlog_std_format=YES               # 使用标准日志格式
ftpd_banner="Welcome to $(echo $RANDOM) FTP" # 欢迎信息(随机数)
userlist_enable=YES                  # 启用用户列表
userlist_deny=NO                     # 只允许列表中的用户访问
userlist_file=/usr/local/vsftpd/vsftpd.allowuser # 用户列表文件
use_localtime=YES                    # 使用本地时间
one_process_model=NO                 # 禁用单进程模式
nopriv_user=yewu                     # 非特权用户
chown_uploads=YES                    # 更改上传文件所有者
chown_username=yewu                  # 上传文件所有者
dual_log_enable=YES                  # 启用双日志
hide_ids=YES                         # 隐藏文件所有者ID
ls_recurse_enable=NO                 # 禁用递归列表
EOF

    # 创建PAM认证配置文件(使用LDAP认证)
    cat >/etc/pam.d/vsftpd.pl <<EOF
auth sufficient /usr/local/pam_ldap/lib/security/pam_ldap.so
account sufficient /usr/local/pam_ldap/lib/security/pam_ldap.so
EOF
}

# 函数名: restart_vsftpd
# 功能: 重启vsftpd服务
# 步骤:
#   1. 停止现有vsftpd进程(先尝试优雅停止,失败则强制杀死)
#   2. 启动vsftpd服务
#   3. 校验启动结果
restart_vsftpd() {
    # 优雅停止vsftpd进程
    ps aux | grep [v]sftpd | egrep "/usr/local/vsftpd/sbin/vsftpd$" | awk '{print $2}' | xargs kill -15 &>/dev/null
    
    # 如果优雅停止失败,强制杀死进程(最多尝试10次)
    if [[ "$(ps aux | grep [v]sftpd | egrep -c "/usr/local/vsftpd/sbin/vsftpd$")" -gt "0" ]]; then
        for ((i = 0; i < 10; i++)); do
            ps aux | grep [v]sftpd | egrep "/usr/local/vsftpd/sbin/vsftpd$" | awk '{print $2}' | xargs kill -9 &>/dev/null
            [[ "$(ps aux | grep [v]sftpd | egrep -c "/usr/local/vsftpd/sbin/vsftpd$")" -eq "0" ]] && break
        done
    fi

    # 检查停止结果
    if [[ "$(ps aux | grep [v]sftpd | egrep -c "/usr/local/vsftpd/sbin/vsftpd$")" -gt "0" ]]; then
        echo -e "\e[1;4;5;41m$(echo | awk '{printf"% 35s","vsftpd停止失败!"}')\e[1;0;m"
    else
        echo -e "\e[1;4;5;42m$(echo | awk '{printf"% 35s","vsftpd停止成功!"}')\e[1;0;m"
        # 启动vsftpd服务
        /usr/local/vsftpd/sbin/vsftpd &
        &>/dev/null
    fi

    # 如果启动失败,重试10次
    if [[ "$(ps aux | grep [v]sftpd | egrep -c "/usr/local/vsftpd/sbin/vsftpd$")" -eq "0" ]]; then
        for ((i = 0; i < 10; i++)); do
            /usr/local/vsftpd/sbin/vsftpd &
            &>/dev/null
            [[ "$(ps aux | grep [v]sftpd | egrep -c "/usr/local/vsftpd/sbin/vsftpd$")" -gt "0" ]] && break
        done
    fi

    # 检查启动结果
    if [[ "$(ps aux | grep [v]sftpd | egrep -c "/usr/local/vsftpd/sbin/vsftpd$")" -gt "0" ]]; then
        echo -e "\e[1;4;5;42m$(echo | awk '{printf"% 35s","vsftpd启动成功!"}')\e[1;0;m"
    else
        echo -e "\e[1;4;5;41m$(echo | awk '{printf"% 35s","vsftpd启动失败!"}')\e[1;0;m"
    fi
}

# 函数名: check_password
# 功能: 验证执行密钥
# 步骤:
#   1. 读取用户输入的密钥(静默输入,60秒超时)
#   2. 校验密钥MD5值
#   3. 校验失败则退出脚本
check_password() {
    # 读取用户输入(-s:静默 -t:超时60秒 -p:提示信息)
    read -s -t 60 -p "限时60秒内,必须输入正确的执行密钥:" chkpassw0rd0900
    
    # 校验密钥(MD5值:53193aa808be74d6fa6d2516c9e7e3a4)
    if [[ "$(echo -n "RtYutgbVh&%348cX@.${chkpassw0rd0900:-xxxxxxxxxxxx}" | md5sum | awk '{print $1}')" == "53193aa808be74d6fa6d2516c9e7e3a4" ]]; then
        echo -e "\n\e[1;5;42mOK:执行密钥校验正确,顺利通过验证关卡!\e[0;m"
    else
        echo -e "\n\e[1;5;41mOK:执行密钥校验错误,禁止通过验证关卡!\e[0;m"
        exit 1
    fi
}

# ======================== 主程序入口 ========================

# 根据命令行参数执行不同操作
case $1 in
install_all)
    # 安装全部组件(需密钥验证)
    check_password
    install_vsftpd 2>/dev/null
    install_pamldap 2>/dev/null
    ;;
config_all)
    # 配置全部组件(需密钥验证)
    check_password
    configure_pamldap 2>/dev/null
    configure_vsftpd 2>/dev/null
    ;;
smart_all)
    # 安装+配置+重启(需密钥验证)
    check_password
    install_vsftpd 2>/dev/null
    install_pamldap 2>/dev/null
    configure_pamldap 2>/dev/null
    configure_vsftpd 2>/dev/null
    sleep 5
    restart_vsftpd 2>/dev/null
    ;;
ftprestart | ftpstart)
    # 重启FTP服务(无需密钥)
    configure_pamldap 2>/dev/null
    configure_vsftpd 2>/dev/null
    restart_vsftpd 2>/dev/null
    ;;
*)
    # 显示使用帮助
    echo -e "\e[1;5;46mUsage: $0 install_all | config_all | smart_all | ftprestart | ftpstart !\e[0;m"
    ;;
esac

总结:

脚本核心功能:该脚本实现了 LDAP 认证的 vsftpd FTP 服务自动化部署,包含 openldap、vsftpd、pam_ldap 的编译安装和配置,通过随机目录存放配置文件提高安全性。 关键特性

  • 软件包下载后进行 MD5 校验,确保文件完整性
  • 自动检测系统依赖(tcp_wrappers、SSL)并适配编译配置
  • 通过时间戳校验安装 / 配置是否成功
  • 生成随机目录结构存放配置文件,提高安全性
  • 执行敏感操作前需要密钥验证

使用方式:脚本支持install_all(安装)、config_all(配置)、smart_all(全流程)、ftprestart/ftpstart(重启)四种操作模式,前三种需要输入正确的执行密钥。

vsftpd核心配置:

anonymous_enable=NO                  # 禁用匿名访问
local_enable=YES                     # 启用本地用户访问
write_enable=YES                     # 启用全局写权限
anon_world_readable_only=NO          # 允许匿名用户读取非世界可读文件
anon_upload_enable=NO                # 禁用匿名用户上传
anon_mkdir_write_enable=NO           # 禁用匿名用户创建目录
anon_other_write_enable=YES          # 允许匿名用户其他写操作(删除/重命名)
chroot_local_user=YES                # 将本地用户限制在其主目录
anon_umask=022                       # 匿名用户创建文件的umask值
local_umask=022                      # 本地用户创建文件的umask值
guest_enable=YES                     # 启用虚拟用户功能
guest_username=yewu                  # 虚拟用户映射到本地yewu用户
listen=YES                           # 以独立模式运行,监听端口
listen_port=21                       # 监听的FTP端口(默认21)
port_enable=YES                      # 启用PORT模式连接
connect_from_port_20=YES             # 启用20端口进行数据传输(PORT模式)
pasv_enable=NO                       # 禁用被动模式(只使用主动模式)
virtual_use_local_privs=NO           # 虚拟用户使用匿名用户权限(而非本地用户)
pam_service_name=vsftpd.vu           # 指定PAM认证服务名(替换原vsftpd.pl)
user_config_dir=/usr/local/vsftpd/virtual # 虚拟用户独立配置文件目录
userlist_enable=YES                  # 启用用户列表功能
userlist_file=/usr/local/vsftpd/vsftpd.allowuser # 允许访问的用户列表文件
userlist_deny=NO                     # 仅允许列表中的用户访问(白名单模式)
ftpd_banner="unkown version"         # FTP服务器欢迎信息
ls_recurse_enable=YES                # 允许用户使用ls -R递归列出目录

PAM认证配置:

auth sufficient /usr/local/pam_ldap/lib/security/pam_ldap.so
account sufficient /usr/local/pam_ldap/lib/security/pam_ldap.so

虚拟用户配置:

local_root=/app/js12590billstore #指定用户登陆后的根目录
anon_upload_enable=YES #启用匿名用户上传权限
write_enable=YES 
anon_world_readable_only=NO
anon_mkdir_write_enable=YES #启用匿名用户创建目录权限
anon_other_write_enable=YES #启用匿名用户删除 / 重命名权限

Categories: 系统运维