目录
3. sudo命令 进程隔离 有限硬件访问权限以及保持系统同步
第十五章 Bash Shell脚本编写 基础篇
1.特性与功能
假设您要查找文件名,检查关联文件是否存在,然后做出相应的响应,显示一条消息,确认或不确认文件的存在。如果只需要执行一次,则只需在终端上键入命令序列即可。但是,如果您需要多次执行此操作,则自动化是必经之路。为了使命令集自动化,您将需要学习如何编写shell脚本,其中最常用的是与bash一起使用。该图说明了部署脚本的一些好处。
注意:在本章和下一章中讨论的许多主题已经在前面介绍了,同时讨论了可以在命令行完成的事情。我们选择重复其中的一些讨论,以使脚本编写各部分保持独立,因此重复是有意的,而不仅仅是草率的编辑。

命令解释器的任务是执行脚本中跟在其后的语句。常用的解释器包括:/ usr / bin / perl,/ bin / bash,/ bin / csh,/ usr / bin / python 和/ bin / sh。
在终端窗口上键入较长的命令序列可能很复杂,耗时且容易出错。通过部署shell脚本,使用命令行成为启动复杂步骤序列的有效且快速的方法。Shell脚本保存在文件中的事实也使使用它们轻松创建新的脚本变体并与多个用户共享标准过程变得容易。
Linux提供了多种选择。/ etc / shells中列出了系统上可用的确切信息。典型的选择是:
/ bin / sh
/ bin / bash
/ bin / tcsh
/ bin / csh
/ bin / ksh
/ bin / zsh
大多数Linux用户使用默认的bash shell,但是具有较长UNIX背景和其他shell的用户可能要覆盖默认的bash shell。
要了解有关UNIX Shell的更多信息,可以阅读此简短的“ Command Shells历史 ”文章。

请记住,从我们前面的讨论中可以看到,shell是一个命令行解释器,它为终端窗口提供用户界面。即使在没有终端窗口的非交互式会话中,它也可以用来运行脚本,就像直接键入命令一样。例如,键入:find . -name "*.c" -ls 与执行包含以下行的脚本文件的功能相同:
#!/bin/bash
find . -name "*.c" -ls
在UNIX环境中开发了任何种类的脚本的任何人都应该识别第一行中的#!/ bin / bash。脚本的第一行以#!开头。,包含将在文件上使用的命令解释器的完整路径(在本例中为/ bin / bash)。正如我们指出的,你可以使用的脚本语言相当多的选择 /usr/bin/perl, /bin/csh, /usr/bin/python, etc.
让我们编写一个简单的bash脚本,该脚本在屏幕上显示一行消息。两种类型:
cat > hello.sh
#!/bin/bash
echo "Hello Linux Foundation Student"
然后按ENTER和CTRL-D保存文件,或者只是在您喜欢的文本编辑器中创建hello.sh。然后,键入chmod + x hello.sh以使该文件对所有用户都可执行。
然后,您可以通过键入./hello.sh 或 执行以下操作来运行脚本:
bash hello.sh
Hello Linux Foundation Student
注意:如果使用第二种形式(bash <shell script>),则不必使文件可执行。

现在,让我们看看如何使用bash脚本创建一个更具交互性的示例。系统将提示用户输入一个值,然后将其显示在屏幕上。该值存储在一个临时变量name中。我们可以通过在变量名称(例如$ name)前使用$来引用shell变量的值。要创建此脚本,需要在您喜欢的编辑器中创建一个名为 getname.sh 的文件,其内容如下:
#!/bin/bash
# Interactive reading of a variable
echo "ENTER YOUR NAME"
read name
# Display variable input
echo The name given was :$name
再次通过执行chmod + x getname.sh使其可执行。
在上面的示例中,当用户键入./getname.sh 并执行了脚本时,将向用户提示字符串 ENTER YOUR NAME。然后,用户需要输入一个值,然后按Enter键。该值将被打印出来。
注意:井号/英镑符号/数字符号(#)用于在脚本中开始注释,可以放置在行中的任何位置(该行的其余部分被视为注释)。但是,请注意#的特殊组合 #!,在第一行中,是唯一的例外。

所有Shell脚本在完成执行后都会生成一个返回值,可以使用exit 语句显式设置该返回值。返回值允许一个进程监视另一个进程的退出状态,通常以父子关系进行。知道过程如何终止将使您能够采取必要的步骤,取决于成功或失败的步骤。

在执行脚本时,可以检查特定的值或条件,并返回成功或失败的结果。按照约定,成功返回0,失败返回非零值。演示成功和失败完成的一种简单方法是在一个存在的文件上执行ls,在不存在的文件上执行ls,返回值存储在以$?表示的环境变量中。:
$ ls /etc/logrotate.conf
/etc/logrotate.conf
$ echo $?
0
在这个例子中,该系统能够定位该文件/etc/logrotate.conf中 和ls 返回的值0表示成功。在不存在的文件上运行时,它返回2。应用程序通常将这些返回值转换为用户容易理解的有意义的消息。

2. 脚本句法规则
脚本要求您遵循标准的语言语法。规则描述了如何定义变量以及如何构造和格式化允许的语句等。该表列出了bash脚本中的一些特殊字符用法:

脚本还可以理解其他特殊字符以及字符组合和构造,例如(..),{..} ,[..] , &&,|| ,', “,$((...)),其中一些我们将在后面讨论。
有时,命令太长,以至于不能轻松地在一行上键入或者难以理解(即使对命令行的长度没有实际的限制)。
在这种情况下,连接运算符(\)是反斜杠字符,用于在几行上继续执行长命令。
这是使用Debian软件包管理在系统上安装一长包软件包的命令示例:
$~/> cd $HOME
$~/> sudo apt-get install autoconf automake bison build-essential
chrpath curl diffstat emacs flex gcc-multilib g++-multilib \
libsdl1.2-dev libtool lzop make mc patch \
screen socat sudo tar texinfo tofrodos u-boot-tools unzip \
vim wget xterm zip
该命令分为多行,以使其易于阅读和理解。每行末尾的\运算符使shell组合(连接)多行并将其作为一个命令执行。

用户有时需要组合多个命令和语句,甚至根据它们之间使用的运算符的行为有条件地执行它们。此方法称为命令链接。
有多种执行此操作的方法,具体取决于您要执行的操作。的; (分号)字符用于分隔这些命令并顺序执行它们,就好像它们是在单独的行中键入的一样。无论前面的命令是否成功,都会执行每个随后的命令。
因此,即使下面的命令失败,以下示例中的三个命令也将全部执行:
$ make ; make install ; make clean
但是,当较早的命令失败时,您可能希望中止后续的命令。您可以使用&&(和)运算符执行此操作,如下所示:
$ make && make install && make clean
如果第一个命令失败,将永远不会执行第二个命令。最后的改进是使用||。(或)运算子,例如:
$ cat file1 || cat file2 || cat file3
在这种情况下,您将继续进行直到成功为止,然后停止执行任何其他步骤。

大多数操作系统接受来自键盘的输入并在终端上显示输出。但是,在shell脚本中,您可以将输出发送到文件。将输出转移到文件的过程称为输出重定向。在前面有关如何使用命令行的部分中,我们已经使用了此功能。
所述 > 字符用于写输出到文件。例如,以下命令将free的输出发送到 /tmp/free.out:
$ free> /tmp/free.out
要检查/tmp/free.out的内容 ,请在命令提示符下键入cat /tmp/free.out。
如果文件存在,两个>字符(>>)会将输出追加到文件中,如果文件不存在,则其作用类似于>。
正如可以将输出重定向到文件一样,可以从文件中读取命令的输入。从文件读取输入的过程称为输入重定向,并使用<字符。
以下三个命令(使用wc程序计算文件中的行,单词和字符的数量)完全等效,并且涉及输入重定向,以及一个对文件内容进行操作的命令:
$ wc < /etc/passwd
49 105 2678 /etc/passwd
$ wc /etc/passwd
49 105 2678 /etcpasswd
$ cat /etc/passwd | wc
49 105 2678
Shell脚本执行命令序列和其他类型的语句。这些命令可以是:
- 编译的应用程序
- 内置bash命令
- Shell脚本或其他解释语言(例如perl和Python)的脚本。
编译的应用程序是二进制可执行文件,通常位于文件系统中的/ usr / bin等知名目录中。Shell脚本始终可以访问诸如rm,ls, df,vi和gzip之类的应用程序,这些应用程序是从低级编程语言(如C)编译而成的程序。
另外,bash具有许多内置命令,这些命令只能用于显示终端shell或shell脚本中的输出。有时,这些命令与系统上的可执行程序具有相同的名称,例如echo可能导致细微的问题。bash的内置命令包括和cd, pwd, echo, read, logout, printf, let,和 ulimit。 因此,与/ bin / echo相比, 命令的内置版本(例如echo)可以预期会有稍微不同的行为 。
bash内置命令的完整列表可以在bash手册页中找到,也可以通过简单地键入 help来找到,我们将在下一页中进行回顾。

在我们前面的关于如何在Linux系统上获得帮助的讨论中,我们已经列举了哪些命令内置了bash版本。再次,这是截屏,列出了哪些命令可用。

用户通常需要将参数值传递给脚本,例如文件名,日期等。脚本将采用不同的路径或根据传递给它们的参数(命令参数)获得不同的值。这些值可以是文本或数字,如下所示:
$ ./script.sh / tmp
$ ./script.sh 100 200
在脚本中,参数或自变量用 $以及数字或特殊字符表示。该表列出了其中一些参数。

如果您输入图中所示的脚本,请使用chmod + x param.sh使该脚本可执行。然后,运行脚本,为其提供几个参数,如图所示。脚本的处理过程如下:
$0 prints the script name: param.sh
$1 prints the first parameter: one
$2 prints the second parameter: two
$3 prints the third parameter: three
$* prints all parameters: one two three four five
The final statement becomes: All done with param.sh

有时,您可能需要将一个命令的结果替换为另一个命令的一部分。它可以通过两种方式完成:
- 通过将内部命令包含在$()中
- 通过用反引号(`)包围内部命令
第二种是反引号形式,在新的脚本和命令中已弃用。无论使用哪种方法,都将在新启动的Shell环境中执行指定的命令,并且将在完成命令替换的地方插入Shell的标准输出。
实际上,任何命令都可以通过这种方式执行。虽然这两种方法都支持命令替换,但是 $() 方法允许命令嵌套。新脚本应始终使用这种更现代的方法。例如:
$ ls / lib / modules / $(uname -r) /
在上面的示例中,命令uname -r的输出 (将类似于4.18.2)插入到ls命令的参数中。

大多数脚本使用包含值的变量,该值可以在脚本中的任何位置使用。这些变量可以是用户定义的,也可以是系统定义的。许多应用程序使用此类环境变量(已在第12章:用户环境中进行了详细介绍)来提供输入,验证和控制行为。
如前所述,标准环境变量的一些示例是HOME,PATH和HOST。引用时,环境变量必须以$符号为前缀,如 $ HOME一样。您可以查看和设置环境变量的值。例如,以下命令显示存储在PATH变量中的值:
$ echo $ PATH
但是,设置或修改变量值时不需要前缀。例如,以下命令将MYCOLOR 变量的值设置为blue:
$ MYCOLOR = blue
你可以得到的环境变量列表与env, set, 或printenv命令。

当我们 在“ 用户环境 ” 一节中讨论了环境变量的导出时,值得在编写bash脚本的上下文中回顾此主题。
默认情况下,在脚本中创建的变量仅可用于该脚本的后续步骤。任何子进程(子外壳程序)都无法自动访问这些变量的值。为了使它们可用于子进程,必须使用export语句将它们提升为环境变量,如下所示:
export VAR=value 或 VAR=value ; export VAR
虽然允许子进程修改导出变量的值,但父进程将看不到任何更改;导出的变量不共享,它们仅被复制和继承。
键入不带参数的export将给出所有当前的环境变量的列表。

函数是实现一组操作的代码块。函数对于多次执行过程(可能使用不同的输入变量)很有用。函数通常也称为子例程。在脚本中使用函数需要两个步骤:
- 声明功能
- 调用函数。
函数声明需要一个用于调用它的名称。正确的语法是:
function_name () {
command...
}
例如,以下函数名为 display:
display () {
echo "This is a sample function"
}
该函数可以任意长,并且可以有很多语句。定义后,以后可以根据需要多次调用该函数。在图中所示的完整示例中,我们还展示了一个常用的改进:如何将参数传递给函数。第一个参数可以称为 $1 , 第二个参数 $2, 以此类推。

3. Shell 脚本结构
使用if语句进行条件决策 是任何有用的编程或脚本语言都必须具备的基本结构。
使用if语句时,后续操作取决于对指定条件的评估,例如:
- 数字或字符串比较
- 命令的返回值(0表示成功)
- 文件存在或权限。
紧凑形式的if 语句的语法 为:
if TEST-COMMANDS; then CONSEQUENT-COMMANDS; fi
更一般的定义是:
if condition
then
statements
else
statements
fi

在以下示例中,一条 if语句检查是否存在某个文件,如果找到该文件,则显示一条消息,指示成功或失败:
if [ -f "$1" ]
then
echo file "$1 exists"
else
echo file "$1" does not exist
fi
我们确实还应该首先检查是否有传递给脚本的参数($ 1),如果没有,则中止。
请注意,使用方括号([])来描述测试条件。您可以执行许多其他类型的测试,例如检查两个数字是否相等,大于或小于,然后据此做出决定。我们将讨论其他测试。
在现代脚本中,您可能会在 [[-f / etc / passwd]] 中看到双括号。这不是错误。这样做绝对是没有错的,并且避免了一些细微的问题,例如引用空的环境变量而不用双引号引起来;我们在这里不会谈论这个。
您可以使用 elif 语句执行更复杂的测试,并采取适当的措施。基本语法为:
if [ sometest ] ; then
echo Passed test1
elif [ somothertest ] ; then
echo Passed test2
fi
在所示的示例中,我们使用字符串测试,稍后将对其进行说明,并显示如何使用read 语句引入环境变量。

bash提供了一组文件条件,可与if 语句一起使用,包括表中的条件。
您可以使用if 语句测试文件属性,例如:
- 文件或目录的存在
- 读写权限
- 可执行权限。
例如,在以下示例中:
if [ -x /etc/passwd ] ; then
ACTION
fi
在if 语句检查文件/ etc / passwd文件是可执行的,它不是。请注意以下非常普遍的做法:
; then
与if语句在同一行。
您可以键入以下内容查看文件条件的完整列表:
man 1 test.

布尔表达式的计算结果为TRUE或FALSE,并使用表中列出的各种布尔运算符获得结果。
请注意,如果您将多个条件与&& 运算符捆绑在一起,则 条件评估为false时,处理将立即停止。例如,如果您有A && B && C并且A为true但B为false,则C将永远不会执行。
同样,如果您使用 || 运算符,只要一切正确,处理就会停止。例如,如果您有A || B || C和A为假,B为真,您也永远不会执行C。
布尔表达式返回TRUE或FALSE。在处理包括字符串或数字以及文件在内的多种数据类型时,我们可以使用此类表达式。例如,要检查文件是否存在,请使用以下条件测试:
[ -e <filename> ]
同样,要检查 number1的值 是否大于number2的值 ,请使用以下条件测试:
[ $number1 -gt $number2 ]
如果 number1大于 number2,则运算符 -gt返回TRUE 。
您可以使用if 语句使用运算符== (两个等号)比较字符串。语法如下:
if [ string1 == string2 ] ; then
ACTION
fi
请注意,使用一个=符号也可以,但是有些人认为它已被弃用。现在来看一个测试字符串的例子。
在此处所示的示例中,if语句用于比较用户提供的输入并相应地显示结果。

您可以将特殊定义的运算符与if语句一起使用以比较数字。下表列出了可用的各种运算符:

比较数字的语法如下:
exp1 -op exp2
现在让我们考虑一个使用各种运算符比较数字的示例:

算术表达式可以通过以下三种方式求值(空格很重要!):
- 使用expr 实用工具 expr 是一个标准的程序,但已过时。语法如下:
expr 8 + 8
echo $(expr 8 + 8) - 使用$((...)) 语法 这是内置的Shell格式。语法如下:
echo $((x + 1)) - 使用内置的shell命令let。 语法如下:
let x=( 1 + 2 ); echo $x
在现代的shell脚本中,使用 expr 更好地替换为var = $((...))。

第十六章 Bash Shell脚本编写 高级篇
1. 字符串操作
让我们更深入地了解如何在脚本中使用字符串。
字符串变量包含一系列文本字符。它可以包括字母,数字,符号和标点符号。一些示例包括:
abcde, 123, abcde 123, abcde-123, &acbde=%123.
字符串运算符包括进行比较,排序和查找长度的运算符。下表演示了一些基本的字符串运算符的用法:

在第一个示例中,我们将第一个字符串与第二个字符串进行比较,并使用if 语句显示适当的消息 。

在第二个示例中,我们传入文件名,然后查看该文件在当前目录中是否存在。

有时,您可能不需要比较或使用整个字符串。要提取字符串的前 n个 字符,我们可以指定: $ {string:0:n}。在这里, 0 是字符串中的偏移量(即,从哪个字符开始),提取需要开始,而 n 是要提取的字符数。
要提取点(.)之后的字符串中的所有字符,请使用以下表达式: $ {string#*.}。
2. case条件语句
case 语句在场景中的变量的实际值可以导致不同的执行路径使用。 case语句通常用于处理命令行选项。
以下是使用case语句的一些优点:
- 它更容易读写。
- 它是嵌套的多层if-then-else-fi 代码块的不错选择。
- 它使您可以一次将一个变量与多个值进行比较。
- 它降低了程序的复杂性。
case语句的基本结构如下所示:
case expression in
pattern1) execute commands;;
pattern2) execute commands;;
pattern3) execute commands;;
pattern4) execute commands;;
* ) execute some default commands or nothing ;;
esac

这是一个case的例子:

3. loop循环语句
通过使用循环构造,您通常可以在选择数据值(例如单个文件)时重复执行一行或多行代码。通常,您需要执行此操作,直到条件测试返回true或false为止。
大多数编程语言通常使用三种类型的循环:
- for
- while
- until
所有这些循环都易于用于重复一组语句,直到退出条件为真为止。

for循环 项目列表中的每个元素进行操作。for循环的语法为:
for variable-name in list
do
execute one iteration for each item in the list until the list is finished
done
在这种情况下,您将根据需要替换变量名和列表(请参见示例)。与其他循环构造一样,重复的语句应该用do和done括起来。
屏幕截图显示了for循环的示例,该循环显示数字1到10的和。

只要控制命令返回true ,while循环就会重复一组语句。语法为:
while condition is true
do
Commands for execution
----
done
需要重复的命令集应该放在do和done之间。您可以使用任何命令或运算符作为条件。通常,它包含在方括号([])中。
屏幕截图显示了一个while循环示例,用于计算数字的阶乘。你知道为什么要计算21!给出错误的结果?

只要控制命令为假,until 循环将重复一组语句。因此,它基本上与while循环相反。语法为:
until condition is false
do
Commands for execution
----
done
与while循环类似,需要重复的命令集应该放在do 和 done之间 。您可以使用任何命令或运算符作为条件。
屏幕截图显示了 until 循环的示例,该循环再次计算阶乘;它与while 循环的测试用例仅稍有不同 。

4. 脚本调试
在使用脚本和命令时,您可能会遇到错误。这些可能是由于脚本中的错误(例如语法不正确)或其他因素(例如缺少文件或执行操作的权限不足)引起的。可能会使用特定的错误代码报告这些错误,但通常只会产生不正确或令人困惑的输出。那么,您如何识别和修复错误?
调试可帮助您解决和解决此类错误,并且是系统管理员执行的最重要任务之一。
在修复错误(或错误)之前,了解其来源至关重要。
您可以通过执行bash –x ./script_file或在脚本的某些部分使用 set -x 和 set + x来在调试模式下运行bash脚本。调试模式有助于识别错误,因为:
- 它使用+ 字符跟踪并在每个命令前面加上前缀。
- 在执行每个命令之前,它将显示每个命令。
- 它可以使用以下命令仅调试脚本的选定部分(如果需要):
set -x # turns on debugging
...
set +x # turns off debugging
此处显示的屏幕快照演示了一个脚本,如果在命令行中使用任何参数运行该脚本,则该脚本将在调试模式下运行。

在UNIX / Linux中,所有运行的程序在启动时都会得到三个打开的文件流,如下表所示:

使用重定向,我们可以将stdout和stderr输出流保存到一个文件或两个单独的文件中,以便在执行程序或命令后进行后续分析。
屏幕快照显示了一个带有简单错误的Shell脚本,然后运行该脚本并将错误输出转移到 error.log。使用cat 显示错误日志的内容会增加调试功能。您看到如何修复脚本了吗?

5. 更多实用技巧
考虑一种情况,您要从具有10,000条记录的文件中检索100条记录。在您对提取的信息进行进一步处理的同时,您将需要一个位置来将提取的信息存储在一个临时文件中。
临时文件(和目录)用于短期存储数据。通常,将其进行排列,以使使用它们的程序终止时这些文件消失。尽管您也可以使用touch 创建临时文件,但在某些情况下,这可能使黑客更容易获得对您数据的访问权限。如果临时文件的名称和文件位置是可预测的,则尤其如此。
最佳实践是为临时存储创建随机且不可预测的文件名。一种方法是使用mktemp 实用程序,如以下示例所示。
该XXXXXXXX由mktemp的替换 用随机字符实用程序以确保临时文件的名称不容易预测,并且只在程序中已知的。

创建临时文件的草率行为可能会导致意外或由于恶意行为而造成实际损失。例如,如果有人要创建一个从root使用的已知临时文件到/ etc / passwd文件的符号链接,如下所示:
$ ln -s / etc / passwd / tmp / tempfile
如果以root身份运行的脚本的行如下所示,则可能会出现大问题:
echo $VAR > /tmp/tempfile
密码文件将被临时文件内容覆盖。
为避免这种情况,请确保通过将以下行替换为以下行来随机化临时文件名:
TEMP = $(mktemp /tmp/tempfile.XXXXXXXX)echo
$ VAR> $ TEMP
请注意,该屏幕截图显示了不同日期的类似名称的临时文件,但其中包含随机生成的字符。

某些命令(如find)将产生大量的输出,这可能会使控制台不堪重负。为了避免这种情况,我们可以将大型输出重定向到名为/ dev / null的特殊文件(设备节点) 。该伪文件也称为位桶或黑洞。
写入其中的所有数据都将被丢弃,并且写入操作绝不会返回失败条件。使用适当的重定向运算符,它可以使输出从通常会向stdout和/或stderr生成输出的命令中消失:
$ ls -lR / tmp> / dev / null
在上面的命令中,将忽略整个标准输出流,但是任何错误仍将出现在控制台上。但是,如果这样做:
$ ls -lR / tmp>&/ dev / null
这两个 标准输出 和 标准错误将被倾入 / dev / null 。

执行以下任务时生成随机数和其他随机数据通常很有用:
- 执行与安全相关的任务
- 重新初始化存储设备
- 擦除和/或隐藏现有数据
- 生成无意义的数据用于测试。
可以使用 $RANDOM 环境变量(从L inux内核的内置随机数生成器派生)或OpenSSL库函数(使用FIPS140 (联邦信息处理标准)算法生成随机数)生成此类随机数。加密数字
要了解FIPS140,请阅读Wikipedia的“ FIPS 140-2 ”文章。
该示例说明如何轻松使用环境变量方法生成随机数。

一些服务器具有硬件随机数发生器,其将不同类型的噪声信号作为输入,例如热噪声和光电效应。换能器将该噪声转换为电信号,再由AD转换器将其转换为数字。该数字被认为是随机的。但是,大多数普通计算机不包含此类专用硬件,而是依靠引导过程中创建的事件来创建所需的原始数据。
无论使用这两个源中的哪一个,系统都会维护这些数字数字/随机位的所谓熵池。从此熵池创建随机数。
Linux内核提供了 / dev / random和/ dev / urandom设备节点,它们使用熵池提供随机数,这些随机数是从熵池中估计的噪声位数中得出的。
/ dev / random 用于需要高质量随机性的地方,例如一次性填充或密钥生成,但是提供值相对较慢。 / dev / urandom更快且适合(足够好)用于大多数加密目的。
此外,当熵池为空时,/ dev / random会被阻止并且不会生成任何数字,直到收集到其他环境噪声(网络流量,鼠标移动等)为止,而/ dev / urandom会重用内部池以产生更多信息伪随机位。

第十七章 打印操作
一些打印机的操作,尝试了,不通用。不做翻译。
第十八章 本地安全法则
1. 了解Linux的安全性
Linux内核允许经过正确身份验证的用户访问文件和应用程序。虽然每个用户都由一个唯一的整数(用户ID或UID)标识,但一个单独的数据库会将用户名与每个UID关联。创建帐户后,新的用户信息将添加到用户数据库,并且必须创建用户的主目录,并在其中填充一些基本文件。命令行程序(例如useradd和userdel )以及 GUI工具用于创建和删除帐户。

对于每个用户,在/ etc / passwd文件中维护以下七个字段:

默认情况下,Linux区分几种帐户类型,以隔离进程和工作负载。Linux有四种类型的帐户:
- root
- System
- Normal
- Network
为了安全的工作环境,建议为帐户授予可能和必要的最低特权,并删除不活动的帐户。last实用工具显示每个用户最后一次登录系统的时间,可用于帮助识别潜在的非活动帐户,这些帐户是系统删除的候选对象。

请记住,在多用户业务系统上使用的实践比在仅影响临时用户的个人桌面系统上可以使用的实践更为严格。对于安全性尤其如此。我们希望向您展示适用于可在所有系统上使用的企业服务器的实践,但是要理解,您可以选择在自己的个人系统上放宽这些规则。
root是Linux / UNIX系统上最特权的帐户。该帐户可以执行系统管理的所有方面,包括添加帐户,更改用户密码,检查日志文件,安装软件等。使用此帐户时,必须格外小心。它没有任何安全限制。
当您以root身份或以root用户身份登录时,shell提示符将显示' # '(如果您使用的是bash并且您尚未自定义该提示符,则正如我们在本课程的其他地方所讨论的那样)。此约定旨在向您警告此帐户的绝对权力。
2. 如何使用root权限
需要root用户特权才能执行以下操作:
- 创建,删除和管理用户帐户
- 管理软件包
- 删除或修改系统文件
- 重新启动系统服务。
可以允许Linux发行版的普通帐户用户安装软件包,更新某些设置,使用某些外围设备以及对系统进行各种更改。但是,需要root特权才能执行管理任务,例如重新启动服务,手动安装软件包以及管理文件系统中普通用户目录之外的部分。

普通帐户用户可以执行一些需要特殊权限的操作;但是,系统配置必须允许行使这种能力。
SUID(执行时设置所有者用户ID,类似于Windows的“运行方式”功能)是赋予文件的一种特殊的文件权限。SUID为用户提供临时权限,使其可以使用文件所有者(可能是root)的权限而不是用户所拥有的权限来运行程序。
下表提供了不需要root特权的操作示例:

3. sudo命令 进程隔离 有限硬件访问权限以及保持系统同步
在Linux中,您可以使用su或sudo临时向普通用户授予root访问权限。这些方法实际上是完全不同的。下面列出了两个命令之间的区别:

sudo 能够跟踪获得根访问权限的失败尝试。用户使用sudo的授权基于存储在/ etc / sudoers文件和/etc/sudoers.d目录中的配置信息。
尝试执行sudo bash 而不成功验证用户身份时,系统日志文件(通常为/ var / log / secure) 中将显示以下消息 :
authentication failure; logname=op uid=0 euid=0 tty=/dev/pts/6 ruser=op rhost= user=op
conversation failed
auth could not identify password for [op]
op : 1 incorrect password attempt ;
TTY=pts/6 ; PWD=/var/log ; USER=root ; COMMAND=/bin/bash
每当 调用sudo时,触发器就会查看/ etc / sudoers 和/etc/sudoers.d 的文件,以确定用户是否有权使用sudo以及他们的特权范围是什么。报告了未知的用户请求,以及即使使用sudo 也不允许用户执行的操作请求。您可以使用visudo编辑sudoers文件,以确保一次只有一个人在编辑该文件,具有适当的权限,并且拒绝写出该文件并在所做的更改有错误时退出。
条目的基本结构是:
who where =(as_whom)what
该文件中包含许多有关如何自定义的文档。现在,大多数Linux发行版都希望您在目录/etc/sudoers.d 添加一个名称与用户相同的文件。该文件包含单个用户的sudo配置,除了影响所有用户的更改外,应保持主配置文件不变。

默认情况下,sudo 命令和所有失败记录在Debian发行系列下的/var/log/auth.log中,以及在 其他系统上的/ var / log / messages 和/或 / var / log / secure 中。这是重要的安全措施,可以跟踪和负责sudo使用。消息的典型条目包含:
- 获取使用者名称
- 信息终端
- 工作目录
- 用户帐号被调用
- 带参数的命令。
运行诸如sudo whoami之类的命令将生成一个日志文件条目,例如:
Dec 8 14:20:47 server1 sudo:op:TTY = pts / 6 PWD = / var / log USER = root COMMAND = / usr / bin / whoami

Linux被认为比许多其他操作系统更安全,因为进程自然相互隔离。一个进程通常无法访问另一进程的资源,即使该进程以相同的用户权限运行。因此,Linux使得病毒和安全漏洞难以(尽管当然不是不可能)访问和攻击系统上的随机资源。
最新的限制风险的其他安全机制甚至还包括:
- 控制组(cgroups)
允许系统管理员对进程进行分组,并将有限的资源关联到每个cgroup。 - 容器
使依靠cgroups在单个系统上运行多个隔离的Linux系统(容器)成为可能。 - 虚拟化
硬件的仿真方式不仅可以隔离进程,而且可以在一个物理主机上将整个系统作为隔离和隔离的来宾(虚拟机)同时运行。

Linux以与常规文件访问极为相似的方式,限制用户对非网络硬件设备的访问。应用程序通过参与文件系统层(与文件所驻留的实际设备或硬件无关)进行交互。然后,该层将在/ dev 目录下打开与要访问的设备相对应的设备专用文件(通常称为设备节点) 。每个设备专用文件都有标准的所有者,组和世界权限字段。与访问标准文件时一样,自然会强制执行安全性。
例如,硬盘表示为/ dev / sd*。尽管root用户可以原始方式读取和写入磁盘,例如通过执行以下操作:
$ echo hello world> / dev / sda1
如图所示,标准权限使普通用户无法这样做。以这种方式写入设备可以很容易地消除存储在其上的文件系统,而如果不花大力气就无法修复。应用程序对硬盘上文件的正常读写是通过文件系统在更高级别完成的,而不是通过直接访问设备节点来完成的。

当发现Linux内核或应用程序和库中的安全性问题时,Linux发行版就具有快速反应并通过更新其软件存储库并发送通知以立即进行更新来对所有系统推出修复程序的良好记录。与安全无关的错误修复和性能改进也是如此。
但是,众所周知,许多系统没有得到足够频繁的更新,并且已经解决的问题被允许在计算机上保留很长时间。对于专有操作系统,尤其是其中用户不了解或不信任供应商的修补策略的情况尤其如此,因为有时更新可能会导致新问题并破坏现有操作。许多最成功的攻击媒介来自利用安全漏洞的漏洞,这些漏洞的修补程序是已知的,但并未得到普遍部署。
因此,最佳实践是利用Linux发行版的自动更新机制,而不要推迟更新。这样的更新将引起新的问题是非常罕见的。
4. 设置密码
系统使用用户凭证验证真实性和身份。
最初,加密的密码存储在/ etc / passwd文件中,每个人都可以读取。这样就很容易破解密码。
在现代系统上,密码实际上以加密格式存储在名为/ etc / shadow的辅助文件中。只有具有root用户访问权限的用户才能修改/读取此文件。

保护密码已成为安全性的关键要素。大多数Linux发行版都依赖于由美国国家安全局(NSA)开发的称为SHA-512(安全哈希算法512位)的现代密码加密算法来加密密码。
SHA-512算法广泛用于安全应用程序和协议。这些安全应用程序和协议包括TLS,SSL,PHP,SSH,S / MIME和IPSec。SHA-512是最受测试的哈希算法之一。
例如,如果您想尝试使用SHA-512编码,则可以使用sha512sum 程序对单词“ test”进行编码,以生成SHA-512形式(参见图形):

IT专业人员遵循几种保护每个用户的数据和密码的良好做法。
- 密码时效是一种确保用户在特定时间段后收到提示提醒他们创建新密码的提示的方法。这样可以确保密码(如果被破解)只能在有限的时间内使用。使用chage可以实现此功能, 后者可以为用户配置密码到期信息。
- 另一种方法是强制用户使用可插入身份验证模块(PAM)设置强密码。可以将PAM配置为自动验证使用passwd 实用程序创建或修改的密码是否足够强。PAM配置是使用名为pam_cracklib.so的库实现的,也可以用pam_passwdqc.so替换该库以获得更多选项。
- 还可以安装密码破解程序,例如John The Ripper,以保护密码文件并检测弱密码条目。建议在您不拥有的任何系统上安装此类工具之前,先获得书面授权。

5. 保护引导进程与硬件资源
您可以使用安全密码来保护引导过程,以防止他人绕过用户身份验证步骤。对于使用旧版本GRUB 引导加载程序1的系统, 您可以调用grub-md5-crypt ,该提示将提示您输入密码,然后进行加密。
然后,您必须通过在超时条目下方添加以下行来编辑/boot/grub/grub.conf:
password --md5 $1$74r8m1$NmkE69eAjXre.oF1k0cyk/
您也可以仅对某些引导选项而不是全部强制输入密码。
但是,对于GRUB 版本2(现在已经几乎完全接管),情况变得更加复杂。当您拥有更大的灵活性时,您可以利用更高级的功能,例如特定于用户的密码(可以是它们的常规登录密码)。另外,您永远不会直接编辑配置文件/boot/grub/grub.cfg,而是在/etc/grub.d中 编辑系统配置文件 ,然后在Linux发行版上运行update-grub或同等实用程序。
要了解更多信息,请阅读以下文章:“ GRUB 2 Password Protection ”。
当物理上可访问硬件时,安全性可能受到以下因素的损害:
- 按键记录
记录计算机用户的实时活动,包括他们按下的键。捕获的数据可以存储在本地,也可以传输到远程计算机。 - 网络嗅探
在网络上捕获和查看网络数据包级别的数据。 - 使用活动或救援磁盘引导
- 重新安装和修改磁盘内容。
您的IT安全策略应从对如何正确保护对服务器和工作站的物理访问的要求开始。对系统的物理访问使攻击者可以轻松地利用几种攻击媒介,从而使所有操作系统级别的建议都不相关。
安全准则是:
- 锁定工作站和服务器。
- 保护您的网络链接,以使不信任的人无法访问它。
- 保护您输入密码的键盘,以确保不会篡改键盘。
- 确保密码以某种方式保护BIOS,以致无法使用实时或应急DVD或USB密钥引导系统。
对于单用户计算机和家庭环境中的计算机,上述某些功能(例如防止从可移动媒体启动)可能会过多,您可以避免实施它们。但是,如果您的系统上的敏感信息需要仔细保护,则不应该存在该敏感信息,或者应该按照上述准则对其进行更好的保护。

像所有软件一样,黑客有时会发现Linux生态系统中的弱点。Linux(通常是开放源代码社区)的强项是暴露和修复此类漏洞的速度。尽管对漏洞的特定讨论不在本课程的范围内,但我们为讨论板(在“ 本地安全 ”主题下)播种了几个相关主题,以鼓励进一步讨论。
BashShell脚本编写指南
本文档详细介绍了BashShell脚本的基础和高级编写技巧,涵盖了脚本的特性、结构、字符串操作、循环语句、调试技巧以及本地安全法则等内容,是Linux系统管理和自动化任务的实用指南。
2146

被折叠的 条评论
为什么被折叠?



