我在工作中有五个 CentOS 6 Linux 系统,遇到了一个相当奇怪的问题,似乎只发生在我的用户ID在我拥有的所有 Linux 系统上...这是我从命令中排除的条目中出现问题的一个例子last
...
mpenning pts/19 Fri Nov 16 10:32 - 10:35 (00:03)
mpenning pts/17 Fri Nov 16 10:21 - 10:42 (00:21)
bill pts/15 sol-bill.local Fri Nov 16 10:19 - 10:36 (00:16)
mpenning pts/1 192.0.2.91 Fri Nov 16 10:17 - 10:49 (12+00:31)
kkim14 pts/14 192.0.2.225 Thu Nov 15 18:02 - 15:17 (4+21:15)
gduarte pts/10 192.0.2.135 Thu Nov 15 12:33 - 08:10 (11+19:36)
gduarte pts/9 192.0.2.135 Thu Nov 15 12:31 - 08:10 (11+19:38)
kkim14 pts/0 :0.0 Thu Nov 15 12:27 - 15:17 (5+02:49)
gduarte pts/6 192.0.2.135 Thu Nov 15 11:44 - 08:10 (11+20:25)
kkim14 pts/13 192.0.2.225 Thu Nov 15 09:56 - 15:17 (5+05:20)
kkim14 pts/12 192.0.2.225 Thu Nov 15 08:28 - 15:17 (5+06:49)
kkim14 pts/11 192.0.2.225 Thu Nov 15 08:26 - 15:17 (5+06:50)
dspencer pts/8 192.0.2.130 Wed Nov 14 18:24 still logged in
mpenning pts/18 alpha-console-1. Mon Nov 12 14:41 - 14:46 (00:04)
您可以看到上面的两个 pts 登录条目没有与之关联的源 IP 地址。我的 CentOS 机器有多达六个其他用户共享系统。大约 10% 的登录会出现此问题,但没有其他用户名表现出这种行为/var/log/secure
。没有源 IP 地址的条目不存在。
问题
考虑到我在这些系统上保留的脚本类型(它们控制着我们的大部分网络基础设施),我对此有点担心,并想了解是什么原因导致我的登录偶尔错过源地址。
- 为什么
last -i
显示0.0.0.0
pts 行条目(另见这个答案) - 除了恶意活动之外,还有什么可以合理解释这种行为吗?
- 除了 bash 历史时间戳之外,还有其他方法可以让我追踪该问题吗?
信息
自从发生这种情况后,我启用了bash
历史时间戳(即HISTTIMEFORMAT="%y-%m-%d %T "
).bash_profile
,并添加了其他一些 bash 历史 hack;然而,这并没有提供之前发生事件时发生了什么的线索。
所有系统均运行 CentOS 6.3...
[mpenning@typo ~]$ uname -a
Linux typo.local 2.6.32-279.9.1.el6.x86_64 #1 SMP Tue Sep 25 21:43:11 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
[mpenning@typo ~]$
编辑
如果我使用last -i mpenning
,我会看到像这样的条目......
mpenning pts/19 0.0.0.0 Fri Nov 16 10:32 - 10:35 (00:03)
mpenning pts/17 0.0.0.0 Fri Nov 16 10:21 - 10:42 (00:21)
对于那些试图回答的人来说:我没有使用命令screen
或 GUI登录。我的所有登录均来自 SSH;要获得赏金,您必须引用权威性参考资料来解释last -i
0.0.0.0
仅通过 SSH 获取的条目。
编辑 2 (针对 ewwhite 的问题)
/etc/resolv.conf
(请注意,我.local
在上面的输出中使用了地址last
来隐藏我公司的信息)
[mpenning@sasmars network]$ cat /etc/resolv.conf
nameserver 192.0.2.40
nameserver 192.0.2.60
domain mycompany.com
search mycompany.com
[mpenning@sasmars network]$
/etc/hosts
信息(请注意,此自定义 hosts 文件仅存在于存在这些问题的其中一台机器上)
[mpenning@sasmars network]$ cat /etc/hosts
127.0.0.1 localhost.localdomain localhost
192.0.2.44 sasmars.mycompany.com sasmars
::1 localhost6.localdomain6 localhost6
## Temporary kludge until I add reverse hostname mappings...
## Firewalls
192.0.2.254 a2-inet-fw1
192.0.2.253 a2-inet-fw2
192.0.2.254 a2-wan-fw1
192.0.2.253 a2-wan-fw2
192.0.2.201 a2-fab-fw1
192.0.2.202 a2-fab-fw2
192.0.2.203 t1-eds-fw1
192.0.2.42 sasvpn
192.0.2.246 sasasa1
192.0.2.10 sasoutfw1
## Wireless
192.0.2.6 saswcs1
192.0.2.2 l2wlc3
192.0.2.4 l2wlc4
192.0.2.12 f2wlc5
192.0.2.16 f2wlc6
192.0.2.14 f2wlc1
192.0.2.8 f2wlc2
[mpenning@sasmars network]$
sftp
输出自/var/log/secure
*
Dec 26 10:36:37 sasmars sshd[26016]: pam_sm_authenticate: called (pam_tacplus v1.3.7)
Dec 26 10:36:37 sasmars sshd[26016]: pam_sm_authenticate: user [mpenning] obtained
Dec 26 10:36:37 sasmars sshd[26016]: tacacs_get_password: called
Dec 26 10:36:37 sasmars sshd[26016]: tacacs_get_password: obtained password
Dec 26 10:36:37 sasmars sshd[26016]: pam_sm_authenticate: password obtained
Dec 26 10:36:37 sasmars sshd[26016]: pam_sm_authenticate: tty [ssh] obtained
Dec 26 10:36:37 sasmars sshd[26016]: pam_sm_authenticate: rhost [192.0.2.91] obtained
Dec 26 10:36:37 sasmars sshd[26016]: pam_sm_authenticate: trying srv 0
Dec 26 10:36:38 sasmars sshd[26016]: Accepted password for mpenning from 192.0.2.91 port 55118 ssh2
Dec 26 10:36:38 sasmars sshd[26016]: pam_sm_setcred: called (pam_tacplus v1.3.7)
Dec 26 10:36:38 sasmars sshd[26016]: pam_unix(sshd:session): session opened for user mpenning by (uid=0)
Dec 26 10:36:38 sasmars sshd[26018]: pam_sm_setcred: called (pam_tacplus v1.3.7)
Dec 26 10:36:38 sasmars sshd[26018]: subsystem request for sftp
Dec 26 10:37:20 sasmars sshd[26016]: pam_unix(sshd:session): session closed for user mpenning
Dec 26 10:37:20 sasmars sshd[26016]: pam_sm_setcred: called (pam_tacplus v1.3.7)
最终解决方案
答案1
script
RedHat 和 Debian 之间的行为差异
链接库
CentOS 6.3-脚本(util-linux-ng 2.17.2)
#ldd /usr/bin/script
linux-vdso.so.1 => (0x00007fff077ff000)
libutil.so.1 => /lib64/libutil.so.1 (0x00007f309f5d1000)
libutempter.so.0 => /usr/lib64/libutempter.so.0 (0x00007f309f3cf000)
libc.so.6 => /lib64/libc.so.6 (0x00007f309f03b000)
/lib64/ld-linux-x86-64.so.2 (0x00007f309f7e1000)
Ubuntu 12.04-脚本(util-linux 2.20.1)
#ldd /usr/bin/script
linux-vdso.so.1 => (0x00007fff375ff000)
libutil.so.1 => /lib/x86_64-linux-gnu/libutil.so.1 (0x00007fc0d7ab0000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc0d76f1000)
/lib64/ld-linux-x86-64.so.2 (0x00007fc0d7cdc000)
私人有限公司
基于上游源代码,script
两个版本都打开了新的pty。以下是测试。
Ubuntu 12.04
john@U64D211:~/tmp$ ls /dev/pts
0 1 5 8 ptmx
john@U64D211:~/tmp$ script
Script started, file is typescript
john@U64D211:~/tmp$ ls /dev/pts
0 1 2 5 8 ptmx
john@U64D211:~/tmp$ last -i
john pts/0 0.0.0.0 Sat Jan 5 09:09 still logged in
reboot system boot 0.0.0.0 Sat Jan 5 09:08 - 09:52 (00:44)
john pts/0 0.0.0.0 Thu Jan 3 00:50 - 01:42 (00:52)
reboot system boot 0.0.0.0 Thu Jan 3 00:48 - 01:43 (00:54)
wtmp begins Tue Jan 1 20:48:28 2013
john@U64D211:~/tmp$ exit
exit
Script done, file is typescript
john@U64D211:~/tmp$ ls /dev/pts
0 1 5 8 ptmx
john@U64D211:~/tmp$
Ubuntu 12.04script
确实打开了新的 pts(2)。它只是没有更新/var/log/wtmp
。
CentOS 6
我跳过了测试,因为我们已经知道script
打开 pty 并使用 wtmp 注册。
性欲旺盛
- 项目:http://freecode.com/projects/libutempter
- 描述:libutempter 为 screen 和 xterm 等终端仿真器提供了库接口,以将用户会话记录到 utmp 和 wtmp 文件中。
因此主要的区别似乎在于libutempter.so.0
CentOS 链接的额外库()script
。
使用 Ubuntu 12.04 进行测试
script
使用 libutempter 进行编译
john@U64D211:~/tmp/util-linux-2.20.1$ sudo apt-get install libutempter-dev
john@U64D211:~/tmp/util-linux-2.20.1$ ./configure --with-utempter
john@U64D211:~/tmp/util-linux-2.20.1$ make
john@U64D211:~/tmp/util-linux-2.20.1$ cd term-utils/
john@U64D211:~/tmp/util-linux-2.20.1/term-utils$ ldd ./script
linux-vdso.so.1 => (0x00007fff54dff000)
libutil.so.1 => /lib/x86_64-linux-gnu/libutil.so.1 (0x00007f289e635000)
libutempter.so.0 => /usr/lib/libutempter.so.0 (0x00007f289e432000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f289e072000)
/lib64/ld-linux-x86-64.so.2 (0x00007f289e861000)
测试
跑步前script
john@U64D211:~/tmp/util-linux-2.20.1/term-utils$ ls /dev/pts
0 1 5 8 ptmx
john@U64D211:~/tmp/util-linux-2.20.1/term-utils$ last -i
john pts/0 0.0.0.0 Sat Jan 5 09:09 still logged in
reboot system boot 0.0.0.0 Sat Jan 5 09:08 - 10:37 (01:28)
john pts/0 0.0.0.0 Thu Jan 3 00:50 - 01:42 (00:52)
reboot system boot 0.0.0.0 Thu Jan 3 00:48 - 01:43 (00:54)
wtmp begins Tue Jan 1 20:48:28 2013
之内script
john@U64D211:~/tmp/util-linux-2.20.1/term-utils$ ./script
Script started, file is typescript
john@U64D211:~/tmp/util-linux-2.20.1/term-utils$ ls /dev/pts
0 1 2 5 8 ptmx
john@U64D211:~/tmp/util-linux-2.20.1/term-utils$ last -i
john pts/2 0.0.0.0 Sat Jan 5 10:37 still logged in
john pts/0 0.0.0.0 Sat Jan 5 09:09 still logged in
reboot system boot 0.0.0.0 Sat Jan 5 09:08 - 10:37 (01:29)
john pts/0 0.0.0.0 Thu Jan 3 00:50 - 01:42 (00:52)
reboot system boot 0.0.0.0 Thu Jan 3 00:48 - 01:43 (00:54)
wtmp begins Tue Jan 1 20:48:28 2013
john@U64D211:~/tmp/util-linux-2.20.1/term-utils$ exit
exit
Script done, file is typescript
script
結束後
john@U64D211:~/tmp/util-linux-2.20.1/term-utils$ ls /dev/pts
0 1 5 8 ptmx
john@U64D211:~/tmp/util-linux-2.20.1/term-utils$ last -i
john pts/2 0.0.0.0 Sat Jan 5 10:37 - 10:37 (00:00)
john pts/0 0.0.0.0 Sat Jan 5 09:09 still logged in
reboot system boot 0.0.0.0 Sat Jan 5 09:08 - 10:37 (01:29)
john pts/0 0.0.0.0 Thu Jan 3 00:50 - 01:42 (00:52)
reboot system boot 0.0.0.0 Thu Jan 3 00:48 - 01:43 (00:54)
wtmp begins Tue Jan 1 20:48:28 2013
john@U64D211:~/tmp/util-linux-2.20.1/term-utils$ last
john pts/2 Sat Jan 5 10:37 - 10:37 (00:00)
john pts/0 :0 Sat Jan 5 09:09 still logged in
reboot system boot 3.2.0-35-generic Sat Jan 5 09:08 - 10:38 (01:30)
john pts/0 :0 Thu Jan 3 00:50 - 01:42 (00:52)
reboot system boot 3.2.0-35-generic Thu Jan 3 00:48 - 01:43 (00:54)
wtmp begins Tue Jan 1 20:48:28 2013
空主机名的根本原因
是的,script.c
请创建wtmp
具有空主机名的条目。请查看util-linux-2.20.1/term-utils/script.c
第 245-247 行中的以下代码块。
#ifdef HAVE_LIBUTEMPTER
utempter_add_record(master, NULL);
#endif
基于libutempter-1.1.5/utempter.h
extern int utempter_add_record (int master_fd, const char *hostname);
因此script.c
实际上是将空的主机名传递进去utempter_add_record
。
RedHat 反向移植
有趣的是,上游util-linux-ng-2.17.2
实际上并不支持libutempter
。看来 Redhat 决定重新添加该支持。
john@U64D211:~/tmp/util-linux-ng-2.17.2$ ./configure --help|grep utemp
上述命令返回空结果。
结论
因此,两个发行版之间的行为差异不是错误,而是一种选择。RedHat 决定支持该功能,而 Debian 则跳过了它。
答案2
这对我来说看起来真是令人费解。它应该使用某个 DNS 名称或 IP 地址。我last.c
也检查了文件,但仍然找不到为什么它没有显示任何内容。也许再过一段时间,我就能弄清楚有关 0.0.0.0 的部分。
int dns_lookup(char *result, int size, int useip, int32_t *a)
307 {
308 struct sockaddr_in sin;
309 struct sockaddr_in6 sin6;
310 struct sockaddr *sa;
311 int salen, flags;
312 int mapped = 0;
313
314 flags = useip ? NI_NUMERICHOST : 0;
315
316 /*
317 * IPv4 or IPv6 ?
318 * 1. If last 3 4bytes are 0, must be IPv4
319 * 2. If IPv6 in IPv4, handle as IPv4
320 * 3. Anything else is IPv6
321 *
322 * Ugly.
323 */
324 if (a[0] == 0 && a[1] == 0 && a[2] == htonl (0xffff))
325 mapped = 1;
326
327 if (mapped || (a[1] == 0 && a[2] == 0 && a[3] == 0)) {
328 /* IPv4 */
329 sin.sin_family = AF_INET;
330 sin.sin_port = 0;
331 sin.sin_addr.s_addr = mapped ? a[3] : a[0];
332 sa = (struct sockaddr *)&sin;
333 salen = sizeof(sin);
334 } else {
335 /* IPv6 */
336 memset(&sin6, 0, sizeof(sin6));
337 sin6.sin6_family = AF_INET6;
338 sin6.sin6_port = 0;
339 memcpy(sin6.sin6_addr.s6_addr, a, 16);
340 sa = (struct sockaddr *)&sin6;
341 salen = sizeof(sin6);
342 }
343
344 return getnameinfo(sa, salen, result, size, NULL, 0, flags);
345 }
上下文中使用的两个全局变量是这两个。
int usedns = 0; /* Use DNS to lookup the hostname. */
72 int useip = 0; /* Print IP address in number format */
所以,理论上,它应该使用 dns 或 IP。
我会看看是否能进一步挖掘出什么。但 ewwhite 提出的问题都是合理的。
答案3
所以我在调试器中运行了最后一步,希望至少能给你一些回答你的问题。但我的感觉是根本原因更深。
为什么 last -i 显示 pts 行条目为 0.0.0.0
解释这一点的最好方法是当你不通过 -i。
原因在于此代码部分last.c
if (usedns || useip)
r = dns_lookup(domain, sizeof(domain), useip, p->ut_addr_v6);
if (r < 0) {
len = UT_HOSTSIZE;
if (len >= sizeof(domain)) len = sizeof(domain) - 1;
domain[0] = 0;
strncat(domain, p->ut_host, len);
}
usedns
和useip
(使用默认选项)均未标记。这会导致逻辑从结构中复制出来,根据该结构,该p->ut_host
结构man utmp
包含写入的任何内容所记录的远程登录名utmp
。
char ut_host[UT_HOSTSIZE]; /* Hostname for remote login, or
kernel version for run-level
messages */
在您的例子中,这里的值为零。这就是为什么运行时last
什么都没有出现的原因。
如果是,last -i
则调用 dns_lookup。这将传递条目 (p->ut_addr_v6) 以通过 DNS 进行解析。在您的例子中,此值还包含零。
大部分dns_lookup
都是表面文章和试探法。基本上重要的是函数getnameinfo
。这是一个库调用,在本例中,它将尽力解析存储在中的二进制值ut_addr_v6
。当此条目包含零时(例如您的情况),您实际上是将其解析为,0.0.0.0
就像您的输出一样last -i
。
除了恶意活动之外,还有什么可以合理解释这种行为吗?
嗯,这可能是一个错误或疏忽。这不太可能是恶意的,因为离开似乎很愚蠢任何作为攻击者进行跟踪,而不是省略源地址。
到目前为止,答案的重点一直在错误的地方。last
只是读取utmp
或wtmp
。但是last
正在尽力利用其拥有的数据。
你的根本原因在于utmp
写作方式!
虽然一些应用程序直接写入,但utmp
我猜问题的根源在于sshd
处理会话管理的方式。
除了 bash 历史时间戳之外,还有其他方法可以让我追踪该问题吗?
utmp
通常不可写,也不应这样写。utmp
由旨在让您登录并设置您的会话的应用程序编写。在您的情况下是sshd
。
sshd 无法正确处理您的用户的原因很奇怪,因为它应该正确复制您来自的主机名。这可能是调试工作的重点。首先将 sshd 的调试输出添加到您的日志中,看看是否出现任何异常。
如果你想解决这个问题(或者,甚至可能发现更多关于这个问题的信息),你可以使用它pam_lastlog
来管理,方法是将其添加到utmp
会议进入 /etc/pam.d/sshd。
事实上,检查它是否已经存在并不会有什么坏处——因为它pam_lastlog
包含一个nohost
选项,它可以明确解释您所经历的行为。
最后,您根本不能使用 last。aulast
通过审计子系统执行相同的工作。
可能值得一试,看看它是否至少能写出正确的地址。如果没有,那么你的问题必须与 sshd 一起使用,因为 sshd 正在将 DNS 名称传递到不同的子系统(如 utmp 或 audit)。
答案4
当您登录到一台机器时,这些可能是最后一条命令中的几个条目。
geekride tty2 Fri Dec 21 15:45 - 15:45 (00:00)
geekride pts/1 Fri Dec 21 13:45 still logged in
geekride pts/1 :pts/0:S.0 Thu Dec 6 12:49 - 00:40 (11:50)
geekride pts/1 10.31.33.47 Thu Dec 6 12:49 - 00:40 (11:50)
当您通过终端或控制台按 CTRL+ALT+F1-6 登录时,会出现第一个带有 tty* 的条目。从它使用的终端上可以清楚地看出这一点。
当您登录计算机并在 GUI 中打开终端窗口时,通常会出现第二个条目。即使您在同一个终端窗口中打开新选项卡,也会有一个条目。
第三种类型的条目是在通过 SSH 登录后打开屏幕会话时出现的。这也会在那里创建一个条目,但没有任何 IP 地址。
第四条记录很正常,大家都能理解。
如果你执行last -i
以下条目,你会看到类似这样的内容:
geekride tty2 0.0.0.0 Fri Dec 21 15:45 - 15:45 (00:00)
geekride pts/9 0.0.0.0 Fri Dec 21 13:45 still logged in
geekride pts/1 0.0.0.0 Thu Dec 6 12:49 - 00:40 (11:50)
我非常确定您的情况属于这两种情况中的任何一种,一种是 GUI 中的终端窗口,另一种是屏幕会话。
希望这有帮助。