Skip to content

Commit

Permalink
made the code work for html and pdf
Browse files Browse the repository at this point in the history
  • Loading branch information
tyrchen committed Nov 11, 2019
1 parent fa48d71 commit c5b869f
Show file tree
Hide file tree
Showing 10 changed files with 100 additions and 54 deletions.
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,16 @@ run:
@http-server $(OUTPUT) -p 8000 -c-1

$(BOOK_HTML):$(RDOCS)
@$(PANDOC) $(RDOCS) -o $(BOOK_HTML).tmp
@sed 's/src="\//src="/g' $(BOOK_HTML).tmp > $(BOOK_HTML)
@echo $(RDOCS)
$(PANDOC) $(RDOCS) -o $(BOOK_HTML)

$(DIRECTORIES):$(OUTPUT)/%:$(SRC)

$(RDOCS):$(OUTPUT)/%.md:$(SRC)/%.md
@echo "Creating revised doc $@ with file $<."
-@$(TRANSFORM) -i $< -o $@
@$(TRANSFORM) -i $< -o $@

$(HTMLS):$(OUTPUT)/%.html:$(SRC)/%.md
$(HTMLS):%.html:%.md
@echo "Creating doc $@ with file $<."
-@$(PANDOC) $< -o $@

Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
---
title: '程序君的公众号'
author:
- 程序君
keywords: [readme]
---

# 程序君的公众号

公众号文章,按照《天叔奇谈》的组织方式,每周出一个版本。
5 changes: 5 additions & 0 deletions resources/quotes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
title: '名人名言'
author: [程序君]
keywords: [quotes]
---
Binary file added src/2019/w45/assets/life.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 28 additions & 20 deletions src/2019/w45/no-title-5.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
---
title: '长日无痕(5): 天叔奇谈'
cover: 'assets/life.jpg'
author:
- 程序君
keywords: [life, study]
---

# 长日无痕(5): 天叔奇谈

(一)
Expand All @@ -10,19 +18,19 @@

我心里咯噔一下。我想起了有次小宝央求我陪她玩不成,摔门而出留下的那句:「工作,就知道工作!你是一个不管孩子的老爹!」

第二天晚上,一年级新学期的家长会,我破天荒第一次参加。说来惭愧,小宝在 Sunny Hills 上了一年学,我却连之前的老师 Miss Wolfe 长啥样都不知道。美国的公校是一年一届老师,新学年的老师是 Mrs. Dunn。家长会上我虽然带着耳朵听,心头挥之不去的是「在孩子的成长中,怎样尽一个爸爸的责任,来减轻妈妈的压力?」。在反思的过程中,我意识到我有两个问题:1) 我空有一身的知识,愿意花时间通过写文章,做讲座把知识和技能传递给别人,却吝于培养自己的孩子。2) 我自诩有很强的学习能力,能够掌握任何我想掌握的知识,却懒得在如何教育孩子身上下功夫,学相关的知识。思来想去,决定先从解决问题 1) 入手,投入时间来培养孩子;之后,再处理 2),不断学习来让自己 __有资格__ 教育孩子。
第二天晚上,一年级新学期的家长会,我破天荒第一次参加。说来惭愧,小宝在 Sunny Hills 上了一年学,我却连之前的老师 Miss Wolfe 长啥样都不知道。美国的公校是一年一届老师,新学年的老师是 Mrs. Dunn。家长会上我虽然带着耳朵听,心头挥之不去的是「在孩子的成长中,怎样尽一个爸爸的责任,来减轻妈妈的压力?」。在反思的过程中,我意识到我有两个问题:1) 我空有一身的知识,愿意花时间通过写文章,做讲座把知识和技能传递给别人,却吝于培养自己的孩子。2) 我自诩有很强的学习能力,能够掌握任何我想掌握的知识,却懒得在如何教育孩子身上下功夫,学相关的知识。思来想去,决定先从解决问题 1) 入手,投入时间来培养孩子;之后,再处理 2),不断学习来让自己 **有资格** 教育孩子。

于是我在教室 —— 小宝平日里白天上课的教室里,用她平日里学习用的纸和笔,庄重地写下了如下计划:

> 每晚 8:30-9:10:
>
> * 周一:历史故事(和小宝一起探索二十四史中的有趣故事,加深小宝的文化认同,以及她对汉字和语文的喜爱)
> * 周二:数学(我们一起探索数字的奥妙,培养她的数感)
> * 周三:Scratch(小宝对编程非常喜爱,我们一起做些有趣的开发,来培养她的逻辑能力,以及把复杂问题拆分的能力)
> * 周四:探索(平日里小宝总问我很多为什么,这些「为什么」我解答得非常肤浅,通过「探索」环节,来更多激发她的好奇心和寻找深度答案的能力)
> * 周五:纪录片(深度讲解一部纪录片,让小宝开拓视野,触及那些「未知的未知」)
> * 周六:几何(其实还是数学,只不过我不想小宝觉得怎么一周会两次数学,几何是从另一个维度解释数学的有力武器)
> * 周日:掷色子或者小宝来选择额外学习哪个内容
>
> - 周一:历史故事(和小宝一起探索二十四史中的有趣故事,加深小宝的文化认同,以及她对汉字和语文的喜爱)
> - 周二:数学(我们一起探索数字的奥妙,培养她的数感)
> - 周三:Scratch(小宝对编程非常喜爱,我们一起做些有趣的开发,来培养她的逻辑能力,以及把复杂问题拆分的能力)
> - 周四:探索(平日里小宝总问我很多为什么,这些「为什么」我解答得非常肤浅,通过「探索」环节,来更多激发她的好奇心和寻找深度答案的能力)
> - 周五:纪录片(深度讲解一部纪录片,让小宝开拓视野,触及那些「未知的未知」)
> - 周六:几何(其实还是数学,只不过我不想小宝觉得怎么一周会两次数学,几何是从另一个维度解释数学的有力武器)
> - 周日:掷色子或者小宝来选择额外学习哪个内容
从长期可行性来说,这个计划能执行到什么程度,我自己心里没底 —— 我自己是否能坚持另说,小宝会不会过了一开始的兴奋期,觉得「一天净在学习都没有玩的时间」而抵触?

Expand Down Expand Up @@ -67,27 +75,27 @@
## 贤者时刻

> It is not worth an intelligent man's time to be in the majority. By definition, there are already enough people to do that.
>
>
> - G. H. Hardy
教钢琴的时候不光要教那些技法,还要带孩子去音乐会,去追寻伟大艺术家的生平,去了解每一首曲子的来龙去脉,练习的意义,以及谁练习过它。

—— 摆在孩子们面前的是拆除了脚手架的庄严肃穆的殿堂。老师们像个讲解员介绍一下
—— 摆在孩子们面前的是拆除了脚手架的庄严肃穆的殿堂。老师们像个讲解员介绍一下

最近一个月每天晚上一个小时花在小宝身上,给她讲课,陪她探索,想方设法让她在快乐中领悟知识,拓展眼界,逐渐建立自己思考和解决问题的方法。目前看,收效不错,一来我在她心目中的形象大为改观:之前她在央求我陪他玩我以在「工作」为由拒绝,她甩过给我一句话:「工作,就知道工作!你是一个不管孩子的老爹!」,而现在她在日记里会写 "I love to learn Scratch. Especially with my dad, because I love my dad.";二来她对学习的态度大为改观,以前总抱怨「学习,总要我学习,你们就不能让一个六岁的孩子多玩玩吗?」,现在每晚还没到晚上 8:30 开课的时间,她就会敲我的书房,问:「老爹,你今天晚上会给我讲什么内容?」,或者周四早上上校车前,专门委托她妈妈嘱咐我:「探索课我想知道为什么姥姥炒好的一盘菜,表面很快就凉了,而里面还很热?」。

此外,游戏中还有英雄的升级系统,通过不断地升级为英雄赋能。

如果把我的课程看做产品,那么,我需要做的是,如何让这个产品的用户着迷。我曾经研究过一段时间的 gamification,记得让人着迷的核心是调用人们内心中的某种感觉 —— 包括正向的和负向的:

* 意义和使命感(meaning)
* 成就感(accomplishment)
* 赋能(empowerment)
* 社交影响力(social influence)
* 所有权(ownership)
* 稀缺性(scarcity)
* 避祸心理(avoidance)
* 不可预测性(unpredictability)
- 意义和使命感(meaning)
- 成就感(accomplishment)
- 赋能(empowerment)
- 社交影响力(social influence)
- 所有权(ownership)
- 稀缺性(scarcity)
- 避祸心理(avoidance)
- 不可预测性(unpredictability)

在我开始给小宝上课时,她已经有了大部分小学二年级数学的能力:加减乘除,基本的几何概念,上千汉字的识字水平,能够自己读西游记的绘本等。所以我的工作需要铺垫的东西并不多。我需要做的,和产品经理一个路子,让用户着迷。这里的着迷有几个方面:

Expand All @@ -109,7 +117,7 @@

在孩子成长的过程中,爸爸和妈妈的分工是天然形成的。妈妈多负责操作性的事情,如搭积木,提升动手能力;爸爸更多促进孩子动脑思考,发现规律,掌握规律。之前,动手的这些事情我是真不爱干;而动脑的事情,我没有干,所以在小宝的成长拼图中,爸爸的部分是缺失的。现在真干起来,我发现自己还挺胜任的 —— 当然,这和老婆打下的基础分不开。

儿童认知心理学强调的是 __认识事物,熟悉事物,掌握事物,操作事物__ 的过程。
儿童认知心理学强调的是 **认识事物,熟悉事物,掌握事物,操作事物** 的过程。

剥洋葱

Expand Down
53 changes: 27 additions & 26 deletions src/2019/w45/wireguard.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
---
title: Wireguard:简约之美
title: 'Wireguard:简约之美'
cover: 'assets/wg.jpg'
author: [程序君]
author:
- 程序君
keywords: [tech, security, wireguard]
---

Expand All @@ -13,15 +14,15 @@ keywords: [tech, security, wireguard]

在具体讲 WG 之前, 我们先来把 VPN 的通用需求抽象一下:

* 安全性(security):保证两个私有网络间的数据可以在不安全的网络(比如互联网)上安全地传输
- 安全性(security):保证两个私有网络间的数据可以在不安全的网络(比如互联网)上安全地传输

* 真实性(authenticity):访问者是合法的用户,访问者访问的是正确的网络
- 真实性(authenticity):访问者是合法的用户,访问者访问的是正确的网络

* 高效性(efficiency):开启 VPN 并不会让访问网络明显变慢,且建立隧道的速度要快
- 高效性(efficiency):开启 VPN 并不会让访问网络明显变慢,且建立隧道的速度要快

* 隐秘性(stealthiness):第三方并不能轻易嗅探到网关的存在
- 隐秘性(stealthiness):第三方并不能轻易嗅探到网关的存在

* 易用性(accessibility):很容易配置,很容易开启和关闭
- 易用性(accessibility):很容易配置,很容易开启和关闭

在不安全的网络上安全地传输数据这件事,我们必须感谢 Martin E Hellman,Bailey W Diffie 和 Ralph C. Merkle。他们的专利 [Cryptographic apparatus and method](https://patents.google.com/patent/US4200770) 提出了后来广为流传的 DH 算法,用于交换密钥。

Expand Down Expand Up @@ -57,7 +58,7 @@ WG 仅有 4k 的内核代码实现!精巧得简直不能再精巧!虽然这
要知道,Linus 平日来评论代码的画风是这样的(Mauro 是一个 Kernel maintainer):

> "It's a bug alright -- in the kernel. How long have you been a maintainer? And you *still* haven't learnt the first rule of kernel maintenance?
> "It's a bug alright -- in the kernel. How long have you been a maintainer? And you _still_ haven't learnt the first rule of kernel maintenance?
> "Shut up, Mauro. And I don't _ever_ want to hear that kind of obvious garbage and idiocy from a kernel maintainer again. Seriously.
Expand All @@ -71,11 +72,11 @@ WG 仅有 4k 的内核代码实现!精巧得简直不能再精巧!虽然这

WG 先定义了一个很重要的概念 —— WireGuard Interface(以下简称 wgi)。为什么要有 wgi?为什么现有的 tunnel 接口不适合?一个 wgi 是这么一个特殊的接口:

* 有一个自己的私钥(curve25519)
- 有一个自己的私钥(curve25519)

* 有一个用于监听数据的 UDP 端口
- 有一个用于监听数据的 UDP 端口

* 有一组 peer(peer 是另一个重要的概念),每个 peer 通过该 peer 的公钥确认身份
- 有一组 peer(peer 是另一个重要的概念),每个 peer 通过该 peer 的公钥确认身份

通过定义这样一个新的接口,wgi 把它和一般的 tunnel 接口区分开。有了这样一个接口的定义,其它数据结构的挂载,以及数据的收发都很清晰明了了。

Expand Down Expand Up @@ -122,19 +123,19 @@ WG 的简洁设计还体现在加密隧道的协商上。它使用了 Noise Prot

WG 的握手报文中会携带:

* unencrypted_ephemeral:发送方为这次握手临时生成的公钥(未加密,用于 ECDH)
- unencrypted_ephemeral:发送方为这次握手临时生成的公钥(未加密,用于 ECDH)

* encrypted_static:用对端公钥和临时生成的私钥 ECDH 出的临时密钥 key1 对称加密对方的公钥
- encrypted_static:用对端公钥和临时生成的私钥 ECDH 出的临时密钥 key1 对称加密对方的公钥

* encrypted_timestamp:用对端公钥和自己的私钥 ECDH 出 key2,key2 混淆进 key1,来加密当前的时间戳
- encrypted_timestamp:用对端公钥和自己的私钥 ECDH 出 key2,key2 混淆进 key1,来加密当前的时间戳

* mac1:对端公钥加上整个报文内容后的哈希
- mac1:对端公钥加上整个报文内容后的哈希

接收方先校验 mac1(简单的身份验证 - 一般的黑客在这一步就跪了),如果不对,直接丢弃;之后验证 encrypted_static(确认眼神 - 除非有私钥,否则黑客在这一步也跪了),验证 encrypted_timestamp(防止重放,所以重放攻击也跪了)。当接收方校验一切 OK 后,它可以生成自己的临时密钥对。此时,接收方因为有了对端的临时公钥,已经可以计算出此次协商后加密数据要用的密钥。但它还需要发送一个握手的回复报文来把自己的临时公钥给发送方以便于发送方可以算出同样的密钥:

* unencrypted_ephemeral:接收方为这次握手临时生成的公钥(未加密,用于 ECDH)
- unencrypted_ephemeral:接收方为这次握手临时生成的公钥(未加密,用于 ECDH)

* mac1:对端公钥加上整个报文内容后的哈希
- mac1:对端公钥加上整个报文内容后的哈希

这样两端都有对方临时生成的公钥,加上自己临时生成的私钥,就可以 ECDH + HKDF(一种把 DH 结果转成对称加密密钥的方法)得到这次握手的两个方向的对称加密的密钥。

Expand All @@ -146,23 +147,23 @@ WG 的握手报文中会携带:

有了密钥之后,用户的数据报文就很好处理了。处理的逻辑非常简单清晰,以至于寥寥数行就可以涵盖:

* 发送:
- 发送:

* 用户态:应用程序发送目标地址是 VPN 对端网络的数据报文
- 用户态:应用程序发送目标地址是 VPN 对端网络的数据报文

* 内核:内核通过路由表发现应该由 wg0 接口发出,所以交给 WG 处理
- 内核:内核通过路由表发现应该由 wg0 接口发出,所以交给 WG 处理

* WG:通过目标地址,在接口的配置中可以反查出要发往哪个 peer,然后用之前和该 peer 协商好的密钥(如果没有协商或者密钥过期,则重新协商)加密报文,并将报文封装在目标地址和目标端口是 peer 的 endpoint 的 UDP 报文中(报文中包含 key_index)
- WG:通过目标地址,在接口的配置中可以反查出要发往哪个 peer,然后用之前和该 peer 协商好的密钥(如果没有协商或者密钥过期,则重新协商)加密报文,并将报文封装在目标地址和目标端口是 peer 的 endpoint 的 UDP 报文中(报文中包含 key_index)

* 接收:
- 接收:

* 内核:数据报文的 UDP 端口是 WG 在监听,将其送给 WG 处理(WG 的 recv 得到该报文)
- 内核:数据报文的 UDP 端口是 WG 在监听,将其送给 WG 处理(WG 的 recv 得到该报文)

* WG:从报文的 key_index 找到哈希表中对应的密钥,解密(这里不是直接解密,而是放入一个解密队列中,这是设计上网络系统的一个小诀窍)
- WG:从报文的 key_index 找到哈希表中对应的密钥,解密(这里不是直接解密,而是放入一个解密队列中,这是设计上网络系统的一个小诀窍)

* WG:查看解密出来的原始报文是否在 peer 允许的 IP 列表中,如果是,就把原始报文交给内核处理。注意,这里这个报文属于哪个 peer,也是从 key_index 中获得
- WG:查看解密出来的原始报文是否在 peer 允许的 IP 列表中,如果是,就把原始报文交给内核处理。注意,这里这个报文属于哪个 peer,也是从 key_index 中获得

* 内核:根据原始报文的目标地址查路由表将报文送出
- 内核:根据原始报文的目标地址查路由表将报文送出

![](assets/wg_routing.jpg)

Expand Down
Binary file added src/cover.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
24 changes: 24 additions & 0 deletions src/intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
title: '序言'
author: [程序君]
keywords: [intro]
---

<style scoped>

.book-cover h1 {
font-size: 48px;
text-align: center;
padding: 60px;
}


</style>

# 程序君的公众号文章 {.book-cover}

![](cover.jpg)

\newpage

# 前言
4 changes: 3 additions & 1 deletion templates/github.wechat
Original file line number Diff line number Diff line change
Expand Up @@ -460,12 +460,14 @@
margin: 20px 10px; max-width: 100%; min-height: 1em; white-space: pre-wrap; color: #545454; text-align: justify; line-height: 28px; box-sizing: border-box !important; word-wrap: break-word !important; background-color: rgb(255, 255, 255);
}
ol, ul {
width: 520px; max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important; background-color: rgb(255, 255, 255);
max-width: 100%; box-sizing: border-box !important; word-wrap: break-word !important; background-color: rgb(255, 255, 255);
}
ol li, ul li {
margin: 5px; max-width: 100%; min-height: 1em; white-space: pre-wrap; text-align: justify; line-height: 1.5; box-sizing: border-box !important; word-wrap: break-word !important;
color: #545454; font-size: 15px;
}
img {width: 100%;}

@media all {
.page-break { display: none; }
}
Expand Down
5 changes: 2 additions & 3 deletions tools/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,18 @@ const path = require('path');
const pipes = [replaceImage, addPageBreak];

async function process(src, dst) {
console.log(`process ${src}`);
const content = await fs.readFile(src, 'utf8');
const result = pipes.reduce((acc, fn) => fn.call(null, acc, { filename: src }), content);
await fs.writeFile(dst, result);
}

function replaceImage(content, context) {
const dir = path.dirname(context.filename).replace('src/', '');
return content.replace('assets/', `${dir}/assets/`);
return content.replace(/assets\//g, `${dir}/assets/`);
}

function addPageBreak(content, _context) {
return `${content}\\newpage`;
return `${content}\n\\newpage\n`;
}

async function main() {
Expand Down

0 comments on commit c5b869f

Please sign in to comment.