[译]UUID和Linux:你需要知道的一切

本文为 UUIDs and Linux: Everything you ever need to know [Update] 的翻译。

UUID在Linux中为何如此特别?我[原作者]不知道!但是这儿有你需要知道的、关于UUID在Linux下的一切!

背景

UUID是128位长的数字,表示为32位的16进制数字,被用于在软件开发中、在没有上下文的情况下唯一地标识信息。RFC 4122描述了UUID的规范,一个UUID的例子是:

13152fae-d25a-4d78-b318-74397eb08184

UUID在Linux最熟悉的场景应该是块设备的标识符了吧。Windows世界中的UUID以Microsoft的全局唯一标识符GUID的形式出现,这些标识符在Microsoft的组件对象模型[COM]中使用。

UUID以各种变体生成:最初大多数是从计算机的MAC派生的,随后使用了基于name的散列值。关于这个问题,有多少UUID,有多大概率会生成一个已有的UUID,这是来自维基百科上 UUID 的文章的一些数字:

在100年里,每秒生成十亿个UUID,生成重复的UUID的概率约为50%。如果每个人都有6亿个UUID,下一个UUID重复的概率约为50%。

fstab中的用法

就像之前提到的,UUID在Linux下被经常用于标识块设备。想象一下,你有两个硬盘通过USB连接上,它们不是持久存储,只能依赖两个设备的名字来区分:有时第一个USB硬盘是“sda”,有时它是“sdb”。所以要唯一寻址正确的磁盘,比如在 /etc/fstab 中,你必须添加如下条目:

UUID=9043278a-1817-4ff5-8145-c79d8e24ea79 /boot ext3 defaults 0 2

对于块设备,uuid是存储在超级块中的。
但请注意,当您使用LVM快照时,不应在fstab中使用UUID。 有关更多详细信息,请参阅下面的“何时不使用它们”。

Linux下的实现和生成

在Linux下UUID在file中生成,你可以通过 /drivers/char/random.c 来生成新的UUID:

$ cat /proc/sys/kernel/random/uuid
eaf3a162-d770-4ec9-a819-ec96d429ea9f

uuidgen,以及ext 2/3/4 工具 E2fsprogs 使用程序库 libuuid 来生成UUID:

$ uuidgen 
f81cc383-aa75-4714-aa8a-3ce39e8ad33c

Bash中如何得到UUID

UUID中最有趣的部分很可能是如何获取硬盘的当前UUID。 如前面已经提到的,有两种主要的方法来获取它们:在特殊目录中的 ls 命令和工具 blkid。

第一,在 /dev/disk/by-uuid 中执行 ls 命令。这个目录包含了名字为UUID,链接到“真正的”块设备文件。对于你很难辨别安装了什么磁盘的情况下,这是很方便的。

$ ls -l /dev/disk/by-uuid
lrwxrwxrwx 1 root root 10 11. Oct 18:02 53cdad3b-4b01-4a6c-a099-be1cdf1acf6d -> ../../sda2

第二,blkid 工具是util-linux软件包的一部分。它提供了一个界面来查询特定设备,并且还支持搜索标签。

$ blkid /dev/sda1
/dev/sda1: LABEL="/" UUID="ee7cf0a0-1922-401b-a1ae-6ec9261484c0" SEC_TYPE="ext2" TYPE="ext3"

这儿还有更多方法!让我们安装 hwinfo:

$ hwinfo --block
[...]
  UDI: /org/freedesktop/Hal/devices/volume_uuid_3e953ee0_79f2_4d94_98b3_5f49ad652b7c
[...]
  Device Files: [...] /dev/disk/by-uuid/3e953ee0-79f2-4d94-98b3-5f49ad652b7c
[...]

就像你看到的,hwinfo列出了大量有关硬件的数据——包括设备的UUID。 当你获取更多关于块设备的信息时使用它。

或者 udevadm?它是udev提供的、从udev数据库查询数据的工具。udev数据库包含了udev系统的所有信息,因此UUID信息只是一部分。 如果你正在写一个“现代”的脚本,它很好地与Linux标准工具集成,我想我会使用udev。 但是对于快速直接的、一次性的命令行工具,和hwinfo一样,它包含了太多的信息。

$ udevadm info -q all -n /dev/sda1|grep uuid
S: disk/by-uuid/9043278a-1817-4ff5-8145-c79d8e24ea79
E: [...] /dev/disk/by-uuid/9043278a-1817-4ff5-8145-c79d8e24ea79
E: ID_FS_UUID=9043278a-1817-4ff5-8145-c79d8e24ea79
E: ID_FS_UUID_ENC=9043278a-1817-4ff5-8145-c79d8e24ea79

在这种情况下,有时也提到udevinfo。 但是,它已被弃用,大多数发行版不再包含。 另外,另一种常提到的检索UUID的方法是从程序库 /lib/vol/vol_id。 但是如bug #476379所述,vol_id只是一个私有的udev函数。因为此接口不稳定它不应该被外部程序(或人)使用。此外,整个库可能会在将来删除——实际上已在某些发行版中删除了。

如何在GUI程序中获取UUID

如果你不善于使用shell,KDE-GUI工具也可以用来查找UUID:/usr/bin/kcmshell4 devinfo

设置UUID

就像评论中提到的,设置UUID有时也是必要的。因为UUID是超级块的一部分,所以根据文件系统的不同,可能要使用不同的工具。比如ext文件系统可以使用tune2fs:

# tune2fs -U new_uuid /dev/sdaX

什么时候不使用UUID

Zhenech 提到,并不是所有的地方都适合使用UUID。

因为挂载两个具有相同的UUID的文件系统是不可行的,所以需要额外关注LVM快照(或者克隆磁盘):因为具有同样的UUID,它们可能会挂载失败。

XFS:dm-2文件系统也会有重复的UUID,也无法挂载。

处理该问题的一种方法是更改UUID,另一种办法是挂载的时候带上nouuid选项。

通过DNS认证来部署Let’s Encrypt

原来是通过http认证的方式来完成ACME 的 Identifier Validation Challenges,但是内网的机器就无法完成这个认证,今天看了下,LE支持dns认证了,所以实践了一下。

首先安装Certbot

然后执行

certbot -d ssl-test.robberphex.com --manual --preferred-challenges dns certonly

对于MacOS用户来说,可以执行certbot –config-dir /usr/local/etc/letsencrypt –logs-dir /usr/local/var/log/letsencrypt –work-dir /usr/local/var/lib/letsencrypt -d ssl-test.robberphex.com –manual –preferred-challenges dns certonly 

  • 需要输入邮箱

  • 同意用户协议

  • 同意记录IP

  • 设置域名的TXT记录


    比如图中,设置_acme-challenge.ssl-test.robberphex.com的TXT记录为x-P6A_dQ4_ggZtPvX_bOUeaY7hSM_IS6o-Gzj3h7LBw,然后回车。

  • 提示证书生成成功


我们来一个最简版的配置:

server {
    listen       8443 ssl;
    server_name  ssl-test.robberphex.com;

    ssl_certificate /usr/local/etc/letsencrypt/live/ssl-test.robberphex.com/fullchain.pem;
    ssl_certificate_key /usr/local/etc/letsencrypt/live/ssl-test.robberphex.com/privkey.pem;

    location / {
        default_type "text/plain";
        return 200 "pong";
    }
}

curl测试(不需要设置DNS):

$ curl -i --resolve ssl-test.robberphex.com:8443:127.0.0.1 https://ssl-test.robberphex.com:8443/
HTTP/1.1 200 OK
Server: nginx/1.10.2
Date: Wed, 14 Dec 2016 07:33:41 GMT
Content-Type: text/plain
Content-Length: 4
Connection: keep-alive

pong

浏览器测试(需要设置DNS或者hosts文件):

证书有效

Continue reading 通过DNS认证来部署Let’s Encrypt

使用repox搭建sbt/maven镜像

最近sbt的速度实在是不能忍受了,所以使用repox搭建了sbt镜像。

搭建过程没有什么好说的,直接sbt assembly,然后

java -Xmx512m -jar target/scala-2.11/repox-assembly-0.1-SNAPSHOT.jar

就好了。


但是发现了两个repox的问题:

  1. 下载文件时,服务器全部下载完后,才能传输给sbt(不支持nginx那种“流式代理”),这在下载大文件时尤其明显。
  2. 有的时候,pom文件总是404
    比如curl https://repo1.maven.org/maven2/org/w3c/css/sac/1.3/sac-1.3.pom -I的结果是200,curl http://106.75.27.110:8078/org/w3c/css/sac/1.3/sac-1.3.pom -I的结果就是404,重试好几次都不行;重启repox就好了。

Flume在hdfs产生大量日志文件的问题

线上两台flume向hdfs写日志,但是后来发现每5分钟产生的日志文件数量很多(远远超过两个的数量)。

后来找了一个时间观察下日志,发现如下日志:

16/12/13 11:38:11 INFO hdfs.BucketWriter: Closing idle bucketWriter hdfs://xxx/xx/xxx_log_20161213/.xxx_log.1481600173693.tmp at 1481600291480

根据日志找代码,发现是BucketWriter在配置了hdfs.idleTimeout的情况下,会在超时后关闭不活跃的日志文件

flume官方文档对此参数的描述如下:

Timeout after which inactive files get closed (0 = disable automatic closing of idle files)

找到原因后,当然就很简单了,由于此参数默认是0,直接删除这个参数的相关配置就好了。每5分钟产生的日志数量就是两个了。

APT Hash sum mismatch

工作当中遇到此问题,然后偶尔看见此文。

此文是 APT Hash sum mismatch 的翻译。

TL;DR

APT 仓库能够提供未压缩和压缩过的文件格式。常用的几个的压缩格式是 gzip, bzip 和lzma。

apt 的 Bug 在处理lzma文件(.xz)读写的时候会偶尔报告“Hash sum mismatch”错误。

本文提供一种变通方式,另外,在特定版本的 apt 中,该bug已经被修复。

使用如下系统的用户应该升级到系统提供的最新的 apt 版本:

  • Ubuntu Trusty (14.04) 及更新的版本
  • Debian Jessie (8) 及更新的版本

更早系统的用户应该升级系统或者使用如下的变通方法。

目前,我们决定关闭 packagecloud 的所有仓库中对 lzma 格式的支持。元数据仍然能够以 gzip, bzip 以及未压缩的格式来获取。packagecloud 仓库的所有者们并不需要做任何事情。

Hash sum mismatch 变通方法

用户可以通过两种方法强制 apt 不使用 lzma 格式的元数据。

第一种办法就是运行 apt-get 的时候添加额外的命令行参数:

apt-get update -o Acquire::CompressionTypes::Order::=gz

(注意:如果你偏向于使用 bzipped 格式,你也可以指定参数为“bz2”)

或者,你可以在全局的 apt 设置中指定这个选项,这样你就不必每次运行 apt 的时候输入这些。

步骤如下:

  1. 创建 /etc/apt/apt.conf.d/99compression-workaround 文件
  2. 添加这些文本到文件中: Acquire::CompressionTypes::Order:: "gz";

现在,apt-get update 会首先尝试 gzip 格式的元数据。

Hash sum mismatch 在新的 apt 版本中被修复

这个 aptbug 在2014年3月21日被修复(git 提交为c4b113e650dbdbb4c5c9c6f36437c94db6b214d9)

这个修复包含在 apt 1.0以及更新的版本中,apt 1.0在如下系统中可用:

  • Ubuntu Trusty (14.04) 及更新的版本
  • Debian Jessie (8) 及更新的版本

如果你在如上系统中看到了这个错误,你应该确认是是否运行着最新的 apt 。

“Hash sum mismatch”到底意味着什么?

顶级的文件包含着仓库其他部分文件的散列值,这是 apt 仓库的组织元数据的方式。

“Hash sum mismatch”错误提示用户: apt 下载的文件的散列值和元数据中的散列值不匹配。

不幸的是,因为 apt 中的 bug,以 lzma 格式(.xz文件)压缩过的元数据会偶尔下载或(在某些情况下)解压错误,造成文件损坏。

结果,损坏的文件的校验值会导致 apt 产生“Hash sum mismatch”错误。

结论

apt 在处理 lzma 格式中一个不幸的错误 导致了臭名昭著的“Hash sum mismatch”错误,加之散列值会被校验,造成了另最终用户困惑的局面。

Ubuntu Trusty 和 Debian Jessies(或者更新的系统)的用户建议升级到系统中最新的 apt 版本。其他用户建议使用上文提供的变通方法来避免食用 lzma 格式的元数据。

packagecloud 的 APT 仓库作者们不需要做任何事情。我们已经完全关闭了 lzma 格式的元数据,仓库用户并不会被此 bug 影响。

Let’s Encrypt/HTTPS配置记录

前几天收到Let’s Encrypt public beta的邮件,所以就尝试了下ssl+https+http/2。

Let’s Encrypt 配置

首先,我之前遇到过Error creating new authz 的错误,就一直没有尝试,后来发现v2ex上有人通过设置NS记录解决了问题(虽然文中说要设置MX记录,但是我只更换了NS记录到CloudFlare),生效时间可能是24h。

不过由于cf会友好地将所有流量切换到它的反代服务器,所以需要关闭此特性;另外,也需要关闭cf中HTTPS的开关,由自己的主机来处理HTTPS请求(所以我仅仅是将cf作为一个DNS服务器而已)。

另外有一个坑:由于我用的是nginx,letsencrypt这个客户端程序需要自己接受80端口的请求来验证服务器身份,所以需要临时关闭nginx。

最后开始生成服务器证书:

第一步:将letsencrypt项目clone到特定目录下:

git clone https://github.com/letsencrypt/letsencrypt.git /opt/letsencrypt

第二步:cd到该目录,然后执行如下命令生成证书:

./letsencrypt-auto --agree-dev-preview --server https://acme-v01.api.letsencrypt.org/directory certonly

依次填写邮箱、域名后,会在 /etc/letsencrypt/live/<domain>/ 目录下生成证书(nginx只用到fullchain.pem和privkey.pem)

注:此命令一定要添加–server选项,否则生成的证书并不被浏览器信任。

证书都已经生成,接下来配置nginx。

nginx配置

注:如果要开HTTP/2的话,就必须要添加nginx mainline的repo了:

[nginx]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=0
enabled=1

首先,我们必须再添加https的server,基本上就是listen配置变为listen 443 ssl http2;(顺便打开HTTP/2)。

至于证书路径,添加两个配置:

ssl_certificate /etc/letsencrypt/live/<domain>/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/<domain>/privkey.pem;

为了开启HSTS,请添加如下选项:

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

另外,不要忘记在http站点的配置中添加到https站点的跳转:

location / {
	return 301 https://$server_name$request_uri;
}

好,至此为止,nginx部分完成。如果在这个时候你打开网站,会发现仍然有很多资源是走的http,很多浏览器都会对此进行警告。这就需要我们在WordPress上进行配置了。

WordPress配置

注:此段主要参考自https://css-tricks.com/moving-to-https-on-wordpress/

首先,在首页生成的文章列表,会受到site address影响,所以必须修改此选项,既将图中的两个都改为https:

general-settings

此外,还有三个SQL用来修改现有文章中的http链接引用:

替换双引号的图片链接:

UPDATE wp_posts 
SET    post_content = ( Replace (post_content, 'src="http://', 'src="//') )
WHERE  Instr(post_content, 'jpeg') > 0 
        OR Instr(post_content, 'jpg') > 0 
        OR Instr(post_content, 'gif') > 0 
        OR Instr(post_content, 'png') > 0;

替换单引号的图片链接:

UPDATE wp_posts 
SET    post_content = ( Replace (post_content, "src='http://", "src='//") )
WHERE  Instr(post_content, 'jpeg') > 0 
        OR Instr(post_content, 'jpg') > 0 
        OR Instr(post_content, 'gif') > 0 
        OR Instr(post_content, 'png') > 0;

替换iframe的链接:

UPDATE wp_postmeta 
SET meta_value=(REPLACE (meta_value, 'iframe src="http://','iframe src="//'));

剩下的事情就是看浏览器的console中有没有其他资源引用了http资源,比如我的blog就是因为之前的fedora release counter导致请求http页面了。

SSH 重用连接

有的时候需要经常 ssh 连接到服务器上,比如,我push一个分支,然后合并并push,然后push tag,然后删除该分支。期间至少需要四次SSH连接(不要问我为什么会有这么复杂的流程!)

另外,之前通过跳板机直连线上机器的方法,在差的网络环境中仍然很慢,也在考虑如何加速的问题。

综上,每次SSH连接的建立,需要首先建立TCP连接,然后认证之。有的时候网络慢,搞得这个过程很不爽,故有此文。

ssh_config的文档中有如下ControlMaster和ControlPath两个参数。ControlMaster表示是否需要多个SSH会话重用一个网络连接,这个当然要开启;ControlPath表示这个网络连接绑定到的本地sock地址。

我的配置如下:

ControlMaster auto
ControlPath ~/.ssh/%r@%h:%p

以time ssh remote_host -C pwd命令为例,在默认配置下,一般执行时间是1s;如果开启了ControlMaster,首次连接的时间是1.2s(事实上,我也不知道为什么会稍微多一点);如果重用了之前的连接,则时间降为0.6s。

Tips:

ssh -O check reomte_host可以看到共享的连接是不是还在;ssh -O exit reomte_host可以直接关闭后台共享的连接(会关闭现有的ssh会话);ssh  -O stop remote_host则表示后台共享的连接不会再接受新的会话(具体而言,不监听sock文件,下次的连接仍然会创建同样的sock文件)。

另外ControlPersist表示连接失效时间,建议设置之。

如何直接通过跳板机ssh到服务器

公司线上服务器都无法直接访问,必须通过一台跳板机来访问。比如要访问机器dev,则必须先ssh到跳板机gateway,然后再ssh到dev机器。

这样做自然可以减少攻击面,但是每次去dev机器执行命令,或者上传文件的时候,都要两次ssh,确实比较麻烦。

故Google之,可以使用ssh config中的ProxyCommand选项,比如我们先定义主机gateway的连接参数:

Host gateway
  HostName gw.example.com
  User XXX

然后我们需要定义主机dev的连接参数:

Host dev
  HostName dev.ip.example.com
  User XXX
  ProxyCommand ssh -q -W %h:%p gateway

这样,ssh dev 命令就会先和gateway建立ssh连接,并把这个中间连接当作一个代理使用。不过需要注意的是,你的公钥除了必须在gateway上有之外,还必须在dev上有,这是和之前不一样的地方。

PS:之前考虑过ProxyCommand ssh gateway nc %h %p ,但是考虑到机器上可能没有nc命令。后来发现ssh有-W参数,果断用之。

PPS:ProxyCommand参数中的-q是为了防止和跳板机的ssh连接产生多余的输出,比如不加-q就会导致每次断开连接的时候会多一句Killed by signal 1. 

如果这样做了,我们就可以玩一些高级的用法了,比如:

ssh dev "sudo tcpdump -s 0 -U -n -i eth0 not port 22 -w -" | wireshark -k -i -

这条命令在远端调用tcpdump抓包,通过管道传回本地,然后让wireshark抓包,就达到了实时抓包的效果了。这比原来的抓包存储到pcap文件中,然后两次scp传回来要好很多啊。

如何设置 nginx 和 php-fpm 的 Max open files 数量

刚开始也和同事一样,以为 Max open files 由 /etc/security/limits.conf 控制。

不过,同事顺便在 /etc/pam.d/su 文件中添加了session required pam_limits.so ,为了探究为什么要添加这一条规则,发现此设置只能在使用 su 命令切换用户的时候无效,使用 sudo 切换用户就无效了,更不用说像 nginx 这种使用 setuid 方式切换用户的了(我们可以通过cat /proc/<pid>/limits 来清楚地知道该进程的 resource limit 信息,我就是这样才知道我们一直以来对 nginx 修改的 Max open files 数量并没有生效)。

好,错误的方法终于说完了,现在我们说正确的方法:

由于使用 setuid 方式并不会通过 PAM ,所以 nginx 提供了 worker_rlimit_nofile 选项来自己定制 worker 的 Max open files 数量,比如,在 nginx.conf 中添加全局参数:

worker_rlimit_nofile 65535;

同样的,php-fpm 也有类似的参数 rlimit_files 只需要在 /etc/php5/fpm/php-fpm.conf 文件中添加该参数:

rlimit_files = 65535

即可设置 nginx 和 php-fpm 的 Max open files 数量。

Continue reading 如何设置 nginx 和 php-fpm 的 Max open files 数量

在 CentOS 7 上部署 L2TP/IPSec VPN 服务

首先安装strongswan和xl2tpd:

yum install strongswan xl2tpd

记得开启IP转发,修改 /etc/sysctl.conf 修改如下:

net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0

并重启使之生效。(执行sysctl -p 应该也是可以的,但一定要验证一下,实在不行就重启吧。)

接下来修改 /etc/strongswan/ipsec.conf 文件,如下:

config setup
 
conn %default
        ikelifetime=60m
        keylife=20m
        rekeymargin=3m
        keyingtries=1
 
conn l2tp
        keyexchange=ikev1 # IKE版本
        left=<对外IP>
        leftsubnet=0.0.0.0/0
        leftprotoport=17/1701
        authby=secret
        leftfirewall=no
        right=%any
        rightprotoport=17/%any
        type=transport
        auto=add

修改/etc/strongswan/ipsec.secrets文件(没有此文件就新建一个):

# ipsec.secrets - strongSwan IPsec secrets file
: PSK "<PSK>"

IPsec的部分就完成了,接下来是L2TP。

/etc/xl2tpd/xl2tpd.conf 文件的 [lns default] 部分如下:

[lns default]
ip range = 10.10.0.2-10.10.0.100
local ip = 10.10.0.1
require chap = yes
refuse pap = yes
require authentication = yes
name = LinuxVPNserver
ppp debug = yes
pppoptfile = /etc/ppp/options.xl2tpd
length bit = yes
bps = 1000000

PPP的部分,这里只设定了chap验证

/etc/ppp/options.xl2tpd

ms-dns  8.8.8.8
ms-dns  8.8.4.4
noccp
auth
crtscts
idle 600
mtu 1200
mru 1200
nodefaultroute
debug
lock
proxyarp
connect-delay 2500

连接密码文件 /etc/ppp/chap-secrets

# Secrets for authentication using CHAP
# client        server  secret                  IP addresses
<user>          *       <password>              *

还要记得开放500,1701和4500端口,并配置iptables转发规则。
注:没有开启firewalld的话,开启转发规则的命令如下:

iptables -t nat -A POSTROUTING -s 10.10.0.0/24 -o eth0 -j MASQUERADE

然后开启服务:

systemctl start strongswan.service
systemctl start xl2tpd.service

连接的时候选L2TP/IPSec VPN with pre-shared keys,PSK就是刚刚配置文件中的<PSK>,用户名和密码都在 /etc/ppp/chap-secrets 中。


参考链接:

http://qiaodahai.com/setup-l2tp-ipsec-vpn-on-centos.html