安装 Hexo

安装 Node.js#

访问 Node.js 官方网站,下载最新版 Node.js 安装包。安装。

安装 Hexo#

按照 Hexo 官方网站 首页的安装指示。

1
2
3
4
5
npm install hexo-cli -g
hexo init blog
cd blog
npm install
hexo server

上面的 5 行命令

  1. 全局安装 hexo-cli 工具包
  2. 创建一个文件夹 blog,里面包括 Hexo 的配置文件、博客的 Markdown 文件、主题的模板文件,以及 Hexo 用到的 node_modules
  3. 进入这个文件夹
  4. 安装 package.json 中的 node_modules
  5. 运行本地服务器,查看一下结果,进入 http://localhost:4000/ 即可查看博客的模样了。

写一篇文章#

blog 文件夹下打开命令行,

Windows 中,在文件浏览器中,按住 Shift 点鼠标右键可以显示高级的功能菜单,里面有“在此处打开命令窗口”或“在此处打开 Powershell 窗口

执行

1
hexo new post this-is-a-blog-s-title

这样会新建一个 source/_posts/this-is-a-blog.md

里面会有一些自带的元数据信息,格式如下。

1
2
3
4
5
6
7
title: 这是一篇博客
date: 2018-10-21 19:40:15
tags:
- hexo
- node.js
- npm
- git

安装 Git#

访问 Git 网站,下载最新版 Git for Windows 安装包。安装。

发布到 GitHub Pages#

在 GitHub 新建一个仓库,命名为你的 GitHub 用户名加上 .github.io(例如:ganlvtech.github.io

然后在 _config.yml 中修改 deploy 的参数。

1
2
3
4
deploy:
type: git
repo: git@github.com:ganlvtech/ganlvtech.github.io.git
branch: master

在国内使用 Disqus 的小技巧

Disqus 是一个的评论社区。它可以通过 iframe 提供嵌入式的评论区。可以用于静态网页。

但是在中国会被墙。

问题引出#

disqus.com 似乎并没有被墙,只有 *.disqus.com 被墙了。

恰巧 Disqus 的评论数据是从 disqus.com 获取的。

也就是说,通过一点小技巧就可以不翻墙用 Disqus。

解决方法#

Disqus 默认使用一个 script 标签来获取数据

1
<script src="//ganlvtech.disqus.com/embed.js">

其实如果阅读代码后可以发现,这个 embed.js 并不会变化。他只是用来搜索所有 script 标签的 src 属性,匹配出类似 ganlvtech 的 shortname。然后利用这个 shortname 来获取评论数据。

又恰巧,disqus有一个没有被墙的 CDN c.disquscdn.com

那么我们把上面的代码改成

1
2
3
4
<script>
var disqus_shortname = "ganlvtech";
</script>
<script src="//c.disquscdn.com/embed.js">

此外还必须添加几条 hosts

1
2
3
151.101.0.134 disqus.com
104.16.80.166 c.disquscdn.com
151.101.26.49 a.disquscdn.com

然后就可以不翻墙使用了。

效果并不是不太好,仅仅只是能用。

Hexo#

Hexo 默认主题 landscape 是支持 Disqus 的,简单地配置一下。

_config.yml 第一级添加

1
disqus_shortname: ganlvtech

在需要打开评论的文章前面的元数据中加上

1
comments: true

themes\landscape\layout\_partial\after-footer.ejs 中把

1
dsq.src = '//' + disqus_shortname + '.disqus.com/<% if (page.comments) { %>embed.js<% } else { %>count.js<% } %>';

改成

1
dsq.src = '//c.disquscdn.com/<% if (page.comments) { %>embed.js<% } else { %>count.js<% } %>';

TODO#

count.js 应该也需要改一下,否则仍然无法使用。因为它依然需要从 ganlvtech.disqus.com 加载脚本。

使用 rel=noopener 打开外部链接

问题引出#

1
<a href="/assets/2018-10-18-external-link-rel-no-opener/malicious.html" target="_blank">在线演示</a>

这种链接的写法看似没有什么问题。

但是如果这个链接的目标地址并不是自己可控的呢?比如下面这个链接

在线演示

我们预期的结果是,打开一个新的页面。但是实际的结果是,的确打开新的页面了,但是原来的页面被更改到另一个网址上了。

代码#

模拟这个问题需要 3 个网页。

index.html#

在原来的论坛、贴吧、空间之类的,发一篇帖子,留下这样一个外部链接。

1
2
<h1>正常页面</h1>
<a href="/malicious.html" target="_blank">恶意外部链接</h1>

需要这里面的链接地址改成自己的页面的绝对地址。

malicious.html#

这个页面利用 opener 指向原来网页的 window 问题,将原来的网页变成了钓鱼页面,用户不注意的话就上钩了。

1
2
3
4
5
6
<h1>一个看似没有威胁的页面</h1>
<script>
if (window.opener) {
opener.location = '/index.html';
}
</script>

phishing.html#

这个就是一个钓鱼页面,或者是点击劫持页面。没有什么特殊之处。

1
2
3
4
5
6
<h1>钓鱼页面</h1>
<form>
<p><label for="username">用户名:</label><input type="text" id="username" name="username"></p>
<p><label for="password">密码:</label><input type="password" id="password" name="password"></p>
<p><input type="submit" id="submit" name="提交"></p>
</form>

说明#

虽然 opener.document 会受跨域限制,opener.location 也同样因为跨域不可以读取,不存在数据泄露的问题,但是 opener.location 是可写的,也就存在钓鱼的可能。

解决#

1
<a href="/assets/2018-10-18-external-link-rel-no-opener/malicious.html" target="_blank" rel="noopener noreferrer">rel noopener noreferrer 的链接</a>

rel noopener noreferrer 的链接

就是在 a 标签增加了一个 rel="noopener noreferrer" 属性,noreferrer 是为了兼容旧版浏览器,新版浏览器两种应该都支持。

参考链接#

Chrome Local Override 本地文件替换

问题#

  1. 我们修改前端的时候,经常遇到一种情况。本地的测试服务器的数据并不是很充足,很多情况是线上的数据引发的问题,如果把线上的数据离线下来会很麻烦。

  2. 比如,我帮别人改他的网站的 js、css,我并不能访问他的数据库。如果我想模拟线上环境,要么爬取数据,要么自己生成假数据。

  3. 还有,如果我想改别人网站的 js 代码,想简单调试一下,最后再做一个插件。我并不想把他整个网站都离线下来,然后本地测试。我想做的只是改某一个文件。

  4. 另外,比如 ajax.googleapis.com 被墙了,我想使用 ajax.proxy.ustclug.org 的镜像代替对应的文件。

解决方案#

我大约两年前发现的 gooreplacer。这个 Chrome 插件,这个插件自动把被墙的 GoogleApis 文件,通过 Chrome 提供的 307 Internal Redirect,在发送请求前直接重定向到新地址。然后访问 Stack Overflow 的时候就可以正常加载脚本了。

gooreplacer GitHub repo

gooreplacer Chrome Web Store

Chrome Local Override#

我大约从 Chrome 59 以来,就不止一次在 Bing、Google 搜索这个问题。直到几天前我找到了一篇 Stack Overflow 文章,Override Javascript file in chrome

从 Chrome 65 以来 Chrome’s DevTools 的 Source 页面多了一个 Local Overrides 选项卡。

其他 Chrome 插件#

除了 gooreplacer,上面的 Stack Overflow 回答中还给出了 RequestlyResource Override 等诸多插件。我简单的看了一下他们。

Requestly 这个插件功能很全,能做的事情非常多,还可以改 User Agent 之类的。

Resource Override 这个就很简洁,只能替换 URL,功能其实和 gooreplacer 差不多,只不过替换规则需要自己输入。

另外,安装这些插件之后,Chrome 有同类插件推荐。我才知道,应用商店中这种 URL Redirector 类型的插件这么多。

我没有详细对比过这些插件。因为功能差不多,我倾向于选择开源并且代码简洁,占用资源少的。

插件 v.s. DevTools#

虽然插件是“即插即用”的,这样很方便,但是也有这些插件做不了的事情。那就是跨域。js、css 文件当然不会被 Same-origin policy 所拦截。但是 Ajax 可就不一样了,Ajax GET 的内容会被拦截。通过 Chrome DevTools 提供的 Local Override 功能简单方便快捷,对开发人员友好。

安装 Pure-FTPd 并启用 TLS

我使用的系统是 Ubuntu Server 18.04 LTS。

其实我用的是 Laravel HomesteadVagrant Box,一个非常方便的虚拟机。

本文参考了 Pure-FTPdUbuntu Community Wiki

安装 Pure-FTPd#

1
sudo apt install pure-ftpd

查看一下端口#

这是一个很常见的指令

1
sudo netstat -tlnp
  • t 表示 TCP,只显示 TCP 连接
  • l 表示 listening,只显示正在监听的 Socket
  • n 表示 numeric,就是只显示 IP 数值,不解析域名
  • p 表示 programs 显示 PID/Program name

可以看到其中这两行,表示 Pure-FTPd 在对外监听 IPv4 和 IPv6 的 21 端口。

1
2
3
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp 0 0 0.0.0.0:21 0.0.0.0:* LISTEN 2663/pure-ftpd (SER
tcp6 0 0 :::21 :::* LISTEN 2663/pure-ftpd (SER

Local Address 是 0.0.0.0 表示对外监听,如果是 127.0.0.1 的话就是只监听本机,外部的请求无法访问到这个端口。

尝试连接一下#

我们可以用 FileZilla 连接一下试试。

暂时用匿名用户 anonymous 登录,可以看到服务器回应 Login authentication failed,证明服务器是安装成功了,但是还没创建 FTP 用户呢。

Laravel Homestead 虚拟机默认的 IP 是 192.168.10.10

启用虚拟账户#

Pure-FTPd 可以使用虚拟账户,这个账户与 Linux 系统的账户无关,仅仅用于 FTP 登录验证。你可以很多 FTP 账户而不会把系统弄乱。

1
sudo ln -s /etc/pure-ftpd/conf/PureDB /etc/pure-ftpd/auth/PureDB

重启 FTP 服务器#

重启 FTP 服务器,应用配置。

1
sudo service pure-ftpd restart

添加账号#

1
sudo pure-pw useradd ganlv -u vagrant -d /home/vagrant/
  • ganlv 是我一会 FTP 登录时输入的用户名。
  • vagrant 表示用哪个 Linux 系统账户来完成 FTP 文件操作。
  • /home/vagrant/ 表示登录 FTP 之后的根目录 /

Linux 下每个文件和文件夹都有用户和用户组,所以需要特定用户才能操作。虽然有虚拟账户系统,但是还是需要用一个系统账户来进行操作的,总不能用 root 账户来操作吧。

其实在登录之后,pure-ftpd 会用权限较低用户(就是设置的 vagrant)创建一个新进程来执行操作,保证以较低的权限来执行操作。

然后输入两遍密码就创建完成了。

其实,这时还是登录不上的。还需要把文本文件 /etc/pure-ftpd/pureftpd.passwd 转换成 /etc/pure-ftpd/pureftpd.pdb,后面的 PDB 文件才是程序真正读取的用户数据库。

1
sudo pure-pw mkdb

可以在添加账号时直接使用 -m 直接 mkdb,把两步合并成一步。

1
sudo pure-pw useradd ganlv -u vagrant -d /home/vagrant/ -m

添加账号并不需要重启 FTP 服务器。

配置 TLS#

启用强制 TLS#

这部分需要使用 root 用户来操作,但是 sudo 又不是很方便,所以直接用 su 切换到 root。

1
sudo su
1
echo 2 > /etc/pure-ftpd/conf/TLS

如果想既允许普通的 FTP 又支持 FTPS,那就用 echo 1

然后 exit 退出 root 账户。

生成证书#

然后去生成一个自签发的证书。

如果 /etc/ssl/private/ 这个目录不存在的话就新建一个。如果存在的话就没事了。要注意这个目录的权限。

1
2
3
mkdir -p /etc/ssl/private/
chown root:root /etc/ssl/private/
chmod 700 /etc/ssl/private/

我的 Ubuntu 是自带这个目录的,默认是 root:ssl-cert 710 的权限,和 StackOverflow 上的一个答案 差不多。我感觉不到什么区别。

1
drwx--x---   2 root ssl-cert  4096 Jun  3 19:59 private

用 openssl 生成证书,填好证书的各种信息,不想看的话就一直回车就行了。

1
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/pure-ftpd.pem -out /etc/ssl/private/pure-ftpd.pem

重启 FTP 服务器#

凡是修改配置文件,肯定要重启,因为配置文件只在程序启动的时候加载,之后就不会再读取配置了。

大功告成#

FileZilla 连接一下试一试

使用 Pure-FTPd 连接到 FTP 服务器

确定即可,信任这个证书,以后就不会再提示了。

站点管理器中也可以要求显式的 FTP 通过 TLS。

要求显式的 FTP 通过 TLS

关于 Pure-FTPd 的配置文件#

Pure-FTPd 并不是使用 /etc/pure-ftpd/pure-ftpd.conf 来作为配置文件,可以看 pure-ftpd.conf 这个文件的开头的说明。

1
2
3
4
5
# If you want to run Pure-FTPd with this configuration
# instead of command-line options, please run the
# following command :
#
# /usr/sbin/pure-ftpd /etc/pure-ftpd/etc/pure-ftpd.conf

如果你想用这个配置文件运行 Pure-FTPd,你必须要在命令行后面加上这个文件路径作为参数。

如果你不用这个配置文件,直接修改 /etc/pure-ftpd/conf/ 下的文件内容就行。

Ubuntu Use Noto Sans CJK SC as Default Fallback Font

如何正确为 Noto Sans CJK 配置 fontconfig 使中文不会被显示为日文字型?

How to#

1
sudo vi /etc/fonts/conf.d/64-language-selector-prefer.conf

Use dd (delete a whole line) and p (paste) to move Noto Sans CJK SC to the first. And then :wq (write and quite). Restart running program may apply the changes.

About The Default Order#

JP, KR, SC, TC. They are sorted by alphabet. So a newly installed copy of Ubuntu would prefer Noto Sans CJK JP as default fallback font.

CJK, Chinese, Japanese, Korean, is also sorted by alphabet resulting in Chinese C first.

hexo deploy v.s. Travis Pages provider

之前用的是 Hexo 自带的 hexo deploy 来部署 GitHub Pages,这个配置文件显得很不美观。

1
2
3
4
5
6
7
8
9
before_script:
- npm install -g hexo-cli
- git config --global user.name "Ganlv"
- git config --global user.email "nospam@example.com"
- sed -i "s/git@github.com:/https:\/\/${__GITHUB_TOKEN__}@github.com\//" _config.yml
- git clone -b master https://github.com/ganlvtech/ganlvtech.github.io.git .deploy_git

script:
- hexo deploy

通过 sed 来修改 _config.yml 的内容,把环境变量 __GITHUB_TOKEN__ 嵌入到 git 仓库的地址当中,然后手动把仓库克隆下来保留提交历史。

而且也不知道是不是因为使用了 nospam 的邮箱,GitHub 无法识别我这个提交的来源用户,就将提交者的视为陌生人了。

hexo deploy travis pages provider different committers

于是,我就想,hexo 只负责生成静态页面,部署到 GitHub Pages 使用 Travis CI 的 Pages Provider,这样提交者会显示成 travisbot,一目了然,知道这个是自动提交的。而且配置文件似乎更简洁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
before_script:
- npm install -g hexo-cli

script:
- hexo generate

deploy:
provider: pages
skip-cleanup: true
keep-history: true
github-token: $GITHUB_TOKEN
local-dir: public
target-branch: master
on:
branch: dev

在执行生成脚本之前只是全局安装 hexo-cli,然后执行 hexo generate 生成静态页面,然后利用 Travis CI 自动部署就完事儿啦,轻而易举,多么简洁。

后来我又发现好像并不用 npm install -g hexo-cli,原来这样写有点多余。

1
2
3
4
5
before_script:
- npm install -g hexo-cli

script:
- hexo generate

因为 npm install 的时候,安装的 hexo 包自带了 hexo-cli

before_script 删掉,script 改成下面这样。

1
2
script:
- ./node_modules/hexo/bin/hexo generate

XX-Net Teredo 问题

偶尔用 XX-Net 来访问 GoogleMDN。教育网原生 IPv6 可以完全正常使用 XX-net,但其他情况下需要 Teredo 隧道,然而我的电脑就是用不了,周围的人都可以,一直不知道为什么。

今天终发现于原因了:我刚装系统的时候手贱把很多防火墙规则都改成禁用了,然后默认就阻止连接了。

发现过程#

我今天再一次查看 XX-Net IPv6 说明,很随意的点开了 Teredo Wikipedia。简单浏览了一下,突然发现这个服务是需要开启 UDP 3544 端口。我猛然意识到我刚装系统之后,在防火墙中把大部分端口都禁了。

Windows Defender Firewall Advanced Settings

在高级安全 Windows Defender 防火墙设置中,把与 IPv6 有关的条目都启用,再试了一下果然就成功了。

自己手欠啊!

我继续看 XX-Net 的 Wiki 中,FAQ 中提到了 Teredo Xbox Support,点进去发现里面说了防火墙问题。

真是懒啊,没耐心去读文档。

关于 GFW 的 SSL 连接检测

当访问 https://zh.wikipedia.org/ 时会在 SSL 握手时直接被 RESET 或 ABORT,因为握手的时候是明文发送域名的。

zh wikipedia abort

由于墙并没有去管 https://en.wikipedia.org/https://www.wikipedia.org/,这两个网站可以正常访问,SSL 握手正常进行,Socket 成功连接了。

www wikipedia ok

这时再访问 https://zh.wikipedia.org/,由于这两个网站在一个服务器上,可以直接使用刚才的 Socket,于是访问中文维基百科时完全处于加密中。

zh wikipedia use www ssl tunnel

也就是说,假如能和对方成功建立起 SSL 连接,那么这种检测证书的方式就会被绕过。

On 23 April 2019, all versions of Wikipedia were blocked in China. Reference

So you must change hosts to resolve zh.wikipedia.org to the same ip with www.wikimedia.org. Visit https://www.wikimedia.org/ first to establish SSL connection with remote server. Then visit whatever you want.