分类 技术 下的文章

在这个系列教程中,将为你提供使用 PGP 和保护你的私钥的最佳体验。

在本系列教程中,我们将提供一个使用 PGP 的实用指南。如果你没有看过前面的文章,你可以通过下面的链接去查看。在这篇文章中,我们将继续讨论如何保护你的密钥,谈一谈将你的子密钥移到一个专门的硬件设备中的一些技巧。

清单

  • 取得一个 GnuPG 兼容的硬件设备(必要)
  • 配置 GnuPG 在设备上工作(必要)
  • 设置用户和管理员的 PIN(必要)
  • 移动子密钥到设备中(必要)

考虑事项

虽然现在主密钥已经不用担心泄露或失窃了,但子密钥仍然在你的家目录中。任何得到它的人都能够解密你的通讯或假冒你的签名(如果他们知道密钥的密码)。并且,每次执行一个 GnuPG 操作都要将密钥加载到操作系统内存中,这将使一些更高级的恶意软件有机会得到你的密钥(想想 Meltdown 和 Spectre)。

完全保护密钥的最好方式就是,将它移到一个专门的硬件设备中,这种硬件设备是一个可操作的智能卡。

智能卡的好处

一个智能卡包含一个加密芯片,它能够存储私钥,并且直接在智能卡内部执行秘密操作。因为密钥内容从来没有离开过智能卡,计算机操作系统并不能检索你插入的智能卡上的私钥。这与前面用于备份目的的加密 USB 存储是不同的 —— 虽然 USB 设备也是插入并解密的,但操作系统是能够去访问私钥内容的。使用外置的加密 USB 介质并不能代替智能卡设备的功能。

智能卡的一些其它好处:

  • 它们很便宜且易于获得
  • 它们小巧且易于携带
  • 它们可以用于多种设备上
  • 它们中的很多都具有防篡改功能(取决于制造商)

可用的智能卡设备

智能卡最初是嵌入到真实钱包大小的卡中,故而得名智能卡。你肯定可以买到并使用 GnuPG 功能的智能卡,并且它们是你能得到的最便宜的可用设备之一。但是,事实上智能卡有一个很重要的缺点:它们需要一个智能卡读卡器,只有极小数的笔记本电脑上有这种读卡器。

由于这个原因,制造商开始推出小型 USB 设备,它的大小和 U 盘类似,内置有微型智能卡,并且在芯片上简单地实现了智能卡协议特性。下面推荐几个这样的设备:

  • Nitrokey Start:开源硬件和自由软件,可用于 GnuPG 的最便宜的选择之一,但是额外的安全特性很少。
  • Nitrokey Pro:类似于 Nitrokey Start,它提供防篡改及更多的安全特性(但没有 U2F,具体查看指南的 U2F 节)。
  • Yubikey 4:专利硬件和软件,但比 Nitrokey Pro 便宜,并且可以用在最新的笔记本电脑上的 USB-C 接口;也提供像 U2F 这样的额外的安全特性。

我们推荐选一个同时具备智能卡功能和 U2F 的设备,在写这篇文章时,只能选择 Yubikey 4。

配置智能卡设备

你的智能卡设备插入任何一台现代的 Linux 或 Mac 工作站上都应该能正常工作。你可以通过运行如下的命令去验证它:

$ gpg --card-status

如果你没有收到错误,有一个完整的卡列表,就表示一切正常。不幸的是,排除为什么设备不能正常工作的所有可能原因,已经超出了本指南的范围。如果你的智能卡使用 GnuPG 时有问题,请通过你的操作系统的常见支持通道寻求支持。

PIN 不一定是数字

注意,尽管名为 “PIN”(暗示你它必须是一个“数字”),不论是用户 PIN 还是管理员 PIN 都不必非要是数字。

当你收到一个新设备时,它可能设置有一个默认的用户和管理员 PIN,对于 Yubikey,它分别是 12345612345678。如果它们的 PIN 不是默认的,请查看设备附带的说明书。

快速设置

为配置你的智能卡,你需要使用 GnuPG 菜单系统,因此这里并没有更方便的命令行开关:

$ gpg --card-edit
[...omitted...]
gpg/card> admin
Admin commands are allowed
gpg/card> passwd

你应该去设置用户 PIN (1)、管理员 PIN (3)、和重置码 (4)。请确保把它们记录并保存到一个安全的地方 —— 尤其是管理员 PIN 和重置码(它允许你去擦除整个智能卡内容)。你很少使用到管理员 PIN,因此如果你不记录下来,很可能会忘掉它。

返回到智能卡主菜单,你也可以设置其它值(比如名字、性别、登入日期、等等),但是这些都不是必需的,一旦你的智能卡丢失了,将导致额外的信息泄露。

将子密钥移到你的智能卡中

退出卡菜单(使用 q 命令)保存所有更改。接下来,我们将你的子密钥移到智能卡中。将需要用到你的 PGP 密钥的密码,在大多数的智能卡操作中都将用到管理员 PIN。记住,那个 [fpr] 表示你的密钥的完整的 40 个字符的指纹。

$ gpg --edit-key [fpr]

Secret subkeys are available.

pub  rsa4096/AAAABBBBCCCCDDDD
    created: 2017-12-07  expires: 2019-12-07 usage: C
    trust: ultimate      validity: ultimate
ssb  rsa2048/1111222233334444
    created: 2017-12-07  expires: never usage: E
ssb  rsa2048/5555666677778888
    created: 2017-12-07  expires: never usage: S
[ultimate] (1). Alice Engineer <[email protected]>
[ultimate] (2)  Alice Engineer <[email protected]>

gpg>

使用 --edit-key 再次进入到菜单模式,你将注意到那个密钥清单有一点小差别。从现在开始,所有的命令都是在这个菜单模式下运行,它用 gpg> 提示符来表示。

首先,我们来选择移到智能卡中的密钥 —— 你可以通过键入 key 1(它表示选择清单中的第一个密钥)来实现:

gpg> key 1

这个输出会有一点细微的差别:

pub  rsa4096/AAAABBBBCCCCDDDD
    created: 2017-12-07  expires: 2019-12-07 usage: C
    trust: ultimate      validity: ultimate
ssb* rsa2048/1111222233334444
    created: 2017-12-07  expires: never usage: E
ssb  rsa2048/5555666677778888
    created: 2017-12-07  expires: never usage: S
[ultimate] (1). Alice Engineer <[email protected]>
[ultimate] (2)  Alice Engineer <[email protected]>

注意与密钥对应的 ssb 行旁边的 * —— 它表示这是当前选定的密钥。它是可切换的,意味着如果你再次输入 key 1,这个 * 将消失,这个密钥将不再被选中。

现在,我们来将密钥移到智能卡中:

gpg> keytocard
Please select where to store the key:
  (2) Encryption key
Your selection? 2

由于它是我们的 [E] 密钥,把它移到加密区中是有很有意义的。当你提交了你的选择之后,将会被提示输入你的 PGP 密钥的保护密码,接下来输入智能卡的管理员 PIN。如果命令没有返回错误,表示你的密钥已经被移到智能卡中了。

重要: 现在再次输入 key 1 去取消选中第一个密钥,并输入 key 2 去选择 [S] 密钥:

gpg> key 1
gpg> key 2
gpg> keytocard
Please select where to store the key:
  (1) Signature key
  (3) Authentication key
Your selection? 1

你可以使用 [S] 密钥同时做签名和验证,但是我们希望确保它在签名区,因此,我们选择 (1)。完成后,如果你的命令没有返回错误,表示操作已成功。

最后,如果你创建了一个 A 密钥,你也可以将它移到智能卡中,但是你需要先取消选中 key 2。完成后,选择 q

gpg> q
Save changes? (y/N) y

保存变更将把你的子密钥移到智能卡后,把你的家目录中的相应子密钥删除(没有关系,因为我们的备份中还有,如果更换了智能卡,你需要再做一遍)。

验证移动后的密钥

现在,如果你执行一个--list-secret-keys 操作,你将看到一个稍有不同的输出:

$ gpg --list-secret-keys
sec#  rsa4096 2017-12-06 [C] [expires: 2019-12-06]
     111122223333444455556666AAAABBBBCCCCDDDD
uid           [ultimate] Alice Engineer <[email protected]>
uid           [ultimate] Alice Engineer <[email protected]>
ssb>  rsa2048 2017-12-06 [E]
ssb>  rsa2048 2017-12-06 [S]

ssb> 的输出中的 > 表示子密钥仅在智能卡上有效。如果你进入到你的密钥目录中,查看目录的内容,你将会看到那个 .key 文件已经被存根替换:

$ cd ~/.gnupg/private-keys-v1.d
$ strings *.key

这个输出将包含一个影子私钥,它表示那个文件仅是个存根,真正的内容在智能卡中。

验证智能卡的功能

验证智能卡能否如期正常运行,你可以通过创建一个签名来验证:

$ echo "Hello world" | gpg --clearsign > /tmp/test.asc
$ gpg --verify /tmp/test.asc

首次运行这个命令时将询问你智能卡的 PIN,在你运行 gpg —verify 之后,它将显示 “Good signature”。

祝贺你,你已经成功将窃取你的开发者数字身份变得更加困难了!

其它常见 GnuPG 操作

下面是使用你的 PGP 密钥需要做的一些常见操作的快速指南。

在下面的所有命令中,[fpr] 表示你的密钥指纹。

挂载主密钥离线存储

下面的一些操作将需要你的主密钥,因此首先需要去挂载你的主密钥离线存储,并告诉 GnuPG 去使用它。首先,找出介质挂载路径,可以通过查看 mount 命令的输出找到它。接着,设置你的 GnuPG 目录为你的介质上备份的目录,并告诉 GnuPG 将那个目录做为它的家目录:

$ export GNUPGHOME=/media/disk/name/gnupg-backup
$ gpg --list-secret-keys

确保你在输出中看到的是 sec 而不是 sec#(这个 # 表示密钥不可用,仍然使用的是惯常的那个 Home 目录)。

更新你惯常使用的那个 GnuPG 工作目录

在你的离线存储上做了任何更改之后,你应该将这些更改同步应用到你惯常使用的工作目录中:

$ gpg --export | gpg --homedir ~/.gnupg --import
$ unset GNUPGHOME
延长密钥过期日期

我们创建的主密钥的默认过期日期是自创建之日起两年后。这样做都是为安全考虑,这样将使淘汰密钥最终从密钥服务器上消失。

延长你的密钥过期日期,从当前日期延长一年,只需要运行如下命令:

$ gpg --quick-set-expire [fpr] 1y

如果为了好记住,你也可以使用一个特定日期(比如,你的生日、1 月 1 日、或加拿大国庆日):

$ gpg --quick-set-expire [fpr] 2020-07-01

记得将更新后的密钥发送到密钥服务器:

$ gpg --send-key [fpr]
吊销身份

如果你需要吊销一个身份(比如,你换了雇主并且旧的邮件地址不再有效了),你可以使用一行命令搞定:

$ gpg --quick-revoke-uid [fpr] 'Alice Engineer <[email protected]>'

你也可以通过使用 gpg --edit-key [fpr] 在菜单模式下完成同样的事情。

完成后,记得将更新后的密钥发送到密钥服务器上:

$ gpg --send-key [fpr]

下一篇文章中,我们将谈谈 Git 如何支持 PGP 的多级别集成。

通过来自 Linux 基金会和 edX 的免费课程 “Linux 入门”学习更多 Linux 知识。


via: https://www.linux.com/blog/learn/pgp/2018/3/protecting-code-integrity-pgp-part-5-moving-subkeys-hardware-device

作者:KONSTANTIN RYABITSEV 译者:qhwdw 校对:wxy

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

不久前,我们总结了所有 在本地挂载 Google Drive 作为虚拟文件系统,并从 Linux 系统访问存储在 Google Drive 中的文件的方法。今天,我们将学习使用 dbxfs 将 Dropbox 文件夹挂载到本地文件系统中。dbxfs 用于在类 Unix 操作系统中本地挂载 Dropbox 文件夹作为虚拟文件系统。虽然在 Linux 中很容易安装 Dropbox 客户端,但这种方法与官方方法略有不同。它是一个命令行 dropbox 客户端,且无需磁盘空间即可访问。dbxfs 是自由开源的,并且是用 Python 3.5+ 编写的。

安装 dbxfs

dbxfs 官方支持 Linux 和 Mac OS。但是,它应该适用于任何提供 FUSE 兼容库或能够挂载 SMB 共享的 POSIX 系统。由于它是用 Python 3.5 编写的,因此可以使用 pip3 包管理器进行安装。如果尚未安装 pip,请参阅以下指南。

并且也要安装 FUSE 库。

在基于 Debian 的系统上,运行以下命令以安装 FUSE:

$ sudo apt install libfuse2

在 Fedora 上:

$ sudo dnf install fuse

安装完所有必需的依赖项后,运行以下命令以安装 dbxfs

$ pip3 install dbxfs

在本地挂载 Dropbox 文件夹

创建一个挂载点以将 Dropbox 文件夹挂载到本地文件系统中。

$ mkdir ~/mydropbox

然后,使用 dbxfs 在本地挂载 dropbox 文件夹,如下所示:

$ dbxfs ~/mydropbox

你将被要求生成一个访问令牌:

要生成访问令牌,只需在 Web 浏览器中输入上面输出的 URL,然后单击 允许 以授权 Dropbox 访问。你需要登录 Dropbox 帐户才能完成授权过程。

下一个页面将生成新的授权码。复制代码并返回终端将其粘贴到 cli-dbxfs 提示符中以完成该过程。

然后,系统会要求你保存凭据以供将来访问。根据你是要保存还是拒绝,输入 YN。然后,你需要为新的访问令牌输入两次密码。

最后,输入 Y 接受 /home/username/mydropbox 作为默认挂载点。如果你要设置不同的路径,输入 N 并输入你选择的位置。

Generate access token 2

完成了!从现在开始,你可以看到你的 Dropbox 文件夹已挂载到本地文件系统中。

更改访问令牌存储路径

默认情况下,dbxfs 会将 Dropbox 访问令牌存储在系统密钥环或加密文件中。但是,你可能希望将其存储在 gpg 加密文件或其他地方。如果是这样,请在 Dropbox 开发者应用控制台上创建个人应用来获取访问令牌。

创建应用后,单击下一步中的生成按钮。此令牌可用于通过 API 访问你的 Dropbox 帐户。不要与任何人共享你的访问令牌。

创建访问令牌后,使用任何你选择的加密工具对其进行加密,例如 CryptomaterCryptkeeperCryptGoCryptrTombToplip**GnuPG 等,并在你喜欢的位置保存。

接下来编辑 dbxfs 配置文件并添加以下行:

"access_token_command": ["gpg", "--decrypt", "/path/to/access/token/file.gpg"]

你可以通过运行以下命令找到 dbxfs 配置文件:

$ dbxfs --print-default-config-file

有关更多详细信息,请参阅 dbxfs 帮助:

$ dbxfs -h

如你所见,使用 dbxfs 在你的文件系统中本地挂载 Dropfox 文件夹并不复杂。经过测试,dbxfs 如常工作。如果你有兴趣了解它是如何工作的,请尝试一下,并在下面的评论栏告诉我们你的体验。

就是这些了。希望这篇文章有用。还有更多好东西。敬请期待!

干杯!


via: https://www.ostechnix.com/dbxfs-mount-dropbox-folder-locally-as-virtual-file-system-in-linux/

作者:SK 选题:lujun9972 译者:geekpi 校对:wxy

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

正如你可能已经知道的那样,运行级别 在许多最近的 Linux 发行版(如 RHEL 7 和 Ubuntu 16.04 LTS)中被 systemd 的目标 替换。有关它们的更多详细信息,请参阅这个指南。在这个简短的教程中,我们将看到如何启动救援模式以及紧急模式。本指南在 Ubuntu 18.04 LTS 中进行了测试,但是下面给出的步骤适用于大多数使用 systemd 作为默认服务管理器的 Linux 发行版。在进一步讨论之前,让我澄清什么是救援模式和紧急模式以及这两种模式的目的是什么。

什么是救援模式?

救援模式相当于使用 SysV 作为默认的服务管理器的 Linux 发行版中的 单用户模式。在救援模式下,将挂载所有本地文件系统,仅启动一些重要服务。但是,不会启动正常服务(例如网络服务)。救援模式在系统无法正常启动的情况下很有用。此外,我们可以在救援模式下执行一些重要的救援操作,例如重置 root 密码

什么是紧急模式?

与救援模式相比,在紧急模式中不启动任何东西。没有服务启动、没有挂载点、没有建立套接字,什么也没有。你所拥有的只是一个原始的 shell。紧急模式适用于调试目的。

在 Ubuntu 18.04 LTS 中进入救援模式

启动你的 Ubuntu 系统。出现 Grub 菜单时,选择第一条并按下 e 进行编辑。

如果你没有看到 Grub 菜单,只需在 BIOS 的 logo 消失后立即按下 ESC 键。

找到以单词 linux 开头的行,并在该行的末尾添加以下内容(要到达末尾,只需按下 CTRL+e 或使用 END 键或左右箭头键):

systemd.unit=rescue.target

添加完成后,只需按下 CTRL+xF10 即可继续启动救援模式。几秒钟后,你将以 root 用户身份进入救援模式(单用户模式)。以下是 Ubuntu 18.04 LTS 服务器版中救援模式的样子:

接下来,输入以下命令将根 (/) 文件系统重新挂载成读/写模式。

mount -n -o remount,rw /

启动到紧急模式

将 Ubuntu 引导到紧急模式与上述方法相同。你只需在编辑 Grub 菜单时将 systemd.unit=rescue.target 替换为 systemd.unit=emergency.target 即可。

添加 systemd.unit=emergency.target 后,按下 Ctrl+xF10 继续启动到紧急模式。

最后,你可以使用以下命令将根文件系统重新挂载成读/写模式:

mount -n -o remount,rw /

在救援模式和紧急模式之间切换

如果你处于救援模式,则不必像上面提到的那样编辑 Grub 条目。相反,只需输入以下命令即可立即切换到紧急模式:

systemctl emergency

同样,要从紧急模式切换到救援模式,请输入:

systemctl rescue

你现在知道了什么是救援模式和紧急模式以及如何在 Ubuntu 18.04 中启动这些模式。就像我已经提到的,这里提供的步骤将适用于许多使用 systemd 的 Linux 版本。

就是这些了。希望这篇文章有用。

还有更多好东西。敬请期待!

干杯!


via: https://www.ostechnix.com/how-to-boot-into-rescue-mode-or-emergency-mode-in-ubuntu-18-04/

作者:SK 选题:lujun9972 译者:geekpi 校对:wxy

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

使用 Node.js 构建一个根据询问创建文件的命令行工具。

当用于构建命令行界面(CLI)时,Node.js 十分有用。在这篇文章中,我将会教你如何使用 Node.js 来构建一个问一些问题并基于回答创建一个文件的命令行工具。

开始

首先,创建一个新的 npm 包(NPM 是 JavaScript 包管理器)。

mkdir my-script
cd my-script
npm init

NPM 将会问一些问题。随后,我们需要安装一些包。

npm install --save chalk figlet inquirer shelljs

这是我们需要的包:

  • Chalk:正确设定终端的字符样式
  • Figlet:使用普通字符制作大字母的程序(LCTT 译注:使用标准字符,拼凑出图片)
  • Inquirer:通用交互式命令行用户界面的集合
  • ShellJS:Node.js 版本的可移植 Unix Shell 命令行工具

创建一个 index.js 文件

现在我们要使用下述内容创建一个 index.js 文件。

#!/usr/bin/env node

const inquirer = require("inquirer");
const chalk = require("chalk");
const figlet = require("figlet");
const shell = require("shelljs");

规划命令行工具

在我们写命令行工具所需的任何代码之前,做计划总是很棒的。这个命令行工具只做一件事:创建一个文件

它将会问两个问题:文件名是什么以及文件后缀名是什么?然后创建文件,并展示一个包含了所创建文件路径的成功信息。

// index.js

const run = async () => {
  // show script introduction
  // ask questions
  // create the file
  // show success message
};

run();

第一个函数只是该脚本的介绍。让我们使用 chalkfiglet 来把它完成。

const init = () => {
  console.log(
    chalk.green(
      figlet.textSync("Node JS CLI", {
        font: "Ghost",
        horizontalLayout: "default",
        verticalLayout: "default"
      })
    )
  );
}

const run = async () => {
  // show script introduction
  init();

  // ask questions
  // create the file
  // show success message
};

run();

然后,我们来写一个函数来问问题。

const askQuestions = () => {
  const questions = [
    {
      name: "FILENAME",
      type: "input",
      message: "What is the name of the file without extension?"
    },
    {
      type: "list",
      name: "EXTENSION",
      message: "What is the file extension?",
      choices: [".rb", ".js", ".php", ".css"],
      filter: function(val) {
        return val.split(".")[1];
      }
    }
  ];
  return inquirer.prompt(questions);
};

// ...

const run = async () => {
  // show script introduction
  init();

  // ask questions
  const answers = await askQuestions();
  const { FILENAME, EXTENSION } = answers;

  // create the file
  // show success message
};

注意,常量 FILENAMEEXTENSIONS 来自 inquirer 包。

下一步将会创建文件。

const createFile = (filename, extension) => {
  const filePath = `${process.cwd()}/${filename}.${extension}`
  shell.touch(filePath);
  return filePath;
};

// ...

const run = async () => {
  // show script introduction
  init();

  // ask questions
  const answers = await askQuestions();
  const { FILENAME, EXTENSION } = answers;

  // create the file
  const filePath = createFile(FILENAME, EXTENSION);

  // show success message
};

最后,重要的是,我们将展示成功信息以及文件路径。

const success = (filepath) => {
  console.log(
    chalk.white.bgGreen.bold(`Done! File created at ${filepath}`)
  );
};

// ...

const run = async () => {
  // show script introduction
  init();

  // ask questions
  const answers = await askQuestions();
  const { FILENAME, EXTENSION } = answers;

  // create the file
  const filePath = createFile(FILENAME, EXTENSION);

  // show success message
  success(filePath);
};

来让我们通过运行 node index.js 来测试这个脚本,这是我们得到的:

完整代码

下述代码为完整代码:

#!/usr/bin/env node

const inquirer = require("inquirer");
const chalk = require("chalk");
const figlet = require("figlet");
const shell = require("shelljs");

const init = () => {
  console.log(
    chalk.green(
      figlet.textSync("Node JS CLI", {
        font: "Ghost",
        horizontalLayout: "default",
        verticalLayout: "default"
      })
    )
  );
};

const askQuestions = () => {
  const questions = [
    {
      name: "FILENAME",
      type: "input",
      message: "What is the name of the file without extension?"
    },
    {
      type: "list",
      name: "EXTENSION",
      message: "What is the file extension?",
      choices: [".rb", ".js", ".php", ".css"],
      filter: function(val) {
        return val.split(".")[1];
      }
    }
  ];
  return inquirer.prompt(questions);
};

const createFile = (filename, extension) => {
  const filePath = `${process.cwd()}/${filename}.${extension}`
  shell.touch(filePath);
  return filePath;
};

const success = filepath => {
  console.log(
    chalk.white.bgGreen.bold(`Done! File created at ${filepath}`)
  );
};

const run = async () => {
  // show script introduction
  init();

  // ask questions
  const answers = await askQuestions();
  const { FILENAME, EXTENSION } = answers;

  // create the file
  const filePath = createFile(FILENAME, EXTENSION);

  // show success message
  success(filePath);
};

run();

使用这个脚本

想要在其它地方执行这个脚本,在你的 package.json 文件中添加一个 bin 部分,并执行 npm link

{
  "name": "creator",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node index.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "chalk": "^2.4.1",
    "figlet": "^1.2.0",
    "inquirer": "^6.0.0",
    "shelljs": "^0.8.2"
  },
  "bin": {
    "creator": "./index.js"
  }
}

执行 npm link 使得这个脚本可以在任何地方调用。

这就是是当你运行这个命令时的结果。

/usr/bin/creator -> /usr/lib/node_modules/creator/index.js
/usr/lib/node_modules/creator -> /home/hugo/code/creator

这会连接 index.js 作为一个可执行文件。这是完全可能的,因为这个 CLI 脚本的第一行是 #!/usr/bin/env node

现在我们可以通过执行如下命令来调用。

$ creator

总结

正如你所看到的,Node.js 使得构建一个好的命令行工具变得非常简单。如果你希望了解更多内容,查看下列包。

  • meow:一个简单的命令行助手工具
  • yargs:一个命令行参数解析工具
  • pkg:将你的 Node.js 程序包装在一个可执行文件中。

在评论中留下你关于构建命令行工具的经验吧!


via: https://opensource.com/article/18/7/node-js-interactive-cli

作者:Hugo Dias 选题:lujun9972 译者:bestony 校对:wxy

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

这篇教程将向你展示如何更新服务器版本或者桌面版本的 Ubuntu。它还解释了更新和升级之间的区别以及你应该了解的有关于 Ubuntu Linux 中的更新的一些其他内容。

如果你是一个新手并已经体验 Ubuntu 数天或几周,你可能想知道如何更新你的 Ubuntu 系统以获取安全补丁,错误修复和应用升级。

更新 Ubuntu 非常简单。我并不是瞎说。它简单得只要运行两个命令。让我来告诉你这两个命令的更多细节。

请注意,本教程适用于 Ubuntu 18.04、16.04 或任何其他版本。命令行方式也适用于基于 Ubuntu 的发行版如 Linux Mint、Linux Lite、elementary OS 等。

通过命令行更新 Ubuntu

如何更新 Ubuntu

在桌面上,打开终端。你可以在菜单里找到它或者使用 Ctrl+Alt+T 快捷键。如果你是登录到一台 Ubuntu 服务器,那你已经在访问一个终端了。

在终端里,你只需要使用以下命令:

sudo apt update && sudo apt upgrade -y

它将询问你密码,而你可以使用你的账号密码。输入时,你将不会看到任何内容在屏幕上,所以请继续输入你的密码并按回车键。

现在,我来解释下上面的命令。

事实上,这不是一条命令。它由两条命令组成。符号 && 是合并两条命令的一个方法,第二条命令仅在前一条命令执行成功时执行。

当命令 apt upgrade 要求你在安装更新前确认时,末尾的参数 -y 会自动输入 yes

请注意,你也可以逐条使用这两条命令:

sudo apt update
sudo apt upgrade

这将花费更长的时间,因为你必须等待第一条命令执行完成后才能输入第二条命令。

说明:sudo apt update

这条命令更新了可用软件包的本地数据库。如果你没运行这条命令,本地数据库将不会被更新,而你的系统将不会知道是否有可用的新版本。

这就是为什么当你运行 sudo apt update,你会在输出中看到大量的 URL。这条命令会从对应的储存库(你在输出中看到的 URL)中获取软件包信息。

更新 Ubuntu Linux

在命令的末尾,它告诉你有多少个软件包可以被更新。你可以使用下列命令查看这些软件包:

apt list --upgradable

补充阅读: 阅读这篇文章了解命令 apt update 的输出中的 Ign、Hit 和 Get 是什么

说明:sudo apt upgrade

这条命令将已安装的软件包版本与本地数据库进行匹配。它收集全部信息,然后列出所有具有更新版本的软件包。此时,它会询问您是否要升级(已安装的软件包更新到新版本)。

通过命令行更新 Ubuntu Linux

你可以键入 yesy 或者只敲回车键去确认安装这些更新。

所以总的来说,sudo apt update 会检查可用的新版本,而 sudo apt upgrade 实际上会执行更新。

命令 update 可能会令人困惑,因为你可能期望通过命令 apt update 安装更新来更新系统,但这并不会发生。

通过 GUI 更新 Ubuntu(适用于桌面用户)

如果你使用桌面版 Ubuntu,你并不需要为了更新系统而打开终端。你可以仍可以使用命令行更新,但这只是一个选择。

在菜单里,找到 “软件更新” 并运行它。

在 Ubuntu 中运行 Software Updater

它将检查你的系统是否有可用的更新。

检查 Ubuntu 是否有可用更新

如果有可用的更新,它将给你提供安装更新的选择。

在 Ubuntu 中通过更新管理器安装更新

现在,点击 “安装”,它可能会向你询问密码。

通过 GUI 在 Ubuntu Linux 中安装更新

一旦你输入你的密码,它将开始安装更新。

通过 GUI 更新 Ubuntu

在某些情况下,你可能需要重启系统才能使已安装的更新正常工作。如果需要重启系统,你将在更新结束时收到通知。

通过 GUI 更新 Ubuntu

如果你不希望马上重启你的系统,可以选择稍后重启。

通过 GUI 在 Ubuntu 中安装更新

提示:如果“软件更新”返回一个错误,你需要在终端是使用命令 sudo apt update。输出的最后几行将包含真正的错误信息。你可以在因特网上搜索该错误并解决问题。

更新 Ubuntu 时要记住几件事

你刚学习了如何更新你的 Ubuntu 系统。如果你感兴趣,你还需要了解一些关于 Ubuntu 更新的内容。

更新后清理

你的系统将会有一些更新后不再需要的软件包。你可用使用这条命令删除这些软件包并释放空间

sudo apt autoremove

在 Ubuntu Server 中内核热修复以避免重启

如果是 Linux 内核更新,你将需要在系统更新后重启。当你不希望服务器停机时,这将会是一个问题。

热修复功能允许 Linux 内核在持续运行时打补丁。换句话说就是你不需要重启你的系统。

如果你在管理服务器,你可能需要在 Ubuntu 中启用热修复

版本升级是不同的

本文讨论的更新是使你安装的 Ubuntu 保持最新。但它不包括版本升级(例如从 Ubuntu 16.04 升级到 18.04)。

Ubuntu 版本 升级完全是另一回事。它更新整个操作系统核心。你需要在这个漫长的过程开始前做好备份。

总结

我希望你喜欢这个关于 Ubuntu 系统更新的教程并学到一些新东西。

如果你有其他问题,请随时提出。如果你是一位经验丰富的 Linux 用户并且有些更好的技巧,请同我们分享。


via: https://itsfoss.com/update-ubuntu/

作者:Abhishek Prakash 选题:lujun9972 译者:LazyWolfLin 校对:wxy

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

如何构建一台网络引导服务器(一) 中,我们提供了一个极简的 iPXE 引导脚本来引导你的网络引导镜像。许多用户除了使用网络引导镜像外,可能在机器本地也有一个操作系统。但是使用常见的工作站的 BIOS 去切换引导加载器是很笨拙的。在本系列文件的第三部分,我们将向你展示如何设置一个更复杂的 iPXE 配置。它将允许终端用户以更容易的方式去选择引导哪个操作系统。它也可以配置为让系统管理员从一台中央服务器来统一管理引导菜单。

一个交互式 iPXE 引导菜单

下面这些命令重定义了网络引导镜像的 boot.cfg 来作为一个交互式的 iPXE 引导菜单,并使用了一个 5 秒倒计时的定时器:

$ MY_FVER=29
$ MY_KRNL=$(ls -c /fc$MY_FVER/lib/modules | head -n 1)
$ MY_DNS1=192.0.2.91
$ MY_DNS2=192.0.2.92
$ MY_NAME=server-01.example.edu
$ MY_EMAN=$(echo $MY_NAME | tr '.' "\n" | tac | tr "\n" '.' | cut -b -${#MY_NAME})
$ MY_ADDR=$(host -t A $MY_NAME | awk '{print $4}')
$ cat << END > $HOME/esp/linux/boot.cfg
#!ipxe

set timeout 5000

:menu
menu iPXE Boot Menu
item --key 1 lcl 1. Microsoft Windows 10
item --key 2 f$MY_FVER 2. RedHat Fedora $MY_FVER
choose --timeout \${timeout} --default lcl selected || goto shell
set timeout 0
goto \${selected}

:failed
echo boot failed, dropping to shell...
goto shell

:shell
echo type 'exit' to get the back to the menu
set timeout 0
shell
goto menu

:lcl
exit

:f$MY_FVER
kernel --name kernel.efi \${prefix}/vmlinuz-$MY_KRNL initrd=initrd.img ro ip=dhcp rd.peerdns=0 nameserver=$MY_DNS1 nameserver=$MY_DNS2 root=/dev/disk/by-path/ip-$MY_ADDR:3260-iscsi-iqn.$MY_EMAN:fc$MY_FVER-lun-1 netroot=iscsi:$MY_ADDR::::iqn.$MY_EMAN:fc$MY_FVER console=tty0 console=ttyS0,115200n8 audit=0 selinux=0 quiet
initrd --name initrd.img \${prefix}/initramfs-$MY_KRNL.img
boot || goto failed
END

上述菜单有五个节:

  • menu 定义了显示在屏幕上的实际菜单内容。
  • failed 提示用户发生了错误,并将用户带到 shell 以错误错误。
  • shell 提供了交互式命令提示符。你可以在引导菜单出现时按下 Esc 键进入,或者是 boot 命令失败时也会进入到命令提示符。
  • lcl 包含一个提供给 iPXE 退出的简单命令,以及返还控制权给 BIOS。在 iPXE 之后,无论你希望缺省引导的设备(即:工作站的本地硬件)是什么,都必须在你的工作站的 BIOS 中正确地作为下一个引导设备列出来。
  • f29 包含前面文章提到同一个网络引导代码,但使用最终的退出代码来替换掉 goto failed

从你的 $HOME/esp/linux 目录中复制更新后的 boot.cfg 到所有客户端系统的 ESP 中。如果一切顺利,你应该会看到类似下面图片的结果:

一个服务器托管的引导菜单

你可以添加到网络引导服务器的另一个特性是,能够从一台中央位置去管理所有客户端的引导菜单。这个特性尤其适用于批量安装(升级)一个新版本的操作系统。在你将新内核和新的 initramfs 复制到所有客户端的 ESP 之后,这个特性可以让你执行一种 原子事务) 去切换所有客户端到新操作系统。

安装 Mojolicious:

$ sudo -i
# dnf install -y perl-Mojolicious

定义 “bootmenu” 应用程序:

# mkdir /opt/bootmenu
# cat << END > /opt/bootmenu/bootmenu.pl
#!/usr/bin/env perl
use Mojolicious::Lite;
use Mojolicious::Plugins;

plugin 'Config';

get '/menu';

app->start;
END
# chmod 755 /opt/bootmenu/bootmenu.pl

为 “bootmenu” 应用程序定义配置文件:

# cat << END > /opt/bootmenu/bootmenu.conf
{
 hypnotoad => {
 listen => ['http://*:80'],
 pid_file => '/run/bootmenu/bootmenu.pid',
 }
}
END

这是一个非常简单的 Mojolicious 应用程序,它监听 80 端口,并且只回应对 /menu 的请求。如果你想快速了解 Mojolicious 能做什么,运行 man Mojolicious::Guides::Growing 去查看手册。按 Q 键退出手册。

boot.cfg 移到我们的网络引导应用程序中作为一个名为 menu.html.ep 的模板:

# mkdir /opt/bootmenu/templates
# mv $HOME/esp/linux/boot.cfg /opt/bootmenu/templates/menu.html.ep

定义一个 systemd 服务去管理引导菜单应用程序:

# cat << END > /etc/systemd/system/bootmenu.service
[Unit]
Description=Serves iPXE Menus over HTTP
After=network-online.target

[Service]
Type=forking
DynamicUser=true
RuntimeDirectory=bootmenu
PIDFile=/run/bootmenu/bootmenu.pid
ExecStart=/usr/bin/hypnotoad /opt/bootmenu/bootmenu.pl
ExecReload=/usr/bin/hypnotoad /opt/bootmenu/bootmenu.pl
AmbientCapabilities=CAP_NET_BIND_SERVICE
KillMode=process

[Install]
WantedBy=multi-user.target
END

在本地防火墙中为 HTTP 服务添加一个例外规则,并启动 bootmenu 服务:

# firewall-cmd --add-service http
# firewall-cmd --runtime-to-permanent
# systemctl enable bootmenu.service
# systemctl start bootmenu.service

wget 测试它:

$ sudo dnf install -y wget
$ MY_BOOTMENU_SERVER=server-01.example.edu
$ wget -q -O - http://$MY_BOOTMENU_SERVER/menu

以上的命令应该会输出类似下面的内容:

#!ipxe

set timeout 5000

:menu
menu iPXE Boot Menu
item --key 1 lcl 1. Microsoft Windows 10
item --key 2 f29 2. RedHat Fedora 29
choose --timeout ${timeout} --default lcl selected || goto shell
set timeout 0
goto ${selected}

:failed
echo boot failed, dropping to shell...
goto shell

:shell
echo type 'exit' to get the back to the menu
set timeout 0
shell
goto menu

:lcl
exit

:f29
kernel --name kernel.efi ${prefix}/vmlinuz-4.19.4-300.fc29.x86_64 initrd=initrd.img ro ip=dhcp rd.peerdns=0 nameserver=192.0.2.91 nameserver=192.0.2.92 root=/dev/disk/by-path/ip-192.0.2.158:3260-iscsi-iqn.edu.example.server-01:fc29-lun-1 netroot=iscsi:192.0.2.158::::iqn.edu.example.server-01:fc29 console=tty0 console=ttyS0,115200n8 audit=0 selinux=0 quiet
initrd --name initrd.img ${prefix}/initramfs-4.19.4-300.fc29.x86_64.img
boot || goto failed

现在,引导菜单服务器已经正常工作了,重新构建 ipxe.efi 引导加载器,使用一个初始化脚本指向它。

第一步,先更新我们在本系列文章的第一部分中创建的 init.ipxe 脚本:

$ MY_BOOTMENU_SERVER=server-01.example.edu
$ cat << END > $HOME/ipxe/init.ipxe
#!ipxe

dhcp || exit
set prefix file:///linux
chain http://$MY_BOOTMENU_SERVER/menu || exit
END

现在,重新构建引导加载器:

$ cd $HOME/ipxe/src
$ make clean
$ make bin-x86_64-efi/ipxe.efi EMBED=../init.ipxe

将更新后的引导加载器复制到你的 ESP 中:

$ cp $HOME/ipxe/src/bin-x86_64-efi/ipxe.efi $HOME/esp/efi/boot/bootx64.efi

将更新后的引导加载器复制到所有的客户端中之后,以后更新引导菜单只需要简单地编辑 /opt/bootmenu/templates/menu.html.ep 文件,然后再运行如下命令:

$ sudo systemctl restart bootmenu.service

做一步的改变

如果引导菜单服务器工作正常,在你的客户端系统上的 boot.cfg 文件将更长。

比如,重新添加 Fedora 28 镜像到引导菜单中:

$ sudo -i
# MY_FVER=28
# MY_KRNL=$(ls -c /fc$MY_FVER/lib/modules | head -n 1)
# MY_DNS1=192.0.2.91
# MY_DNS2=192.0.2.92
# MY_NAME=$(</etc/hostname)
# MY_EMAN=$(echo $MY_NAME | tr '.' "\n" | tac | tr "\n" '.' | cut -b -${#MY_NAME})
# MY_ADDR=$(host -t A $MY_NAME | awk '{print $4}')
# cat << END >> /opt/bootmenu/templates/menu.html.ep

:f$MY_FVER
kernel --name kernel.efi \${prefix}/vmlinuz-$MY_KRNL initrd=initrd.img ro ip=dhcp rd.peerdns=0 nameserver=$MY_DNS1 nameserver=$MY_DNS2 root=/dev/disk/by-path/ip-$MY_ADDR:3260-iscsi-iqn.$MY_EMAN:fc$MY_FVER-lun-1 netroot=iscsi:$MY_ADDR::::iqn.$MY_EMAN:fc$MY_FVER console=tty0 console=ttyS0,115200n8 audit=0 selinux=0 quiet
initrd --name initrd.img \${prefix}/initramfs-$MY_KRNL.img
boot || goto failed
END
# sed -i "/item --key 2/a item --key 3 f$MY_FVER 3. RedHat Fedora $MY_FVER" /opt/bootmenu/templates/menu.html.ep
# systemctl restart bootmenu.service

如果一切顺利,你的客户端下次引导时,应该看到如下图所示的结果:


via: https://fedoramagazine.org/how-to-build-a-netboot-server-part-3/

作者:Gregory Bartholomew 选题:lujun9972 译者:qhwdw 校对:wxy

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