比尔盖子 博客

Author: 比尔盖子 (page 3 of 21)

布局与输入法独立的可能性 a.k.a 我为什么在勉强使用 Fcitx

输入源管理的统一

前两年 GNOME 开始将 iBus 集成为其的一部分,为用户提供更为一致的输入体验,引发了众多非议,包括强迫用户使用如此之烂的 iBus “输入法”(显然是把 iBus 框架和 ibus-pinyin 引擎混为一谈的),阻止用户换用其它输入框架等等。 Continue reading

PGP 邮件自动加密方案

互联网安全随着以 NSA、GCHQ 为首的大规模政府监视已经变得岌岌可危了。Google 虽然还有一些信任度,但也不是像以前一样可以完全信任了。就说电子邮件吧,自建服务器才是目前最为可行的方案。然而,一旦服务器被入侵,上面的明文邮件也会全部泄露。

只有数学还是可信的。如果我们的邮件服务器在接收到邮件的同时,对邮件进行 PGP 加密再储存(已经加密就不需要再加密了),就是一个完美的安全方案了。如果这封邮件本身还是通过 TLS 加密的 SMTP 投递过来的,那么可以说牢不可破了。我们还可以把服务器配置成一个转发服务器,继续用自己原先的邮箱服务。

原本盖子打算写一个 Postfix 的 Filter 脚本来解决这个问题,然而,盖子发现几年前,就有人编写了 Exim 和 qpsmtpd 的插件支持,都是 Perl,在此分享:

注意,这会导致垃圾邮件也会被加密。一定要配合完善的反垃圾措施使用,qpsmtpd 的反垃圾就非常强大。

GPG 密钥更换

pub rsa4096/717E3FB8 2013-03-20 [已吊销:2015-01-01]
uid [ revoked] Tom Li (Biergaizi) (My Gmail) biergaizi2009@gmail.com
uid [ revoked] Tom Li (Biergaizi) admin@biergaizi.com

吊销原因:密钥已泄漏

This key was considered as a unsafe key, because the private key was copied to multiple computers, including some semi-public computers. Although there is no any evidence shows anybody stole my private key, but I think I’d better to revoke the key and generate new keys. The new keys are E88E8D6D (crypt), 01EE20CD (sign), and 26DC385B (auth). Tom Li Thu Jan 1 16:31:50 CST 2015

服务器 SSH 密钥变更

从即刻起,服务器 SSH 的密钥变更为 c5:87:39:99:9f:1f:74:c0:36:83:44:93:c8:c3:bd:d9 (ECDSA)。请从 .ssh/known_hosts 中删除 biergiazi.com (106.187.38.29) 的旧记录,并重新核对指纹并信任服务器。同时,SSH 使用的加密算法限制也更加严格,3DES、RC4 等垃圾弱算法将不被服务器接受。

望互相转告,以免影响诸位的正常使用。

什么?你说 NSA 在 ECDSA 中植入了后门?那也比继续用之前貌似早就泄漏的 RSA 密钥强。

备注:

PuTTY 受到英国的密码学出口限制,以及专利的障碍,导致虽然 ECDSA 一直在 wishlish 中,但几年后依然没有实现,无法正常连接使用 ECDSA 的服务器。而且,PuTTY 开发早已不活跃。

推荐使用以下替代品,同为 FOSS:

  • IceIV + PuTTY

    • PuTTY 的 fork,操作与界面不变,但增加大量新特性,甚至支持 ADB
  • TeraTerm

    • 早年作为 Windows 超级终端 的替代品开发,可扩展性强,甚至支持 VBA 宏,而且默认的配色方案是好评如潮的 Solarized

想加密数据?不要购买 ACOS5-64 智能卡!

更新 1:经过 tonghuix 与官方的交涉,现在我获得了全套管理工具,和 PKCS#11 的动态库,已经可以和 gpg 联合使用了。然而灵活性还是太差,因为私有中间件只能在 x86 上工作,打算进行反向工程。

如果你熟悉并使用以 RSA 为代表的非对称加密算法,来加密文件、签名电子邮件或者登录 SSH 服务器,那么你肯定会遇到密钥便携性的问题。如果你带着个装有私钥的 U 盘四处跑,私钥什么时候被拷贝走了估计你也不会知道。

为了解决这个问题,可以将私钥放到一个黑盒里,并由黑盒完成全部的加密和签名,同时没有办法从中获取私钥,还可以设置 PIN 码。这个黑盒就是智能卡。有两种规格的智能卡可以实现上述需求:其一是 OpenPGP card,由 FSFE 主导开发,开箱即用,但很难获得;第二是各类符合 PKCS#11 标准的智能卡。

ACOS5-64 就是一款 PKCS#11 的智能卡。它不但支持非对称加密,还支持对称加密,从特性上看,是一款十分强大的智能卡。然而千万不要购买它,这卡……没!有!驱!动!对,你没有看错!

既然支持 PKCS#11,那么应该直接使用通用工具就可以了吧?图样图森破!PKCS#11 允许厂商提供一个动态库来提供 PKCS#11,而这个动态库就相当于私有驱动。而 ACOS5-64 支持 PKCS#11,却没有提供驱动。事实上,ACS 公司确实开发了相关的驱动,但附在 SDK 里,需要另行购买,然而,这个驱动究竟是 Windows Only,还是也有 so 模块我们不得而知。而且,就算有 Linux 模块,但因为这是二进制驱动,因此无法在非 x86 下正常工作。

但不同于其它卡片的是,此卡的具体规格和 Datasheet 均可获得,不需要签署 NDA。曾有一 MIT 学生在 2010 年企图在 OpenSC 框架下实现此卡驱动,然而他仅仅完成了基本功能,以及 RSA 1024 的支持,最终因为众多坑放弃开发。

也就是说,这卡在任何系统平台与应用程序上,均完全无法使用!

wget 滚动显示文件名的 Bug —— 低级错误永不可避

更新 2:wget 1.16.1 版本已包括补丁,问题彻底解决。
更新 1:我的补丁已经被接受了,Bug 已修复。

wget 是 GNU 开发的实用下载工具,最近它刚刚发布了 v1.16。

进度条样式

先介绍一下背景,wget 向来就有两种进度条,“点形”和“条形”,其中,“条形”还有可选的跑马灯效果。

刷屏点形”是指这样的效果

  0K .......... .......... .......... .......... ..........  0%  107K 8m30s
 50K .......... .......... .......... .......... ..........  0% 52.0K 13m0s
100K .......... .......... .......... .......... ..........  0% 31.3K 18m21s
150K .......... .......... .......... .......... ..........  0% 22.0K 24m4s

如果你的终端设备功能有限,不能做到即时更新屏幕内容,或者是重定向到了文件,“点形”进度条就很实用。

“条形”就是指这样的效果

filename   0%[                     ] 134.05K  59.7KB/s

很适合可以即时更新的终端。

这两种类型可以使用

wget --progress=dot
wget --progress=bar

切换。

跑马灯

现在问题来了。如果文件名称很长,条形进度条左侧那一点点空间显示不下该怎么办呢?wget 的开发者想出了一个跑马灯效果,很像大街上的 LED 横幅,不停的让文字滚动,这样用户就可以“管窥”整个文件名了。

然而,盖子发现一个特别郁闷,而且能逼死强迫症患者的问题。wget 的滚动有 Bug,文件名的最后一个字符始终不显示!就象这样:

this_is_a_
his_is_a_f
is_is_a_fi
s_is_a_fil
_is_a_file
is_a_file_
s_a_file_n
_a_file_na
a_file_nam
            <-- WTF!
this_is_a_

实现

progress.c 中,不难发现这段代码

  if (((orig_filename_cols > MAX_FILENAME_COLS) && !opt.noscroll) && !done)
    offset_cols = ((int) bp->tick) % (orig_filename_cols - MAX_FILENAME_COLS);      
  else
    offset_cols = 0;
  offset_bytes = cols_to_bytes (bp->f_download, offset_cols, cols_ret);
  bytes_in_filename = cols_to_bytes (bp->f_download + offset_bytes, MAX_FILENAME_COLS, cols_ret);
  memcpy (p, bp->f_download + offset_bytes, bytes_in_filename);
  p += bytes_in_filename;

由于有一些字符占用 1 字节,有些占用 2 字节,因此下面部分的代码全都在处理把字符转换成字节数的问题,其实这段代码做的事情很简单

 if (orig_filename_cols > MAX_FILENAME_COLS
     && !opt.noscroll  // 没有禁用跑马灯滚动效果
     && !done)  // 下载仍未结束

     offset_cols = bp->tick % (orig_filename_cols - MAX_FILENAME_COLS);

bp->tickint,每刷新一次进度条,它会就自增 1,可以把它理解成进度条刷新的次数,(orig_filename_cols - MAX_FILENAME_COLS) 就不用多说了,显然是计算文件名超出最大允许长度的字符数。

最后,从 offset_cols 开始截取 orig_filename_cols,一直截取 MAX_FILENAME_COLS 个字符。

用取余数运算来实现不断截取字符串的特性,看起来还是挺巧妙的。不过,正是这里的代码存在着问题。

范围差 1

写程序的时候,经常因为该 + 1/- 1 而忘记了(len() vs. 下标),不该加减 1 的时候乱加减,导致典型的越界往往和正确范围只差 1 位。

再比如这些令人困惑的表述

def range(begin, end)
/* 11 日到 21 日间断电 */
/* 变量取值范围 0 ~ 256 */

到底包不包括这个 end,或者包不包括 21 日,或者 256?P.S:幸好数学家们早就意识到了这个问题,发明了区间表示法,圆括号表示“排除”,方括号表示“包括”。

wget 的“最后一个字符始终不显示”的 Bug,具有典型的“范围差 1”的特征,那真正的问题到底出不出在这里呢?

实验

为了看看 wget 的这个算法到底有没有 Bug,写个程序检验一下。

FILENAME = "this_is_a_file_name"
MAX = 10

def cut(string, _min, _max):
    assert _max <= len(string) - 1 
    return string[_min:_max]

for i in range(0, 20):
    offset = i % (len(FILENAME) - MAX)
    print(offset, offset + MAX, cut(FILENAME, offset, offset + MAX))

这就是 Python 版本的简单算法实现了,运行之后:

0 10 this_is_a_
1 11 his_is_a_f
2 12 is_is_a_fi
3 13 s_is_a_fil
4 14 _is_a_file
5 15 is_a_file_
6 16 s_a_file_n
7 17 _a_file_na
8 18 a_file_nam
                 <-- WTF!
0 10 this_is_a_

果然出错?那么问题究竟出在哪里呢?拿这个例子分析一下,”this_is_a_file_name” 有 19 个字符,而最大的允许字符是 10 个。那么,那么,相差 9,看来编写者认为 9 就是需要的滚动次数。然而,滚动 9 次,就是有 10 种组合啊!

果然忘记 + 1,而接下来就不用多说了。就等开发者接受补丁了。真是低级错误不可避啊……

OpenRC 无法正确配置 IPv6

Gentoo 的 OpenRC 配置 IP 的方式是非常简洁的,只需简单修改 /etc/conf.d/net 就能完成任务。
然而,比尔盖子的 Gentoo Hardened 一直无法正确的配置 IPv6,主要表现是可以配置地址和 DNS,却认为网关是无效地址。

config_eth0="106.187.49.164/24
             2400:8900::f03c:91ff:fe73:f8c7/64
"
routes_eth0="
    default via 106.187.49.1
    default via fe80::1
"

dns_servers_eth0="106.187.34.20 106.187.35.20 106.187.36.20 2400:8900::2 2400:8900::3"

有问题的就是这个 fe80::1

今天解决这个问题,发现这个问题的成因和解决方案都简单的气人。事实上,OpenRC 支持使用不同的工具初始化网络。在默认情况下,OpenRC 初始化网络的工具是最传统的工具,这些工具对 IPv6 支持有问题。而新的 iproute2 就没有这个问题。

emerge iproute2

然后在 /etc/conf.d/net 行首加 modules="iproute2" 让 OpenRC 使用 iproute2 的工具链配置网络即可。

高性能 ext4 临时文件系统

很多时候,我们的文件系统中并不保存重要数据。比如 /var/tmp, /usr/src/usr/portage,对于这些目录,我们可以专门创建一个文件系统,并以牺牲数据安全性(因为我们不需要)换取最高性能。

如果你的 ext4 分区中储存的是 /usr/portage 这样大量小文件构成的数据库,可以使用以下参数格式化:

mkfs.ext4 -b 2048 -i 2048 -O "dir_index" /dev/sdX

-b 2048 -i 2048 确保分区中有足够的 inodes,避免因大量小文件导致明明有空间却没有 inode 的困境发生;”dir_index” 会让内核以类似 ReiserFS 的 btree 储存文件,对于大量小文件有很大的性能提升。但其它类型的数据分区,比如 /usr/src/var/tmp 请勿使用此方法格式化,请使用默认参数。

使用不安全的写入方式提升性能:

tune2fs -o journal_data_writeback /dev/sdX

禁用日志:

tune2fs -O ^has_journal /dev/sdX

使用以下挂载参数(写在 /etc/fstab):

defaults,noatime,nodiratime,barrier=0,nosuid,nodev,data=writeback

noatimenodiratime 彻底禁用一切访问时间记录;barrier=0 禁用为安全保护而设计的写入屏障;nosuidnodev 提升安全性;data=writeback 使用不安全而最高效的写入方式。

效果拔群!

服务器 SSH 端口再次变更

由于众所周知的原因,现将服务器端口号重新变更为 22000。望互相转告,以免影响诸位的正常使用。

啥?你说我为啥不用 iptables 转发端口?抵抗的行为会让服务器 IP 遭殃的……

GRUB 2 里的 play 命令

GRUB 2 中,有个 play 命令,用来让蜂鸣器播放音频。这可不是什么玩法……

它接受无限个参数,对于一个典型的三参数的调用,文档如下

play [tempo] [pitch] [duration] {pitch2 duration2...}

tempo 是全局速度,所有的 pitch 都受到它的影响;pitch 即要播放的音频,单位为赫兹;duration 是持续时间。

为了播放音乐,需要搞清楚一个调用究竟是多长时间。把 play 看作一个函数,接受 tempo 和 duration 两个参数。经过试验:

tempo duration 时间
60 1 1 s
60 2 2 s
30 1 2 s
30 2 4 s
120 1 1/2 s
120 2 1 s

从这里大概可以看出来,

时长 (s) = 60 / t * d

这样,如果使 t = 60000,d 就正好对应于 0.001 s,即一毫秒。这样,就可以轻松的将基于毫秒时长的 Linux 下 beep 调用翻译成 GRUB 2 的 play 调用了。

另外,这还可以用于 sleep。GRUB 2 提供的 sleep 分辨率只有一秒,但使用 play,让 pitch = 0 Hz,就曲线实现了毫秒级的 sleep 命令。

Olderposts Newerposts

Copyright © 2023 比尔盖子 博客

Theme by Anders NorenUp ↑