分类目录归档:Linux

利用SSH的用户配置文件管理SSH会话

一般,在Shell中可以如下远程登录一台机器:

ssh user@hostname -p port

然后输入密码(如果有放置公钥则直接过)。

但是如果有很多机器要操作,就可以利用SSH中的用户配置文件来管理会话(man ssh_config)。

SSH的用户配置文件是放在当前用户根目录下的.ssh文件夹里(~/.ssh/config,不存在可新建),其配置写法如下:

Host			别名
	HostName	主机名
	Port		端口
	User		用户名
	IdentityFile	秘钥文件的路径(如果私钥不是放置默认位置,这里可以指定)

配置好后就可以使用别名登录:

ssh 别名
scp 别名:~/test .

如果有多个机器,对应在配置中写入。

可见,SSH的过程,总是首先读取config文件,然后解析,然后看看是否匹配别名,如果匹配,就使用匹配的别名对应的配置登录。

生成SSH秘钥:

root@vfeelit:~# ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): 
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.

把公钥放到远程机器的~/.ssh/authorized_keys文件中。

Linux桌面与VNC服务安装记录

CentOS 7.x中安装VNC:VNC安装的是TigerVnc
安装GNOME Desktop

yum groupinstall "GNOME Desktop" "Graphical Administration Tools"

安装TigerVnc

yum install tigervnc-server

TigerVnc主要安装了两个软件包:

rpm -qa | grep vnc
tigervnc-server-minimal-1.8.0-1.el7.x86_64
tigervnc-server-1.8.0-1.el7.x86_64


rpm -ql tigervnc-server-minimal-1.8.0-1.el7.x86_64
/usr/bin/Xvnc
/usr/bin/vncconfig
/usr/bin/vncpasswd


rpm -ql tigervnc-server-1.8.0-1.el7.x86_64
/etc/sysconfig/vncservers #被/usr/lib/systemd/system/vncserver@.service替换
/usr/bin/vncserver
/usr/bin/x0vncserver
/usr/lib/systemd/system/vncserver@.service
/usr/lib/systemd/system/xvnc.socket
/usr/lib/systemd/system/xvnc@.service

Vncserver实际是Xvnc的包装。默认,VNC针对每个用户开一个桌面,每个桌面对应一个端口号,比如桌面号是:1,说明端口对应5901。

模板配置文件:

[Unit]
Description=Remote desktop service (VNC)
After=syslog.target network.target

[Service]
Type=forking
User=<USER>

# Clean any existing files in /tmp/.X11-unix environment
ExecStartPre=-/usr/bin/vncserver -kill %i
ExecStart=/usr/bin/vncserver %i
PIDFile=/home/<USER>/.vnc/%H%i.pid
ExecStop=-/usr/bin/vncserver -kill %i

[Install]
WantedBy=multi-user.target

这个模板配置中已经说明应该如何操作,首先拷贝这个模板文件,把用真实的用户替代,%i就是桌面号(:1),以下是一个针对root用户的模板:

#######################################
# /usr/lib/systemd/system/vncserver@:1.service
[Unit]
Description=Remote desktop service (VNC)
After=syslog.target network.target

[Service]
Type=forking
User=root

# Clean any existing files in /tmp/.X11-unix environment
ExecStartPre=-/usr/bin/vncserver -kill :1
ExecStart=/usr/bin/vncserver :1
PIDFile=/root/.vnc/%H:1.pid
ExecStop=-/usr/bin/vncserver -kill :1

[Install]
WantedBy=multi-user.target

注意这是针对root用户的,所以PIDFile文件位置是/root/.vnc,如果是普通用户应该是/home/xxx/.vnc。

以下一个自动脚本:

cp /lib/systemd/system/vncserver@.service /lib/systemd/system/vncserver@\:1.service
sed -i 's/%i/:1/g' /lib/systemd/system/vncserver@\:1.service
sed -i 's/<USER>/root/g' /lib/systemd/system/vncserver@\:1.service
sed -i 's/home\/root/root/g' /lib/systemd/system/vncserver@\:1.service

设置密码(注意是在当前用户下),比如是root,就在root下运行vncpasswd,会把密码写入到当前用户的.vnc目录中。

自启动设置:

systemctl enable vncserver@:1.service
systemctl start vncserver@:1.service
systemctl status vncserver@:1.service
systemctl disable initial-setup-text.service

systemctl enable vncserver@:2.service
systemctl start vncserver@:2.service
systemctl status vncserver@:2.service
systemctl disable initial-setup-text.service

以上的脚本实际是/usr/bin/vncserver的包装器,后面的桌面号会锁定到用户,如果进程异常退出,在重启时VNC会自动分配另一个桌面号。锁定的逻辑实际是往tmp中添加一个文件,所以如果进程异常退出需要手动删除锁定文件。

另外,如果修改了/lib/systemd/system/vncserver@\:1.service文件,需要运行systemctl daemon-reload。

命令行用法:

/usr/bin/vncserver -h

usage: vncserver [:<number>] [-name <desktop-name>] [-depth <depth>]
                 [-geometry <width>x<height>]
                 [-pixelformat rgbNNN|bgrNNN]
                 [-fp <font-path>]
                 [-cc <visual>]
                 [-fg]
                 [-autokill]
                 [-noxstartup]
                 [-xstartup <file>]
                 <Xvnc-options>...

       vncserver -kill <X-display>

       vncserver -list

所以如果需要指定分辨率,可以添加-geometry 1440×900, 如果需要指定颜色深度(会影响网络传输流量),可以指定-depth 24

Root用户无法启动Chrome,修改/usr/bin/google-chrome:

if [[ -n "$CHROME_USER_DATA_DIR" ]]; then
  # Note: exec -a below is a bashism.
  exec -a "$0" "$HERE/chrome"  \
    --user-data-dir="$CHROME_USER_DATA_DIR" "$@"
else
  exec -a "$0" "$HERE/chrome"  "$@" --no-sandbox --user-data-dir
fi

Ubuntu中安装VNC(vnc4server):
更新系统:

apt-get update
apt-get upgrade

Gnome桌面环境安装:

apt-get install x-window-system-core
apt-get install gdm
apt-get install ubuntu-desktop
apt-get install gnome-panel gnome-settings-daemon metacity nautilus gnome-terminal

安装VNC

apt-get install vnc4server

dpkg -l | grep vnc
dpkg -L vnc4server
/usr/bin/x0vnc4server
/usr/bin/vnc4passwd
/usr/bin/Xvnc4
/usr/bin/vnc4config
/usr/bin/vnc4server

修改配置:

cp ~/.vnc/xstartup  ~/.vnc/xstartup.bak
vi ~/.vnc/xstartup

#!/bin/sh  
  
export XKL_XMODMAP_DISABLE=1  
unset SESSION_MANAGER  
unset DBUS_SESSION_BUS_ADDRESS  
  
[ -x /etc/vnc/xstartup ] && exec /etc/vnc/xstartup  
[ -r $HOME/.Xresources ] && xrdb $HOME/.Xresources  
xsetroot -solid grey  
vncconfig -iconic &  
 
gnome-panel &  
gnome-settings-daemon &  
metacity &  
nautilus &  
gnome-terminal &

启动服务,需要指定桌面号(需要切换到具体的用户):

vncserver -kill :1
vncserver :1

Ubuntu中的vnc4server实际没有提供自启动脚本,启动关闭都需要首先切换到具体用户,并且需要准确对应桌面号,为了开机启动,需要在rc.local中加入类似脚本:

su www -c '/usr/bin/vncserver -name xxx -geometry 1366x768 :10’

注:进程如果异常退出,对应的桌面号可能被锁定。

时间同步客户端 – Chrony

Centos7.x中已经默认安装此时间同步客户端。

安装Chrony:

#ubuntu/debian:
apt-get install chrony

#Centos/redhat
yum install chrony

时间服务器删除添加:

#删除默认的Server
sed -i "/server/d" /etc/chrony.conf

#添加
#打开/etc/chrony.conf,新增一行:
server ntp.aliyun.com iburst

重启并检查:

/etc/init.d/chronyd restart
#或
systemctl restart chronyd

#查看是否正常
chronyc tracking

Reference ID    : CB6B0658 (203.107.6.88)
Stratum         : 3
Ref time (UTC)  : Mon Mar 12 02:01:35 2018
System time     : 0.000003723 seconds fast of NTP time
Last offset     : -0.003573318 seconds
RMS offset      : 0.003573318 seconds
Frequency       : 1.765 ppm slow
Residual freq   : +55.304 ppm
Skew            : 6.448 ppm
Root delay      : 0.064887173 seconds
Root dispersion : 0.001230678 seconds
Update interval : 2.1 seconds
Leap status     : Normal

其它说明—————————————————————–

rpm -qa | grep chrony
chrony-2.1.1-4.el7.centos.x86_64

rpm -ql chrony-2.1.1-4.el7.centos.x86_64
/etc/NetworkManager/dispatcher.d/20-chrony
/etc/chrony.conf   *****
/etc/chrony.keys
/etc/dhcp/dhclient.d/chrony.sh
/etc/logrotate.d/chrony
/usr/bin/chronyc  *****
/usr/lib/systemd/ntp-units.d/50-chronyd.list
/usr/lib/systemd/system/chrony-dnssrv@.service
/usr/lib/systemd/system/chrony-dnssrv@.timer
/usr/lib/systemd/system/chrony-wait.service
/usr/lib/systemd/system/chronyd.service
/usr/libexec/chrony-helper
/usr/sbin/chronyd  *****
/var/lib/chrony
/var/lib/chrony/drift
/var/lib/chrony/rtc
/var/log/chrony

主要三个文件,/etc/chrony.conf配置,/usr/bin/chronyc是客户端工具,/usr/sbin/chronyd是后台进程(使用/usr/lib/systemd/system/chronyd.service来管理)。

当Chrony启动时,它会读取/etc/chrony.conf配置文件中的设置。配置参数:

1 server – 该参数可以多次用于添加时钟服务器,必须以”server “格式使用。一般而言,你想添加多少服务器,就可以添加多少服务器。

    server 0.centos.pool.ntp.org
    server 3.europe.pool.ntp.org

2 stratumweight – stratumweight指令设置当chronyd从可用源中选择同步源时,每个层应该添加多少距离到同步距离。默认情况下,CentOS中设置为0,让chronyd在选择源时忽略源的层级。

3 driftfile – chronyd程序的主要行为之一,就是根据实际时间计算出计算机增减时间的比率,将它记录到一个文件中是最合理的,它会在重启后为系统时钟作出补偿,甚至可能的话,会从时钟服务器获得较好的估值。

4 rtcsync – rtcsync指令将启用一个内核模式,在该模式中,系统时间每11分钟会拷贝到实时时钟(RTC)。

5 allow / deny – 这里你可以指定一台主机、子网,或者网络以允许或拒绝NTP连接到扮演时钟服务器的机器。

    allow 192.168.4.5
    deny 192.168/16

6 cmdallow / cmddeny – 跟上面相类似,只是你可以指定哪个IP地址或哪台主机可以通过chronyd使用控制命令

7 bindcmdaddress – 该指令允许你限制chronyd监听哪个网络接口的命令包(由chronyc执行)。该指令通过cmddeny机制提供了一个除上述限制以外可用的额外的访问控制等级。

    bindcmdaddress 127.0.0.1
    bindcmdaddress ::1

8 makestep – 通常,chronyd将根据需求通过减慢或加速时钟,使得系统逐步纠正所有时间偏差。在某些特定情况下,系统时钟可能会漂移过快,导致该调整过程消耗很长的时间来纠正系统时钟。该指令强制chronyd在调整期大于某个阀值时步进调整系统时钟,但只有在因为chronyd启动时间超过指定限制(可使用负值来禁用限制),没有更多时钟更新时才生效。

也可以通过运行chronyc命令来修改设置,命令如下:
1 accheck – 检查NTP访问是否对特定主机可用
2 activity – 该命令会显示有多少NTP源在线/离线

chronyc> activity 
200 OK
4 sources online
0 sources offline
0 sources doing burst (return to online)
0 sources doing burst (return to offline)
0 sources with unknown address

3 add server – 手动添加一台新的NTP服务器。
4 clients – 在客户端报告已访问到服务器
5 delete – 手动移除NTP服务器或对等服务器
6 settime – 手动设置守护进程时间
7 tracking – 显示系统时间信息

chronyc> tracking
Reference ID    : 38.126.113.10 (38.126.113.10)
Stratum         : 3
Ref time (UTC)  : Mon Apr 24 06:29:55 2017
System time     : 0.000244395 seconds slow of NTP time
Last offset     : -0.000066831 seconds
RMS offset      : 0.000231604 seconds
Frequency       : 18.263 ppm fast
Residual freq   : -0.038 ppm
Skew            : 0.126 ppm
Root delay      : 0.012766 seconds
Root dispersion : 0.007974 seconds
Update interval : 518.7 seconds
Leap status     : Normal

CentOS 7.x 升级内核

默认,CentOS 7.x下,运行yum update会自动升级内核,不过一般都是非常保守的升级:

yum list kernel*
已安装的软件包
kernel.x86_64      3.10.0-514.el7                                         @anaconda     
kernel.x86_64      3.10.0-514.10.2.el7                                    @updates      
kernel.x86_64      3.10.0-514.16.1.el7                                    @updates 

这里可以看到,默认安装的是3.10.0-514.el7, 后来更新两次,分别安装了3.10.0-514.10.2.el7和3.10.0-514.16.1.el7。可见,内核版本号并没有发生任何改变,只是横杆后(-)的数字发生改变,这个应该是发行商维护的(非Linux内核维护),主要应该是一些补丁包添加。

如果要升级到最新的Linux内核,可以去kernel.org去下载源代码编译,不过随着内核越来越复杂,编译涉及到的内容非常多,所以注定是一件费时费力的事情。

首先了解一下kernel.org提供的Linux内核的版本(https://www.kernel.org/category/releases.html)。简单来说,RC版本就是预发布版本,Mainline主线版本,一般2-3月发布一次。Mainline之后就会释放稳定(Stable)版本。还有Longterm版本,表示是长期支持的版本。大部分发行版本Linux都会考虑使用Longterm版本的内核,比如CentOS中就使用3.10.0这个版本。

Longterm release kernels
Version Maintainer Released Projected EOL
4.9 Greg Kroah-Hartman 2016-12-11 Jan, 2019
4.4 Greg Kroah-Hartman 2016-01-10 Feb, 2018
4.1 Sasha Levin 2015-06-21 Sep, 2017
3.16 Ben Hutchings 2014-08-03 Apr, 2020
3.12 Jiri Slaby 2013-11-03 May, 2017
3.10 Willy Tarreau 2013-06-30 Oct, 2017
3.4 Li Zefan 2012-05-20 Apr, 2017
3.2 Ben Hutchings 2012-01-04 May, 2018

从这个表格看,3.10.0这个内核从2013年开始发布,到现在(2017年)已几年过去了,看起来稍微是有点旧了。而且2017年以后,至少需要使用4.4的内核。

为了便利地升级内核,可以使用elrepo(http://elrepo.org)这个软件源,以yum的方法快速安装:

# import key
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
# install elrepo repo
rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-2.el7.elrepo.noarch.rpm
# install kernel
yum --enablerepo=elrepo-kernel install  kernel-ml-devel kernel-ml -y
# modify grub
grub2-set-default 0
# reboot
reboot

以上安装的是最新的稳定版。源elrepo提供两个内核,kernel-ml中的ml是指mainline, 是mainline后的最新稳定版本;还有一个叫kernel-lt,它是基于长期支持版本的,比如当前kernel-lt就是指4.4版本的内核。

可以浏览如下链接,查看具体的软件包:
http://elrepo.org/linux/kernel/el7/x86_64/RPMS/

要确定可以使用哪些内核,可以使用:

yum --enablerepo=elrepo-kernel list kernel*

关于Grub2:
“Briefly, a boot loader is the first software program that runs when a computer starts. It is responsible for loading and transferring control to an operating system kernel software (such as Linux or GNU Mach). The kernel, in turn, initializes the rest of the operating system (e.g. a GNU system). ”

查看下安装:

rpm -qa | grep grub2
grub2-tools-2.02-0.44.el7.centos.x86_64
grub2-2.02-0.44.el7.centos.x86_64

rpm -ql grub2-2.02-0.44.el7.centos.x86_64
/boot/grub2/grub.cfg
/boot/grub2/grubenv
/etc/grub2.cfg  #软连接到/boot/grub2/grub.cfg

rpm -ql grub2-tools-2.02-0.44.el7.centos.x86_64
/etc/default/grub

/etc/grub.d
/etc/grub.d/00_header
/etc/grub.d/01_users
/etc/grub.d/10_linux
/etc/grub.d/20_linux_xen
/etc/grub.d/20_ppc_terminfo
/etc/grub.d/30_os-prober
/etc/grub.d/40_custom
/etc/grub.d/41_custom
/etc/grub.d/README

/etc/prelink.conf.d/grub2.conf
/etc/sysconfig/grub -> /etc/default/grub

/usr/bin/grub2-editenv
/usr/bin/grub2-file
/usr/bin/grub2-fstest
/usr/bin/grub2-glue-efi
/usr/bin/grub2-kbdcomp
/usr/bin/grub2-menulst2cfg
/usr/bin/grub2-mkfont
/usr/bin/grub2-mkimage
/usr/bin/grub2-mklayout
/usr/bin/grub2-mknetdir
/usr/bin/grub2-mkpasswd-pbkdf2
/usr/bin/grub2-mkrelpath
/usr/bin/grub2-mkrescue
/usr/bin/grub2-mkstandalone
/usr/bin/grub2-render-label
/usr/bin/grub2-script-check
/usr/bin/grub2-syslinux2cfg

/usr/sbin/grub2-bios-setup
/usr/sbin/grub2-get-kernel-settings
/usr/sbin/grub2-install
/usr/sbin/grub2-macbless
/usr/sbin/grub2-mkconfig
/usr/sbin/grub2-ofpathname
/usr/sbin/grub2-probe
/usr/sbin/grub2-reboot
/usr/sbin/grub2-rpm-sort
/usr/sbin/grub2-set-default
/usr/sbin/grub2-setpassword
/usr/sbin/grub2-sparc64-setup

Grub简单来说就是一个引导程序。机器子上电自检后,会从启动硬盘寻找引导程序,控制权交给引导程序后,由它负责系统引导,最关键的任务就是从什么地方载入内核。

配置文件/etc/grub2.cfg是一个软连接,指向/boot/grub2/grub.cfg,这个是主要的配置文件,其中有启动菜单项(多个,第一个标号为0,第二个为1,依次类推):

menuentry 'CentOS Linux (4.10.12-1.el7.elrepo.x86_64) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-3.10.0-514.el7.x86_64-advanced-3f38705e-ad35-43b8-b624-4f021e320c87' {
	load_video
	set gfxpayload=keep
	insmod gzio
	insmod part_msdos
	insmod xfs
	set root='hd0,msdos1'
	if [ x$feature_platform_search_hint = xy ]; then
	  search --no-floppy --fs-uuid --set=root --hint-bios=hd0,msdos1 --hint-efi=hd0,msdos1 --hint-baremetal=ahci0,msdos1 --hint='hd0,msdos1'  f89b61bd-58b6-48c5-98db-ec8a00d84a3e
	else
	  search --no-floppy --fs-uuid --set=root f89b61bd-58b6-48c5-98db-ec8a00d84a3e
	fi
	linux16 /vmlinuz-4.10.12-1.el7.elrepo.x86_64 root=/dev/mapper/cl-root ro crashkernel=auto rd.lvm.lv=cl/root rd.lvm.lv=cl/swap rhgb quiet LANG=zh_CN.UTF-8
	initrd16 /initramfs-4.10.12-1.el7.elrepo.x86_64.img
}

一般,升级内核后会自动添加一个菜单项(在第0个位置)。默认,Grub取哪个菜单引导,取决于/boot/grub2/grubenv中的saved_entry配置:

saved_entry=0

这样表示取排在第0个位置的菜单作为默认引导。

也可以直接是菜单项名称,比如:

saved_entry=CentOS Linux (4.10.12-1.el7.elrepo.x86_64) 7 (Core)

可以直接编辑/boot/grub2/grubenv中的saved_entry,也可以使用工具grub2-editenv来编辑/boot/grub2/grubenv,不过如果仅仅想改变saved_entry这个环境变量,则可以使用grub2-set-default来进行,比如grub2-set-default 0就是把/boot/grub2/grubenv中的saved_entry设置为0。

为了防止载入不确定的内核(比如编辑了菜单),一般不建议使用索引号,可以如下设置,锁定默认到指定菜单:

grub2-set-default 'CentOS Linux (4.10.12-1.el7.elrepo.x86_64) 7 (Core)'

Ubuntu 16.x 装机参考

系统镜像可到:https://mirrors.aliyun.com/ubuntu-releases/下载

#更改限制
vi /etc/security/limits.conf
root soft nofile 65535
root hard nofile 65535
* soft nofile 65535
* hard nofile 65535

#解锁root
sudo passwd -u root
#为root设置密码
sudo passwd root

#切换到root
su root

为了避免麻烦,先关闭防火墙:

#查看启动了哪些服务
systemctl list-unit-files | grep enabled
systemctl stop ufw.service
systemctl disable ufw.service

默认没有安装openssh-server(Ubuntu 16.04默认安装时需要选中,否则需要自己安装)

apt install openssh-server

systemctl start sshd.service
systemctl enable sshd.service

默认安装的openssh-server是禁止root链接的,它的逻辑是先以一个普通用户登录,如果需要切换,再用su来进行。如果麻烦可以修改:

vi /etc/ssh/sshd_config

PermitRootLogin prohibit-password	#改为yes(prohibit-password禁止密码,就是使用公钥还是可以的)
PermitEmptyPasswords yes		#是否允许空密码,默认是no

#重启服务
systemctl restart sshd.service

更换软件源头,参考:http://wiki.ubuntu.org.cn/%E6%A8%A1%E6%9D%BF:16.04source

deb http://cn.archive.ubuntu.com/ubuntu/ xenial main restricted universe multiverse
deb http://cn.archive.ubuntu.com/ubuntu/ xenial-security main restricted universe multiverse
deb http://cn.archive.ubuntu.com/ubuntu/ xenial-updates main restricted universe multiverse
deb http://cn.archive.ubuntu.com/ubuntu/ xenial-backports main restricted universe multiverse
##测试版源
deb http://cn.archive.ubuntu.com/ubuntu/ xenial-proposed main restricted universe multiverse
# 源码
deb-src http://cn.archive.ubuntu.com/ubuntu/ xenial main restricted universe multiverse
deb-src http://cn.archive.ubuntu.com/ubuntu/ xenial-security main restricted universe multiverse
deb-src http://cn.archive.ubuntu.com/ubuntu/ xenial-updates main restricted universe multiverse
deb-src http://cn.archive.ubuntu.com/ubuntu/ xenial-backports main restricted universe multiverse
##测试版源
deb-src http://cn.archive.ubuntu.com/ubuntu/ xenial-proposed main restricted universe multiverse
# Canonical 合作伙伴和附加
deb http://archive.canonical.com/ubuntu/ xenial partner
deb http://extras.ubuntu.com/ubuntu/ xenial main

这里的http://cn.archive.ubuntu.com/ubuntu/是阿里云资源,可以替换为http://mirrors.163.com/ubuntu/

安装桌面

apt update
apt upgrade

apt-get install --no-install-recommends ubuntu-desktop

add-apt-repository ppa:embrosyn/cinnamon
apt install cinnamon

Sockets代理 之 Shadowsocks

ShadowSocks – http://shadowsocks.org

实现一个Socks代理,很多语言都提供有内置支持或扩展包可用,需要编写的代码也不会太多。ShadowSocks这个工具提供了多种语言的实现,从http://shadowsocks.org/en/download/servers.html可以看到,有Python、Go、C with libev、C++ with Qt, 这些实现版本中,都应该包含服务端和客户端工具,不过也有一些专门的客户端(主要提供图形操作界面),比如
Windows客户端(https://github.com/shadowsocks/shadowsocks-windows/releases)
Linux下的客户端(https://github.com/shadowsocks/shadowsocks-qt5/releases)
Mac OS X下的客户端(https://github.com/shadowsocks/ShadowsocksX-NG/releases),Android下的客户端(https://github.com/shadowsocks/shadowsocks-android,在Android下可打开https://play.google.com/store/apps/details?id=com.github.shadowsocks进行安装)。这个代理软件与一般的Socks5代理稍不一样的是:它不是一个纯粹的代理,它加入了加密的功能。 这个工具开启的代理功能和SSH开启的代理有本质不一样,SSH开启的代理会在本地和服务端之间建立一条持久的TCP链接,而shadowsocks只是在服务器开启了监听端口,客户端的链接随到随链。所以,对于需要HOLD大量链接的情况,C with libev是一个很好的选择。

注:
由于shadowsock不是一个纯的Sockets代理,所以必须搭配客户端使用。

服务端,这里关注C实现的版本:https://github.com/shadowsocks/shadowsocks-libev,它提供了比较完整的功能。客户端关注Windows下的版本,它是.NET软件,可能需要升级你的.NET版本。

在CentOS 7.x上,可以使用了一个yum源来安装(参考https://copr.fedorainfracloud.org/coprs/librehat/shadowsocks/):

#https://copr.fedorainfracloud.org/coprs/librehat/shadowsocks/repo/epel-7/librehat-shadowsocks-epel-7.repo
#vi /etc/yum.repos.d/librehat-shadowsocks.repo

[librehat-shadowsocks]
name=Copr repo for shadowsocks owned by librehat
baseurl=https://copr-be.cloud.fedoraproject.org/results/librehat/shadowsocks/epel-7-$basearch/
type=rpm-md
skip_if_unavailable=True
gpgcheck=1
gpgkey=https://copr-be.cloud.fedoraproject.org/results/librehat/shadowsocks/pubkey.gpg
repo_gpgcheck=0
enabled=1
enabled_metadata=1

安装:

yum install shadowsocks

rpm -qa | grep shadowsocks
shadowsocks-libev-2.5.5-1.el7.centos.x86_64

rpm -ql shadowsocks-libev-2.5.5-1.el7.centos.x86_64
/etc/default/shadowsocks-libev
/etc/shadowsocks-libev/config.json
/usr/bin/ss-local
/usr/bin/ss-manager
/usr/bin/ss-nat
/usr/bin/ss-redir
/usr/bin/ss-server
/usr/bin/ss-tunnel
/usr/lib/systemd/system/shadowsocks-libev.service
/usr/lib64/libshadowsocks-libev.so

编译安装(新版本可能有问题)

# Ubuntu
apt-get install --no-install-recommends build-essential automake autoconf libtool libssl-dev libpcre3-dev asciidoc xmlto
# CentOS
yum install gcc autoconf libtool automake make zlib-devel openssl-devel asciidoc xmlto

#克隆源码,或直接下载压缩包
git clone https://github.com/shadowsocks/shadowsocks-libev.git
cd shadowsocks-libev
./configure --prefix=/usr/local/ss
make
make install

#######################################
# CentOS下编译安装新版本参考:https://shadowsocks.be/4.html,以下是从脚本中提取的安装步骤
#安装必须的软件包
yum install epel-release
yum install -y unzip openssl openssl-devel gettext gcc autoconf libtool automake make asciidoc xmlto udns-devel libev-devel pcre pcre-devel git c-ares-devel

#下载安装
wget https://github.com/jedisct1/libsodium/releases/download/1.0.13/libsodium-1.0.13.tar.gz
tar zxf libsodium-1.0.13.tar.gz
cd libsodium-1.0.13
./configure —prefix=/usr && make && make install

wget http://dl.teddysun.com/files/mbedtls-2.6.0-gpl.tgz
tar xf mbedtls-2.6.0-gpl.tgz
cd mbedtls-2.6.0-gpl
make SHARED=1 CFLAGS=-fPIC
make DESTDIR=/usr install

wget https://c-ares.haxx.se/download/c-ares-1.12.0.tar.gz
tar zxvf c-ares-1.12.0.tar.gz
cd c-ares-1.12.0
./configure
make && make install

ldconfig

wget https://github.com/shadowsocks/shadowsocks-libev/releases/download/v3.1.0/shadowsocks-libev-3.1.0.tar.gz
tar zxf shadowsocks-libev-3.1.0.tar.gz
cd shadowsocks-libev-3.1.0
./configure --disable-documentation
make && make install

默认配置在/etc/default/shadowsocks-libev,配置文件/etc/shadowsocks-libev/config.json,服务启动脚本/usr/lib/systemd/system/shadowsocks-libev.service。默认实际使用到的就是/usr/bin/ss-server(如果要启动客户端,修改为ss-local即可),查看配置文件:

{
    "server":"120.xx.xx.28",
    "server_port":8388,
    "local_port":1080,
    "password":"xxxxxxxx",
    "timeout":60,
    "method":"aes-256-cfb"
}

不管是使用ss-local和ss-server,这几个参数都一样,注意:作为服务端,local_port应该是没有用的。客户端和服务端,都预置了密码和对称加密使用的算法,简单来说,它们通过密码认证,然后使用预置的算法产生一个秘钥,用这个秘钥来加解密传输的数据。不难看出,它跟传统的CA不同,秘钥的交换不是通过加密隧道来交换的。如果需要安全,不要使用这个工具。

这个包也包括了其它工具:

/usr/bin/ss-local	客户端,和服务端的配置一样
/usr/bin/ss-manager	用来管理服务端,比如在服务端开启多个端口(动态拉起ss-server)
/usr/bin/ss-nat		管理NAT映射
/usr/bin/ss-redir	客户端工具,用来实现透明代理
/usr/bin/ss-server	服务端
/usr/bin/ss-tunnel	用来实现本地端口转发,比如目的地址符合条件时,转发到本地某个端口

对于客户端,最典型的应用,就是在内网架设一个服务器,上面安装ShadowSocks-livev,然后启动客户端(也可以把参数写入配置,用c指定读取的配置文件):

ss-local -s 120.xx.xx.x -p 8388 -b 192.168.1.250 -l 1080 -k xxxxx -m aes-256-cfb

需要代理的机器,指定为192.168.1.250即可,这样就可以避免在每个终端上安装客户端。不过,如果需要连接多个代理服务器,就需要启动多个ss-local进程,并且指定不同的端口。

注:可以在服务器上设置多用户(实际是启动多个ss-server,用端口区分),客户端可以配置多个远程服务器,但是需要指定默认,需要更换时,需要切换一下默认远程服务器,从这里可知,一个客户端要对应哪个远程是随意的,目前看起来缺少的是在客户端管理远程服务器的工具(比如只启动一个客户端,动态或安装一定规则对应到不同远程服务器)

Window下的客户端(https://github.com/shadowsocks/shadowsocks-windows/releases):
ss

当然,这个客户端还提供了 系统代理模式(就是直接修改系统代理) 系统代理模式(全局与PAC) 切换服务器等功能。

Linux下的客户端不支持配置多个端口(不同端口对应不同的服务端),所以可以启动多个客户端来解决,比如分别对应建立配置文件,然后写一段简单粗暴的定时脚本,就可以Hold它:

#!/bin/bash

live=`ps -efH | grep '/etc/shadowsocks-libev/config_4441.json' | grep -v 'grep' | wc -l`
if [ $live -eq 0 ]; then
    /usr/bin/ss-local -a root -c /etc/shadowsocks-libev/config_4441.json -u 2>&1 > /dev/null &
fi

live=`ps -efH | grep '/etc/shadowsocks-libev/config_4442.json' | grep -v 'grep' | wc -l`
if [ $live -eq 0 ]; then
    /usr/bin/ss-local -a root -c /etc/shadowsocks-libev/config_4442.json -u 2>&1 >> /dev/null &
fi
 
live=`ps -efH | grep '/etc/shadowsocks-libev/config_4443.json' | grep -v 'grep' | wc -l`
if [ $live -eq 0 ]; then
    /usr/bin/ss-local -a root -c /etc/shadowsocks-libev/config_4443.json -u 2>&1 >> /dev/null &
fi

live=`ps -efH | grep '/etc/shadowsocks-libev/config_4444.json' | grep -v 'grep' | wc -l`
if [ $live -eq 0 ]; then
    /usr/bin/ss-local -a root -c /etc/shadowsocks-libev/config_4444.json -u 2>&1 >> /dev/null &
fi

exit 0

需要说明的是shadowsocks-qt5是一个跨平台的客户端(https://github.com/shadowsocks/shadowsocks-qt5/releases),以下链接是安装和使用说明:https://github.com/shadowsocks/shadowsocks-qt5/wiki:

Ubuntu通过PPA方式安装:

sudo add-apt-repository ppa:hzwhuang/ss-qt5
sudo apt-get update
sudo apt-get install shadowsocks-qt5

这个ppa对应的链接是:https://launchpad.net/~hzwhuang/+archive/ubuntu/ss-qt5
ss-ubuntu

而对应的Window版本长这个样子:
ss-window

最后:
作为服务端,如果使用的是Linux(CentOS或Ubuntu),最好的选择是shadowsocks-libev,而对于Windows,看起来只有一个选择(https://github.com/shadowsocks/libQtShadowsocks/releases,提供一个叫libQtShadowsocks的工具,既可做服务端,也可以做客户端); 作为客户端,在Windows和Ubuntu下https://github.com/shadowsocks/shadowsocks-qt5/releases这个较好。

这是一个百花齐放的开源产品。

CentOS 升级OpenSSL与CURL

#查看安装
rpm -qa | grep curl
curl-7.29.0-25.el7.centos.x86_64
libcurl-devel-7.29.0-25.el7.centos.x86_64
libcurl-7.29.0-25.el7.centos.x86_64

rpm -ql curl-7.29.0-25.el7.centos.x86_64
/usr/bin/curl

rpm -ql libcurl-7.29.0-25.el7.centos.x86_64
/usr/lib64/libcurl.so.4 -> libcurl.so.4.3.0
/usr/lib64/libcurl.so.4.3.0

rpm -ql libcurl-devel-7.29.0-25.el7.centos.x86_64
/usr/bin/curl-config
/usr/include/curl
/usr/include/curl/curl.h
/usr/include/curl/curlbuild-64.h
/usr/include/curl/curlbuild.h
/usr/include/curl/curlrules.h
/usr/include/curl/curlver.h
/usr/include/curl/easy.h
/usr/include/curl/mprintf.h
/usr/include/curl/multi.h
/usr/include/curl/stdcheaders.h
/usr/include/curl/typecheck-gcc.h
/usr/lib64/libcurl.so -> libcurl.so.4.3.0
/usr/lib64/pkgconfig/libcurl.pc
/usr/share/aclocal/libcurl.m4

#在Ubuntu下查看 默认使用OpenSSL
curl -V
curl 7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap pop3 pop3s rtmp rtsp smtp smtps telnet tftp 
Features: GSS-Negotiate IDN IPv6 Largefile NTLM NTLM_WB SSL libz TLS-SRP

#在CentOS下查看 默认使用NSS
curl -V
curl 7.29.0 (x86_64-redhat-linux-gnu) libcurl/7.29.0 NSS/3.19.1 Basic ECC zlib/1.2.7 libidn/1.28 libssh2/1.4.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp scp sftp smtp smtps telnet tftp 
Features: AsynchDNS GSS-Negotiate IDN IPv6 Largefile NTLM NTLM_WB SSL libz

#CentOS中使用一个第三方源升级
#http://www.city-fan.org/ftp/contrib/yum-repo/有专门针对rhel的源
#wget www.city-fan.org/ftp/contrib/yum-repo/city-fan.org-release-1-13.rhel6.noarch.rpm
wget www.city-fan.org/ftp/contrib/yum-repo/city-fan.org-release-1-13.rhel7.noarch.rpm
rpm -Uvh city-fan.org-release-1-13.rhel7.noarch.rpm

yum repolist

yum update libcurl
======================================================================================================================================
 Package                                 Arch                  Version                              Repository                   Size
======================================================================================================================================
Updating:
 libcurl                                 x86_64                7.51.0-3.0.cf.rhel7                  city-fan.org                387 k
Installing for dependencies:
 libmetalink                             x86_64                0.1.3-1.rhel7                        city-fan.org                 25 k
 libnghttp2                              x86_64                1.7.1-1.el7                          epel                         61 k
 libpsl                                  x86_64                0.7.0-1.el7                          city-fan.org                 45 k
 libssh2-devel                           x86_64                1.8.0-1.0.cf.rhel7                   city-fan.org                 72 k
 nspr-devel                              x86_64                4.11.0-1.el7_2                       updates                     114 k
 nss-devel                               x86_64                3.21.0-9.el7_2                       updates                     211 k
 nss-softokn-devel                       x86_64                3.16.2.3-14.2.el7_2                  updates                      26 k
 nss-softokn-freebl-devel                x86_64                3.16.2.3-14.2.el7_2                  updates                      46 k
 nss-util-devel                          x86_64                3.21.0-2.2.el7_2                     updates                      71 k
Updating for dependencies:
 curl                                    x86_64                7.51.0-3.0.cf.rhel7                  city-fan.org                430 k
 libcurl-devel                           x86_64                7.51.0-3.0.cf.rhel7                  city-fan.org                793 k
 libssh2                                 x86_64                1.8.0-1.0.cf.rhel7                   city-fan.org                102 k

#在CentOS 7.x中升级后(多了nghttp2/1.7.1)
curl -V
curl 7.51.0 (x86_64-redhat-linux-gnu) libcurl/7.51.0 NSS/3.21 Basic ECC zlib/1.2.7 libpsl/0.7.0 (+libicu/50.1.2) libssh2/1.8.0 nghttp2/1.7.1
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp 
Features: AsynchDNS IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz HTTP2 UnixSockets Metalink PSL

除了使用YUM来升级,也可以到官方下载RPM包(http://mirror.city-fan.org/ftp/contrib/sysutils/Mirroring/),比如:libcurl-7.51.0-3.0.cf.rhel7.x86_64.rpm:

rpm -Uvh libcurl-7.51.0-3.0.cf.rhel7.x86_64.rpm 
warning: libcurl-7.51.0-3.0.cf.rhel7.x86_64.rpm: Header V4 DSA/SHA1 Signature, key ID b56a8bac: NOKEY
error: Failed dependencies:
	libnghttp2.so.14()(64bit) is needed by libcurl-7.51.0-3.0.cf.rhel7.x86_64
	libpsl.so.0()(64bit) is needed by libcurl-7.51.0-3.0.cf.rhel7.x86_64
	libssh2(x86-64) >= 1.8.0 is needed by libcurl-7.51.0-3.0.cf.rhel7.x86_64
	libcurl = 7.29.0-25.el7.centos is needed by (installed) curl-7.29.0-25.el7.centos.x86_64

直接使用RPM包安装,我们需要手动解决依赖,libnghttp2和libpsl在epel源中,直接yum安装就可以,不过libssh2版本就不符合要求了。

YUM或RPM包安装(CentOS系),默认使用NSS,这个东西类似OpenSSL,用来加密,对于HTTP来说就是提供HTTPS服务。如果需要用OpenSSL,那么就需要自己编译了:

cd curl-7.51.0/

./configure --without-nss --with-ssl && make &&make install

curl -V
curl 7.51.0 (x86_64-pc-linux-gnu) libcurl/7.51.0 OpenSSL/1.0.1e zlib/1.2.7 libssh2/1.8.0
Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp 
Features: IPv6 Largefile NTLM NTLM_WB SSL libz UnixSockets

编译:

yum install zlib zlib-devel

###### OpenSSL
wget https://www.openssl.org/source/openssl-1.0.2j.tar.gz
tar zxvf openssl-1.0.2j.tar.gz
cd openssl-1.0.2j

#默认就是安装到/usr/local/ssl
./config --prefix=/usr/local/ssl shared zlib 
make && make install 

#配置库文件搜索路径
echo  "/usr/local/ssl/lib"  >>  /etc/ld.so.conf
ldconfig -v

#安全起见把这两个包拷贝进去
cp /usr/local/ssl/lib/libssl.so.1.0.0 /usr/lib64
cp /usr/local/ssl/lib/libcrypto.so.1.0.0 /usr/lib64/


###### Curl
wget https://curl.haxx.se/download/curl-7.51.0.tar.gz
tar zxvf curl-7.51.0.tar.gz
cd curl-7.51.0

#指定SSL安装类目
./configure --prefix=/usr/local/curl --without-nss --with-ssl=/usr/local/ssl
make && make install

#安装后配置(用自己安装的替换系统原来的)
mv /usr/bin/curl /usr/bin/curl.old
ln -s /usr/local/curl/bin/curl /usr/bin/curl

echo  "/usr/local/curl/lib"  >>  /etc/ld.so.conf
ldconfig -v

# 以下操作参考
#mv /usr/lib64/libcurl.so.4.4.0 /usr/lib64/libcurl.so.4.4.0.old
#ln -s /usr/local/curl/lib/libcurl.so.4.4.0 /usr/lib64/libcurl.so.4.4.0

Bash Shell脚本实例

0 ——-

#!/bin/bash

#定位KILL命令
cmm="/usr/bin/kill"
if [ ! -f "$cmm" ]; then
    cmm="/bin/kill"
fi

#保持多少个进程
hold=2
if [ -n "$1" ]; then
    hold=$1
fi

#内存占用阈值(100M)
threshold=102400

#获取具体路径
pwd=$(cd `dirname $0`; pwd)

#搜索进程
search=$pwd'/artisan queue:work --queue=adc'

#循环杀掉超过阈值的进程
ps aux | grep $search | grep -v grep | awk '{print $2"|"$6}' | cat | while read line
do
    pid=`echo $line | cut -d "|" -f 1`
    rss=`echo $line | cut -d "|" -f 2`
    if [ $rss -gt $threshold ]; then
        kill=`$cmm -9 $pid`
        date=`date "+%Y-%m-%d %H:%M:%S"`
        echo $date' - 'PID:$pid' - RSS('$rss'K) > Threshold('$threshold'K) - Was Killed.'
    fi
done

#杀掉后维持进程数量
live=`ps -efH | grep $search | grep -v 'grep' | wc -l`
if [ $live -lt $hold ]; then
    for ((i=1; i<=$hold-$live; i=i+1))
    do
        php $search' --tries=3 --sleep=2 --timeout=300' 2>&1 >> /dev/null &
    done
fi

exit 0





#!/bin/bash

#定位KILL命令
cmm="/usr/bin/kill"
if [ ! -f "$cmm" ]; then
    cmm="/bin/kill"
fi

#操作系统位数
bit='32'
if [ $(getconf WORD_BIT) = '32' -a $(getconf LONG_BIT) = '64' ]; then
    bit='64'
fi

#端口
port=9515
if [ -n "$1" ]; then
    port=$1
fi

#获取具体路径
pwd=$(cd `dirname $0`; pwd)

#搜索进程
search=$pwd/chromedriver_linux$bit' --port='$port

#杀掉原来进程
ps aux | grep $search | grep -v grep | awk '{print $2}' | cat | while read pid
do
    kill=`$cmm -9 $pid`
    date=`date "+%Y-%m-%d %H:%M:%S"`
    echo $date' - 'PID:$pid' Was Killed.'
done

#重启进程
$pwd/chromedriver_linux$bit --port=$port 2>&1 >> /dev/null &

exit 0

##############################################################
##定时杀firefox

#!/bin/bash

#定位KILL命令
cmm="/usr/bin/kill"
if [ ! -f "$cmm" ]; then
    cmm="/bin/kill"
fi

#循环杀掉进程
ps aux | grep 'firefox/firefox' | grep -v grep | awk '{print $2"|"$6}' | cat | while read line
do
    pid=`echo $line | cut -d "|" -f 1`
    rss=`echo $line | cut -d "|" -f 2`

    kill=`$cmm -9 $pid`
    date=`date "+%Y-%m-%d %H:%M:%S"`
    echo $date' - 'PID:$pid' - RSS('$rss'K) Threshold('$threshold'K) - Firefox Was Killed.'
done

exit 0

1 内存监控,超过阀值则杀掉:

vi kill.sh

#!/bin/bash

#内存占用超过120M
threshold=120000
cmm="/usr/bin/kill"
if [ ! -f "$cmm" ]; then
    cmm="/bin/kill"
fi

ps aux | grep 'artisan queue:work' | grep -v grep | awk '{print $2"|"$6}' | cat | while read line
do
    pid=`echo $line | cut -d "|" -f 1`
    rss=`echo $line | cut -d "|" -f 2`

    if [ $rss -gt $threshold ]; then
        kill=`$cmm -9 $pid`
	date=`date "+%Y-%m-%d %H:%M:%S"`
        if [ $kill ]; then
            echo $date' -- 'PID:$pid' - RSS('$rss'K) > threshold('$threshold'K) - Was Killed.'
        elif
            ech $date' -- 'PID:$pid' - RSS('$rss'K) > threshold('$threshold'K) - Kill Failed.' 
        fi
    fi
done

这里被杀掉的进程由于有使用进程监控器监控,被杀掉后会自动重启。

2 内存监控,超过阀值则杀掉并重启

#!/bin/bash

#判断进程数量
live=`ps -efH | grep 'agent.js' | grep -v 'grep' | wc -l`
if [ $live -eq 0 ]; then
	/usr/local/bin/node /var/www/ebay-ap/agent.js 2>&1 >> /var/www/ebay-ap/log.txt &
fi

#判断是否超过阀值
threshold=204800

cmm="/usr/bin/kill"
if [ ! -f "$cmm" ]; then
    cmm="/bin/kill"
fi

ps aux | grep 'agent.js' | grep -v grep | awk '{print $2"|"$6}' | cat | while read line
do
    pid=`echo $line | cut -d "|" -f 1`
    rss=`echo $line | cut -d "|" -f 2`

    if [ $rss -gt $threshold ]; then
        date=`date "+%Y-%m-%d %H:%M:%S"`
        kill=`$cmm -9 $pid`
        if [ $kill ]; then
            echo $date -- PID: ${pid} Kill Failed [RSS: ${rss} gt ${threshold}]
        else
            echo $date -- PID: ${pid} Kill Success [RSS: ${rss} gt ${threshold}]
            /usr/local/bin/node /var/www/ebay-ap/agent.js 2>&1 >> /var/www/ebay-ap/log.txt &
        fi
    fi
done

如果进程异常退出了,则自动拉起。然后判断内存是否超标,如果超标则先杀掉然后再重启。这个方式看起来粗暴了一些,不过是非常有效的。

3 封杀某个区域的IP

#下载中国IP库
wget http://www.ipdeny.com/ipblocks/data/countries/cn.zone

#运行以下脚本
#!/bin/bash

COUNTRY = "cn"
IPTABLES = /sbin/iptables
EGREP = /bin/egrep

if [ "$(id -u)" != "0" ]; then
   echo "you must be root" 1>&2
   exit 1
fi

resetrules() {
    $IPTABLES -F
    $IPTABLES -t nat -F
    $IPTABLES -t mangle -F
    $IPTABLES -X
}

resetrules

for c in $COUNTRY
do
        country_file = $c.zone

        IPS = $($EGREP -v "^#|^$" $country_file)
        for ip in $IPS
        do
           echo "blocking $ip"
           $IPTABLES -A INPUT -s $ip -j DROP
        done
done

exit 0

4 防DD脚本

#!/bin/sh

IGNORE_IP_LIST="/usr/local/ddos/ignore.ip.list"
IPT="/sbin/iptables"

KILL=1

# NO_OF_CONNECTIONS默认是150,这是一个经验值,如果服务器性能比较高,可以设置200以上,以避免误杀
NO_OF_CONNECTIONS=150

# 解锁的时间,单位为秒,可以设置更长时间
BAN_PERIOD=86400

# 解除对IP的封锁 过程就是运行一个睡眠脚本
unbanip()
{
	# 产生解除IP封锁时用到的随机脚本文件
        UNBAN_SCRIPT=`mktemp /tmp/unban.XXXXXXXX`

	# 临时文件
        TMP_FILE=`mktemp /tmp/unban.XXXXXXXX`

	# 将被解除封锁的IP
        UNBAN_IP_LIST=`mktemp /tmp/unban.XXXXXXXX`

	# 产生解除IP封锁的脚本内容
        echo '#!/bin/sh' > $UNBAN_SCRIPT
	# $BAN_PERIOD睡眠时间,表示$UNBAN_SCRIPT睡眠多久后继续
        echo "sleep $BAN_PERIOD" >> $UNBAN_SCRIPT

	# 输入重定向,行对于$line变量,是当前需要堵塞的IP,脚本运行过程中产生
	while read line; do
		# 解除IP封锁
		echo "$IPT -D INPUT -s $line -j DROP" >> $UNBAN_SCRIPT

		# 把将要解除堵塞的IP写入$UNBAN_IP_LIST
		echo $line >> $UNBAN_IP_LIST
	done < $BANNED_IP_LIST

        # 从$IGNORE_IP_LIST中去掉$UNBAN_IP_LIST,把结果写入$TMP_FILE(差集)
        echo "grep -v --file=$UNBAN_IP_LIST $IGNORE_IP_LIST > $TMP_FILE" >> $UNBAN_SCRIPT

	# 移动$TMP_FILE到$IGNORE_IP_LIST
        echo "mv $TMP_FILE $IGNORE_IP_LIST" >> $UNBAN_SCRIPT

	# 删除$UNBAN_SCRIPT
        echo "rm -f $UNBAN_SCRIPT" >> $UNBAN_SCRIPT
	# 删除$UNBAN_IP_LIST
        echo "rm -f $UNBAN_IP_LIST" >> $UNBAN_SCRIPT
	# 删除$TMP_FILE,经过上面的移动操作,$TMP_FILE其实已经不存在
        echo "rm -f $TMP_FILE" >> $UNBAN_SCRIPT

	# 在后台运行$UNBAN_SCRIPT
	. $UNBAN_SCRIPT &
}


TMP_PREFIX='/tmp/ddos'
TMP_FILE="mktemp $TMP_PREFIX.XXXXXXXX"

# 产生临时文件,存放已经被堵塞的IP
BANNED_IP_LIST=`$TMP_FILE`

# 产生临时文件,存放当前可能被堵塞的IP
BAD_IP_LIST=`$TMP_FILE`

#取回IP列表并按照数量从高到底排序
netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr > $BAD_IP_LIST

# 如果配置为需要堵塞
if [ $KILL -eq 1 ]; then            
	# 超过100就记录
	NUM_CONNECTIONS=100  
	# 按照日期存放
	CNTS_LOG="/usr/local/ddos/$(date +%Y)/$(date +%m)/"
	mkdir -p $CNTS_LOG
	CNTS_LOG="$CNTS_LOG$(date +%Y%m%d).log"
	while read line; do
		CURR_CONN=$(echo $line | cut -d" " -f1)
		CURR_IP=$(echo $line | cut -d" " -f2)
		if [ $CURR_CONN -lt $NUM_CONNECTIONS ]; then
			break
		fi
		echo "$CURR_IP with $CURR_CONN connections at `date`" >> $CNTS_LOG
	done < $BAD_IP_LIST

        IP_BAN_NOW=0
        while read line; do
		# 当前这个IP有多少个连接
                CURR_LINE_CONN=$(echo $line | cut -d" " -f1)            
		# 当前IP
                CURR_LINE_IP=$(echo $line | cut -d" " -f2)          

		# 如果这个IP链接数小于预设,终止(因为数据经过排序)
                if [ $CURR_LINE_CONN -lt $NO_OF_CONNECTIONS ]; then     
			break
                fi

		# 计算当前IP在$IGNORE_IP_LIST出现了多少次
                IGNORE_BAN=`grep -c $CURR_LINE_IP $IGNORE_IP_LIST`
		# 如果当前IP已经在$IGNORE_IP_LIST,跳过,可以通过这个方式设置永不堵塞某些IP
                if [ $IGNORE_BAN -ge 1 ]; then
			continue
                fi

		# 进入到这里表示当前必定有IP要被堵塞
                IP_BAN_NOW=1

		# 添加堵塞了的IP到当前堵塞列表,$BANNED_IP_LIST会应用到unbanip函数
                # 如果不实现自动解锁,注释如下行
                echo $CURR_LINE_IP >> $BANNED_IP_LIST

		# 添加堵塞了的IP到$IGNORE_IP_LIST
                # 如果不实现自动解锁,注释如下行
                echo $CURR_LINE_IP >> $IGNORE_IP_LIST
                
		# 开始iptables封锁
                $IPT -I INPUT -s $CURR_LINE_IP -j DROP
                
        done < $BAD_IP_LIST

	# $IP_BAN_NOW等于1表示有IP被封锁了
        if [ $IP_BAN_NOW -eq 1 ]; then
		# 发送通知

		# 同时开始运行解除堵塞程序
                #unbanip                             
        fi
fi

# 清除临时产生的文件
rm -f $TMP_PREFIX.*

函数unbanip实际就是运行一个睡眠脚本,可以把其去掉(不自动解封):

#!/bin/sh

IGNORE_IP_LIST="/usr/local/ddos/ignore.ip.list"
IPT="/sbin/iptables"
KILL=1
NO_OF_CONNECTIONS=150

TMP_PREFIX='/tmp/ddos'
TMP_FILE="mktemp $TMP_PREFIX.XXXXXXXX"


BAD_IP_LIST=`$TMP_FILE`
netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr > $BAD_IP_LIST

if [ $KILL -eq 1 ]; then
        IP_BAN_NOW=0
        while read line; do
		# 当前这个IP有多少个连接
                CURR_LINE_CONN=$(echo $line | cut -d" " -f1)            
		# 当前IP
                CURR_LINE_IP=$(echo $line | cut -d" " -f2)          

		# 如果这个IP链接数小于预设,终止(因为数据经过排序)
                if [ $CURR_LINE_CONN -lt $NO_OF_CONNECTIONS ]; then     
			break
                fi

		# 计算当前IP在$IGNORE_IP_LIST出现了多少次
                IGNORE_BAN=`grep -c $CURR_LINE_IP $IGNORE_IP_LIST`
		# 如果当前IP已经在$IGNORE_IP_LIST,跳过,可以通过这个方式设置永不堵塞某些IP
                if [ $IGNORE_BAN -ge 1 ]; then
			continue
                fi

		# 进入到这里表示当前必定有IP要被堵塞
                IP_BAN_NOW=1
                
		# 开始iptables封锁
                $IPT -I INPUT -s $CURR_LINE_IP -j DROP
        done < $BAD_IP_LIST

	# $IP_BAN_NOW等于1表示有IP被封锁了
        if [ $IP_BAN_NOW -eq 1 ]; then
		# 发送通知 CURL                           
        fi
fi

# 清除临时产生的文件
rm -f $TMP_PREFIX.*

如果仅仅希望监控一下哪些IP链接数过多,则可以如下改造:

#!/bin/sh

CNTS_LOG="/root/dd/log.txt"
IGNORE_IP_LIST="/root/dd/ignore.ip.list"
CONNECTIONS=5
 
TMP_PREFIX='/tmp/dd'
TMP_FILE="mktemp $TMP_PREFIX.XXXXXXXX"

BAD_IP_LIST=`$TMP_FILE`
netstat -ntu | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr > $BAD_IP_LIST

while read line; do
    CURR_CONN=$(echo $line | cut -d" " -f1)
    CURR_IP=$(echo $line | cut -d" " -f2)
    if [ $CURR_CONN -lt $CONNECTIONS ]; then
        break
    fi

    IGNORE=`grep -c $CURR_LINE_IP $IGNORE_IP_LIST`
    if [ $IGNORE -ge 1 ]; then
        continue
    fi

    echo "`date "+%Y-%m-%d %H:%M:%S"`|$CURR_IP|$CURR_CONN" >> $CNTS_LOG
done < $BAD_IP_LIST
 
rm -f $TMP_PREFIX.*

Linux Bash Shell编程

Bash Shell基础

1 Bash Shell

1.1 系统的合法shell与/etc/shells
Linux下可以使用的shell,可以检查一下/etc/shells这个文件,有下面几个可以用的shell:

#CentOS 7.X中的输出
cat /etc/shells

/bin/sh             #/bin/bash的链接
/bin/bash
/sbin/nologin       #用户不具备Shell环境(一般指无法登陆),指定这个值

/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin

/bin/tcsh
/bin/csh

系统某些服务在运行过程中,会去检查用户能够使用的Shell,而这些Shell的查询就是借助/etc/shells这个文件。在/etc/passwd这个文件内,每行的最后一个段指明了用户使用的shell(如果一个用户不能登录,通常是/sbin/nologin)。

1.2 Bash Shell功能
1) 命令记忆能力(history)
在用户的主文件夹内的.bash_history用来记录历史记录,但是它记录的是前一次登录以前所执行过得命令,而至于这一次登录所执行的命令都被暂存在临时内存中,当注销系统后,该命令记忆才会记录到.bash_history中。
2) 命令与文件补全功能
3) 命令别名设置功能(alias)
可以使用alias直接设置命令别名,直接输入alias可以输出当前存在的命令别名。

alias
alias cp='cp -i'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'
alias ls='ls --color=auto'
alias mv='mv -i'
alias rm='rm -i'
alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'

4) 作业控制、前台、后台控制(job control, foreground, background)
5) 程序脚本
6) 通配符(Wildcard)

1.3 Bash Shell的内置命令: type
为了方便Shell的操作,Bash已经内置了很多命令,利用type可以查看命令是否是内置命令。

2 Shell的变量

2.1 变量的显示与设置: echo unset
在Bash当中,当一个变量名称未被设置时,默认的内容是空的。变量的设置规则:
1) 变量与变量内容以一个等号连接,等号两边不能接空格,注意,如果有空格则是逻辑比较
2) 变量名称只能是英文字母和数字,但开头不能是数字
3) 变量内容如含有空格,需要使用单引号或双引号括起来(如没有引号括起来,但是包含了转义,最终会转义输出),但双引号内特殊字符保持本性,单引号内的任何字符都是一般文本
4) 可以使用转义符将特殊字符变成一般字符
5) 如需要引用其它命令的结果,可以使用反单引号或$()语法,比如`uname –r`和$(uname -r)等同
6) 如要为变量增加内容,可以使用$变量名或${变量名}累加内容(不需要所谓的连接符)
7) 如变量需要在其它子进程中执行,需要以export来使变量变成环境变量:export PATH。
8) 通常大写字符为系统默认变量
9) 取消变量的方法是使用unset 变量名称

2.2 环境变量

用env查看环境变量:

env

XDG_SESSION_ID=167086
HOSTNAME=xxxxx  #主机名
TERM=xterm
SHELL=/bin/bash  #使用的是哪个Shell
HISTSIZE=1000  #历史记录大小
SSH_CLIENT=113.88.168.31 17444 22
SSH_TTY=/dev/pts/3
USER=root
LS_COLORS=
MAIL=/var/spool/mail/root
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin  #执行文件查找的路径
PWD=/root
LANG=en_US.UTF-8 #语系数据,中文编码通常是zh_CN.UTF-8
HISTCONTROL=ignoredups
SHLVL=1
HOME=/root  #代表用户主文件夹
LOGNAME=root
SSH_CONNECTION=113.88.168.31 17444 120.24.42.192 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
XDG_RUNTIME_DIR=/run/user/0
_=/usr/bin/env

用set查看所有变量(含环境变量与自定义变量)

PS1     提示符号设置,可以man bash查询PS1的相关说明
$       当前的Shell的PID,$也是一个变量,要引用时使用$$
?       上个命令的回传码,一般,一个命令成功执行,则会回传一个0值
OSTYPE, HOSTTYPE, MACHTYPE 主机硬件与内核的等级

自定义变量转成环境变量
子进程会继承父进程的环境变量,不会继承自定义变量。直接执行export 变量名,可以把变量转成环境变量,如果要相反操作,用declare。

2.3 影响显示结果的语系变量(locale)
可以使用locale来查询Linux支持多少语系。locale –a其实是列出/usr/lib/locale里面的文件。当设置了LANG或者是LC_ALL时,则其他的语系变量就会被这两个变量所替代。LC_ALL是整体的语系变量,它在/etc/sysconfig/i18n中设置:LANG=”zh_CN.UTF-8”。(CentOS 7.x中对应的配置文件是/etc/locale.conf文件)

2.4 变量键盘读取、数组与声明: read,array,declare
1) read

用法:
read [-pt] variable
参数:
-p 后面可以接提示符
-t 后面可以接等待秒数
 
例子:
read –p “Please putin your name:” –t 30 named
等待用户30秒输入它的名字赋值给named变量

2) declare / typeset
declare / typeset都用来声明变量的类型。

用法:
declare [-aixr] variable
参数:
-a 定义为数据
-i 定义为整数
-x 定义成环境变量
-r 定义变量为只读

默认Bash对于变量有几个基本定义:
变量类型默认为字符串,所以若不指定变量类型,则1+2为一个字符串而不是计算式
Bash环境中的数值运算,默认最多仅能到达整数,所以1/3结果是0

另,declare +x sum 把 – 换成 + 代表取消操作。一般,除非需要特别指定变量类型,否则都不需要使用declare关键字。

3) 数组变量类型
在Bash里,数组的设置方式:var[index]=content

2.5 与文件系统及程序的限制关系: ulimit
Bash是可以限制用户的某些系统资源的,包括可以打开的文件数量、可以使用的CPU时间、可以使用的内存总量等。

用法:
ulimit [-SHacdfltu] [配额]
-H hard limit
-S soft limit,可以超过,但有警告值,必须比hard小
-a 后面不接任何参数,可列出所有的限制额度
-f 此shell可以创建的最大文件容量
-d
-l 可用锁定的内存量
-t 可使用的最大CPU时间
-u 单用户可以使用最大进程数量

2.6 变量内容的删除、替代与替换

3 命令别名与历史命令

3.1 命令别名设置:alias, unalias
3.2 历史命令:history

4 Bash Shell的操作环境

4.1 路径与命令查找顺序
命令运行顺序:
1) 以相对/绝对路径执行命令,如 /bin/ls 或 ./ls (最优先)
2) 由alias找到该命令来执行
3) 由bash内置(bulitin)命令来执行
4) 通过$PATH变量的顺序找到第一个命令来执行

4.2 bash的登录与欢迎信息:/etc/issue, /etc/motd

#查看/etc/issue:
CeontOS release 5.6 (Final)
Kernel \r on an \m

#如同$PS1变量一样,issue这个文件的内容也可以使用反斜杠为变量调用:
\d  本地时间的日期
\l  显示第几个终端机接口
\m  显示硬件等级
\n  显示主机网络名称
\o  显示domain name
\r  操作系统的版本(相当于uname -r)
\t  显示本地端时间的时间
\s  操作系统名称
\v  操作系统版本

如果你想要让用户登录后取得一些信息,那么可以将信息加入/etc/motd里面,这样所有人登录后都会获取此信息。

4.3 Bash的环境配置文件

4.3.1 登录Shell 和 非登录Shell
登录Shell需要完整的登录流程,非登录Shell取得Bash接口的方法不需要重复登录的流程。

登录Shell 和 非登录Shell它们读取的配置文件数据并不一样。
登录Shell只会读取如下两个配置文件:
1) /etc/profile: 这是系统整体的设置,最好勿改动此文件
2) ~/.bash_profile 或 ~/.bash_login 或 ~/.profile: 属于用户个人设置,要改自己的数据就放这里。

4.3.2 /etc/profile
这个配置文件可以利用用户的标示符UID来决定很多重要的变量数据。每个用户登录取得Bash时一定会读取的配置文件。这个文件设置的变量主要有:PATH, MAIL, USER, HOSTNAME, HISTSIZE。 除此还调用外部数据:
1) /etc/inputrc
/etc/profile会主动判读用户有没有自定输入的按键功能,如果没有,它就会决定设置”INPUTRC=/etc/inputrc”这个变量。此文件内容为Bash的热键等数据。
2 /etc/profile.d/*.sh
这个目录下面的文件规定了Bash操作接口的颜色、语系、ll与ls命令的命令别名、vi的命令别名、which的命令别名等。如果需要帮所有用户设置一些共享的命名别名时,可以在这个目录下面创建.sh的文件,并写入数据。

4.3.3 /etc/sysconfig/i18n
这个文件是由/etc/profile.d/lang.sh调用的。bash默认使用何种语系的配置文件。

4.3.4 ~/.bash_profile
~/.bash_profile 或 ~/.bash_login 或 ~/.profile从左到右,只会读取其中一个。查看~/.bash_profile发现,它其实还运行了. ~/.bashrc

在CentOS的登录Shell环境下,最终被读取的配置文件是”~/.bashrc”这个文件。所以可以将自己的偏好设置写入该文件。

4.3.5 source: 读入环境配置文件的命令

用法:
source 配置文件名
例子:source ~/.bashrc 和 . ~/.bashrc一样

利用source或小数点都可以将配置文件的内容读进当前的shell环境中。

4.3.6 ~/.bashrc
非登录Shell仅会读取~/.bashrc。查看其内容发现它会主动调用/etc/bashrc,而它定义如下数据:
依据不同的UID规定umask的值和提示符(PS1)
调用/etc/profile.d/*.sh的设置

4.3.7 其它相关配置文件
/etc/man.conf
规定了使用man的时候man page的路径到哪里去寻找。
~/.bash_history
~/.bash_logout
当注销bash后系统再所完什么操作后才离开。

4.4 终端机的环境设置:stty, set

4.5 通配符与特殊符号

*       0个或多个
?       任意一个字符
[]      在其中的一个字符
[-]     如[a-z] [0-9]
[^]     排除之中的字符的一个字任意字符

#除了通配符,bash中的特殊符号:
#       注释
\       转移符号
|       管道
;       分隔符号
~       用户的主文件夹
$       变量引用符
&       后台运行
!       逻辑运算非
/       目录符
>, >>   数据流重定向,输出导向

5 数据流重定向

执行一个命令的时候,这个命令可能会由文件读入数据,经过处理之后,再将数据输出到屏幕上。标准输出与标准错误输出这两个命令默认都是输出到屏幕上面来。标准输出指的是命令执行所回传的正确的信息,而标准错误输出可理解为命令执行失败后,所回传的错误信息。

数据流重定向可以将stdout与stderr分别传送到其它的文件或设备去,所用到的特殊字符:
1) 标准输入(stdin): 代码为0,使用< 或 <<(指定结束输入的定界符); [bash] cat > catfile << "eof" [/bash] cat从标准输入接收输入然后显示,这里定位到catfile中,输入遇到定界符eof即刻结束输入。 2) 标准输出(stdout):代码为1,使用> 或 >>
3) 标准错误输出(stderr): 代码为2,使用2> 或 2>>

/dev/null垃圾桶设备与特殊写法
丢弃错误信息
2> /dev/null
如果所有信息都丢弃或都写入一个文件
> /dev/null 2>&1 或 &> /dev/null 此为特殊写法

标准输入: < 与<<
将原本需要有键盘输入的数据改由文件内容来替代。

命令执行的判断依据(; , && ||)
使用逗号可以在一行中连续执行多个命令
每个命令执行都有回传码,成功执行回传0,失败回传1,bash使用$?在动态记录这个这个值(?是一个变量,$也是变量,表示Bash的进程,使用$$来引用)。&& 和 ||可以用来分隔两个命令,使用&&时表示第一个命令执行成功则执行第二个,||表示第一个执行失败则执行第二个(判断依据是当前bash的$?的记录,所以当有多个&& 和 ||时需要注意,它判断的依据不是前面的命令,而是当前的bash记录的$?,当然,命令的执行会影响到$?值)

6 管道命令(pipe)

管道命令使用 | 界定符号。管道命令 | 仅能处理经由前面一个命令传来的正确信息,也就是标准输出的信息,对于标准错误输出并没有直接处理的能力。

在每个管道后面接的第一个数据必定是命令,而且这个命令必须要能接收标准输入的数据才行,这样的命令才可以是管道命令,管道命令有两个要注意的地方:
管道命令仅会处理标准输出,对于标准错误输出会予以忽略
管道命令必须要能够接收来自前一个命令的数据成为标准输入继续处理才行。

6.1 选取命令: cut, grep
一般,选取信息通常是针对行来分析的,并不是整篇信息分析的。

cut
主要用途在于将同一行里面的数据进行分解。

用法:
cut –d ‘分隔符’ -f fields
cut –c 字符范围
参数:
-d 后面接分隔字符,与-f 一起使用
-f 依据-d的分隔字符将一段信息切割为数段,用-f 取出第几段的意思
-c 以字符的单位取出固定字段区间
 
例子:
echo $PATH | cut -d ‘:’ -f 5 用:分割,取出第5段
echo $PATH | cut –d ‘:’ -f 3,5 用:分割,取出第3和第5段

cut在处理多空格相连的数据时比较困难。

grep
分析一行信息,如果符合条件则把整行信息输出。

用法:
grep [-acinv] [--color=auto] ‘查找字符串’ filename
参数:
-a 将binary文件以text文件的方式查找书籍
-c 计算找到’查找字符串’的次数
-i 忽略大小写
-n 输出行号
-v 反向选择,即显示出不符合的行
--color=auto 将找到的关键字部分加上颜色显示
 
例子:
last | grep ‘root’ | cut –d ‘ ’ -f 1
grep -n "conf" /etc/*  #找出文件含有conf字符串的行(文件)

6.2 排序命令:sort, wc, uniq

sort
对行进行排序,排序的字符与语系的编码有关,建议使用LANG=C来让语系统一。

用法:
sort [-fbMnrtuk] [file or stdin]
参数:
-f 忽略大小写
-b 忽略最前面的空格符
-M 以月份的名字来排序
-n 使用纯数字进行排序(默认以文字类型来排序)
-r 反向排序
-u 就是uniq,去重复
-t 分割符号,默认是用[Tab]来分隔
-k 以哪个区间来进行排序
 
例子:
cat /etc/passwd | sort -t ':' -k 3 –n
用冒号分隔取第三段当做纯数字来排序(-n 表示纯数字)

uniq
排序完成,将重复的行去掉。

用法:
uniq [-ic]
参数:
-i 忽略大小写
-c 进行计数
 
例子:
last | cut -d ‘ ‘ -f 1 | sort | uniq

wc

用法:
wc [-lwm]
参数:
-l 行数
-w 字数
-m 字符数

6.3 双向重定向: tee
tee会同时将数据流送到文件与屏幕,而输出到屏幕的,其实就是stdout,可以让下个命令继续处理。

用法:
tee [-a] file
参数:
-a 以累加(append)的方式将数据加入file当中
 
例子:
last | tee last.list | cut -d “ ” -f 1
利用tee保存一份数据到last.list并输出到屏幕。

6.4 字符转换命令:tr,col,join,paste,expand

tr

tr [-ds] SET1
参数:
-d 删除信息当中的SET1这个字符串
-s 替换掉重复的字符
 
例子:
last | tr ‘[a-z]’ ‘[A-Z]’
cat /root/passwd | tr –d ‘\r’

col

col [-xb]
参数:
-x 将tab键转换成对等的空格键
-b 在文字内有反斜杠(/)时,仅保留反斜杠最后接的那个字符

join

join [-ti12] file1 file2
参数:
-t join默认以空格符分割数据,并且对比第一个字段的数据,如两个文件相同,则将两条数据练成一行,且第一个字段放在第一个,-t指定分割符号
-i 忽略大小写
-1 数字1,代表第一个文件用药哪个字段来分析的意思
-2代表第二个文件用药哪个字段来分析
 
/etc/passwd的第四个字段是GID,哪个GID记录在/etc/group当中的第三个字段
join -t ‘:’ -1 4 /etc/passwd -2 3 /etc/group

相当关系数据库中的关联引用。注:在使用join之前,你所需要处理的文件应该要事先经过排序处理。

paste
直接将两行贴在一起,且中间以[tab]键隔开。

paste [-d] file1 file2
参数:
-d 后面可以接分隔符,模式是[tab]
- 如果file部分写成-,表示来自标准输入的数据
 
例子:
paste /etc/passed /etc/shadow
cat /etc/group | paste /etc/passwd /etc/shadow - | head –n 3
里面的横杆(-)表示来自cat的输出

expand
将[tab]按键转成空格键。

expand [-t] file
参数:
-t 后面可以接数字,一般,一个[tab]按键可以用8个空格键替换,也可以自行定义一个[tab]按键代表多少个字符。

6.5 切割命令:split
将大文件依据文件大小或行数来切割成小文件。

split [-bl] file PREFIX
参数:
-b 后接要切割成的文件大小,单位是b, k, m等
-l 以行数进行切割
PREFIX 代表前导符,可以作为切割文件的前导文字

6.6 参数代换:xargs
xargs再产生某个命令的参数。xargs可以读入stdin的数据,并且以空格符或断行字符进行分辨,将stdin的数据分割成arguments。使用xargs的原因是很多命令其实并不支持管道命令,因此可以通过xargs来提供该命令引用标准输入之用。

用法:
xargs [-0epn] command
参数:
-0 如果stdin含有特殊字符,这个参数可以将它还原成一般字符。
-e 这个是EOF意思。后面可以接一个字符串,当xargs分析道这个字符串时,就会停止工作。
-p 在执行每个命令的参数时,都会询问用户的意思
-n 后面接次数,每次command命令执行时,要使用几个参数的意思
 
例子:
cut –d ‘:’ –f1 /etc/passwd | xargs –p –n 5 finger
这样finger将从前面获取5个参数。
 
find /sbin –perm +7000 | xargs ls –l
通过xargs把前面的输出作为后面命令的参数,这个就是让ls原本不支持管道的命令间接支持管道的用法。

6.7 关于减号 – 的用途
在管道命令当中,经常会使用到前一个命令的stdout作为这次的stdin, 某些命令需要用到文件名来进行处理时,该stdin与stdout可以利用减号来替代(经过管道后前面的stdout就是后面的stdin,可以用-来引用前面的stdout或叫当前命令的stdin)。

tar –cvf – /home | tar –xvf –
前面把/home压缩到stdout,被管道带到下一个命令,下个命令使用减号引用管道带过来的前一个命令的stdout,作为其输入,然后再接压缩到stdout。

Bash Shell 正则表达式
1 基础正则表达式
1.1 语系对正则表达式的影响
使用正则表达式时,需要留意当时环境的语系,否则结果可能有差异。可以使用统一语系,比如LANG=C

[:alnum:]       英文大小写字符和数字,即0-9,a-z, A-Z
[:alpha:]       英文大小写字符,即a-z, A-Z
[:blank:]       空格和[Tab]
[:cntrl:]       键盘上控制键,即包括CR, LF, Tab, Del等
[:digit:]       数字,即0-9
[:graph:]       除了空格键以为所有按键
[:lower:]       小写字母
[:print:]       可以被打印的字符
[:punct:]       标点符号
[:upper:]       大写字母
[:space:]       空格
[:xdigit:]      十六进制的数字类型

1.2 基础正则表达式字符

^word        以word开头
word$        以word结尾
.        一个任意字符
\        转义字符
*        重复任意次
[list]       选取列出来的其中一个字符
[n1-n2]      选择n1到n2之间的一个字符,比如[0-9]
[^list]      选取排除了列出来的字符的其它任一字符
\{n,m\}      重复n次,最多不超m次,如果是\{n \}则表示重复n次,如果是\{n,\}则上重复n次以上

1.3 扩展正则表达式

+       重复一次或一次以上
?       重复零次或一次
|       用或的方式找出数个字符串
()      分组
()+     重复组

Bash Shell 工具- sed

rpm -qa | grep sed
sed-4.2.2-5.el7.x86_64

工具sed并不是Bash Shell的组成部分(grep cut等这些是内置命令),不过这个工具是Bash Shell编程不缺少的部分。

sed本身是一个管道命令,可以分析标准输入,而且sed还可以将数据进行替换、删除、新增、选取特定行等。

用法:
sed [-nefr] [动作]
参数:
-n 使用安静模式,一般,所有来自STDIN的数据都会列出到屏幕上,加入-n后,只有经过sed处理的那行才会被列出
-e 直接在命令行模式上进行sed的动作编辑
-f 直接将sed的动作写在一个文件内,-f filename则可以执行filename内的sed动作
-r sed的动作支持的是扩展正则语法
-i 直接修改读取的文件内容
 
动作说明:
[n1,[,n2]]function
n1, n2 选择进行动作的行数
 
function 参数
a 新增,a后可以接字符串
c 替换
d 删除
i  插入
p 打印
s 替换 通常可以搭配正则,如1,20s/old/new/g

例子:
1) 以行为单位的新增/删除
nl /etc/passwd | sed ‘2,5d’
将2~5行删除,d表示删除,sed后面接的动作务必用单引号括住,应该应该是sed –e,不用也可以。如果要删除3行以后所有行,nl /etc/passwd | sed ‘3,$d’,这个$表示最后一行,如果只要删除第二行,则nl /etc/passwd | sed ‘2d’

nl /etc/passwd | sed ‘2a vfeelit’
在第二行后加上vfeelit字符串,如果把a改成i,则是在第二行之前插入,如果要增加多行,每行后面需要使用转义字符分隔。

2) 以行为单位的替换与显示
nl /etc/passwd | sed ‘2,5c vfeelit’
这里的c表示替换,2到5行用给定的字符替换。

nl /etc/passwd | sed –n ‘5,7p’
这里的-n表示只有经过处理的打印出来(p),sed是把所有的都进行输出的,比如要列出11-20行,没有sed工具之前需要这样:head –n 20 | tail –n 10

3) 部分数据的查找并替换
sed的查找与替换与vi相当类似:
sed ‘s/查找字符串/新字符串/g’ 字母s前可以限定行数,比如2,10

cat /etc/man.config | grep ‘MAN’ | sed ‘s/#.*$//g’ | sed ‘/^$/d’

4) 直接修改文件内容
sed –s ‘s/\.$/\!/g’ regular.txt
直接在regular.txt中修改最后字符为.的修改为!

sed的”-i”参数可以直接修改文件内容。

Bash Shell 工具- awk

rpm -qa | grep awk
gawk-4.0.2-4.el7.x86_64

awk主要是处理每一行的字段内的数据,而默认的字段的分隔符为空格或[tab]键。

用法:
awk ‘条件类型1 {动作1} 条件类型2 {动作2} ..’ filename

注:注意其严格的格式,awk后接单引号,动作需要用大括号括起来。
例子:
last –n 5 | awk ‘{print $1 “\t” $3}’
显示每一行的第一和第三字段。

每一行的每个字段都是有变量名称的,那就是$1,$2 … 等变量名称。还有$0,表示整行数据的意思。

整个awk处理流程:
-读入一行,将数据填入$0,$1,$2这些变量中
-依据条件类型,判断是否执行后面的动作
-做完所有条件和动作
-如果还有后续行,重复

awk以行为单位,以字段为最小处理单位。至于数据有几行几列,则需要awk内置变量的支持:

NF      每一行拥有的字段总算
NR      带便目前处理的是第几行
FS      目前的分隔字符,默认是空格键

例子:
last –n 5 | awk ‘{print $1 “\t lines: “ NR “\t coleus:” NF}’

awk的逻辑运算符跟一般的没有差别,==表示判断是否等于,赋值使用=
例子:

cat /etc/passwd | awk ‘{FS=”:”} $3<10 {print $1 “\t ” $3}’

这里第一行没有正确显示,因为读入第一行时,那些$1,$2变量还是以空格作为分隔的,虽然用来FS=”:”,但是是从第二行开始生效的。可以使用BEGIN这个关键字预先设置awk的变量。

cat /etc/passwd | awk ‘BEGIN {FS=”:”} $3<10 {print $1 “\t ” $3}’

几个重要说明:
所有awk的动作,即在{}内的动作,如果有需要多个命令辅助时,可利用分号分隔
逻辑运行如果是等于判断务必使用==
格式化输出时,在printf的格式设置当中,务必加上\n才能进行分行
与bash shell变量不同,在awk中,变量可以直接使用,不需要加上$符号

Bash Shell 脚本
1 Shell Script入门
Shell Script编写注意事项:
1) 命令的执行从上而下,从左而右地分析与执行
2) 命令、参数间的多个空白会被忽略,空白行也被忽略,[tab]按键被视为空格键
3) 如果读取到一个Enter符号(CR),就尝试开始执行命令(以行为分隔)
4) #符号为注释

执行Shell Script方法:
1) 直接执行:Shell Script文件必须要具备可读可执行的权限,然后:
使用绝对路径来执行 比如/root/shell.sh
使用相对路径来执行 比如./shell.sh
变量PATH功能,将shell.sh放在PATH指定的目录内,如/bin/,然后让shell自动搜索PATH获取这个脚本并执行

2) 以bash进程来执行:通过bash shell.sh 或sh shell.sh来执行
如果shell.sh在/bin内具有rx的权限,那就直接输入shell.sh即可执行该脚本程序。因为/bin/sh是/bin/bash的链接文件,使用sh就是间接使用bash,此时shell.sh只要有r的权限即可被执行。

Shell Script组成部分
1) 第一行#!/bin/bash 声明这个script使用的shell名称,这样就能根据声明加载Shell(这里是bash)的相关环境配置文件(一般来说就是非登录Shell的~/.bashr)
2) 程序说明部分,以#开头,添加必要的说明信息
3) 主要环境变量声明
4) 程序部分
5) 告知执行结果
一个命令的执行是否成功,可以使用$?这个变量来查看。那么也可以利用exit这个命令来让程序中断,并且回传一个数值给系统。比如exit 0,这代表离开Script并且回传一个0给系统,如果执行echo $?则可得到0的值。利用exit n (n是数字)的功能,可以自定义错误信息。

范例:
1) 交互式脚本:变量内容由用户决定

#!/bin/bash
read –p “Please input you full name: ” fullname #提示用户输入
echo –e “\n Your full name is: $fullname”

2) 数值运算:简单的加减乘除
用declare来把变量定义成整数后才能进行加减运行,此外还可以利用$((计算式))来进行数值运算。Bash Shell里面仅支持整数。

#!/bin/bash
#
firstnu=10
secnu=6
total=$(($firstnu*$secnu))
echo –e “\n The result is: $total”

在数值运行上,也可以使用declare –i total=$firestnu*$secnu。

Shell Script的执行方式区别
脚本的执行除了以上提到的方式还可以利用source或小数点来执行。

1) 利用直接执行的方式来执行
直接命令执行(绝对路基、相对路径或$PATH内),或利用bash/sh来执行脚本时,该脚本都会使用一个新的bash环境来执行脚本内的命令。当子进程完成后,子进程内的各项变量或操作将会结束而不会传回到父进程中。
2) 利用source来执行脚本:在父进程中执行

2 判断式
2.1 利用test命令的测试功能
以下只列常用:

关于某个文件的“文件类型”判断,如test –e feliename表示是否存在

-e 		该文件是否存在
-f		该文件是否存在并且是文件
-d		该文件是否存在并且是目录

关于文件权限的检测,如test –r filename表示是否可读(但root除外)

-r		检测文件是否存在并且可读
-w 		检测文件是否存在并且可写
-x 		检测文件是否存在并且可执行
-u 		检测文件是否存在并且具有SUID属性
-g 		检测文件是否存在并且具有SGID属性
-k 		检测文件是否存在并且具有Sticky bit属性

关于两个整数之间的判断,如test n1 –eq n2

-eq		两数值相等 equal
-ne		两数值不等 not equal
-gt		n1大于n2 greater than
-lt		n1小于n2 less than
-ge		n1大于等于n2 grater than or equal
-le		n1小于等于n2 less than or equal

判定字符串的数据

test –z string	判定字符串是否为0,若string为空字符串,则为true
test –n string	判定字符串是否为非0,若string为空字符串,则为false
test str1=str2	判定str1是否等于str2,若相等,则回传ture
test str1!=str2	判定str1是否不等于str2,若相等,则回传false

多重条件判定,如test –r filename –a –x filename

-a		两个条件同时成立
-o		其中一个条件成立
!		取反,如test ! –x file,当file不具有x时,回传true

2.2 利用判断符号[]
可以使用[]来替代test,如[ -z “$HOME” ]; echo $?
注:在bash中用中括号作为shell的判断式时,必须要注意中括号的两端需要有空格符来分隔,如[空格”$HOME”空格==空格”$MAIL”空格]

2.3 Shell Script的默认变量($0,$1….)

Script针对参数已经设置好一些变量名称:

/path/to/scriptname  opt1  opt2  opt3
$0	             $1    $2    $3

执行的脚本文件名为$0变量,第一个接的参数就是$1。除此,还有一些特殊变量:

$#		代表后接的参数个数
$@		代表”$1”、 ”$2”、 ”$3”之意,每个变量是独立的(用双引号括起来)
$* 		代表”$1c$2c$3c$4”,其中c为分隔字符,默认为空格键

shift:造成参数变量号码偏移(**)
shift会移动变量,而且shift后面可以接数字,代表拿掉最前面几个参数的意思。

3 条件判断式
3.1 利用if..then
单层、简单条件判断式

if [ 条件判断式 ]; then
    内容
fi

如果有多个条件要判断时,可以有多个中括号来隔开,括号之间以&&或||隔开:
&&代表AND, ||代表or
比如:[ “$yn” == ”Y” –o “$yn” == ”y” ] 可以替代为:[ “$yn” == ”Y” ] || [ “$yn” == ”y” ]

多重、复杂条件判断式

if [ 条件判断式 ]; then
else
fi
 
if [ 条件判断式一 ]; then
elif [ 条件判断式二 ]; then
else
fi

elif也是个判断式,因此出现elif后面都要接then来处理。

3.2 利用case..esac判断
语法:

case $变量名称 in
    “第一个变量内容”)
        程序段
        ;;
    “第二个变量内容”)
        程序段
        ;;
    *)                  #最后一个变量内容都会用*来代表所有权其它值
        程序段
        exit 1
        ;;
esac

3.3 利用funtion功能
函数可以在Shell Script当中做出一个类似自定义执行命令的东西,因为Shell Script的执行方式是由上而下、由左而右的,因此在Shell Script当中的function的设置一定要在程序的最前面。

另外,function也是拥有内置变量的,它的内置变量与Shell Script很类似,函数名称代表$0,而后续接的变量也是以$1$2..来替代的(如果要向函数传递参数,对应位置的参数将替换$1$2..,比如一个函数叫getList,传递参数格式: getList aaa bbb, 这个函数内$1等于aaa,$2等于bbb)。

4 循环
4.1 while do done, until do done (不定循环)

while [ condition ]
do
    程序段
done
 
until [ condition ]
do
        程序段
done

#计算1+2+3+…+100
#!/bin/bash
#
s=0;
i=0
while [ “$i” != “100” ]
do
    i=$(($i+1))
    s=$(($s+$i))
done
echo “The result is: $s”

4.2 for..do..done(固定循环)

for var in con1 con2 con3 …
do
    程序段
done

4.3 for…do…done的数值处理

for ((初始值; 限制值; 执行步长))
do
    程序段
done

###
for ((i=1; i<=$nu; i=i+1))
do
    s=$(($s+$i))
done

4.4 Shell Script的追踪与调试

用法;
sh [-nvx] script.sh
参数:
-n 不要执行script,仅检查语法
-v 在执行script前先将其内容输出到屏幕上
-x 将使用到的script内容显示到屏幕上

由sh –x的方式来将命令执行过程也显示出来,如此用户可以判断程序代码执行到哪一段时会出现相关信息。

Linux 查看进程

杀掉登录用户:

w
 20:46:40 up 12 days,  4:28,  4 users,  load average: 0.30, 0.12, 0.07
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
root     pts/0    112.97.63.53     20:28    0.00s  0.07s  0.00s w
root     pts/2    125.94.151.83    14:08    3:31m  0.04s  0.04s -bash

ps -ef | grep pts/2

#向上寻找父进程ID
kill -9 pid

动态查看进程CPU内存

#-d 1表示1秒刷新
top -d 1 -p pid

这个可以查看百分比,比如内存占用,如果要查看进程实际霸占的内存,可以通过ps命令。

查看进程霸占内存:

ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND

RSS对应实际霸占的内存(KB),这里需要知道,进程占用内存一般是指堆内存(其它一般占比较少),而堆内存内中是有空闲的,不过这个都归到进程内存占用。另外,如果要查看进程更加详细的信息:

cat /proc/pid/status

Name:	node
State:	S (sleeping)
Tgid:	31897
Ngid:	0
Pid:	31897    进程号
PPid:	1        父进程
TracerPid:	0
Uid:	0	0	0	0
Gid:	0	0	0	0
FDSize:	256
Groups:	0 
VmPeak:	 1302912 kB
VmSize:	 1301888 kB
VmLck:	       0 kB
VmPin:	       0 kB
VmHWM:	  123372 kB
VmRSS:	  122772 kB实际霸占内存
VmData:	 1262976 kB
VmStk:	     136 kB
VmExe:	   19980 kB
VmLib:	    4056 kB
VmPTE:	     636 kB
VmSwap:	       0 kB
Threads:	9  线程数量
SigQ:	0/31225
SigPnd:	0000000000000000
ShdPnd:	0000000000000000
SigBlk:	0000000000000000
SigIgn:	0000000000001000
SigCgt:	0000000188004202
CapInh:	0000000000000000
CapPrm:	0000001fffffffff
CapEff:	0000001fffffffff
CapBnd:	0000001fffffffff
Seccomp:	0
Cpus_allowed:	7fff
Cpus_allowed_list:	0-14
Mems_allowed:	00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001
Mems_allowed_list:	0
voluntary_ctxt_switches:	1373
nonvoluntary_ctxt_switches:	4465