比尔盖子 博客

Sticky post
不成熟的优化是万恶之源。

by Donald E. Knuth

x86 的六种重启方法

31337 h4x0r 有一回对我说道,“你读过 x86 平台的底层代码么?”我略略点一点头。他说,“读过底层,……我便考你一考。x86 的电脑上,怎样重启的?”我想,脚本小子一样的人,也配考我么?便回过脸去,不再理会。

31337 h4x0r 等了许久,很恳切的说道,“不会重启电脑罢?……我教给你,记着!这些方法应该记着。将来做黑客的时候,开发要用。”我暗想我和黑客的等级还很远呢,而且我们编程序也不是用来重启机器;又好笑,又不耐烦,懒懒的答他道,“谁要你教,不就是 ACPI 去写一个 ResetReg 么?”

31337 h4x0r 显出极高兴的样子,将两个指头的长指甲敲着键盘,点头说,“对呀对呀!……x86 有六样重启法,你知道么?”我愈不耐烦了,努着嘴走远。

31337 h4x0r 刚启动了 Emacs,想在编辑器上打字,见我毫不热心,便又叹一口气,显出极惋惜的样子。

Linux 内核支持全部六种方法……但为什么要支持六种?

有一些重启方法可能存在硬件实现问题,例如 8042 键盘控制器的方法,是支持最广泛,但也是电脑硬件 bug 最可能发生的方法。尽管 Linux 内核会逐一尝试每一种方法,很可能在尝试其中一种后导致硬件卡死。由于这些机器多半只在 Windows 上测试过,因此直到 2010 年左右,在 Linux 上重新启动机器时无法断电依然是比较常见的问题。此时,用户就需要逐一尝试这些重启方法,找到一种能用的,并向上游报告,上游将机器的型号和需要的重启方法加入表中。在现在出产的新机器上,这种问题已经非常罕见。如果你日后遇到了重启的问题,希望你能想起这篇文章,并对你有所帮助。

没错,六种方法重启,而且每种都不一定能工作,重启时需要逐一尝试,必然时候还需要查表,这就是充满历史遗产的 x86 世界的现状。

方法一:8042 键盘控制器的错误使用方法

8042 键盘控制器控制 x86 计算机上的 PS/2 键盘鼠标,但这大概也是 x86 计算机上能力最强大的硬件设备,因为它名义上是键盘控制器,但由于 IBM 工程师的脑洞,实际上还拥有控制 CPU 和内存的功能。历史上,8042 与 CPU 的 Reset 信号相连,还与内存总线上的一个逻辑门相连。

向 8042 键盘控制器(端口 0x64)写入 0xFE. 这会触发 CPU 的重置信号,并重启计算机。

ioperm(0x64, 1, 1);
outb(0xfe, 0x64);

在 Linux 上,可以使用内核参数 reboot=kbd,让 Linux 内核使用这种方式实现重新启动的功能。

方法二:向 PCI 的 0xCF9 端口发送重置信号

Intel 的 ICH/PCH 南桥芯片同时负责部分电源工作。向 PCI 的 0xCF9 端口发送重置信号,可以要求南桥芯片重启计算机。

ioperm(0xcf9, 1, 1);
uint8_t reboot_code = 0x06                 /* 热启动,冷启动 0x0E */
uint8_t cf9 = inb(0xcf9) & ~reboot_code;
outb(cf9|2, 0xcf9);                                    /* Request hard reset */
usleep(50);
outb(cf9|reboot_code, 0xcf9);            /* Actually do the reset */

在 Linux 上,可以使用内核参数 reboot=pci,让 Linux 内核使用这种方式实现重新启动的功能。

方法三:让 CPU 产生三重异常

如果代码触发了 CPU 的一个异常,并触发了事先注册的异常处理代码,但是异常处理代码本身也触发了异常,这就叫做双重异常。但这个异常处理代码本身也注册了异常处理代码,而且在执行这个异常处理代码的异常处理代码时又触发了异常,就产生了三重异常。从 80286 开始,三重异常会导致 CPU 进入 SHUTDOWN 状态,主板电路会重新启动系统。

例如:

load_idt(&no_idt);              /* 清空中断描述表 */
__asm__ __volatile__("int3");   /* 触发中断 */
/* 此时发生中断,由于 IDT 为空,无论是中断的异常处理代码还是双重异常处理代码都不存在,触发了三重异常
/* CPU 进入 SHUTDOWN 状态,主板重置 */

在 Linux 上,可以使用内核参数 reboot=triple,让 Linux 内核使用这种方式实现重新启动的功能。

尽管这个特性就在 80286 的手册附录里,而且 IBM 的工程师看到了而且还实现了必要的电路,然而 IBM 的工程师完全没有意识到这可以用来在切换到实模式时重置 CPU,而是将键盘控制器的一个端口接到 CPU 的 Reset 信号线上来进行重置 CPU 实现模式切换……

这个方法的成功率几乎是最高的,但大多数操作系统(包括 Linux 内核)不到万不得已,不会使用。虽然原则上这是为了正确进行电源管理,然而事实上的原因是,三重异常往往意味着操作系统出现了严重的故障,例如将内存中负责处理 Swap 的代码本身也移动到了 Swap 里,会立刻导致整个系统重启,数据荡然无存,开发者本来对三重异常感到恐惧,然而现在却要自己主动触发一个,实在是不能接受。此外,开发者也都认为触发个三重异常让 CPU 被迫重置实在是太残忍了,不忍心下手……因此,这种方法只会被开发者当作最终的手段。

方法四:切入实模式,跳转到 BIOS 代码重启

禁用各种东西,将 CPU 从保护模式切换回实模式,然后 ljmp $0xffff, $0x0000 跳转到 BIOS 的重置代码。 至于 BIOS 怎么重启,谁知道呢?不过只要 BIOS 实现正确,就可以工作(仅限于 32 位计算机)。

例如:

movl %cr0, %eax
andl $0x00000011, %eax
orl  $0x60000000, %eax
movl %eax, %cr0
movl %eax, %cr3
movl %cr0, %ebx
andl $0x60000000, %ebx
jz   f
invd
f:   andb $0x10, al
movl %eax,    %cr0
ljmp $0xffff, $0x0000

在 Linux 上,可以使用内核参数 reboot=bios,让 Linux 内核使用这种方式实现重新启动的功能。

方法五:通过 ACPI 重新启动

ACPI 是现代 x86 计算机必不可少的标准电源管理接口,自然也提供了重新启动计算机的功能。

在支持 ACPI 的机器固件提供的「固定 ACPI 描述表」(Fixed ACPI Description Table, FADT)中,保存有 reset_registerreset_value 两个数值,告知操作系统重启的方法。

rr = &acpi_gbl_FADT.reset_register;      /* 获取重启寄存器   */
reset_value = acpi_gbl_FADT.reset_value; /* 获取重启魔法数值 */

/* The reset register can only exist in I/O, Memory or PCI config space
 * on a device on bus 0. */

/* 在 PCI、内存和 IO 地址中寻找重启寄存器 */
switch (rr->space_id) {

/* 如果寄存器在 PCI,就写 PCI */
case ACPI_ADR_SPACE_PCI_CONFIG:
    /* The reset register can only live on bus 0. */
    bus0 = pci_find_bus(0, 0);
    if (!bus0)
        return;
    /* Form PCI device/function pair. */
    devfn = PCI_DEVFN((rr->address >> 32) & 0xffff,
              (rr->address >> 16) & 0xffff);
    printk(KERN_DEBUG "Resetting with ACPI PCI RESET_REG.");
    /* Write the value that resets us. */
    pci_bus_write_config_byte(bus0, devfn,
            (rr->address & 0xffff), reset_value);
    break;

/* 如果是内存或者 I/O 地址,直接用 writeb() / outb() 写内存或 I/O */
/* acpi_reset() 调用的是 acpi_os_write_port(), acpi_hw_write() */
/* 但只是多了一些检查,最后依然调用的是 writeb() / outb() */
case ACPI_ADR_SPACE_SYSTEM_MEMORY:
case ACPI_ADR_SPACE_SYSTEM_IO:
    printk(KERN_DEBUG "ACPI MEMORY or I/O RESET_REG.\n");
    acpi_reset();
    break;
}

写入数值后触发硬件重启。 在 Linux 上,可以使用内核参数 reboot=acpi,让 Linux 内核使用这种方式实现重新启动的功能。

方法六:通过 EFI/UEFI 重启

EFI/UEFI 是更现代计算机固件的接口标准,其中包括 UEFI 启动服务和 UEFI 运行服务,它们提供了一组标准的函数调用,提供一些和硬件相关的服务。好比系统调用为应用程序提供服务,UEFI 服务则为操作系统提供服务。在操作系统启动后,会运行 ExitBootServices 禁用 UEFI 启动服务,但是 UEFI 运行服务的代码会一直处于内存中,而且随时可以被系统调用执行。

efi_mode = EFI_RESET_WARM;
/* 或 efi_mode = EFI_RESET_COLD; */

/* reset_system 是 efi 结构体中的函数指针,其原型是:*/
/* void efi_reset_system_t (int reset_type, efi_status_t status, unsigned long data_size, efi_char16_t *data); */
/* 系统在通过 EFI 启动时,正确的内存地址会被初始化 */
efi.reset_system(efi_mode, EFI_SUCCESS, 0, NULL);

由于 EFI 的高度复杂性,可以直接阅读 EFI 相关文献。 在 Linux 上,可以使用内核参数 reboot=efi,让 Linux 内核使用这种方式实现重新启动的功能。

Linux 内核的逐一尝试

Linux 内核中,默认使用一个无限循环的 switch {} 语句逐一执行这些重启方法,如果成功了,那么机器就会重启,否则就会切换到下一种方法。如果硬件有已知问题,Linux 内核会查表,并根据机器型号选择合适的方式,并可能会进行一些 workaround。最后,有专门支持的特定计算机硬件会使用硬件自身的方法重启。

for (;;) {
    switch (reboot_type) {
    case BOOT_ACPI:
        // ...
        reboot_type = BOOT_KBD;
        break;

    case BOOT_KBD:
        /* 代码 */
        if (attempt == 0 && orig_reboot_type == BOOT_ACPI) {
            attempt = 1;
            reboot_type = BOOT_ACPI;
        } else {
            reboot_type = BOOT_EFI;
        }
        break;

    case BOOT_EFI:
        /* 代码 */
        reboot_type = BOOT_BIOS;
        break;

    case BOOT_BIOS:
        /* 代码 */
        reboot_type = BOOT_CF9_SAFE;
        break;

    case BOOT_CF9_FORCE:
        port_cf9_safe = true;
        /* Fall through */
    case BOOT_CF9_SAFE:
        /* 代码 */
        reboot_type = BOOT_TRIPLE;
        break;

    case BOOT_TRIPLE:
        /* 代码 */
        /* We're probably dead after this, but... */
        reboot_type = BOOT_KBD;
        break;
    }
}

为什么用键盘控制器能重启电脑?

随着个人计算机和半导体技术突飞猛进的发展,到了 1984 年,IBM 发表了具有划时代意义的 IBM PC/AT 计算机(即 IBM 5170)。PC/AT 搭载了强大的 80268 CPU,支持的内存也从 20 位的 1 MiB 变成了 24 位的 16 MiB。这一扩展地址可就惨了。在旧的 8086 上,尝试访问大于 1 MiB 的内存,会溢出回到内存的开头,后来有程序员也发现了这个现象,并利用它优化程序。但在 80286 上,由于大于 1 MiB 的内存确实存在,因此不会溢出也不会回到内存开头……本来 80286 默认使用的是实模式(而不是新的保护模式),就是为了完美兼容现有的 8086 程序的,但由于 Intel 并没有意识到这个问题,因此导致大量现有的程序停止工作。

IBM 的工程师为了解决这个兼容问题,他们在主板的内存地址总线的信号线上装了一个逻辑门,作为信号开关。当关闭时,就会一直产生信号 0,相当于关闭了超过 1 MiB 的内存,同时由于电路的特点,也能让这个溢出魔法重新恢复正常。但是,在哪里控制这个开关呢?PC AT 计算机使用 8042 芯片控制键盘,工程师看到芯片上正好有多余的端口,于是脑洞大开,用这个键盘芯片来控制 1 MiB 内存的开启。这就是著名的 A20。至今,让键盘控制器开启 A20 依然是进入保护模式不可或缺的一步。

同时,由于很多时候还需要在系统里兼容旧程序,有时还需要进入新的保护模式后,再切换回旧的实模式,但 80268 进行这个模式切换时,必须进行 CPU 重置。于是,IBM 的工程师把 8042 键盘控制器上另一个端口,连接到了CPU 的 Reset 信号线上……直到今天,x86 计算机上依然需要兼容 AT 机的 8042,因此,8042 键盘控制器能用来重启几乎一切 x86 电脑。

后来发现,键盘控制器的反应速度实在是太慢了,如果需要频繁 A20 切换会有性能问题,因为 IBM 当初的这个坑爹设计,日后系统开发者和 Intel 还花费了很大的代价解决。到了今天,x86 处理器里有一个专门模拟 A20 的 I/O 口……

如果说 8086 芯片是通往 x86 大坑的一把洛阳铲,那么这台 IBM 电脑则是造就了 x86 体系无数大坑的挖掘机。觉得 AT 很陌生?那在 AT 后面加个「X」呢?此外,你垃圾键盘右上角的 3 个刺眼的指示灯,Windows 用户不知道有什么用的 SysRq 按键,以及主板里一没电就掉时间日期,还时不时漏液损坏主板的纽扣电池,还有那经常出现诡异问题不能正常引导系统的 MBR,全都是 AT 的设计。更别说那个电源适配器负载一低就没有 +12V 输出,于是 IBM 在低端机器上安装了个 50 W 的功率电阻用来费电,幸好这一点上我们没有保持兼容……无论怎么说,这开启了一个时代,你现在的计算机很可能依然或多或少的兼容 AT。我们所说的「IBM 兼容机」指的就是 PC/AT 兼容。

GnuPG 2.1.18 不识别智能卡的解决方法

如果你通过 PC/SC 访问 OpenPGP Card,在升级到 GnuPG 2.1.18 后,你的智能卡将会停止工作。智能卡将无法被 gpg --card-status 正常识别,提示各类错误,例如:

gpg: selecting openpgp failed: No such device
gpg: OpenPGP card not available: No such device

在 GnuPG 中,本身就通过 ccid 支持了标准的 OpenPGP Card,一张标准的智能卡和兼容的读卡器可以直接和 GnuPG 一起工作,无需修改任何配置或安装任何程序。

但是,很多 OpenPGP Card 并非完全遵守 v2 标准,例如开发版本的 gnuk(遵守 v3 标准);或者 Yubikey(带有扩展特性),我们就需要通过 PC/SC 作为中间层访问智能卡。

这时,竟态条件就产生了。初次使用智能卡时,GnuPG 会和 PC-SC 抢占智能卡设备的控制权,过去 PC-SC 总是赢家,偶然,如果 GnuPG 赢了,智能卡就会无法正常工作。

因此,可以通过禁用 GnuPG 内建的智能卡支持,解决这个问题。

echo "disable-ccid" >> ~/.gnupg/scdaemon.conf

重启 pcscdgpg-agentscdaemon 后见效。如果你是 PC-SC 用户,建议总是禁用 ccid 避免冲突。

那么为何升级到 2.1.18 之后才会出现这个问题?日本 FSIJ 的 Niibe Yutaka,也就是 gnuk 的核心开发者主动跳出来表示,他把一个 bug 添加进了 scdaemon。以前,scdaemon 发现内建的 ccid 无法识别智能卡后,会顺便调用 PC/SC 再试试看,但重构代码时忘记保留这个特性,他已经编写了这个补丁修复了问题,而且已经被 Debian 采纳。

来源:https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=852702

最近的 OpenSSH 漏洞究竟有多大危害?

OpenSSH 刚刚更新到了 7.4 版,修复了已公开的 CVE-2016-10009, CVE-2016-10010, CVE-2016-10011, CVE-2016-10012 以及一个没有 CVE 的漏洞。其中,CVE-2016-10009 可以用来“任意执行代码”,因此,强烈建议大家立即更新所有的服务器,而且客户端也不能除外。但是,这些漏洞究竟有多大危害呢?是否可以利用这些漏洞控制服务器呢?我们一起来仔细看看:

CVE-2016-10009:sshd 服务器可以利用转发的 agent-socket 文件欺骗本机的 ssh-agent 加载一个恶意 PKCS#11 模块,任意执行代码。换句话说,是恶意服务器在客户端的机器上远程执行代码。

谁让你连恶意服务器的????

CVE-2016-10010:关闭 UsePrivilegeSeparation 权限隔离功能时,ssh 转发的 socket 文件将以 root 权限创建。

谁让你关闭权限隔离的????UsePrivilegeSeparation sandbox 是标准配置。

CVE-2016-10011:即使打开权限隔离,假如攻击者可以让 sshd 创建的子进程提权,那么服务器私钥理论上可能通过 realloc() 的过程泄漏给子进程。

泄漏私钥,听上去有点危险。前提是这攻击是理论上的,要使用,你得先有办法让子进程提权啊?????你有办法吗???再者,你们的 sshd 都应该开前向安全了吧……

CVE-2016-10012:sshd 服务器如果启用了认证前压缩(也就是 Compression yes),其中的某些边界检查可能会被编译优化去除,导致攻击者有可能从经过权限隔离的子进程中攻击高权限的父进程。

谁让你打开认证前压缩的?你当 CRIME 攻击是闹着玩的啊????CRIME 攻击之后 sshd 的默认配置都是 Compression delayed,也就是认证后压缩,如果你这都不知道那你的服务器就已经不安全了。再者,从子进程中怎么攻击父进程?目前还没有已知的方法。

无名漏洞AllowUsersDenyUsers 选项可以接受无效的 IP 段。假如管理员想拉黑用户,但却输入了无效的 IP 段,那么用户就不会被禁止登录,而管理员也不知道。

谁让你把 IP 段输错了?????再者,你拉黑恶意 IP 不使用 iptables 防火墙吗???


可见,这些漏洞确实不容忽视,强烈建议所有人立刻更新。然而,如果你想搞个大新闻说利用这些漏洞可以控制服务器,那还是图样图森破了。如果你平时在运维服务器的时候已经遵循了最佳安全实践,那么其实际危害是很低的。

新服务器密钥指纹

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

* RSA
    * SHA256 | DSsHk7JuE8iNPV9yVESiRptOPv4eepQKpWxaDoOk9m4
    * MD5 | 92:a7:2f:a8:07:76:3f:f7:03:f2:54:e9:06:ea:19:9e

* Ed25519
    * SHA256 | MTJ6TdQezosVmHLR258Fp967mSZlfK96V4oQprP96qw
    * MD5 | 6f:b2:9b:0d:05:51:12:25:23:02:ea:74:33:3d:28:d5
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEJVIRsjlaWj4OSKDx+tPrBeiOjW0FAlhLgf8ACgkQ+tPrBeiO
jW2raw/9HKZ2c9tjs/u001P1TL7+sLriK12SzBkXR7jfTCRjXAR4Qxh9QnU5hZ8/
tnXH5qmKQxQXTrZqZtkbsNNQbwLBT5c0N7ZfYoJAh1occveAOaE9z6W7CT/8y1H5
1KiYXKhWs0ShbFs+VT9yasvgwGInTjERvXUk9JBa5DCjz1tFBDAzE51h5fQD8CA2
5USJHMbx4wDVAskZ5uSeXWV+xMY1U9JgDbZeSefFJLV+rzd1tt9rCWrgz7lQRDmT
ufQ8hMOX3Dkp6Zllyi1OVrxbep8zMSZgRPaIhcdnIG9A+9dBRQ5QJV6uBzMocXlP
FlSjqtlGqjCdcdqQg7ka9fMBhox/hE34lKeNdubT9TVv7jWy8EmUxUhIKd+72wUC
9sdt6g/yLeboPa88YwKH9LvmOlsJS69P4m40i4rJprZSaERyNTkGP1GoFl167v1k
nDIrwkmI6Os53H1rt7e3jYbgg/cJxOF7+734T+RbVsHO+t/GvgoD9OCP3avpzbF/
SNl4rGBlNbbqN7Trw4a8w+SRl3FLiH6HSt64xrNqhttb/wJHO0SmR/aaXqYVa3Og
VKCaXgIuOqaOw3aDuoSt4Qh//BDRInC/89QLEbHBxjvFxnGF+BVIg3Kij3lVIlXi
cJmkh+a4j94KfTvV3ZQkM61jTkeASAUc2UP10cZTPABinUT1fdk=
=0t8H
-----END PGP SIGNATURE-----

使用 cryptsetup 加密 swap

Swap 分区不利于系统安全。当 RAM 中的数据被写入到硬盘等永久存储设备时,就会面临更高的数据泄漏风险。如果内存中的密钥恰好被写入到了 Swap 中,就大大降低了攻击者的难度,只需要简单得进行数据恢复,无论是什么加密算法都不在话下。不过,随着现在的计算机内存容量越来越来,Swap 的使用已经没有那么广泛了。

不过,很多情况下 Swap 依然是非常有用的,这时候我们可以使用 cryptsetup 对 Swap 加密一下消除安全隐患,整个过程非常简单,也不需要记忆任何的密钥。作为副作用,这意味着系统无法在关机后解密 Swap,因此无法休眠了,此外,写入 Swap 时的 CPU 占用率也会有所上升,但如果你的 CPU 支持 AES 指令集,影响会十分有限。

思路非常简单,首先,我们使用 cryptsetup 使用一个随机密钥打开 Swap 分区或文件,然后,再将 cryptsetup 在 /dev/mapper/ 中创建的抽象设备作为 Swap 使用。由于密钥是随机的,因此每次抽象设备中的数据也都是完全随机的,我们在使用之前还需要重新 mkswap 创建 Swap。

我们假设已经有一个 Swap 文件位于 /swap,因此抽象设备将是 /dev/mapper/swap。如果你使用其他文件或分区自然没有问题,记得手工执行 cryptsetup 并观察一下 /dev/mapper 里的设备叫什么名字。如果你想自己创建 /swap,别忘了在创建后使用 chmod 600 /swap 保护原始文件。

cryptsetup open --type plain /swap swap --key-file /dev/urandom 
chmod 600 /dev/mapper/swap 
mkswap /dev/mapper/swap
swapon /dev/mapper/swap

创建此脚本,赋予可执行权限,并执行此脚本,Swap 立刻可以使用了。由于这是一个一次性 Swap,我们并不需要关闭它,但如果测试需要,可以这样:

swapoff /dev/mapper/swap
cryptsetup close /dev/mapper/swap

为了方便,我使用了一个 Systemd 的 Unit 文件来做到自动挂载。

[Unit]
Requires=swap.target

[Service]
ExecStart=/usr/local/sbin/encrypted-swap
Type=oneshot

[Install]
WantedBy=multi-user.target

其中 Requires=swap.target 代表此脚本在 Systemd 执行内建的 swap 服务后执行,避免可能的竞态条件;/usr/local/sbin/encrypted-swap 代表脚本的位置;Type=oneshot 代表此服务只运行一次,然后立刻退出。首先关闭现有的 Swap,把此文件命名为 encrypted-swap.service,并 systemd start encrypted-swap 看看是否正常,若正常便可 systemctl enable encrypted-swap 开机自启动了。如果你有现有的自动挂载 Swap 配置,如 /etc/fstab,你需要手工取消,避免冲突。

此外,cryptsetup 加密 plain 文件的默认算法是 aes-cbc-essiv:sha256,没有安全问题。你可以使用 cryptsetup --help 查看默认的 plain 类型加密算法。

Linode Xen 下 grsecurity >= 4.3 崩溃的解决

更新:官方已在 grsecurity-3.1-4.7.4-201609152234.patch 中修复问题,不再需要此 workaround。

自从 Linux 4.3 开始,在 Linode 上使用 PaX/grsecurity 时,内核会在被 pv-grub 执行后不久立即崩溃。由于崩溃是在启动后极早期立刻发生的,没有任何可以用来调试的日志,同时公司也不是盖子开的,也没有办法得到母机上有意义的调试信息。这导致了盖子的 VPS 内核从去年 12 月开始被锁定在 4.2.7。由于不知什么时候产生了 Linode 东京机房会在 2016 年 6 月从 Xen 迁移到 KVM 的错觉,也没有花精力去尝试调试这个问题。

Continue reading

自由软件基金会的历史包袱 —— GFDL

当我们需要发布文档的时,可以选择有许多不同的许可证。事实上,老牌的 MIT、BSD 或 GPL 均可以用于任何作品,包括文档在内;但你可能会说,这些都并非专门为文档制定,因此需要一些更合适的,没问题,你可以立刻加入 CC 协议豪华午餐;但你可能又会说,你已经采用 GPL 许可证作为了项目的主许可,采用另一个组织发布的许可证,不太整齐划一,因此希望使用 GFDL 许可证。

这并非没有道理,《GNU 自由文档许可证》是 FSF 专门为文档量身定制的。如果你是 FSF 的支持者,FSF、GNU 和自由看上去都会是首选,更何况这也是一个老牌的许可证,比 AGPL 等许可证的岁数都大得多,看上去也挺靠谱的。然而,在 21 世纪 10 年代的今天,GFDL 已经不再是好选择,我将通过这篇文章证明这点。

Continue reading

网络维护日

日子快到了。

当年,HD DVD 密钥被泄漏了,随后就遭到的财阀的施压,导致这个能破解拷贝 DVD 的密钥到处被封杀。于是,美国网民创作了一面“言论自由之旗”,这面旗帜把遭到封杀的高清 DVD 密钥用旗帜上的颜色条表示了出来,从而绕过了审查。

而如今一到了中国互联网维护日,各种形式的自我审查就会接踵而至。很明显,中国的网民也需要自己的言论自由之旗。受到美国网民的启发,我设计了“中国版言论自由之旗”,同样象征言论自由。这是一面纯色的旗帜,旗帜颜色的 HTML 编号为 #890604。出乎意料很巧合的,这是一面暗红色的旗帜。在就连“蜡烛”图标都会遭到屏蔽的网络环境下,一种颜色就可以表达无声的抗议。

Chinese Free Speech Flag

本文章和这面旗帜均采用公有领域授权,尺寸比例是 2:3 或 3:5。请将本文转载到你的博客上;并将本旗帜和传播到社交网络的四面八方。没有任何文字说明,也不会遭到封杀,是蜡烛的绝佳替代品。同样,大家可以在任何艺术作品/设计中使用颜色 #890604。只需要注意一点:很多时候输出或上传的图片会被无损压缩,甚至是上传后的二次无损压缩,因此请多多测试,选择适当的格式传播。通常,gif 格式的图片不会被二次压缩破坏信息。SVG 矢量图的版本可以在维基共享里找到

Chromium 还是 FireFox?

比尔盖子从去年开始进行全系统加固,无论是个人电脑还是服务器都部署了 Gentoo Hardened,并采取了不同程度的安全措施。Chromium、FireFox,以及基于 Webkit 的外壳浏览器是自由软件界的御三家,从特性上来说各有独自的优势 —— 如果要从这一点上进行谈论恐怕会触发圣战,这也并不是比尔盖子撰写本文的目的。本文企图从系统安全的角度来说明浏览器的选择。

Continue reading

« Older posts

Copyright © 2017 比尔盖子 博客

匿名浏览:http://x4wttqqrkud5pttgqlpxgevtr4rbqpa6lkwdiw3o3m6q4deeldgq.b32.i2p
警告:残留有明网混合资源,访问前请自行屏蔽明网流量

Theme by Anders NorenUp ↑