1. 账号和口令

1 )禁用或删除无用账号

减少系统无用账号,降低安全风险

#删除不必要的账号
userdel <用户名> 
#锁定不必要的账号
passwd -l <用户名> 
#解锁必要的账号
passwd -u <用户名> 

2 )检查特殊账号

检查是否存在空口令和root权限的账号

#查看空口令账号
awk -F: '($2=="")' /etc/shadow 
#为空口令账号设定密码
passwd <用户名> 
#查看UID为零的账号,确认只有root账号
awk -F: '($3==0)' /etc/passwd 

3 )添加口令策略

加强口令的复杂度,降低被猜解的可能性

 vi /etc/login.defs 修改配置文件
 PASS_MAX_DAYS 90 #新建用户的密码最长使用天数
 PASS_MIN_DAYS 0 #新建用户的密码最短使用天数
 PASS_WARN_AGE 7 #新建用户的密码到期提前提醒天数
 
 #或者使用chage命令修改用户设置
 #将此用户的密码最长使用天数设为30,最短使用天数设为0,密码2000年1月1日过期,过期前七天警告用户
 chage -m 0 -M 30 -E 2000-01-01 -W 7 <用户名>
 
 #设置连续输错三次密码,账号锁定五分钟
 vi /etc/pam.d/common-auth 修改配置文件,添加 
 auth required pam_tally.so onerr=fail deny=3 unlock_time=300

4 )限制用户su

限制能su到root的用户

 vi /etc/pam.d/su修改配置文件,在配置文件中添加行
 例如,只允许test组用户su到root,则添加 
 auth required pam_wheel.so group=test

5 )设置登录超时

设置系统登录后,连接超时时间,增强安全性

vi /etc/profile 修改配置文件,将以 TMOUT= 开头的行注释,设置为TMOUT=180,即超时时间为三分钟

2. 服务

1 )关闭不必要的服务

关闭不必要的服务(如普通服务和xinetd服务),降低风险

使用命令systemctl disable <服务名>设置服务在开机时不自动启动

2 )SSH服务安全

对SSH服务进行安全加固,防止暴力破解成功

vim /etc/ssh/sshd_config 编辑配置文件
创建普通权限账号并配置密码,防止无法远程登录
#限制root用户远程登录
设置 PermitRootLogin 值为 no
#修改默认登录端口
设置 Port 值为 50683
#修改SSH使用的协议版本
设置 Protocol 版本为 2
#修改允许密码错误次数(默认6次)
设置 MaxAuthTries 值为 3
systemctl restart sshd

#如有必要设置不允许密码登录
PubkeyAuthentication yes   //可以使用公钥登录
PasswordAuthentication no  //不允许使用密码登录

3. 文件系统

1 )设置umask值

设置默认的umask值,增强安全性

vi /etc/profile 修改配置文件,添加行 umask 027
即新创建的文件属主拥有读写执行权限,同组用户拥有读和执行权限,其他用户无权限

4. 日志系统

1 )syslogd日志

启用日志功能,并配置日志记录

Linux系统默认启用以下类型日志:
系统日志(默认)/var/log/messages
cron日志(默认)/var/log/cron
安全日志(默认)/var/log/secure
#具体参考rsyslog配置

2 )记录所有用户的登录和操作日志

通过脚本代码实现记录所有用户的登录操作日志,防止出现安全事件后无据可查

 vim /etc/bashrc打开配置文件,输入
export PROMPT_COMMAND='{ date "+%Y-%m-%d %T ##### $(who am i |awk "{print \$1\" \"\$2\" \"\$5}") #### $(pwd) #### $(history 1 | { read x cmd; echo "$cmd"; })"; } >> /var/log/history.log'
source /etc/bashrc  加载配置生效

同时,建议您使用OSS服务收集存储日志

安全相关项目:https://github.com/workerwork/security-deploy

EPC服务器加固脚本:

#!/bin/bash -
###################################################################################################################################################
#security.sh
#author:dongfeng
#date:2019-03-27
###################################################################################################################################################
function test_input() {
	while :
	do
		read -p "$1:" tmp
		if [ "$tmp" -gt 0 ] 2>/dev/null;then
			eval $2=$tmp
			break
		else
			echo -e "\033[31minvalid input!\033[0m"
			continue
		fi
	done
}

function set_passwd() {
	[[ ! -f /etc/login.defs.bak ]] && cp -rf /etc/login.defs /etc/login.defs.bak
	[[ ! -f /etc/pam.d/system-auth.bak ]] && cp -rf /etc/pam.d/system-auth /etc/pam.d/system-auth.bak
	[[ ! -f /etc/pam.d/sshd.bak ]] && cp -rf /etc/pam.d/sshd /etc/pam.d/sshd.bak
	test_input "Maximum number of days a password may be used[90]" A
	test_input "Minimum number of days allowed between password changes[10]" B
	test_input "Minimum acceptable password length[8]" C
	test_input "Number of days warning given before a password expires[7]" D
	sed -i '/^PASS_MAX_DAYS/c\PASS_MAX_DAYS   '$A'' /etc/login.defs
	sed -i '/^PASS_MIN_DAYS/c\PASS_MIN_DAYS   '$B'' /etc/login.defs
	sed -i '/^PASS_MIN_LEN/c\PASS_MIN_LEN     '$C'' /etc/login.defs
	sed -i '/^PASS_WARN_AGE/c\PASS_WARN_AGE   '$D'' /etc/login.defs
 	echo -e "\033[32mThe password has been strengthened.\nThe new password must not be the same as the old one,\nand the new password must contain both numerals, lowercase letters and capital letters!\033[0m"
	sed -i '/pam_pwquality.so/c\password    requisite     pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=  difok=1 minlen=8 ucredit=-1 lcredit=-1 dcredit=-1' /etc/pam.d/system-auth 
	echo -e "\033[32mThe password has been strengthened. If the wrong password is entered more than three times, the account is locked!!\033[0m"
	n=`cat /etc/pam.d/sshd | grep "auth required pam_tally2.so "|wc -l`
	if [ $n -eq 0 ];then
		sed -i '/%PAM-1.0/a\auth required pam_tally2.so deny=3 unlock_time=150 even_deny_root root_unlock_time=300' /etc/pam.d/sshd
	fi
}

function unset_passwd() {
	[[ -f /etc/login.defs.bak ]] && cp -rf /etc/login.defs.bak /etc/login.defs
	[[ -f /etc/pam.d/system-auth.bak ]] && cp -rf /etc/pam.d/system-auth.bak /etc/pam.d/system-auth
	[[ -f /etc/pam.d/sshd.bak ]] && cp -rf /etc/pam.d/sshd.bak /etc/pam.d/sshd
}

function set_rootlogin() {
	[[ ! -f /etc/ssh/sshd_config.bak ]] && cp -rf /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
	sed -i '/PermitRootLogin/c\PermitRootLogin no'  /etc/ssh/sshd_config
	systemctl restart sshd.service
	echo -e "\033[32mDisabled root remote login!!\033[0m"
}

function unset_rootlogin() {
	[[ -f /etc/ssh/sshd_config.bak ]] && cp -rf /etc/ssh/sshd_config.bak /etc/ssh/sshd_config
	systemctl restart sshd.service
}

function set_history_timeout() {
	[[ ! -f /etc/profile.bak ]] && cp -rf /etc/profile /etc/profile.bak
	[[ ! -f /etc/bashrc.bak ]] && cp -rf /etc/bashrc /etc/bashrc.bak
	test_input "Set timeout for system login[300]" F
	sed -i '/^HISTSIZE/a\TMOUT='$F'' /etc/profile
	test_input "Set history number[1000]" E
	sed -i '/^HISTSIZE/c\HISTSIZE='$E'' /etc/profile
	source /etc/profile
	cat /etc/passwd | grep "^epc:" || echo -e "\033[32mADD user\033[31mepc \033[32mdefault passwd\033[31mEPC@baicells\033[0m"
	useradd epc && echo epc:EPC@baicells | chpasswd && usermod -g epc -G wheel,epc epc
	local file_cfg='/etc/bashrc'
	local prompt_command_format='{ date "+%Y-%m-%d %T ##### $(who am i |awk "{print \$1\" \"\$2\" \"\$5}") #### $(pwd) #### $(history 1 | { read x cmd; echo "$cmd"; })"; } >> /home/epc/history.log'
	source $file_cfg
	[[ ! -d /home/epc ]] && mkdir -p /home/epc
	[[ ! -f /home/epc/history.log ]] && touch /home/epc/history.log
	chown epc:epc /home/epc/history.log
	chmod 666 /home/epc/history.log
	sed -i "/.*\(export PROMPT_COMMAND=\).*/d" $file_cfg
	echo "export PROMPT_COMMAND='$prompt_command_format'" >> $file_cfg
	echo -e "\033[32mHistory has been recorded:/home/epc/history.log\033[0m"
}

function unset_history_timeout() {
	[[ -f /etc/profile.bak ]] && cp -rf /etc/profile.bak /etc/profile
	[[ -f /etc/bashrc.bak ]] && cp -rf /etc/bashrc.bak /etc/bashrc
	source /etc/profile
	source /etc/bashrc
}

function set_su() {
	[[ ! -f /etc/pam.d/su.bak ]] && cp -rf /etc/pam.d/su /etc/pam.d/su.bak
	[[ ! -f /etc/login.defs.bak ]] && cp -rf /etc/login.defs /etc/login.defs.bak
	sed -i '/pam_wheel.so use_uid/c\auth            required        pam_wheel.so use_uid ' /etc/pam.d/su
	n=`cat /etc/login.defs | grep SU_WHEEL_ONLY | wc -l`
	if [ $n -eq 0 ];then
		echo SU_WHEEL_ONLY yes >> /etc/login.defs
	fi
	echo -e "\033[32mOnly allow users of the wheel group to switch to root using the Su command!!\033[0m"
}

function unset_su() {
	[[ -f /etc/pam.d/su.bak ]] && cp -rf /etc/pam.d/su.bak /etc/pam.d/su
	[[ -f /etc/login.defs.bak ]] && cp -rf /etc/login.defs.bak /etc/login.defs
}

function check_user() {
	echo "Accounts in the system will be checked...."
	echo "Users with login privileges in the system:"
	awk -F: '($7=="/bin/bash"){print $1}' /etc/passwd
	echo "********************************************"
	echo -e "Users with UID=0 in the system:\033[34m$(awk -F: '($3=="0"){print $1}' /etc/passwd)\033[0m"
	echo "********************************************"
	N=`awk -F: '($2==""){print $1}' /etc/shadow|wc -l`
	echo -e "Users with empty password in the system:\033[34m$N\033[0m"
	if [ $N -eq 0 ];then
		echo -e "\033[32mNo empty password user in the system!!\033[0m"
 		echo "********************************************"
	else
 		i=1
 		while [ $N -gt 0 ]
 		do
    		None=`awk -F: '($2==""){print $1}' /etc/shadow|awk 'NR=='$i'{print}'`
    		echo "------------------------"
    		echo $None
    		echo -e "\033[31mPassword must be setted for users!!\033[0m"
    		passwd $None
    		let N--
 		done
 		M=`awk -F: '($2==""){print $1}' /etc/shadow|wc -l`
 		if [ $M -eq 0 ];then
  			echo -e "\033[32mNo user with empty password in the system!\033[0m"
 		else
			echo -e"\033[31mUsers with empty password in the system:$M\033[0m"
 		fi
	fi
}

function lock_file() {
	echo "Important files in the system will be locked, after which delete user or group will not be allowed."
	read -p "Warning: This script will not allow to add or delete user or group after running!! Determine the input Y and cancel the input N;Y/N:" i
	case $i in
	[Y,y])
		chattr +i /etc/passwd
		chattr +i /etc/shadow
		chattr +i /etc/group
		chattr +i /etc/gshadow
		echo "Lock success!";;
	[N,n])
		chattr -i /etc/passwd
		chattr -i /etc/shadow
		chattr -i /etc/group
		chattr -i /etc/gshadow
		echo "Unlock success!";;
	*)
		echo "Plsease input Y/y or N/n:"
	esac
}

function unlock_file() {
	chattr -i /etc/passwd
	chattr -i /etc/shadow
	chattr -i /etc/group
	chattr -i /etc/gshadow
}

function set_sshd() {
	[[ ! -f /etc/ssh/sshd_config.bak ]] && cp -rf /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
	sed -i '/Port 22/c\Port 50683' /etc/ssh/sshd_config
	systemctl restart sshd.service
	echo -e "\033[32mSSH port changed:22->50683\033[0m"
} 

function unset_sshd() {
	[[ -f /etc/ssh/sshd_config.bak ]] && cp -rf /etc/ssh/sshd_config.bak /etc/ssh/sshd_config
	systemctl restart sshd.service
}

function clear_iptables() {
	iptables -F          
	iptables -F -t nat  
	iptables -X         
	iptables -X -t nat	
	iptables -Z         
	iptables -Z -t nat  
}

function default_policy_iptables() {
	iptables -P INPUT ACCEPT      
	iptables -P OUTPUT ACCEPT     
	iptables -P FORWARD ACCEPT    
}

function test_interface() {
	local interface=$(ifconfig | awk -F: '/mtu/{print $1}' | sort -u)
	echo "All interfaces in the system:"
	echo $interface
	while :
	do
		read -p "Please input the interface name which use the iptables:" interface_tmp
		local flag=("true")
		for interface_i in $interface_tmp
		do
			local flag_i=false
			for interface_j in $interface
			do
				if [[ $interface_i == $interface_j ]];then
					flag_i=true
					break
				fi
			done
			flag+=("$flag_i")
		done
		for f in ${flag[@]}
		do
			if [[ $f != "true" ]];then
				continue 2
			fi
		done
		interface_name=$interface_tmp
		return 0
	done
}


function check_ipaddr() {
	while :
	do
		local f1=true
		local f2=true
		read -p "Please input the IP which use the redis service:" ip_tmp
		for ip in $ip_tmp
		do
			echo $ip | grep "^[0-9]\{1,3\}\.\([0-9]\{1,3\}\.\)\{2\}[0-9]\{1,3\}$" > /dev/null;
			if [ $? -ne 0 ];then
				echo -e "\033[31mIP addresses must be 1-3 digits\033[0m"
				f1=false	
				break
			fi
			ipaddr=$ip
			a=`echo $ipaddr|awk -F . '{print $1}'` 
			b=`echo $ipaddr|awk -F . '{print $2}'`
			c=`echo $ipaddr|awk -F . '{print $3}'`
			d=`echo $ipaddr|awk -F . '{print $4}'`
			for num in $a $b $c $d
			do
				if [ $num -gt 255 ] || [ $num -lt 0 ];then
					echo -e "\033[31mIn $ipaddr ,the field $num is error!\033[0m" 
					f2=false
					break
				fi
			done
		done
		if [[ $f1 == "true" ]] && [[ $f2 == "true" ]];then
			redis_ip=$ip_tmp
			return 0
		fi
	done
}

function add_firewalld_iptables() {
	test_interface
	check_ipaddr
	for interface_one in $interface_name
	do	
		iptables -A INPUT -p tcp --dport 50683 -i ${interface_one} -j ACCEPT
		iptables -A INPUT -p tcp --dport 6633 -i ${interface_one} -j ACCEPT
		iptables -A INPUT -p tcp --dport 6640 -i ${interface_one} -j ACCEPT
		iptables -A INPUT -p tcp --dport 80 -i ${interface_one} -j ACCEPT
		iptables -A INPUT -p tcp --dport 8080 -i ${interface_one} -j ACCEPT
		#iptables -A INPUT -p tcp --dport 3306 -i ${interface_one} -j ACCEPT
		iptables -A INPUT -p udp --dport 2123 -i ${interface_one} -j ACCEPT
		iptables -A INPUT -p udp --dport 2124 -i ${interface_one} -j ACCEPT
		iptables -A INPUT -p udp --dport 2125 -i ${interface_one} -j ACCEPT
		iptables -A INPUT -p udp --dport 2100 -i ${interface_one} -j ACCEPT
		iptables -A INPUT -p udp --dport 2101 -i ${interface_one} -j ACCEPT
		iptables -A INPUT -p udp --dport 2102 -i ${interface_one} -j ACCEPT
		iptables -A INPUT -p udp --dport 2103 -i ${interface_one} -j ACCEPT
		iptables -A INPUT -p udp --dport 2104 -i ${interface_one} -j ACCEPT
		iptables -A INPUT -p sctp --dport 36412 -i ${interface_one} -j ACCEPT
		iptables -A INPUT -p udp --dport 2152 -i ${interface_one} -j ACCEPT
		for ip in $redis_ip
		do
			iptables -A INPUT -s $ip -p tcp --dport 6379 -i ${interface_one} -j ACCEPT
		done
		iptables -A INPUT -m state --state RELATED,ESTABLISHED -i ${interface_one} -j ACCEPT
		iptables -A INPUT -p icmp -i ${interface_one} -j ACCEPT
		[[ $interface_one ]] && iptables -A INPUT -p all -i ${interface_one} -j DROP
		echo -e "\033[32mThe public network interface \033[34m$interface_one\033[32m bind iptables now!\033[0m"
	done
}

function install_iptables_services() {
	yum install -y epel-release &>/dev/null
	yum install -y iptables-services &>/dev/null
	systemctl enable iptables &>/dev/null
	systemctl start iptables &>/dev/null
	echo -e "\033[32miptables-services enabled!\033[0m"
}

function set_iptables() {
	clear_iptables
	default_policy_iptables
	add_firewalld_iptables
	service iptables save
}

function unset_iptables() {
	clear_iptables
	service iptables save
}

function main() {
	set_passwd
	set_rootlogin
	set_history_timeout
	set_su
	check_user
	#lock_file
	set_sshd
	install_iptables_services
	set_iptables
}

function unset_main() {
	unset_passwd
	unset_rootlogin
	unset_history_timeout
	unset_su
	#unlock_file
	unset_sshd
	unset_iptables
}

case "$1" in
"")
	main
	exit;;
"-d")
	unset_main
	exit;;
"-h")
	echo "**************************"
	echo "$0       install"
	echo "$0 -d    uninstall"
	echo "$0 -h    help"
	echo "**************************"
esac