Merge remote-tracking branch 'LCTT/master'

This commit is contained in:
Xingyu Wang 2019-10-10 12:07:18 +08:00
commit b5756e70db
5 changed files with 455 additions and 476 deletions

View File

@ -1,8 +1,8 @@
[#]: collector: (lujun9972)
[#]: translator: (wxy)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: reviewer: (wxy)
[#]: publisher: (wxy)
[#]: url: (https://linux.cn/article-11441-1.html)
[#]: subject: (9 essential GNU binutils tools)
[#]: via: (https://opensource.com/article/19/10/gnu-binutils)
[#]: author: (Gaurav Kamathe https://opensource.com/users/gkamathe)
@ -12,7 +12,7 @@ GNU binutils 里的九种武器
> 二进制分析是计算机行业中最被低估的技能。
![Tools for the sysadmin][1]
![](https://img.linux.net.cn/data/attachment/album/201910/10/115409g9nkdm2omutduw7u.jpg)
想象一下,在无法访问软件的源代码时,但仍然能够理解软件的实现方式,在其中找到漏洞,并且更厉害的是还能修复错误。所有这些都是在只有二进制文件时做到的。这听起来就像是超能力,对吧?
@ -20,7 +20,7 @@ GNU binutils 里的九种武器
二进制分析是计算机行业中最被低估的技能。它主要由恶意软件分析师、反向工程师和使用底层软件的人使用。
本文探讨了 binutils 可用的一些工具。我使用的是 RHEL但是这些示例应在任何 Linux 发行版上运行。
本文探讨了 binutils 可用的一些工具。我使用的是 RHEL但是这些示例应在任何 Linux 发行版上可以运行。
```
[~]# cat /etc/redhat-release
@ -31,23 +31,23 @@ Red Hat Enterprise Linux Server release 7.6 (Maipo)
[~]#
```
请注意,某些打包命令(例如 `rpm`)在基于 Debian 的发行版中可能不可用,因此请在适用时使用等效的 `dpkg` 命令。
请注意,某些打包命令(例如 `rpm`)在基于 Debian 的发行版中可能不可用,因此请使用等效的 `dpkg` 命令替代
### 软件开发的基础知识
在开源世界中,我们很多人都专注于源代码形式的软件。当软件的源代码随时可用时,很容易获得源代码的副本,打开喜欢的编辑器,喝杯咖啡,然后开始探索。
在开源世界中,我们很多人都专注于源代码形式的软件。当软件的源代码随时可用时,很容易获得源代码的副本,打开喜欢的编辑器,喝杯咖啡,然后就可以开始探索
但是源代码不是在 CPU 上执行的代码,在 CPU 上执行的是二进制或机器语言指令。二进制或可执行文件是编译源代码时获得的。熟练的调试人员通常会通过了解这种差异来获得优势
但是源代码不是在 CPU 上执行的代码,在 CPU 上执行的是二进制或者说是机器语言指令。二进制或可执行文件是编译源代码时获得的。熟练的调试人员深谙通常这种差异。
### 编译的基础知识
在深入研究 binutils 软件包本身之前,最好先了解编译的基础知识。
编译是将程序从某种编程语言(C/C++)的源代码或文本形式转换为机器代码的过程。
编译是将程序从某种编程语言(如 C/C++)的源代码(文本形式)转换为机器代码的过程。
机器代码是 CPU或一般而言硬件可以理解的 1 和 0 的序列,因此可以由 CPU 执行或运行。该机器码以特定格式保存到文件,通常称为可执行文件或二进制文件。在 Linux和使用 [Linux 兼容二进制][3]的 BSD这称为 [ELF][4]<ruby>可执行和可链接格式<rt>Executable and Linkable Format</rt></ruby>)。
呈现给定源文件的可执行文件或二进制文件之前编译过程将经历一系列复杂的步骤。以这个源程序C 代码)为例。打开你喜欢的编辑器,然后键入以下程序:
生成给定的源文件的可执行文件或二进制文件之前编译过程将经历一系列复杂的步骤。以这个源程序C 代码)为例。打开你喜欢的编辑器,然后键入以下程序:
```
#include <stdio.h>
@ -61,7 +61,7 @@ int main(void)
#### 步骤 1用 cpp 预处理
[C 预处理程序cpp][5]用于扩展所有宏并包括头文件。在此示例中,头文件 `stdio.h` 将被包含在源代码中。`stdio.h` 是一个头文件,其中包含有关程序内使用的 `printf` 函数的信息。对源代码运行 `cpp`,其结果指令保存在名为 `hello.i` 的文件中。可以使用文本编辑器打开该文件以查看其内容。打印 “hello world” 的源代码在该文件的底部。
[C 预处理程序cpp][5]用于扩展所有宏并将头文件包含进来。在此示例中,头文件 `stdio.h` 将被包含在源代码中。`stdio.h` 是一个头文件,其中包含有关程序内使用的 `printf` 函数的信息。对源代码运行 `cpp`,其结果指令保存在名为 `hello.i` 的文件中。可以使用文本编辑器打开该文件以查看其内容。打印 “hello world” 的源代码在该文件的底部。
```
[testdir]# cat hello.c
@ -84,7 +84,7 @@ total 24
#### 步骤 2用 gcc 编译
在此阶段,无需创建目标文件就将步骤 1 中的预处理源代码转换为汇编语言指令。这个阶段使用 [GNU 编译器集合gcc][6]。对 `hello.i` 文件运行带有 `-S` 选项的 `gcc` 命令后,它将创建一个名为 `hello.s` 的新文件。该文件包含 C 程序的汇编语言指令。
在此阶段,无需创建目标文件就将步骤 1 中生成的预处理源代码转换为汇编语言指令。这个阶段使用 [GNU 编译器集合gcc][6]。对 `hello.i` 文件运行带有 `-S` 选项的 `gcc` 命令后,它将创建一个名为 `hello.s` 的新文件。该文件包含 C 程序的汇编语言指令。
你可以使用任何编辑器或 `cat` 命令查看其内容。
@ -130,7 +130,7 @@ ret
#### 步骤 3用 as 汇编
汇编程序的目的是将汇编语言指令转换为机器语言代码,并生成扩展名为 `.o` 的目标文件。此阶段使用默认情况下在所有 Linux 平台上都可用的 GNU 汇编器。
汇编的目的是将汇编语言指令转换为机器语言代码,并生成扩展名为 `.o` 的目标文件。此阶段使用默认情况下在所有 Linux 平台上都可用的 GNU 汇编器。
```
testdir]# as hello.s -o hello.o
@ -144,7 +144,7 @@ total 32
[testdir]#
```
现在,你有了第一个 ELF 格式的文件;但是,还不能执行它。稍后,你将看到“目标文件”和“可执行文件”之间的区别。
现在,你有了第一个 ELF 格式的文件;但是,还不能执行它。稍后,你将看到“<ruby>目标文件<rt>object file</rt></ruby>”和“<ruby>可执行文件<rt>executable file</rt></ruby>”之间的区别。
```
[testdir]# file hello.o
@ -155,7 +155,7 @@ hello.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
这是编译的最后阶段,将目标文件链接以创建可执行文件。可执行文件通常需要外部函数,这些外部函数通常来自系统库(`libc`)。
你可以使用 `ld` 命令直接调用链接器;但是,此命令有些复杂。相反,你可以使用带有 `-v` (详细)标志的 `gcc` 编译器,以了解链接是如何发生的。(使用 `ld` 命令进行链接作为一个练习,你可以自行探索。)
你可以使用 `ld` 命令直接调用链接器;但是,此命令有些复杂。相反,你可以使用带有 `-v`(详细)标志的 `gcc` 编译器,以了解链接是如何发生的。(使用 `ld` 命令进行链接作为一个练习,你可以自行探索。)
```
[testdir]# gcc -v hello.o
@ -187,7 +187,6 @@ total 44
`a.out` 运行 `file` 命令,结果表明它确实是 ELF 可执行文件:
```
[testdir]# file a.out
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=48e4c11901d54d4bf1b6e3826baf18215e4255e5, not stripped
@ -203,7 +202,7 @@ a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (
### 探索 binutils 工具
练习为使用 binutils 软件包中的工具提供了良好的背景。我的系统带有 binutils 版本 2.27-34 你的 Linux 发行版上的版本可能有所不同。
上面这个练习为使用 binutils 软件包中的工具提供了良好的背景。我的系统带有 binutils 版本 2.27-34你的 Linux 发行版上的版本可能有所不同。
```
[~]# rpm -qa | grep binutils
@ -234,11 +233,11 @@ binutils 软件包中提供了以下工具:
/usr/bin/strip
```
上面的编译练习已经探索了其中的两个工具:用作汇编程序的 `as` 命令,用作链接程序的 `ld` 命令。 继续阅读以了解上述 GNU binutils 软件包工具中的其他七个。
上面的编译练习已经探索了其中的两个工具:用作汇编器的 `as` 命令,用作链接器的 `ld` 命令。继续阅读以了解上述 GNU binutils 软件包工具中的其他七个。
#### readelf显示 ELF 文件信息
上面的练习提到了术语“目标文件”和“可执行文件”。使用该练习中的文件,使用带有 `-h`(标题)选项的 `readelf` 命令,以将文件的 ELF 标题转储到屏幕上。请注意,以 `.o` 扩展名结尾的目标文件显示为 `Type: REL (Relocatable file)`(可重定位文件):
上面的练习提到了术语“目标文件”和“可执行文件”。使用该练习中的文件,通过带有 `-h`(标题)选项的 `readelf` 命令,以将文件的 ELF 标题转储到屏幕上。请注意,以 `.o` 扩展名结尾的目标文件显示为 `Type: REL (Relocatable file)`(可重定位文件):
```
[testdir]# readelf -h hello.o
@ -249,7 +248,7 @@ Type: REL (Relocatable file)
[...]
```
如果尝试执行此文件,将收到一条错误消息,指出无法执行。这仅表示它尚不具备在 CPU 上执行所需的信息。
如果尝试执行此目标文件,会收到一条错误消息,指出无法执行。这仅表示它尚不具备在 CPU 上执行所需的信息。
请记住,你首先需要使用 `chmod` 命令在对象文件上添加 `x`(可执行位),否则你将得到“权限被拒绝”的错误。
@ -278,10 +277,9 @@ Class: ELF64
[testdir]# ./a.out Hello World
```
`readelf` 命令可提供有关二进制文件的大量信息。在这里,它会告诉你它是 ELF64 位格式,这意味着它只能在 64 位 CPU 上执行,而不能在 32 位 CPU 上运行。它还告诉你它应在 X86-64Intel/AMD架构上执行。二进制文件的入口点是地址 `0x400430`,它就是 C 源程序中 `main` 函数的地址。
在你知道的其他系统二进制文件上尝试一下 `readelf` 命令,例如 `ls`。请注意,在 RHEL 8 或 Fedora 30 及更高版本的系统上,由于安全原因改用了位置无关可执行文件([PIE][7]),因此你的输出(尤其是 `Type:`)可能会有所不同。
`readelf` 命令可提供有关二进制文件的大量信息。在这里,它会告诉你它是 ELF 64 位格式,这意味着它只能在 64 位 CPU 上执行,而不能在 32 位 CPU 上运行。它还告诉你它应在 X86-64Intel/AMD架构上执行。该二进制文件的入口点是地址 `0x400430`,它就是 C 源程序中 `main` 函数的地址。
在你知道的其他系统二进制文件上尝试一下 `readelf` 命令,例如 `ls`。请注意,在 RHEL 8 或 Fedora 30 及更高版本的系统上,由于安全原因改用了<ruby>位置无关可执行文件<rt>position independent executable</rt></ruby>[PIE][7]),因此你的输出(尤其是 `Type:`)可能会有所不同。
```
[testdir]# readelf -h /bin/ls
@ -311,7 +309,7 @@ libattr.so.1 => /lib64/libattr.so.1 (0x00007f060cc84000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f060ca68000)
```
`libc` 库文件运行 `readelf` 以查看它是哪种文件。正如它指出的那样,它是一个 `DYN (Shared object file)`(共享对象文件),这意味着它不能直接直接执行;必须由内部使用了该库提供的任何函数的可执行文件使用它。
`libc` 库文件运行 `readelf` 以查看它是哪种文件。正如它指出的那样,它是一个 `DYN (Shared object file)`(共享对象文件),这意味着它不能直接执行;必须由内部使用了该库提供的任何函数的可执行文件使用它。
```
[testdir]# readelf -h /lib64/libc.so.6
@ -339,7 +337,7 @@ file1: ASCII text
size: file1: File format not recognized
```
现在,在上面的练习中,对对象文件和可执行文件运行 `size` 命令。请注意,根据 `size` 命令的输出,可执行文件(`a.out`)的信息要比目标文件(`hello.o`)多得多:
现在,在上面的练习中,对目标文件和可执行文件运行 `size` 命令。请注意,根据 `size` 命令的输出可以看出,可执行文件(`a.out`)的信息要比目标文件(`hello.o`)多得多:
```
[testdir]# size hello.o
@ -352,7 +350,7 @@ text data bss dec hex filename
但是这里的 `text`、`data` 和 `bss` 节是什么意思?
`text` 节是指二进制文件的代码部分,其中包含所有可执行指令。`data` 节是所有初始化数据所在的位置,`bss` 节是所有未初始化数据的存储位置。
`text` 节是指二进制文件的代码部分,其中包含所有可执行指令。`data` 节是所有初始化数据所在的位置,`bss` 节是所有未初始化数据的存储位置。LCTT 译注:一般来说,在静态的映像文件中,各个部分称之为<ruby><rt>section</rt></ruby>,而在运行时的各个部分称之为<ruby><rt>segment</rt></ruby>,有时统称为段。)
比较其他一些可用的系统二进制文件的 `size` 结果。
@ -386,7 +384,7 @@ text data bss dec hex filename
Hello World
```
另一方面,在 `a.out`(可执行文件)上运行 `strings` 会显示在链接阶段二进制文件中包含的其他信息:
另一方面,在 `a.out`(可执行文件)上运行 `strings` 会显示在链接阶段二进制文件中包含的其他信息:
```
[testdir]# strings -d a.out
@ -407,7 +405,7 @@ Hello World
#### objdump显示目标文件信息
另一个可以从二进制文件中转储机器语言指令的 binutils 工具称为 `objdump`。使用 `-d` 选项,可从二进制文件中反汇编所有汇编指令。
另一个可以从二进制文件中转储机器语言指令的 binutils 工具称为 `objdump`。使用 `-d` 选项,可从二进制文件中反汇编所有汇编指令。
回想一下,编译是将源代码指令转换为机器代码的过程。机器代码仅由 1 和 0 组成,人类难以阅读。因此,它有助于将机器代码表示为汇编语言指令。汇编语言是什么样的?请记住,汇编语言是特定于体系结构的;由于我使用的是 Intelx86-64架构因此如果你使用 ARM 架构编译相同的程序,指令将有所不同。
@ -427,9 +425,9 @@ e: b8 00 00 00 00 mov $0x0,%eax
14: c3 retq
```
该输出乍一看似乎令人生畏,但请花一点时间来理解它,然后再继续。回想一下,`.text` 节包含所有的机器代码指令。汇编指令可以在第四列中看到(即 `push`、`mov`、`callq`、`pop`、`retq` 等)。这些指令作用于寄存器,寄存器是 CPU 内置的存储器位置。本示例中的寄存器是 `rbp`、`rsp`、`edi`、`eax` 等,并且每个寄存器都有特殊的含义。
该输出乍一看似乎令人生畏,但请花一点时间来理解它,然后再继续。回想一下,`.text` 节包含所有的机器代码指令。汇编指令可以在第四列中看到(即 `push`、`mov`、`callq`、`pop`、`retq` 等)。这些指令作用于寄存器,寄存器是 CPU 内置的存储器位置。本示例中的寄存器是 `rbp`、`rsp`、`edi`、`eax` 等,并且每个寄存器都有特殊的含义。
现在对可执行文件(`a.out`)运行 `objdump` 并查看得到的内容。可执行文件`objdump` 的输出可能很大,因此我使用 `grep` 命令将其缩小到 `main` 函数:
现在对可执行文件(`a.out`)运行 `objdump` 并查看得到的内容。可执行文件的 `objdump` 的输出可能很大,因此我使用 `grep` 命令将其缩小到 `main` 函数:
```
[testdir]# objdump -d a.out | grep -A 9 main\>
@ -449,7 +447,7 @@ e: b8 00 00 00 00 mov $0x0,%eax
* 目标文件 `hello.o` 具有以下指令:`callq e`
* 可执行文件 `a.out` 由以下指令组成,该指令带有一个地址和函数:`callq 400400 <puts@plt>`
  
上面的汇编指令正在调用 `puts` 函数。请记住,你在源代码中使用了 `printf` 函数。编译器插入了对 `puts` 库函数的调用,以将 `Hello World` 输出到屏幕。
上面的汇编指令正在调用 `puts` 函数。请记住,你在源代码中使用了一个 `printf` 函数。编译器插入了对 `puts` 库函数的调用,以将 `Hello World` 输出到屏幕。
查看 `put` 上方一行的说明:
@ -458,7 +456,7 @@ e: b8 00 00 00 00 mov $0x0,%eax
该指令将二进制文件中地址 `$0x4005d0` 处存在的内容移动到名为 `edi` 的寄存器中。
该存储位置的内容中还能是别的什么吗?是的,你猜对了:它不过是文本 `Hello, World`。你是如何确定的?
这个存储位置的内容中还能是别的什么吗?是的,你猜对了:它就是文本 `Hello, World`。你是如何确定的?
`readelf` 命令使你可以将二进制文件(`a.out`)的任何节转储到屏幕上。以下要求它将 `.rodata`(这是只读数据)转储到屏幕上:
@ -472,13 +470,13 @@ Hex dump of section '.rodata':
你可以在右侧看到文本 `Hello World`,在左侧可以看到其二进制格式的地址。它是否与你在上面的 `mov` 指令中看到的地址匹配?是的,确实匹配。
#### strip从目标文件中丢弃符号
#### strip从目标文件中剥离符号
该命令通常用于在将二进制文件交付给客户之前减小二进制文件的大小。
请记住,由于重要信息已从二进制文件中删除,因此它会阻碍调试过程。但是,这个二进制文件可以完美地执行。
请记住,由于重要信息已从二进制文件中删除,因此它会妨碍调试。但是,这个二进制文件可以完美地执行。
`a.out` 可执行文件运行,并注意会发生什么。首先,通过运行以下命令确保二进制文件没有被剥离(`not stripped`
`a.out` 可执行文件运行该命令,并注意会发生什么。首先,通过运行以下命令确保二进制文件没有被剥离(`not stripped`
```
[testdir]# file a.out
@ -492,7 +490,7 @@ a.out: ELF 64-bit LSB executable, x86-64, [......] not stripped
8440 a.out
```
现在对该可执行文件运行 `strip` 命令,并使用 `file` 命令确保它可以正常工作
现在对该可执行文件运行 `strip` 命令,并使用 `file` 命令以确保正常完成
```
[testdir]# strip a.out
@ -500,7 +498,7 @@ a.out: ELF 64-bit LSB executable, x86-64, [......] not stripped
```
剥离二进制文件后,此小程序的大小从之前的 `8440` 字节减小为 `6296` 字节。对于这样小的一个程序都能有这么大的节省,难怪大型程序经常被剥离。
剥离二进制文件后,此小程序的大小从之前的 `8440` 字节减小为 `6296` 字节。对于这样小的一个程序都能有这么大的空间节省,难怪大型程序经常被剥离。
```
[testdir]# du -b a.out
@ -511,7 +509,7 @@ a.out: ELF 64-bit LSB executable, x86-64, [......] not stripped
`addr2line` 工具只是在二进制文件中查找地址,并将其与 C 源代码程序中的行进行匹配。很酷,不是吗?
为此编写另一个测试程序;只是这一次确保使用 `gcc``-g` 标志进行编译,这将为二进制文件添加其调试信息,并包含有助于调试的行号(由源代码中提供):
为此编写另一个测试程序;只是这一次确保使用 `gcc``-g` 标志进行编译,这将为二进制文件添加其调试信息,并包含有助于调试的行号(由源代码中提供):
```
[testdir]# cat -n atest.c
@ -550,7 +548,7 @@ Within function2
Within main
```
现在使用 `objdump` 来标识函数开始的内存地址。你可以使用 `grep` 命令来过滤出所需的特定行。函数的地址在下面突出显示:
现在使用 `objdump` 来标识函数开始的内存地址。你可以使用 `grep` 命令来过滤出所需的特定行。函数的地址在下面突出显示`55 push %rbp` 前的地址)
```
[testdir]# objdump -d a.out | grep -A 2 -E 'main>:|function1>:|function2>:'
@ -568,7 +566,7 @@ Within main
400548: 48 89 e5 mov %rsp,%rbp
```
现在,使用 `addr2line` 工具从映射二进制文件中的这些地址到 C 源代码匹配的地址:
现在,使用 `addr2line` 工具从二进制文件中的这些地址映射到 C 源代码匹配的地址:
```
[testdir]# addr2line -e a.out 40051d
@ -618,7 +616,7 @@ nm: a.out: no symbols
### 结论
GNU binutils 工具为有兴趣分析二进制文件的人提供了许多选项,这只是它们可以为你做的事情的一角。请阅读每种工具的手册页,以了解有关它们以及如何使用它们的更多信息。
GNU binutils 工具为有兴趣分析二进制文件的人提供了许多选项,这只是它们可以为你做的事情的冰山一角。请阅读每种工具的手册页,以了解有关它们以及如何使用它们的更多信息。
--------------------------------------------------------------------------------
@ -627,7 +625,7 @@ via: https://opensource.com/article/19/10/gnu-binutils
作者:[Gaurav Kamathe][a]
选题:[lujun9972][b]
译者:[wxy](https://github.com/wxy)
校对:[校对者ID](https://github.com/校对者ID)
校对:[wxy](https://github.com/wxy)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出

View File

@ -1,188 +0,0 @@
[#]: collector: (lujun9972)
[#]: translator: (Morisun029)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (Mutation testing by example: Failure as experimentation)
[#]: via: (https://opensource.com/article/19/9/mutation-testing-example-failure-experimentation)
[#]: author: (Alex Bunardzic https://opensource.com/users/alex-bunardzichttps://opensource.com/users/jocunddew)
Mutation testing by example: Failure as experimentation
======
Develop the logic for an automated cat door that opens during daylight
hours and locks during the night, and follow along with the .NET
xUnit.net testing framework.
![Digital hand surrounding by objects, bike, light bulb, graphs][1]
In the [first article][2] in this series, I demonstrated how to use planned failure to ensure expected outcomes in your code. In this second article, I'll continue developing my example project—an automated cat door that opens during daylight hours and locks during the night.
As a reminder, you can follow along using the .NET xUnit.net testing framework by following the [instructions here][3].
### What about the daylight hours?
Recall that test-driven development (TDD) centers on a healthy amount of unit tests.
The first article implemented logic that fulfills the expectations of the **Given7pmReturnNighttime** unit test. But you're not done yet. Now you need to describe the expectations of what happens when the current time is greater than 7am. Here is the new unit test, called **Given7amReturnDaylight**:
```
       [Fact]
       public void Given7amReturnDaylight()
       {
           var expected = "Daylight";
           var actual = dayOrNightUtility.GetDayOrNight();
           Assert.Equal(expected, actual);
       }
```
The new unit test now fails (it is very desirable to fail as early as possible!):
```
Starting test execution, please wait...
[Xunit.net 00:00:01.23] unittest.UnitTest1.Given7amReturnDaylight [FAIL]
Failed unittest.UnitTest1.Given7amReturnDaylight
[...]
```
It was expecting to receive the string value "Daylight" but instead received the string value "Nighttime."
### Analyze the failed test case
Upon closer inspection, it seems that the code has trapped itself. It turns out that the implementation of the **GetDayOrNight** method is not testable!
Take a look at the core challenge we have ourselves in:
1. **GetDayOrNight relies on hidden input. **
The value of **dayOrNight** is dependent upon the hidden input (it obtains the value for the time of day from the built-in system clock).
2. **GetDayOrNight contains non-deterministic behavior. **
The value of the time of day obtained from the system clock is non-deterministic. It depends on the point in time when you run the code, which we must consider unpredictable.
3. **Low quality of the GetDayOrNight API.**
This API is tightly coupled to the concrete data source (system **DateTime**).
4. **GetDayOrNight violates the single responsibility principle.**
You have implemented a method that consumes and processes information at the same time. It is a good practice that a method should be responsible for performing a single duty.
5. **GetDayOrNight has more than one reason to change.**
It is possible to imagine a scenario where the internal source of time may change. Also, it is quite easy to imagine that the processing logic will change. These disparate reasons for changing must be isolated from each other.
6. **The API signature of GetDayOrNight is not sufficient when it comes to trying to understand its behavior.**
It is very desirable to be able to understand what type of behavior to expect from an API by simply looking at its signature.
7. **GetDayOrNight depends on global shared mutable state.**
Shared mutable state is to be avoided at all costs!
8. **The behavior of the GetDayOrNight method cannot be predicted even after reading the source code.**
That is a scary proposition. It should always be very clear from reading the source code what kind of behavior can be predicted once the system is operational.
### The principles behind what failed
Whenever you're faced with an engineering problem, it is advisable to use the time-tested strategy of _divide and conquer_. In this case, following the principle of _separation of concerns_ is the way to go.
> **separation of concerns** (**SoC**) is a design principle for separating a computer program into distinct sections, so that each section addresses a separate concern. A concern is a set of information that affects the code of a computer program. A concern can be as general as the details of the hardware the code is being optimized for, or as specific as the name of a class to instantiate. A program that embodies SoC well is called a modular program.
>
> ([source][4])
The **GetDayOrNight** method should be concerned only with deciding whether the date and time value means daylight or nighttime. It should not be concerned with finding the source of that value. That concern should be left to the calling client.
You must leave it to the calling client to take care of obtaining the current time. This approach aligns with another valuable engineering principle—_inversion of control_. Martin Fowler explores this concept in [detail, here][5].
> One important characteristic of a framework is that the methods defined by the user to tailor the framework will often be called from within the framework itself, rather than from the user's application code. The framework often plays the role of the main program in coordinating and sequencing application activity. This inversion of control gives frameworks the power to serve as extensible skeletons. The methods supplied by the user tailor the generic algorithms defined in the framework for a particular application.
>
> \-- [Ralph Johnson and Brian Foote][6]
### Refactoring the test case
So the code needs refactoring. Get rid of the dependency on the internal clock (the **DateTime** system utility):
```
` DateTime time = new DateTime();`
```
Delete the above line (which should be line 7 in your file). Refactor your code further by adding an input parameter **DateTime** time to the **GetDayOrNight** method.
Here's the refactored class **DayOrNightUtility.cs**:
```
using System;
namespace app {
   public class DayOrNightUtility {
       public string GetDayOrNight(DateTime time) {
           string dayOrNight = "Nighttime";
           if(time.Hour &gt;= 7 &amp;&amp; time.Hour &lt; 19) {
               dayOrNight = "Daylight";
           }
           return dayOrNight;
       }
   }
}
```
Refactoring the code requires the unit tests to change. You need to prepare values for the **nightHour** and the **dayHour** and pass those values into the **GetDayOrNight** method. Here are the refactored unit tests:
```
using System;
using Xunit;
using app;
namespace unittest
{
   public class UnitTest1
   {
       DayOrNightUtility dayOrNightUtility = [new][7] DayOrNightUtility();
       DateTime nightHour = [new][7] DateTime(2019, 08, 03, 19, 00, 00);
       DateTime dayHour = [new][7] DateTime(2019, 08, 03, 07, 00, 00);
       [Fact]
       public void Given7pmReturnNighttime()
       {
           var expected = "Nighttime";
           var actual = dayOrNightUtility.GetDayOrNight(nightHour);
           Assert.Equal(expected, actual);
       }
       [Fact]
       public void Given7amReturnDaylight()
       {
           var expected = "Daylight";
           var actual = dayOrNightUtility.GetDayOrNight(dayHour);
           Assert.Equal(expected, actual);
       }
   }
}
```
### Lessons learned
Before moving forward with this simple scenario, take a look back and review the lessons in this exercise.
It is easy to create a trap inadvertently by implementing code that is untestable. On the surface, such code may appear to be functioning correctly. However, following test-driven development (TDD) practice—describing the expectations first and only then prescribing the implementation—revealed serious problems in the code.
This shows that TDD is the ideal methodology for ensuring code does not get too messy. TDD points out problem areas, such as the absence of single responsibility and the presence of hidden inputs. Also, TDD assists in removing non-deterministic code and replacing it with fully testable code that behaves deterministically.
Finally, TDD helped deliver code that is easy to read and logic that's easy to follow.
In the next article in this series, I'll demonstrate how to use the logic created during this exercise to implement functioning code and how further testing can make it even better.
--------------------------------------------------------------------------------
via: https://opensource.com/article/19/9/mutation-testing-example-failure-experimentation
作者:[Alex Bunardzic][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://opensource.com/users/alex-bunardzichttps://opensource.com/users/jocunddew
[b]: https://github.com/lujun9972
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/rh_003588_01_rd3os.combacktoschoolseriesk12_rh_021x_0.png?itok=fvorN0e- (Digital hand surrounding by objects, bike, light bulb, graphs)
[2]: https://opensource.com/article/19/9/mutation-testing-example-part-1-how-leverage-failure
[3]: https://opensource.com/article/19/8/mutation-testing-evolution-tdd
[4]: https://en.wikipedia.org/wiki/Separation_of_concerns
[5]: https://martinfowler.com/bliki/InversionOfControl.html
[6]: http://www.laputan.org/drc/drc.html
[7]: http://www.google.com/search?q=new+msdn.microsoft.com

View File

@ -1,246 +0,0 @@
[#]: collector: (lujun9972)
[#]: translator: ( )
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (7 steps to securing your Linux server)
[#]: via: (https://opensource.com/article/19/10/linux-server-security)
[#]: author: (Patrick H. Mullins https://opensource.com/users/pmullins)
7 steps to securing your Linux server
======
Harden your Linux server in seven easy steps.
![computer servers processing data][1]
This primer will introduce you to basic Linux server security. While it focuses on Debian/Ubuntu, you can apply everything presented here to other Linux distributions. I also encourage you to research this material and extend it where applicable.
### 1\. Update your server
The first thing you should do to secure your server is to update the local repositories and upgrade the operating system and installed applications by applying the latest patches.
On Ubuntu and Debian:
```
`$ sudo apt update && sudo apt upgrade -y`
```
On Fedora, CentOS, or RHEL:
```
`$ sudo dnf upgrade`
```
### 2\. Create a new privileged user account
Next, create a new user account. You should never log into your server as **root**. Instead, create your own account ("**&lt;user&gt;**"), give it **sudo** rights, and use it to log into your server.
Start out by creating a new user:
```
`$ adduser <username>`
```
Give your new user account **sudo** rights by appending (**-a**) the **sudo** group (**-G**) to the user's group membership:
```
`$ usermod -a -G sudo <username>`
```
### 3\. Upload your SSH key
You'll want to use an SSH key to log into your new server. You can upload your [pre-generated SSH key][2] to your new server using the **ssh-copy-id** command:
```
`$ ssh-copy-id <username>@ip_address`
```
Now you can log into your new server without having to type in a password.
### 4\. Secure SSH
Next, make these three changes:
* Disable SSH password authentication
* Restrict **root** from logging in remotely
* Restrict access to IPv4 or IPv6
Open **/etc/ssh/sshd_config** using your text editor of choice and ensure these lines:
```
PasswordAuthentication yes
PermitRootLogin yes
```
look like this:
```
PasswordAuthentication no
PermitRootLogin no
```
Next, restrict the SSH service to either IPv4 or IPv6 by modifying the **AddressFamily** option. To change it to use only IPv4 (which should be fine for most folks) make this change:
```
`AddressFamily inet`
```
Restart the SSH service to enable your changes. Note that it's a good idea to have two active connections to your server before restarting the SSH server. Having that extra connection allows you to fix anything should the restart go wrong.
On Ubuntu:
```
`$ sudo service sshd restart`
```
On Fedora or CentOS or anything using Systemd:
```
`$ sudo systemctl restart sshd`
```
### 5\. Enable a firewall
Now you need to install a firewall, enable it, and configure it only to allow network traffic that you designate. [Uncomplicated Firewall][3] (UFW) is an easy-to-use interface to **iptables** that greatly simplifies the process of configuring a firewall.
You can install UFW with:
```
`$ sudo apt install ufw`
```
By default, UFW denies all incoming connections and allows all outgoing connections. This means any application on your server can reach the internet, but anything trying to reach your server cannot connect.
First, make sure you can log in by enabling access to SSH, HTTP, and HTTPS:
```
$ sudo ufw allow ssh
$ sudo ufw allow http
$ sudo ufw allow https
```
Then enable UFW:
```
`$ sudo ufw enable`
```
You can see what services are allowed and denied with:
```
`$ sudo ufw status`
```
If you ever want to disable UFW, you can do so by typing:
```
`$ sudo ufw disable`
```
You can also use [firewall-cmd][4], which is already installed and integrated into some distributions.
### 6\. Install Fail2ban
[Fail2ban][5] is an application that examines server logs looking for repeated or automated attacks. If any are found, it will alter the firewall to block the attacker's IP address either permanently or for a specified amount of time.
You can install Fail2ban by typing:
```
`$ sudo apt install fail2ban -y`
```
Then copy the included configuration file:
```
`$ sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local`
```
And restart Fail2ban:
```
`$ sudo service fail2ban restart`
```
That's all there is to it. The software will continuously examine the log files looking for attacks. After a while, the app will build up quite a list of banned IP addresses. You can view this list by requesting the current status of the SSH service with:
```
`$ sudo fail2ban-client status ssh`
```
### 7\. Remove unused network-facing services
Almost all Linux server operating systems come with a few network-facing services enabled. You'll want to keep most of them. However, there are a few that you might want to remove. You can see all running network services by using the **ss** command:
```
`$ sudo ss -atpu`
```
The output from **ss** will differ depending on your operating system. This is an example of what you might see. It shows that the SSH (sshd) and Ngnix (nginx) services are listening and ready for connection:
```
tcp LISTEN 0 128 *:http *:* users:(("nginx",pid=22563,fd=7))
tcp LISTEN 0 128 *:ssh *:* users:(("sshd",pid=685,fd=3))
```
How you go about removing an unused service ("**&lt;service_name&gt;**") will differ depending on your operating system and the package manager it uses.
To remove an unused service on Debian/Ubuntu:
```
`$ sudo apt purge <service_name>`
```
To remove an unused service on Red Hat/CentOS:
```
`$ sudo yum remove <service_name>`
```
Run **ss -atup** again to verify that the unused services are no longer installed and running.
### Final thoughts
This tutorial presents the bare minimum needed to harden a Linux server. Additional security layers can and should be enabled depending on how a server is used. These layers can include things like individual application configurations, intrusion detection software, and enabling access controls, e.g., two-factor authentication.
--------------------------------------------------------------------------------
via: https://opensource.com/article/19/10/linux-server-security
作者:[Patrick H. Mullins][a]
选题:[lujun9972][b]
译者:[译者ID](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://opensource.com/users/pmullins
[b]: https://github.com/lujun9972
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/server_data_system_admin.png?itok=q6HCfNQ8 (computer servers processing data)
[2]: https://opensource.com/article/19/4/ssh-keys-seahorse
[3]: https://launchpad.net/ufw
[4]: https://www.redhat.com/sysadmin/secure-linux-network-firewall-cmd
[5]: https://www.fail2ban.org/wiki/index.php/Main_Page

View File

@ -0,0 +1,192 @@
[#]: collector: (lujun9972)
[#]: translator: (Morisun029)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (Mutation testing by example: Failure as experimentation)
[#]: via: (https://opensource.com/article/19/9/mutation-testing-example-failure-experimentation)
[#]: author: (Alex Bunardzic https://opensource.com/users/alex-bunardzichttps://opensource.com/users/jocunddew)
以变异测试为例:基于故障的试验
======
基于 .NET 的 xUnit.net 测试框架,开发一款自动猫门的逻辑,让门在白天开放,夜间锁定,
![Digital hand surrounding by objects, bike, light bulb, graphs][1]
在本系列的[第一篇文章][2]中,我演示了如何使用设计的故障来确保代码中的预期结果。 在第二篇文章中,我将继续开发示例项目——一款自动猫门,该门在白天开放,夜间锁定。
在此提醒一下,您可以按照[此处的说明][3]使用 .NET 的 xUnit.net 测试框架。
### 关于白天时间
回想一下测试驱动开发TDD围绕着大量的单元测试。
第一篇文章中实现了满足 **Given7pmReturnNighttime** 单元测试期望的逻辑。 但还没有完, 现在您需要描述当前时间大于7点时期望发生的结果。 这是新的单元测试,称为 **Given7amReturnDaylight**
```
[Fact]
public void Given7amReturnDaylight()
{
var expected = "Daylight";
var actual = dayOrNightUtility.GetDayOrNight();
Assert.Equal(expected, actual);
}
```
现在,新的单元测试失败了(越早失败越好!):
```
Starting test execution, please wait...
[Xunit.net 00:00:01.23] unittest.UnitTest1.Given7amReturnDaylight [FAIL]
Failed unittest.UnitTest1.Given7amReturnDaylight
[...]
```
期望接收到字符串值是 "Daylight" ,但实际接收到的值是 "Nighttime"。
### 分析失败的测试用例
经过仔细检查,代码本身似乎已经出现问题。 事实证明,**GetDayOrNight** 方法的实现是不可测试的!
看看我们面临的核心挑战:
1. **GetDayOrNight 依赖隐藏输入。 **
**dayOrNight** 的值取决于隐藏输入(它从内置系统时钟中获取一天的时间值)。
2. **GetDayOrNight 包含非确定性行为。 **
从系统时钟中获取到的时间值是不确定的。 (因为)该时间取决于你运行代码的时间点,而这一点我们认为这是不可预测的。
3. **GetDayOrNight API 的质量差。**
该 API 与具体的数据源(系统 **DateTime**) 紧密耦合。
4. **GetDayOrNight violates 违反了单一责任原则。**
该方法实现同时使用和处理信息。优良作法是一种方法应负责执行一项职责。
5. **GetDayOrNight 有多个更改原因。**
可以想象内部时间源可能会更改的情况。同样,很容易想象处理逻辑也将改变。这些变化的不同原因必须相互隔离。
6. **当(我们)尝试了解 GetDayOrNight 行为时,会发现它的 API 签名不足。 **
最理想的做法就是通过简单的查看API的签名就能了解API预期的行为类型。。
7. **GetDayOrNight 取决于全局共享可变状态。**
要不惜一切代价避免共享的可变状态!
8. **即使在阅读源代码之后,也无法预测 GetDayOrNight方法的行为。**
这是一个严重的问题。 通过阅读源代码,应该始终非常清楚,系统一旦开始运行,便可以预测出其行为。
### 失败背后的原则
每当您遇到工程问题时,建议使用久经考验的分而治之策略。 在这种情况下,遵循关注点分离的原则是一种可行的方法。
> **separation of concerns** (**SoC**) 是一种用于将计算机程序分为不同模块的设计原理,以便每个模块都可以解决一个关注点。 关注点是影响计算机程序代码的一组信息。 关注点信息可能与要优化代码的硬件的细节一样概括,也可能与要实例化的类的名称一样具体。完美体现 SoC 的程序称为模块化程序。
>
> ([source][4])
**GetDayOrNight** 方法应仅与确定日期和时间值表示白天还是夜晚有关。 它不应该与寻找该值的来源有关。该问题应留给调用客户端。
必须将这个问题留给调用客户端,以获取当前时间。 这种方法符合另一个有价值的工程原理-控制反转。 Martin Fowler [在这里][5]详细探讨了这一概念。
> 框架的一个重要特征是用户定义的用于定制框架的方法通常来自于框架本身而不是从用户的应用程序代码调用来的。 该框架通常在协调和排序应用程序活动中扮演主程序的角色。 控制权的这种反转使框架有能力充当可扩展的框架。 用户提供的方法为框架中的特定应用程序量身制定泛化算法。
>
> \-- [Ralph Johnson and Brian Foote][6]
### 重构测试用例
因此,代码需要重构。 摆脱对内部时钟的依赖(**DateTime** 系统实用程序):
```
` DateTime time = new DateTime();`
```
删除上述代码在你的文件中应该是第7行。 通过将输入参数 **DateTime** 时间添加到 **GetDayOrNight** 方法,进一步重构代码。
这是重构类 **DayOrNightUtility.cs**:
```
using System;
namespace app {
public class DayOrNightUtility {
public string GetDayOrNight(DateTime time) {
string dayOrNight = "Nighttime";
if(time.Hour &gt;= 7 &amp;&amp; time.Hour &lt; 19) {
dayOrNight = "Daylight";
}
return dayOrNight;
}
}
}
```
重构代码需要更改单元测试。 需要准备 **nightHour****dayHour** 的测试数据,并将这些值传到**GetDayOrNight** 方法中。 以下是重构的单元测试:
```
using System;
using Xunit;
using app;
namespace unittest
{
public class UnitTest1
{
DayOrNightUtility dayOrNightUtility = [new][7] DayOrNightUtility();
DateTime nightHour = [new][7] DateTime(2019, 08, 03, 19, 00, 00);
DateTime dayHour = [new][7] DateTime(2019, 08, 03, 07, 00, 00);
[Fact]
public void Given7pmReturnNighttime()
{
var expected = "Nighttime";
var actual = dayOrNightUtility.GetDayOrNight(nightHour);
Assert.Equal(expected, actual);
}
[Fact]
public void Given7amReturnDaylight()
{
var expected = "Daylight";
var actual = dayOrNightUtility.GetDayOrNight(dayHour);
Assert.Equal(expected, actual);
}
}
}
```
### 经验教训
在继续开发这种简单的场景之前,请先回顾复习一下本次练习中所学到的东西。
运行无法测试的代码,很容易在不经意间制造陷阱。 从表面上看这样的代码似乎可以正常工作。但是遵循测试驱动开发TDD的实践首先描述期望结果---执行测试---暴露了代码中的严重问题。
这表明 TDD 是确保代码不会太凌乱的理想方法。 TDD 指出了一些问题区域,例如缺乏单一责任和存在隐藏输入。 此外TDD 有助于删除不确定性代码,并用行为明确的完全可测试代码替换它。
最后TDD 帮助交付易于阅读、逻辑易于遵循的代码。
在本系列的下一篇文章中,我将演示如何使用在本练习中创建的逻辑来实现功能代码,以及如何进行进一步的测试使其变得更好。
--------------------------------------------------------------------------------
via: https://opensource.com/article/19/9/mutation-testing-example-failure-experimentation
作者:[Alex Bunardzic][a]
选题:[lujun9972][b]
译者:[Morisun029](https://github.com/译者ID)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://opensource.com/users/alex-bunardzichttps://opensource.com/users/jocunddew
[b]: https://github.com/lujun9972
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/rh_003588_01_rd3os.combacktoschoolseriesk12_rh_021x_0.png?itok=fvorN0e- (Digital hand surrounding by objects, bike, light bulb, graphs)
[2]: https://opensource.com/article/19/9/mutation-testing-example-part-1-how-leverage-failure
[3]: https://opensource.com/article/19/8/mutation-testing-evolution-tdd
[4]: https://en.wikipedia.org/wiki/Separation_of_concerns
[5]: https://martinfowler.com/bliki/InversionOfControl.html
[6]: http://www.laputan.org/drc/drc.html
[7]: http://www.google.com/search?q=new+msdn.microsoft.com

View File

@ -0,0 +1,223 @@
[#]: collector: (lujun9972)
[#]: translator: (wxy)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (7 steps to securing your Linux server)
[#]: via: (https://opensource.com/article/19/10/linux-server-security)
[#]: author: (Patrick H. Mullins https://opensource.com/users/pmullins)
安全强化你的 Linux 服务器的七个步骤
======
> 通过七个简单的步骤来加固你的 Linux 服务器。
![computer servers processing data][1]
这篇入门文章将向你介绍基本的 Linux 服务器安全知识。虽然主要针对 Debian/Ubuntu但是你可以将此处介绍的所有内容应用于其他 Linux 发行版。我也鼓励你研究这份材料,并在适用的情况下进行扩展。
### 1、更新你的服务器
保护服务器安全的第一件事是更新本地存储库,并通过应用最新的修补程序来升级操作系统和已安装的应用程序。
在 Ubuntu 和 Debian 上:
```
$ sudo apt update && sudo apt upgrade -y
```
在 Fedora、CentOS 或 RHEL
```
$ sudo dnf upgrade
```
### 2、创建一个新的特权用户
接下来,创建一个新的用户帐户。永远不要以 root 身份登录服务器,而是创建你自己的帐户(用户),赋予它 `sudo` 权限,然后使用它登录你的服务器。
首先创建一个新用户:
```
$ adduser <username>
```
通过将 `sudo` 组(`-G`)附加(`-a`)到用户的组成员身份里,从而授予新用户帐户 `sudo` 权限:
```
$ usermod -a -G sudo <username>
```
### 3、上传你的 SSH 密钥
你应该使用 SSH 密钥登录到新服务器。你可以使用 `ssh-copy-id` 命令将[预生成的 SSH 密钥][2]上传到你的新服务器:
```
$ ssh-copy-id <username>@ip_address
```
现在,你无需输入密码即可登录到新服务器。
### 4、安全强化 SSH
接下来,进行以下三个更改:
* 禁用 SSH 密码认证
* 限制 root 远程登录
* 限制对 IPv4 或 IPv6 的访问
使用你选择的文本编辑器打开 `/etc/ssh/sshd_config` 并确保以下行:
```
PasswordAuthentication yes
PermitRootLogin yes
```
改成这样:
```
PasswordAuthentication no
PermitRootLogin no
```
接下来,通过修改 `AddressFamily` 选项将 SSH 服务限制为 IPv4 或 IPv6。要将其更改为仅使用 IPv4对大多数人来说应该没问题请进行以下更改
```
AddressFamily inet
```
重新启动 SSH 服务以启用你的更改。请注意,在重新启动 SSH 服务器之前,与服务器建立两个活动连接是一个好主意。有了这些额外的连接,你可以在重新启动出错的情况下修复所有问题。
在 Ubuntu 上:
```
$ sudo service sshd restart
```
在 Fedora 或 CentOS 或任何使用 Systemd 的系统上:
```
$ sudo systemctl restart sshd
```
### 5、启用防火墙
现在你需要安装防火墙、启用防火墙并对其进行配置以仅允许你指定的网络流量。Ubuntu 上的)[简单的防火墙][3]UFW是一个易用的 iptables 界面,可大大简化防火墙的配置过程。
你可以通过以下方式安装 UFW
```
$ sudo apt install ufw
```
默认情况下UFW 拒绝所有传入连接,并允许所有传出连接。这意味着服务器上的任何应用程序都可以访问互联网,但是任何尝试访问服务器的内容都无法连接。
首先,确保你可以通过启用对 SSH、HTTP 和 HTTPS 的访问来登录:
```
$ sudo ufw allow ssh
$ sudo ufw allow http
$ sudo ufw allow https
```
然后启用 UFW
```
$ sudo ufw enable
```
你可以通过以下方式查看允许和拒绝了哪些服务:
```
$ sudo ufw status
```
如果你想禁用 UFW可以通过键入以下命令来禁用
```
$ sudo ufw disable
```
你还可以(在 RHEL/CentOS 上)使用 [firewall-cmd][4],它已经安装并集成到某些发行版中。
### 6、安全 Fail2ban
[Fail2ban][5] 是一种用于检查服务器日志以查找重复或自动攻击的应用程序。如果找到任何攻击,它会更改防火墙以永久地或在指定的时间内阻止攻击者的 IP 地址。
你可以通过键入以下命令来安装 Fail2ban
```
$ sudo apt install fail2ban -y
```
然后复制随附的配置文件:
```
$ sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
```
重启 Fail2ban
```
$ sudo service fail2ban restart
```
这样就行了。该软件将不断检查日志文件以查找攻击。一段时间后,该应用程序将建立相当多的封禁的 IP 地址列表。你可以通过以下方法查询 SSH 服务的当前状态来查看此列表:
```
$ sudo fail2ban-client status ssh
```
### 7、移除无用的网络服务
几乎所有 Linux 服务器操作系统都启用了一些面向网络的服务。你可能希望保留其中大多数,然而,有一些你或许希望删除。你可以使用 `ss` 命令查看所有正在运行的网络服务LCTT 译注:应该是只保留少部分,而所有可用确认无关的、无用的服务都应该停用或删除。)
```
$ sudo ss -atpu
```
`ss` 的输出将取决于你的操作系统。这是一个可能的示例。它显示 SSH`sshd`)和 Ngnix`nginx`)服务正在侦听网络并准备连接:
```
tcp LISTEN 0 128 *:http *:* users:(("nginx",pid=22563,fd=7))
tcp LISTEN 0 128 *:ssh *:* users:(("sshd",pid=685,fd=3))
```
删除未使用的服务的方式因你的操作系统及其使用的程序包管理器而异。
要删除 Debian / Ubuntu 上未使用的服务:
```
$ sudo apt purge <service_name>
```
要在 Red Hat/CentOS 上删除未使用的服务:
```
$ sudo yum remove <service_name>
```
再次运行 `ss -atup` 以确认这些未使用的服务没有安装和运行。
### 总结
本教程介绍了加固 Linux 服务器所需的最起码的措施。可以并且应该根据服务器的使用方式启用其他安全层。这些安全层可以包括诸如各个应用程序配置、入侵检测软件IDS以及启用访问控制例如双因素身份验证之类的东西。
--------------------------------------------------------------------------------
via: https://opensource.com/article/19/10/linux-server-security
作者:[Patrick H. Mullins][a]
选题:[lujun9972][b]
译者:[wxy](https://github.com/wxy)
校对:[校对者ID](https://github.com/校对者ID)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出
[a]: https://opensource.com/users/pmullins
[b]: https://github.com/lujun9972
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/server_data_system_admin.png?itok=q6HCfNQ8 (computer servers processing data)
[2]: https://opensource.com/article/19/4/ssh-keys-seahorse
[3]: https://launchpad.net/ufw
[4]: https://www.redhat.com/sysadmin/secure-linux-network-firewall-cmd
[5]: https://www.fail2ban.org/wiki/index.php/Main_Page