Linux 中国徽标征集活动公开评选

Summary: 本次公开投票将在微信投票上进行，敬请来自其他平台的读者和爱好者移步至微信投票。

个人觉得微信投票不太公平呢。很多人根本不会点开Github看详情就投了，而且还可以发给好友投票。而且分成两个多选也很不科学，即使下面乘了4/30也不公平。希望最后还是看委员会结果吧，微信投票就图一乐惹(*￣︶￣)

吐槽一下感觉有些作品感觉是用画图软件画来凑数的吧(*￣︶￣)而且有些设计风格我实在不能苟同。我比较欣赏的设计风格大家可以参考一下MangoDB的官网，是开源的项目呢

4号，16号个人感觉喜欢。

28号作品

把重设logo的精力放在改进网页代码上不好吗？像这样字体不能放大否则就出窗口的网站真的已经很少了～

喜欢4号,简约大气 gif做的也很好

本次公开投票将在微信投票上进行，敬请来自其他平台的读者和爱好者移步至微信投票。

# Linux 中国徽标征集活动公开评选

<img src="content_image/album_201910_22_142227vq0gjj00qabbo09z.jpg" alt="" title="">

一个月前，我们发起了徽标公开征集活动，这次活动得到了很多成员和爱好者的积极关注，截止至昨天，我们收到了 25 位设计师提交的多份设计投稿。

按照计划，我们将从今天开始，至 10 月 30 日公示投稿，并发起投票活动请大家投票支持。并在投票结果的基础上，由我们的管理委员会做最终裁定（意即，并不会以投票结果作为最终结果，投票结果会作为一个重要的参考因素）。

**本次公开投票将在微信投票上进行，敬请来自其他平台的读者和爱好者移步至微信投票。**

此外，在活动中，一直有声音表示，原本的徽标就挺好，或者原本徽标只需要做适当修改即可。对于这种意见，说实话我是有点吃惊的。不过尊重大家的意见，我决定将原本的徽标也放入评选，如果原本的徽标胜出，那这次的一等奖可能就要空缺了，我们就不会更换徽标（或对原本的徽标做少量修改）。

以下是这次投稿的徽标，

* 按投稿时间列出
* 变体视作同一徽标
* 尺寸裁剪至同一大小（128px），缩放过程中会有精度损失
* 设计思路在此不展示，可访问在 GitHub 上的原始投稿了解

### 徽标投稿

#### 1、原本的徽标

|  |
| --- |
|     |

#### 2、wxy

|  |
| --- |
|  |

地址： <https://github.com/LCTT/logo/tree/master/wxy>

#### 3、RedInLinux

|  |
| --- |
|  |

地址：<https://github.com/LCTT/logo/tree/master/RedInLinux>

#### 4、1466587594

|  |
| --- |
|     |

地址：<https://github.com/LCTT/logo/tree/master/1466587594>

#### 5、tinnx

|  |
| --- |
|  |

地址：<https://github.com/LCTT/logo/tree/master/tinnx>

#### 6、alim0x

|  |
| --- |
|  |

地址：<https://github.com/LCTT/logo/tree/master/alim0x>

#### 7、flag

|  |
| --- |
|   |

地址：<https://github.com/LCTT/logo/tree/master/flag-2>

#### 8、WSJ

|  |
| --- |
|  |

地址：<https://github.com/LCTT/logo/tree/master/WSJ>

#### 9、logo0281

|  |
| --- |
|  |

地址：<https://github.com/LCTT/logo/tree/master/logo0281>

#### 10、logo0964

|  |
| --- |
|  |

地址：<https://github.com/LCTT/logo/tree/master/logo0964>

#### 11、long

|  |
| --- |
|  |

地址：<https://github.com/LCTT/logo/tree/master/long>

#### 12、kokialoves

|  |
| --- |
|  |

地址：<https://github.com/LCTT/logo/tree/master/kokialoves>

#### 13、hacker

|  |
| --- |
|  |

地址：<https://github.com/LCTT/logo/tree/master/hacker>

#### 14、lartpang

|  |
| --- |
|   |

地址：<https://github.com/LCTT/logo/tree/master/lartpang>

#### 15、OLC

|  |
| --- |
|   |

地址：<https://github.com/LCTT/logo/tree/master/OLC>

#### 16、garywill

|  |
| --- |
|     |

地址：<https://github.com/LCTT/logo/tree/master/garywill>

#### 17、lightyisu

|  |
| --- |
|  |

地址：<https://github.com/LCTT/logo/tree/master/lightyisu>

#### 18、lightyisu2

|  |
| --- |
|  |

地址：<https://github.com/LCTT/logo/blob/master/lightyisu2>

#### 19、icekylin-design-1

|  |
| --- |
|       |

地址：<https://github.com/LCTT/logo/tree/master/icekylin-design>

#### 20、icekylin-design-2

|  |
| --- |
|      |

地址：<https://github.com/LCTT/logo/tree/master/icekylin-design>

#### 21、icekylin-design-3

|  |
| --- |
|      |

地址：<https://github.com/LCTT/logo/tree/master/icekylin-design>

#### 22、whsasf\_work

|  |
| --- |
|   |
|   |

地址：<https://github.com/LCTT/logo/tree/master/whsasf_work>

#### 23、whsasf\_work2

|  |
| --- |
|     |

地址：<https://github.com/LCTT/logo/tree/master/whsasf_work2>

#### 24、whsasf\_work3

|  |
| --- |
|  |

地址：<https://github.com/LCTT/logo/tree/master/whsasf_work3>

#### 25、CodingOctocat

|  |
| --- |
|  |

地址：<https://github.com/LCTT/logo/tree/master/CodingOctocat>

#### 26、JUNEN-1

|  |
| --- |
|     |

地址：<https://github.com/LCTT/logo/tree/master/JUNEN>

#### 27、JUNEN-2

|  |
| --- |
|     |

地址：<https://github.com/LCTT/logo/tree/master/JUNEN>

#### 28、ZIN

|  |
| --- |
|  |

地址：<https://github.com/LCTT/logo/tree/master/ZIN>

#### 29、Fine

|  |
| --- |
|  |

地址：<https://github.com/LCTT/logo/tree/master/Fine>

#### 30、SmarterC

|  |
| --- |
|     |

地址：<https://github.com/LCTT/logo/blob/master/SmarterC>

#### 31、water0902

|  |
| --- |
|        |

地址：<https://github.com/LCTT/logo/tree/master/water0902>

#### 32、logo yk

|  |
| --- |
|  |

地址：<https://github.com/LCTT/logo/blob/master/logo%20yk>

#### 33、schiway

|  |
| --- |
|  |

地址：<https://github.com/LCTT/logo/blob/master/schiway>

#### 34、aimeDesign

|  |
| --- |
|   |

地址：<https://github.com/LCTT/logo/blob/master/aimeDesign>


在 Linux 中使用 Bash 脚本删除早于 “X” 天的文件/文件夹

Summary: 该脚本可防止来自 监控工具 的警报，因为我们会在填满磁盘空间之前删除旧的日志文件。

# 在 Linux 中使用 Bash 脚本删除早于 “X” 天的文件/文件夹

<img src="content_image/album_201910_22_150713e1brlrxnowxcxx7d.jpg" alt="" title="">

[磁盘使用率](https://www.2daygeek.com/linux-check-disk-usage-files-and-directories-folders-size-du-command/) 监控工具能够在达到给定阈值时提醒我们。但它们无法自行解决 [磁盘使用率](https://www.2daygeek.com/linux-check-disk-space-usage-df-command/) 问题。需要手动干预才能解决该问题。

如果你想完全自动化此类操作，你会做什么。是的，可以使用 bash 脚本来完成。

该脚本可防止来自 [监控工具](https://www.2daygeek.com/category/monitoring-tools/) 的警报，因为我们会在填满磁盘空间之前删除旧的日志文件。

我们过去做了很多 shell 脚本。如果要查看，请进入下面的链接。

* [如何使用 shell 脚本自动化日常活动？](https://www.2daygeek.com/category/shell-script/)

我在本文中添加了两个 bash 脚本，它们有助于清除旧日志。

### 1）在 Linux 中删除早于 “X” 天的文件夹的 Bash 脚本

我们有一个名为 `/var/log/app/` 的文件夹，其中包含 15 天的日志，我们将删除早于 10 天的文件夹。

```
$ ls -lh /var/log/app/

drwxrw-rw- 3 root root  24K Oct  1 23:52 app_log.01
drwxrw-rw- 3 root root  24K Oct  2 23:52 app_log.02
drwxrw-rw- 3 root root  24K Oct  3 23:52 app_log.03
drwxrw-rw- 3 root root  24K Oct  4 23:52 app_log.04
drwxrw-rw- 3 root root  24K Oct  5 23:52 app_log.05
drwxrw-rw- 3 root root  24K Oct  6 23:54 app_log.06
drwxrw-rw- 3 root root  24K Oct  7 23:53 app_log.07
drwxrw-rw- 3 root root  24K Oct  8 23:51 app_log.08
drwxrw-rw- 3 root root  24K Oct  9 23:52 app_log.09
drwxrw-rw- 3 root root  24K Oct 10 23:52 app_log.10
drwxrw-rw- 3 root root  24K Oct 11 23:52 app_log.11
drwxrw-rw- 3 root root  24K Oct 12 23:52 app_log.12
drwxrw-rw- 3 root root  24K Oct 13 23:52 app_log.13
drwxrw-rw- 3 root root  24K Oct 14 23:52 app_log.14
drwxrw-rw- 3 root root  24K Oct 15 23:52 app_log.15
```

该脚本将删除早于 10 天的文件夹，并通过邮件发送文件夹列表。

你可以根据需要修改 `-mtime X` 的值。另外，请替换你的电子邮箱，而不是用我们的。

```
# /opt/script/delete-old-folders.sh

#!/bin/bash
prev_count=0
fpath=/var/log/app/app_log.*
find $fpath -type d -mtime +10  -exec ls -ltrh {} \; > /tmp/folder.out
find $fpath -type d -mtime +10  -exec rm -rf {} \;
count=$(cat /tmp/folder.out | wc -l)
if [ "$prev_count" -lt "$count" ] ; then
MESSAGE="/tmp/file1.out"
TO="[email protected]"
echo "Application log folders are deleted older than 15 days" >> $MESSAGE
echo "+----------------------------------------------------+" >> $MESSAGE
echo "" >> $MESSAGE
cat /tmp/folder.out | awk '{print $6,$7,$9}' >> $MESSAGE
echo "" >> $MESSAGE
SUBJECT="WARNING: Apache log files are deleted older than 15 days $(date)"
mail -s "$SUBJECT" "$TO" < $MESSAGE
rm $MESSAGE /tmp/folder.out
fi
```

给 `delete-old-folders.sh` 设置可执行权限。

```
# chmod +x /opt/script/delete-old-folders.sh
```

最后添加一个 [cronjob](https://www.2daygeek.com/crontab-cronjob-to-schedule-jobs-in-linux/) 自动化此任务。它于每天早上 7 点运行。

```
# crontab -e

0 7 * * * /bin/bash /opt/script/delete-old-folders.sh
```

你将看到类似下面的输出。

```
Application log folders are deleted  older than 20 days
+--------------------------------------------------------+
Oct 11 /var/log/app/app_log.11
Oct 12 /var/log/app/app_log.12
Oct 13 /var/log/app/app_log.13
Oct 14 /var/log/app/app_log.14
Oct 15 /var/log/app/app_log.15
```

### 2）在 Linux 中删除早于 “X” 天的文件的 Bash 脚本

我们有一个名为 `/var/log/apache/` 的文件夹，其中包含15天的日志，我们将删除 10 天前的文件。

以下文章与该主题相关，因此你可能有兴趣阅读。

* [如何在 Linux 中查找和删除早于 “X” 天和 “X” 小时的文件？](https://www.2daygeek.com/how-to-find-and-delete-files-older-than-x-days-and-x-hours-in-linux/)
* [如何在 Linux 中查找最近修改的文件/文件夹](https://www.2daygeek.com/check-find-recently-modified-files-folders-linux/)
* [如何在 Linux 中自动删除或清理 /tmp 文件夹内容？](https://www.2daygeek.com/automatically-delete-clean-up-tmp-directory-folder-contents-in-linux/)

```
# ls -lh /var/log/apache/

-rw-rw-rw- 3 root root  24K Oct  1 23:52 2daygeek_access.01
-rw-rw-rw- 3 root root  24K Oct  2 23:52 2daygeek_access.02
-rw-rw-rw- 3 root root  24K Oct  3 23:52 2daygeek_access.03
-rw-rw-rw- 3 root root  24K Oct  4 23:52 2daygeek_access.04
-rw-rw-rw- 3 root root  24K Oct  5 23:52 2daygeek_access.05
-rw-rw-rw- 3 root root  24K Oct  6 23:54 2daygeek_access.06
-rw-rw-rw- 3 root root  24K Oct  7 23:53 2daygeek_access.07
-rw-rw-rw- 3 root root  24K Oct  8 23:51 2daygeek_access.08
-rw-rw-rw- 3 root root  24K Oct  9 23:52 2daygeek_access.09
-rw-rw-rw- 3 root root  24K Oct 10 23:52 2daygeek_access.10
-rw-rw-rw- 3 root root  24K Oct 11 23:52 2daygeek_access.11
-rw-rw-rw- 3 root root  24K Oct 12 23:52 2daygeek_access.12
-rw-rw-rw- 3 root root  24K Oct 13 23:52 2daygeek_access.13
-rw-rw-rw- 3 root root  24K Oct 14 23:52 2daygeek_access.14
-rw-rw-rw- 3 root root  24K Oct 15 23:52 2daygeek_access.15
```

该脚本将删除 10 天前的文件并通过邮件发送文件夹列表。

你可以根据需要修改 `-mtime X` 的值。另外，请替换你的电子邮箱，而不是用我们的。

```
# /opt/script/delete-old-files.sh

#!/bin/bash
prev_count=0
fpath=/var/log/apache/2daygeek_access.*
find $fpath -type f -mtime +15  -exec ls -ltrd {} \; > /tmp/file.out
find $fpath -type f -mtime +15  -exec rm -rf {} \;
count=$(cat /tmp/file.out | wc -l)
if [ "$prev_count" -lt "$count" ] ; then
MESSAGE="/tmp/file1.out"
TO="[email protected]"
echo "Apache Access log files are deleted older than 20 days"  >> $MESSAGE
echo "+--------------------------------------------- +" >> $MESSAGE
echo "" >> $MESSAGE
cat /tmp/file.out | awk '{print $6,$7,$9}' >> $MESSAGE
echo "" >> $MESSAGE
SUBJECT="WARNING: Apache log folders are deleted older than 15 days $(date)"
mail -s "$SUBJECT" "$TO" < $MESSAGE
rm $MESSAGE /tmp/file.out
fi
```

给 `delete-old-files.sh` 设置可执行权限。

```
# chmod +x /opt/script/delete-old-files.sh
```

最后添加一个 [cronjob](https://www.2daygeek.com/crontab-cronjob-to-schedule-jobs-in-linux/) 自动化此任务。它于每天早上 7 点运行。

```
# crontab -e

0 7 * * * /bin/bash /opt/script/delete-old-folders.sh
```

你将看到类似下面的输出。

```
Apache Access log files are deleted older than 20 days
+--------------------------------------------------------+
Oct 11 /var/log/apache/2daygeek_access.11
Oct 12 /var/log/apache/2daygeek_access.12
Oct 13 /var/log/apache/2daygeek_access.13
Oct 14 /var/log/apache/2daygeek_access.14
Oct 15 /var/log/apache/2daygeek_access.15
```

---

via: <https://www.2daygeek.com/bash-script-to-delete-files-folders-older-than-x-days-in-linux/>

作者：[Magesh Maruthamuthu](https://www.2daygeek.com/author/magesh/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


如何在批处理模式下运行 top 命令

Summary: 可能已经知道 top 命令的绝大部分操作，除了很少的几个操作，如果我没错的话，批处理模式就是其中之一。

# 如何在批处理模式下运行 top 命令

<img src="content_image/album_201910_22_235420ylswdescv5ddffit.jpg" alt="" title="">

[top 命令](https://www.2daygeek.com/linux-top-command-linux-system-performance-monitoring-tool/) 是每个人都在使用的用于 [监控 Linux 系统性能](https://www.2daygeek.com/category/system-monitoring/) 的最好的命令。你可能已经知道 `top` 命令的绝大部分操作，除了很少的几个操作，如果我没错的话，批处理模式就是其中之一。

大部分的脚本编写者和开发人员都知道这个，因为这个操作主要就是用来编写脚本。

如果你不了解这个，不用担心，我们将在这里介绍它。

### 什么是 top 命令的批处理模式

批处理模式允许你将 `top` 命令的输出发送至其他程序或者文件中。

在这个模式中，`top` 命令将不会接收输入并且持续运行，直到迭代次数达到你用 `-n` 选项指定的次数为止。

如果你想解决 Linux 服务器上的任何性能问题，你需要正确的 [理解 top 命令的输出](https://www.2daygeek.com/understanding-linux-top-command-output-usage/)。

### 1) 如何在批处理模式下运行 top 命令

默认地，`top` 命令按照 CPU 的使用率来排序输出结果，所以当你在批处理模式中运行以下命令时，它会执行同样的操作并打印前 35 行：

```
# top -bc | head -35

top - 06:41:14 up 8 days, 20:24,  1 user,  load average: 0.87, 0.77, 0.81
Tasks: 139 total,   1 running, 136 sleeping,   0 stopped,   2 zombie
%Cpu(s):  0.0 us,  3.2 sy,  0.0 ni, 96.8 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  3880940 total,  1595932 free,   886736 used,  1398272 buff/cache
KiB Swap:  1048572 total,   514640 free,   533932 used.  2648472 avail Mem

PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
     1 root      20   0  191144   2800   1596 S   0.0  0.1   5:43.63 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
     2 root      20   0       0      0      0 S   0.0  0.0   0:00.32 [kthreadd]
     3 root      20   0       0      0      0 S   0.0  0.0   0:28.10 [ksoftirqd/0]
     5 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [kworker/0:0H]
     7 root      rt   0       0      0      0 S   0.0  0.0   0:33.96 [migration/0]
     8 root      20   0       0      0      0 S   0.0  0.0   0:00.00 [rcu_bh]
     9 root      20   0       0      0      0 S   0.0  0.0  63:05.12 [rcu_sched]
    10 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [lru-add-drain]
    11 root      rt   0       0      0      0 S   0.0  0.0   0:08.79 [watchdog/0]
    12 root      rt   0       0      0      0 S   0.0  0.0   0:08.82 [watchdog/1]
    13 root      rt   0       0      0      0 S   0.0  0.0   0:44.27 [migration/1]
    14 root      20   0       0      0      0 S   0.0  0.0   1:22.45 [ksoftirqd/1]
    16 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [kworker/1:0H]
    18 root      20   0       0      0      0 S   0.0  0.0   0:00.01 [kdevtmpfs]
    19 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [netns]
    20 root      20   0       0      0      0 S   0.0  0.0   0:01.35 [khungtaskd]
    21 root       0 -20       0      0      0 S   0.0  0.0   0:00.02 [writeback]
    22 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [kintegrityd]
    23 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [bioset]
    24 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [kblockd]
    25 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [md]
    26 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [edac-poller]
    33 root      20   0       0      0      0 S   0.0  0.0   1:19.07 [kswapd0]
    34 root      25   5       0      0      0 S   0.0  0.0   0:00.00 [ksmd]
    35 root      39  19       0      0      0 S   0.0  0.0   0:12.80 [khugepaged]
    36 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [crypto]
    44 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [kthrotld]
    46 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [kmpath_rdacd]
```

### 2) 如何在批处理模式下运行 top 命令并按内存使用率排序结果

在批处理模式中运行以下命令按内存使用率对结果进行排序：

```
# top -bc -o +%MEM | head -n 20

top - 06:42:00 up 8 days, 20:25,  1 user,  load average: 0.66, 0.74, 0.80
Tasks: 146 total,   1 running, 145 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  3880940 total,  1422044 free,  1059176 used,  1399720 buff/cache
KiB Swap:  1048572 total,   514640 free,   533932 used.  2475984 avail Mem

 PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 18105 mysql     20   0 1453900 156096   8816 S   0.0  4.0   2:12.98 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid
  1841 root      20   0  228980 107036   5360 S   0.0  2.8   0:05.56 /usr/local/cpanel/3rdparty/perl/528/bin/perl -T -w /usr/local/cpanel/3rdparty/bin/spamd --max-children=3 --max-spare=1 --allowed-ips=127.0.0.+
  4301 root      20   0  230208 104608   1816 S   0.0  2.7   0:03.77 spamd child
  8139 nobody    20   0  257000  27108   3408 S   0.0  0.7   0:00.04 /usr/sbin/httpd -k start
  7961 nobody    20   0  256988  26912   3160 S   0.0  0.7   0:00.05 /usr/sbin/httpd -k start
  8190 nobody    20   0  256976  26812   3140 S   0.0  0.7   0:00.05 /usr/sbin/httpd -k start
  8353 nobody    20   0  256976  26812   3144 S   0.0  0.7   0:00.04 /usr/sbin/httpd -k start
  8629 nobody    20   0  256856  26736   3108 S   0.0  0.7   0:00.02 /usr/sbin/httpd -k start
  8636 nobody    20   0  256856  26712   3100 S   0.0  0.7   0:00.03 /usr/sbin/httpd -k start
  8611 nobody    20   0  256844  25764   2228 S   0.0  0.7   0:00.01 /usr/sbin/httpd -k start
  8451 nobody    20   0  256844  25760   2220 S   0.0  0.7   0:00.04 /usr/sbin/httpd -k start
  8610 nobody    20   0  256844  25748   2224 S   0.0  0.7   0:00.01 /usr/sbin/httpd -k start
  8632 nobody    20   0  256844  25744   2216 S   0.0  0.7   0:00.03 /usr/sbin/httpd -k start
```

上面命令的详细信息：

* `-b`：批处理模式选项
* `-c`：打印运行中的进程的绝对路径
* `-o`：指定进行排序的字段
* `head`：输出文件的第一部分
* `-n`：打印前 n 行

### 3) 如何在批处理模式下运行 top 命令并按照指定的用户进程对结果进行排序

如果你想要按照指定用户进程对结果进行排序请运行以下命令：

```
# top -bc -u mysql | head -n 10

top - 06:44:58 up 8 days, 20:27,  1 user,  load average: 0.99, 0.87, 0.84
Tasks: 140 total,   1 running, 137 sleeping,   0 stopped,   2 zombie
%Cpu(s): 13.3 us,  3.3 sy,  0.0 ni, 83.3 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  3880940 total,  1589832 free,   885648 used,  1405460 buff/cache
KiB Swap:  1048572 total,   514640 free,   533932 used.  2649412 avail Mem

 PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
 18105 mysql     20   0 1453900 156888   8816 S   0.0  4.0   2:16.42 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid
```

### 4) 如何在批处理模式下运行 top 命令并按照处理时间进行排序

在批处理模式中使用以下 `top` 命令按照处理时间对结果进行排序。这展示了任务从启动以来已使用的总 CPU 时间。

但是如果你想要检查一个进程在 Linux 上运行了多长时间请看接下来的文章：

* [检查 Linux 中进程运行时间的五种方法](https://www.2daygeek.com/how-to-check-how-long-a-process-has-been-running-in-linux/)

```
# top -bc -o TIME+ | head -n 20

top - 06:45:56 up 8 days, 20:28,  1 user,  load average: 0.56, 0.77, 0.81
Tasks: 148 total,   1 running, 146 sleeping,   0 stopped,   1 zombie
%Cpu(s):  0.0 us,  3.1 sy,  0.0 ni, 96.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  3880940 total,  1378664 free,  1094876 used,  1407400 buff/cache
KiB Swap:  1048572 total,   514640 free,   533932 used.  2440332 avail Mem

 PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
     9 root      20   0       0      0      0 S   0.0  0.0  63:05.70 [rcu_sched]
   272 root      20   0       0      0      0 S   0.0  0.0  16:12.13 [xfsaild/vda1]
  3882 root      20   0  229832   6212   1220 S   0.0  0.2   9:00.84 /usr/sbin/httpd -k start
     1 root      20   0  191144   2800   1596 S   0.0  0.1   5:43.75 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
  3761 root      20   0   68784   9820   2048 S   0.0  0.3   5:09.67 tailwatchd
  3529 root      20   0  404380   3472   2604 S   0.0  0.1   3:24.98 /usr/sbin/rsyslogd -n
  3520 root      20   0  574208    572    164 S   0.0  0.0   3:07.74 /usr/bin/python2 -Es /usr/sbin/tuned -l -P
   444 dbus      20   0   58444   1144    612 S   0.0  0.0   2:23.90 /usr/bin/dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation
 18105 mysql     20   0 1453900 157152   8816 S   0.0  4.0   2:17.29 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid
   249 root       0 -20       0      0      0 S   0.0  0.0   1:28.83 [kworker/0:1H]
    14 root      20   0       0      0      0 S   0.0  0.0   1:22.46 [ksoftirqd/1]
    33 root      20   0       0      0      0 S   0.0  0.0   1:19.07 [kswapd0]
   342 root      20   0   39472   2940   2752 S   0.0  0.1   1:18.17 /usr/lib/systemd/systemd-journald
```

### 5) 如何在批处理模式下运行 top 命令并将结果保存到文件中

如果出于解决问题的目的，你想要和别人分享 `top` 命令的输出，请使用以下命令重定向输出到文件中：

```
# top -bc | head -35 > top-report.txt

# cat top-report.txt

top - 06:47:11 up 8 days, 20:30,  1 user,  load average: 0.67, 0.77, 0.81
Tasks: 133 total,   4 running, 129 sleeping,   0 stopped,   0 zombie
%Cpu(s): 59.4 us, 12.5 sy,  0.0 ni, 28.1 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  3880940 total,  1596268 free,   843284 used,  1441388 buff/cache
KiB Swap:  1048572 total,   514640 free,   533932 used.  2659084 avail Mem

 PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
  9686 daygeekc  20   0  406132  62184  43448 R  94.1  1.6   0:00.34 /opt/cpanel/ea-php56/root/usr/bin/php-cgi
  9689 nobody    20   0  256588  24428   1184 S   5.9  0.6   0:00.01 /usr/sbin/httpd -k start
     1 root      20   0  191144   2800   1596 S   0.0  0.1   5:43.79 /usr/lib/systemd/systemd --switched-root --system --deserialize 22
     2 root      20   0       0      0      0 S   0.0  0.0   0:00.32 [kthreadd]
     3 root      20   0       0      0      0 S   0.0  0.0   0:28.11 [ksoftirqd/0]
     5 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [kworker/0:0H]
     7 root      rt   0       0      0      0 S   0.0  0.0   0:33.96 [migration/0]
     8 root      20   0       0      0      0 S   0.0  0.0   0:00.00 [rcu_bh]
     9 root      20   0       0      0      0 R   0.0  0.0  63:05.82 [rcu_sched]
    10 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [lru-add-drain]
    11 root      rt   0       0      0      0 S   0.0  0.0   0:08.79 [watchdog/0]
    12 root      rt   0       0      0      0 S   0.0  0.0   0:08.82 [watchdog/1]
    13 root      rt   0       0      0      0 S   0.0  0.0   0:44.28 [migration/1]
    14 root      20   0       0      0      0 S   0.0  0.0   1:22.46 [ksoftirqd/1]
    16 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [kworker/1:0H]
    18 root      20   0       0      0      0 S   0.0  0.0   0:00.01 [kdevtmpfs]
    19 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [netns]
    20 root      20   0       0      0      0 S   0.0  0.0   0:01.35 [khungtaskd]
    21 root       0 -20       0      0      0 S   0.0  0.0   0:00.02 [writeback]
    22 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [kintegrityd]
    23 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [bioset]
    24 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [kblockd]
    25 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [md]
    26 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [edac-poller]
    33 root      20   0       0      0      0 S   0.0  0.0   1:19.07 [kswapd0]
    34 root      25   5       0      0      0 S   0.0  0.0   0:00.00 [ksmd]
    35 root      39  19       0      0      0 S   0.0  0.0   0:12.80 [khugepaged]
    36 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 [crypto]
```

### 如何按照指定字段对结果进行排序

在 `top` 命令的最新版本中， 按下 `f` 键进入字段管理界面。

要使用新字段进行排序， 请使用 `up`/`down` 箭头选择正确的选项，然后再按下 `s` 键进行排序。最后按 `q` 键退出此窗口。

```
Fields Management for window 1:Def, whose current sort field is %CPU
    Navigate with Up/Dn, Right selects for move then  or Left commits,
    'd' or  toggles display, 's' sets sort.  Use 'q' or  to end!
 PID     = Process Id             nsUTS   = UTS namespace Inode
 USER    = Effective User Name    LXC     = LXC container name
 PR      = Priority               RSan    = RES Anonymous (KiB)
 NI      = Nice Value             RSfd    = RES File-based (KiB)
 VIRT    = Virtual Image (KiB)    RSlk    = RES Locked (KiB)
 RES     = Resident Size (KiB)    RSsh    = RES Shared (KiB)
 SHR     = Shared Memory (KiB)    CGNAME  = Control Group name
 S       = Process Status         NU      = Last Used NUMA node
 %CPU    = CPU Usage
 %MEM    = Memory Usage (RES)
 TIME+   = CPU Time, hundredths
 COMMAND = Command Name/Line
 PPID    = Parent Process pid
 UID     = Effective User Id
 RUID    = Real User Id
 RUSER   = Real User Name
 SUID    = Saved User Id
 SUSER   = Saved User Name
 GID     = Group Id
 GROUP   = Group Name
 PGRP    = Process Group Id
 TTY     = Controlling Tty
 TPGID   = Tty Process Grp Id
 SID     = Session Id
 nTH     = Number of Threads
 P       = Last Used Cpu (SMP)
 TIME    = CPU Time
 SWAP    = Swapped Size (KiB)
 CODE    = Code Size (KiB)
 DATA    = Data+Stack (KiB)
 nMaj    = Major Page Faults
 nMin    = Minor Page Faults
 nDRT    = Dirty Pages Count
 WCHAN   = Sleeping in Function
 Flags   = Task Flags
 CGROUPS = Control Groups
 SUPGIDS = Supp Groups IDs
 SUPGRPS = Supp Groups Names
 TGID    = Thread Group Id
 OOMa    = OOMEM Adjustment
 OOMs    = OOMEM Score current
 ENVIRON = Environment vars
 vMj     = Major Faults delta
 vMn     = Minor Faults delta
 USED    = Res+Swap Size (KiB)
 nsIPC   = IPC namespace Inode
 nsMNT   = MNT namespace Inode
 nsNET   = NET namespace Inode
 nsPID   = PID namespace Inode
 nsUSER  = USER namespace Inode
```

对 `top` 命令的旧版本，请按 `shift+f` 或 `shift+o` 键进入字段管理界面进行排序。

要使用新字段进行排序，请选择相应的排序字段字母， 然后按下回车键排序。

```
Current Sort Field:  N  for window 1:Def
 Select sort field via field letter, type any other key to return
  a: PID        = Process Id
  b: PPID       = Parent Process Pid
  c: RUSER      = Real user name
  d: UID        = User Id
  e: USER       = User Name
  f: GROUP      = Group Name
  g: TTY        = Controlling Tty
  h: PR         = Priority
  i: NI         = Nice value
  j: P          = Last used cpu (SMP)
  k: %CPU       = CPU usage
  l: TIME       = CPU Time
  m: TIME+      = CPU Time, hundredths
* N: %MEM       = Memory usage (RES)
  o: VIRT       = Virtual Image (kb)
  p: SWAP       = Swapped size (kb)
  q: RES        = Resident size (kb)
  r: CODE       = Code size (kb)
  s: DATA       = Data+Stack size (kb)
  t: SHR        = Shared Mem size (kb)
  u: nFLT       = Page Fault count
  v: nDRT       = Dirty Pages count
  w: S          = Process Status
  x: COMMAND    = Command name/line
  y: WCHAN      = Sleeping in Function
  z: Flags      = Task Flags
 Note1:
   If a selected sort field can't be
   shown due to screen width or your
   field order, the '<' and '>' keys
   will be unavailable until a field
   within viewable range is chosen.
 Note2:
   Field sorting uses internal values,
   not those in column display.  Thus,
   the TTY & WCHAN fields will violate
   strict ASCII collating sequence.
   (shame on you if WCHAN is chosen)
```

---

via: <https://www.2daygeek.com/linux-run-execute-top-command-in-batch-mode/>

作者：[Magesh Maruthamuthu](https://www.2daygeek.com/author/magesh/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[way-ww](https://github.com/way-ww) 校对：[校对者ID](https://github.com/%E6%A0%A1%E5%AF%B9%E8%80%85ID)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


你需要知道的 DevSecOps 流程及工具

# 你需要知道的 DevSecOps 流程及工具

>
> DevSecOps 对 DevOps 进行了改进，以确保安全性仍然是该过程的一个重要部分。
>
>
>

<img src="content_image/album_201910_23_002010fvzh282e8ghhdzpk.jpg" alt="" title="">

到目前为止，DevOps 在 IT 世界中已广为人知，但其并非完美无缺。试想一下，你在一个项目的现代应用程序交付中实施了所有 DevOps 工程实践。你已经到达开发流程的末尾，但是渗透测试团队（内部或外部）检测到安全漏洞并提出了报告。现在，你必须重新启动所有流程，并要求开发人员修复该漏洞。

在基于 DevOps 的软件开发生命周期（SDLC）系统中，这并不繁琐，但它确实会浪费时间并影响交付进度。如果从 SDLC 初期就已经集成了安全性，那么你可能已经跟踪到了该故障，并在开发流程中就消除了它。但是，如上述情形那样，将安全性推到开发流程的最后将导致更长的开发生命周期。

这就是引入 DevSecOps 的原因，它以自动化的方式巩固了整个软件交付周期。

在现代 DevOps 方法中，组织广泛使用容器托管应用程序，我们看到 [Kubernetes](https://opensource.com/resources/what-is-kubernetes) 和 [Istio](https://opensource.com/article/18/9/what-istio) 使用的较多。但是，这些工具都有其自身的漏洞。例如，云原生计算基金会（CNCF）最近完成了一项 [kubernetes 安全审计](https://www.cncf.io/blog/2019/08/06/open-sourcing-the-kubernetes-security-audit/)，发现了几个问题。DevOps 开发流程中使用的所有工具在流程运行时都需要进行安全检查，DevSecOps 会推动管理员去监视工具的存储库以获取升级和补丁。

### 什么是 DevSecOps?

与 DevOps 一样，DevSecOps 是开发人员和 IT 运营团队在开发和部署软件应用程序时所遵循的一种思维方式或文化。它将主动和自动化的安全审计以及渗透测试集成到敏捷应用程序开发中。

要使用 [DevSecOps](https://resources.whitesourcesoftware.com/blog-whitesource/devsecops)，你需要：

* 从 SDLC 开始就引入安全性概念，以最大程度地减少软件代码中的漏洞。
* 确保每个人（包括开发人员和 IT 运营团队）共同承担在其任务中遵循安全实践的责任。
* 在 DevOps 工作流程开始时集成安全控件、工具和流程。这些将在软件交付的每个阶段启用自动安全检查。

DevOps 一直致力于在开发和发布过程中包括安全性以及质量保证（QA）、数据库管理和其他所有方面。然而，DevSecOps 是该过程的一个演进，以确保安全永远不会被遗忘，成为该过程的一个重要部分。

### 了解 DevSecOps 流程

典型的 DevOps 流程有不同的阶段；典型的 SDLC 流程包括计划、编码、构建、测试、发布和部署等阶段。在 DevSecOps 中，每个阶段都会应用特定的安全检查。

* **计划**：执行安全性分析并创建测试计划，以确定在何处、如何以及何时进行测试的方案。
* **编码**：部署整理工具和 Git 控件以保护密码和 API 密钥。
* **构建**：在构建执行代码时，请结合使用静态应用程序安全测试（SAST）工具来跟踪代码中的缺陷，然后再部署到生产环境中。这些工具针对特定的编程语言。
* **测试**：在运行时使用动态应用程序安全测试（DAST）工具来测试您的应用程序。 这些工具可以检测与用户身份验证，授权，SQL 注入以及与 API 相关的端点相关的错误。
* **发布**：在发布应用程序之前，请使用安全分析工具来进行全面的渗透测试和漏洞扫描。
* **部署**：在运行时完成上述测试后，将安全的版本发送到生产中以进行最终部署。

### DevSecOps 工具

SDLC 的每个阶段都有可用的工具。有些是商业产品，但大多数是开源的。在我的下一篇文章中，我将更多地讨论在流程的不同阶段使用的工具。

随着基于现代 IT 基础设施的企业安全威胁的复杂性增加，DevSecOps 将发挥更加关键的作用。然而，DevSecOps 流程将需要随着时间的推移而改进，而不是仅仅依靠同时实施所有安全更改即可。这将消除回溯或应用交付失败的可能性。

---

via: <https://opensource.com/article/19/10/devsecops-pipeline-and-tools>

作者：[Sagar Nangare](https://opensource.com/users/sagarnangare) 选题：[lujun9972](https://github.com/lujun9972) 译者：[lnrCoder](https://github.com/lnrCoder) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


变异测试：基于故障的试验

# 变异测试：基于故障的试验

>
> 基于 .NET 的 xUnit.net 测试框架，开发一款自动猫门的逻辑，让门在白天开放，夜间锁定。
>
>
>

<img src="content_image/album_201910_23_171508cd7q7ilais29disa.jpg" alt="" title="">

在本系列的[第一篇文章](/article-11483-1.html)中，我演示了如何使用设计的故障来确保代码中的预期结果。在第二篇文章中，我将继续开发示例项目：一款自动猫门，该门在白天开放，夜间锁定。

在此提醒一下，你可以按照[此处的说明](/article-11468-1.html)使用 .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` 违反了单一责任原则。

该方法实现同时使用和处理信息。优良作法是一种方法应负责执行一项职责。
5. `GetDayOrNight` 有多个更改原因。

可以想象内部时间源可能会更改的情况。同样，很容易想象处理逻辑也将改变。这些变化的不同原因必须相互隔离。
6. 当（我们）尝试了解 `GetDayOrNight` 行为时，会发现它的 API 签名不足。

最理想的做法就是通过简单的查看 API 的签名，就能了解 API 预期的行为类型。
7. `GetDayOrNight` 取决于全局共享可变状态。

要不惜一切代价避免共享的可变状态！
8. 即使在阅读源代码之后，也无法预测 `GetDayOrNight` 方法的行为。

这是一个严重的问题。通过阅读源代码，应该始终非常清晰，系统一旦开始运行，便可以预测出其行为。

### 失败背后的原则

每当你遇到工程问题时，建议使用久经考验的<ruby> 分而治之 <rt>  divide and conquer </rt></ruby>策略。在这种情况下，遵循<ruby> 关注点分离 <rt>  separation of concerns </rt></ruby>的原则是一种可行的方法。

>
> 关注点分离（SoC）是一种用于将计算机程序分为不同模块的设计原理，以便每个模块都可以解决一个关注点。关注点是影响计算机程序代码的一组信息。关注点可以和要优化代码的硬件的细节一样概括，也可以和要实例化的类的名称一样具体。完美体现 SoC 的程序称为模块化程序。
>
>
> （[出处](https://en.wikipedia.org/wiki/Separation_of_concerns)）
>
>
>

`GetDayOrNight` 方法应仅与确定日期和时间值表示白天还是夜晚有关。它不应该与寻找该值的来源有关。该问题应留给调用客户端。

必须将这个问题留给调用客户端，以获取当前时间。这种方法符合另一个有价值的工程原理——<ruby> 控制反转 <rt>  inversion of control </rt></ruby>。Martin Fowler [在这里](https://martinfowler.com/bliki/InversionOfControl.html)详细探讨了这一概念。

>
> 框架的一个重要特征是用户定义的用于定制框架的方法通常来自于框架本身，而不是从用户的应用程序代码调用来的。该框架通常在协调和排序应用程序活动中扮演主程序的角色。控制权的这种反转使框架有能力充当可扩展的框架。用户提供的方法为框架中的特定应用程序量身制定泛化算法。
>
>
> – [Ralph Johnson and Brian Foote](http://www.laputan.org/drc/drc.html)
>
>
>

### 重构测试用例

因此，代码需要重构。摆脱对内部时钟的依赖（`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 >= 7 && time.Hour < 19) {
               dayOrNight = "Daylight";
           }
           return dayOrNight;
       }
   }
}
```

重构代码需要更改单元测试。 需要准备 `nightHour` 和 `dayHour` 的测试数据，并将这些值传到`GetDayOrNight` 方法中。 以下是重构的单元测试：

```
using System;
using Xunit;
using app;

namespace unittest
{
   public class UnitTest1
   {
       DayOrNightUtility dayOrNightUtility = new DayOrNightUtility();
       DateTime nightHour = new DateTime(2019, 08, 03, 19, 00, 00);
       DateTime dayHour = new 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](https://opensource.com/users/alex-bunardzic) 选题：[lujun9972](https://github.com/lujun9972) 译者：[Morisun029](https://github.com/Morisun029) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


Linux sudo 漏洞可能导致未经授权的特权访问

又发一遍

# Linux sudo 漏洞可能导致未经授权的特权访问

>
> 在 Linux 中利用新发现的 sudo 漏洞可以使某些用户以 root 身份运行命令，尽管对此还有所限制。
>
>
>

<img src="content_image/album_201910_23_173934huyi6siys2u33w9z.png" alt="" title="">

[sudo](https://www.networkworld.com/article/3236499/some-tricks-for-using-sudo.html) 命令中最近发现了一个严重漏洞，如果被利用，普通用户可以 root 身份运行命令，即使在 `/etc/sudoers` 文件中明确禁止了该用户这样做。

将 `sudo` 更新到版本 1.8.28 应该可以解决该问题，因此建议 Linux 管理员尽快这样做。

如何利用此漏洞取决于 `/etc/sudoers` 中授予的特定权限。例如，一条规则允许用户以除了 root 用户之外的任何用户身份来编辑文件，这实际上将允许该用户也以 root 用户身份来编辑文件。在这种情况下，该漏洞可能会导致非常严重的问题。

用户要能够利用此漏洞，需要在 `/etc/sudoers` 中为**用户**分配特权，以使该用户可以以其他用户身份运行命令，并且该漏洞仅限于以这种方式分配的命令特权。

此问题影响 1.8.28 之前的版本。要检查你的 `sudo` 版本，请使用以下命令：

```
$ sudo -V
Sudo version 1.8.27     <===
Sudoers policy plugin version 1.8.27
Sudoers file grammar version 46
Sudoers I/O plugin version 1.8.27
```

该漏洞已在 CVE 数据库中分配了编号 [CVE-2019-14287](http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-14287)。它的风险是，任何被指定能以任意用户运行某个命令的用户，即使被明确禁止以 root 身份运行，它都能逃脱限制。

下面这些行让 `jdoe` 能够以除了 root 用户之外的其他身份使用 `vi` 编辑文件（`!root` 表示“非 root”），同时 `nemo` 有权运行以除了 root 身份以外的任何用户使用 `id` 命令：

```
# affected entries on host "dragonfly"
jdoe dragonfly = (ALL, !root) /usr/bin/vi
nemo dragonfly = (ALL, !root) /usr/bin/id
```

但是，由于存在漏洞，这些用户中要么能够绕过限制并以 root 编辑文件，或者以 root 用户身份运行 `id` 命令。

攻击者可以通过指定用户 ID 为 `-1` 或 `4294967295` 来以 root 身份运行命令。

```
sudo -u#-1 id -u
```

或者

```
sudo -u#4294967295 id -u
```

响应为 `1` 表明该命令以 root 身份运行（显示 root 的用户 ID）。

苹果信息安全团队的 Joe Vennix 找到并分析该问题。

---

via: <https://www.networkworld.com/article/3446036/linux-sudo-flaw-can-lead-to-unauthorized-privileges.html>

作者：[Sandra Henry-Stocker](https://www.networkworld.com/author/Sandra-Henry_Stocker/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


Summary: 开源社区和行业趋势的每周总览。

开源社区和行业趋势的每周总览。

# 每周开源点评：Kubernetes 网络、OpenStack Train 以及更多的行业趋势

>
> 开源社区和行业趋势的每周总览。
>
>
>

<img src="content_image/album_201910_24_060738rt1fr5vac56aj5zi.png" alt="Person standing in front of a giant computer screen with numbers, data" title="Person standing in front of a giant computer screen with numbers, data">

作为我在具有开源开发模型的企业软件公司担任高级产品营销经理的角色的一部分，我为产品营销人员、经理和其他影响者定期发布有关开源社区，市场和行业趋势的定期更新。以下是该更新中我和他们最喜欢的五篇文章。

### OpenStack Train 中最令人兴奋的功能

* [文章地址](https://www.redhat.com/en/blog/look-most-exciting-features-openstack-train)

>
> 考虑到 Train 版本必须提供的所有技术优势（[你可以在此处查看版本亮点](https://releases.openstack.org/train/highlights.html)），你可能会对 Red Hat 认为这些将使我们的电信和企业客户受益的顶级功能及其用例感到好奇。以下我们对该版本最兴奋的功能的概述。
>
>
>

**影响**：OpenStack 对我来说就像 Shia LaBeouf：它在几年前达到了炒作的顶峰，然后继续产出了好的作品。Train 版本看起来是又一次令人难以置信的创新下降。

### 以 Ansible 原生的方式构建 Kubernetes 操作器

* [文章地址](https://www.cncf.io/webinars/building-kubernetes-operators-in-an-ansible-native-way/)

>
> 操作器简化了 Kubernetes 上复杂应用程序的管理。它们通常是用 Go 语言编写的，并且需要懂得 Kubernetes 内部的专业知识。但是，还有另一种进入门槛较低的选择。Ansible 是操作器 SDK 中的一等公民。使用 Ansible 可以释放应用程序工程师的精力，最大限度地利用时间来自动化和协调你的应用程序，并使用一种简单的语言在新的和现有的平台上进行操作。在这里我们可以看到如何做。
>
>
>

**影响**：这就像你发现可以用搅拌器和冷冻香蕉制作出不错的冰淇淋一样：Ansible（通常被认为很容易掌握）可以使你比你想象的更容易地做一些令人印象深刻的操作器魔术。

### Kubernetes 网络：幕后花絮

* [文章地址](https://itnext.io/kubernetes-networking-behind-the-scenes-39a1ab1792bb)

>
> 尽管围绕该主题有很多很好的资源（链接在[这里](https://github.com/nleiva/kubernetes-networking-links)），但我找不到一个示例，可以将所有的点与网络工程师喜欢和讨厌的命令输出连接起来，以显示背后实际发生的情况。因此，我决定从许多不同的来源收集这些信息，以期帮助你更好地了解事物之间的联系。
>
>
>

**影响**：这是一篇对复杂主题（带有图片）阐述的很好的作品。保证可以使 Kubenetes 网络的混乱程度降低 10％。

### 保护容器供应链

* [文章地址](https://www.devprojournal.com/technology-trends/open-source/securing-the-container-supply-chain/)

>
> 随着容器、软件即服务和函数即服务的出现，人们开始着眼于在使用现有服务、函数和容器映像的过程中寻求新的价值。[Red Hat](https://www.redhat.com/en) 的容器首席产品经理 Scott McCarty 表示，关注这个重点既有优点也有缺点。“它使我们能够集中精力编写满足我们需求的新应用程序代码，同时将对基础架构的关注转移给其他人身上，”McCarty 说，“容器处于一个最佳位置，提供了足够的控制，而卸去了许多繁琐的基础架构工作。”但是，容器也会带来与安全性相关的劣势。
>
>
>

**影响**：我在一个由大约十位安全人员组成的小组中，可以肯定地说，整天要考虑软件安全性需要一定的倾向。当你长时间凝视深渊时，它也凝视着你。如果你不是如此倾向的软件开发人员，请听取 Scott 的建议并确保你的供应商考虑安全。

### 15 岁的 Fedora：为何 Matthew Miller 看到 Linux 发行版的光明前景

* [文章链接](https://www.techrepublic.com/article/fedora-at-15-why-matthew-miller-sees-a-bright-future-for-the-linux-distribution/)

>
> 在 TechRepublic 的一个大范围采访中，Fedora 项目负责人 Matthew Miller 讨论了过去的经验教训、软件容器的普遍采用和竞争性标准、Fedora 的潜在变化以及包括 systemd 在内的热门话题。
>
>
>

**影响**：我喜欢 Fedora 项目的原因是它的清晰度；该项目知道它代表什么。像 Matt 这样的人就是为什么能看到光明前景的原因。

*我希望你喜欢这张上周让我印象深刻的列表，并在下周一回来了解更多的开放源码社区、市场和行业趋势。*

---

via: <https://opensource.com/article/19/10/kubernetes-openstack-and-more-industry-trends>

作者：[Tim Hildred](https://opensource.com/users/thildred) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


DevOps 专业人员如何成为网络安全拥护者

# DevOps 专业人员如何成为网络安全拥护者

>
> 打破信息孤岛，成为网络安全的拥护者，这对你、对你的职业、对你的公司都会有所帮助。
>
>
>

<img src="content_image/album_201910_24_202520u09xw2vm4w2jm0mx.jpg" alt="" title="">

安全是 DevOps 中一个被误解了的部分，一些人认为它不在 DevOps 的范围内，而另一些人认为它太过重要（并且被忽视），建议改为使用 DevSecOps。无论你同意哪一方的观点，网络安全都会影响到我们每一个人，这是很明显的事实。

每年，[黑客行为的统计数据](https://hostingtribunal.com/blog/hacking-statistics/) 都会更加令人震惊。例如，每 39 秒就有一次黑客行为发生，这可能会导致你为公司写的记录、身份和专有项目被盗。你的安全团队可能需要花上几个月（也可能是永远找不到）才能发现这次黑客行为背后是谁，目的是什么，人在哪，什么时候黑进来的。

运维专家面对这些棘手问题应该如何是好？呐我说，现在是时候成为网络安全的拥护者，变为解决方案的一部分了。

### 孤岛势力范围的战争

在我和我本地的 IT 安全（ITSEC）团队一起肩并肩战斗的岁月里，我注意到了很多事情。一个很大的问题是，安全团队和 DevOps 之间关系紧张，这种情况非常普遍。这种紧张关系几乎都是来源于安全团队为了保护系统、防范漏洞所作出的努力（例如，设置访问控制或者禁用某些东西），这些努力会中断 DevOps 的工作并阻碍他们快速部署应用程序。

你也看到了，我也看到了，你在现场碰见的每一个人都有至少一个和它有关的故事。一小撮的怨恨最终烧毁了信任的桥梁，要么是花费一段时间修复，要么就是两个团体之间开始一场小型的地盘争夺战，这个结果会使 DevOps 实现起来更加艰难。

### 一种新观点

为了打破这些孤岛并结束势力战争，我在每个安全团队中都选了至少一个人来交谈，了解我们组织日常安全运营里的来龙去脉。我开始做这件事是出于好奇，但我持续做这件事是因为它总是能带给我一些有价值的、新的观点。例如，我了解到，对于每个因为失败的安全性而被停止的部署，安全团队都在疯狂地尝试修复 10 个他们看见的其他问题。他们反应的莽撞和尖锐是因为他们必须在有限的时间里修复这些问题，不然这些问题就会变成一个大问题。

考虑到发现、识别和撤销已完成操作所需的大量知识，或者指出 DevOps 团队正在做什么（没有背景信息）然后复制并测试它。所有的这些通常都要由人手配备非常不足的安全团队完成。

这就是你的安全团队的日常生活，并且你的 DevOps 团队看不到这些。ITSEC 的日常工作意味着超时加班和过度劳累，以确保公司，公司的团队，团队里工作的所有人能够安全地工作。

### 成为安全拥护者的方法

这些是你成为你的安全团队的拥护者之后可以帮到它们的。这意味着，对于你做的所有操作，你必须仔细、认真地查看所有能够让其他人登录的方式，以及他们能够从中获得什么。

帮助你的安全团队就是在帮助你自己。将工具添加到你的工作流程里，以此将你知道的要干的活和他们知道的要干的活结合到一起。从小事入手，例如阅读公共漏洞披露（CVE），并将扫描模块添加到你的 CI/CD 流程里。对于你写的所有代码，都会有一个开源扫描工具，添加小型开源工具（例如下面列出来的）在长远看来是可以让项目更好的。

**容器扫描工具：**

* [Anchore Engine](https://github.com/anchore/anchore-engine)
* [Clair](https://github.com/coreos/clair)
* [Vuls](https://vuls.io/)
* [OpenSCAP](https://www.open-scap.org/)

**代码扫描工具：**

* [OWASP SonarQube](https://github.com/OWASP/sonarqube)
* [Find Security Bugs](https://find-sec-bugs.github.io/)
* [Google Hacking Diggity Project](https://resources.bishopfox.com/resources/tools/google-hacking-diggity/)

**Kubernetes 安全工具：**

* [Project Calico](https://www.projectcalico.org/)
* [Kube-hunter](https://github.com/aquasecurity/kube-hunter)
* [NeuVector](https://github.com/neuvector/neuvector-helm)

### 保持你的 DevOps 态度

如果你的工作角色是和 DevOps 相关的，那么学习新技术和如何运用这项新技术创造新事物就是你工作的一部分。安全也是一样。我在 DevOps 安全方面保持到最新，下面是我的方法的列表。

* 每周阅读一篇你工作的方向里和安全相关的文章.
* 每周查看 [CVE](https://cve.mitre.org/) 官方网站，了解出现了什么新漏洞.
* 尝试做一次黑客马拉松。一些公司每个月都要这样做一次；如果你觉得还不够、想了解更多，可以访问 Beginner Hack 1.0 网站。
* 每年至少一次和那你的安全团队的成员一起参加安全会议，从他们的角度来看事情。

### 成为拥护者是为了变得更好

你应该成为你的安全的拥护者，下面是我们列出来的几个理由。首先是增长你的知识，帮助你的职业发展。第二是帮助其他的团队，培养新的关系，打破对你的组织有害的孤岛。在你的整个组织内建立由很多好处，包括设置沟通团队的典范，并鼓励人们一起工作。你同样能促进在整个组织中分享知识，并给每个人提供一个在安全方面更好的内部合作的新契机。

总的来说，成为一个网络安全的拥护者会让你成为你整个组织的拥护者。

---

via: <https://opensource.com/article/19/9/devops-security-champions>

作者：[Jessica Repka](https://opensource.com/users/jrepka) 选题：[lujun9972](https://github.com/lujun9972) 译者：[hopefully2333](https://github.com/hopefully2333) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


用 Git 帮助写作者更好地完成工作

真的挺有意思的

# 用 Git 帮助写作者更好地完成工作

>
> 如果你是一名写作者，你也能从使用 Git 中受益。在我们的系列文章中了解有关 Git 鲜为人知的用法。
>
>
>

[Git](https://git-scm.com/) 是一个少有的能将如此多的现代计算封装到一个程序之中的应用程序，它可以用作许多其他应用程序的计算引擎。虽然它以跟踪软件开发中的源代码更改而闻名，但它还有许多其他用途，可以让你的生活更轻松、更有条理。在这个 Git 系列中，我们将分享七种鲜为人知的使用 Git 的方法。

今天我们来看看写作者如何使用 Git 更好的地完成工作。

### 写作者的 Git

有些人写小说，也有人撰写学术论文、诗歌、剧本、技术手册或有关开源的文章。许多人都在做一些各种写作。相同的是，如果你是一名写作者，或许能从使用 Git 中受益。尽管 Git 是著名的计算机程序员所使用的高度技术性工具，但它也是现代写作者的理想之选，本文将向你演示如何改变你的书写方式以及为什么要这么做的原因。

但是，在谈论 Git 之前，重要的是先谈谈“副本”（或者叫“内容”，对于数字时代而言）到底是什么，以及为什么它与你的交付*媒介*不同。这是 21 世纪，大多数写作者选择的工具是计算机。尽管计算机看似擅长将副本的编辑和布局等过程结合在一起，但写作者还是（重新）发现将内容与样式分开是一个好主意。这意味着你应该在计算机上像在打字机上而不是在文字处理器中进行书写。以计算机术语而言，这意味着以*纯文本*形式写作。

### 以纯文本写作

这个假设曾经是毫无疑问的：你知道自己的写作所要针对的市场，你可以为书籍、网站或软件手册等不同市场编写内容。但是，近来各种市场趋于扁平化：你可能决定在纸质书中使用为网站编写的内容，并且纸质书可能会在以后发布 EPUB 版本。对于你的内容的数字版本，读者才是最终控制者：他们可以在你发布内容的网站上阅读你的文字，也可以点击 Firefox 出色的[阅读视图](https://support.mozilla.org/en-US/kb/firefox-reader-view-clutter-free-web-pages)，还可能会打印到纸张上，或者可能会使用 Lynx 将网页转储到文本文件中，甚至可能因为使用屏幕阅读器而根本看不到你的内容。

你只需要逐字写下你的内容，而将交付工作留给发布者。即使你是自己发布，将字词作为写作作品的一种源代码也是一种更聪明、更有效的工作方式，因为在发布时，你可以使用相同的源（你的纯文本）生成适合你的目标输出（用于打印的 PDF、用于电子书的 EPUB、用于网站的 HTML 等）。

用纯文本编写不仅意味着你不必担心布局或文本样式，而且也不再需要专门的工具。无论是手机或平板电脑上的基本的记事本应用程序、计算机附带的文本编辑器，还是从互联网上下载的免费编辑器，任何能够产生文本内容的工具对你而言都是有效的“文字处理器”。无论你身在何处或在做什么，几乎可以在任何设备上书写，并且所生成的文本可以与你的项目完美集成，而无需进行任何修改。

而且，Git 专门用来管理纯文本。

### Atom 编辑器

当你以纯文本形式书写时，文字处理程序会显得过于庞大。使用文本编辑器更容易，因为文本编辑器不会尝试“有效地”重组输入内容。它使你可以将脑海中的单词输入到屏幕中，而不会受到干扰。更好的是，文本编辑器通常是围绕插件体系结构设计的，这样应用程序本身很基础（它用来编辑文本），但是你可以围绕它构建一个环境来满足你的各种需求。

[Atom](http://atom.io) 编辑器就是这种设计理念的一个很好的例子。这是一个具有内置 Git 集成的跨平台文本编辑器。如果你不熟悉纯文本格式，也不熟悉 Git，那么 Atom 是最简单的入门方法。

#### 安装 Git 和 Atom

首先，请确保你的系统上已安装 Git。如果运行 Linux 或 BSD，则 Git 在软件存储库或 ports 树中可用。你使用的命令将根据你的发行版而有所不同。例如在 Fedora 上：

```
$ sudo dnf install git
```

你也可以下载并安装适用于 [Mac](https://git-scm.com/download/mac) 和 [Windows](https://git-scm.com/download/win) 的 Git。

你不需要直接使用 Git，因为 Atom 会充当你的 Git 界面。下一步是安装 Atom。

如果你使用的是 Linux，请通过软件安装程序或适当的命令从软件存储库中安装 Atom，例如：

```
$ sudo dnf install atom
```

Atom 当前没有在 BSD 上构建。但是，有很好的替代方法，例如 [GNU Emacs](http://gnu.org/software/emacs)。对于 Mac 和 Windows 用户，可以在 [Atom 网站](http://atom.io)上找到安装程序。

安装完成后，启动 Atom 编辑器。

#### 快速指导

如果要使用纯文本和 Git，则需要适应你的编辑器。Atom 的用户界面可能比你习惯的更加动态。实际上，你可以将它视为 Firefox 或 Chrome，而不是文字处理程序，因为它具有可以根据需要打开或关闭的选项卡和面板，甚至还可以安装和配置附件。尝试全部掌握 Atom 如许之多的功能是不切实际的，但是你至少可以知道有什么功能。

当打开 Atom 时，它将显示一个欢迎屏幕。如果不出意外，此屏幕很好地介绍了 Atom 的选项卡式界面。你可以通过单击 Atom 窗口顶部选项卡上的“关闭”图标来关闭欢迎屏幕，并使用“文件 > 新建文件”创建一个新文件。

使用纯文本格式与使用文字处理程序有点不同，因此这里有一些技巧，以人可以理解的方式编写内容，并且 Git 和计算机可以解析，跟踪和转换。

#### 用 Markdown 书写

如今，当人们谈论纯文本时，大多是指 Markdown。Markdown 与其说是格式，不如说是样式，这意味着它旨在为文本提供可预测的结构，以便计算机可以检测自然的模式并智能地转换文本。Markdown 有很多定义，但是最好的技术定义和备忘清单在 [CommonMark 的网站](https://commonmark.org/help/)上。

```
# Chapter 1

This is a paragraph with an *italic* word and a **bold** word in it.
And it can even reference an image.

```

从示例中可以看出，Markdown 读起来感觉不像代码，但可以将其视为代码。如果你遵循 CommonMark 定义的 Markdown 规范，那么一键就可以可靠地将 Markdown 的文字转换为 .docx、.epub、.html、MediaWiki、.odt、.pdf、.rtf 和各种其他的格式，而*不会*失去格式。

你可以认为 Markdown 有点像文字处理程序的样式。如果你曾经为出版社撰写过一套样式来控制章节标题及其样式，那基本上就是一回事，除了不是从下拉菜单中选择样式以外，你需要给你的文字添加一些小记号。对于任何习惯“以文字交谈”的现代阅读者来说，这些表示法都是很自然的，但是在呈现文本时，它们会被精美的文本样式替换掉。实际上，这就是文字处理程序在后台秘密进行的操作。文字处理器显示粗体文本，但是如果你可以看到使文本变为粗体的生成代码，则它与 Markdown 很像（实际上，它是更复杂的 XML）。使用 Markdown 可以消除这种代码和样式之间的阻隔，一方面看起来更可怕一些，但另一方面，你可以在几乎所有可以生成文本的东西上书写 Markdown 而不会丢失任何格式信息。

Markdown 文件流行的文件扩展名是 .md。如果你使用的平台不知道 .md 文件是什么，则可以手动将该扩展名与 Atom 关联，或者仅使用通用的 .txt 扩展名。文件扩展名不会更改文件的性质。它只会改变你的计算机决定如何处理它的方式。Atom 和某些平台足够聪明，可以知道该文件是纯文本格式，无论你给它以什么扩展名。

#### 实时预览

Atom 具有 “Markdown 预览” 插件，该插件可以向你显示正在编写的纯文本 Markdown 及其（通常）呈现的方式。

要激活此预览窗格，请选择“包 > Markdown 预览 > 切换预览” 或按 `Ctrl + Shift + M`。

此视图为你提供了两全其美的方法。无需承担为你的文本添加样式的负担就可以写作，而你也可以看到一个通用的示例外观，至少是以典型的数字化格式显示文本的外观。当然，关键是你无法控制文本的最终呈现方式，因此不要试图调整 Markdown 来强制以某种方式显示呈现的预览。

#### 每行一句话

你的高中写作老师不会看你的 Markdown。

一开始它不那么自然，但是在数字世界中，保持每行一个句子更有意义。Markdown 会忽略单个换行符（当你按下 `Return` 或 `Enter` 键时），并且只在单个空行之后才会创建一个新段落。

每行写一个句子的好处是你的工作更容易跟踪。也就是说，假如你在段落的开头更改了一个单词，如果更改仅限于一行而不是一个长的段落中的一个单词，那么 Atom、Git 或任何应用程序很容易以有意义的方式突出显示该更改。换句话说，对一个句子的更改只会影响该句子，而不会影响整个段落。

你可能会想：“许多文字处理器也可以跟踪更改，它们可以突出显示已更改的单个单词。”但是这些修订跟踪器绑定在该字处理器的界面上，这意味着你必须先打开该字处理器才能浏览修订。在纯文本工作流程中，你可以以纯文本形式查看修订，这意味着无论手头有什么，只要该设备可以处理纯文本（大多数都可以），就可以进行编辑或批准编辑。

诚然，写作者通常不会考虑行号，但它对于计算机有用，并且通常是一个很好的参考点。默认情况下，Atom 为文本文档的行进行编号。按下 `Enter` 键或 `Return` 键后，一*行*就是一行。

如果（在 Atom 的）一行的行号中有一个点而不是一个数字，则表示它是上一行折叠的一部分，因为它超出了你的屏幕。

#### 主题

如果你是一个在意视觉形象的人，那么你可能会非常注重自己的写作环境。即使你使用普通的 Markdown 进行编写，也并不意味着你必须使用程序员的字体或在使你看起来像码农的黑窗口中进行书写。修改 Atom 外观的最简单方法是使用[主题包](https://atom.io/themes)。主题设计人员通常将深色主题与浅色主题区分开，因此你可以根据需要使用关键字“Dark”或“Light”进行搜索。

要安装主题，请选择“编辑 > 首选项”。这将在 Atom 界面中打开一个新标签页。是的，标签页可以用于处理文档*和*用于配置及控制面板。在“设置”标签页中，单击“安装”类别。

在“安装”面板中，搜索要安装的主题的名称。单击搜索字段右侧的“主题”按钮，以仅搜索主题。找到主题后，单击其“安装”按钮。

要使用已安装的主题或根据喜好自定义主题，请导航至“设置”标签页中的“主题”类别中。从下拉菜单中选择要使用的主题。更改会立即进行，因此你可以准确了解主题如何影响你的环境。

你也可以在“设置”标签的“编辑器”类别中更改工作字体。Atom 默认采用等宽字体，程序员通常首选这种字体。但是你可以使用系统上的任何字体，无论是衬线字体、无衬线字体、哥特式字体还是草书字体。无论你想整天盯着什么字体都行。

作为相关说明，默认情况下，Atom 会在其屏幕上绘制一条垂直线，以提示编写代码的人员。程序员通常不想编写太长的代码行，因此这条垂直线会提醒他们不要写太长的代码行。不过，这条竖线对写作者而言毫无意义，你可以通过禁用 “wrap-guide” 包将其删除。

要禁用 “wrap-guide” 软件包，请在“设置”标签中选择“折行”类别，然后搜索 “wrap-guide”。找到该程序包后，单击其“禁用”按钮。

#### 动态结构

创建长文档时，我发现每个文件写一个章节比在一个文件中写整本书更有意义。此外，我不会以明显的语法 `chapter-1.md` 或 `1.example.md` 来命名我的章节，而是以章节标题或关键词（例如 `example.md`）命名。为了将来为自己提供有关如何编写本书的指导，我维护了一个名为 `toc.md` （用于“目录”）的文件，其中列出了各章的（当前）顺序。

我这样做是因为，无论我多么相信第 6 章都不可能出现在第 1 章之前，但在我完成整本书之前，几乎难以避免我会交换一两个章节的顺序。我发现从一开始就保持动态变化可以帮助我避免重命名混乱，也可以帮助我避免僵化的结构。

### 在 Atom 中使用 Git

每位写作者的共同点是两件事：他们为流传而写作，而他们的写作是一段旅程。你不能一坐下来写作就完成了最终稿件。顾名思义，你有一个初稿。该草稿会经过修订，你会仔细地将每个修订保存一式两份或三份的备份，以防万一你的文件损坏了。最终，你得到了所谓的最终草稿，但很有可能你有一天还会回到这份最终草稿，要么恢复好的部分，要么修改坏的部分。

Atom 最令人兴奋的功能是其强大的 Git 集成。无需离开 Atom，你就可以与 Git 的所有主要功能进行交互，跟踪和更新项目、回滚你不喜欢的更改、集成来自协作者的更改等等。最好的学习方法就是逐步学习，因此这是在一个写作项目中从始至终在 Atom 界面中使用 Git 的方法。

第一件事：通过选择 “视图 > 切换 Git 标签页” 来显示 Git 面板。这将在 Atom 界面的右侧打开一个新标签页。现在没什么可看的，所以暂时保持打开状态就行。

#### 建立一个 Git 项目

你可以认为 Git 被绑定到一个文件夹。Git 目录之外的任何文件夹都不知道 Git，而 Git 也不知道外面。Git 目录中的文件夹和文件将被忽略，直到你授予 Git 权限来跟踪它们为止。

你可以通过在 Atom 中创建新的项目文件夹来创建 Git 项目。选择 “文件 > 添加项目文件夹”，然后在系统上创建一个新文件夹。你创建的文件夹将出现在 Atom 窗口的左侧“项目面板”中。

#### Git 添加文件

右键单击你的新项目文件夹，然后选择“新建文件”以在项目文件夹中创建一个新文件。如果你要导入文件到新项目中，请右键单击该文件夹，然后选择“在文件管理器中显示”，以在系统的文件查看器中打开该文件夹（Linux 上为 Dolphin 或 Nautilus，Mac 上为 Finder，在 Windows 上是 Explorer），然后拖放文件到你的项目文件夹。

在 Atom 中打开一个项目文件（你创建的空文件或导入的文件）后，单击 Git 标签中的 “<ruby> 创建存储库 <rt>  Create Repository </rt></ruby>” 按钮。在弹出的对话框中，单击 “<ruby> 初始化 <rt>  Init </rt></ruby>” 以将你的项目目录初始化为本地 Git 存储库。 Git 会将 `.git` 目录（在系统的文件管理器中不可见，但在 Atom 中可见）添加到项目文件夹中。不要被这个愚弄了：`.git` 目录是 Git 管理的，而不是由你管理的，因此一般你不要动它。但是在 Atom 中看到它可以很好地提醒你正在由 Git 管理的项目中工作。换句话说，当你看到 `.git` 目录时，就有了修订历史记录。

在你的空文件中，写一些东西。你是写作者，所以输入一些单词就行。你可以随意输入任何一组单词，但要记住上面的写作技巧。

按 `Ctrl + S` 保存文件，该文件将显示在 Git 标签的 “<ruby> 未暂存的改变 <rt>  Unstaged Changes </rt></ruby>” 部分中。这意味着该文件存在于你的项目文件夹中，但尚未提交给 Git 管理。通过单击 Git 选项卡右上方的 “<ruby> 暂存全部 <rt>  Stage All </rt></ruby>” 按钮，以允许 Git 跟踪这些文件。如果你使用过带有修订历史记录的文字处理器，则可以将此步骤视为允许 Git 记录更改。

#### Git 提交

你的文件现在已暂存。这意味着 Git 知道该文件存在，并且知道自上次 Git 知道该文件以来，该文件已被更改。

Git 的<ruby> 提交 <rt>  commit </rt></ruby>会将你的文件发送到 Git 的内部和永久存档中。如果你习惯于文字处理程序，这就类似于给一个修订版命名。要创建一个提交，请在 Git 选项卡底部的“<ruby> 提交 <rt>  Commit </rt></ruby>”消息框中输入一些描述性文本。你可能会感到含糊不清或随意写点什么，但如果你想在将来知道进行修订的原因，那么输入一些有用的信息会更有用。

第一次提交时，必须创建一个<ruby> 分支 <rt>  branch </rt></ruby>。Git 分支有点像另外一个空间，它允许你从一个时间轴切换到另一个时间轴，以进行你可能想要也可能不想要永久保留的更改。如果最终喜欢该更改，则可以将一个实验分支合并到另一个实验分支，从而统一项目的不同版本。这是一个高级过程，不需要先学会，但是你仍然需要一个活动分支，因此你必须为首次提交创建一个分支。

单击 Git 选项卡最底部的“<ruby> 分支 <rt>  Branch </rt></ruby>”图标，以创建新的分支。

通常将第一个分支命名为 `master`，但不是必须如此；你可以将其命名为 `firstdraft` 或任何你喜欢的名称，但是遵守当地习俗有时会使谈论 Git（和查找问题的答案）变得容易一些，因为你会知道有人提到 “master” 时，它们的真正意思是“主干”而不是“初稿”或你给分支起的什么名字。

在某些版本的 Atom 上，UI 也许不会更新以反映你已经创建的新分支。不用担心，做了提交之后，它会创建分支（并更新 UI）。按下 “<ruby> 提交 <rt>  Commit </rt></ruby>” 按钮，无论它显示的是 “<ruby> 创建脱离的提交 <rt>  Create detached commit </rt></ruby>” 还是 “<ruby> 提交到主干 <rt>  Commit to master </rt></ruby>。

提交后，文件的状态将永久保留在 Git 的记忆之中。

#### 历史记录和 Git 差异

一个自然而然的问题是你应该多久做一次提交。这并没有正确的答案。使用 `Ctrl + S` 保存文件和提交到 Git 是两个单独的过程，因此你会一直做这两个过程。每当你觉得自己已经做了重要的事情或打算尝试一个可能会被干掉的疯狂的新想法时，你可能都会想要做次提交。

要了解提交对工作流程的影响，请从测试文档中删除一些文本，然后在顶部和底部添加一些文本。再次提交。 这样做几次，直到你在 Git 标签的底部有了一小段历史记录，然后单击其中一个提交以在 Atom 中查看它。

查看过去的提交时，你会看到三种元素：

1. 绿色文本是该提交中已被添加到文档中的内容。
2. 红色文本是该提交中已从文档中删除的内容。
3. 其他所有文字均未做更改。

#### 远程备份

使用 Git 的优点之一是，按照设计它是分布式的，这意味着你可以将工作提交到本地存储库，并将所做的更改推送到任意数量的服务器上进行备份。你还可以从这些服务器中拉取更改，以便你碰巧正在使用的任何设备始终具有最新更改。

为此，你必须在 Git 服务器上拥有一个帐户。有几种免费的托管服务，其中包括 GitHub，这个公司开发了 Atom，但奇怪的是 GitHub 不是开源的；而 GitLab 是开源的。相比私有软件，我更喜欢开源，在本示例中，我将使用 GitLab。

如果你还没有 GitLab 帐户，请注册一个帐户并开始一个新项目。项目名称不必与 Atom 中的项目文件夹匹配，但是如果匹配，则可能更有意义。你可以将项目保留为私有，在这种情况下，只有你和任何一个你给予了明确权限的人可以访问它，或者，如果你希望该项目可供任何互联网上偶然发现它的人使用，则可以将其公开。

不要将 README 文件添加到项目中。

创建项目后，它将为你提供有关如何设置存储库的说明。如果你决定在终端中或通过单独的 GUI 使用 Git，这是非常有用的信息，但是 Atom 的工作流程则有所不同。

单击 GitLab 界面右上方的 “<ruby> 克隆 <rt>  Clone </rt></ruby>” 按钮。这显示了访问 Git 存储库必须使用的地址。复制 “SSH” 地址（而不是 “https” 地址）。

在 Atom 中，点击项目的 `.git` 目录，然后打开 `config` 文件。将下面这些配置行添加到该文件中，调整 `url` 值的 `seth/example.git` 部分以匹配你自己独有的地址。

```
[remote "origin"]
  url = git@gitlab.com:seth/example.git
  fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
  remote = origin
  merge = refs/heads/master
```

在 Git 标签的底部，出现了一个新按钮，标记为 “<ruby> 提取 <rt>  Fetch </rt></ruby>”。由于你的服务器是全新的服务器，因此没有可供你提取的数据，因此请右键单击该按钮，然后选择“<ruby> 推送 <rt>  Push </rt></ruby>”。这会将你的更改推送到你的 GitLab 帐户，现在你的项目已备份到 Git 服务器上。

你可以在每次提交后将更改推送到服务器。它提供了即刻的异地备份，并且由于数据量通常很少，因此它几乎与本地保存一样快。

### 撰写而 Git

Git 是一个复杂的系统，不仅对修订跟踪和备份有用。它还支持异步协作并鼓励实验。本文介绍了一些基础知识，但还有更多关于 Git 的文章和整本的书，以及如何使用它使你的工作更高效、更具弹性和更具活力。 从使用 Git 完成小任务开始，使用的次数越多，你会发现自己提出的问题就越多，最终你将学到的技巧越多。

---

via: <https://opensource.com/article/19/4/write-git>

作者：[Seth Kenlon](https://opensource.com/users/sethhttps://opensource.com/users/noreplyhttps://opensource.com/users/seth) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


十年生死两茫茫，Linux QQ 突然复活！

无论如何，还是值得表扬和鼓励的。

期盼多年的linuxQQ终于再次回来。正如文中所说的，如果越来越多的公司为linux提供优质的日常和企业级应用，那系统排名中linux就不是其它那项了，就应该独立的显示为linux。

坐井观天

很难想象，腾讯这样的公司linux版本的qq做的这么差

linux有了软件生态就能发展，是基于一个错误认知。linux需要的是教育出一批乐意用且会用linux的用户。因为linux各个版本是基于技术在发展，而不是用户偏好。配置文件说拆就拆，系统文件说改就改，不跟着学又怎么办？如果用户不能一直随着技术发展去学习，很快会耗散掉。当然，最基本的需求是完全可以满足的，但是遇到一些问题，身边总要起码有一两个同事是会用linux稍微能钻研一下的人吧。这是国内推广linux最大的瓶颈。而国内大学则是基于windows培养学生的。<br />
最后，我不看好腾讯，我觉得这是应付上面的一个临时性qq。

说实话,这篇报道写的真烂.尤其是使用linux的理由居然是QQ?

如果你真的喜欢linux,想qq有各种办法去实现.

我使用linux的原因恰恰就是为了避开这些毒瘤应用.

即便现在出了linux平台的应用,我也会放在隔离空间运行.

居然在linux社区发布这种内容,微软来的记者?

linux发展至今,从来没有因为qq而崛起,同样离开qq也一样长存.

我使用 linux的原因恰恰是因为可以避开这些毒瘤应用.

即便现在linux版的QQ出来了,如果要运行,也是在隔离空间中.

文章中,居然说 没有QQ还怎么用Linux? 你是微软请来的记者?

在一个linux社区出现这种文章,真是悲哀!

为什么你需要 QQ 因为你需要与麻瓜们聊些有的没的，然而你不得不去跟他们聊些有的没的，然后我们这些傲罗们用起了 wineQQ/wineTim;  现在给了一个能与麻瓜愉巴拉巴拉的机会，应该“感谢”腾讯才是

来自福建福州的 Yandex Browser 19.10|Windows 7 用户

你不用不代表别人不用，没有qq等常用软件才是linux无法普及的根本原因，你竟然还冷嘲热讽，到底是何居心？是不是微软派来的卧底？

赞同，不过现在工作中，交流用的都是企业微信、钉钉，非常不便，有不能改成其他交流方式，烦人

开什么国际玩笑，android某种程度上也算是linux的一个发行版，按你的说法，是因为它有QQ才称霸世界的？

PS: linux.cn第一次验证码提示错误的问题又出现了

搞笑的鸡肋。要手机扫码登陆的没有群共享的可有可无的聊天工具，<br />
纯净的LINUX 不需要鹅厂的垃圾，喜欢用LINUX的人不会因为一个QQ或者VX而不用。

玩 Linux 的都比较低调。

玩 Linux 的都比较低调。

Linux QQ的确有很长的路，我还是愿意花时间试试看

说不定 交付上去 是有钱拿的   这个仅代表个人参考意见

楼主是脑子不够用吗？  linux  从来就不是为桌面系统而生的，它是为了开发或者称为研究人员而生的，而且它开源的意义也在此，

「近两年来，自主研发的操作系统不断的刷屏，我们也一次次为自主研发的操作系统而欢呼。」

你把我逗笑了，究竟有多**才会为这些“自主研发”的操作系统“欢呼”。

使用linux的理由，的确不应该是QQ。呃...除非它想统治世界

已经凉勿悲伤。

# 十年生死两茫茫，Linux QQ 突然复活！

### BREAKING NEWS!!!

就在 1024 将将过去的深夜，突然，有人在 QQ 群内丢了一个链接和一句话“Linux QQ 发布了”，顿时在静悄悄的 QQ 群内，大家纷纷冒了出来。一时间，群内就和开了锅一样热闹，大家纷纷抄起心爱的土琵琶 Linux，各种截屏与测试消息齐飞，臭虫共吐槽一色。

为什么这么热闹？因为 Linux 桌面在中国得不到日常用户的青睐的一个主要吐槽点就是，“没有 QQ 啊！”，自从 10 年前——没错，就是 10 年前——据说是一位实习生随手做的 Linux QQ 停更并停用后，在 Linux 中使用 QQ 就成了一件麻烦事。这十年来，中国的 Linux 社区们有过种种努力和尝试，如，被封杀的 libqq，也有逐渐摸索和改善后的 Wine QQ 方案，各路 Linux 爱好者们在入门 Linux 之后大都会总结或整理一份 Linux QQ 秘籍，当然，随着各路国内的 Linux 操作系统的成熟，也纷纷提供了各种成熟的 Linux QQ 解决方案——至少能用吧。但是，这些都总有隔靴挠痒之憾。

好了，我想你肯定已经等急了，那么下面是 Linux QQ 的下载链接：

>
> <https://im.qq.com/linuxqq/download.html>
>
>
>

在下载安装时，我们继续聊聊 Linux QQ。

据[官方的消息](https://im.qq.com/linuxqq/features.html)，这次是 “Linux QQ全新回归”，并且支持“支持x64、ARM64、MIPS64三种架构”，“每种架构支持Debian系、红帽系、Arch Linux系、其它发行版中的一种或几种”，也“优化消息体验，完善消息收发能力”，版本则是明晃晃的“Linux QQ 2.0.0 Beta”，而在十年前，最后一个公开发布的版本是“QQ for Linux 1.0 Beta1”（据闻有未公示的 Beta2 版本）。

之所以支持 ARM64、MIPS64，我想这应该是对龙芯（mips64el）、飞腾（arm64）的额外支持吧。

据大家的反馈，这次的更新作为十年后第一次发布，亮点略有，但是大致还是沿袭了之前的版本，也存在一些 bug，有的同学经过几次重启、输入法适配，还是可以完成 QQ 基本功能的体验。

新版 Linux QQ 的发布，让不少极客们大呼“有生之年”。对于开发者来说，Linux 是他们所推崇的，但一直以来，因为中国办公习惯等历史原因，QQ 和微信的 Linux 版本很难获得新的用户。毕竟，一旦老板要给你发一个文件时，你发现你没有办法方便的传递到电脑上，或者在更多的场景下存在问题，都会让 Linux 的新用户们望而却步。

### 成也萧何败萧何

在 10 月 24 日这个程序员节上发布了 Linux QQ 2.0 版本让人觉得有点无厘头，甚至让人觉得可能仅仅是一个快闪活动，频繁的断线都让人担心是不是到了 25 日，可能 Linux QQ 就又不能用了。在 1024 程序员节的释出，使得 Linux QQ 可以以一个怀旧的形态重新出现在人们的眼前。但是，同样的，在这个节日的释出，也让人觉得这件事似乎并没有相信中的那么重视，对于 Linux QQ 的前景蒙上了一层阴影。

而且，腾讯并非一个慈善组织，对于腾讯这样的一个企业，是否会长期为一个很难产生现金流，而且小众的操作系统提供长期维护还尚未可知，更是让人无法相信 Linux QQ 的未来。

### 生态决定了操作系统的一切

近两年来，自主研发的操作系统不断的刷屏，我们也一次次为自主研发的操作系统而欢呼。但是，高潮之后，还能剩下来的是什么？

**我一直强调，操作系统卖的不是系统本身，而是卖因其产生的生态，由生态倒逼用户为系统本身付费**。就如同我尝试很多次从 iPhone 转回 Andorid ，从 macOS 转回 Windows，但是，也正如我自己每次失败后的总结一样，我没有办法完全抛弃掉 iPhone 、macOS，因为其所带来的生态是我无法迁移到 Android 上的，被操作系统所挟裹，不得不去面对的现实是：最终，我还是回到了 iPhone 所构建的生态。

对于 Linux 也是一样的，Linux 的服务端的用户诉求是明确的、简单直接的、绝大多数场景都可以被覆盖掉的。而桌面端的产品可能会有数万种不同类型的需求，这使得产品经理在进行设计时预见不了需求的问题，也就很正常的问题。

我们回过头去看，Linux 想要占领桌面市场还需要什么呢？需要的不仅仅是一个 Linux QQ，我们需要给更多人提供 Linux 上的微信、办公套件等等一系列软件。没有这些软件，我们就很难让用户迈过入门的第一关：**我如何登录 QQ /微信和我的同事、家人沟通**。如果他发现，我用了 Linux ，但是我却没有办法使用 QQ /微信进行沟通，那么我用 Linux 还有什么意义呢？

对于 Linux 来说，除了去适配这些我们所熟悉的软件以外，更是需要有厂商能够面向 Linux 开发出独占的杀手级用户，这样，才能够更好的去推动一个操作系统的发展。

### Linux 距离成为流行操作系统，还差多少个 QQ 微信

如果 Linux QQ 仍然继续更新，那对于 Linux 来说，是一大利好消息，一些做的好的、做的不好的应用，都可能会重新考虑创建自己的 Linux 版本。而过去，可能没有这样的机会。

你怎么看呢？


Pylint：让你的 Python 代码保持一致

Summary: 当你想要争论代码复杂性时，Pylint 是你的朋友。

当你想要争论代码复杂性时，Pylint 是你的朋友。

# Pylint：让你的 Python 代码保持一致

>
> 当你想要争论代码复杂性时，Pylint 是你的朋友。
>
>
>

<img src="content_image/album_201910_25_170108bq8kq28esj8q9eq4.jpg" alt="OpenStack source code \(Python\) in VIM" title="OpenStack source code (Python) in VIM">

Pylint 是更高层级的 Python 样式强制程序。而 [flake8](https://opensource.com/article/19/5/python-flake8) 和 [black](https://opensource.com/article/19/5/python-black) 检查的是“本地”样式：换行位置、注释的格式、发现注释掉的代码或日志格式中的错误做法之类的问题。

默认情况下，Pylint 非常激进。它将对每样东西都提供严厉的意见，从检查是否实际实现声明的接口到重构重复代码的可能性，这对新用户来说可能会很多。一种温和地将其引入项目或团队的方法是先关闭*所有*检查器，然后逐个启用检查器。如果你已经在使用 flake8、black 和 [mypy](https://opensource.com/article/19/5/python-mypy)，这尤其有用：Pylint 有相当多的检查器和它们在功能上重叠。

但是，Pylint 独有之处之一是能够强制执行更高级别的问题：例如，函数的行数或者类中方法的数量。

这些数字可能因项目而异，并且可能取决于开发团队的偏好。但是，一旦团队就参数达成一致，使用自动工具*强制化*这些参数非常有用。这是 Pylint 闪耀的地方。

### 配置 Pylint

要以空配置开始，请将 `.pylintrc` 设置为

```
[MESSAGES CONTROL]

disable=all
```

这将禁用所有 Pylint 消息。由于其中许多是冗余的，这是有道理的。在 Pylint 中，`message` 是一种特定的警告。

你可以通过运行 `pylint` 来确认所有消息都已关闭：

```
$ pylint <my package>
```

通常，向 `pylint` 命令行添加参数并不是一个好主意：配置 `pylint` 的最佳位置是 `.pylintrc`。为了使它做*一些*有用的事，我们需要启用一些消息。

要启用消息，在 `.pylintrc` 中的 `[MESSAGES CONTROL]` 下添加

```
enable=<message>,
       ...
```

对于看起来有用的“消息”（Pylint 称之为不同类型的警告）。我最喜欢的包括 `too-many-lines`、`too-many-arguments` 和 `too-many-branches`。所有这些会限制模块或函数的复杂性，并且无需进行人工操作即可客观地进行代码复杂度测量。

*检查器*是*消息*的来源：每条消息只属于一个检查器。许多最有用的消息都在[设计检查器](https://pylint.readthedocs.io/en/latest/technical_reference/features.html#design-checker)下。默认数字通常都不错，但要调整最大值也很简单：我们可以在 `.pylintrc` 中添加一个名为 `DESIGN` 的段。

```
[DESIGN]
max-args=7
max-locals=15
```

另一个有用的消息来源是“重构”检查器。我已启用一些最喜欢的消息有 `consider-using-dict-comprehension`、`stop-iteration-return`（它会查找正确的停止迭代的方式是 `return` 而使用了 `raise StopIteration` 的迭代器）和 `chained-comparison`，它将建议使用如 `1 <= x < 5`，而不是不太明显的 `1 <= x && 5 > 5` 的语法。

最后是一个在性能方面消耗很大的检查器，但它非常有用，就是 `similarities`。它会查找不同部分代码之间的复制粘贴来强制执行“不要重复自己”（DRY 原则）。它只启用一条消息：`duplicate-code`。默认的 “最小相似行数” 设置为 4。可以使用 `.pylintrc` 将其设置为不同的值。

```
[SIMILARITIES]
min-similarity-lines=3
```

### Pylint 使代码评审变得简单

如果你厌倦了需要指出一个类太复杂，或者两个不同的函数基本相同的代码评审，请将 Pylint 添加到你的[持续集成](https://opensource.com/business/15/7/six-continuous-integration-tools)配置中，并且只需要对项目复杂性准则的争论一次就行。

---

via: <https://opensource.com/article/19/10/python-pylint-introduction>

作者：[Moshe Zadka](https://opensource.com/users/moshez) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


如何获取 Linux 中的目录大小

Summary: 我将向您展示如何使用各种选项获取文件夹大小。

我将向您展示如何使用各种选项获取文件夹大小。

# 如何获取 Linux 中的目录大小

<img src="content_image/album_201910_25_171357abibvrznqqvqrrq7.jpg" alt="" title="">

你应该已经注意到，在 Linux 中使用 [ls 命令](https://www.2daygeek.com/linux-unix-ls-command-display-directory-contents/) 列出的目录内容中，目录的大小仅显示 4KB。这个大小正确吗？如果不正确，那它代表什么，又该如何获取 Linux 中的目录或文件夹大小？这是一个默认的大小，是用来存储磁盘上存储目录的元数据的大小。

Linux 上有一些应用程序可以 [获取目录的实际大小](https://www.2daygeek.com/how-to-get-find-size-of-directory-folder-linux/)。其中，磁盘使用率（`du`）命令已被 Linux 管理员广泛使用。

我将向您展示如何使用各种选项获取文件夹大小。

### 什么是 du 命令?

[du 命令](https://www.2daygeek.com/linux-check-disk-usage-files-directories-size-du-command/) 表示 <ruby> 磁盘使用率 <rt>  Disk Usage </rt></ruby>。这是一个标准的 Unix 程序，用于估计当前工作目录中的文件空间使用情况。

它使用递归方式总结磁盘使用情况，以获取目录及其子目录的大小。

如同我说的那样, 使用 `ls` 命令时，目录大小仅显示 4KB。参见下面的输出。

```
$ ls -lh | grep ^d

drwxr-xr-x  3 daygeek daygeek 4.0K Aug  2 13:57 Bank_Details
drwxr-xr-x  2 daygeek daygeek 4.0K Mar 15  2019 daygeek
drwxr-xr-x  6 daygeek daygeek 4.0K Feb 16  2019 drive-2daygeek
drwxr-xr-x 13 daygeek daygeek 4.0K Jan  6  2019 drive-mageshm
drwxr-xr-x 15 daygeek daygeek 4.0K Sep 29 21:32 Thanu_Photos
```

### 1) 在 Linux 上如何只获取父目录的大小

使用以下 `du` 命令格式获取给定目录的总大小。在该示例中，我们将得到 `/home/daygeek/Documents` 目录的总大小。

```
$ du -hs /home/daygeek/Documents
或
$ du -h --max-depth=0 /home/daygeek/Documents/
20G    /home/daygeek/Documents
```

详细说明：

* `du` – 这是一个命令
* `-h` – 以易读的格式显示大小 (例如 1K 234M 2G)
* `-s` – 仅显示每个参数的总数
* `--max-depth=N` – 目录的打印深度

### 2) 在 Linux 上如何获取每个目录的大小

使用以下 `du` 命令格式获取每个目录（包括子目录）的总大小。

在该示例中，我们将获得每个 `/home/daygeek/Documents` 目录及其子目录的总大小。

```
$ du -h /home/daygeek/Documents/ | sort -rh | head -20

20G    /home/daygeek/Documents/
9.6G    /home/daygeek/Documents/drive-2daygeek
6.3G    /home/daygeek/Documents/Thanu_Photos
5.3G    /home/daygeek/Documents/Thanu_Photos/Camera
5.3G    /home/daygeek/Documents/drive-2daygeek/Thanu-videos
3.2G    /home/daygeek/Documents/drive-mageshm
2.3G    /home/daygeek/Documents/drive-2daygeek/Thanu-Photos
2.2G    /home/daygeek/Documents/drive-2daygeek/Thanu-photos-by-month
916M    /home/daygeek/Documents/drive-mageshm/Tanisha
454M    /home/daygeek/Documents/drive-mageshm/2g-backup
415M    /home/daygeek/Documents/Thanu_Photos/WhatsApp Video
300M    /home/daygeek/Documents/drive-2daygeek/Thanu-photos-by-month/Jan-2017
288M    /home/daygeek/Documents/drive-2daygeek/Thanu-photos-by-month/Oct-2017
226M    /home/daygeek/Documents/drive-2daygeek/Thanu-photos-by-month/Sep-2017
219M    /home/daygeek/Documents/Thanu_Photos/WhatsApp Documents
213M    /home/daygeek/Documents/drive-mageshm/photos
163M    /home/daygeek/Documents/Thanu_Photos/WhatsApp Video/Sent
161M    /home/daygeek/Documents/Thanu_Photos/WhatsApp Images
154M    /home/daygeek/Documents/drive-2daygeek/Thanu-photos-by-month/June-2017
150M    /home/daygeek/Documents/drive-2daygeek/Thanu-photos-by-month/Nov-2016
```

### 3) 在 Linux 上如何获取每个目录的摘要

使用如下 `du` 命令格式仅获取每个目录的摘要。

```
$ du -hs /home/daygeek/Documents/* | sort -rh | head -10

9.6G    /home/daygeek/Documents/drive-2daygeek
6.3G    /home/daygeek/Documents/Thanu_Photos
3.2G    /home/daygeek/Documents/drive-mageshm
756K    /home/daygeek/Documents/Bank_Details
272K    /home/daygeek/Documents/user-friendly-zorin-os-15-has-been-released-TouchInterface1.png
172K    /home/daygeek/Documents/user-friendly-zorin-os-15-has-been-released-NightLight.png
164K    /home/daygeek/Documents/ConfigServer Security and Firewall (csf) Cheat Sheet.pdf
132K    /home/daygeek/Documents/user-friendly-zorin-os-15-has-been-released-Todo.png
112K    /home/daygeek/Documents/user-friendly-zorin-os-15-has-been-released-ZorinAutoTheme.png
96K    /home/daygeek/Documents/distro-info.xlsx
```

### 4) 在 Linux 上如何获取每个目录的不含子目录的大小

使用如下 `du` 命令格式来展示每个目录的总大小，不包括子目录。

```
$ du -hS /home/daygeek/Documents/ | sort -rh | head -20

5.3G    /home/daygeek/Documents/Thanu_Photos/Camera
5.3G    /home/daygeek/Documents/drive-2daygeek/Thanu-videos
2.3G    /home/daygeek/Documents/drive-2daygeek/Thanu-Photos
1.5G    /home/daygeek/Documents/drive-mageshm
831M    /home/daygeek/Documents/drive-mageshm/Tanisha
454M    /home/daygeek/Documents/drive-mageshm/2g-backup
300M    /home/daygeek/Documents/drive-2daygeek/Thanu-photos-by-month/Jan-2017
288M    /home/daygeek/Documents/drive-2daygeek/Thanu-photos-by-month/Oct-2017
253M    /home/daygeek/Documents/Thanu_Photos/WhatsApp Video
226M    /home/daygeek/Documents/drive-2daygeek/Thanu-photos-by-month/Sep-2017
219M    /home/daygeek/Documents/Thanu_Photos/WhatsApp Documents
213M    /home/daygeek/Documents/drive-mageshm/photos
163M    /home/daygeek/Documents/Thanu_Photos/WhatsApp Video/Sent
154M    /home/daygeek/Documents/drive-2daygeek/Thanu-photos-by-month/June-2017
150M    /home/daygeek/Documents/drive-2daygeek/Thanu-photos-by-month/Nov-2016
127M    /home/daygeek/Documents/drive-2daygeek/Thanu-photos-by-month/Dec-2016
100M    /home/daygeek/Documents/drive-2daygeek/Thanu-photos-by-month/Oct-2016
94M    /home/daygeek/Documents/drive-2daygeek/Thanu-photos-by-month/Nov-2017
92M    /home/daygeek/Documents/Thanu_Photos/WhatsApp Images
90M    /home/daygeek/Documents/drive-2daygeek/Thanu-photos-by-month/Dec-2017
```

### 5) 在 Linux 上如何仅获取一级子目录的大小

如果要获取 Linux 上给定目录的一级子目录（包括其子目录）的大小，请使用以下命令格式。

```
$ du -h --max-depth=1 /home/daygeek/Documents/

3.2G    /home/daygeek/Documents/drive-mageshm
4.0K    /home/daygeek/Documents/daygeek
756K    /home/daygeek/Documents/Bank_Details
9.6G    /home/daygeek/Documents/drive-2daygeek
6.3G    /home/daygeek/Documents/Thanu_Photos
20G    /home/daygeek/Documents/
```

### 6) 如何在 du 命令输出中获得总计

如果要在 `du` 命令输出中获得总计，请使用以下 `du` 命令格式。

```
$ du -hsc /home/daygeek/Documents/* | sort -rh | head -10

20G    total
9.6G    /home/daygeek/Documents/drive-2daygeek
6.3G    /home/daygeek/Documents/Thanu_Photos
3.2G    /home/daygeek/Documents/drive-mageshm
756K    /home/daygeek/Documents/Bank_Details
272K    /home/daygeek/Documents/user-friendly-zorin-os-15-has-been-released-TouchInterface1.png
172K    /home/daygeek/Documents/user-friendly-zorin-os-15-has-been-released-NightLight.png
164K    /home/daygeek/Documents/ConfigServer Security and Firewall (csf) Cheat Sheet.pdf
132K    /home/daygeek/Documents/user-friendly-zorin-os-15-has-been-released-Todo.png
112K    /home/daygeek/Documents/user-friendly-zorin-os-15-has-been-released-ZorinAutoTheme.png
```

---

via: <https://www.2daygeek.com/find-get-size-of-directory-folder-linux-disk-usage-du-command/>

作者：[Magesh Maruthamuthu](https://www.2daygeek.com/author/magesh/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[lnrCoder](https://github.com/lnrCoder) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


用 GameHub 集中管理你 Linux 上的所有游戏

# 用 GameHub 集中管理你 Linux 上的所有游戏

你在 Linux 上是怎么[玩游戏的呢](https://itsfoss.com/linux-gaming-guide/)? 让我猜猜，要不就是从软件中心直接安装，要不就选 Steam、GOG、Humble Bundle 等平台，对吧？但是，如果你有多个游戏启动器和客户端，又要如何管理呢？好吧，对我来说这简直令人头疼 —— 这也是我发现 [GameHub](https://tkashkin.tk/projects/gamehub/) 这个应用之后，感到非常高兴的原因。

GameHub 是为 Linux 发行版设计的一个桌面应用，它能让你“集中管理你的所有游戏”。这听起来很有趣，是不是？下面让我来具体说明一下。

### 集中管理不同平台 Linux 游戏的 GameHub

让我们看看，对玩家来说，让 GameHub 成为一个[不可或缺的 Linux 应用](https://itsfoss.com/essential-linux-applications/)的功能，都有哪些。

#### Steam、GOG & Humble Bundle 支持

它支持 Steam、[GOG](https://www.gog.com/) 和 [Humble Bundle](https://www.humblebundle.com/monthly?partner=itsfoss) 账户整合。你可以登录你的 GameHub 账号，从而在你的库管理器中管理所有游戏。

对我来说，我在 Steam 上有很多游戏，Humble Bundle 上也有一些。我不能确保它支持所有平台，但可以确信的是，主流平台游戏是没有问题的。

#### 支持原生游戏

[有很多网站专门推荐 Linux 游戏，并支持下载](https://itsfoss.com/download-linux-games/)。你可以通过下载安装包，或者添加可执行文件，从而管理原生游戏。

可惜的是，现在无法在 GameHub 内搜索 Linux 游戏。如上图所示，你需要分别下载游戏，随后再将其添加到 GameHub 中。

#### 模拟器支持

用模拟器，你可以在 [Linux 上玩复古游戏](https://itsfoss.com/play-retro-games-linux/)。正如上图所示，你可以添加模拟器（并导入模拟的镜像）。

你可以在 [RetroArch](https://www.retroarch.com/) 查看已有的模拟器，但也能根据需求添加自定义模拟器。

#### 用户界面

当然，用户体验很重要。因此，探究下用户界面都有些什么，也很有必要。

我个人觉得，这一应用很容易使用，并且黑色主题是一个加分项。

#### 手柄支持

如果你习惯在 Linux 系统上用手柄玩游戏 —— 你可以轻松在设置里添加，启用或禁用它。

#### 多个数据提供商

因为它需要获取你的游戏信息（或元数据），也意味着它需要一个数据源。你可以看到下图列出的所有数据源。

这里你什么也不用做 —— 但如果你使用的是 steam 之外的其他平台，你需要为 [IDGB 生成一个 API 密钥](https://www.igdb.com/api)。

我建议只有出现 GameHub 中的提示/通知，或有些游戏在 GameHub 上没有任何描述/图片/状态时，再这么做。

#### 兼容性选项

你有不支持在 Linux 上运行的游戏吗？

不用担心，GameHub 上提供了多种兼容工具，如 Wine/Proton，你可以利用它们来玩游戏。

我们无法确定具体哪个兼容工具适用于你 —— 所以你需要自己亲自测试。然而，对许多游戏玩家来说，这的确是个很有用的功能。

### GameHub: 如何安装它呢？

首先，你可以直接在软件中心或者应用商店内搜索。 它在 “Pop!\_Shop” 之下。所以，它在绝大多数官方源中都能找到。

如果你在这些地方都没有找到，你可以手动添加源，并从终端上安装它，你需要输入以下命令：

```
sudo add-apt-repository ppa:tkashkin/gamehub
sudo apt update
sudo apt install com.github.tkashkin.gamehub
```

如果你遇到了 “add-apt-repository command not found” 这个错误，你可以看看，[add-apt-repository not found error.](https://itsfoss.com/add-apt-repository-command-not-found/)这篇文章，它能帮你解决这一问题。

这里还提供 AppImage 和 FlatPak 版本。 在[官网](https://tkashkin.tk/projects/gamehub/) 上，你可以针对找到其他 Linux 发行版的安装手册。

同时，你还可以从它的 [GitHub 页面](https://github.com/tkashkin/GameHub/releases)下载之前版本的安装包.

[GameHub](https://tkashkin.tk/projects/gamehub/)

### 如何在 GameHub 上管理你的游戏？

在启动程序后，你可以将自己的 Steam/GOG/Humble Bundle 账号添加进来。

对于 Steam，你需要在 Linux 发行版上安装 Steam 客户端。一旦安装完成，你可以轻松将账号中的游戏导入 GameHub。

对于 GOG & Humble Bundle，登录后，就能直接在 GameHub 上管理游戏了。

如果你想添加模拟器或者本地安装文件，点击窗口右上角的 “+” 按钮进行添加。

### 如何安装游戏？

对于 Steam 游戏，它会自动启动 Steam 客户端，从而下载/安装游戏（我希望之后安装游戏，可以不用启动 Steam！）

但对于 GOG/Humble Bundle，登录后就能直接、下载安装游戏。必要的话，对于那些不支持在 Linux 上运行的游戏，你可以使用兼容工具。

无论是模拟器游戏，还是本地游戏，只需添加安装包或导入模拟器镜像就可以了。这里没什么其他步骤要做。

### 注意

GameHub 是相当灵活的一个集中游戏管理应用。 用户界面和选项设置也相当直观。

你之前是否使用过这一应用呢？如果有，请在评论里写下你的感受。

而且，如果你想尝试一些与此功能相似的工具/应用，请务必告诉我们。

---

via: <https://itsfoss.com/gamehub/>

作者：[Ankush Das](https://itsfoss.com/author/ankush/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wenwensnow](https://github.com/wenwensnow) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


如何在 CentOS8/RHEL8 中配置 Rsyslog 服务器

# 如何在 CentOS8/RHEL8 中配置 Rsyslog 服务器

Rsyslog 是一个自由开源的日志记录程序，在 CentOS 8 和 RHEL 8 系统上默认可用。它提供了一种从客户端节点到单个中央服务器的“集中日志”的简单有效的方法。日志集中化有两个好处。首先，它简化了日志查看，因为系统管理员可以在一个中心节点查看远程服务器的所有日志，而无需登录每个客户端系统来检查日志。如果需要监视多台服务器，这将非常有用，其次，如果远程客户端崩溃，你不用担心丢失日志，因为所有日志都将保存在中心的 Rsyslog 服务器上。rsyslog 取代了仅支持 UDP 协议的 syslog。它以优异的功能扩展了基本的 syslog 协议，例如在传输日志时支持 UDP 和 TCP 协议，增强的过滤功能以及灵活的配置选项。让我们来探讨如何在 CentOS 8 / RHEL 8 系统中配置 Rsyslog 服务器。

### 预先条件

我们将搭建以下实验环境来测试集中式日志记录过程：

* Rsyslog 服务器 CentOS 8 Minimal IP 地址： 10.128.0.47
* 客户端系统 RHEL 8 Minimal IP 地址： 10.128.0.48

通过上面的设置，我们将演示如何设置 Rsyslog 服务器，然后配置客户端系统以将日志发送到 Rsyslog 服务器进行监视。

让我们开始！

### 在 CentOS 8 上配置 Rsyslog 服务器

默认情况下，Rsyslog 已安装在 CentOS 8 / RHEL 8 服务器上。要验证 Rsyslog 的状态，请通过 SSH 登录并运行以下命令：

```
$ systemctl status rsyslog
```

示例输出:

如果由于某种原因 Rsyslog 不存在，那么可以使用以下命令进行安装：

```
$ sudo yum install rsyslog
```

接下来，你需要修改 Rsyslog 配置文件中的一些设置。打开配置文件：

```
$ sudo vim /etc/rsyslog.conf
```

滚动并取消注释下面的行，以允许通过 UDP 协议接收日志：

```
module(load="imudp") # needs to be done just once
input(type="imudp" port="514")
```

同样，如果你希望启用 TCP rsyslog 接收，请取消注释下面的行：

```
module(load="imtcp") # needs to be done just once
input(type="imtcp" port="514")
```

保存并退出配置文件。

要从客户端系统接收日志，我们需要在防火墙上打开 Rsyslog 默认端口 514。为此，请运行：

```
# sudo firewall-cmd  --add-port=514/tcp  --zone=public  --permanent
```

接下来，重新加载防火墙保存更改：

```
# sudo firewall-cmd --reload
```

示例输出：

接下来，重启 Rsyslog 服务器:

```
$ sudo systemctl restart rsyslog
```

要在启动时运行 Rsyslog，运行以下命令：

```
$ sudo systemctl enable rsyslog
```

要确认 Rsyslog 服务器正在监听 514 端口，请使用 `netstat` 命令，如下所示：

```
$ sudo netstat -pnltu
```

示例输出：

完美！我们已经成功配置了 Rsyslog 服务器来从客户端系统接收日志。

要实时查看日志消息，请运行以下命令：

```
$ tail -f /var/log/messages
```

现在开始配置客户端系统。

### 在 RHEL 8 上配置客户端系统

与 Rsyslog 服务器一样，登录并通过以下命令检查 rsyslog 守护进程是否正在运行：

```
$ sudo systemctl status rsyslog
```

示例输出：

接下来，打开 rsyslog 配置文件：

```
$ sudo vim /etc/rsyslog.conf
```

在文件末尾，添加以下行：

```
*.* @10.128.0.47:514           # Use @ for UDP protocol
*.* @@10.128.0.47:514          # Use @@ for TCP protocol
```

保存并退出配置文件。就像 Rsyslog 服务器一样，打开 514 端口，这是防火墙上的默认 Rsyslog 端口：

```
$ sudo firewall-cmd  --add-port=514/tcp  --zone=public  --permanent
```

接下来，重新加载防火墙以保存更改：

```
$ sudo firewall-cmd --reload
```

接下来，重启 rsyslog 服务：

```
$ sudo systemctl restart rsyslog
```

要在启动时运行 Rsyslog，请运行以下命令：

```
$ sudo systemctl enable rsyslog
```

### 测试日志记录操作

已经成功安装并配置 Rsyslog 服务器和客户端后，就该验证你的配置是否按预期运行了。

在客户端系统上，运行以下命令：

```
# logger "Hello guys! This is our first log"
```

现在进入 Rsyslog 服务器并运行以下命令来实时查看日志消息：

```
# tail -f /var/log/messages
```

客户端系统上命令运行的输出显示在了 Rsyslog 服务器的日志中，这意味着 Rsyslog 服务器正在接收来自客户端系统的日志：

就是这些了！我们成功设置了 Rsyslog 服务器来接收来自客户端系统的日志信息。

---

via: <https://www.linuxtechi.com/configure-rsyslog-server-centos-8-rhel-8/>

作者：[James Kiarie](https://www.linuxtechi.com/author/james/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


如何使用物联网设备来确保儿童安全？

# 如何使用物联网设备来确保儿童安全？

<img src="content_image/album_201910_27_064228phqcuk9cuggkkk1t.jpg" alt="" title="Visual Internet of things_EB May18">

IoT （物联网）设备正在迅速改变我们的生活。这些设备无处不在，从我们的家庭到其它行业。根据一些预测数据，到 2020 年，将会有 100 亿个 IoT 设备。到 2025 年，该数量将增长到 220 亿。目前，物联网已经在很多领域得到了应用，包括智能家居、工业生产过程、农业甚至医疗保健领域。伴随着如此广泛的应用，物联网显然已经成为近年来的热门话题之一。

多种因素促成了物联网设备在多个学科的爆炸式增长。这其中包括低成本处理器和无线连接的的可用性，以及开源平台的信息交流推动了物联网领域的创新。与传统的应用程序开发相比，物联网设备的开发成指数级增长，因为它的资源是开源的。

在解释如何使用物联网设备来保护儿童之前，必须对物联网技术有基本的了解。

### IoT 设备是什么？

IoT 设备是指那些在没有人类参与的情况下彼此之间可以通信的设备。因此，许多专家并不将智能手机和计算机视为物联网设备。此外，物联网设备必须能够收集数据并且能将收集到的数据传送到其他设备或云端进行处理。

然而，在某些领域中，我们需要探索物联网的潜力。儿童往往是脆弱的，他们很容易成为犯罪分子和其他蓄意伤害者的目标。无论在物理世界还是数字世界中，儿童都很容易面临犯罪的威胁。因为父母不能始终亲自到场保护孩子；这就是为什么需要监视工具了。

除了适用于儿童的可穿戴设备外，还有许多父母监视应用程序，例如 Xnspy，可实时监控儿童并提供信息的实时更新。这些工具可确保儿童安全。可穿戴设备确保儿童身体上的安全性，而家长监控应用可确保儿童的上网安全。

由于越来越多的孩子花费时间在智能手机上，毫无意外地，他们也就成为诈骗分子的主要目标。此外，由于恋童癖、网络自夸和其他犯罪在网络上的盛行，儿童也有可能成为网络欺凌的目标。

这些解决方案够吗？我们需要找到物联网解决方案，以确保孩子们在网上和线下的安全。在当代，我们如何确保孩子的安全？我们需要提出创新的解决方案。 物联网可以帮助保护孩子在学校和家里的安全。

### 物联网的潜力

物联网设备提供的好处很多。举例来说，父母可以远程监控自己的孩子，而又不会显得太霸道。因此，儿童在拥有安全环境的同时也会有空间和自由让自己变得独立。

而且，父母也不必在为孩子的安全而担忧。物联网设备可以提供 7x24 小时的信息更新。像 Xnspy 之类的监视应用程序在提供有关孩子的智能手机活动信息方面更进了一步。随着物联网设备变得越来越复杂，拥有更长使用寿命的电池只是一个时间问题。诸如位置跟踪器之类的物联网设备可以提供有关孩子下落的准确详细信息，所以父母不必担心。

虽然可穿戴设备已经非常好了，但在确保儿童安全方面，这些通常还远远不够。因此，要为儿童提供安全的环境，我们还需要其他方法。许多事件表明，儿童在学校比其他任何公共场所都容易受到攻击。因此，学校需要采取安全措施，以确保儿童和教师的安全。在这一点上，物联网设备可用于检测潜在威胁并采取必要的措施来防止攻击。威胁检测系统包括摄像头。系统一旦检测到威胁，便可以通知当局，如一些执法机构和医院。智能锁等设备可用于封锁学校（包括教室），来保护儿童。除此之外，还可以告知父母其孩子的安全，并立即收到有关威胁的警报。这将需要实施无线技术，例如 Wi-Fi 和传感器。因此，学校需要制定专门用于提供教室安全性的预算。

智能家居实现拍手关灯，也可以让你的家庭助手帮你关灯。同样，物联网设备也可用在屋内来保护儿童。在家里，物联网设备（例如摄像头）为父母在照顾孩子时提供 100％ 的可见性。当父母不在家里时，可以使用摄像头和其他传感器检测是否发生了可疑活动。其他设备（例如连接到这些传感器的智能锁）可以锁门和窗，以确保孩子们的安全。

同样，可以引入许多物联网解决方案来确保孩子的安全。

### 有多好就有多坏

物联网设备中的传感器会创建大量数据。数据的安全性是至关重要的一个因素。收集的有关孩子的数据如果落入不法分子手中会存在危险。因此，需要采取预防措施。IoT 设备中泄露的任何数据都可用于确定行为模式。因此，必须对提供不违反用户隐私的安全物联网解决方案投入资金。

IoT 设备通常连接到 Wi-Fi，用于设备之间传输数据。未加密数据的不安全网络会带来某些风险。这样的网络很容易被窃听。黑客可以使用此类网点来入侵系统。他们还可以将恶意软件引入系统，从而使系统变得脆弱、易受攻击。此外，对设备和公共网络（例如学校的网络）的网络攻击可能导致数据泄露和私有数据盗用。 因此，在实施用于保护儿童的物联网解决方案时，保护网络和物联网设备的总体计划必须生效。

物联网设备保护儿童在学校和家里的安全的潜力尚未发现有什么创新。我们需要付出更多努力来保护连接 IoT 设备的网络安全。此外，物联网设备生成的数据可能落入不法分子手中，从而造成更多麻烦。因此，这是物联网安全至关重要的一个领域。

---

via: <https://opensourceforu.com/2019/10/how-to-use-iot-devices-to-keep-children-safe/>

作者：[Andrew Carroll](https://opensourceforu.com/author/andrew-carroll/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[Morisun029](https://github.com/Morisun029) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


那个你们眼中的硬件厂商，其实已经是云计算的急先锋

# 那个你们眼中的硬件厂商，其实已经是云计算的急先锋

从业计算机行业以来，我接触过很多计算机硬件厂商，不知自何时开始，从我们的身边的个人电脑到企业级的服务器和存储设备，越来越多的见到了戴尔的身影。就在我前两天参加的戴尔科技峰会的一个媒体群访会上，戴尔科技集团全球副总裁、大中华区数据中心销售总经理孔大勇先生就笑言，“在座的诸位中有 15 台笔记本电脑，其中 9 台是戴尔的”。更别说如浩如烟海般充斥在数据中心的各式戴尔设备。

可能在很多人眼里，戴尔还是那个生产、销售硬件服务器的戴尔，但是很多人不知道的是，戴尔已经在云计算领域布局多年。

云计算这个 IT 时髦词虽然已经提出很久了，但是令人有些意外的是，这个时髦词居然历久弥新，在 IT 领域这么变化日新月异的行业中，还能不断焕发新的变化。从早期的分布式计算、虚拟技术，到现在的容器技术，从公有云、私有云，到混合云、多云，不经意间，一篇篇新的技术篇章掀过，我们所面临的已经是一个崭新的云时代。

这次有幸参加戴尔科技峰会 2019，让我恍若置身一场云原生大会，除了很多的新技术突破和硬件新品发布，戴尔也在云计算领域取得了长足的发展。由于我们长期以来一直关注云技术的发展，所以我专门着眼于观察戴尔在云计算方面的新变化，下面就让我来给大家介绍一下本次大会中我的感受。

### 戴尔的云端之路

自 2010 年戴尔发布<ruby> 高效企业生态系统 <rp>  （ </rp> <rt>  Efficient Enterprise Ecosystem </rt> <rp>  ） </rp></ruby>（E3）的云计算战略以后，就开始不断布局云计算生态，将戴尔从一个传统的计算机硬件企业，转变成为一个软硬件两把抓的云计算强企。

得益于多年企业级硬件的产品与设备，戴尔从一开始就为其在企业级场景下的硬件设施打好了基础，而近年来，戴尔也不断并购了一些企业，并以此为契机，增加了自己在云计算领域的能力。

近年来，戴尔收购了 EMC，从而掌握了虚拟化领域巨头 VMWare，又在其后收购了Pivotal （后者是顶尖的云计算平台公司，我们所熟悉的 12306 的架构中，就有一大部分在使用 Pivotal 的产品），不断增强自己在云计算领域的能力，并借助于自己在传统硬件领域的强项，去改变企业、政府对于云计算的认识，协助他们完成数字化转型。同时，可能会令很多人意外的是，戴尔也是 Kubernetes 项目代码贡献度全球第二的企业，拥有丰富的 Kubernetes 开发人才。

和我们所熟悉的很多公有云厂商不同，他们很多是从开始做云的时候，就决定要做公有云，希望去服务尽可能多的用户，服务更多的中长尾用户。戴尔则是更关注于企业级市场，关注企业用户的需求。当然，这也是源自于戴尔在服务器等企业级市场的深耕，能够精确把握企业对于云计算的需求。

### 源自不同认识的混合云

在不断服务企业级客户的过程中，戴尔收集到了大量的用户反馈，企业级用户往往不同于个人用户，将自己的所有业务都集中在一个平台上，企业级客户会同时使用两家甚至是更多的云平台，以确保自己的业务不会因为某一家的云服务出现问题而整体业务崩盘。单一平台所构建的信息孤岛对于企业级用户来说，是一个很大的风险，这也是为什么近些年来，我们看到 Kubernetes 的不断发展。企业面临着信息孤岛的问题而不知如何解决，Kubernetes 是一个标准化的云计算解决方案，帮助他们提升产品和能力的可迁移性，而戴尔则对该问题提供了更进一步的解决方案。

前面我们提到，VMWare 目前是属于戴尔公司的，而 VMWare 本身，是目前全球用量最大的云计算虚拟化平台，你所知道或不知道的云计算平台，他们的背后运转的很多都是 VMWare 计算平台。得益于 VMWare 多年的积累，戴尔拥有丰富的私有云建设经验，可以在企业现有的基础设施上快速构建起一套能力强大的私有云体系。同时，因为不少云计算平台使用的也是 VMWare，使得企业自行构建的私有云能够更加轻松的与使用了 VMWare 的公有云结合，构建出一体化体验的云计算平台，让用户可以在一个平台上调度和管理多个不同的云计算平台，在这个平台上做计算、做数据挖掘、做资源调度，帮助企业更加简单的构建私有云。

这样构建出的私有云，拥有真正意义上的“混合”云的能力，而不再是仅仅与某一家云计算厂商提供的能力所结合，它可以让你同时和多个不同的云计算平台对接，还可以和你自己企业内部构建的私有云所结合，让不同云计算平台的业务可以通过戴尔所提供的服务和产品无缝对接，让业务正常运转。

### VCF 如何解决问题

由我们所熟悉的一些云计算公司，诸如 AWS、Azure、Google ，以及全球 4000 多家 VCPP（VMware Cloud Provider Program）项目成员所组成的VCF（VMware Cloud Foundation）建立起一套云计算基础设施的标准，都采用统一的 VCF 作为其标准，构建自己的云计算服务。而基于VMware最佳实践来搭建的软件定义的数据中心，其中所有的云都是基于同一个标准、同一个架构，其中的产品也就可以随意的在不同的云计算平台中任意迁移。

对于企业来说，VCF 的存在大大降低了其产品在不同服务商之间切换的成本，企业可以根据自己的需求，随意的在其所使用的几个云服务商之间进行切换，甚至是切换到私有云上！相比于我们熟悉的 Kubernetes，VCF 在易用性上的投入，更是让企业可以简单的实现业务需求，支撑业务的整体运转。

### SDDC 的未来

SDDC 是 VCF 的一个产品组件，想来这个词去描述戴尔的混合云战略真是再准确不过了。

什么是 SDDC？和我们所熟悉的 SDN（<ruby> 软件定义网络 <rp>  （ </rp> <rt>  Software-defined Network </rt> <rp>  ） </rp></ruby>）类似，SDDC 就是<ruby> 软件定义的数据中心 <rp>  （ </rp> <rt>  Software-Defined DataCenter </rt> <rp>  ） </rp></ruby>，以软件的方式来定义数据中心。而能够做到这些，源自于戴尔自己多年制作企业级服务器和其所收购的 VMWare 多年来在虚拟化领域的投入。VMware 目前是全球私有云环境里面市场占有率体量也是最大的。和其他云计算厂商相比，戴尔拥有业界体量最大、份额最高、方案最全、价值最高的方案，可以满足用户的不同场景下的需求。

在戴尔看来，未来企业级云计算的市场会逐渐归一，所有的公有云也好、私有云也好，终将会回归到混合云的模式，因为企业不可能将所有的核心数据都交付给云计算厂商，他们需要一个私有云，帮助承载和处理企业内部的一些机密数据。而私有云和公有云的对接，就会逐渐成为一个大的问题。做虚拟化出身的 VMWare 帮助戴尔解决了自己的问题，提出了 SDDC 的方式，来完成这个业务需求，并基于 VMWare 多年的积累，为 SDDC 加入大量云计算企业的支持，为客户拉了一针强心剂。

### 在中国，为中国

在过去的数年里，戴尔不但在技术研究方面深入到云计算行业，也将自己的云计算产品推向市场，接受市场的考验。

比如说，戴尔近几年借助于区域服务商，将其云计算的产品铺设到政府和企业场景中，帮助他们完成信息化、数字化。

很多企业和部门内部的基础设施虽然投入昂贵，但是，由于维护和管理不善，从而导致大量的病毒、木马潜伏在服务器内，占用了设备性能，并造成了数据泄露和安全隐患。此外，也因为基础设施维护的不完善，没有办法有效利用基础设施资源，更谈不上数据的共享。同时导致了各部门都需要自行建设基础设施，造成了大量的基础设施和资源的浪费，数据中心建设标准参差不齐，无法保证数据的安全和业务的稳定，为服务埋下了隐患。

引入戴尔的混合云方案以后，机构对机房进行统一，建设一个主数据中心，从而完成管理集群、高性能集群、vSAN 集群等，帮助他们构建了一套性能良好、基础设施完备的内部云方案，同时，得益于 VMWare 本身基础设施建设的能力，还引入了 vSAN，借助其拓展能力，实现了双活的副数据中心的构建，以确保数据的安全和业务的运转。

借助不断推动企业、政府的数字化、信息化，戴尔实现了自己在几年前定下的诺言，“在中国、为中国”，帮助中国变得越来越好。

### 结语

这一次的戴尔科技峰会让我大开眼界，过去那个只做硬件的戴尔，如今不仅做了云，还做取得了长足的发展，站在了云计算技术的前沿，不知道未来，戴尔还能够为我们带来什么更加亮眼的产品？


MX Linux排行都是刷的。要说最好的发行版还是Manjaro( ￣▽￣)σ

# 随着 Debian 10.1 “Buster” 的发布，MX Linux 19 也发布了

<img src="content_image/album_201910_28_005713x1p1yyzja2tyxjn1.jpg" alt="MX Linux 19" title="MX Linux 19">

MX Linux 18 是我在[最佳 Linux 发行版](/article-11411-1.html)中的主要推荐的发行版之一，特别是当你在考虑 Ubuntu 以外的发行版时。

它基于 Debian 9.6 “Stretch”，具有令人难以置信的快速流畅的体验。

现在，作为该发行版的主要升级版本，MX Linux 19 带来了许多重大改进和变更。在这里，我们将看一下主要亮点。

### MX Linux 19 中的新功能

#### Debian 10 “Buster”

这个值得一提，因为 Debian 10 实际上是 MX Linux 18 所基于的 Debian 9.6 “Stretch” 的主要升级。

如果你对 Debian 10 “Buster” 的变化感到好奇，建议你阅读有关 [Debian 10 “Buster” 的新功能](/article-11071-1.html)的文章。

#### Xfce 桌面 4.14

[Xfce 4.14](https://xfce.org/about/news) 正是 Xfce 开发团队提供的最新产品。就个人而言，我不是 Xfce 桌面环境的粉丝，但是当你在 Linux 发行版（尤其是 MX Linux 19）上使用它时，它超快的性能会让你惊叹。

或许你会感兴趣，我们也有一个快速指南来帮助你[自定义 Xfce](https://itsfoss.com/customize-xfce/)。

#### 升级的软件包及最新的 Debian 内核 4.19

除了 [GIMP](https://itsfoss.com/gimp-2-10-release/)、MESA、Firefox 等的更新软件包之外，它还随附有 Debian “Buster” 可用的最新内核 4.19。

#### 升级的 MX 系列应用

如果你以前使用过 MX Linux，则可能会知道它已经预装了有用的 MX 系列应用，可以帮助你快速完成更多工作。

像 MX-installer 和 MX-packageinstaller 这样的应用程序得到了显著改进。

除了这两个以外，所有其他 MX 工具也已不同程度的进行了更新和修复错误、添加了新的翻译（或只是改善了用户体验）。

#### 其它改进

考虑到这是一次重大升级，很明显，底层的更改要多于表面（包括最新的 antiX live 系统更新）。

你可以在他们的[官方公告](https://mxlinux.org/blog/mx-19-patito-feo-released/)中查看更多详细信息。你还可以从开发人员那里观看以下视频，它介绍了 MX Linux 19 中的所有新功能：

### 获取 MX Linux 19

即使是你现在正在使用 MX Linux 18 版本，你也[无法](https://mxlinux.org/migration/)升级到 MX Linux 19。你需要像其他人一样进行全新安装。

你可以从此页面下载 MX Linux 19：

* [下载 MX Linux 19](https://mxlinux.org/download-links/)

### 结语

在 MX Linux 18 上，我在使用 WiFi 适配器时遇到了问题，通过[论坛](https://forum.mxlinux.org/viewtopic.php?t=52201)解决了该问题，但看来 MX Linux 19 仍未解决该问题。因此，如果你在安装 MX Linux 19 之后遇到了相同的问题，你可能想要查看一下我的[论坛帖子](https://forum.mxlinux.org/viewtopic.php?t=52201)。

如果你使用的是 MX Linux 18，那么这绝对是一个令人印象深刻的升级。

你尝试过了吗？你对新的 MX Linux 19 版本有何想法？让我知道你在以下评论中的想法。

---

via: <https://itsfoss.com/mx-linux-19/>

作者：[Ankush Das](https://itsfoss.com/author/ankush/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


构建一个即时消息应用（二）：OAuth

Summary: 在这篇帖子中，我们将会通过为应用添加社交登录功能进入后端开发。

在这篇帖子中，我们将会通过为应用添加社交登录功能进入后端开发。

# 构建一个即时消息应用（二）：OAuth

<img src="content_image/album_201910_28_070221l7wmy37l9llsl23z.jpg" alt="" title="">

[上一篇：模式](/article-11396-1.html)。

在这篇帖子中，我们将会通过为应用添加社交登录功能进入后端开发。

社交登录的工作方式十分简单：用户点击链接，然后重定向到 GitHub 授权页面。当用户授予我们对他的个人信息的访问权限之后，就会重定向回登录页面。下一次尝试登录时，系统将不会再次请求授权，也就是说，我们的应用已经记住了这个用户。这使得整个登录流程看起来就和你用鼠标单击一样快。

如果进一步考虑其内部实现的话，过程就会变得复杂起来。首先，我们需要注册一个新的 [GitHub OAuth 应用](https://github.com/settings/applications/new)。

这一步中，比较重要的是回调 URL。我们将它设置为 `http://localhost:3000/api/oauth/github/callback`。这是因为，在开发过程中，我们总是在本地主机上工作。一旦你要将应用交付生产，请使用正确的回调 URL 注册一个新的应用。

注册以后，你将会收到“客户端 id”和“安全密钥”。安全起见，请不要与任何人分享他们 ?

顺便让我们开始写一些代码吧。现在，创建一个 `main.go` 文件：

```
package main

import (
    "database/sql"
    "fmt"
    "log"
    "net/http"
    "net/url"
    "os"
    "strconv"

    "github.com/gorilla/securecookie"
    "github.com/joho/godotenv"
    "github.com/knq/jwt"
    _ "github.com/lib/pq"
    "github.com/matryer/way"
    "golang.org/x/oauth2"
    "golang.org/x/oauth2/github"
)

var origin *url.URL
var db *sql.DB
var githubOAuthConfig *oauth2.Config
var cookieSigner *securecookie.SecureCookie
var jwtSigner jwt.Signer

func main() {
    godotenv.Load()

    port := intEnv("PORT", 3000)
    originString := env("ORIGIN", fmt.Sprintf("http://localhost:%d/", port))
    databaseURL := env("DATABASE_URL", "postgresql://root@127.0.0.1:26257/messenger?sslmode=disable")
    githubClientID := os.Getenv("GITHUB_CLIENT_ID")
    githubClientSecret := os.Getenv("GITHUB_CLIENT_SECRET")
    hashKey := env("HASH_KEY", "secret")
    jwtKey := env("JWT_KEY", "secret")

    var err error
    if origin, err = url.Parse(originString); err != nil || !origin.IsAbs() {
        log.Fatal("invalid origin")
        return
    }

    if i, err := strconv.Atoi(origin.Port()); err == nil {
        port = i
    }

    if githubClientID == "" || githubClientSecret == "" {
        log.Fatalf("remember to set both $GITHUB_CLIENT_ID and $GITHUB_CLIENT_SECRET")
        return
    }

    if db, err = sql.Open("postgres", databaseURL); err != nil {
        log.Fatalf("could not open database connection: %v\n", err)
        return
    }
    defer db.Close()
    if err = db.Ping(); err != nil {
        log.Fatalf("could not ping to db: %v\n", err)
        return
    }

    githubRedirectURL := *origin
    githubRedirectURL.Path = "/api/oauth/github/callback"
    githubOAuthConfig = &oauth2.Config{
        ClientID:     githubClientID,
        ClientSecret: githubClientSecret,
        Endpoint:     github.Endpoint,
        RedirectURL:  githubRedirectURL.String(),
        Scopes:       []string{"read:user"},
    }

    cookieSigner = securecookie.New([]byte(hashKey), nil).MaxAge(0)

    jwtSigner, err = jwt.HS256.New([]byte(jwtKey))
    if err != nil {
        log.Fatalf("could not create JWT signer: %v\n", err)
        return
    }

    router := way.NewRouter()
    router.HandleFunc("GET", "/api/oauth/github", githubOAuthStart)
    router.HandleFunc("GET", "/api/oauth/github/callback", githubOAuthCallback)
    router.HandleFunc("GET", "/api/auth_user", guard(getAuthUser))

    log.Printf("accepting connections on port %d\n", port)
    log.Printf("starting server at %s\n", origin.String())
    addr := fmt.Sprintf(":%d", port)
    if err = http.ListenAndServe(addr, router); err != nil {
        log.Fatalf("could not start server: %v\n", err)
    }
}

func env(key, fallbackValue string) string {
    v, ok := os.LookupEnv(key)
    if !ok {
        return fallbackValue
    }
    return v
}

func intEnv(key string, fallbackValue int) int {
    v, ok := os.LookupEnv(key)
    if !ok {
        return fallbackValue
    }
    i, err := strconv.Atoi(v)
    if err != nil {
        return fallbackValue
    }
    return i
}
```

安装依赖项：

```
go get -u github.com/gorilla/securecookie
go get -u github.com/joho/godotenv
go get -u github.com/knq/jwt
go get -u github.com/lib/pq
ge get -u github.com/matoous/go-nanoid
go get -u github.com/matryer/way
go get -u golang.org/x/oauth2
```

我们将会使用 `.env` 文件来保存密钥和其他配置。请创建这个文件，并保证里面至少包含以下内容：

```
GITHUB_CLIENT_ID=your_github_client_id
GITHUB_CLIENT_SECRET=your_github_client_secret
```

我们还要用到的其他环境变量有：

* `PORT`：服务器运行的端口，默认值是 `3000`。
* `ORIGIN`：你的域名，默认值是 `http://localhost:3000/`。我们也可以在这里指定端口。
* `DATABASE_URL`：Cockroach 数据库的地址。默认值是 `postgresql://root@127.0.0.1:26257/messenger?sslmode=disable`。
* `HASH_KEY`：用于为 cookie 签名的密钥。没错，我们会使用已签名的 cookie 来确保安全。
* `JWT_KEY`：用于签署 JSON <ruby> 网络令牌 <rt>  Web Token </rt></ruby>的密钥。

因为代码中已经设定了默认值，所以你也不用把它们写到 `.env` 文件中。

在读取配置并连接到数据库之后，我们会创建一个 OAuth 配置。我们会使用 `ORIGIN` 信息来构建回调 URL（就和我们在 GitHub 页面上注册的一样）。我们的数据范围设置为 “read:user”。这会允许我们读取公开的用户信息，这里我们只需要他的用户名和头像就够了。然后我们会初始化 cookie 和 JWT 签名器。定义一些端点并启动服务器。

在实现 HTTP 处理程序之前，让我们编写一些函数来发送 HTTP 响应。

```
func respond(w http.ResponseWriter, v interface{}, statusCode int) {
    b, err := json.Marshal(v)
    if err != nil {
        respondError(w, fmt.Errorf("could not marshal response: %v", err))
        return
    }
    w.Header().Set("Content-Type", "application/json; charset=utf-8")
    w.WriteHeader(statusCode)
    w.Write(b)
}

func respondError(w http.ResponseWriter, err error) {
    log.Println(err)
    http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
```

第一个函数用来发送 JSON，而第二个将错误记录到控制台并返回一个 `500 Internal Server Error` 错误信息。

### OAuth 开始

所以，用户点击写着 “Access with GitHub” 的链接。该链接指向 `/api/oauth/github`，这将会把用户重定向到 github。

```
func githubOAuthStart(w http.ResponseWriter, r *http.Request) {
    state, err := gonanoid.Nanoid()
    if err != nil {
        respondError(w, fmt.Errorf("could not generte state: %v", err))
        return
    }

    stateCookieValue, err := cookieSigner.Encode("state", state)
    if err != nil {
        respondError(w, fmt.Errorf("could not encode state cookie: %v", err))
        return
    }

    http.SetCookie(w, &http.Cookie{
        Name:     "state",
        Value:    stateCookieValue,
        Path:     "/api/oauth/github",
        HttpOnly: true,
    })
    http.Redirect(w, r, githubOAuthConfig.AuthCodeURL(state), http.StatusTemporaryRedirect)
}
```

OAuth2 使用一种机制来防止 CSRF 攻击，因此它需要一个“状态”（`state`）。我们使用 `Nanoid()` 来创建一个随机字符串，并用这个字符串作为状态。我们也把它保存为一个 cookie。

### OAuth 回调

一旦用户授权我们访问他的个人信息，他将会被重定向到这个端点。这个 URL 的查询字符串上将会包含状态（`state`）和授权码（`code`）： `/api/oauth/github/callback?state=&code=`。

```
const jwtLifetime = time.Hour * 24 * 14

type GithubUser struct {
    ID        int     `json:"id"`
    Login     string  `json:"login"`
    AvatarURL *string `json:"avatar_url,omitempty"`
}

type User struct {
    ID        string  `json:"id"`
    Username  string  `json:"username"`
    AvatarURL *string `json:"avatarUrl"`
}

func githubOAuthCallback(w http.ResponseWriter, r *http.Request) {
    stateCookie, err := r.Cookie("state")
    if err != nil {
        http.Error(w, http.StatusText(http.StatusTeapot), http.StatusTeapot)
        return
    }

    http.SetCookie(w, &http.Cookie{
        Name:     "state",
        Value:    "",
        MaxAge:   -1,
        HttpOnly: true,
    })

    var state string
    if err = cookieSigner.Decode("state", stateCookie.Value, &state); err != nil {
        http.Error(w, http.StatusText(http.StatusTeapot), http.StatusTeapot)
        return
    }

    q := r.URL.Query()

    if state != q.Get("state") {
        http.Error(w, http.StatusText(http.StatusTeapot), http.StatusTeapot)
        return
    }

    ctx := r.Context()

    t, err := githubOAuthConfig.Exchange(ctx, q.Get("code"))
    if err != nil {
        respondError(w, fmt.Errorf("could not fetch github token: %v", err))
        return
    }

    client := githubOAuthConfig.Client(ctx, t)
    resp, err := client.Get("https://api.github.com/user")
    if err != nil {
        respondError(w, fmt.Errorf("could not fetch github user: %v", err))
        return
    }

    var githubUser GithubUser
    if err = json.NewDecoder(resp.Body).Decode(&githubUser); err != nil {
        respondError(w, fmt.Errorf("could not decode github user: %v", err))
        return
    }
    defer resp.Body.Close()

    tx, err := db.BeginTx(ctx, nil)
    if err != nil {
        respondError(w, fmt.Errorf("could not begin tx: %v", err))
        return
    }

    var user User
    if err = tx.QueryRowContext(ctx, `
        SELECT id, username, avatar_url FROM users WHERE github_id = $1
    `, githubUser.ID).Scan(&user.ID, &user.Username, &user.AvatarURL); err == sql.ErrNoRows {
        if err = tx.QueryRowContext(ctx, `
            INSERT INTO users (username, avatar_url, github_id) VALUES ($1, $2, $3)
            RETURNING id
        `, githubUser.Login, githubUser.AvatarURL, githubUser.ID).Scan(&user.ID); err != nil {
            respondError(w, fmt.Errorf("could not insert user: %v", err))
            return
        }
        user.Username = githubUser.Login
        user.AvatarURL = githubUser.AvatarURL
    } else if err != nil {
        respondError(w, fmt.Errorf("could not query user by github ID: %v", err))
        return
    }

    if err = tx.Commit(); err != nil {
        respondError(w, fmt.Errorf("could not commit to finish github oauth: %v", err))
        return
    }

    exp := time.Now().Add(jwtLifetime)
    token, err := jwtSigner.Encode(jwt.Claims{
        Subject:    user.ID,
        Expiration: json.Number(strconv.FormatInt(exp.Unix(), 10)),
    })
    if err != nil {
        respondError(w, fmt.Errorf("could not create token: %v", err))
        return
    }

    expiresAt, _ := exp.MarshalText()

    data := make(url.Values)
    data.Set("token", string(token))
    data.Set("expires_at", string(expiresAt))

    http.Redirect(w, r, "/callback?"+data.Encode(), http.StatusTemporaryRedirect)
}
```

首先，我们会尝试使用之前保存的状态对 cookie 进行解码。并将其与查询字符串中的状态进行比较。如果它们不匹配，我们会返回一个 `418 I'm teapot`（未知来源）错误。

接着，我们使用授权码生成一个令牌。这个令牌被用于创建 HTTP 客户端来向 GitHub API 发出请求。所以最终我们会向 `https://api.github.com/user` 发送一个 GET 请求。这个端点将会以 JSON 格式向我们提供当前经过身份验证的用户信息。我们将会解码这些内容，一并获取用户的 ID、登录名（用户名）和头像 URL。

然后我们将会尝试在数据库上找到具有该 GitHub ID 的用户。如果没有找到，就使用该数据创建一个新的。

之后，对于新创建的用户，我们会发出一个将用户 ID 作为主题（`Subject`）的 JSON 网络令牌，并使用该令牌重定向到前端，查询字符串中一并包含该令牌的到期日（`Expiration`）。

这一 Web 应用也会被用在其他帖子，但是重定向的链接会是 `/callback?token=&expires_at=`。在那里，我们将会利用 JavaScript 从 URL 中获取令牌和到期日，并通过 `Authorization` 标头中的令牌以 `Bearer token_here` 的形式对 `/api/auth_user` 进行 GET 请求，来获取已认证的身份用户并将其保存到 localStorage。

### Guard 中间件

为了获取当前已经过身份验证的用户，我们设计了 Guard 中间件。这是因为在接下来的文章中，我们会有很多需要进行身份认证的端点，而中间件将会允许我们共享这一功能。

```
type ContextKey struct {
    Name string
}

var keyAuthUserID = ContextKey{"auth_user_id"}

func guard(handler http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        var token string
        if a := r.Header.Get("Authorization"); strings.HasPrefix(a, "Bearer ") {
            token = a[7:]
        } else if t := r.URL.Query().Get("token"); t != "" {
            token = t
        } else {
            http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
            return
        }

        var claims jwt.Claims
        if err := jwtSigner.Decode([]byte(token), &claims); err != nil {
            http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
            return
        }

        ctx := r.Context()
        ctx = context.WithValue(ctx, keyAuthUserID, claims.Subject)

        handler(w, r.WithContext(ctx))
    }
}
```

首先，我们尝试从 `Authorization` 标头或者是 URL 查询字符串中的 `token` 字段中读取令牌。如果没有找到，我们需要返回 `401 Unauthorized`（未授权）错误。然后我们将会对令牌中的申明进行解码，并使用该主题作为当前已经过身份验证的用户 ID。

现在，我们可以用这一中间件来封装任何需要授权的 `http.handlerFunc`，并且在处理函数的上下文中保有已经过身份验证的用户 ID。

```
var guarded = guard(func(w http.ResponseWriter, r *http.Request) {
    authUserID := r.Context().Value(keyAuthUserID).(string)
})
```

### 获取认证用户

```
func getAuthUser(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()
    authUserID := ctx.Value(keyAuthUserID).(string)

    var user User
    if err := db.QueryRowContext(ctx, `
        SELECT username, avatar_url FROM users WHERE id = $1
    `, authUserID).Scan(&user.Username, &user.AvatarURL); err == sql.ErrNoRows {
        http.Error(w, http.StatusText(http.StatusTeapot), http.StatusTeapot)
        return
    } else if err != nil {
        respondError(w, fmt.Errorf("could not query auth user: %v", err))
        return
    }

    user.ID = authUserID

    respond(w, user, http.StatusOK)
}
```

我们使用 Guard 中间件来获取当前经过身份认证的用户 ID 并查询数据库。

这一部分涵盖了后端的 OAuth 流程。在下一篇帖子中，我们将会看到如何开始与其他用户的对话。

* [源代码](https://github.com/nicolasparada/go-messenger-demo)

---

via: <https://nicolasparada.netlify.com/posts/go-messenger-oauth/>

作者：[Nicolás Parada](https://nicolasparada.netlify.com/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[PsiACE](https://github.com/PsiACE) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


过渡到 nftables

# 过渡到 nftables

<img src="content_image/album_201910_29_085827o8b7rbswjjr7ijsr.jpg" alt="" title="">

>
> 开源世界中的每个主要发行版都在演进，逐渐将 nftables 作为了默认防火墙。换言之，古老的 iptables 现在已经消亡。本文是有关如何构建 nftables 的教程。
>
>
>

当前，有一个与 nftables 兼容的 iptables-nft 后端，但是很快，即使是它也不再提供了。另外，正如 Red Hat 开发人员所指出的那样，有时它可能会错误地转换规则。我们需要知道如何构建自己的 nftables，而不是依赖于 iptables 到 nftables 的转换器。

在 nftables 中，所有地址族都遵循一个规则。与 iptables 不同，nftables 在用户空间中运行，iptables 中的每个模块都运行在内核（空间）中。它很少需要更新内核，并带有一些新功能，例如映射、地址族和字典。

### 地址族

地址族确定要处理的数据包的类型。在 nftables 中有六个地址族，它们是：

* ip
* ipv6
* inet
* arp
* bridge
* netdev

在 nftables 中，ipv4 和 ipv6 协议可以被合并为一个称为 inet 的单一地址族。因此，我们不需要指定两个规则：一个用于 ipv4，另一个用于 ipv6。如果未指定地址族，它将默认为 ip 协议，即 ipv4。我们感兴趣的领域是 inet 地址族，因为大多数家庭用户将使用 ipv4 或 ipv6 协议。

### nftables

典型的 nftables 规则包含三个部分：表、链和规则。

表是链和规则的容器。它们由其地址族和名称来标识。链包含 inet/arp/bridge/netdev 等协议所需的规则，并具有三种类型：过滤器、NAT 和路由。nftables 规则可以从脚本加载，也可以在终端键入，然后另存为规则集。

对于家庭用户，默认链为过滤器。inet 系列包含以下钩子：

* Input
* Output
* Forward
* Pre-routing
* Post-routing

### 使用脚本还是不用？

最大的问题之一是我们是否可以使用防火墙脚本。答案是：这是你自己的选择。这里有一些建议：如果防火墙中有数百条规则，那么最好使用脚本，但是如果你是典型的家庭用户，则可以在终端中键入命令，然后（保存并在重启时）加载规则集。每种选择都有其自身的优缺点。在本文中，我们将在终端中键入它们以构建防火墙。

nftables 使用一个名为 `nft` 的程序来添加、创建、列出、删除和加载规则。确保使用以下命令将 nftables 与 conntrackd 和 netfilter-persistent 软件包一起安装，并删除 iptables：

```
apt-get install nftables conntrackd netfilter-persistent
apt-get purge iptables
```

`nft` 需要以 root 身份运行或使用 `sudo` 运行。使用以下命令分别列出、刷新、删除规则集和加载脚本。

```
nft list ruleset
nft flush ruleset
nft delete table inet filter
/usr/sbin/nft -f /etc/nftables.conf
```

### 输入策略

就像 iptables 一样，防火墙将包含三部分：输入（`input`）、转发（`forward`）和输出（`output`）。在终端中，为输入（`input`）策略键入以下命令。在开始之前，请确保已刷新规则集。我们的默认策略将会删除所有内容。我们将在防火墙中使用 inet 地址族。将以下规则以 root 身份添加或使用 `sudo` 运行：

```
nft add table inet filter
nft add chain inet filter input { type filter hook input priority 0 \; counter \; policy drop \; }
```

你会注意到有一个名为 `priority 0` 的东西。这意味着赋予该规则更高的优先级。挂钩通常赋予负整数，这意味着更高的优先级。每个挂钩都有自己的优先级，过滤器链的优先级为 0。你可以检查 nftables Wiki 页面以查看每个挂钩的优先级。

要了解你计算机中的网络接口，请运行以下命令：

```
ip link show
```

它将显示已安装的网络接口，一个是本地主机、另一个是以太网端口或无线端口。以太网端口的名称如下所示：`enpXsY`，其中 `X` 和 `Y` 是数字，无线端口也是如此。我们必须允许本地主机的流量，并且仅允许从互联网建立的传入连接。

nftables 具有一项称为裁决语句的功能，用于解析规则。裁决语句为 `accept`、`drop`、`queue`、`jump`、`goto`、`continue` 和 `return`。由于这是一个很简单的防火墙，因此我们将使用 `accept` 或 `drop` 处理数据包。

```
nft add rule inet filter input iifname lo accept
nft add rule inet filter input iifname enpXsY ct state new, established, related accept
```

接下来，我们必须添加规则以保护我们免受隐秘扫描。并非所有的隐秘扫描都是恶意的，但大多数都是。我们必须保护网络免受此类扫描。第一组规则列出了要测试的 TCP 标志。在这些标志中，第二组列出了要与第一组匹配的标志。

```
nft add rule inet filter input iifname enpXsY tcp flags \& \(syn\|fin\) == \(syn\|fin\) drop
nft add rule inet filter input iifname enpXsY tcp flags \& \(syn\|rst\) == \(syn\|rst\) drop
nft add rule inet filter input iifname enpXsY tcp flags \& \(fin\|rst\) == \(fin\|rst\) drop
nft add rule inet filter input iifname enpXsY tcp flags \& \(ack\|fin\) == fin drop
nft add rule inet filter input iifname enpXsY tcp flags \& \(ack\|psh\) == psh drop
nft add rule inet filter input iifname enpXsY tcp flags \& \(ack\|urg\) == urg drop
```

记住，我们在终端中键入这些命令。因此，我们必须在一些特殊字符之前添加一个反斜杠，以确保终端能够正确解释该斜杠。如果你使用的是脚本，则不需要这样做。

### 关于 ICMP 的警告

互联网控制消息协议（ICMP）是一种诊断工具，因此不应完全丢弃该流量。完全阻止 ICMP 的任何尝试都是不明智的，因为它还会导致停止向我们提供错误消息。仅启用最重要的控制消息，例如回声请求、回声应答、目的地不可达和超时等消息，并拒绝其余消息。回声请求和回声应答是 `ping` 的一部分。在输入策略中，我们仅允许回声应答、而在输出策略中，我们仅允许回声请求。

```
nft add rule inet filter input iifname enpXsY icmp type { echo-reply, destination-unreachable, time-exceeded } limit rate 1/second accept
nft add rule inet filter input iifname enpXsY ip protocol icmp drop
```

最后，我们记录并丢弃所有无效数据包。

```
nft add rule inet filter input iifname enpXsY ct state invalid log flags all level info prefix \”Invalid-Input: \”
nft add rule inet filter input iifname enpXsY ct state invalid drop
```

### 转发和输出策略

在转发和输出策略中，默认情况下我们将丢弃数据包，仅接受已建立连接的数据包。

```
nft add chain inet filter forward { type filter hook forward priority 0 \; counter \; policy drop \; }
nft add rule inet filter forward ct state established, related accept
nft add rule inet filter forward ct state invalid drop
nft add chain inet filter output { type filter hook output priority 0 \; counter \; policy drop \; }
```

典型的桌面用户只需要端口 80 和 443 即可访问互联网。最后，允许可接受的 ICMP 协议并在记录无效数据包时丢弃它们。

```
nft add rule inet filter output oifname enpXsY tcp dport { 80, 443 } ct state established accept
nft add rule inet filter output oifname enpXsY icmp type { echo-request, destination-unreachable, time-exceeded } limit rate 1/second accept
nft add rule inet filter output oifname enpXsY ip protocol icmp drop
nft add rule inet filter output oifname enpXsY ct state invalid log flags all level info prefix \”Invalid-Output: \”
nft add rule inet filter output oifname enpXsY ct state invalid drop
```

现在我们必须保存我们的规则集，否则重新启动时它将丢失。为此，请运行以下命令：

```
sudo nft list ruleset. > /etc/nftables.conf
```

我们须在引导时加载 nftables，以下将在 systemd 中启用 nftables 服务：

```
sudo systemctl enable nftables
```

接下来，编辑 nftables 单元文件以删除 `Execstop` 选项，以避免在每次引导时刷新规则集。该文件通常位于 `/etc/systemd/system/sysinit.target.wants/nftables.service`。现在重新启动nftables：

```
sudo systemctl restart nftables
```

### 在 rsyslog 中记录日志

当你记录丢弃的数据包时，它们直接进入 syslog，这使得读取该日志文件非常困难。最好将防火墙日志重定向到单独的文件。在 `/var/log` 目录中创建一个名为 `nftables` 的目录，并在其中创建两个名为 `input.log` 和 `output.log` 的文件，分别存储输入和输出日志。确保系统中已安装 rsyslog。现在转到 `/etc/rsyslog.d` 并创建一个名为 `nftables.conf` 的文件，其内容如下：

```
:msg,regex,”Invalid-Input: “ -/var/log/nftables/Input.log
:msg,regex,”Invalid-Output: “ -/var/log/nftables/Output.log & stop
```

现在，我们必须确保日志是可管理的。为此，使用以下代码在 `/etc/logrotate.d` 中创建另一个名为 `nftables` 的文件：

```
/var/log/nftables/* { rotate 5 daily maxsize 50M missingok notifempty delaycompress compress postrotate invoke-rc.d rsyslog rotate > /dev/null endscript }
```

重新启动 nftables。现在，你可以检查你的规则集。如果你觉得在终端中键入每个命令很麻烦，则可以使用脚本来加载 nftables 防火墙。我希望本文对保护你的系统有用。

---

via: <https://opensourceforu.com/2019/10/transition-to-nftables/>

作者：[Vijay Marcel D](https://opensourceforu.com/author/vijay-marcel/) 选题：[lujun9972](https://github.com/lujun9972) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出