分类 linux 下的文章

OpenSSH建立二层和三层加密隧道

OpenSSH 可以使用tun/tap设备来创建一个加密隧道,SSH隧道类似mode TCP模式下的OpenVPN,对于有需求快速设置一个基于IP的VPN来说非常方便。使用SSH隧道的优点:

  1. 不需要安装和配置额外的软件
  2. 使用SSH认证并自带加密,不需要使其它VPN软件一样配置共享密钥或者证书
  3. 兼容性强,可以建立二层隧道或三层隧道,所有3/4层协议如:ICMP、TCP/UDP 等都可支持
  4. 配置过程简单

当然也有缺点:

  1. 基于TCP协议,其传输效率较低
  2. 隧道依赖于单个TCP连接,容易中断或假死
  3. 需要ROOT权限

这里还要说明一下二层和三层的区别:

  • layer 2 : 交换单元是帧,只识别MAC地址,只有交换功能,相关协议:Ethernet、VLAN、STP、PPP、FDDI、ARP(OSI模型)、MPLS(介于2层3层之间)等,在Linux中是虚拟点对点设备,显示为tap
  • layer 3 : 交换单元是包,能识别MAC地址和IP,有交换和路由功能,相关协议:IP、ICMP、IGMP、IPsec等,在Linux中是虚拟以太网设备,显示tun

在Linux中tun/tap都可以设置IP,只是模拟的工作层有区别,tun是模拟三层网络设备,收发的是IP包,无法处理以太网数据帧。tap模拟的是二层设备,收发的是以为网数据帧,更接近物理网卡,可以和物理网卡通过网桥绑定。我们日常用的wmware虚拟机中的nat网络,对应的就是tun,桥接网络对应的就是tap。

一、ssh server端配置

首先要使用ssh的二层和三层隧道,尽量保证系统差别不要太大,我这里两边都是oracle linux 8,SSH版本更新到最新。server端需要允许root登录和隧道,编辑/etc/ssh/sshd_config:

PermitRootLogin yes
PermitTunnel yes
#建议添加
TCPKeepAlive yes

PermitTunnel yes代表允许point-to-point (layer 3)和ethernet (layer 2)

二、三层隧道配置

2.1 建立三层隧道

在client端执行:

ssh -w any:any  root@[server_ip] 

如果客户端和服务器端没有其它的tun设备,就会在客户端和服务器端各生成一个名为tun0设备,指定any可以自动分配可用的设备。查看设备:

[root@centos8 ~]# ip addr
3: tun0: <POINTOPOINT,MULTICAST,NOARP> mtu 1500 qdisc noop state DOWN group default qlen 500
    link/none 

可以看到有个tun0 <POINTOPOINT,MULTICAST,NOARP>,这个设备是未激活的状态,如果用ifconfig命令查看,需要加上-a参数才能看到。

为安全起见,建议使用以下命令:

ssh -NTCf -w 5:5 root@[server_ip]
  • “-N”:不执行远程命令
  • “-T”:不分配终端
  • “-C”:压缩传输的流量
  • “-f”:在客户端后台运行

执行完该命令client和server各自会创建一个tun5的点对点设备,下一步就是需要激活设备并分配IP。

2.2 分配IP并激活隧道

分配IP不要与服务器和客户端网段有冲突,这里假设:

客户端IP:10.0.0.2 tun5
服务器IP:10.0.0.1 tun5

服务器激活点对点设备并分配IP和路由:

ip link set tun5 up
ip addr add 10.0.0.1/32 peer 10.0.0.2 dev tun5

客户端同理:

ip link set tun5 up
ip addr add 10.0.0.2/32 peer 10.0.0.1 dev tun5

这时隧道已经建立成功,此时用查看一下状态:

[root@centos8 ~]# ip addr
14: tun5: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UNKNOWN group default qlen 500
    link/none 
    inet 10.0.0.1 peer 10.0.0.2/32 scope global tun5

可以看到比之前多了“UP,LOWER_UP”,说明已经激活。正常情况下客户端和服务器已经可以相互ping。

客户端用一条命令也能搞定:

ssh \
  -o PermitLocalCommand=yes \
  -o LocalCommand="ip link set tun5 up && ip addr add 10.0.0.2/32 peer 10.0.0.1 dev tun5 " \
  -o TCPKeepAlive=yes \
  -w 5:5  root@[server_ip] \
  'ip link set tun5 up && ip addr add 10.0.0.1/32 peer 10.0.0.2 dev tun5'
  

2.3 添加其它网段的路由

如果服务器端有其它内网段,需要继续添加客户端路由才能ping通,例如,客户端想与服务器端网段10.0.12.0/24互通,需在客户端执行:

ip route add  10.0.12.0/24 dev tun5

按需添加即可,更改路由是高风险操作,需要格外小心,同时也要确保ssh连接不要中断。

三、二层隧道配置

由于linux中允许为tap设备设置ip,所以二层隧道和三层隧道也可以完全一样的进行配置。区别是tap设备可以和物理网卡进行绑定,也就是桥接,同时由于工作在二层,允许Arp通过,能支持STP生成树、PPP等协议。

3.1 建立二层隧道

在客户端执行:

ssh  -o Tunnel=ethernet -w 6:6    root@[server_ip] 

这里参数 -o 需要在 -w 前面,查看一下客户端和服务器的网卡:

[root@centos8 ~]# ip addr
20: tap6: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000

在ip命令中 tap6: <BROADCAST,MULTICAST>,和物理网卡显示的一样,说明模拟的是物理网卡。

3.2 创建并配置网桥

二层可以进行网卡桥接,桥接可以多块网卡,也可以一块网卡,这里测试为了省事只用一块网卡,创建网桥有三种工具:

  1. 使用bridge-utils中的brctl命令
  2. 使用oracle linux 8 系列自带的nmcli
  3. 使用ip命令

建议用ip命令建立:

ip link add br0 type bridge

将tap6加入网桥:

ip link set tap6 master br0

配置服务器端网桥IP信息并启动网桥:

ip address add 10.0.0.1/32 dev br0
ip link set tap6 up
ip link set br0 up

客户端流程一样,只是客户端的ip是10.0.0.2

在客户端测试ARP包能不能通过:

[root@oracle8 ~]# arping -I br0 10.0.0.1
ARPING 10.0.0.1 from 10.0.0.2 br0
Unicast reply from 10.0.0.1 [3A:E7:2F:3F:42:24]  193.222ms
Unicast reply from 10.0.0.1 [3A:E7:2F:3F:42:24]  191.903ms

如果是三层隧道,就无法使用arping。

看似很多内容,其实使用很简单,OpenSSH套件功能真强大。

参考文章
https://help.ubuntu.com/community/SSH_VPN
https://www.cyberciti.biz/faq/centos-8-add-network-bridge-br0-with-nmcli-command/
https://gobomb.github.io/post/build-l2-l3-vpn-by-openssh/

通过curl命令分析http性能

通常HTTP的性能分析是通过浏览器的开发者工具进行查看,但这种方式只能通过图形页面进行查看。如果想做性能监控或者在命令行下分析,可以通过curl命令来统计各阶段的耗时。我们先来看看一个HTTP请求的过程:

Screen-Shot-2018-10-16-at-14.51.29-1.png
(图片来自cloudflare)

每次http请求经过这些过程: 客户端发起请求-->DNS解析-->TCP连接-->SSL等协议握手-->服务器处理-->内容传输-->完成

查看curl 命令的手册,curl命令支持以下阶段的时间统计:

  1. time_namelookup : 从请求开始到DNS解析完成的耗时
  2. time_connect : 从请求开始到TCP三次握手完成耗时
  3. time_appconnect : 从请求开始到TLS握手完成的耗时
  4. time_pretransfer : 从请求开始到向服务器发送第一个GET请求开始之前的耗时
  5. time_redirect : 重定向时间,包括到内容传输前的重定向的DNS解析、TCP连接、内容传输等时间
  6. time_starttransfer : 从请求开始到内容传输前的时间
  7. time_total : 从请求开始到完成的总耗时

我们常关注的HTTP性能指标有:

  1. DNS请求耗时 : 域名的NS及本地使用DNS的解析速度
  2. TCP建立耗时 : 服务器网络层面的速度
  3. SSL握手耗时 : 服务器处理HTTPS等协议的速度
  4. 服务器处理请求时间 : 服务器处理HTTP请求的速度
  5. TTFB : 服务器从接收请求到开始到收到第一个字节的耗时
  6. 服务器响应耗时 :服务器响应第一个字节到全部传输完成耗时
  7. 请求完成总耗时

注意: 如果想分析HTTP性能的瓶颈,不建议使用带有重定向的请求进行分析,重定向会导致建立多次TCP连接或多次HTTP请求,多次请求的数据混在一起,数据不够直观,因此 time_redirect 对实际分析意义不大。

其中的运算关系:

  1. DNS请求耗时 = time_namelookup
  2. TCP三次握手耗时 = time_connect - time_namelookup
  3. SSL握手耗时 = time_appconnect - time_connect
  4. 服务器处理请求耗时 = time_starttransfer - time_pretransfer
  5. TTFB耗时 = time_starttransfer - time_appconnect
  6. 服务器传输耗时 = time_total - time_starttransfer
  7. 总耗时 = time_total

用curl命令统计以上时间:

curl -w '\ntime_namelookup=%{time_namelookup}\ntime_connect=%{time_connect}\ntime_appconnect=%{time_appconnect}\ntime_redirect=%{time_redirect}\ntime_pretransfer=%{time_pretransfer}\ntime_starttransfer=%{time_starttransfer}\ntime_total=%{time_total}\n\n' -o /dev/null -s -L 'https://www.nixops.me/'

以上内容不够直观,curl -w参数支持模板,新建一个文件timing.txt,内容如下:

time_namelookup=%{time_namelookup}\n
time_connect=%{time_connect}\n
time_appconnect=%{time_appconnect}\n
time_redirect=%{time_redirect}\n
time_pretransfer=%{time_pretransfer}\n
time_starttransfer=%{time_starttransfer}\n
time_total=%{time_total}\n

使用模板在执行一次:

curl -w "@timing.txt" -o /dev/null -s -L 'https://www.nixops.me/'

将上述功能生成脚本stat.sh

#!/bin/bash
#author : will
#website: https://www.nixops.me

Default_URL=https://www.nixops.me
URL=${1:-$Default_URL}

Result=`curl -o /dev/null -s $URL \
        -w \
        'time_namelookup=%{time_namelookup}
time_connect=%{time_connect}
time_appconnect=%{time_appconnect}
time_redirect=%{time_redirect}
time_pretransfer=%{time_pretransfer}
time_starttransfer=%{time_starttransfer}
time_total=%{time_total}
'`


declare $Result

curl_timing(){
    printf "\e[92mcURL Timing: \e[0m\n"
    for i in $Result
    do  
            IFS='='
            printf "\e[96m%18s \e[0m: %10s \n" $i
    done
}
stat_timing(){

    Result_TCP=`printf "%.6f" $(echo $time_connect - $time_namelookup |bc -l)`
    Result_TLS=`printf "%.6f" $(echo $time_appconnect - $time_connect |bc -l)`
    Result_Server=`printf "%.6f" $(echo $time_starttransfer - $time_pretransfer |bc -l)`
    Result_TTFB=`printf "%.6f" $(echo $time_starttransfer - $time_appconnect |bc -l)`
    Result_Transfer=`printf "%.6f" $(echo $time_total - $time_starttransfer |bc -l)`

    printf "\n\e[92mResource Timing: \e[0m\n"
    printf "\e[96m%18s \e[0m: %.6f \n" "DNS Lookup" $time_namelookup
    printf "\e[96m%18s \e[0m: %.6f \n" "TCP Connection" $Result_TCP
    
    if  [ `echo "$time_appconnect == 0"|bc` -eq 0 ]
    then
        printf "\e[96m%18s \e[0m: %.6f \n" "TLS Handshake" $Result_TLS
    fi

    printf "\e[96m%18s \e[0m: %.6f \n" "Server Processing" $Result_Server
    printf "\e[96m%18s \e[0m: %.6f \n" "TTFB" $Result_TTFB
    printf "\e[96m%18s \e[0m: %.6f \n" "Content Transfer" $Result_Transfer
    printf "\e[96m%18s \e[0m: %.6f \n" "Finish" $time_total
}

curl_timing
stat_timing 

执行一下:

# ./stat.sh https://www.baidu.com

cURL Timing: 
   time_namelookup :   0.004087 
      time_connect :   0.006480 
   time_appconnect :   0.022001 
     time_redirect :   0.000000 
  time_pretransfer :   0.022026 
time_starttransfer :   0.025635 
        time_total :   0.025658 

Resource Timing: 
        DNS Lookup : 0.004087 
    TCP Connection : 0.002393 
     TLS Handshake : 0.015521 
 Server Processing : 0.003609 
              TTFB : 0.003634 
  Content Transfer : 0.000023 
            Finish : 0.025658 

从上述结果,就可以直观的分析出http各阶段的耗时,方便我们进行性能瓶颈。

参考文章:

https://cizixs.com/2017/04/11/use-curl-to-analyze-request/
https://blog.cloudflare.com/a-question-of-timing/
https://curl.se/docs/manpage.html
https://github.com/reorx/httpstat

openssl命令流水账

Openssl命令用法比较多,整理了一下平时常用的命令,做了个流水账,方便以后复杂粘贴。

一、生成公钥和私钥

openssl genrsa -out nixops.me.key 2048
openssl rsa -in nixops.me.key -pubout -out nixops.me.crt

一条命令生成:

openssl  req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout nixops.me.key -out nixops.me.crt

验证私钥和公钥:

openssl rsa -check -in nixops.me.key
openssl x509 -text -noout -in nixops.me.crt  

二、生成私钥和证书请求文件csr

openssl  genrsa -out nixops.me.key 2048
openssl req -new -sha256 -key nixops.me.key -out nixops.me.csr

查看csr 信息:

openssl req -noout -text -in nixops.me.csr

三、自建CA

生成CA私钥:

openssl genrsa -out CAKey.pem 2048

生成CA证书:

openssl req -x509 -sha256 -new -nodes -key CAKey.pem -days 3650 -out CACert.pem

也可以使用不用交互,直接提供相关信息:

openssl req -x509 -sha256 -new -nodes -key CAKey.pem -days 3650 -out CACert.pem  -subj "/C=CN/ST=Guangdong/L=Shenzhen/O=myorganization/OU=nixps LTD/CN=nixops.me"

查看CA证书详细信息:

openssl x509 -in CACert.pem -text

使用CA签发证书:

openssl x509 -req -CA CACert.pem -CAkey CAKey.pem  -CAcreateserial -in nixops.me.csr -out nixops.me.issue.crt -days 365 -sha256

查看签发者信息:

openssl x509  -noout -issuer -issuer_hash -in nixops.me.issue.crt

四、使用openssl加密解密文件

4.1 生成并验证私钥

openssl genrsa -out nixops.me.pem 2048
openssl rsa -in nixops.me.pem -text -noout

4.2 导出公钥并验证

openssl rsa -in nixops.pem -pubout -out pub.pem 
openssl rsa -in pub.pem -pubin -text -noout 

4.3 加解密小文件

使用这种方式1024位的私钥可以加密小于86字节的文件,2048位的私钥可以加密小于214字节的文件。
用公钥加密:

openssl rsautl -encrypt -inkey pub.pem -pubin -in file.txt -out file.bin 

用私钥解密:

openssl rsautl -decrypt -inkey nixops.pem -in file.bin 

4.4 加解密大文件

用公钥加密:

openssl  smime  -encrypt -aes256  -in  Large.zip  -binary  -outform DEM  -out  Encrypted.zip  pub.pem

用私钥解密:

openssl  smime -decrypt  -in  Encrypted.zip  -binary -inform DEM -inkey nixops.pem  -out  Large.zip 

五、证书格式转化

一般有以下几种标准格式:

  • .DER .CER : 二进制格式,只保存证书,不保存私钥。
  • .PEM :文本格式,可保存证书,可保存私钥,通常网上的.key后缀的私钥,其实就是PEM格式。
  • .CRT :可以是二进制格式,可以是文本格式,只保存证书,不保存私钥。
  • .PFX .P12 :即PKCS12,是二进制格式,同时包含证书和私钥,一般有密码保护。
  • .JKS :JAVA的专属二进制格式,同时包含证书和私钥,一般有密码保护。

5.1 DER/CER/CRT 转 PEM

先查看证书信息,在转格式:

openssl x509 -in cert.der -inform der -text -noout
openssl x509 -in cert.der -inform der -outform pem -out cert.pem

5.2 PEM转DER/CER/CRT

openssl x509 -in cert.pem -text -noout
openssl x509 -in cert.pem -outform der -out cert.der

5.3 PFX转PEM

openssl pkcs12 -info -nodes -in site.pfx
openssl pkcs12 -in site.pfx -out site.pem -nodes

5.4 JKS转PEM

需要JDK中提供的keytool工具配合openssl,先用keytool转成PKCS12格式:

keytool -importkeystore -srckeystore cert.jks -destkeystore cert.pkcs -srcstoretype JKS -deststoretype PKCS12

在用openssl转成pem格式:

openssl pkcs12 -in cert.pkcs -out cert.pem

六、其它一些技巧

6.1 移除证书中的密码

openssl rsa -in cert.key -out nopass.key

6.2 查看公钥的hash

openssl x509 -noout -hash -in cert.pem

6.3 查看在线网站的证书信息

openssl s_client -connect www.baidu.com:443 -showcerts

6.4 查看网站证书的有效期

查看本地证书:

openssl x509 -dates -noout -in file.pem  #查看证书签发时间和有效期
openssl x509 -startdate -noout -in file.pem #查看签发时间
openssl x509 -enddate -noout -in file.pem   #查看有效时间
openssl x509 -checkend 86400 -noout -in file.pem #检查证书是否在一天内过期,用echo $?即可判断

查看在线证书:

openssl s_client -connect www.baidu.com:443 -servername www.baidu.com 2> /dev/null |  openssl x509 -noout  -dates

提取过期时间:

openssl s_client  -connect www.baidu.com:443 -servername www.baidu.com 2>/dev/null |openssl x509 -enddate -noout |cut -d "=" -f 2

用date命令转换一下日期格式:

date --date="$(openssl s_client  -connect www.baidu.com:443 -servername www.baidu.com 2>/dev/null |openssl x509 -enddate -noout |cut -d "=" -f 2)" --iso-8601

6.5 检查网站是否接受指定版本的SSL协议

协议有TLS 1.0(tls1)、TLS 1.1(tls1_1) TLS 1.2(tls1_2),在高版本的openssl中默认已经禁用了SSL V2(ssl2)、SSL V3(ssl3)

openssl s_client  -connect www.baidu.com:443 -tls1

6.6 检查网站是否支持指定的加密算法

openssl s_client  -connect www.baidu.com:443 -tls1_2 -cipher 'ECDHE-RSA-AES128-GCM-SHA256'

参考文章:
https://jamielinux.com/docs/openssl-certificate-authority/index.html
https://stackoverflow.com/questions/21297853/how-to-determine-ssl-cert-expiration-date-from-a-pem-encoded-certificate
https://geekflare.com/openssl-commands-certificates/

CentOS 8系统迁移至CentOS Stream

2020年12月8日,CentOS官方发布公告称CentOS 8将于2021年12月31日结束支持,取而代之的是CentOS Stream。

以前centos作为RHEL的重新编译版,即下游发型版: Fedora -> RHEL -> CentOS
现在是和Fedora一样专注于RHEL的上游测试分支: Fedora -> CentOS Stream -> RHEL

CentOS Stream发布方式改为滚动更新,不像传统的操作系统有着明确的系统版本号,而是在升级过程中使用来自上游的最新软件包版本以及内核功能等。其定位是吸收Fedora中测试稳定的软件,制作成相对稳定的滚动发型版,即给RHEL做测试,又为RHEL培养客户。对于稳定性要求不高的场景,可以直接从CentOS 8迁移至CentOS Stream。

迁移之前,先备份好数据及重要的配置!!备份好数据及重要的配置!!备份好数据及重要的配置!!

一、更新CentOS 8系统至最新

dnf update 

重启后查看版本号:

cat /etc/redhat-release 
CentOS Linux release 8.3.2011

二、安装CentOS Stream 软件源

dnf install centos-release-stream

将当前的CentOS 8源替换成CentOS Stream源:

dnf swap centos-{linux,stream}-repos

三、迁移并替换系统软件及内核

distro-sync工具会比对本地软件和CentOS Steam源中的软件及内核,并进行必要的升级、降级或者保持不变,这一步中有差异的软件选择Y替换成CentOS Stream提供的版本:

dnf distro-sync

这一步骤会下载比较多的软件并安装,耗时比较久。下载安装完成后,重启系统并验证版本:

cat /etc/redhat-release 
CentOS Stream release 8

四、收尾验证

移除无用和冲突的软件包:

dnf  autoremove

CentOS默认会保留5个旧内核,移除旧版的内核和旧版的kernel-headers、kernel-devel等软件包,有三种方式:

  1. 更改/etc/yum.conf中的 installonly_limit=5,之后使用dnf autoremove移除
  2. 安装yum-utils,并使用命令:package-cleanup --oldkernels --count=1进行移除
  3. rpm -qa 找出旧版内核,手动删除

清除了旧内核之后,如果之前使用了内核模块的软件,如wireguard等,需重新配置。

检查并重新设置开启启动,如firewalld等软件。另外,由于很多系统内核、系统软件和库文件被替换或升级,有些配置可能被更改或者软件无法启动,需一一进行验证。

总体来说,过程比较简单,也比较容易迁移。但是迁移毕竟是高风险操作,容易导致软件无法启动及灵异问题,排查起来比较麻烦,有条件仍然建议重新安装。

红帽官方不建议将CentOS Stream用于生产环境中,随着CentOS Stream的发布,Red Hat宣布放宽RHEL系统的独立开发者订阅,允许最多免费使用16个RHEL系统。如果服务器比较多,可以考虑迁移到Oracle Linux、Rocky Linux、AlmaLinux等基于RHEL的下游系统。

体验Wireguard的简单之美

wireguard是Linus Torvalds为数不多夸过的技术,称赞它是:it's a work of art。

wireguard优点和缺点官网有很好的介绍,我简单总结一下,优点:

  1. 快速 速度是比传统的IPsec和OpenVPN等快的多,已经合并到高版本的内核,性能更高
  2. 现代 有良好的设计及考量
  3. 简单 源代码只有三四千行,同时管理和配置也非常简单
  4. 安全 使用了最先进的加密技术,并且经过多位密码学家的审查,而且由于简单,攻击面更小
  5. 通用 跨平台,支持各种操作系统

缺点:

  1. Wireguard专注实现简单可靠的加密,不关注流量混淆,容易被DPI检测到,同时由于特征明显,流量有可能被中继或在握手阶段阻断
  2. 专注性能,只能使用UDP,不支持TCP模式,当然可以借助udptunnel和udp2raw等软件实现
  3. 使用最先进的算法,没有专有硬件加速支持,有可能导致CPU使用率高或DoS,这点现代的cpu基本无需担心
  4. 非量子安全

总结一下,wireguard配置简单且安全高效,适合服务器之间互联。由于国内udp限速及特征明显,不适合个人当梯子用。

一、服务器端安装wireguard

如果系统是BSD之类的系统安装在用户空间,直接安装即可,如果是linux系统强烈建议使用最新的发行版,并更新内核到最新。

1.1 更新内核并重启

Centos系列:

yum update  -y  
reboot

1.2 安装epel源及ELRepo源:

centos 7系统:

sudo yum install epel-release https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm
sudo yum install yum-plugin-elrepo

centos 8系统:

dnf install elrepo-release epel-release -y

安装wireguard:

sudo yum install kmod-wireguard wireguard-tools -y

二、配置服务器端

2.1 创建wireguard配置目录并生成公私钥:

mkdir -p /etc/wireguard/
wg genkey | sudo tee /etc/wireguard/server_private.key | wg pubkey | sudo tee /etc/wireguard/server_public.key

2.2 编辑wireguard的配置文件

vim /etc/wireguard/wg0.conf

写入以下内容:

[Interface]
Address = 10.10.0.1/24
ListenPort = 51820
PrivateKey = 生成的server_private.key内容

[Peer]
PublicKey = 客户端的client public.key内容
AllowedIPs = 10.10.0.2/32

客户端的公钥需在客户端生成,并替换这里的内容,已可以在执行一下2.1节中的命令,生成另外一对公私钥供客户端使用。

设置权限,只允许root读取wireguard内容:

 chmod 600 /etc/wireguard/ -R

2.3配置ip forwarding及防火墙

2.3.1 配置ip forwarding:

vim /etc/sysctl.conf

net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1

使以上配置生效:

sysctl -p

2.3.2 配置地址伪装并放行端口

centos 7有firewalld和iptables两种防火墙,如果使用firewalld作为防火墙:

firewall-cmd --zone=public --permanent --add-masquerade
firewall-cmd --permanent --add-port=51820/udp
systemctl reload firewalld

如果使用iptables作为防火墙:

iptables -A FORWARD -i wg0 -j ACCEPT
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE 
iptables -I INPUT 1 -p udp --dport 51820 -j ACCEPT

ip6tables -A FORWARD -i wg0 -j ACCEPT
ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
ip6tables -I INPUT 1 -p udp --dport 51820 -j ACCEPT

2.4启动服务器端

可以使用wg-quick或者systemctl进行管理,使用wg-quick命令:

启动wireguard:

wg-quick up /etc/wireguard/wg0.conf

关闭wireguard:

wg-quick down /etc/wireguard/wg0.conf
wg-quick down wg0

或者使用systemctl命令:

systemctl start wg-quick@wg0.service

查看启动日志:

journalctl -eu wg-quick@wg0.service

设置为开机启动:

systemctl enable wg-quick@wg0.service

启动成功后,使用ifconfig命令即可看到wg0的网卡,使用wg命令可以看到详细的信息。

三、客户端配置

wireguard在各平台都有客户端,下载地址: https://www.wireguard.com/install/

3.1 客户端配置

如果开客户端是图形界面,一般打开客户端就有生成公私钥对的选项;如果是客户端也是linux等服务器系统,可以按上面服务器端的生成方法,使用已有的公私钥对也可以。

客户端配置文件:

[Interface]
PrivateKey = 客户端自己的私钥
Address = 10.10.0.2/32
DNS = 1.1.1.1,8.8.8.8

[Peer]
PublicKey = 服务器端的公钥
#PresharedKey = XXX    # 可选
AllowedIPs = 0.0.0.0/0
Endpoint = 服务器公网地址:51820
PersistentKeepalive = 10

客户端有几个配置需注意:

  1. 这里需将客户端的公钥,添加到2.2节中的服务器端配置中,并重启服务器端
  2. AllowedIPs = 0.0.0.0/0 表示客户端所有流量都通过VPN服务器中转,即全局代理
  3. PersistentKeepalive = 10,每10秒发送keepalive心跳,如果没有此配置,客户端连接后,需在有流量活动的情况下才会激活连接
  4. DNS建议使用公共dns,如果服务器端有配置dns server,可以指定成服务器端vpn的IP
  5. PresharedKey 预共享密钥能增强安全性,每个peer均不相同,通过“wg genpsk”命令生成

启动客户端后,在服务器端使用wg命令查看是否有客户端信息,并测试客户端到服务器、服务器到客户端的连接情况。

多个客户端时,需在server端配置多个peer,每个peer指定独立的静态ip和各自的公钥。

四、一些技巧

wireguard支持PreUp、PostUp、PreDown、PostDown参数,可以在wireguard启动、关闭前后执行一些自定义脚本或命令,实现一些特殊的需求。

4.1 设置策略路由

wiregard提供Table参数进行设置路由表,Table默认值是auto,由wireguard自动设置路由,也可以按需创建路由表,并设置策略路由,客户端和服务器端都可以设置该参数。在 [interface] 下面添加以下的配置即可:

Table = 1234
PostUp = ip rule add ipproto tcp dport 25 table 1234
PreDown = ip rule delete ipproto tcp dport 25 table 1234

4.2 通过AllowedIPs进行ACL设置

AllowedIPs控制了哪些流量允许使用该vpn,未匹配的流量会直连。个人翻墙时会使用全局VPN,但服务器之间连接时,基本不会把wiregaurd当做全局VPN使用。设置这里就非常方便。

AllowedIPs = 0.0.0.0/0,::/0 表示所有ipv4及ipv6的流量都要走vpn,适合个人用户。
AllowedIPs = 10.10.0.0/24,192.168.1.0/24 表示只有目的IP是这两个网段时走wireguard,其余流量走服务器默认的路由。

4.3 VPN kill switch功能

VPN Kill Switch 持续监控客户端与VPN服务器的连接,连接意外断开时会阻止设备客户端访问网络。从而防止泄露客户端的隐私。很多商业VPN都提供该功能,实现起来很简单,通过PreDown、PostDown执行更改客户端的防火墙、路由表、或者网卡配置即可。

五、故障排查

5.1 查看wireguard日志

wireguard的客户端都会有连接的日志,查看wireguard服务器端日志只能通过查看内核日志的方方式,内核要支持Dynamic Debugging,正常情况下需编译内核开启CONFIG_DYNAMIC_DEBUG,有些发型版已经默认开启。可以执行以下命令进行查看:

mount | grep debug

debugfs on /sys/kernel/debug type debugfs (rw,relatime)

如果和上面的显示一样,就说明已开启。配置开启调试日志:

modprobe wireguard 
echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control

开启之后,可以通过以下两个命令查看日志:

 dmesg -wH
 journalctl -kf

由于wireguard对低版本内核支持不好,实际测试中centos 7系统日志很少,centos 8系统日志就比较详细,这也是为什么安装wireguard之前建议升级内核的原因。

5.2 其它遇到的故障

报错1: linux做为客户端时启动wireguard报错: RTNETLINK answers: Operation not supported

该报错需重启服务器或客户端

报错2: vpn服务器ping客户端时报错: ping: sendmsg: Required key not available

该错最有可能是服务器端的peer部分AllowedIPs配置不对,网上一些教程使用SaveConfig=true,该配置是在关闭或重启wireguard时才添加配置,导致如果客户端连接后如果服务器端未重启,就会出现该错误,不建议使用saveConfig=true。

报错3: 服务器端ping客户端时提示: ping: sendmsg: Destination address required
一般是服务器端更改配置后,客户端未重新连接,客户端重新连接即可

报错4: 启动wireguard时报错: Error: Unknown device type. Unable to access interface: Protocol not supported 或执行 modprobe wireguard 命令报错: modprobe: FATAL: Module wireguard not found in directory

这个错误通常是因为升级内核导致的,wireguard的内核模块安装在旧版内核上,新内核没有wireguard模块,解决方法是卸载旧版内核、旧版kernel-header、旧版kernel-devel等,重新安装wireguard即可。

报错5: yum或dnf无法安装kmod-wireguard

dnf install kmod-wireguard
Repository epel is listed more than once in the configuration
Last metadata expiration check: 1:02:21 ago on Thu 13 May 2021 02:45:45 PM HKT.
Error: 
 Problem: cannot install the best candidate for the job
  - nothing provides kernel(__skb_flow_dissect) = 0xd82bed9c needed by kmod-wireguard-3:1.0.20210424-1.el8_3.elrepo.x86_64
  - nothing provides kernel(flow_keys_basic_dissector) = 0xa7e38f12 needed by kmod-wireguard-3:1.0.20210424-1.el8_3.elrepo.x86_64
  - nothing provides kernel(ipv6_stub) = 0xf3965b90 needed by kmod-wireguard-3:1.0.20210424-1.el8_3.elrepo.x86_64
  - nothing provides kernel(totalram_pages) = 0xde9360ba needed by kmod-wireguard-3:1.0.20210424-1.el8_3.elrepo.x86_64
(try to add '--skip-broken' to skip uninstallable packages or '--nobest' to use not only best candidate packages)

此错误是因为centos stream系统默认没有wireguard导致,需执行以下命令启用COPR库中的wireguard:

dnf install epel-release
dnf copr enable jdoss/wireguard
dnf install wireguard-dkms wireguard-tools

综上,配置非常简单,功能可玩性高!

参考文章:
https://www.wireguard.com/
https://www.linuxbabe.com/centos/wireguard-vpn-server-centos

ssh启用两步验证的一些玩法

正常的服务器都是使用 用户名+密码或者用户名+ssh key的方式登录,配合防火墙及合理的ssh配置,能达到很好的安全性。但是无论密码还是ssh key都是固定不变的,一旦被渗透等就会导致登录信息泄露,这时如果启用了多因子认证,就能防止黑客通过ssh登录。多因子认证是在原有的认证基础上,在加一个或多个基于时间不断变化的验证码、或生物指纹、面部识别的认证,只要这个信息不泄漏,即使原认证信息泄露也无法登录。

最常见的多因子验证,就是谷歌的两步验证。类linux的系统中,很多软件都是使用pam模块做认证的,谷歌开源了google-authenticator-libpam工具,理论上支持pam认证的软件都可以启用谷歌两步认证。下面就说一下ssh配置两步认证的过程和一些玩法。

一、编译

centos 的epel源中有现成的包,debian中也有,直接使用dnf就能安装,我这里使用编译的方式安装。

dnf install git autoconf automake  make  libtool gcc   

在命令行显示二维码用到 libqrencode ,

dnf install qrencode

两步认证用到了pam认证框架,需安装pam-devel

dnf install pam-devel

编译:

git clone https://github.com/google/google-authenticator-libpam.git
cd google-authenticator-libpam

./bootstrap.sh
./configure

make
sudo make install

主要用到以下两个文件

  1. 执行程序: /usr/local/bin/google-authenticator
  2. pam库文件:/usr/local/lib/security/pam_google_authenticator.so

二、设置时间同步

TOTP是基于时间的,在初始化两步验证之前,先对一下时间,如果有ntpdate命令,执行:

ntpdate time.cloudflare.com

把这这个命令加到定时任务中,一定要确保服务器时间准确。

如果系统是redhat8,系统中ntp已经被chrony替代,执行以下命令:

dnf install -y chrony

systemctl enable chronyd

systemctl start chronyd

查看时间同步时间源服务器:

chronyc sources 

查看同步状态:

chronyc tracking 

三、初始化两步验证

准备工作做完后,就可以初始化两步验证,首先需要手机或个人电脑中安装两步验证的客户端,例如:谷歌验证器、authy等。

3.1 手动初始化

在服务器上执行:

google-authenticator

google-authenticator 初始化步骤:

  第1个:是否想启用基于时间的令牌,选择y

  第2个:是否更新认证文件(root用户是/root/.google_authenticator),由于第一次设置文件不存在,选择y 

  第3个:是否禁止多个用户公用认证口令,有可能会产生中间人欺骗,即令牌用过一次即失效,禁止选择y

  第4个:默认1个口令的有效期是30s,为了防止主机时间和口令客户端时间不一致,最多允许客户端和服务器误差4分钟,服务器上已经设置时间同步,选择n

  第5个:是否启用防暴力破解,30s内不得超过3次登陆测试,选择y

这里需要用手机客户端扫描生成的二维码,初始化完成后会生成两步验证的配置文件,root用户在/root/.google_authenticator,普通用户在/home/user/.google_authenticator,文件的属主要和用户一致,不然会导致无法登录。

3.2 使用参数方式初始化

和手动初始化一样,只是使用参数的方式,不需要交互,方便进行自动化和脚本调用

google-authenticator -t -d -r 3 -R 30 -w 3 -s /root/.google_authenticator
-t: 基于时间的令牌
-d: 不允许令牌重用
-r 3: 限制错误次数为3次
-R 30: 限制验证时间窗口为 30s
-w 3:  验证窗口为3个,当前验证令牌的前一个和后一个均是生效的,也就是允许误差
-s : 保存位置,默认为~/.google_authenticator

四、配置ssh

配置sshd_config,编辑 /etc/ssh/sshd_config,添加:

ChallengeResponseAuthentication yes
UsePAM yes

重启sshd

systemctl restart sshd

五、配置pam

5.1 登录时先输入两步验证码,在输入ssh密码的配置方法

编辑 /etc/pam.d/sshd,在第一行添加:

auth  required  /usr/local/lib/security/pam_google_authenticator.so  [authtok_prompt='请先输入两步验证码:' ]  secret=/root/.google_authenticator  no_increment_hotp

no_increment_hotp :不统计失败的两步认证,防止有被暴力破解时锁定用户。

配置好后,可以用ssh或者xshell客户端登录一下,登录过程:

ssh root@172.16.29.100                                                 
'请先输入两步验证码:'
Password:
Last login: Wed Jan 20 17:09:39 2021 from 172.16.29.1
[root@nixops.me ~]#

5.2 先输入ssh密码,在输入两步验证码

编辑 /etc/pam.d/sshd,在最后一行添加:

   auth  required  /usr/local/lib/security/pam_google_authenticator.so nullok

通过以上的配置,ssh密码 + 谷歌两步验证的登录方式就配置完成了。

六、一些玩法

因为使用了pam框架,有一些基于pam的程序,都可以试着启用两步验证

6.1 普通用户使用su命令时,需要两步验证码

编辑/etc/pam.d/su,找到开头这里:

#%PAM-1.0
auth            required        pam_env.so
auth            sufficient      pam_rootok.so

在 pam_rootok.so这一行下面,加入

    auth required /usr/local/lib/security/pam_google_authenticator.so [authtok_prompt='请先输入两步验证码:' ] secret=/root/.google_authenticator no_increment_hotp

加入后,root切换至普通用户不需要两步验证,普通用户切换至root,或者普通用户之间切换需要先进行两步验证

6.2 sudo命令需要两步验证码

sudo 实际上是以root用户执行命令,在pam的规则中指定一下user即可,编辑/etc/pam.d/sudo,在第一行加入

    auth requisite /usr/local/lib/security/pam_google_authenticator.so [authtok_prompt='请先输入两步验证码:' ] secret=/root/.google_authenticator user=root

6.3 特定IP或者网段不进行两步验证

编辑/etc/pam.d/sshd,在顶部添加:

    auth [success=1 default=ignore] pam_access.so accessfile=/etc/totp_access.conf
    
    auth       required /usr/local/lib/security/pam_google_authenticator.so [authtok_prompt='请先输入两步验证码:' ] secret=/root/.google_authenticator  no_increment_hotp

规则文件/etc/totp_access.conf的内容为:

    + : ALL : 10.20.10.100
    + : ALL : 172.16.19.0/24
    + : ALL : LOCAL
    - : ALL : ALL

6.4 ssh三重认证

安全不在于登录过程中多几道认证,但是对于某些要求较高或者特殊的场景下,可能会有需要多重认证的情况,三重认证包括以下认证方式:

  1. publickey (SSH key)
  2. password publickey (SSH key 的密码)
  3. keyboard-interactive (两步验证码)

和ssh密码认证的方式类似,只是把密码认证改成了ssh publickey + publickey password而已,配置思路也是一样,只是需要sshd_config中多加两条配置,用来指定登录使用的认证方式:

PubkeyAuthentication yes
AuthenticationMethods publickey,password publickey,keyboard-interactive

6.5 多用户启用两步认证

如果服务器中不只有一个用户,pam配置中可以使用变量${USER}来配置多用户,不需要为每个用户写一条pam配置,例如:

auth       required     pam_google_authenticator.so secret=/home/${USER}/.ssh/google_authenticator nullok

如果想做自动化,可以定制一个.bash_profile文件,将这个.bash_profile文件放到/etc/skel/中,这样每次创建用户后就会复制到用户目录下,当用户第一次登录后,调用其中的脚本生成/home/${USER}/.google_authenticator,从而自动绑定两步认证,这里只提供思路,不做具体实现。

参考文章:
https://www.redhat.com/sysadmin/mfa-linux
https://wiki.archlinux.org/index.php/Google_Authenticator
https://www.digitalocean.com/community/tutorials/how-to-set-up-multi-factor-authentication-for-ssh-on-centos-8

手把手教你用ffmpeg制作表情包

在油管上看linux大神在芬兰阿尔托大学演讲视频时,突然发现了一个名场面,有观众说英伟达对Linux系统支持不够好,大神一通说明后来了句:Nvidia,FXXK YOU! 原视频在下面,需要梯子,时间是49:56。

这么出名的场面,就想做成表情包玩玩,于是就想到了曾经把我折磨的死去活来的ffmpeg。

1. 下载视频

首先需要使用youtube-dl下载原视频,先列出视频可下载的格式:

youtube-dl -F 'https://www.youtube.com/watch?v=MShbP3OpASA'   
                                                                                                                                                                                                   
[youtube] MShbP3OpASA: Downloading webpage
[info] Available formats for MShbP3OpASA:
format code  extension  resolution note
249          webm       audio only tiny   55k , opus @ 50k (48000Hz), 21.72MiB
250          webm       audio only tiny   69k , opus @ 70k (48000Hz), 25.71MiB
140          m4a        audio only tiny  131k , m4a_dash container, mp4a.40.2@128k (44100Hz), 
58.10MiB
251          webm       audio only tiny  135k , opus @160k (48000Hz), 46.92MiB
160          mp4        256x144    144p  109k , avc1.4d400c, 25fps, video only, 17.00MiB
278          webm       256x144    144p  114k , webm container, vp9, 25fps, video only, 
39.68MiB
242          webm       426x240    240p  225k , vp9, 25fps, video only, 44.87MiB
133          mp4        426x240    240p  244k , avc1.4d4015, 25fps, video only, 41.77MiB
243          webm       640x360    360p  415k , vp9, 25fps, video only, 100.11MiB
134          mp4        640x360    360p  583k , avc1.4d401e, 25fps, video only, 86.82MiB
244          webm       854x480    480p  758k , vp9, 25fps, video only, 184.20MiB
135          mp4        854x480    480p 1199k , avc1.4d401e, 25fps, video only, 138.93MiB
247          webm       1280x720   720p 1506k , vp9, 25fps, video only, 251.06MiB
136          mp4        1280x720   720p 2117k , avc1.4d401f, 25fps, video only, 220.55MiB
248          webm       1920x1080  1080p 2633k , vp9, 25fps, video only, 381.07MiB
137          mp4        1920x1080  1080p 3637k , avc1.640028, 25fps, video only, 318.59MiB
18           mp4        640x360    360p  389k , avc1.42001E, 25fps, mp4a.40.2@ 96k (44100Hz), 178.03MiB
22           mp4        1280x720   720p  608k , avc1.64001F, 25fps, mp4a.40.2@192k (44100Hz) (best) 

我们做gif表情包,不需要声音,下载一个高质量的Viedo only视频即可,下载指定视频:

​    youtube-dl -f 137 -o linux.mp4 'https://www.youtube.com/watch?v=MShbP3OpASA'

2. 裁剪视频

下载好之后,找到要裁剪的时间段:49:58,裁剪时长3秒,进行裁剪:

​     ffmpeg -i linux.mp4 -ss 00:49:58.00  -t 00:00:03.00    linux_3s.mp4

3. 添加文字

ffmpeg中可以用drawtext filter为视频添加水印或添加文字,需要编译ffmpeg添加支持:

  1. 使用drawtext,编译时需要加上--enable-libfreetype
  2. 要使用多种字体,需要加上--enable-libfontconfig
  3. 需要字体变形,需要加上--enable-libfribidi

查看ffpmeg编译参数:

ffmpeg -version                                                         

ffmpeg version 4.3.1 Copyright (c) 2000-2020 the FFmpeg developers
built with Apple clang version 12.0.0 (clang-1200.0.32.28)
configuration: --prefix=/usr/local/Cellar/ffmpeg/4.3.1_9 --enable-shared --enable-pthreads --enable-version3 --enable-avresample --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libbluray --enable-libdav1d --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-librtmp --enable-libspeex --enable-libsoxr --enable-videotoolbox --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack
libavutil      56. 51.100 / 56. 51.100
libavcodec     58. 91.100 / 58. 91.100
libavformat    58. 45.100 / 58. 45.100
libavdevice    58. 10.100 / 58. 10.100
libavfilter     7. 85.100 /  7. 85.100
libavresample   4.  0.  0 /  4.  0.  0
libswscale      5.  7.100 /  5.  7.100
libswresample   3.  7.100 /  3.  7.100
libpostproc    55.  7.100 / 55.  7.100

参数没有问题,就可以使用drawtext filter添加文字了:

    ffmpeg -i linux_3s.mp4 -vf "drawtext=fontcolor=yellow:fontsize=100:fontfile=msyh.ttf:text='英伟达不讲武德':x=(w-text_w)/2:y=h-th-20"  -y out_3s.mp4

设置文字位置:

  • 顶端左侧: x=0:y=0 (边缘留白10像素: x=10:y=10)
  • 顶端居中: x=(w-text_w)/2:y=0 (边缘留白10像素: x=(w-text_w)/2:y=10)
  • 顶端右侧: x=w-tw:y=0 (边缘留白10像素: x=w-tw-10:y=10)
  • 画面中心: x=(w-text_w)/2:y=(h-text_h)/2
  • 底端左侧: x=0:y=h-th (边缘留白10像素: x=10:y=h-th-10)
  • 底端居中: x=(w-text_w)/2:y=h-th (边缘留白10像素: x=(w-text_w)/2:y=h-th-10)
  • 底端右侧: x=w-tw:y=h-th (边缘留白10像素: x=w-tw-10:y=h-th-10)

如果有多句话,可以指定时间段进行添加:

ffmpeg -i linux_3s.mp4 -vf "drawtext=enable='between(t,1,2.5)':fontcolor=yellow:fontsize=100:fontfile=msyh.ttf:text='英伟达不讲武德':x=(w-text_w)/2:y=h-th-20"  -y out_3s.mp4

enable='between(t,1,2.5)' 指定的是在1s-2.5s之间添加文字。

4. 缩放视频尺寸

裁剪好后的源视频是1920x1080 1080p格式的,做成gif是全屏的比较大,需要先进行缩放:

    ffmpeg -i out_3s.mp4 -vf scale=360:-2  small_3s.mp4

注意 sacle 值必须是偶数,这里的 -2 表示要求压缩出来的视频尺寸长宽都保持为偶数.如果需要保持长宽比,根据宽度值自适应高度,可以使用 -1

我这里只进行缩小就可以了,你也可能有加速播放和慢速播放的需要,也可以用实现。

4.1 加倍速播放视频

    ffmpeg -i out_3s.mp4 -filter:v "setpts=0.5*PTS" out_speedup.mp4

4.2 定义帧率 16fps

    ffmpeg -i out_3s.mp4 -r 16 -filter:v "setpts=0.125*PTS" -an out_16fps.mp4

4.3 慢倍速播放视频

    ffmpeg -i out_3s.mp4 -filter:v "setpts=2.0*PTS" out_slowdown.mp4

5.将视频 MP4 转化为 GIF

经过以上步骤,已经有了我们处理完成的视频文件,只需要转换成gif即可:

    ffmpeg -i small_3s.mp4 small.gif

转化成gif默认转换是中等质量的,压缩的比较厉害,可以修改比特率提高gif质量:

    ffmpeg -i small_3s.mp4 -b 2048k small.gif

统过以上步骤就完成了我们要制作的gif,ffmpeg的生态比较丰富,目前很多在线制作表情包的网站和一些Telegram上的bot,就是通过ffmpeg进行制作。也可以找其它的视频进行制作,比如,张学友:食屎啦你、有钱真的可以为所欲为、王镜泽: 真香 等等。

附上最终做好的gif,和裁剪好的3秒源视频:

small.gif
裁剪后3秒源视频

Linux的OOM Killer机制研究

简单来说,linux的内存分为虚拟内存和物理内存,进程在申请内存时,首先申请的是虚拟内存,等到真正需要的时候才去申请物理内存。内核通过这种机制避免内存的浪费,实现了按需分配物理内存。

所以虚拟内存可以大于实际的物理内存,超过这部分就是memory overcommit,而一旦进程需要申请的物理内存超过实际内存和交换空间的总和,内核就会用OOM Killer,选择杀掉一个或部分进程,保障系统和其它进程有可用的内存。

1. overcommit 设置

在 Linux 中,可以通过内核参数vm.overcommit_memory去控制是否允许 overcommit:

  • 默认值是 0,在这种情况下,只允许轻微的 overcommit,但一次申请大量内存会被禁止,root能使用的overcommit也比普通用户多
  • 设置为 1,表示总是允许 overcommit
  • 设置为 2,则表示总是禁止 overcommit,系统分配的内存不能超过系统内存+交换内存

2. OOM Killer 设置

linux内核会为每个进程算一个分数,发生OOM时分数最高的进程会被kill掉,主要看以下几个参数:

  • /proc/PID/oom_score ,OOM 最终得分,值越大越有可能被杀掉
  • /proc/PID/oom_score_adj ,取值范围为-1000到1000,计算oom_score时会加上该参数
  • /proc/PID/oom_adj ,取值是-17到+15,该参数主要是为兼容旧版内核

只要允许overcommit,就会有oom killer发生,所以关闭overcommit就可以关闭OOM Killer:

sysctl -w vm.overcommit_memory=2
echo "vm.overcommit_memory=2" >> /etc/sysctl.conf

如果不想关闭OOM,也可以直接调整相应进程的oom_score_adj和oom_adj值,来按需进行设置OOM的优先级:

echo -1000 > /proc/PID/oom_score_adj 

或者:

echo -17 > /proc/PID/oom_adj 

3. 找出最可能产生OOM的进程

通过脚本计算一下所有进程的oom_score和oom_score_adj,就可以找出最可能产生OOM的进程,系统进程的OOM score是0,需要排除,脚本是网上找的,内容如下:

#!/bin/bash

printf 'PID\tOOM_Score\tOOM_Adj\tCommand\n'

while read -r pid comm; 
do
     [ -f /proc/$pid/oom_score ] && [ $(cat /proc/$pid/oom_score) != 0 ] && printf '%d\t%d\t\t%d\t%s\n' "$pid" "$(cat /proc/$pid/oom_score)" "$(cat /proc/$pid/oom_score_adj)" "$comm"; 
done < <(ps -e -o pid= -o comm=) | sort -k 2nr

4. 手动触发OOM killer

学习的时候需要测试OOM,可以用以下方法手动触发OOM

  1. 前台执行一个循环,不停的为变量赋值,最终就会耗光内存
    for b in {0..99999999}; do a=$b$a; done
  1. 多次循环触发OOM-killer
    for x in {1..10}; do echo "Start trigger OOM-killer: $x"; bash -c "for b in {0..99999999}; do a=$b$a; done"; done
  1. 基本上面的方式,边触发OOM-killer,边监控OOM score
    #!/bin/bash

    oom(){
        echo "Start trigger OOM-killer" && bash -c "for b in {0..99999999}; do a=$b$a; done"
    }

    oom &

    ppid=`ps -ef |grep -v grep |grep  99999999 |awk '{print $3}'`
    pid=`pgrep -P $ppid`

    echo ppid: $ppid
    echo fork pid: $pid

    while [ -d "/proc/$pid" ];
    do
        echo $pid :  oom_score: `cat /proc/$pid/oom_score`
    done

5. OOM监控

OOM发生时,会在/var/log/message中出现如下日志:

Nov 26 10:51:25 nixops.me kernel: Out of memory: Kill process 18903 (bash) score 645 or sacrifice child
Nov 26 10:51:25 nixops.me kernel: Killed process 18903, UID 0, (bash) total-vm:3640712kB, anon-rss:3441992kB, file-rss:4kB

zabbix之类的监控软件可以通过关键字过滤,来进行监控。如果有ELK这类的日志系统,可以收集messages日志,用logstash插件进行告警。

参考文章:
https://askubuntu.com/questions/1188024/how-to-test-oom-killer-from-command-line/1188169#1188169
https://learning-kernel.readthedocs.io/en/latest/mem-management.html

最新文章

最近回复

分类

归档

统计

  • 文章总数:168篇
  • 分类总数:5个
  • 评论总数:103条
  • 页面总数:172个
  • 本站运行:4870天

其它