Keep learning, keep living...

0%

最近CentOS7服务器上遇到一个系统崩溃:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
[92957.332974] BUG: unable to handle kernel NULL pointer dereference at 0000000000000a30
[92957.332981] IP: [<ffffffffc086077d>] nf_ct_deliver_cached_events+0x2d/0x110 [nf_conntrack]

......

[92957.333112] CPU: 1 PID: 5027 Comm: Verdict3 Kdump: loaded Tainted: G OE ------------ T 3.10.0-1160.el7.x86_64 #1
[92957.333114] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 12/12/2018
[92957.333116] task: ffff893cad135280 ti: ffff893c4f524000 task.ti: ffff893c4f524000
[92957.333118] RIP: 0010:[<ffffffffc086077d>] [<ffffffffc086077d>] nf_ct_deliver_cached_events+0x2d/0x110 [nf_conntrack]
[92957.333125] RSP: 0018:ffff893c4f527810 EFLAGS: 00010246
[92957.333127] RAX: 0000000000000000 RBX: ffff893ca41e5140 RCX: ffffffffa035de80
[92957.333128] RDX: ffff893c4f527fd8 RSI: 0000000000000200 RDI: ffff893ca41e5140
[92957.333130] RBP: ffff893c4f527850 R08: 0000000000000000 R09: ffff893ca41e5178
[92957.333131] R10: 0000000000000001 R11: ffff893ca41e5140 R12: ffff893ca41e5140
[92957.333132] R13: ffff893c9491a500 R14: ffffffffa0367e40 R15: 0000000000000000
[92957.333135] FS: 00007fbda1d78700(0000) GS:ffff893d3fd00000(0000) knlGS:0000000000000000
[92957.333137] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[92957.333138] CR2: 0000000000000a30 CR3: 000000007fda4000 CR4: 00000000001607e0
[92957.333202] Call Trace:
[92957.333221] [<ffffffffc08372ce>] ipv4_confirm+0x4e/0x100 [nf_conntrack_ipv4]
[92957.333227] [<ffffffff9fc94e38>] nf_iterate+0x98/0xe0
[92957.333231] [<ffffffff9fc96240>] nf_reinject+0x160/0x1b0
[92957.333239] [<ffffffffc0a48566>] nfqnl_recv_verdict+0x216/0x310 [nfnetlink_queue]
[92957.333244] [<ffffffff9f9b4716>] ? nla_parse+0xb6/0x120
[92957.333247] [<ffffffffc09b03a2>] nfnetlink_rcv_msg+0x162/0x270 [nfnetlink]
[92957.333251] [<ffffffffc09b0240>] ? nfnetlink_net_exit_batch+0x70/0x70 [nfnetlink]
[92957.333254] [<ffffffff9fc9249b>] netlink_rcv_skb+0xab/0xc0
[92957.333257] [<ffffffffc09b089f>] nfnetlink_rcv+0x28f/0x575 [nfnetlink]
[92957.333260] [<ffffffff9fc90773>] ? __netlink_lookup+0xd3/0x130
[92957.333263] [<ffffffff9fc91e20>] netlink_unicast+0x170/0x210
[92957.333267] [<ffffffff9f99ca72>] ? memcpy_fromiovec+0x62/0xb0
[92957.333269] [<ffffffff9fc921c8>] netlink_sendmsg+0x308/0x420
[92957.333272] [<ffffffff9fc343a6>] sock_sendmsg+0xb6/0xf0
[92957.333277] [<ffffffff9fd86c8f>] ? __schedule+0x3af/0x860
[92957.333279] [<ffffffff9fc35269>] ___sys_sendmsg+0x3e9/0x400
[92957.333284] [<ffffffff9f712040>] ? futex_wake+0x90/0x180
[92957.333287] [<ffffffff9f714d2a>] ? do_futex+0x12a/0x5a0
[92957.333289] [<ffffffff9fc36921>] __sys_sendmsg+0x51/0x90
[92957.333292] [<ffffffff9fc36972>] SyS_sendmsg+0x12/0x20
[92957.333296] [<ffffffff9fd93f92>] system_call_fastpath+0x25/0x2a
阅读全文 »

SSH不仅可以使用远程管理主机,还可以利用SSH客户端和SSH服务器之间的加密连接为其他服务实际中继服务,这一般称为SSH隧道(SSH Tunneling)或者SSH端口转发

SSH端口转发分为三种类型:

  • 动态端口转发
  • 本地端口转发
  • 远程端口转发

无论是哪种端口转发,整个过程都涉及四个角色:

  • 业务客户端
  • SSH客户端
  • SSH服务器
  • 业务服务端

这些角色进程并不要求都位于独立主机上,它们可以位于同一台主机上,使用localhost或者127.0.0.1进行网络访问。

本文基于这4种角色来简要说明这三种端口转发的过程。三种端口转发都依赖SSH客户端SSH服务器建立一个加密连接在二者之间转发数据。

阅读全文 »

近期我们的BASH脚本中遇到一个BUG,最终分析到是变量值为空时没有用""包裹导致的。

我们使用一个测试脚本来进行分析。测试脚本t.sh内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash

kof=""

if [ ! -f $kof ]; then
echo "1"
fi

if [ ! -f "$kof" ]; then
echo "2"
fi

echo "END"

测试脚本的执行结果如下:

1
2
3
[root@default ~]# bash t.sh
2
END

可以看到当变量$kof为空时,第一个if语句块中的语句没有被执行,而第二个if语句块中的语句被执行了。这两个语句块的差别就在于第二个语句块中的$kof变量用""进行了包裹。

阅读全文 »

近期遇到一个服务器上内核模块无法卸载的问题。执行rmmod命令返回错误信息:

1
Device or resource busy

通过Google搜索,发现其他人也遇到过类似的问题,原因基本指向是编译内核模块的gcc版本和编译内核的gcc版本不一致。

于是开始检查我们的环境。

阅读全文 »

seccomp代表secure computing,是早在2.6.12版本就引入到内核的特性,用来限制进程可以使用的系统调用。它作用于进程里的线程(task)。

最初,seccomp只允许使用read, write, _exit, sigreturn4个系统调用,调用其他系统调用时,内核会发送SIGKILL信号终止进程。当时seccomp的提出主要是想用于出租空闲的CPU算力。这种模式叫做STRICT模式。它限制过于严格,在实际应用上并没有太多发展。Linus Torvald甚至建议把它从内核中砍掉。

阅读全文 »

OTPOne Time Password的缩写,是进一步加强身份认证的安全性的校验方法, 一般配合常规密码一起使用。OTP动态生成,只使用一次。手机验证码就是典型的OTP方式。用户请求一个短信验证码, 服务器随机生成一个验证码,然后临时存储起来,通过短信发送给用户。用户在系统上输入验证码发送回服务器,服务器根据临时存储的验证码进行校验。

生成OTP业内有许多标准算法,比如SHA-1。所有这些算法都包括两部分输入:

  • 种子值(seed):静态值,和帐号关联的密钥
  • 可变因子(moving factor):每次生成OTP都变化的部分
阅读全文 »

之前的文章<<使用eBPF和BCC调查创建文件的进程>>介绍了基于BCC来实现eBPF程序。BCC实现了对eBPF的封装,用户态部分提供Python API, 内核态部分使用的eBPF程序还是通过C语言来实现。运行时BCC会把eBPFC程序编译成字节码、加载到内核执行,最后再通过用户空间的前端程序获取执行状态。
可以在BCCBPF调用中,指定参数debug=4, 我们可以看到BCC的执行过程, 如:

1
2
3
4
5
6
7
from bcc import BPF
BPF(text="""
#include <linux/ptrace.h>
int kprobe__tty_write(struct pt_regs *ctx, struct file *file, const char __user *buf, size_t count) {
return 0;
}
""", debug=4)

eBPF编程的门槛还是比较高的,在当前还是快速发展的情况,API也还不稳定,对程序员的C语言、编译过程和内核等知识都有比较高的要求。BCC把这些都封装起来给用户提供了一个更为简单的使用框架。但本身也存在的一些问题,比如:

  • 每次执行时都需要重新编译
  • 执行程序的机器都需要安装内核头文件

eBPF: extended Berkeley Packet Filter是对BPF(现在称为cBPF: classic BPF)的扩展, 现在尽管还叫做BPF, 但应用场景已经远远超过了它的名称的范畴。它的应用范围的扩大主要得益于这几方面:

  • 内核中BPF字节码虚拟机扩展为一个通用的执行引擎
  • 执行可节码的安全校验
  • JIT支持,可以直接将字节码指令转成内核可执行的原生指令运行

这样在安全性、可编程性和性能方面的提升都使得eBPF在包过滤以外的其他领域获取巨大的应用空间。

阅读全文 »

在我们的一个CentOS 7.8系统上,/tmp目录下偶尔会有一些随机名称的文件产生。业务比较久远,已经不清楚这些文件的来源,需要确定是哪个进程在创建它们。

LinuxVFS: Virtual File System通用文件模型的方式在物理存储介质上的文件系统和用户接口之间建立一个虚拟文件系统的抽象层。其中最重要的两个结构是:

  • inode: 存放文件的一般信息,每个inode结构都有自己的编号,这个号码唯一标识了文件系统中的文件。
  • dentry: 存放文件名称信息,以及与对应文件进行链接的有关信息。

VFS创建文件的最主要的步骤是:

  1. 为要创建的文件创建一个dentry结构
  2. 为要创建的文件创建一个inode结构并分配inode编号
  3. inode编号和文件名映射关系保存在所分配的dentry结构中
  4. dentry写入到父目录的数据区
阅读全文 »

SOCKS是一个比较简单的通用代理协议,用于在客户端与服务器之间代理网络数据包。最新的版本是5, 所以一般叫做SOCKS5,协议规范是RFC1928。但SOCKS5并不兼容之前的SOCKS4SOCKS4ASOCKS5SOCKS4的基础上添加了UDP转发功能和校验机制。

SOCKS5的工作过程简单可以归纳为协商、请求、和转发三个阶段。以TCP代理场景来看, 一般流程是:

  1. 客户端建立TCP连接
  2. 客户端发送客户端侧支持的校验方法
  3. 服务端回应选择的校验方法
  4. 客户端和服务端之间按选择的校验方法完成校验
  5. 客户端发送所需的请求给服务端。SOCKS5支持不同的请求类型,包括CONNECT, BIND, UDPASSOCIATE等。
  6. 服务端接收到请求,从中解析出目的地址,建立到目的地址的连接。
  7. 发送成功信息给客户端。
  8. 客户端开始发送应用层信息。
  9. 服务端在客户端和目的地址之间转发应用层信息。

其中,2-4完成协商阶段,5-7完成请求阶段,之后进入转发阶段。

当前常用的校验方法是USERNAME/PASSWORD(RFC1929)和GSS-API(RFC1961),具体的校验过程是由不同校验方法来自定义的。

使用USERNAME/PASSWORD校验方法的TCP代理时序图如下:

阅读全文 »

我们的业务Docker镜像是在centos/systemd镜像基础上构建的,业务进程由systemd来启动。最近需要对业务逻辑进行改造,需要识别传入的环境变量。看上去是相当简单的改动,但在我们的进程中加入读取环境变量的逻辑却发现读取不到传入的变量内容,最终定位原因是在systemd的环境变量的处理。

阅读全文 »