分类 技术 下的文章

这篇文章能让你了解如何使用 Checksec ,来识别一个可执行文件的安全属性,了解安全属性的含义,并知道如何使用它们。

编译源代码会生成一个二进制文件(LCTT 译注:即 .o 文件)。在编译期间,你可以向 gcc 编译器提供 标志 flags ,以启用或禁用二进制文件的某些属性,这些属性与安全性相关。

Checksec 是一个漂亮的小工具,同时它也是一个 shell 脚本。Checksec 可以识别编译时构建到二进制文件中的安全属性。编译器可能会默认启用一些安全属性,你也可以提供特定的标志,来启用其他的安全属性。

本文将介绍如何使用 Checksec ,来识别二进制文件的安全属性,包括:

  1. Checksec 在查找有关安全属性的信息时,使用了什么底层的命令
  2. 在将源代码编译成二进制文件时,如何使用 GNU 编译器套件 GNU Compiler Collection (即 GCC)来启用安全属性

安装 checksec

要在 Fedora 和其他基于 RPM 的 Linux 系统上,安装 Checksec,请使用以下命令:

$ sudo dnf install checksec

对于基于 Debian 的 Linux 发行版,使用对应的 apt 命令,来安装 Checksec。

$ sudo apt install checksec

shell 脚本

在安装完 Checksec 后,能够发现 Checksec 是一个单文件的 shell 脚本,它位于 /usr/bin/checksec,并且这个文件挺大的。Checksec 的一个优点是你可以通过快速通读这个 shell 脚本,从而了解 Checksec 的执行原理、明白所有能查找有关二进制文件或可执行文件的安全属性的系统命令

$ file /usr/bin/checksec
/usr/bin/checksec: Bourne-Again shell script, ASCII text executable, with very long lines

$ wc -l /usr/bin/checksec
2111 /usr/bin/checksec

以下的命令展示了如何对你每天都会使用的:ls 命令的二进制文件运行 Checksec。Checksec 命令的格式是:checksec --file=,后面再跟上二进制文件的绝对路径:

$ checksec --file=/usr/bin/ls
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable     FILE
Full RELRO      Canary found      NX enabled    PIE enabled     No RPATH   No RUNPATH   No Symbols        Yes   5       17              /usr/bin/ls

当你在终端中对某个二进制文件运行 Checksec 时,你会看到安全属性有颜色上的区分,显示什么是好的安全属性(绿色),什么可能不是好的安全属性(红色)。我在这里说 “可能” 是因为即使有些安全属性是红色的,也不一定意味着这个二进制文件很糟糕,它可能只是表明发行版供应商在编译二进制文件时做了一些权衡,从而舍弃了部分安全属性。

Checksec 输出的第一行提供了二进制文件的各种安全属性,例如 RELROSTACK CANARYNX 等(我将在后文进行详细解释)。第二行打印出给定二进制文件(本例中为 ls)在这些安全属性的状态(例如,NX enabled 表示为堆栈中的数据没有执行权限)。

示例二进制文件

在本文中,我将使用以下的 “hello world” 程序作为示例二进制文件。

#include <stdio.h>

int main()
{
        printf("Hello World\n");
        return 0;
}
 

请注意,在编译源文件 hello.c 的时候,我没有给 gcc 提供任何额外的标志:

$ gcc hello.c -o hello
 
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=014b8966ba43e3ae47fab5acae051e208ec9074c, for GNU/Linux 3.2.0, not stripped

$ ./hello
Hello World

使用 Checksec 运行二进制文件 hello,打印的某些安全属性的状态,与上面的 ls 二进制文件的结果不同(在你的屏幕上,某些属性可能显示为红色):

$ checksec --file=./hello
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       Fortifiable     FILE
Partial RELRO   No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   85) Symbols       No    0       0./hello
$

(LCTT 译注:在我的 Ubuntu 22.04 虚拟机,使用 11.3.0 版本的 gcc,结果与上述不太相同,利用默认参数进行编译,会得到 RELRO、PIE、NX 保护是全开的情况。)

更改 Checksec 的输出格式

Checksec 允许自定义各种输出格式,你可以使用 --output 来自定义输出格式。我将选择的输出格式是 JSON 格式,并将输出结果通过管道传输到 jq 实用程序,来得到漂亮的打印。

接下来,确保你已安装好了 jq,因为本教程会使用 jq 从 Checksec 的输出结果中,用 grep 来快速得到某一特定的安全属性状态,并报告该安全属性是否启动(启动为 yes,未启动为 no):

$ checksec --file=./hello --output=json | jq
{
  "hello": {
    "relro": "partial",
    "canary": "no",
    "nx": "yes",
    "pie": "no",
    "rpath": "no",
    "runpath": "no",
    "symbols": "yes",
    "fortify_source": "no",
    "fortified": "0",
    "fortify-able": "0"
  }
}

看一看所有的安全属性

上面的二进制文件 hello 包括几个安全属性。我将该二进制文件与 ls 的二进制文件进行比较,以检查启用的安全属性有何不同,并解释 Checksec 是如何找到此信息。

1、符号(Symbol)

我先从简单的讲起。在编译期间,某些 符号 symbols 包含在二进制文件中,这些符号主要用作于调试。开发软件时,需要用到这些符号,来调试和修复错误。

这些符号通常会从供用户普遍使用的最终二进制文件中删除。删除这些符号不会影响到二进制文件的执行。删除符号通常是为了节省空间,因为一旦符号被删除了,二进制文件就会稍微小一些。在闭源或专有软件中,符号通常都会被删除,因为把这些符号放在二进制文件中,可以很容易地推断出软件的内部工作原理。

根据 Checksec 的结果,在二进制文件 hello 中有符号,但在 ls 的二进制文件中不会有符号。同样地,你还可以用 file 命令,来找到符号的信息,在二进制文件 hello 的输出结果的最后,看到 not stripped,表明二进制文件 hello 有符号:

$ checksec --file=/bin/ls --output=json | jq | grep symbols
    "symbols": "no",

$ checksec --file=./hello --output=json | jq | grep symbols
    "symbols": "yes",

$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=014b8966ba43e3ae47fab5acae051e208ec9074c, for GNU/Linux 3.2.0, not stripped

Checksec 是如何找到符号的信息呢?Checksec 提供了一个方便的 --debug 选项,来显示运行了哪些函数。因此,运行以下的命令,会显示在 shell 脚本中运行了哪些函数:

$ checksec --debug --file=./hello

在本教程中,我试图寻找 Checksec 查找安全属性信息时,使用了什么底层命令。由于 Checksec 是一个 shell 脚本,因此你始终可以使用 Bash 功能。以下的命令将输出从 shell 脚本中运行的每个命令:

$ bash -x /usr/bin/checksec --file=./hello

如果你滚动浏览上述的输出结果的话,你会看到 echo_message 后面有各个安全属性的类别。以下显示了 Checksec 检测二进制文件是否包含符号时,运行的底层命令:

+ readelf -W --symbols ./hello
+ grep -q '\\.symtab'
+ echo_message '\033[31m96) Symbols\t\033[m  ' Symbols, ' symbols="yes"' '"symbols":"yes",'

上面的输出显示,Checksec 利用 readelf,来读取二进制文件,并提供一个特殊 --symbols 标志,来列出二进制文件中的所有符号。然后它会查找一个特殊值:.symtab,它提供了所能找到的条目的计数(即符号的个数)。你可以在上面编译的测试二进制文件 hello 上,尝试以下命令,得到与 Checksec 查看二进制文件类似的符号信息:

$ readelf -W --symbols ./hello
$ readelf -W --symbols ./hello | grep -i symtab

(LCTT 译注:也可以通过直接查看 /usr/bin/checksec 下的 Checksec 源文件。)

如何删除符号

你可以在编译后或编译时删除符号。

  • 编译后: 在编译后,你可以使用 strip,手动地来删除二进制文件的符号。删除后,使用 file 命令,来检验是否还有符号,现在显示 stripped,表明二进制文件 hello 无符号了:
$ gcc hello.c -o hello
$
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=322037496cf6a2029dcdcf68649a4ebc63780138, for GNU/Linux 3.2.0, not stripped
$
$ strip hello
$
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=322037496cf6a2029dcdcf68649a4ebc63780138, for GNU/Linux 3.2.0, stripped
$ 
  • 编译时: 你也可以在编译时,用 -s 参数让 gcc 编译器帮你自动地删除符号:
$ gcc -s hello.c -o hello
$
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=247de82a8ad84e7d8f20751ce79ea9e0cf4bd263, for GNU/Linux 3.2.0, stripped
$

重新运行 Checksec,你可以看到现在二进制文件 hellosymbols 这一属性的值是no

$ checksec --file=./hello --output=json | jq | grep symbols
    "symbols": "no",
$

2、Canary(堆栈溢出哨兵)

Canary 是放置在缓冲区和 stack 上的控制数据之间的已知值,它用于监视缓冲区是否溢出。当应用程序执行时,会为其分配两种内存,其中之一就是 。栈是一个具有两个操作的数据结构:第一个操作 push,将数据压入堆栈;第二个操作 pop,以后进先出的顺序从栈中弹出数据。恶意的输入可能会导致栈溢出,或使用特制的输入破坏栈,并导致程序崩溃:

$ checksec --file=/bin/ls --output=json | jq | grep canary
    "canary": "yes",
$
$ checksec --file=./hello --output=json | jq | grep canary
    "canary": "no",
$

Checksec 是如何确定二进制文件是否启用了 Canary 的呢?使用上述同样的方法,得到 Checksec 在检测二进制文件是否启用 Canary 时,运行的底层命令:

$ readelf -W -s ./hello | grep -E '__stack_chk_fail|__intel_security_cookie'
启用 Canary

为了防止栈溢出等情况,编译器提供了 -stack-protector-all 标志,它向二进制文件添加了额外的代码,来检查缓冲区是否溢出:

$ gcc -fstack-protector-all hello.c -o hello

$ checksec --file=./hello --output=json | jq | grep canary
    "canary": "yes",

Checksec 显示 Canary 属性现已启用。你还可以通过以下方式,来验证这一点:

$ readelf -W -s ./hello | grep -E '__stack_chk_fail|__intel_security_cookie'
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __stack_chk_fail@GLIBC_2.4 (3)
    83: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __stack_chk_fail@@GLIBC_2.4
$

3、位置无关可执行文件(PIE)

位置无关可执行文件 Position-Independent Executable (PIE),顾名思义,它指的是放置在内存中某处执行的代码,不管其绝对地址的位置,即代码段、数据段地址随机化(ASLR):

$ checksec --file=/bin/ls --output=json | jq | grep pie
    "pie": "yes",

$ checksec --file=./hello --output=json | jq | grep pie
    "pie": "no",

通常,PIE 仅对 libraries 启用,并不对独立命令行程序启用 PIE。在下面的输出中,hello 显示为 LSB executable,而 libc 标准库(.so) 文件被标记为 LSB shared object

$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=014b8966ba43e3ae47fab5acae051e208ec9074c, for GNU/Linux 3.2.0, not stripped

$ file /lib64/libc-2.32.so
/lib64/libc-2.32.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=4a7fb374097fb927fb93d35ef98ba89262d0c4a4, for GNU/Linux 3.2.0, not stripped

Checksec 查找是否启用 PIE 的底层命令如下:

$ readelf -W -h ./hello | grep EXEC
  Type:                              EXEC (Executable file)

如果你在共享库上尝试相同的命令,你将看到 DYN,而不是 EXEC

$ readelf -W -h /lib64/libc-2.32.so | grep DYN
  Type:                              DYN (Shared object file)
启用 PIE

要在测试程序 hello.c 上启用 PIE,请在编译时,使用以下命令:

$ gcc -pie -fpie hello.c -o hello`

你可以使用 Checksec,来验证 PIE 是否已启用:

$ checksec --file=./hello --output=json | jq | grep pie
    "pie": "yes",
$

现在,应该会显示为 “ PIE 可执行 pie executable ”,其类型从 EXEC 更改为 DYN

$ file hello
hello: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=bb039adf2530d97e02f534a94f0f668cd540f940, for GNU/Linux 3.2.0, not stripped

$ readelf -W -h ./hello | grep DYN
  Type:                              DYN (Shared object file)

4、NX(堆栈禁止执行)

NX 代表 不可执行 non-executable 。它通常在 CPU 层面上启用,因此启用 NX 的操作系统可以将某些内存区域标记为不可执行。通常,缓冲区溢出漏洞将恶意代码放在堆栈上,然后尝试执行它。但是,让堆栈这些可写区域变得不可执行,可以防止这种攻击。在使用 gcc 对源程序进行编译时,默认启用此安全属性:

$ checksec --file=/bin/ls --output=json | jq | grep nx
    "nx": "yes",

$ checksec --file=./hello --output=json | jq | grep nx
    "nx": "yes",

Checksec 使用以下底层命令,来确定是否启用了 NX。在尾部的 RW 表示堆栈是可读可写的;因为没有 E,所以堆栈是不可执行的:

$ readelf -W -l ./hello | grep GNU_STACK
  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x10
演示如何禁用 NX

我们不建议禁用 NX,但你可以在编译程序时,使用 -z execstack 参数,来禁用 NX:

$ gcc -z execstack hello.c -o hello

$ checksec --file=./hello --output=json | jq | grep nx
    "nx": "no",

编译后,堆栈会变为可读可写可执行(RWE),允许在堆栈上的恶意代码执行:

$ readelf -W -l ./hello | grep GNU_STACK
  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10

5、RELRO(GOT 写保护)

RELRO 代表 “ 重定位只读 Relocation Read-Only ”。可执行链接格式(ELF)二进制文件使用全局偏移表(GOT)来动态地解析函数。启用 RELRO 后,会设置二进制文件中的 GOT 表为只读,从而防止重定位攻击:

$ checksec --file=/bin/ls --output=json | jq | grep relro
    "relro": "full",

$ checksec --file=./hello --output=json | jq | grep relro
    "relro": "partial",

Checksec 使用以下底层命令,来查找是否启用 RELRO。在二进制文件 hello 仅启用了 RELRO 属性中的一个属性,因此,在 Checksec 验证时,显示 partial

$ readelf -W -l ./hello | grep GNU_RELRO
  GNU_RELRO      0x002e10 0x0000000000403e10 0x0000000000403e10 0x0001f0 0x0001f0 R   0x1

$ readelf -W -d ./hello | grep BIND_NOW
启用全 RELRO

要启用全 RELRO,请在 gcc 编译时,使用以下命令行参数:

$ gcc -Wl,-z,relro,-z,now hello.c -o hello

$ checksec --file=./hello --output=json | jq | grep relro
    "relro": "full",

现在, RELRO 中的第二个属性也被启用,使程序变成全 RELRO:

$ readelf -W -l ./hello | grep GNU_RELRO
  GNU_RELRO      0x002dd0 0x0000000000403dd0 0x0000000000403dd0 0x000230 0x000230 R   0x1

$ readelf -W -d ./hello | grep BIND_NOW
 0x0000000000000018 (BIND_NOW)       

6、Fortify

Fortify 是另一个安全属性,但它超出了本文的范围。Checksec 是如何在二进制文件中验证 Fortify,以及如何在 gcc 编译时启用 Fortify,作为你需要解决的课后练习。

$ checksec --file=/bin/ls --output=json | jq  | grep -i forti
    "fortify_source": "yes",
    "fortified": "5",
    "fortify-able": "17"

$ checksec --file=./hello --output=json | jq  | grep -i forti
    "fortify_source": "no",
    "fortified": "0",
    "fortify-able": "0"

其他的 Checksec 功能

关于安全性的话题是永无止境的,不可能在本文涵盖所有关于安全性的内容,但我还想提一下 Checksec 命令的一些其他功能,这些功能也很好用。

对多个二进制文件运行 Checksec

你不必对每个二进制文件都进行一次 Checksec。相反,你可以提供多个二进制文件所在的目录路径,Checksec 将一次性为你验证所有文件:

$ checksec --dir=/usr

对进程运行 Checksec

Checksec 除了能检查二进制文件的安全属性,Checksec 还能对程序起作用。以下的命令用于查找你系统上所有正在运行的程序的安全属性。如果你希望 Checksec 检查所有正在运行的进程,可以使用 --proc-all,或者你也可以使用进程名称,选择特定的进程进行检查:

$ checksec --proc-all

$ checksec --proc=bash

对内核运行 Checksec

除了本文介绍的用 Checksec 检查用户态应用程序的安全属性之外,你还可以使用它来检查系统内置的 内核属性 kernel properties

$ checksec --kernel

快来试一试 Checksec 吧

Checksec 是一个能了解哪些用户空间和内核的安全属性被启用的好方法。现在,你就可以开始使用 Checksec,来了解每个安全属性是什么,并明白启用每个安全属性的原因,以及它能阻止的攻击类型。


via: https://opensource.com/article/21/6/linux-checksec

作者:Gaurav Kamathe 选题:lujun9972 译者:chai001125 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

通过安全的网络连接在远程计算机上调用命令或程序。

有一天,我正在测试如何在 将文件或目录复制到多个位置和系统时保持完整的文件权限。当我想检查远程系统上的文件权限时,我必须通过 SSH 登录它并检查属性。从远程系统多次登录和注销的过程让我有点烦,我想,如果我可以在远程 Linux 系统上通过 SSH 执行命令就好了。

幸运的是,在浏览了 ssh 命令的手册页后,我找到了一个解决办法。

如果你想知道如何本地运行远程系统上运行命令或脚本,而不登录到远程系统,下面的内容会告诉你如何做。

1、通过 SSH 在远程 Linux 系统上执行命令

从本地系统通过 SSH 在远程系统上运行命令或脚本的典型方法是:

$ ssh <username@IP_Address-or-Doman_name> <Command-or-Script>

允许我给你们举几个例子:

1.1、通过 SSH 在远程系统上运行单个命令

假设你想要 查找远程 Linux 系统的内核详细信息。为此,只需运行:

$ ssh [email protected] uname -a

这里,

  • sk 是远程系统的用户名,
  • 192.168.225.22 是远程系统的 IP 地址,
  • uname -a 是我想在远程系统上运行的命令。

示例输出:

通过 SSH 在远程 Linux 系统上执行命令

看到没?我并没有实际登录到远程系统,但通过 SSH 在远程系统上执行了 uname 命令,并在本地系统的终端上显示了输出。

你还可以像下面这样用引号指定命令。

$ ssh [email protected] "uname -a"

或者,

$ ssh [email protected] 'uname -a'

如果你已经 更改了 SSH 协议的默认端口,只需使用 -p 参数指定它。

$ ssh -p 2200 [email protected] uname -a

1.2、通过 SSH 在远程主机上执行多个命令

你还可以在远程主机上运行多个命令,方法是将它们放在引号中。

$ ssh [email protected] "uname -r && lsb_release -a"

或者:

$ ssh [email protected] "uname -r ; lsb_release -a"

上面的命令将显示我的 Ubuntu 服务器的内核版本和发行版详细信息。

示例输出:

在 Linux 上通过 SSH 在远程主机上运行多个命令

正如一位读者在下面的评论部分提到的那样,你应该用引号指定多个命令。如果不使用引号,第一个命令将在远程系统上执行,第二个命令将仅在本地计算机上执行。整个带引号的命令将按预期在远程计算机上运行。

提示: 了解 &&; 在命令中的区别:

&& 操作符只有在第一个命令成功时才执行第二个命令。

示例:

sudo apt-get update && sudo apt-get upgrade

在上述示例中,如果第一个命令成功,才会执行 sudo apt-get upgrade。否则,它将不会运行。

; 操作符会执行第二个命令,无论第一个命令是成功还是失败。

示例:

sudo apt-get update ; sudo apt-get upgrade

在上述示例中,即使第一个命令失败,sudo apt-get upgrade 也会执行。

1.3、通过 SSH 在远程机器上调用有 sudo 权限的命令

有些命令需要 sudo 权限才能运行。例如,以下命令将在我的远程系统上安装 apache2

$ ssh -t [email protected] sudo apt install apache2

示例输出:

通过 SSH 在远程机器上运行有 Sudo 权限的命令

注意到了吗?我在上面的命令中使用了 -t 标志,我们需要使用它来强制进行伪终端分配。它用于在远程机器上执行任意基于屏幕的程序,这非常有用。例如,在实现菜单服务时。

另外,我输入了两次密码。第一次是远程用户的密码,以便从本地系统通过 SSH 访问远程系统,第二次是为了向远程用户赋予 sudo 权限,以便安装应用程序(在本例中为 apache2)。

让我们用以下命令检查 Apache 服务是否正在运行:

$ ssh -t [email protected] sudo systemctl status apache2
[email protected]'s password: 
[sudo] password for sk: 
● apache2.service - The Apache HTTP Server
Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled)
Drop-In: /lib/systemd/system/apache2.service.d
└─apache2-systemd.conf
Active: active (running) since Thu 2019-12-19 11:08:03 UTC; 52s ago
Main PID: 5251 (apache2)
Tasks: 55 (limit: 2318)
CGroup: /system.slice/apache2.service
├─5251 /usr/sbin/apache2 -k start
├─5253 /usr/sbin/apache2 -k start
└─5254 /usr/sbin/apache2 -k start

Dec 19 11:08:03 ubuntuserver systemd[1]: Starting The Apache HTTP Server...
Dec 19 11:08:03 ubuntuserver apachectl[5227]: AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 2409:4072:51f:a1b6:a00:27ff:f
Dec 19 11:08:03 ubuntuserver systemd[1]: Started The Apache HTTP Server.

同样的,我们可以通过 SSH 在本地系统上运行远程系统上的任何命令或脚本。

1.4、通过 SSH 在远程系统上运行本地脚本

让我们在本地系统上创建一个简单的脚本来显示关于远程系统的发行版名称、包管理和基本细节等。

$ vi system_information.sh

添加以下行:

#!/bin/bash
#Name: Display System Details
#Owner: OSTechNIx
#----------------------------
echo /etc/*_ver* /etc/*-rel*; cat /etc/*_ver* /etc/*-rel*

按下 ESC 键,输入 :wq 保存退出。

现在,通过 SSH 命令在远程系统上运行这个脚本:

$ ssh [email protected] 'bash -s' < system_information.sh

示例输出:

[email protected]'s password: 
/etc/debian_version /etc/lsb-release /etc/os-release
buster/sid
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.2 LTS"
NAME="Ubuntu"
VERSION="18.04.2 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.2 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic

如果你没有在上面的命令中指定 bash -s,你将获得远程系统的详细信息,但伪终端不会被分配。

1.5、将远程主机的命令输出保存到本地主机

如果你希望与支持团队或同事共享远程系统上运行的命令输出,那么这非常有用。

以下命令将通过 SSH 在远程系统运行 du -ah,并将输出保存在本地系统的 diskusage.txt 文件中。

$ ssh [email protected] du -ah > diskusage.txt

然后,你可以通过使用 cat 命令或文本编辑器查看 diskusage.txt 文件来分析磁盘使用细节。

$ cat diskusage.txt 
4.0K ./.profile
4.0K ./.gnupg/private-keys-v1.d
8.0K ./.gnupg
76K ./data/image.jpg
128K ./data/file.pdf
20K ./data/text.docx
5.9M ./data/audio.mp3
6.1M ./data
0 ./.sudo_as_admin_successful
4.0K ./pacman?inline=false
4.0K ./.bash_logout
4.0K ./.wget-hsts
4.0K ./.bash_history
0 ./.cache/motd.legal-displayed
4.0K ./.cache
4.0K ./deb-pacman_1.0-0.deb
4.0K ./.bashrc
6.2M .

1.6、配置 SSH 密钥认证,避免输入密码

如果你经常在远程系统上运行命令,你可能需要配置基于 SSH 密钥的身份验证,以便每次跳过密码输入。更多细节可以在以下链接中找到。

Linux 系统下如何配置 SSH 密钥认证

配置了基于 SSH 密钥的认证后,我们可以通过 SSH 在远程机器上执行命令,从而不需要输入密码:

$ ssh [email protected] sudo apt update

2、通过 sshpass 在远程机器上运行命令

如果你不想配置基于 SSH 密钥的身份验证,你可以使用 sshpass 实用程序。

2.1、什么是 sshpass?

sshpass 是为使用键盘交互密码身份验证模式运行 ssh 而设计的,但它以非交互的方式。简单来说,sshpass 提供了非交互式的方式来验证 SSH 会话。

SSH 使用直接 TTY 访问来确保密码确实是由交互式键盘用户发出的。sshpass 在一个专用 tty 中运行 SSH,让它误以为从交互用户那里获得了密码。

2.2、在 Linux 中安装 sshpass

在许多 Linux 发行版的默认仓库中都有 sshpass 实用程序。例如,在 Debian、Ubuntu 及其衍生版本中,你可以使用下面的命令来安装 sshpass

$ sudo apt install sshpass

2.3、通过 SSH 和 sshpass 在远程机器上执行命令

sshpass 可以通过参数接受密码,或者通过环境变量读取密码,也可以从文本文件中读取密码。

警告: 所有这些方法都是 高度不安全的。所有系统用户都可以通过 ps 命令看到命令中的密码。不建议在生产中使用这些方法。最好使用基于密钥的身份验证。

让我们看看每种方法的示例。

将密码作为参数提供

将密码作为参数提供,使用 -p 选项,如下所示:

$ sshpass -p <remote-password> ssh remoteuser@ip-address <command-to-execute>

示例输出:

$ sshpass -p ubuntu ssh [email protected] uname -a

其中,

  • -p ubuntu - 提供远程系统的密码。
  • [email protected] - 远程系统用户名和地址。
  • uname -a - 要在远程计算机上执行的命令。

示例输出:

Linux Ubuntu22CT 5.15.60-1-pve #1 SMP PVE 5.15.60-1 (Mon, 19 Sep 2022 17:53:17 +0200) x86_64 x86_64 x86_64 GNU/Linux
密码作为环境变量提供

在这个方法中,我们声明一个名为 SSHPASS 的环境变量,用远程环境的密码作为其值。然后我们使用 -e 标志,如下所示:

$ SSHPASS=ubuntu sshpass -e ssh [email protected] uname -a
从文本文件中读取密码

使用 echo 命令在文本文件中追加密码:

$ echo "ubuntu" > mypassword.txt

现在,将密码文件传递给带有 -f 标志的 sshpass,如下所示:

$ sshpass -f mypassword.txt ssh [email protected] uname -a

通过 SSH 和 sshpass 在远程机器上执行命令

总结

在本教程中,我们学习了一些通过安全的网络连接在远程计算机上调用命令或程序的方法。在所有的方法中,sshpass 方法是最不安全的,建议用户避免在生产系统中使用它。


via: https://ostechnix.com/execute-commands-on-remote-linux-systems-via-ssh/

作者:sk 选题:lkxed 译者:MjSeven 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

如果你正在寻找一个优秀的、通用的、开源的、带有 Python 集成的代码编辑器,那么你可以尝试一下 Codium。

在过去几年内,我有幸和中学生们一起,并带他们入门 Python 开发 和树莓派 400。这一切都很有趣,树莓派对于学生和我来说都是一个很好的平台。我们使用了 Code with Mu,并且一切都很成功。我们的 Python 技能随着经验的增长而增长,因此最近我开始寻找给这些学生提供更多东西的方法。

我参与了一个 Python 课程并在课程中接触了微软的 Visual Studio Code。我在课程中学到了很多关于如何为 Python 设置虚拟环境,以及如何为 Python 编程配置 VS Code 的知识。在学习过程中,我也认识了 Codium,它基本上是没有微软品牌和遥测的 VS Code。

如果你正在寻找一个优秀的、通用的、开源的、带有 Python 集成的代码编辑器,那么你可以尝试一下 Codium。下面是我在 Linux 系统上为 Python 设置 Codium 的方法。

在 Linux 上安装或更新 Python

首先,确保你正在运行最新版本的 Python。你可以使用你的软件包管理器来完成这项工作。在 Debian 和基于 Debian 的系统上:

$ sudo apt install python3-pip

在 Fedora、CentOS、Mageia、OpenMandriva 和类似的系统上:

$ sudo dnf update python3

在某些系统上,你可能还需要安装创建 Python 虚拟环境的软件:

$ sudo apt install python3.10-venv

安装 Codium

接下来,在你的电脑上 安装 Codium。在 Linux 上,你可以下载一个包并使用你的包管理器安装它,或者 使用 Flatpak

在安装好 Codium 之后,打开你的应用程序或活动菜单,输入 code 以启动它。

安装 VS Code Python 扩展

代码其实不是什么特别的东西。它只是一些其他应用程序(编译器或运行时)解释的纯文本。你可以在 Codium 中编写 Python 代码而不需要特殊的扩展。但是,有一个 Python 扩展可以为你带来一些方便的功能。

点击“ 文件 File ”菜单,选择“ 首选项 Preferences ”,然后选择“ 扩展 Extensions ”。在“ 扩展 Extensions ”面板中,搜索 Python IntelliSense 扩展。

VS Code 和 Codium 都有一个扩展管理器,它会在页面左侧打开,允许你安装附加模块

你已经在 Codium 中设置了 Python。剩下的就是把它用起来。

为 VS Code 或 Codium 设置虚拟环境

我们可以创建一个项目目录,并将其添加到 Codium 中,这样在工作时,你创建和保存的文件都将默认保存到活动项目目录。这是一种快速的管理方式,可以让你不必经常点击文件保存和打开对话框。

在你创建一个虚拟 Python 环境作为工作目录时,Codium 会检测到它(因为你已经安装了 Python 扩展)。当你激活一个虚拟环境文件夹作为活动项目目录时,Codium 会自动运行使用虚拟环境所需的激活代码。

要为 Python 创建一个虚拟环境,请打开终端并输入:

$ python3 -m venv ~/PythonCoding

添加项目目录

在 Codium 中,点击“ 文件 File ”菜单,选择“ 将文件夹添加到工作区 Add Folder to Workspace ”。打开你刚刚设置的虚拟环境(对我来说,是 /home/don/PythonCoding)。

现在你已经准备好写一些 Python 代码了!在你的工作区中创建一个新的 Python 文件并插入一些基本代码。当你输入时,你可能会注意到,Codium 会为环境包含的 Python 模块提供自动补齐建议。

import sys
print("Codium running Python " + sys.version)

现在点击 Codium 窗口右上角的“运行”按钮。这会在窗口底部打开一个控制台面板显示你的代码的输出:

(PythonCode) sh-5.1$ /home/bogus/PythonCode/bin/python
/home/bogus/PythonCode/app.py
Codium running Python 3.10.6 (main…)[GCC 12.1.0]
(PythonCode) sh-5.1$

就像你从输出中看到的,Codium 在 PythonCode 环境中运行,并成功运行了你的 Python 代码。

Codium 和 Python

使用 Codium 编写 Python 代码比以往任何时候都更容易,但 Python 并不是 Codium 支持的唯一语言。你可以轻松地从 Open VSX Registry 中找到并安装其他扩展,这是一个中立的开源 VS Code 扩展 “市场”。

Codium 的界面比一些基本的编辑器更复杂,但它有我在学习过程中所需要的东西。如果你需要一个更专业的编辑器,或者你想从当前的编辑器切换到新的编辑器,那么试试 Codium 吧。


via: https://opensource.com/article/22/11/python-vs-code-codium

作者:Don Watkins 选题:lkxed 译者:Cubik65536 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

在这篇文章中,我们将逐步解释如何在 RHEL 9 系统上安装 Node.js。

Node.js 基于谷歌的 V8 JavaScript 引擎构建,它是一个自由开源的跨平台 JavaScript 运行时环境,主要用于构建服务器端应用。它使用事件驱动和异步模型,帮助开发人员构建高度可扩展的数据密集型的实时应用(RTA)。你可以使用 NodeJS 来构建前端和后端应用。

Node.js 通常用于构建以下应用:

  • 聊天应用
  • 流媒体应用
  • 浏览器游戏
  • 命令行工具
  • 嵌入式系统

在其技术栈中使用 NodeJS 的顶级公司包括 PayPal、NetFlix 和 Uber 等等。

安装 Node.js 主要有以下三种方式:

  • 从 NodeSource 仓库安装 Node.js
  • 从发行版的官方仓库安装 Node.js
  • 使用 NVM 安装 Node.js

让我们看看如何使用这些方法在 RHEL 9 上安装 Node.js。

先决条件:

  • 最小化安装的 RHEL 9 系统
  • 具有管理员权限的 sudo 用户
  • 互联网连接
  • Red Hat 订阅或本地配置的仓库

从 NodeSource 存储库安装 Node.js

NodeSource 是一家技术公司,旨在帮助组织运行生产就绪的 Node.js 应用,关注资源使用以及增强的安全性和应用程序性能。它提供了最新版本的 Node.js 和 NPM。

要从 NodeSource 安装 Node.js,首先,按如下所示更新系统包。

$ sudo dnf update -y

接下来,安装这期间所需的构建工具。其中包括 GCC C/C++ 编译器、Perl 和 Python 调试器等等。

$ sudo dnf groupinstall 'Development Tools' -y

接下来,我们将从 NodeSource 安装 Node.js 18.x。为此,请下载并运行 NodeSource 设置脚本,如下所示。

$ curl -fsSL https://rpm.nodesource.com/setup_18.x | sudo bash -

该脚本在其他任务中将 NodeSource 仓库添加到你的系统。

在输出的末尾,你将看到一些关于如何安装 Node.js 和 NPM 的附加说明。

因此,要安装 Node.js 和 NPM(Node 包管理器),请运行以下命令:

$ sudo dnf install nodejs -y

安装完成后,按如下所示验证 Node.js 和 NPM 的版本。

$ node -v
$ npm -v

输出显示我们正在运行 Node v18.12,它是最新的 LTS 版本和 NPM 8.19.2。

从官方 RHEL 仓库安装 Node.js

安装 NodeJS 和 NPM 的另一种方法是从发行版的官方仓库中安装它们。但是,这种方法不提供最新版本。

如果你不介意不安装最新版本的 Node 和 NPM。 那么在命令行上运行以下命令。

$ sudo dnf update -y
$ sudo dnf install nodejs npm -y

使用 NVM 安装 Node.js

最后,你可以使用 NVM(Node 版本管理器)安装 Node.js,这是一种用于管理系统上 Node 版本的工具。该工具可帮助开发人员在需要不同版本 Node.js 的不同项目上高效工作。

默认情况下没安装 NVM。你需要通过运行 官方 GitHub 页面 上提供的 Shell 脚本来安装它。

$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash

这会下载 NVM 并将其保存在主目录的 .nvm 目录中。

安装后,关闭终端会话并打开一个新终端。然后运行以下命令确认 NVM 已经安装。

$ command -v nvm

接下来,你可以使用以下命令列出所有可用的 Node.js 版本:

$ nvm ls-remote

或者,你可以列出 Node.js 版本的所有最新 LTS 版本,如图所示。

$ nvm ls-remote | grep -i latest

要安装最新版本的 Node.js(当前为 v19.0.0),请运行以下命令:

$ nvm install node

然后,你可以验证安装的 Node.js 版本,如下所示。

$ node -v

此外,你可以安装特定版本的 Node.js。例如,要安装 v18.2.0,请运行以下命令:

$ nvm install v18.12.0

要列出系统上所有已安装的 NodeJS 版本,请运行以下命令:

$ nvm ls

第一行带有 “->” 符号的条目指向当前使用的 Node.js 版本。然后是其他版本。

要切换到另一个版本的 Node.js,请使用以下语法:

$ nvm use <version>

例如,要使用 Node 版本 19.0.0,请运行以下命令:

$ nvm use 19.0.0

再次检查已安装的 Node.js 版本,这次“->” 符号将指向 v19.0.0。

总结

在本指南中,我们演示了如何使用三种不同的方法安装 Node.js。此外,我们还提供了几种使用 NVM 管理 Node 版本的方法。我们希望可以帮助你轻松地在 RHEL 上安装 NodeJS,并选择你想要在项目中使用的版本。


via: https://www.linuxtechi.com/how-to-install-nodejs-on-rhel/

作者:James Kiarie 选题:lkxed 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

你可以通过以下方式确定你的 Linux 发行版中是否正在运行 systemd 或其它初始化系统。

首个进程在你启动 Linux 发行版时开始运行,它称为初始化进程 init( 初始化 initialization 的缩写)。它的进程标识符为 1(即 pid=1)。基于 Unix 的系统中的所有进程和应用程序都是这个初始化进程的后代。

根据功能和特性,存在不同类型的初始化进程。例如,systemd、Runit、OpenRC、sysVinit 等。其中,systemd 是最流行和最现代的一种,被包括 Ubuntu 和 Fedora 在内的所有现代 Linux 发行版使用和采用。

与传统的基于 Unix 的初始化系统相比,systemd 及其性能一直存在争议。但这就是另外一个话题了。

让我们看看如何确定在 Linux 发行版中运行的是 systemd 还是其它初始化系统。

systemd 还是其它初始化系统?

不幸的是,没有直接的命令可以找到它。你可以从初始化进程追溯它,它基本上是到 /sbin/init 的符号链接,即 pid=1。

使用 strings 命令打印嵌入在二进制文件 /sbin/init 中的文本并使用以下命令搜索 init

strings /sbin/init | grep init

示例 1

在下面的输出中,它是一个运行 Debian(Peppermint OS)的 sysVinit 系统。如你所见,它清楚地显示了 init 进程名称。

strings /sbin/init | grep init

显示使用 sysVinit 而不是 systemd 的示例

如果在上述同一个系统中找 systemd,那么不会有任何结果。因此,你可以得出结论,你正在运行 sysVinit 而不是 systemd。

示例 2

如果你在 systemd 系统中运行上述命令,你可以在输出的第一行轻松看到 systemd 及其版本。

strings /sbin/init | grep systemd

显示它使用 systemd 的示例

示例 3

你也可以尝试使用 pstree 命令打印进程树,它应该会显示第一个进程名称。它应该是 systemdinit,如下例所示。

pstree

pstree 显示使用 systemd

pstree 显示使用 init

这就好了。这样你就可以轻松找出你的发行版是使用 systemd 还是其他的。


via: https://www.debugpoint.com/systemd-or-init/

作者:Arindam 选题:lkxed 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

大家好!今年早些时候,我在写《DNS 是如何工作的》 时,有人问我——为什么人们有时在域名的末尾加一个点?例如,如果你通过运行 dig example.com 查询 example.com 的 IP,你会看到一下内容:

$ dig example.com
example.com.        5678    IN  A   93.184.216.34

执行完 dig 命令后,example.com 有一个 . ——变成了 example.com.!发生了什么?

有些 DNS 工具也要求传给它的域名后加一个 .:如果你在使用 miekg/dns 时传给它 example.com,它会报错:

// trying to send this message will return an error
m := new(dns.Msg)
m.SetQuestion("example.com", dns.TypeA)

最初我以为我知道这个问题的答案(“呃,末尾的点意味着域名是完全限定的?”)。这是对的 —— 一个 完全限定域名 fully qualified domain name (FQDN)是一个末尾有 . 的域名!

但是为什么末尾的点是有用且重要的呢?

在 DNS 的请求/响应中,域名的末尾并没有 “.”

我曾经(错误地)认为 “为什么末尾有一个点?”的答案可能是 “在 DNS 请求/响应中,域名末尾有一个 .,所以我们把它放进去,以匹配你的计算机实际发送/接收的内容”。但事实并不是这样!

当计算机发送 DNS 请求/响应时,域名的末尾并没有点。实际上,域名中没有点。

域名会被编码成一系列的长度/字符串对。例如,域名 example.com 被编码为这 13 个字节。

7example3com0

编码后的内容一个点也没有。一个 ASCII 域名(如 example.com)被转成了各种 DNS 软件的 DNS 请求/响应中使用的格式。

今天我们来讨论域名被转成 DNS 响应的一个地方:区域文件。

区域文件中域名末尾的 “.”

一些人管理域名的 DNS 记录的方法是创建一个被称为 “区域文件” 的文本文件,然后配置一些 DNS 服务器软件(如 nsdbind)来为该区域文件中指定的 DNS 记录提供服务。

下面是一个对应 example.com 的示例区域文件:

orange  300   IN    A     1.2.3.4
fruit   300   IN    CNAME orange
grape   3000  IN    CNAME example.com.

在这个文件中,任何不以 . 结尾的域名(比如 orange)后都会自动加上 .example.com。所以 orange 成了 orange.example.com 的简称。DNS 服务器从它的配置中得知这是一个 example.com 的区域文件,所以它知道在所有不以点结尾的名字后面自动添加 example.com

我想这里的想法只是为了少打几个字符——如果要打出全称,区域文件会是这样:

    orange.example.com.  300   IN    A     1.2.3.4
    fruit.example.com.   300   IN    CNAME orange.example.com.
    grape.example.com.   3000  IN    CNAME example.com.

确实多了很多字符。

你也可以不通过区域文件来使用 DNS

尽管官方的 DNS RFC(RFC 1035)中定义了区域文件格式,但你也可以不通过区域文件来使用 DNS。例如,AWS Route 53 就不用区域文件来存储 DNS 记录!你可以通过 Web 界面或 API 来创建记录,我猜他们是用某种数据库而不是一堆文本文件来存储记录。

不过,Route 53(像许多其他 DNS 工具一样)确实支持导入和导出区域文件,这个功能或许在你更换 DNS 提供商时很有用。

dig 命令输出中末尾的 “.”

现在我们来讨论下 dig 命令的输出:

$ dig example.com
; <<>> DiG 9.18.1-1ubuntu1.1-Ubuntu <<>> +all example.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10712
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 65494
;; QUESTION SECTION:
;example.com.           IN  A

;; ANSWER SECTION:
example.com.        81239   IN  A   93.184.216.34

有一件奇怪的事是,几乎每一行都以 ;; 开头,这是怎么回事?; 是区域文件中的注释字符!

我想 dig 以这种奇怪的方式输出的原因可能是为了方便你粘贴这些内容到区域文件时,不用修改就可以直接用。

这也是 example.com 末尾有个 . 的原因 —— 区域文件要求域名末尾必须有点(否则它们会被解释为是相对于该区域的)。因此 dig 也这么处理了。

我真的希望 dig 有一个 +human 选项,以更人性化的方式打印出这些信息,但现在我太懒了,懒得花工夫去实际贡献代码来做这件事(而且我并不擅长 C),所以我只能在我的博客上抱怨一下 :smiley:

curl 命令输出中末尾的 “.”

我们来看下另一个末尾有 . 的例子:curl

我家里有台计算机名为 grapefruit,其上运行着 Web 服务器。当我执行 curl grapefruit 时,会输出:

$ curl grapefruit
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

<html>
<head>
......

这样运行没问题!但是如果我在域名后加一个 . 会怎样呢?它报错了:

$ curl grapefruit.
curl: (6) Could not resolve host: grapefruit.

发生了什么?为了搞清楚,我们需要先来学习下搜索域:

初识搜索域

当我执行 curl grapefrult 时,它是怎么被转成一个 DNS 请求的?你可能会认为我的计算机会向域名 grapefruit 发送一个请求,对吗?但事实并不是这样。

让我们用 tcpdump 来看看到底是什么域名在被查询。

$ sudo tcpdump -i any port 53
[...] A? grapefruit.lan. (32)

实际上是向 grapefruit.lan. 发送的请求。为什么呢?

解释一下:

  1. curl 调用函数 getaddrinfo 来查询 grapefruit
  2. getaddrinfo 查询了我计算机上的文件 /etc/resolv.conf
  3. /etc/resolv.conf 包含两行内容:
nameserver 127.0.0.53
search lan
  1. 因为有 search lan 这行内容,所以 getaddrinfograpefruit 的末尾添加了一个 lan,去查询 grapefruit.lan

什么时候搜索域被使用?

现在我们知道了一些奇怪的事情:当我们查询一个域名时,有时会有一个额外的东西(如 lan)被加到最后。但是什么时候会发生这种情况呢?

  1. 如果我们在域名末尾添加一个 .,那么这时不会用到搜索域
  2. 如果域名中间包含一个 .(如 example.com),那么默认也不会用到搜索域。但是可以通过修改配置来改变处理逻辑(在 ndots 里有更详细的说明)

我们现在知道了 curl grapefruit.curl grapefruit 结果不一样的原因——因为一个查询的是 grapefruit.,而另一个查询的是 grapefruit.lan.

我的计算机怎么知道使用哪个搜索域呢?

当我连接路由时,它会通过 DHCP 告诉我它的搜索域是 lan —— 它也是通过这个方式给我的计算机分配 IP。

所以为什么要在域名末尾加一个点呢?

现在我们已经了解了区域文件和搜索域,下面是我认为的人们要在域名末尾加点的原因:

有两种情况下,域名会被修改,并在末尾添加其他东西。

  • example.com 的区域文件中,grapefruit 会被转为 grapefruit.example.com
  • 在我的本地网络(我的计算机已经配置了使用搜索域 lan),grapefruit 被转为 grapefruit.lan

因此,由于域名在某些情况下实际上可能被转成其他名字,人们就在结尾处加一个 .,以此来表示 “这是域名,末尾不需要添加任何东西,这就是全部内容”。否则会引起混乱。

“这就是全部内容”的技术术语是**“完全限定域名”,简称为“FQDN”**。所以 google.com. 是一个完全限定域名,而 google.com 不是。

我总是要提醒自己这样做的原因,因为我很少使用区域文件和搜索域,所以我经常觉得——“我当然是指 google.com 而不是 google.com.something.else! 我为什么要指其他东西?那太傻了!”

但是有些人确实在使用区域文件和搜索域(例如 Kubernetes 中使用了搜索域!),所以结尾的 . 很有用,可以让人确切的知道,不应该再添加其他东西。

什么时候在末尾添加 “.”?

以下是关于何时在域名末尾加 ". " 的几个简单说明:

需要添加:配置 DNS 时

在配置 DNS 时,使用完全限定域名从来都不是坏事。你不一定要这样做:非完全限定域名通常也能正常工作,但我从来没有遇到过不接受完全限定域名的 DNS 软件。

有些 DNS 软件需要这样做:现在我为 jvns.ca 使用的 DNS 服务器让我在域名的末尾加上 .(例如在 CNAME 记录中),并提示如果我不添加,它将在我输入的内容末尾加上 .jvns.ca。我不同意这个设计决定,但这不是什么大问题,我只是在最后加一个 .

不需要加:在浏览器中

令人困惑的是,在浏览器中,在域名结尾处加一个 . 不能正常运行。例如,如果我在浏览器中输入 https://twitter.com.,它就会报错。它会返回 404。

我认为这里发生的事情是,它将 HTTP Host 标头设置为 Host:twitter.com.,而对端的 Web 服务器则期望 Host:twitter.com

同样地,https://jvns.ca. 由于某种原因,返回了一个 SSL 错误。

我认为相对域名在过去是比较常见的

最后一件事:我认为“相对”域名(比如我用 grapefruit 来指代我家的另一台计算机 grapefruit.lan)在过去更常用,因为 DNS 是在大学或其他有大型内部网络的大机构中开发的。

在今天的互联网上,使用“绝对”域名(如 example.com)似乎更为普遍。


via: https://jvns.ca/blog/2022/09/12/why-do-domain-names-end-with-a-dot-/

作者:Julia Evans 选题:lujun9972 译者:lxbwolf 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出