用微软的Hyper-V编译并安装OpenWRT

由于一些众所周知的原因,国内访问很多海外网站会出现访问速度缓慢甚至打不开的问题,其中不乏一些日常经常使用的例如搜索、邮箱等应用,虽然目前有很多解决方案来解决电脑、手机上面的网络加速问题,但是每个终端都要单独适配非常麻烦,因此直接在路由器端进行配置,让所有连入路由器的设备都可以无感加速,成为了一个相对『完美』的解决方案,因此我在家里的Gen8上面,用Hyper-V虚拟了一个OpenWRT的软路由来提供此类服务。

Hyper-V由于本身不开源,因此OpenWRT的官方编译版本在很早的时候就移除了针对Hyper-V的支持,因此为了让OpenWRT可以完美的运行在Hyper-V上,我们需要自行编译相关驱动组件。当然网上也有很多大神已经编译好的固件,但是大都加入了一些我自己不太使用的功能,作为一个洁癖,当然是自己编译一个了。
    
参考了Chiphell大神tedaz的文章非官方编译,Hyper-V的Openwrt x86和x86-64版,Chaos Calmer正式版后,修改整理如下。

 准备工作

首先你需要准备一个Linux的编译环境,建议选择Ubuntu这类比较流行的Linux发行版本,并参考OpenWRT官方的OpenWRT编译系统-安装,安装好编译所需要的一些依赖包

以Ubuntu 64bit为例,需要执行下面的命令来安装相关的依赖

1
sudo apt-get install build-essential subversion libncurses5-dev zlib1g-dev gawk gcc-multilib flex git-core gettext libssl-dev

 

 修改源码

首先从OpenWRT的官网Git下载OpenWRT的源码,建议下载15.05分支的,Trunk分支下的代码通常不是非常稳定。

1
git clone git://git.openwrt.org/15.05/openwrt.git

 

更新OpenWRT所有的feeds,以便你在后续可以提前把需要的ipk(例如luci、shadowsocks的一些依赖等)打包到固件中

1
2
3
cd openwrt
./scripts/feeds update -a
./scripts/feeds install -a

 

下载完成后,需要针对源码进行如下的修改。

修改/package/kernel/linux/modules/virtual.mk,在最后加入下面的内容,修改内核,添加让OpenWRT内核支持Hyper-V

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#
# Hyper-V Drives depends on x86 or x86_64.
#
define KernelPackage/hyperv-balloon
SUBMENU:=$(VIRTUAL_MENU)
DEPENDS:=@(TARGET_x86||TARGET_x86_64)
TITLE:=Microsoft Hyper-V Balloon Driver
KCONFIG:= \
CONFIG_HYPERV_BALLOON \
CONFIG_HYPERVISOR_GUEST=y \
CONFIG_PARAVIRT=n \
CONFIG_HYPERV=y
FILES:=$(LINUX_DIR)/drivers/hv/hv_balloon.ko
AUTOLOAD:=$(call AutoLoad,06,hv_balloon)
endef

define KernelPackage/hyperv-balloon/description
Microsofot Hyper-V balloon driver.
endef

$(eval $(call KernelPackage,hyperv-balloon))

define KernelPackage/hyperv-net-vsc
SUBMENU:=$(VIRTUAL_MENU)
DEPENDS:=@(TARGET_x86||TARGET_x86_64)
TITLE:=Microsoft Hyper-V Network Driver
KCONFIG:= \
CONFIG_HYPERV_NET \
CONFIG_HYPERVISOR_GUEST=y \
CONFIG_PARAVIRT=n \
CONFIG_HYPERV=y
FILES:=$(LINUX_DIR)/drivers/net/hyperv/hv_netvsc.ko
AUTOLOAD:=$(call AutoLoad,35,hv_netvsc)
endef

define KernelPackage/hyperv-net-vsc/description
Microsoft Hyper-V Network Driver
endef

$(eval $(call KernelPackage,hyperv-net-vsc))

define KernelPackage/hyperv-util
SUBMENU:=$(VIRTUAL_MENU)
DEPENDS:=@(TARGET_x86||TARGET_x86_64)
TITLE:=Microsoft Hyper-V Utility Driver
KCONFIG:= \
CONFIG_HYPERV_UTILS \
CONFIG_HYPERVISOR_GUEST=y \
CONFIG_PARAVIRT=n \
CONFIG_HYPERV=y
FILES:=$(LINUX_DIR)/drivers/hv/hv_util.ko
AUTOLOAD:=$(call AutoLoad,10,hv_util)
endef

define KernelPackage/hyperv-util/description
Microsoft Hyper-V Utility Driver
endef

$(eval $(call KernelPackage,hyperv-util))

#
# Hyper-V Storage Drive needs to be in kernel rather than module to load the root fs.
#
define KernelPackage/hyperv-storage
SUBMENU:=$(VIRTUAL_MENU)
DEPENDS:=@(TARGET_x86||TARGET_x86_64) +kmod-scsi-core
TITLE:=Microsoft Hyper-V Storage Driver
KCONFIG:= \
CONFIG_HYPERV_STORAGE=y \
CONFIG_HYPERVISOR_GUEST=y \
CONFIG_PARAVIRT=n \
CONFIG_HYPERV=y
FILES:=$(LINUX_DIR)/drivers/scsi/hv_storvsc.ko
AUTOLOAD:=$(call AutoLoad,40,hv_storvsc)
endef

define KernelPackage/hyperv-storage/description
Microsoft Hyper-V Storage Driver
endef

$(eval $(call KernelPackage,hyperv-storage))</code></pre>

 

修改/target/linux/x86/64/config-default和/target/linux/x86/config-3.18,均添加以下内容(这里后面如果内核有跟心,就修改对应的config-内核版本号即可)

1
2
3
4
5
6
7
8
# CONFIG_HYPERV is not set
# CONFIG_HYPERV_BALLOON is not set
# CONFIG_HYPERV_NET is not set
# CONFIG_HYPERV_STORAGE is not set
# CONFIG_HYPERV_UTILS is not set
# CONFIG_FB_HYPERV is not set
# CONFIG_HID_HYPERV_MOUSE is not set
# CONFIG_HYPERV_KEYBOARD is not set

至此,修改完成。网上还有很多人建议修改Build相关的部分,考虑到后面我们统一使用其他工具来把img转成vhd,因此这一步我们直接生成img即可,不用修改Build相关的内容。

 配置编译参数

OpenWRT有GUI的配置工具,因此我们回到OpenWRT源码主目录,执行make menuconfig,选择Target System为x86,Subtarget有x86_64,以便支持多核心大内存的使用。但是如果你需要在OpenWRT上使用XWare这类只有32位的工具,那就只能编译32位的包了。

f:id:briteming:20180718164729p:plain

f:id:briteming:20180718164906p:plain

然后在Kernel modules里的Virtualization Support里勾选上4个Hyper-V相关的。

https://archive.is/wdj36/71dd3fa1e531c5f921b4590a12c0e8416165ccc0

 

最后取消Kernel modules里的Network Devices中其他网卡即可。
当然,如果你希望提前编译类似于shadowsocks之类的工具,可以参考Shadowsocks OpenWRT上的说明,下载相应的package并在配置编译参数时选中这个包即可,相关依赖会自动选中。

 开始编译

使用命令make可以进行编译,make V=s可以让它输出调试信息,带上-j1参数表示用1个核心编译。建议你选择一台4核心以上的机器来进行编译以免等待的时间太久,由于编译过程中需要下载很多内容,因此流畅的网络(能自动翻墙是最好的)会大大加快你的编译速度。

我之前使用了E3-1230V2的CPU,用了6个核心编译,首次编译大概花了接近1小时的事件才全部编译完成。

 转换格式,大功告成

编译完成后,在BIN目录下,可以找到编译完成的img文件(一般是压缩包),解压后,我们使用下面的命令来转化为Hyper-V可用的vhd格式。

1
qemu-img convert -f raw -O vpc openwrt-x86-generic-combined-ext4.img openwrt-x86-generic-combined-ext4.vhd

 

然后直接在Hyper-V下面使用就一切OK啦。

from 

http://archive.is/wdj36

使用DNS-Forwarder提升ChinaDNS稳定性

很多人使用Openwrt的路由器配合SS来提升访问海外网站的速度和稳定性,其中ChinaDNS被很多人作为防DNS污染的利器,但是大多数人目前还是使用ss-tunnel创建一个UDP转发隧道作为ChinaDNS的上游来进行查询,这在不少地区的ISP环境下常常出现不稳定的现象,aa65535的DNS-Forwarder for OpenWrt很好的解决了这个问题,参考Wiki整理如下:

 安装DNS-Forwarder for OpenWrt

从v1.1.0版本开始,DNS-Forwarder for OpenWrt把Luci设置界面分离到了OpenWRT-Dist-Luci,因此根据你的平台不同,你需要分别下载两个程序:

下载完成后,在路由器上执行opkg install安装即可

 配置DNS-Forwarder和ChinaDNS

DNS-Forwarder配置比较简单,只需要设置监听端口和上游DNS即可,这里我设置了监听端口为5352

然后配置ChinaDNS的上游服务器,把原先使用UDP隧道转发的上游去掉,改为DNS-Forwarder的地址,这里是127.0.0.1:5352,另一个上游服务器保持不变,可以是114.114.114.114这种公共DNS,也可以是你的ISP的DNS

 完成

设置完成,如果你第一次使用ChinaDNS的话,需要在网络-DHCP/DNS中,把ChinaDNS配置为本地的DNS服务器,如下图所示,如果之前已经配置了,那这一步就可以忽略了。

在这个模式下,ChinaDNS使用DNS-Forwarder作为上游,而DNS-Forwarder又使用TCP协议直接向上游(通常是8.8.8.8)服务器进行查询,如果配合了SS,TCP查询会自动走SS线路,也同时解决了线路优化的问题.

Linux服务器的IP配置攻略

在Linux 网络设备在配置时被赋予别名,该别名由一个描述性的缩略词和一个编号组成。某种类型的第一个设备的编号为 0,其他设备依次被编号为 1、2、3,等。但是网卡并不是作为裸设备出现在/dev目录下,而是存在内存中。eth0, eth1是以太网卡接口。它们用于大多数的以太网卡,包括许多并行端口以太网卡。本文主要讨论这类网卡。 为Linux以太网卡设定IP地址的方式非常灵活,你可以选择适合你工作情况的方法:
1. 使用ifconfig命令

ifconfig命令是最重要的Linux网络命令之一,最主要的用途是设定、修改网卡的IP地址,修改网卡ip地址:
# ifconfig eth0  192.168.149.129 netmask 255.255.255.0
默认情况下, ifconfig 显示活动的网络设备。给这个命令添加一个 -a 开关就能看到所有设备。但是ifconfig命令设置网络设备的ip地址系统重新启动后设置会自动失效。所以它主要用于网卡状态调试。假设您要建立一个临 时的网络配置以供测试。您可以使用发行版本中的工具来编辑配置,但是需要注意在完成测试之后,将所有设置恢复回去。通过使用 ifconfig ,我们无需影响已保存的设置,就能够快速地配置网卡。
查看指定网卡的接口状态:
#ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 00:0C:29:F6:9B:27
inet addr:192.168.149.129  Bcast:192.168.149.255  Mask:255.255.255.0
inet6 addr: fe80::20c:29ff:fef6:9b27/64 Scope:Link
UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
RX packets:120 errors:0 dropped:0 overruns:0 frame:0
TX packets:116 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:12600 (12.3 KiB)  TX bytes:12705 (12.4 KiB)
Interrupt:10 Base address:0x1424
可以看到修改后 的ip地址生效。小贴式:ifconfig修改的ip地址,在服务器重新启动后会失效。
2. 使用ip命令
ip命令是iproute2软件包里面的一个强大的网络配置工具,它能够替代一些传统的网络管理工具。例如:ifconfig、route等。 现在,绝大多数 Linux 发行版和绝大多数 UNIX都使用古老的arp, ifconfig和route命令。虽然这些工具能够工作,但它们在Linux2.2和更高版本的内核上显得有一些落伍。使用iproute2前你应该确 认已经安装了这个工具。这个包的名字在RedHat Linux 9.0叫作“iproute2”,也可以在:ftp://ftp.inr.ac.ru/ip-routing/ 下载源代码安装。如果希望在以太网接口eth0上增加一个地址10.0.0.1,掩码长度为24位,标准广播地址,标签为eth0:Alias:
#ip addr add 10.0.0.1/24 brd + dev eth0 label eth0:Alias
3. 使用 netconfig命令
netconfig命令可以设置网络设备的ip地址,netconfig命令可以永久保存设置。
使用方法是:“netconfig ethX”。使用命令“netconfig eth0”后会在命令行下弹出一个对话框进行确认,选择“是”,如图1 。
图1 是否进行联网配置对话框
这时即可进行设定见图2
图2 netconfig配置界面
设定结束后用“tab”键选择“OK”即可保存设置并且退出。然后使用命令激活即可生效:
#service network restart
或者使用等价命令组(先禁用后启用):
#ifdown eht0
#ifup eth0
小贴士:netconfig命令修改的ip地址,在服务器重新启动后不会失效。
4. 使用neat命令

使用neat命令需要配置好X window系统,在命令行下运行“neat”命令后添加IP地址和其他相关参数后保存设置,从新启动网络和网络服务或计算机,见图3。
图3 图形界面添加IP地址
另外neat命令还有一个同价命令:“redhat-config-network”,二者完全相同。Neat和redhat-config-config命令可以永久保存设置。
5. 修改TCP/IP网络配置文件
除非另行指定,Red Hat Linux 系统中大多数配置文件都在 /etc 目录中。网卡相关的TCP/IP网络配置文件是:/etc/sysconfig/network-scripts/ifcfg-ethx。其中x从0开 始,第一个以太网配置文件即:/etc/sysconfig/network-scripts/ifcfg-eth0。使用vi编辑器修改这个文件,也可 以修改网卡IP地址。比如文件:ifcfg-eth0  代表是以太网实际网卡0的配置文件,比如文件:ifcfg-eth0:1  代表是以太网实际网卡0的配置文件。
#vi /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0        #设定网卡的名称,要跟文件名称对应 #
ONBOOT=yes        #是否在开机的的时候启动网卡#
BOOTPROTO=static      #启动的时候的 IP 取得的协议,这里是固定的,
如果是动态主机的话,要改成 dhcp 才行#
IPADDR=192.168.1.2     #IP 地址#
NETMASK=255.255.255.0   #子网掩吗#
NETWORK=192.168.1.0    #该网段的第一个 IP#
BROADCAST=192.168.1.255  #最后一个同网段的广播地址#
GATEWAY=192.168.1.2    #网关地址#
#GATEWAYDEV=eth0
存盘后使用命令:“service network restart “激活即可生效。这个方法同样可以永久保存设置。
6. 在一个网卡上配置多个ip地址
有时候想要在网卡上配置多个ip地址,同样可以使用ifconfig命令完成:
ifconfig eth0:1 192.168.149.122 netmask 255.255.255.0
ifconfig eth0:2 192.168.149.123 netmask 255.255.255.0
以上是对网卡eth0修改了2个IP地址的方法,重新使用ipconfig命令查看,可以看到一个网卡上已经配置多个ip地址。如图4。
图4在一个网卡上配置多个ip地址
7. 无线网卡在Linux下的ip配置全攻略
随着Linux网络技术的快速增长,硬件厂商大大加速对硬件产品对Linux的技术支持。使得Linux 支持的无线网卡的数量在过去的一两年里增长显著。对于有些设备,配置无线连接非常简单:只要在您使用的 Linux 发行版本,插入无线网卡,在设置过程中单击鼠标,并输入正确的联网参数就可以了。设置无线网络相关步骤如下:
(1)用“iwconfig”命令来显示无线网卡(eth0、eth1)的信息。在以下的步骤中,用ethX表示无线网卡的名称。
(2)设置无线网卡的操作模式为Managed:
#iwconfig ethX mode Managed
(3)如果采用了WEP加密,需要设置WEP密码:
#iwconfig ethX key password XXXXXX
对应40位和128位加密,password分别为6位和10位的十六进制数字。
(4)设置SSID,其中ESSID为无线接入(Access Point)的SSID。
# iwconfig ethX essid ESSID
(5)启动无线网卡:
#ifconfig ethX up
总结:
可以看到Linux 下网卡的ip设置的方法比较灵活,不同的命令可以完成相同的任务。注意由于ip地址是Linux服务器的关键参数所以完成以上操作要有管理员权限。
from archive.is/13svf
--------------------------
 
Linux服务器如何一个网卡绑定多个IP
 

系统:Redhat9
目的:为一个以太网卡配置多个ip地址

    linux操作系统中配置网络接口,一般是通过网络配置工具实现,但实质是修改与网络相关的配置文件起作用,故可直接修改文件达到目的。
   由于发行版本不同,其有自己专用的配置工具,但也有一些通用的配置工具,如ifconfig,ifup,ifdown
查看
#ifconfig      //当前网络接口情况
#ifconfig -a   //主机所有网络接口情况
#ifconfig eth0 //eth0接口情况

配置
1.ifconfig
格式:ifconfig 网络设备 ip地址 hw MAC地址 netwask broadcase地址 … …
(详见ifconfig –help)
例如:#ifconfig eth0 192.168.1.41 broadcase 192.168.1.255 netmask 255.255.255.0
注:用ifconfig为网卡指定ip只是用来调试网络用,并不会更改系统网卡相关的配置文件。

2.rh9网络配置工具netconfig
格式:netconfig -d 网络设备 –ip –hwaddr –netmask ……
(详见netconfig –help)
例如:#netconfig -d eht0 –ip=192.168.1.41 –netmask=255.255.255.0
注:直接修改网络接口相关的配置文件

3.修改网络配置文件
rh中与以太网相关的配置文件位于:/etc/sysconfig/network-scripts目录下,如ifcfg-eth0。可以增加配置文件如ifcfg-eth1来增加新的网络接口。
激活和终止
格式:ifconfig 网络设备 up/down
     或ifup/ifdown 网络设备
例如:#ifconfig eth0 up
虚拟网络接口
即:为一个网卡配置多个ip
与一般配置基本相同,只是一般网络接口是eth0,eth1,…,而虚拟网络接口为:(以eth0为例)eth0:0,eth0:1,eth0:2,…
另外需要注意的是在设置虚拟接口时,每个接口都有不同的物理地址
例如:#netconfig -d eth0:0 –ip=192.168.1.42 –hwaddr=00:11:00:00:B1:05 –netmask=255.255.255.0 –broadcase=192.168.1.255
(eth0:hw是00:11:00:00:B1:04)

在Linux服务器上开启安全的SNMP代理

我们已经介绍了监控宝的服务器监控,http://blog.jiankongbao.com/?p=133,那么如何在监控宝中创建和使用它呢?这篇文章中会有详细的介绍。

我们知道,监控宝使用标准的SNMP协议来为用户提供服务器监控功能,这意味着被监控的服务器上必须运行SNMP代理程序(snmpd),接下来我们将详细介绍如何在Linux服务器上安装、开启Snmp代理,并且进行必要的安全配置,随后我们会在其它文章中介绍Windows服务器的配置方法。

关于SNMP协议的定义和描述,可以浏览RFC1157,这里还描述了SNMP的设计动机和原理,到目前为止,SNMP已经被无数的设备用来作为监控协议,并且工作得非常可靠,至于安全性和必要的安全配置,我们在随后会详细介绍,你完全可以让SNMP代理程序在你的服务器上安全的运行。

在各种Linux分发版中,大多数都已经默认集成了snmpd,比如在suse10中,你可以这样开启snmpd:

suse10:~ # /etc/init.d/snmpd start

如果没有默认安装,你要做的就是自己来编译snmpd,按照下边的步骤,非常简单。

编译、安装以及安全设置

对于Linux平台,我们推荐使用Net-SNMP,它实现了标准的SNMP协议,并且包括了代理程序以及各种SNMP工具。

http://net-snmp.sourceforge.net/

关于Net-SNMP编译、安装、安全设置以及授权IP地址等的详细介绍,请参考我们的Wiki,如下:

http://wiki.jiankongbao.com/doku.php/文档:安全指引#linux_snmp

在监控宝中添加服务器

现在我们来看看如何在监控宝中添加服务器监控,通过新增的导航,你会很容易进入添加服务器的页面,这里分为两部分,首先是填写服务器信息,包括服务器名称和IP地址,以及操作系统类型。
这些都非常简单,之所以要选择操作系统类型,是因为Linux和Windows的机制存在一些差异,所以它们的SNMP信息库会有一些不同,我们需要根据不同的系统来提供相应的监控项目。
72
接下来是SNMP设置,主要是选择不同的SNMP协议,以及填写身份验证信息,前边已经有过详细的介绍,这里你只需要将前边配置的信息如实填写即可。
74
73
填写完后,点击提交按钮,这时候监控宝会使用这些信息来尝试连接你的SNMP监控代理,需要一些时间,这一步非常重要。
76
不妙,如果你填写的信息无法连接到服务器的SNMP代理程序,监控宝会提示你,这时候,请你仔细检查之前介绍的那些配置,以及防火墙策略,也许你没有开放udp161端口。
75
找到问题后再次提交,如果连接成功,则会看到成功页面,如下:
78
然后点击“添加监控项目”,便可以为这台服务器选择监控项目,对于Linux服务器,监控宝目前提供了以下的监控项目,你可以全部勾选,然后提交。
77
然后就等待数据和图表吧,一切就这么简单,如果有疑问或需要帮助,请发邮件到 help (at) jiankongbao.com
 

玩转 Windows 10 中的 Linux 子系统

在今年的 Build 2016 上,微软向全世界介绍了他们还处于 Beta 阶段的 Windows 下的 Linux 子系统(Windows Subsystem for Linux)(WSL),它可以让开发者们在 Windows 10 下通过 Bash shell 运行原生的 Ubuntu 用户态二进制程序。如果你参与了 Windows Insider 计划,你就可以在最新的 Windows 10 年度升级版的 Insider 构建版中体验这个功能了。
Web 开发人员们不用再苦恼所用的 Windows 开发平台上没有合适的 Linux 工具和库了。WSL 是由 Windows 内核团队与 Canonical 合作设计和开发的,可以让 Windows 10 下的开发者们在拥有 Windows 中那些强力支持之外,还能使用 Linux 下丰富的开发环境与工具,而不用启动到另外的操作系统或者使用虚拟机。这绝对是一个“来自开发者,服务开发者”的 Windows 10 特色,它的目的是让开发者们每天的开发工作都变得顺畅而便捷。
在本文中,我会展示给你一些我认为非常有趣的功能,以及告诉你一些可以让你找到更多信息的资源。首先,我会展示 WSL 所集成的那些主要命令(比如 ssh)是如何操作服务器和设备的。其次,我会演示使用 Bash 脚本是如何以简明的方式来自动化执行任务的。其三,我会利用极棒的命令行编译器、一些其它工具以及对 *nix 兼容的能力来玩一个轻量级的古典黑客级游戏: NetHack。最后,我会展示如何使用已有的 Python 脚本和其它来自网上的脚本。
从我的第一台 286 上运行的 Windows 3.0 开始,Windows 就一直是我的主要操作系统和开发环境。不过,我身边也有很多 Linux 服务器和设备。从树莓派和路由器/网关设备这样的物联网设备,到 Minecraft 服务器,它们堆满了我的办公室的每个角落。而我经常要从我的主工作站中去管理和配置这些 Linux 计算机。

管理服务器和设备

我在我的家中运行着一台无显示器的 Ubuntu Minecraft 服务器,这是我去年给我十岁大的儿子的圣诞礼物,但是它已经变成了我的玩具而不是他的(好吧,主要是我的玩具)。我以前在我的 Windows 10 电脑上使用几个客户端来管理它,不过我现在想使用 Windows 中的 Bash 里面的 ssh 命令行来管理它。使用类似 PuTTY 或来自 Cygwin 的 Tera Term 这样的应用当然也可以,但是我想试试真正原生而自然的体验也是一种不错的选择。Cygwin 就像是在披萨店订购的披萨一样,好吃,但是没有那种氛围。
我已经使用 WSL 中的 ssh-keygen 和 ssh-copy-id 设置好了公私密钥对,所以使用 ssh 只需要如下简单输入即可:
$ ssh <username>@<server>
我还为此创建了一个别名,以便更快一些。这是一个标准的 Linux/Bash 功能:
$ alias mc='ssh <user>@<server>'
现在,我要访问我的 Minecraft 服务器只需要在 Windows 10 下的 Bash 中输入“mc”即可。
当然,同样的方法你也可以用于任何 Linux 上的 Web 或数据库服务器上,甚至树莓派或其它的物联网设备也可以。
在终端里面进行 ssh 只是为了方便而已,不过当你在 shell 中工作时,如果还有类似 apt、node、Ruby、Python 等等工具时,你就有了自动化各种工作的可能。

远程脚本

假如说你有一大堆 Linux 服务器和设备,而你要在它们上面执行一个远程命令的话,如果已经配置好公私密钥对,你就可以在 Bash 中直接远程执行命令。
举个例子说,想知道远程服务器自从上次重启后已经运行了多长时间了,你只需要输入:
$ ssh <user>@<server> 'last -x|grep reboot'
ssh 会连接到该服务器并执行 last -x 命令,然后搜索包含“reboot”的一行。我在我的 Ubuntu Minecraft 服务器上运行的结果如下:
reboot   system boot  4.4.0-28-generic Thu Jul  7 08:14   still running
这只是一台服务器,如果你有许多服务器的话,你可以自动化这个过程。我在 WSL 里我的主目录下创建了一个名为 servers.txt 的文件,它包含了一系列 Linux 服务器/设备的名称,每个一行。然后我就可以创建一个脚本来读取这个文件。
在使用了很多年像树莓派这样的设备之后,我已经变成了一个 nano 人(在 VMS 上我是一个 LSEdit 人),下面是我用我喜爱的 nano 编辑器打开的脚本。
当然,你也可以使用 vim 、 emacs 或者其它可以用在 Ubuntu 终端上的编辑器。
该脚本是 Bash 脚本,要执行该脚本,输入:
$ ./foreachserver.sh 'last -x|grep reboot'
它将迭代输出文件中的每个服务器/设备,然后通过 ssh 远程执行该命令。当然,这个例子非常简单,但是你可以像这样把你的本地脚本或其它命令变成远程的。Bash 脚本语言足够丰富,所以你可以使用它来完成你的大多数远程管理任务。你可以用你下载到 WSL 或远程系统中的其它应用来扩展它的使用。
你是否需要在工作中把本地的 Windows 文件或资源用于其它的 Linux 计算机吗?或者,你根本不使用 Linux ?Bash 可以操作本地的 Windows 文件或资源,还是说它就是一个完全独立的环境?

使用 Windows 文件

WSL 系统可以通过 /mnt// 目录(挂载点)来访问你计算机上的文件系统。举个例子,你的 Windows 上的 C: 和 D: 根目录可以在 WSL 中相应地通过 /mnt/c 和 /mnt/d 访问。当你要把你的 Windows 下的项目文件、下载的内容和其它文件用到 Linux/Bash 之中时这很有用。
上图显示的两个目录分别对应于我的计算机上的 SSD 和硬盘:
这是逻辑挂载,所以当你在 shell 中使用类似 mount 这样的命令时它们不会显示。但是它们可以如你预期的那样工作。举个例子,在 Windows 中,我在我的 C 盘根目录下放了一个名为 test.txt 的文件,我可以在 WSL 中如下访问它:
在 Build  Tour 大会期间,我们要确保所有的演示都可以在没有互联网时也能正常工作(你绝不会知道会场的网络是什么样子的) ,所以为了让 Bash/WSL 可以演示 Git 操作,该演示访问的是本地计算机上的 Windows 文件,我在 Windows 上的 C:gitNetHack 下设置一个本地仓库。 要在 WSL 中进行 clone 操作,我执行了如下命令:
$ git –clone file:///mnt/c/git/NetHack
该命令告诉 git 使用 file:// 协议,并 clone 了位于 /mnt/c/git/NetHack 下的仓库。你可以以类似的方式来访问你的 Windows 下的所有文件。
警示:就像在其它终端中一样,如果你不小心的话,你可以在 Bash 中修改/删除 Windows 文件系统中的文件。举个例子,你可以像下面这样来干掉你的 Windows ,假如你有合适的权限的话。
$ rm -rf /mnt/c/  [千万别试!][千万别试!][千万别试!]
我之所以郑重提醒是因为我们很多人都是刚刚接触 Linux 命令,它们不是 Windows 命令。
这种可以让文件系统集成到一起的魔法来自 DrvFs。如果你希望了解该文件系统的更多细节,以及它是如何工作在 WSL 中的,WSL 团队为此写了一篇详细的文章
当然, 文件系统访问只是 WSL 其中的一部分功能而已,许多开发任务还需要通过 HTTP 或其它网络协议访问远程资源。

发起 HTTP 请求

从脚本或命令行而不是从一个编译好的程序或 Web 页面上发起 REST 或其它 HTTP(或 FTP)请求是很有用的。就像在大多数 Linux 发行版一样,WSL 也包括了类似 curl 或 wget 获取资源这样的标准功能,它们可以用来发起 HTTP 或者其它网络请求。举个例子,下面是使用 curl 对 Github 发起 REST 请求来获取我个人的属性信息:
$ curl -i https://api.github.com/users/Psychlist1972
HTTP/1.1 200 OK
Server: GitHub.com
Date: Wed, 13 Jul 2016 02:38:08 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 1319
Status: 200 OK
...
{
  "login": "Psychlist1972",
  "avatar_url": "https://avatars.githubusercontent.com/u/1421146?v=3",
  "url": "https://api.github.com/users/Psychlist1972",
  "name": "Pete Brown",
  "company": "Microsoft",
   ...
}
$
你可以用它和 Bash 脚本来创建一个 REST API 的快速测试客户端,也可以用来探测一个 Web 页面或服务器并报告其返回的状态。它用来从网上下载文件也很棒,你可以简单地重定向输出到一个文件而不是在屏幕上显示它:
$ curl -i https://api.github.com/users/Psychlist1972 > pete.json
我也是一个 PowerShell 用户,甚至还使用 Windows 10 MIDI in PowerShell 创建了一些有趣的扩展,也修复过出现在特定的录音硬件设备上的一些文件问题。作为长时间的 .NET 开发者和爱好者,我经常使用和扩展 PowerShell 以满足我的项目需求。但是  PowerShell 并不是一个可以运行所有的那些 Bash 脚本和针对 Linux 的开源工具的地方。我希望以一种最简单、最舒服的方式来完成这些任务,在某种意义上,这意味着我们需要在 Bash 中完成它们。
我已经一掠而过的介绍了 Bash、Bash 脚本以及你可以在 shell 中完成的任务。到目前为止,我谈论的都是有助于开发工作的那些功能。但是在 WSL 中实际的开发和编译工作是怎样的?我在 Build Tour 大会上演示了下面这个部分。

Build Tour 大会上的演示:NetHack

这个夏初,来自微软的讲演者们向大家演示了一些来自 Windows 和微软云上的很酷的开发者新功能。作为其中的一部分,我以一种好玩的方式来演示了 WSL,而且这是一种和开发者们相关的方式。
我个人想要展示使用 git 和一些传统的终端开发工具,我已经写好了 Bash 的演示程序,包括了这些基础的东西(用 Python 和 Ruby 写的“Hello World”),不过我还是想要更有冲击力一些。
我回想起我在大学的时光,那时我们在 Unix(DEC Ultrix 及 SunOS)和 VAX/VMS 之间折腾,Unix 几乎全是命令行环境。在我们学校,绝大多数使用图形工作站的用户只是为了在不同的窗口打开多个终端会话而已,当然,会在桌面背景放上一张超酷的月相图。大部分学生都是使用 VT-220 终端来打开他们的会话(学校离波士顿不远,所以我们有很多 DEC 设备)。
那时,计算机系的学生们主要玩两大游戏:MUD (主要是 lpMUD 和当时刚出的 DikuMUD)和 NetHack。NetHack 和其它的 Roguelikes 类游戏被视为历史上最有影响力的游戏之一,它们是许多现在流行的地牢冒险和角色扮演类游戏的鼻祖。
NetHack 有很长的历史,现在的它包含了来自几十年前的几十万行 *nix 代码,以及后来补充的一些代码。该游戏使用 curses (及其替代品)作为终端交互方式,需要通过 lex、 yacc(或 flex 和 bison)和 cc(或 gcc),以及一堆其它的开发工具构建。
它是由 C 语言编写的,并包括了一些用 Bourne shell 编写的复杂的脚本配置功能。我觉得它是一个体现 WSL 和 Bash on Windows 10 的开发者能力的不错而有趣的方式。由于使用了 curses(在 Linux 和 WSL 中是 libncurses 库),它也可以用来展示 Windows 10 中命令行窗口中的终端模拟能力。
以前,在我们的分时 Ultrix 服务器上从源代码构建 NetHack 要花费掉我们很多时间,而现在我的个人计算机上只需要几分钟就可以搞定。我喜欢这种技术进步。在 Linux 或 WSL 上配置和编译 NetHack 有容易和复杂两种方式。为了节省时间,我们会以容易的方式来进行。

前置需求

首先,更新你的 WSL 环境,确保你的软件是最新的。在安装新的软件包之前,这是一个好的做法。
$ sudo apt update
$ sudo apt upgrade
然后,安装必须的开发工具。最简单的办法就是使用 build-essential 软件包,它包括了 Linux 开发者构建以 C/C++ 开发的软件时所需的绝大部分程序。
$ sudo apt install build-essential
这要花几分钟。如果你想更加深入地了解,你可以分别安装 gcc、gdb、make、flex、bison 以及 NetHack 文档中提到的其它工具。不过如果你是一位开发者,有时候你可能还需要一些其它工具。 build-essential 基本上提供了你所需的工具集。
然后,安装 git。如你所想,很容易:
$ sudo apt install git
就像在 Linux 中一样,你可以添加一个 git 的 PPA 来获取较新的版本,不过这里我们有一个就行了。
最后,我们需要安装 curses(实际上是 ncurses)来进行终端屏幕交互。
$ sudo apt install libncurses-dev
当我们完成这些步骤之后,就可以开始构建 NetHack 了。

构建 NetHack

官方的 NetHack 仓库放在 GitHub 上,首先我们需要把它抓取下来放到我们的主目录里面。
$ cd ~$ git clone http://github.com/NetHack/NetHack
因为 NetHack 支持很多种操作系统,所以我们需要做一些基础配置来告诉它我们使用的是 Linux,并且用开源的 gcc 代替了了 Unix 上 cc 的作用。
如我所提到的,这有好几种办法可以实现。有些人想很周到,将这些配置信息放到了 hints 文件中。相信我,使用 hints 文件会避免遇到该 GitHub 仓库中提到的很多麻烦。在 README 文件和其它文档中并没有着重提到如何使用 hints 文件,我们可以这样做:
$ cd NetHack/sys/unix
$ ./setup.sh hints/linux
这将会设置 Makefile 正确的使用 Linux 下的工具、库及其路径。这个设置脚本很强大,它做了大量的配置工作,很高兴它在 WSL 中工作的也很好。如果你很好奇这个脚本是如何写的,你可以使用你的编辑器打开它一窥究竟。
然后,开始最终的构建:
$ cd ~/NetHack
$ make all
构建完成之后,你需要安装它。这其实就是将可执行文件复制到目标位置:
$ make install
它会安装到你的 ~/nh 文件夹下, NetHack 放在 ~/nh/install/games 目录,名为 nethack。要运行它,切换到该目录(或输入完整路径)并输入:
$ cd ~/nh/install/games
$ nethack
然后,屏幕会清屏并显示你可以玩 NetHack 了。注意,所有的东西都是在这个 Ubuntu Linux 环境中完成的,根本不需要任何 Windows 特有的东西。

玩 NetHack

由于终端游戏的局限性和 NetHack 的复杂性,这里只能一带而过。对于初次接触它的人来说,还有一些神秘的地方,不过我觉得我们程序员们从来不怕挑战未知。
方向键和 vi(vim)中的一样,HJKL 是左、下、上、右。要退出游戏,你可以在地下城顶层找到楼梯出口然后使用它就可以,或者直接按下 CTRL-C 强制退出。
在 NetHack 中, @ 符号代表你自己,每一层都由房间、走廊、门,和向上及向下的楼梯组成。怪物宝箱和物品以各种 ASCII  字符组成,你慢慢就会熟悉它们。为了符合 Roguelikes 游戏规范,并没有存盘功能,你只有一条命。如果你死了就只能重玩,地下城环境是随机生成的,各种物品也是打乱放置的。
NetHack 游戏的目的是在地下城生存,收集金子和物品,尽可能的干掉各种怪物。除了这些目的之外,你就是不断在其中玩来找它们。规则大致遵循“龙与地下城(DnD)”的武器、技能等规则。
下面的 NetHack 截屏上可以看到有三个房间和两个走廊。向上的楼梯在左上角的房间里,我现在在右上角的房间,还有一些宝箱和其它物品。
如果在你的游戏中没有显示颜色,可以创建一个名为 ~/.nethackrc 的文件,并放入如下内容:
OPTIONS=color:true,dark_room:true,menucolors:true
注:如果 ASCII 字符图形不是你的菜,但是你喜欢这种类型的游戏,你可以在微软商店搜索“roguelike”来找到视觉上更好看的这种游戏。
当然,NetHack 很古老了,可能只有特定年龄段的人们喜欢它。不过,构建它用到了大量重要的开发工具和 *nix 操作系统功能,也包括终端模拟功能。从这里可以看到,从 gcc、gdb、make、bison 和 flex 到更现代一些的 git,都在 WSL 里面工作的很好。
如果你想看看 Build Tour 大会上的演示,你可以在 Build Tour 加拿大大会上看到这个讲演。WSL 的这部分演示在 6:20 开始。
希望你能喜欢在 NetHack 地下城中的探险。
C 和 C++ 都很伟大,就像其他的那些经典的开发工具一样。你甚至还可以用普通的 Bash 脚本做到很多。不过,也有很多开发者喜欢用 Python 做为他们的脚本语言。

Python

你可以在网上找到很多 Python 脚本的例子,这意味着 Python 越来越流行,也越来越有用了。当然,大多数情况下这些例子都是运行在 Linux 下的。在过去,这就需要我们有另外一台安装着 Linux 的机器来运行它们,或者使用虚拟机和多引导,否则就需要修改一些东西才能让他们运行在 Windows 下的 Python 环境中。
这是都不是无法解决的问题,但是它会日渐消磨开发人员每天的生活。通过 WSL,不用折腾你就拥有了一个兼容的、具有 Python 功能和 shell 变量的子系统。
要安装最新的 Python 开发版本和 Python 包安装器 pip,在 Bash 中执行如下命令:
$ sudo apt install python-pip python-dev
$ sudo pip install --upgrade pip
现在 Python 安装好了,我要展示给你如何从网上获取一个典型的 Linux 下的 Python 例子并让它直接工作起来。我去 Activestate Python 菜谱站找一个排名第一的 Python 例子。好吧,我走眼了,排名第一的是打印出整数名称的脚本,这看起来没啥意思,所以我选择了第二名:俄罗斯方块。我们每天都能看到 Python 出现在各种地方,所以这次让我们去玩另外一个游戏。
我打开了 nano 编辑器,从 Windows 上的浏览器中打开的页面上复制了这 275 行 Python 代码,然后粘贴到我的 WSL 终端窗口终端中的 nano 中,并保存为 tetris.py ,然后执行它:
$ python tetris.py
它马上就清屏并出现了俄罗斯方块的游戏。同 NetHack 一样,你可以使用同样的 vi 标准的方向键来移动(以前是使用鼠标和 WSAD 键来移动,而右手使用 HJKL 键更方便)。
如我所提到的,你当然可以不用 WSL 就在 Windows 中运行 Python。然而,要想快速简便,不用修改 Linux 下的 Python 代码,只需要简单的复制粘贴代码即可运行,则可以极大的提高开发者的效率。
这是真的。这并不是要替代 Windows 原生的工具,比如 Python、PowerShell、C# 等等,而是当你需要在现代的开发工作流程中快速而有效地完成一些事情时,可以避免种种折腾。
包括 Bash、Python 以及其它所有的 Linux 原生的命令行开发工具,WSL 为我的开发工作提供了所有需要的工具。这不是一个 Linux 服务器,甚至也不是一个完整的客户端,相反,它就是一个可以让我避免每天折腾,让我在 Windows 上开发更有效率、更有快感的一个东西!

重置你的 WSL 环境

随便去试吧,如果你搞坏了你的 WSL 环境,它很容易重新安装。在进行之前,请确保做好了任何重要内容的备份。
C:> lxrun.exe /uninstall /full
C:> lxrun.exe /install

你使用 Bash 和 WSL 的感觉如何?

我们希望 WSL ,特别是 Bash 可以在 Windows 10 中帮你带来更高的效率,减少每天的开发中的折腾。
你对 Windows 10 上的 WSL 怎么看?你喜欢使用它吗?
开发团队做了大量的工作希望让 WSL 成为一个为开发者提供的强大的终端工具。如果你有任何反馈或运行出现问题,我们推荐你查看一下 GitHub 反馈页面,以及 用户之声的反馈和投票站点。我们真的希望听到你的声音。

更多参考与延伸阅读

Linux Shell 编程是一个庞大的话题,在网上有很多这方面的内容。如果你还不够熟悉它们,想要了解更多,可以看看各种 Bash 教程。可以从这一份开始
还有一些其他的参考资料也许对你有用:
哦,当然,要更多的了解 NetHack,请访问 NetHack 主站

openssl基本密码学操作

 

openssl的基本检查

使用以下命令检测版本,-a可以提供完整数据。

openssl version
openssl version -a

speed test

speed测试是openssl为你跑一下不同算法在你机器上的实际执行速度,这项测试在openssl中是一项非常有指导意义的测试。一方面,他给出了你选择算法的依据,通过实际数据告诉你每个算法能跑多快。另一方面,他可以用来评估不同硬件对算法的加速能力。如果仅仅是给出了选择算法的能力,我们一般都可以得到一个一般性结论,例如chacha20比AES快。但实际上很多CPU带有AESNI指令集,这种情况下AES的执行速度反而会更高。所以运行性能是和执行平台紧密相关的。关于这部分,可以参考Intel对OpenSSL的性能优化

具体的测试方法是

openssl speed

后面可以跟算法,只测试特定的算法集。

我这里跑了一遍全集,挑几个重点算法说一下性能吧。

hash算法

  1. sha256,标杆性hash算法,64字节小数据140M/s,8k大数据353M/s。sha512,170/470。hash算法的内部状态越长,在连续计算时的速度越快。
  2. sha1,251/768。
  3. md5,243/575。(你没看错,md5比sha1还慢)
  4. rmd160比sha256还慢,whirlpool比sha256慢。最快的是ghash,小数据4222/9732。但是见鬼的是,我查不着这是TMD什么算法(openssl list -digest-algorithms的输出里没有)。
  5. 最合适的算法,应该就是sha-512/256了吧。很安全,速度比sha256快,长度也不算太长,还能防御LEA(Length extension attack)。

对称算法

  1. aes-128-cbc,标杆算法,120/125(M/s)。aes-192,93/103。aes-256,86/88。aes的内部状态越长,在连续计算时的速度越慢。这点和hash正好相反。
  2. camellia128,138/167。camellia192,110/128。camellia256,109/124。这是一种大批量数据计算非常优越的算法,AES在计算大批量时性能上升并不快。
  3. des比aes慢的多,只有66M/s。3DES更慢,只有25M/s。
  4. 没有chacha20。
  5. 不考虑chacha20的情况下,最好的算法应该是camellia128。当然,工业标杆是aes-128-cbc。

非对称算法

  1. rsa 1024/2048/3072/4096的sign效率分别是8698/1351/453/206(个/s),verify效率分别是131847/46297/22970/13415。rsa也是随着内部状态上升效率下降的,而且下降非常快。而且verify效率远高于sign。
  2. dsa 1024/2048的sign效率分别是9836/3280,verify的效率分别是10584/3616。
  3. ecdsa 192/224/256/384的sign效率分别是12696/12672/21016/4383,verify效率分别是3200/5630/9994/1019。能很明显看出来,sign效率比verify高。256位的时候由于某种效应性能达峰,后续直接断崖下跌。
  4. ecdh 192/224/256/384的效率为3642/8339/15094/1183。同样能看出这种效应。
  5. rsa和ecc不具有互换性。rsa参数选择建议2048,ecc参数选择建议256。

对称加解密

openssl支持多种对称加密算法,可以直接对文件加解密。在使用前,我们首先列出系统上支持的算法。

openssl enc -ciphers

输出很复杂,不列举。我们直接讲我的机器上分析后的结果。

  1. 第一段是密码算法。在我这里,支持以下算法:aes, bf, blowfish, camellia, cast, chacha20, des, des3, desx, id(ea), rc2, rc4, seed。
  2. 最后一段有可能是模式。在我这里,支持以下模式:ECB,CBC,CFB,OFB,CNT。其中CFB,OFB和CTR(CNT)是可流式的,其余都是块式的。
  3. enc的manpages里明确说了,enc不支持CCM或是GCM这类的authenticated encryption。推荐是使用CMS。

例如我们使用比较流行的chacha20来加密一个文件src,里面可以随便写一句话。

openssl enc -chacha20 < src > dst

注意dst应该会比src大。因为默认情况下,openssl会为密码加一个salt,然后把salt保存到加密结果上去。再从passwd+salt里推导出key和IV(默认sha256)。默认的salt为8bytes,合64bits。key为32bytes,合256bits。IV为16bytes,合128bits。具体情况可以用openssl enc -P -chacha20来打印。

另一点让我比较惊讶的就是,chacha20是一种流式算法。如果你采用-aes-128-ecb的话(这是一种典型的块式算法,已经研究的比较透彻了),输出是长度对16对整加16字节。而chacha20的输出纯粹比输入长16个字节。我觉得很好奇,于是就找了这个源码研究了下。

算法的核心状态机是一个64字节的数组,第一个分组16字节填充固定数据,第二个分组32字节填充key,第三个分组8字节填充nonce,最后8字节填充IV。然后通过一个变形算法,把这个核心状态变成一个out数组,再XOR到目标数据上去。每次输出一个out数组,nonce都会自动增长。

如果他的算法没错的话,chacha20非但是一个流式算法,而且主体算法就是CTR的变形。那么chacha20就会有CTR的几个特性,例如明文-密文对应,加密-解密过程是同一个。而且如果每次nonce不变的话,对CPA的抵抗会有问题。(公司里有个场景正好是这种nonce不能变的)

另外,这个加密过程有几个细节。一个是可以用-a或者-base64开关来获得一个纯文本的结果(当然,代价就是增加空间消耗)。第二个是可以用-k来指定密码,用-kfile来指定密码文件,而不是现场输入。当然,这样做的代价就是可能会记入command history,或者有磁盘记录。最后一个是-z,可以在加密前先做一遍压缩。

相应的,解密指令就是

openssl enc -d -chacha20 < dst

另外,openssl还提供了以算法为基础的写法。例如chacha20的加密指令也可以写成这样。

openssl chacha20 < src > dst

大家举一反三,我就不罗嗦了。

摘要生成

先说一句,本章一般人不需要阅读。性子急的朋友请先看最后一段。

openssl用于摘要的方法主要是dgst。首先老规矩,我们先看有哪些摘要算法。

openssl list -digest-commands

在贝壳这里的机器上,算法基本有这么几类。blake2,gost,md4,md5,rmd160,sha1,sha2。不用说,md4/5,sha1都是不安全的。我查了一下,gost和原生ripemd也是不安全的。blake2,ripemd160,sha2还是安全的。所以推荐算法是blake2,sha2。具体来说算法就是blake2b512,blake2s256,sha224,sha256,sha384,sha512,第一选择是sha256。很可惜,没有sha-512/256。

然后我们就可以用来算hash了。例如

openssl sha256 < src

可以看到输出了吧。

OK,下面要说一个悲伤的事实。为什么我们没听说过人家用这个功能呢?因为linux的coreutils里面,有md5sum和sha256sum。我查了一下,支持blake2,CRC,md5,sha1,sha224,sha256,sha384,sha512。上面数的各种推荐算法,还有最常用的MD5,你都能直接用。不用苦逼的用openssl拼。

所以这一章,其实是废话来着。

RSA的生成和使用

RSA支持的功能比较全面,加解密,签署验证,还有验证还原操作。可以说是用途最广的一个算法族。

公私钥对的生成和管理

生成密钥

openssl genrsa 2048 > rsa.key

在openssl里,in和out经常和stdin和stdout有相同的含义。两者经常可以互换使用。例如上面指令,其实也可以写成openssl genrsa 2048 -out rsa.key。但是如果用stdout写出,会使得openssl无法控制权限(毕竟它不知道你要写文件)。所以,这样生成的密钥,权限为其他人可读。常规请用-out写出,比较安全。

查看密钥

openssl rsa -text -in rsa.key

可以看到很多数据,modulus,publicExponent,privateExponent,prime1,prime2,exponent1,exponent2,coefficient。具体意义可以在这里查看。

可以注意到,除了最基本的p=prime1,q=prime2,n=modulus,e=publicExponent,d=privateExponent外。openssl还额外保存了三个数,exponent1=d mod (p-1),exponent2=d mod (q-1),coefficient=(inverse of q) mod p。为啥我也不明白。关于prime1啦,prime2的详细解释,请看这篇

分离公钥

openssl rsa -pubout < rsa.key > rsa.pub

分离之后可以查看

openssl rsa -text -pubin -in rsa.pub

可以看到,只有modulus和publicExponent了。

另外,你可以把key加密或解密(很多场合下会用到)。方法如下:

openssl rsa -aes128 < rsa.key > rsa.enc
openssl rsa < rsa.enc > rsa.key

很多教程里会告诉你用-des或-3des,根据密码学常识你就知道,这是错的。idea也建议不要用,因此推荐用aes(优先)或者camellia。

加解密

数据加密

openssl rsautl -encrypt -pubin -inkey rsa.pub < src > dst

注意输出长度和位数相等(这里是2048)。

数据解密

openssl rsautl -decrypt -inkey rsa.key < dst > src.new
diff src src.new

注意公钥加密私钥解密。

签署验证

数据签署

openssl rsautl -sign -inkey rsa.key < src > dst

注意输出长度和位数相等(这里是2048)。

数据验证有多种方法,第一种是直接用rsautl

openssl rsautl -verify -pubin -inkey rsa.pub < dst > src.new
diff src src.new

注意私钥签署公钥验证。

另一种是用pkeyutl,注意这里有两种效果。

openssl pkeyutl -verify -pubin -inkey rsa.pub -sigfile sig < src
openssl pkeyutl -verifyrecover -pubin -inkey rsa.pub < sig

这里就是RSA系列sign算法比较特殊的地方。一般的sign都是验证sig和src是否具有对应关系,RSA的verify直接能解出原始数据来,这也算某种意义上的“验证了对应关系”。所以rsautl的verify,在pkeyutl的通配模式里,其实是verifyrecover。

ECC的生成和使用

ECC支持签署,验证和derivation。其中签署用的是ECDSA算法(很遗憾,不是EdDSA),Kx用的是ECDH。

公私钥对的生成和管理

ECC的生成比较特殊。在ECC里,你不止要设定一个长度,而是要选择一条曲线。因此第一步,需要列出所有支持的曲线。

openssl ecparam -list_curves

我这里的数据很长,具体不列了。但是可以看出几个特点。

  1. 不支持25519曲线。
  2. 某几条曲线不支持ECDSA。

随后,你可以选择一个曲线来生成密钥。例如我们选择secp256r1。

openssl ecparam -genkey -name secp256r1 > ecc.key

查看密钥

openssl ec -text < ecc.key

可以看到,ECC的数据就要比RSA简单的多,只有一个priv和一个pub。其余主要是说你用了什么曲线。

分离公钥并查看

openssl ec -pubout < ecc.key > ecc.pub
openssl ec -text -pubin < ecc.pub

其实很多同学看出来了,openssl在处理ECC时和RSA的参数基本是类似的,只是ec和rsa指令的区别而已。对于已经生成好的key而言,我们可以抽离具体的key算法,用一个比较通用的办法来处理公钥提取问题。

openssl pkey -pubout < ecc.key > ecc.pub

pkey指令也可以用于其他方面,例如加解密。具体就不赘述了。

签署和验证

ECC的签署和验证就要借助于pkey指令了,具体来说,是pkeyutl指令。注意,这里的形态和RSA的形态不一样。

openssl pkeyutl -sign -inkey ecc.key < src > sig
openssl pkeyutl -verify -pubin -inkey ecc.pub -sigfile sig < src

这里无法直接解出原始数据,只能验证得到是否正确的结果。ECC是不支持verifyrecover的。

derivation

我们先假定你生成了两对key和pub,随后你可以用这两对key和pub推导出一个双方共同的秘密。

openssl pkeyutl -derive -inkey ecc1.key -peerkey ecc2.pub

我们可以看到,这么生成出来的数据是一堆乱码。所以加上hexdump让输出比较可读。

openssl pkeyutl -derive -inkey ecc1.key -peerkey ecc2.pub -hexdump

我们也可以换一个顺序来生成。

openssl pkeyutl -derive -inkey ecc2.key -peerkey ecc1.pub -hexdump

可以看到,结果并没有差别。

key derivation的时候,双方互相给对方发一个pub。随后利用对方的pub和自己的priv计算出一个共享的s。攻击者虽然有双方的pub,然而无法得到s。当然,如我们在这里说过,Mallory永远可以通过拦截pub的发送过程来发出攻击。

DH的生成和使用

私钥生成

生成dh私钥

openssl dhparam -outform PEM -out dhparam.pem 1024

查看私钥

openssl dhparam -in dhparam.pem -text

注意,双方的p和g都是现场约定的,公钥A可以很快计算生成,因此都无需保存。

DH的私钥一般不用于加密和签署(你看,他连公钥都没有)。DH是一个Kx算法,因此DH的私钥只用于derivation操作。

derivation

根据pkeyutl的manpage,dh的私钥应当支持derivation操作。然而杯具的是,我实际测试openssl pkeyutl -derive -inkey dhparam1.pem -peerkey dhparam2.pem无法执行,不知道是不是因为dhparam都是priv的缘故。但是dhparam里确实没有生成公钥的参数。

无论如何,在nginx里,dhparam是一个重要参数。如果你使用默认的dhparam,会被警告不安全。

DSA的生成和使用

首先,DSA只支持签署和验证。

公私钥对的生成

和RSA非常像,但是有点区别。

openssl dsaparam -genkey 2048 > dsa.key

另外注意,dsaparam参数是不支持加密的。如果要加密,需要写成这个样子。

openssl dsaparam -genkey 2048 | openssl dsa -aes128 > dsa.key

同类,如果要读取内容的话,可以这么做。

openssl dsa -text < dsa.key

剥离公钥这么做。

openssl dsa -pubout < dsa.key > dsa.pub

我们把公钥和私钥分别打出来,可以发现,公钥的要素是pub,P,Q,G。私钥多一项priv。

签署验证

这是另一个奇怪的地方。根据pkeyutl的manpage,DSA的key支持sign(而且只支持sign)。可是我在实验openssl pkeyutl -sign -in src -inkey dsa.key -out sig的时候,又失败了。这个例子是直接抄的manpage,错误提示是Public Key operation error。

---------

 

openssl证书相关

鸣谢和前言

首先感谢Ivan Ristić。本篇很多内容可以从他的这本书里读到。大家如果有钱,可以买来支持一下。没钱但是英文好的话,也可以直接读一下(这本书在网上是公开的)。本文没有直接复制,引用,或是完全写成读书笔记。所以原则上是不受书的版权限制的。不过如果作者有异议的话,我愿意下线这篇文章。

First of all, I wanna say thank you to Ivan Ristić. I got a lot of help from this book. You can buy one if you want, or read it personally (it’s public). This is really a good book.

In this article (I mean the one you are reading currently), I don’t copy or reference directly from that book. So as far as I know, it shouldn’t have any copyright problem. But if the author (Ivan Ristić) have any problem, I’m willing to take this article down.

PKI证书体系结构

公钥体系的问题

在介绍证书体系之前,我们首先先回顾一下公钥体系解决了什么问题,又引入了什么问题。

在公钥体系之前,为了和某人安全的通讯,我们需要预先共享一个公共的秘密x。而且对每个通讯组合,都需要一个独立的x。如果你偷懒,在A-C里复用了A-B的x,那么C就有能力截听甚至篡改A-B通讯。那么,随着通讯组合的上升,独立的秘密x就会暴涨(数学很容易求出,一般期望是C(n, 2),复杂度是O(n^2))。这么大的数据量,存储和使用都很困难。公钥体系引入后,每个人只要保留自己的私钥,而通过公钥和别人握手。Eva即使能听到内容,也无法获得公共的秘密x。在这个模式下,只要每人一个公私钥对就行,数据量大大减少。

然而,这也引入了主动攻击者Mallory可以通过MITM攻击的可能性。因此,不仅需要给予对方公钥,更需要给予对方可信的公钥。这就是公钥体系需要解决的首要问题。

当然,可信给予公钥的最简单方法,就是离线向对方交付。但是你可以想象你要上所有网站,都需要物理的到他们店里,获取一份公钥的情况么?而且还要定期更新。这种模式在大多数场景下都无法工作,只能小范围内用于调试。

私钥-公钥-证书

前面我们已经唠叨了不少了,私钥签署公钥验证,这是基本模式。那么我们能不能通过对公钥进行签署来解决这个问题呢?发送公钥的时候同时发送签名,对方验证签名,就知道这个公钥是否被伪造了。

也行,也不行。如果对方要验证你的公钥,那么他就需要有签署者的公钥。而签署者的公钥又如何给你呢?这时线下模型就不是一个不可能的选项了。毕竟公司千千万,但是签署者不会太多。

但是仅签署公钥并不解决问题。毕竟如果不绑定到身份,那么攻击者也完全可以签署一份出来。好比A向CA申请签署了一个公钥,B也这么干了。C在获得公钥的时候无法分辨谁是谁,因为都是合法的签名。那么怎么办呢?于是在签署里,除了你的公钥,签名,还需要附加你的身份信息(尤其是域名——如果用于https通讯的话)。这样,虽然攻击者能签署出另一本证书,但是由于身份不符,所以无法发起MITM。而整个身份数据,公钥,各种元信息,加上整体签署,合起来就叫证书。

目前PKI方案,基本就是三级签署制。你的系统(或浏览器)里安装RootCA,RootCA签署Intermediate,Intermediate签署你的证书。这三层都是一个上级可以签署多个下级,从而形成一颗树。你可以查看一下Firefox的设置,安全,证书。里面会有所有的预装证书和部分的其他来源证书(如果有的话)。

之所以是三级方案,是因为根证书可能存在系统里长期不换。这种情况下万一泄漏了很麻烦。所以RootCA都是签署出一些中间证书,然后就把RootCA干掉,key放在冷存储里,扔保险柜里安全存放。然后用中间证书签署各种实际证书。这样万一中间证书泄漏,可以很容易的吊销。

证书和证书链

既然证书有签署顺序,验证就有验证顺序。

在大部分系统里,存在着信任根证书域,其中保存着所有的信任根。在使用的时候,对方会提供他的证书。在验证时,你会发现,啊咧,还差着Intermediate呢。

所以,在配置证书时,除了证书本身外,很重要的一点就是配置证书链(cert chain)。证书链里保存了多本证书,一般是从最上面一本根证书到最下面一本自己手里的证书,完整的一个链条。

以PEM格式而言(具体下面会说),证书链的做法就是把自己的Cert放在最上面,然后是Intermediate,最后是rootCA。后面的每一本证书都要能证明前面的证书,根证书可要可不要。分行复制进去,仅此而已。具体可以查看这里

证书的申请-验证-发行

那么,既然有签署机构,我们如何申请证书,这些机构又如何来认证你的身份呢?

首先,你需要找代理机构。是的,这些签署机构一般都不会亲自来办理全套业务,一般都是找代理商做商业运作。等你找到代理商,你需要做的第二件事情就是——交钱。毕竟这么大一堆机构,养人很花钱的呐。

等交了钱了,你就是他们的客户,有权找他们申请证书了。正常来说,申请证书的第一步是——自己生成一份私钥。是的,既然签署者只需要你的公钥,那么私钥他就不需要接触。很多非正规代理商是他们帮你生成的,请干掉这些代理商,换一家靠谱点的。第二步是将公钥和你的身份信息写入一个req文件,可靠的发送给代理商。第三步是代理商验证你的身份和你写在req里的身份是否一致。如果一致,他们会把你这个req发到一个特别的机器上进行签署。这个机器只能用于签署,上面的私钥是无法拷走的。最后,他们会把签署好的crt文件发给你——其实这步不发给你也行,crt文件是可以公开给全世界看的。只要丢到一个所有人都能拿到的地方,那也算是完成了签署。当然,发给你个人更像是一种交付。

可能有人看出来了,这里的核心就是“供应商如何验证你的身份”。一般我们用于通讯的证书都是用在HTTPS上的,有三种验证方式。

  • DV: 只要验证域名。对应的方法就是,给域名whois信息里写的邮箱发邮件,让你确认。或者是让你写入一个TXT的域名记录,以证明对域名有控制权。或者你已经配置了http服务的情况下,给特定的URL放一个特定的文件也行。
  • OV: 验证项目要多一点,要提供工商营业执照。
  • EV: 验证项目比OV还要多一点,一般要求企业提供DUNS。偶尔甚至有要求律师函或电话验证的。

申请流程

构造req

使用以下指令生成req

openssl req -new -key rsa.key -out shell.csr

基本来说,需要你填几个参数。国家,省份,城市,组织名(公司名),Unit名字(部门),Common Name,Email,最后还有一个可选的password。其中Comman Name是最重要的一个参数,这里填写的是你身份有关的数据,例如你申请的证书对应的域名。

生成req之后,你就可以发送给代理商了。当然,如果你要再看一眼,也可以举一反三类比办理。

openssl req -text -in shell.csr -noout

req除了能直接构造外,还能从现有证书直接构造。这对于续签证书很方便。

openssl x509 -x509toreq -in shell.crt -out shell_new.csr -signkey rsa.key

另外,req构造也可以用配置文件来进行。将以下内容存入req.cnf,再执行openssl req -new -config req.cnf -key rsa.key -out shell.csr,其他没有区别。CN对应上面的Common Name,emailAddress对应Email,O对应Organization Name,L对应Locality Name,C对应Country Name,都很好猜。如果你想要看详细解说的话,可以看这份文档

[req]
prompt = no
distinguished_name = dn
req_extensions = ext
input_password = PASSPHRASE

[dn]
CN = www.feistyduck.com
emailAddress = webmaster@feistyduck.com
O = Feisty Duck Ltd
L = London
C = GB

[ext]
subjectAltName = DNS:www.feistyduck.com,DNS:feistyduck.com

注意最后的subjectAltName,这个是用于签署多个域名的。一本证书可以签多个域名,在正常的req -new构造里,是看不到这个选项的。

签署

自签证书

几乎所有讲openssl签署的文章,第一课都是自签证书。自签证书的基本指令大概是这样的:

openssl x509 -req -days 365 -in shell.csr -signkey rsa.key -out shell.crt

开关中,-req指定输入可以是一个csr(否则按照x509的默认假定,会出错)。shell.csr是上面构造出来的那个证书申请。注意-days,这个参数在这里出现(而不是在证书申请中),说明证书有效期是由签署者现场指定的。在req指令中是可以加入-days参数的,但是其意义为,在指定-x509参数的情况下(下文说明),一同指定天数。实际上还是签署时指定有效期。

如果你打算一行中完成整个签署流程,可以这么做。

openssl req -new -x509 -days 365 -key rsa.key -out shell.crt

或者是更简短的形式

openssl req -new -x509 -days 365 -key rsa.key -out shell.crt -subj '/C=CN/L=SH/O=home/CN=*.shell.org/emailAddress=shell@shell.org'

其中-x509指定生成一个自签证书,而不是证书申请。-subj是证书的subject部分,在x509 -text看证书时能看到的。格式照上面抄就行。注意-subj对req也有效。

alternative name

建立一个文件shell.ext,内容如下:

subjectAltName = DNS:*.facebook.com, DNS:facebook.com

然后将这个文件加入到签署指令中去:

openssl x509 -req -days 365 -in shell.csr -signkey rsa.key -out shell.crt -extfile shell.ext

你就可以得到一个带有alternative name的cert:

openssl x509 -in shell.crt -text -noout

注意,extfile指令对req不生效。req要增加alternative name,只能用config文件。

扩展属性

// TODO: 将来有空再写吧

多种证书格式转换

这节我也不是很擅长,所以基本就是抄的cookbook。

证书格式

证书格式比较复杂,常见证书格式有以下几种:

  • DER cert: 二进制格式,DER ASN.1编码。
  • PEM cert: 平文本格式,base64过的DER数据,以—–BEGIN CERTIFICATE—–开头(openssl的很多文件都有类似的boundary)。
  • DER key: 二进制格式,DER ASN.1编码。
  • PEM key: 平文本格式,base64过的DER数据。
  • PKCS7: RFC2315定义,一般叫做.p7b或者p7c文件。
  • PKCS12: 可以同时在文件内存入证书链和私钥,一般叫.p12或者.pfx。

PEM和DER互相转换

不就是base64互转么。基本思路是这样。

openssl x509 -inform PEM -in shell.crt -outform DER -out shell.der

原理很简单,自己看看就行。

PKCS12

这个就比较复杂了。首先给出PEM的cert和key转换为p12的代码。

openssl pkcs12 -export -name shell -out shell.p12 -inkey rsa.key -in shell.crt -certfile shell.crt

上面打包了三个文件,一个key,一个crt,一个上位证书。我的上级证书就是自己(自签的)。导出的时候需要输入一个密码。

反过来就比较麻烦了。如你所见,反过来比较类似于解压。如果要把所有内容塞到一个文件里去,可以这么做。

openssl pkcs12 -in shell.p12 -out shell.pem -nodes

开关-nodes的意思是no-des,不要给key做加密的意思,不是node-s。

如果你要分别解开,需要用-nocerts,-nokeys -clcerts,-nokeys -cacerts三个开关。三次输入密码,回车。

openssl pkcs12 -in shell.p12 -nocerts -out shell.key -nodes
openssl pkcs12 -in shell.p12 -nokeys -clcerts -out shell.crt
openssl pkcs12 -in shell.p12 -nokeys -cacerts -out shell-ca.crt

我所熟知的范围内,需要用到PKCS12转换技巧的地方,只有Debian的SSO方案。那是一个用户端证书方案,是由Debian生成一张证书,然后由用户自己导入浏览器内部的。生成的证书是PEM格式,连同key一起,转换为p12文件,手工导入chrome里。

PKCS7

从来没用过,不花时间了,直接抄指令。

openssl crl2pkcs7 -nocrl -out fd.p7b -certfile fd.crt -certfile fd-chain.crt
openssl pkcs7 -in fd.p7b -print_certs -out fd.pem

算法选择和TLS配置加固

前面讲了一堆算法,可以想见,在TLS中用的不会是某个固定算法,而是在多个算法中选择一个。那么理所当然,为了兼容旧的系统,OpenSSL中可能带有很多旧算法(甚至还在使用)。那么这些旧的算法就可能成为一个漏洞。例如MITM时强制降级到旧的算法,用已知的漏洞去攻击。因此,OpenSSL允许用户自行选择算法族。更大的算法族提供更好的兼容性,但是安全性上就有不足。禁用老式算法可以提供额外的安全保证,但是可能老的系统就无法连接。

查看系统中的所有算法

用这个指令查看系统中所有算法:

openssl ciphers -v

上面的指令可以跟关键字来做查询。当然,如果只是查找的话,我一般习惯是用grep。上面的指令一个关键用途是,对下面给出的算法最佳配置列出当前系统中的可用算法组合。

列出一堆算法,抓几个关键点。Kx是密钥交换(Key eXchange),Au是验证(Authentication),Enc是加密(Encryption),Mac是消息签名。每一个名字,其实都是几种算法的组合。其中还要排除掉一些冲突无法组合或者无需组合的情况。例如AESGCM就没有MAC,因为GCM是AEAD的,带有验证功能。

要知道更细节的意义,可以看cookbook的对应章节。cookbook里也有很多东西没有,例如CHACHA20系列算法,在cookbook里只在下面配置的注释里面提到了。不过大家既然都看过加密相关的章节了,这部分内容想想就应该能明白,所以不赘述了。

算法配置的最佳实践

关于openssl配置的最佳实践,我建议还是读ssllabs的指南nginx的ssl配置。全文读一读。

简单来说,TLS的配置分为两个部分,协议(protocol)和算法(cipher)。协议分为SSLv2/SSLv3和TLS1.0/1.11.2。TLSv1.3最近才发布(1.2发布于2008年,1.3于2018年),所以暂时略去不提。在这些协议中,SSLv2绝对不可用(DROWN attack),SSLv3建议不要用(POODLE attack)。TLS1.0也有弱点(BEAST attack),但是由于客户端仍然广为使用,需要做兼容的同学可以开启。TLSv1.1和TLSv1.2是安全的,但是1.2提供了更多的现代密码算法(主要是AEAD类)。

顺便一提,我看到的1.3最大的好处,一个是握手的轮数减少了。传统TLS握手需要两次往返。1.3只要一轮。去除了一些不安全的算法(Camellia居然也是不安全的?好吧,大家还是用aes128好了)。大家要知道具体细节的话,可以看这里

算法的选择就比较复杂。你首先需要考量对以前版本的兼容性。例如BEAST attack还是RC4 attack。这个问题的细节可以看nginx的ssl配置中,The BEAST attack and RC4一节。也可以看这份文档。个人觉得还是后面这份写的明白点。简单来说,两个坑你总要中一个。趋势上,更多的人中RC4问题。如果你要追求安全,还可以同时关闭TLSv1.0和RC4。当然,缺点就是兼容性问题。

nginx的ssl配置给出了很实用的算法选择指南。简单版本中,算法选择只有四类:EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH。也就是说,Kx算法中只有两个安全的,EECDH和EDH。Enc算法中也只有两个安全的,AESGCM和AES256。

根据我们前面的密码学知识,我们知道EECDH和EDH分别是前向安全的Kx协议中,对应DLP和ECDLP问题的两个实现。最前面的E表示这个实现的参数每次都是临时产生(严格来说只有带E的算法才是前向安全的)。AESGCM是AEAD算法,AES256是非AEAD算法。之所以有后者还是考虑了兼容性问题的。然而如果你在最新的openssl上执行这个设定,你会看到Mac中有个东西跳出来,SHA1。

这是因为这个配置写在2015年,还没有考虑到Google的SHA1碰撞实例。而且即便是Google的例子中,要碰撞一个实例也是非常费力的。所以从实证角度讲,我不觉得有人能在TLS流程中构造出一个可行的Mac碰撞。但是既然有替代品,去掉SHA1的成本很低,没必要继续使用。所以在近代,要用这个配置的话,需要写成这个样子。

ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:!SHA1';

可用算法总计16种。

复杂版本是这个样子的:EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4。注意,如果你要在bash中执行这个配置,必须使用”,而不是”“。因为有!的存在。

根据我们的密码学知识,我们知道这个配置禁用了几个不安全的算法。DES,MD5,PSK,RC4(上面有说)。但是这个配置还漏了不少东西,SHA1,RSA(as Kx, not aRSA)。然而这个配置却不能简单的干掉SHA1,因为SSLv3的所有Mac算法都是SHA1的。如果你干掉SHA1,等于禁用SSLv3(虽然强烈建议这么做)。至于RSA,是因为不满足前向安全性,好像是今年新出的提示。所以复杂版本至少要长这样:

ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:ECDHE-RSA-AES128-GCM-SHA256:AES256+EECDH:DHE-RSA-AES128-GCM-SHA256:AES256+EDH:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4:!RSA';

我这里是53个组合。如果更进一步关掉SHA1,则只剩下35个组合。

另外需要说明的一点(网上搜到的大多数教程都不会说),在OpenSSL v1.1的某些版本里,支持了CHACHA20。理论来说,你可以将CHACHA20的支持加入到你的配置里去。这个算法更快(尤其是对CPU不支持AESNI的系统,例如很多手机)。

ssl_ciphers 'EECDH+CHACHA20:EDH+CHACHA20:EECDH+AESGCM:EDH+AESGCM:EECDH+AES256:EDH+AES256:!SHA1';

我的版本是1.1.0f,CHACHA20在里面总共有7种算法组合。其中只有三种是新的算法族合用的。所以如果没问题的话,上面的配置应该有19种算法组合,而且CHACHA20优先。

如果你觉得配置OK了,可以去这个网站检测一下自己网站的安全性。如果你的配置正确,上面的ciphers应该不会出现任何ciphers方面的问题。不过整体得分可能并不高,ciphers只是长征的第一步。

其他安全加固

首先是最重要的,SSL一定要升级到最高版本,能多高多高。现在都2018年了,我还能在线上看到heartbleed漏洞。如果还有这种级别的漏洞,就不要提什么安全加固了,OK?

然后,保证在nginx里有这两行配置:

ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;

session cache听起来是一个提升性能的配置。

第三点是打开HSTS。一般浏览器会在输入域名的时候自动补全http://,服务器会在http的80端口上收到请求,然后301到https上继续访问。一般来说这样没什么问题。但是攻击者可以MITM之前的http过程,使其不定向到https,而是定向到攻击者Mallory。Mallory用https向上游访问。在这个过程里,客户根本不知道服务器有TLS保护。

HSTS通过特定配置挫败这个攻击。在用户首次访问网站的时候,HSTS会增加一个Header: Strict-Transport-Security。这个Header指明当前网站多久内不应用http去访问。在nginx里,可以这么配置。

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";

在用户首次访问网站过后,浏览器就会缓存这个设定。再次访问的时候就不会被MITM了。当然,这个防御并不能应付首次攻击。

第四点是增加DH的复杂度。OpenSSL的默认复杂度是1024,我们至少应该增加到2048,推荐4096。因此需要先生成dh参数:

openssl dhparam -out dhparam.pem 4096

随后在nginx中加入这行:

ssl_dhparam /etc/ssl/certs/dhparam.pem;

最后一点就是,有条件的话,开启OCSP stagling。

ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;

这条之所以在最后,是因为这条是性能优化,不是安全加固。

自建CA

所谓自建CA,就是自行建立一个CA根,为下属签署证书。比较简单的自建CA模型往往是CA-cert,复杂的甚至可以做到CA-Intermediate-cert(和目前各大证书代理机构结构相同)。

自建CA,最简单的方法是使用easy-rsa项目。

setup

首先你需要获得一份easy-rsa,我用的是Debian stretch中自带的2.2.2-2。你也可以直接去上游下载一份,我看到的最新版本是3.0.4。获得后,复制文件到一个临时目录,而后的生成之类行为都是基于这个目录的。你需要妥善保管整个目录(主要是保管keys目录,vars文件丢了可以重新编辑),保证不丢失和不泄密。

在完成复制后,首先需要设定一些环境变量。编辑vars文件,你可以看到一个bash脚本,里面有很多环境变量。一般来说,你只需要修改最下面’KEY_COUNTRY’开始的那些内容,包括国家,地区,城市,组织,邮件地址之类的。随后source vars引用文件,使得变量生效。后续所有操作都是假定变量生效的,就不一一重复了。

在引用文件后,你会看到提示。一个是openssl.cnf文件找不到。你可以看一下,目录里有openssl-0.9.6.cnf openssl-0.9.8.cnf openssl-1.0.0.cnf三份文件。随便链接一份到openssl.cnf就行。这种情况下,正常人都会链接openssl-1.0.0.cnf吧。另一个提示是,如果执行./clean-all,会清除全部文件。实际上,由于我们什么都没建立,因此不用担心,大胆执行。执行完成后,可以看到生成了一个keys目录,带有几个文件。后续所有的私钥和证书生成都是在这里完成。

准备的最后一步,是生成一份CA。执行./build-ca,你可以看到keys目录下出现了一份crt和key。其中crt是rootCA,可以用于分发。key需要谨慎保护。如果这份key泄漏了,那么攻击者就可以任意签署证书了。crl并不能解决这个问题,唯一的补救措施是干掉整个CA树,重新生成CA,并为所有人重签署证书。近代PKI体系中为了规避这个问题,采用的是CA-Intermediate-cert结构。

sign

很简单,./build-key common_name。如果是域名,照写域名就行,但是泛域名最好不要直接写进去,在输入CN时修改一下。因为cn同时会作为文件名,文件名里带有*会比较麻烦。如果没问题的话,最后可以看到问你要challenge password。这个东西,根据我查到的资料,应该是在吊销时需要的。然而我吊销时也用不上,貌似是个废物。不要设定就好了。最后是两个y,确认签署。

签署完成后,在keys目录中就可以看到很多新东西。*.old是备份,不用管。test1.*是刚刚生成的证书,crt是证书,csr是req,key是私钥。build-key这个脚本,其实就是自动生成key,根据你的要求生成csr,最后自动用ca签掉你的csr。但是注意,这里生成的crt,头部包含了-text的全部输出。在作为crt使用的时候建议删除,并把ca.crt合并进去(具体解释见后)。

如果你有印象的话,会发现目录里会多出几个东西来,这些基本都是和序列号有关的。PKI要求,同一个CA必须为签署的每本证书配备一个对应的序列号(Serial Number),序列号必须唯一。你用x509指令看一下刚刚生成的crt文件,可以看到sn为1。此时查看serial文件,可以看到内容为02。serial文件就是下一个可用的sn。

理解了serial,index.txt也简单了。这就是一个签署记录表,记录了当前CA签署的每个crt的sn,subject,还有当前状态什么的。有了这些数据,未来才能进行对应的吊销。最后就是01.pem,这个是基于序列号的命名,内容和crt一致。

附带讲解一下,build-key-pass可以生成一份带密码的私钥,build-key-server可以生成服务器端证书,build-key-pkcs12可以生成pkcs12证书。这些比较简单,就不罗嗦了。

sign with req

还记得我们说过标准CA的过程是,签署过程中不应该帮你生成私钥。那么我们来模拟一遍客户自己生成私钥的过程吧。

客户自行生成私钥和req的细节可以看上面,也可以直接使用easy-rsa的build-req脚本。同比,使用./build-req cn,经过类似过程,会生成一些文件。但是你仔细看的话,只有key和csr,并没有crt。这是因为这步是在客户手里做的,csr会被发送给服务方签署。

服务器端也很简单,拿到文件后,用./sign-req cn来签署。把crt发还给客户就行。

revoke

这个也不难,./revoke-full cn就行了。例如我吊销test1,完成后看一下index.txt,01那行被改成了R。openssl crl -in keys/crl.pem -text -noout(其实直接./list-crl就行)看一下,吊销是成功的。再吊销test2,结果一样。

关键是crl的生效方式。吊销颁发给客户的证书,crl需要被放到服务器上读取。吊销服务器证书,crl需要发给所有客户。一般而言,后者在技术上不具有可行性。所以往往会为证书里签上一个CRL URL,把CRL放在某个URL里完事。

但是这又带来另一个问题。随着PKI体系和https的推行,大量的公司签署服务器证书,也有大量公司被吊销证书。客户端获得CRL的时候,需要获得完整文件,庞大的问题造成了严重的性能问题。因此才酝酿了OCSP。OCSP每次只获得一条记录。

intermediate

这是我用的最少的一个功能,一般用easy-rsa的人根本不会搞什么intermediate。

签署没任何困难,./build-inter cn就完了。然后我们用openssl x509 -in keys/int1.crt -text -noout仔细看一下证书,你会发现,这本证书的X509v3 Basic Constraints很有趣,是CA:TRUE。

然后要怎么用呢?

首先,你要再搞一个easy-rsa目录,重新编辑vars文件(内容可以一样,也可以不一样),重新source生效,用clean-all生成基础环境。这些都和setup没区别,但是后面不要build-ca,用./inherit-inter ../easy-rsa/keys/ int1,其中../easy-rsa/keys/是前面你签署证书的keys目录,int1是前面刚刚签署出来的intermediate证书。

完成后,前面一个证书体系签署的intermediate就会变成你新目录的ca,而前面的crt和当前的crt会合并成一个export-ca.crt。这是你的证书链文件,发布的时候使用这本。其他和正常的PKI体系没有任何区别。

证书购买中的一些细节

  • EV没有泛域证书: 虽然是常识,但是还是有人不知道。

二手华为路由器hg255d刷OpenWrt固件

刷机有危险,刷机前请做好相关知识储备!!!
 
本文仅适合 华为hg255d v4 版本路由,某宝入手二手华为hg255d路由器,CPU: Ralink RT3052F Flash:4M RAM:32M 此为背景。硬改可参考[2].

 连接路由,上传u-boot

设置本地连接为 192.168.1.100 子网掩码:255.255.255.0 网关丶DNS不用填写(以后只要更新固件就是192.168.1.100的固定ip切记切记),网线与hg255d连接好,保持机器关闭状态.
先在浏览器输入http://192.168.1.1/upload.html 然后按住路由的reset键并接通电源,保持reset按住,浏览器刷新,大约十秒左右就可以进入如图所示的界面
此时一定要抓紧选择ralink.bin(此固件内已包含改版u-boot和电信原版u-boot,后面u-boot-rom-hg255d_hg256.bin是升级用的)固件并开始刷入,因为在10-20秒以后,路由将正常启动,页面输入固件将会失败。切记速度!!!
lan口灯狂闪,ralink固件写入结束,大约5min之后路由会自动重启,稍等片刻在浏览器中输入192.168.1.1帐号密码同为admin.进入ralink的设置界面.

 刷u-boot

u-boot 即Bootloader ,从设备加电到启动系统这段时间内固化这主板上的一段代码块,同电脑的BIOS类似。刷u-boot,只需要执行一个shell就可以了.找到 系统管理->系统指令。
1
执行命令:/hg255d/hg255d-flash-uboot.sh   提交一下
 
大约1-2秒就结束了,很快的,然后手动重启路由器。路由重启后,只要是电源灯频闪基本上就说明刷u-boot成功了.(如想恢复电信原固件,用tftp重刷ralink.bin固件,执行/hg255d/hg255d-flash-tel-uboot.sh就OK了)

 刷openwrt

还是开机的时候按住wps/wlan/reset按钮任一按钮,然后刷入固件openwrt-ramips-rt305x-hg255d-squashfs-sysupgrade.bin. 当进度条上传完成,不要断电和动路由器,路由器会自动重启
写入固件大约10秒钟,此时电源灯为常亮,然后大约是5分钟的等待时间,电源灯会灭一下,然后频闪,之后稍等一会就可以192.168.1.1进入openwrt …

默认账号:root 密码:admin,然后你就可以看到下面的画面
固件和工具
链接:http://pan.baidu.com/s/1bpMd0OB 密码:zhe0

 配置网络

这一部分参考老高[4]的博客,来简要介绍openwrt中br-lan,eth0,eth0.1,eth0.2 这几个接口的意思。

 有线网配置

openwrt的有线网络配置文件位于/etc/config/network,参考老高[4]的配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
config interface 'loopback' # 本地回环地址
option ifname 'lo'
option proto 'static'
option ipaddr '127.0.0.1'
option netmask '255.0.0.0'

config globals 'globals'
option ula_prefix 'auto'

config interface 'lan' # 内网口,默认地址192.168.1.1
option ifname 'eth0.1'
option force_link '1'
option type 'bridge'
option proto 'static'
option ipaddr '192.168.1.1' # 如果用openwrt做二级路由,且与一级路由地址192.168.1.1冲突,可修改这里
option netmask '255.255.255.0'
option ip6assign '60'
option macaddr '64:09:80:05:e1:bb'

config interface 'wan' # 外网口
option ifname 'eth0.2'
option _orig_ifname 'eth0.2'
option _orig_bridge 'false'
option proto 'pppoe' # 可以看出这里使用光纤拨号的方式
option macaddr '64:09:80:05:e1:bc' # mac 地址
option username '11100026295' # 宽带账号
option password '380926' # 宽带密码

config interface 'wan6' # ipv6 不用动
option _orig_ifname '@wan'
option _orig_bridge 'false'
option proto '6to4'

config switch # 交换机
option name 'mt762x'
option reset '1'
option enable_vlan '1'

config switch_vlan # 虚拟出两个 vlan (可以将物理地址屏蔽开,用于不同网段之间通信),这里是vlan 1
option device 'mt762x'
option vlan '1'
option ports '0 1 2 3 5 6t'

config switch_vlan # 这里是vlan2
option device 'mt762x'
option vlan '2'
option ports '4 6t'
 

 无线网络配置

openwrt的无线网络配置文件位于/etc/config/wireless,配置如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
config wifi-device 'rai0'
option type 'mt7612'
option mode '14'
option channel 'auto'
option txpower '100'
option ht '20+40+80'
option country 'US'
option disabled '0'

config wifi-iface
option device 'rai0'
option network 'lan'
option mode 'ap'
option ssid 'BYBY_WORLD' # 2.4G 热点名称
option encryption 'psk2' # 加密方式
option key 'xxx'

config wifi-device 'ra0'
option type 'rt2860v2'
option mode '9'
option channel 'auto'
option txpower '100'
option ht '40'
option country 'US'
option disabled '0'

config wifi-iface
option device 'ra0'
option network 'lan'
option mode 'ap'
option wps 'pbc'
option ssid 'HELLO_WORLD' # 5G 热点名称
option encryption 'psk2' # 加密方式
option key 'xxx'
 

 名词解释

  • II. pppoe-wan
    虚拟设备,他就是常见的拨号宽带上网,需要有ISP提供的用户名密码,连接后方可激此接口!
  • III. lo
    虚拟设备,自身的回环网设备。
  • IV. ra0 rai0
    这两个是成对出现,一看就知道是无线设备,它们各自对应一个SSID,分别是2.4G和5G。
  • V. 虚拟局域网
    开启虚拟局域网 对应 config switch.划分子网 你可以在网络->交换机中找到!

    对照着图下面的配置应该很容易就懂了。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    config switch_vlan
    option device 'mt762x'
    option vlan '1'
    option ports '0 1 2 3 5 6t'

    config switch_vlan
    option device 'mt762x'
    option vlan '2'
    option ports '4 6t'
  • VI. eth0
    eth0是一块物理网卡。eth0.1 eth0.2都是从此设备上虚拟出来的。
    1
    2
    eth0.1 是vlan1分出的lan口。
    eth0.2 是vlan分出的wan口。
  • VII. br-lan
    虚拟设备,用于LAN口设备桥接,可以用brctl show查看使用情况。
    1
    2
    3
    4
    5
    ~ brctl show

    bridge name bridge id STP enabled interfaces
    br-lan 7fff.64098005e1bb no eth0.1 rai0 ra0
    br-lan = eth0.1 + rai0 + ra0,即将有线LAN口和无线网统一划分为 LAN,便于管理!

 参考

from archive.is/XlCca