一台新的 Ubuntu 服务器通常时区可能不是想要的时区,可以通过如下步骤设定时区。
检查当前时区,在命令行下输入 date
:
date
可以查看当前的时间。
输入 timedatectl
可以查看更具体的时区。
修改为东八区北京时间。
sudo timedatectl set-timezone "Asia/Shanghai"
也可以通过软链接来修改系统的时区,在 Linux 下 /etc/localtime
中定义了系统要使用的时区。正确的配置在 /usr/share/zonefine
目录中
mv /etc/localtime /etc/localtime-backup
ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
修改完成后可以通过 timedatectl
来验证。
DMARC 是 Domain-based Message Authentication Reporting & Conformance 的缩写,是一个标准的电子邮件验证标准。1 DMARC 帮助邮箱管理员防止黑客或其他攻击者伪装(Spoofing)其组织和域名。Spoofing(伪装)是一种电子邮件攻击方式,攻击者通过伪装邮件地址中的 From 字段,来假装发件人。DMARC 会检查电子邮件是否来自邮件中声称的发送者。
DMARC 构建在 [[SPF]] 和 [[DKIM]] 基础之上,来防止域名欺诈。
之前提到过自建域名邮箱 的文章中就配置过 DMARC,这篇文章就对 DMARC 具体展开讲讲。
DMARC 建立在广泛使用的 SPF 和 DKIM 协议上, 并且添加了域名对齐检查和报告发送功能。这样可以有效保护域名免受钓鱼攻击。
来自 dmarc.org 的示意图:
根据 dmarc.org 的说法:
随着社交网络和电子商务的繁荣,垃圾邮件发送者和钓鱼攻击发起者基于利益的原因,想要入侵用户的账户,破解用户的信用卡等。Email 的相对容易攻击的特性备受罪犯们的青睐。只是简单地把企业的 logo 嵌入到 email 中,就能获取用户的信任。
用户很难辨别一封假的 email,邮件提供商也很难判断哪些邮件有可能会伤害用户。邮件发送者基本上对邮件认证的问题一无所知,因为他们缺少合理的反馈机制。那些尝试部署 SPF 和 DKIM 的企业的进展非常慢,因为没有监督进度和除错的机制。
DMARC 解决了这些问题。它帮助 email 发送者和接收者来共同保护 email,避免了昂贵的入侵损失。
DMARC 记录作为 TXT 记录发布到 DNS 中。指示邮件收件服务当验证失败时,应当如何处理收到的邮件。
比如以下发布在域名 “sender.exampledomain.com” 上的 DMARC 记录:
v=DMARC1;p=reject;pct=100;rua=mailto:postmaster@exampledomain.com
在这个例子中表示发件人要求收件人如果遇到验证未通过的邮件,则完全拒绝,并且发送相关报告到 postmaster@exampledomain.com
。如果发件人在测试配置,”reject” 可以被替换成 “quarantine”,表示验证未通过的 邮件将被隔离。
DMARC 记录使用可扩展的 “标签-值” 语法。
这是一个典型的 DMARC 记录:
v=DMARC1; p=none; rua=mailto:dmarc_report@example.com; ruf=mailto:dmarc_report@example.com; pct=100; adkim=s; aspf=s;
它由多个 key-value 标签组成。这些标签会告诉邮件服务提供商如何发送 DMARC 报告:
v
是 DMARC 协议版本,它的值必须是 DMARC1
p
是策略,策略将被应用到验证失败的邮件上,可以设置成 ‘none’, ‘quarantine’, 或者 ‘reject’
rua
是一组用来接收报告的电子邮件地址(DMARC aggregate)ruf
是一系列用来接受失败报告的电邮地址(DMARC failure)pct
对失败邮件应用策略的百分比adkim
制定 DKIM 的对齐策略,可选 s
或 r
aspf
制定 SPF 的对齐策略,可选 s
或 r
如果不想配置报告失败的邮箱,可以
v=DMARC1; p=none;, p=reject;, p=quarantine;
DMARC 策略是在 DMARC 记录中制定的 p 标签。它指示邮件服务提供商应该如何处理验证失败的邮件。
DMARC 策略可以取三个值中的一个:none (monitor), quarantine 和 reject。
– none: 邮件提供商对验证失败邮件不采取任何处理。这个模式用来收集 DMARC 报告。 – quarantine: 邮件提供商把验证失败邮件放到垃圾邮件文件夹。 – reject: 邮件提供商拒收验证失败邮件。
DMARC 对齐策略。DMARC 通过检验 header 部分是否和发送的域名相匹配,这个动作称为 alignment
,这个部分验证依赖 SPF 和 DKIM。
用户可以在 DMARC 中配置两种 alignment 模式,strict
和 relaxed
。上面提到的 aspf
和 adkim
选项就是这个作用。s
和 r
分别是两个缩写。
验证方式 | Strict | Relaxed |
---|---|---|
SPF | SPF 验证域名,和邮件 Header 中 From 一致 | Header 中的 From 必须匹配域名或子域名 |
DKIM | From 中的地址和 DKIM 配置一致 | From 必须匹配 DKIM signature d= 后配置的域名或子域名 |
Envelope sender address: 是 Return-Path 头的邮件地址,收件人是看不到该地址的
From: 地址,收件人看到的地址
SPF alignment example
Envelop sender address | Header From | strict | relaxed |
---|---|---|---|
admin@example.com | admin@example.com | Pass | Pass |
admin@mail.example.com | admin@example.com | Fail | Pass |
admin@example.com | admin@example.org | Fail | Fail |
DKIM alignment example
DKIM signature domain 中的 d=
后面部分。
From | DKIM d= | strict | relaxed |
---|---|---|---|
admin@example.com | example.com | Pass | Pass |
admin@mail.example.com | example.com | Fail | Pass |
admin@example.org | example.com | Fail | Fail |
发布 DMARC 记录需要你有权限修改域名的 DNS 记录。
在 DNS 记录后台,需要创建一条 DMARC 记录。
创建一条 TXT 记录:
Type: TXT Host: _dmarc TXT Value: (DMARC record generated above) TTL: 1 hour
以 Cloudflare 后台为例:
如果用其他的域名服务的话,界面类似。
保存更改。现在你已经在域名 yourdomain.com 上面成功发布 DMARC 记录了。
如果不知道怎么写 DMARC 记录也可以在这个网站 通过表单的形式生成。
常见的语句:
下文中使用 statement 和 expression 来表达一些语句的区别:
通用:
if InitSimpleStatement; Condition {
// do something
} else {
// other
}
else
是可选的()
或者不加。;
也是可选的举例:
if a < 4 {
return 0
} else {
return 1
}
更复杂一些的:
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().UnixNano())
if n := rand.Int(); n%2 == 0 {
fmt.Println(n, "is an even number.")
} else {
fmt.Println(n, "is an odd number.")
}
n := rand.Int() % 2 // this n is not the above n.
if n % 2 == 0 {
fmt.Println("An even number.")
}
if ; n % 2 != 0 {
fmt.Println("An odd number.")
}
}
一种特殊的条件分支语句。
switch i {
case 0:
fmt.Printf("0")
case 1:
fmt.Printf("1")
case 2:
fmt.Printf("2")
case 3:
fallthrough
case 4, 5, 6:
fmt.Printf("4,5,6")
default:
fmt.Printf("Default")
}
for 循环的格式:
for InitSimpleStatement; Condition; PostSimpleStatement {
// do something
}
()
for i := 0; i < 10; i++ {
fmt.Println(i)
}
几个变种:
var i = 0
for ; i < 10; {
fmt.Println(i)
i++
}
for i < 20 {
// do something
i++
}
for i := 0; ; i++ {
if i >= 10 {
break
}
}
使用 for range
遍历 map:
for key, value := range oldMap {
newMap[key] = value
}
Java 语言不支持 goto 关键字,但是 goto 是作为关键字保留了下来,但是 Java 中可以使用 label
关键字来达到 goto
的效果。
Go 语言支持 goto statement,goto
关键字必须跟一个 label 来表达跳转到的地方。
package main
import "fmt"
func main() {
i := 0
Here: // declare a label
fmt.Println(i)
i++
if i < 10 {
goto Here // goto label
}
}
需要注意的是 label 对于最里层的代码块是不可见的。
break
和 continue
关键字后面都可以接 label
defer 关键字会推迟方法的执行,知道外层的方法返回之后再执行。
defer 的调用的参数会立即
A defer statement defers the execution of a function until the surrounding function returns.
The deferred call’s arguments are evaluated immediately, but the function call is not executed until the surrounding function returns.
package main
import "fmt"
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
最近收到 Oracle(甲骨文) 一封名为「Oracle Cloud Infrastructure Compute - Resource Maintenance Reminder」 的邮件,大致意思就是如果 Oracle Always Free 的机器使用率比较低的话,Oracle 就会判定 Compute Instances 处于 Idle (闲置)状态,Oracle 将保留回收实例的权力。
如何判定机器是处于闲置状态呢,在 Oracle 给出的官方文档 是这样描述的:
• CPU utilization for the 95th percentile is less than 10% ,95% 时间 CPU 利用率低于 10% • Network utilization is less than 10% ,网络利用率低于 10% •Memory utilization is less than 10% (applies to A1 shapes only),内存利用率低于 10%(仅适用于 A1 类型的 ARM 实例)
Oracle 给出的描述非常清晰,就目前我的理解,应该是只要上述三个条件中的一个满足了就会触发 Oracle 的回收机制,首先 Oracle 会发送一封警告邮件 Action Recommended,标题就是上面的。然后会告知用户,7 天之内如果没有满足上述标准就会回收实例。
Oracle Cloud Infrastructure Customer,
Oracle Cloud Infrastructure (OCI) will be reclaiming idle Always Free compute resources from Always Free customers. Reclaiming idle resources allows OCI to efficiently provide services to Always Free customers. Your account has been identified as having one or more compute instances that have been idle for the past 7 days. These idle instances will be stopped one week from today, January 30,2023. If your idle Always Free compute instance is stopped, you can restart it as long as the associated compute shape is available in your region. You can keep idle compute instances from being stopped by converting your account to Pay As You Go (PAYG). With PAYG, you will not be charged as long as your usage for all OCI resources remains within the Always Free limits.
因为我之前申请的两台 AMD 和一台 24 G 内存的 ARM,都不敢将生产的东西放在上面,怕得就是 Oracle 突然变卦,所以上面跑的程序都比较少,现在看起来 Oracle 至少在变卦之前还是会发出声明的,可以将一些 Docker 迁移到机器上跑跑了。
解决方案其实也比较简单,一个就是按照官网所提供的方式升级成付费账号,用多少支付多少(Pay As You Go (PAYG));另外一个方案就是真实地把机器利用起来,跑一些程序,或者将起作为 [[k3s]] 节点加入集群跑起来,看看满足其使用的最低要求。
说实话,网上有一些教程让用户禁用 oracle cloud agent(root 模式下执行 snap remove oracle-cloud-agent
),禁止 Oracle 监控实例的运行状况。因为可以猜测的就是 Oracle 会在 VM 实例中跑监控程序,然后批量地监控用户机器的使用情况,包括 CPU,内存,网络等等,如果把这个 agent 卸载掉了是否 Oracle 就检测不到了。说实话我是不推荐这个方法的,因为 VM 运行在 Oracle 的机房,要检测还是能通过其他手段来检测到的。
所以个人还是推荐将 VM 好好利用起来,个人在过去尝试过的 Self-hosted 应用,非常不错的都在这里 管理了起来,也可以在 GitHub Awesome Self hosted 项目里面找找自己感兴趣的利用起来。
lookbusy 是一个可以虚拟产生 CPU、内存、磁盘等负载的工具。
lookbusy 常用的一些使用方法。
-c
: 每个 cpu 的期望利用率,以百分比为单位(默认 50 )。若选择 curve 使用模式,则应给出 MIN-MAX 形式的范围。-n
:保持忙碌的 CPU 数量(默认值:自动检测出的全部核心数)-r
: 第一种模式:fixed,保持指定百分比的 CPU 利用率,第二种模式:curve,CPU 利用率随给定区间上下浮动。-p
: 曲线内峰值利用率的偏移量,默认单位是秒,可以添加 m、h、d 作为其他单位-P
: 曲线内峰值利用率的偏移量,可以添加 m、h、d 作为其他单位-m
: 以字节为单位,后面是 KB、MB 或 GB(其他单位)-M
: 单位为毫秒(默认值为 1000)-d
:用于磁盘流失的文件大小(以字节为单位,后跟 KB、MB、GB 或 TB(其他单位))-b
:用于 I/O 的块大小(以字节为单位,后跟 KB、MB 或 GB)-D
:迭代之间的休眠时间,以毫秒为单位(默认值为 100)-f
: 要用作缓冲区的文件 / 目录的路径(默认 /tmp)使用举例:
lookbusy -c 50 # 占用所有 CPU 核心各 50%
lookbusy -c 50 -n 2 # 占用两个 CPU 核心各 50%
lookbusy -c 50-80 -r curve # 占用所有 CPU 核心在 50%-80% 左右浮动
lookbusy -m 128MB -M 1000 # 每 1000 毫秒,循环释放并分配 128MB 内存
lookbusy -d 1GB -b 1MB -D 10 # 每 10 毫秒,循环进行 1MB 磁盘写入,临时文件不超过 1GB
nohup lookbusy -c 50 -D 10 > /dev/null 2>&1 & #占用所有 CPU 核心各 50%,且后台运行,不记录日志
在 Oracle ARM 机器上可以直接使用 docker-compose 启动。
Koel 是著名的个人音乐在线播放与电台程序。
Koel 后端使用 Laravel PHP 框架,前端使用 Vue,还使用了 SASS 的 CSS 框架。数据库使用 MariaDB(MySQL)。
使用 Docker compose:
version: '3'
services:
koel:
container_name: koel
image: hyzual/koel
restart: always
ports:
- 8081:80
environment:
- DB_CONNECTION=mysql
- DB_HOST=${DB_HOST}
- DB_USERNAME=${DB_USERNAME}
- DB_PASSWORD=${DB_PASSWORD}
- DB_DATABASE=${DB_DATABASE}
volumes:
- ${MUSIC_PATH}:/music:ro
- ${COVERS_PATH}:/var/www/html/public/img/covers
- ${SEARCH_INDEX_PATH}:/var/www/html/storage/search-indexes
安装完成后进行初始化:
docker exec -it <container_name_for_koel> bash
php artisan koel:init --no-assets
从 v5.1.0 开始 Koel 不会在安装的时候去设置 admin 账户,会使用一个默认的用户名:
email: admin@koel.dev
password: KoelIsCool
可以通过界面重置密码,或者执行命令:
docker exec -it <container_name_for_koel> php artisan koel:admin:change-password
当前的 Koel 可识别这些音频扩展:.mp3,.ogg,.m4a(实验)和 .flac
也有几个快捷键呀:
在逛 Twitter 的时候看到有人分享了一款 Launcher,叫做 [[Raycast]],因为平时一直使用 [[Alfred]] 倒是没有引起我的太多注意,但今天又看到了一次,所以就想好好来了解一下这个新出来的启动器。
在 macOS 上其实有非常多的启动器,包括了系统默认的 [[Spotlight]],以及很多第三方应用,包括了 [[Alfred]], [[LaunchBar]] 在内的无数优质应用。这些应用有着各自的特点。但最重要的一点就是提高我们打开应用的速度,以及在各个小细节方面提高易用程度,比如我最喜欢的 Alfred 的一个贴心功能就是历史粘贴板,在 Linux 上我一直都是用的 [[fcitx]] 默认的快捷键 Ctrl+;
,所以在 macOS 也用一样的快捷键。
安装:
brew install --cask raycast
写过代码的人应该对 Snippet 非常熟悉,我们可以定义一些常用的代码块,然后通过简单的几个字母来快速补充一段内容。
在 Raycast 中,可以通过输入 Create snippet
来创建 Snippet,在弹出的设置窗口中输入想要设置的内容。
注意下面的 Keyword, 输入 Keyword 的内容就是唤起整个 Snippet 的快捷键。
比如当我输入 :blog
的时候就会自动补全成https://blog.einverne.info
如果有读者记得的话,我在不就之前介绍过一款跨平台的 Text Expander —- Espanso,也能够做到相同的事情。有兴趣可以查看espanso:Rust 编写的跨平台开源文本扩展工具。
在 Raycast 中输入 Clipboard History
可以打开粘贴板历史。
这里值得注意的是 Raycast 所有数据都是本地加密存储,所以可以不用担心粘贴板的隐私问题。
同样,在应用设置中,可以设置快捷键,我就设置成和 Linux 下一致的 Ctrl+;
在 Raycast 中输入 f
可以看到 File Search,授权 Raycast 搜索本地文件之后,就可以直接在 Raycast 中根据文件名一键搜索到。
在 Raycast 中输入 dw
就可以看到 Define Word 的选项,这个可以用来查单词。
在 Raycast 的扩展中有一个 Switch Windows 的插件,可以设置一个快捷键,比如 Option + Tab,这样就可以快速调用出窗口切换功能,直接输入关键字 Enter,就能切换窗口,再不用 Cmd + Tab 来换切换了。当然可以根据自己的需要设置成任意的快捷键。
Raycast 支持直接搜索图片中的内容,还可以选中图片之后使用,cmd + Shift + a 使用 Cleanshot 对图片标注。
输入 Store
可以打开 Raycast 的商店,然后可以在其中安装、启用非常多的三方插件。
目前Raycast已经接入了 GitHub、Jira、G Suite、Linear、Asana、zoom,Notion,Things,Raindrop,Todolist 等服务,能够快速完成特定操作。
首先在 Store 中安装 Browser Bookmarks
然后在设置中启用 Chrome:
注意选择正确的 Profile。
然后就可以在 Raycast 中输入 Browser Bookmark 来搜索 Chrome 的书签了。
不过需要注意的是,只有书签存在中文、英文才能被搜索出来,如果书签没有描述,或者你忘记了域名,那么也是无可奈何的。
在 Raycast 中输入 Floating Notes 之后就会在桌面最顶层开启一个悬浮便签,便签可以移动位置,但是永远在所有窗口的最上面。非常方便用来记录快速的想法,待办事项等等。
在输入 Float 之后,有两个选项:
Raycast 可以直接连接 Calendar,因为我的 macOS 上的日历同步了 Google Calendar 的数据,所以可以直接看到我的日程。
在 Raycast 中输入 My Schedule
即可。第一次可能需要授权。
同样可以输入 reminder
来查看待办事项,或者根据提示来创建待办事项。
比如输入 volume 可以调整音量。
其他比如计算器、汇率转换、时间查询等等基础功能就不再展开。
如果有时间会再讲讲如何在 Raycast 中执行自定义脚本,或者如何编写自己的扩展。 Raycast 支持的脚本语言,Bash、Swift、AppleScript。
还记得之前听的一个播客,里面提到基于协议的应用一般不会轻易地被历史淘汰,想想基于 HTTP 协议的 Web 浏览器,基于 SMTP 的电子邮件,基于 [[BitTorrent]] 协议的文件交换协议,在过去的几十年里,基于 HTTP 上层的网站,以及技术更新换代了好几波,但底层的协议依然还是 HTTP(HTTPS),而倒闭的电子邮箱服务提供商也不在少数。但如今互联网依然还依赖 HTTP 协议,电子邮箱虽然没有那么流行但也是基础服务之一。
过去很多年里面,我陆陆续续一直在寻找一个完美代替 Gmail 的存在,我的代办事项中一直存在一条叫做如何自建邮箱服务的 TODO,我陆陆续续体验过很多新出现的加密邮箱服务(Proton Mail,Skiff),但也还是一直用着 Gmail。陆陆续续把很多数据从 Google 的服务中迁移出来,但唯独摆脱不了 Gmail。但是最近体验了一些自建的邮箱服务之后,感觉自己可以现在再来尝试一下自建邮件服务器了,只需要花费很少的钱去购买一个属于自己的域名就可以拥有无限制的邮箱。
过去我曾经体验过的自建邮箱服务器:
还有一些比如 [[iRedMail]]、[[modoboa]]、[[zimbra]] 等等的服务还没有仔细看,但都能实现邮件服务器。
在尝试的过程中觉得自己还需要补习一些基本的知识,因为在自建邮箱服务的时候不可避免的需要配置多个 DNS 记录,包括了 [[MX 记录]],[[SPF]],[[DMARC]] 等等。如果理解了邮件的发送过程,会对这些有更多的理解。所以下面就再学习记录一下邮件发送过程。
假设用户 a@gmail.com 发送一封邮件到 b@qq.com,会执行如下的流程。
为了简化理解,下面的所有操作都是通过网页版的 Web 应用触发。
当我们在 Gmail 网页上撰写一封邮件,并点击发送按钮之后。Gmail 会用自己的内部协议链接 Gmail 的 Outgoing SMTP 邮件服务器。
Outgoing SMTP 验证用户权限,然后将邮件以 MIME 格式发送到发送队列中。
Gmail SMTP 服务器会通过 DNS 查询到域名 qq.com
MX 记录(dig MX qq.com
),找到邮件服务器的 IP 所在。
在 Linux 下也可以通过 dig mx qq.com
来查询到。这一步在对应到自建的邮件服务器的时候,就是通过配置 DNS 的 MX 记录来实现的。
一般情况下会配置一个 A 记录 mx.example.com
指向服务器的 IP 地址。然后再配置一个 [[MX 记录]],@
全部域名的 MX 请求全部转发给 mx.example.com
。
MX 这里指的是 Mail Exchanger。
当 Gmail 的服务器找到 QQ 邮箱的 IP 地址之后,邮件就会通过 SMTP 协议连接服务器的连接,尝试发送给 QQ 的服务器。
为了简化理解,SMTP 传输的时候就直接声明,我 a@gmail.com ,我要发送邮件到 b@qq.com ,内容是某某某。
这中间会发现不存在任何验证发送方身份的过程,这也就意味着任何人都可以伪装一个任意的发送邮箱以一个伪装的邮箱发送邮件。SMTP 最早是建立在相互信任的基础之上的,所以也给后面的恶意使用留下了一些漏洞,为了修复这个漏洞发明了 [[SPF]]。具体见下文。
QQ 邮箱的服务器接收到 Gmail 的邮件之后,再根据用户名决定发给具体谁的邮箱。
[[SMTP]] 是 Simple Mail Transfer Protocol 的缩写,从协议名称上也能看出来是一个邮件传输协议。
上文提到过 SMTP 协议发送邮件的过程中没有验证发送方,这也就意味着发信方可以任意指定发件人邮箱地址,这会存在一些安全问题。
具体来说,本来我的 Gmail 邮箱是 a@gmail.com
,假如有不法分子,就可以利用这个漏洞,伪装成自己是 a@gmail.com
给别人发送邮件。
[[SPF]] 的目的就是为了防止伪造发信人。
SPF 的实现原理非常简单,就是通过添加一条 DNS 记录。
如果邮件服务器收到一封来自主机 1.1.1.1
的邮件,并且发件人是 a@gmai.com
,为了确认发件人,邮件服务器就会去查询 gmail.com
的 SPF 记录。如果域名设置了 SPF 记录,允许 1.1.1.1
的 IP 地址发送邮件,那么收件的邮件服务器就会认为邮件是合法的,否则就会退信。
有了 SPF 记录之后,如果有人想要伪装成 a@gmail.com
他既不能修改 gmail.com 的 DNS 解析,也无法伪造 IP 地址,就有效的防止了伪装。
在自建邮件服务器的时候,经常会让我们设置一个 TXT 记录,配置值为 v=spf1 mx ~all
,这表示的意思是允许当前域名的 MX 记录对应的 IP 地址。
下面再举个非常常见的例子:
v=spf1 a mx ip4:173.10.10.10 -all
表示允许当前域名配置的 A 记录,MX 记录的 IP 地址,以及一个额外的 IP 进行发信。
通过上面的描述我们知道通过 SPF 机制可以有效地规避了发送邮件方伪造发件人的问题。但实际使用的时候,如果你使用多个邮箱,然后设置了其中 c@163.com 邮箱自动转发到 a@gmail.com 中。
那么这个时候如果 b@qq.com
QQ 邮箱发送了一封邮件到 c@163.com
邮箱,163 邮箱原封不动地将邮件转发到 Gmail 邮箱,这个时候发件人是 b@qq.com
,但是 Gmail 回去查询 qq.com 的 SPF 记录,但会发现并不包含 163 邮箱的 IP 地址,会误判转发的邮件。
所以又诞生了 DKIM
[[DKIM]] 是 DomainKeys Identified Mail 的缩写,允许发送者通过在邮件的 header 中包含一段数字签名来验证邮件。DKIM 使用公私密钥来确保邮件内容是从授信的邮件服务器发送的。
还是利用上面的例子,因为我们把所有发送到 163 邮箱的邮件都转发到了 Gmail 邮箱,所以来自 QQ 邮箱的邮件在验证 SPF 时会失败。
那么在 DKIM 中,发送邮件的服务器,比如 QQ 邮箱,会使用公私钥对邮件内容进行签名,并将签名和邮件内容一起发送。当 Gmail 收到从 163 邮箱转发过来的 QQ 邮箱邮件的时候,就会去查询 qq.com
的 DNS 记录,拿到公钥。然后使用公钥和签名来验证邮件内容。如果验签不通过,则将邮件判定为伪造。
这里就需要我们再配置一个 DKIM 的 DNS 记录。TXT,键值是页面中的内容。
在 Mailu 的后台可以看到 DKIM 的设置
经过了 SPF 和 DKIM 的保证是不是就可以完美的发送接收邮件了,其实并不能,我们通过邮件后台来看一下邮件的原始文本。
MIME-Version: 1.0
Return-Path: xxx@fake.com
DKIM-Signature: d=fake.com,b=adceabkekd12
Date: Tue, 22 Mar 2022 06:37:58 +0000
Content-Type: multipart/alternative;
boundary="--=_RainLoop_587_997816661.1647931078"
From: admin@a.com
Message-ID: <a67d96a38592cdad46cca89e98dda26d@techfm.club>
Subject: Seems it works
To: "Somebody" <a@gmail.com>
----=_RainLoop_587_997816661.1647931078
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable
~~
----=_RainLoop_587_997816661.1647931078
Content-Type: text/html; charset="utf-8"
Content-Transfer-Encoding: quoted-printable
<!DOCTYPE html><html><head><meta http-equiv=3D"Content-Type" content=3D"t=
ext/html; charset=3Dutf-8" /></head><body><div data-html-editor-font-wrap=
per=3D"true" style=3D"font-family: arial, sans-serif; font-size: 13px;"><=
br>~~<signature></signature></div></body></html>
----=_RainLoop_587_997816661.1647931078--
SPF 解决了接收方验证发件人域名 SPF 记录内 IP 地址从而验证发件人的问题。但是因为 SPF 定义的发件人是 RFC5321 协议中规定的 Return-Path,而 DKIM 在邮件头中直接包含了域名,只要使用该域名的公钥验证通过即可。
而现在的邮件服务给用户展示的发件人都是 From 字段,而不是 SPF 的 Return-Path
,也不是 DKIM 的 DKIM-Sginatur: d=
,所以攻击者可以通过伪造这两个字段,发送如上的邮件,完美通过 SPF 和 DKIM 检查,因为 SPF 检查 Return-Path
而 DKIM 验证的 d=
也是 fake.com
所以最终用户看到的发件人却是 admin@q.com
。
所以就诞生了 [[DMARC]]。DMARC 结合了 SPF 和 DKIM,规定了 Return-Path
和 DKIM-Signature: d=
两个至少需要有一个与 From 头对应,否则判定为失败。
当邮件服务器接收到邮件时,先验证 DKIM,SPF,然后再根据 DMARC 的配置,检查。这样就能确保最终用户看到的 From
字段和 SPF、DKIM 认证的发件人一致了。
之前看到轻量云服务器做活动,500 GB 的存储空间一年只需要 9.9 元,配合之前的轻量服务器正好买了 3 年。
挂载到之前的轻量云服务器作为数据盘。
如果感兴趣可以 Follow 我的 Twitter,之后有更多的优惠会及时发出来。
腾讯轻量云 1T 磁盘一年 19.9 看着还不错啊.https://t.co/jh5IKgWSy1 pic.twitter.com/TaQHx5lxGa
— Ein Verne (@einverne) March 11, 2022
我本人的轻量云并没有那么多的流量,所以放弃了 1T 的空间选择了折中的 500G。
sudo fdisk -l
# 创建文件系统
sudo mkfs -t ext4 /dev/vdb
# 新建挂载点
sudo mkdir /mnt
# 挂载
sudo mount /dev/vdb
sudo df -TH
Linux 系统开机自动挂载磁盘
sudo blkid /dev/vdb
/dev/vdb: UUID="8cxxxxxx-9a49-49bf-8185-xxxxxxxxxxxx" TYPE="ext4"
修改 /etc/fstab
文件
《设备信息》 《挂载点》 《文件系统格式》 《文件系统安装选项》 《文件系统转储频率》 《启动时的文件系统检查顺序》
UUID=d489ca1c-5057-4536-81cb-ceb2847f9954 /mnt ext4 defaults 0 0
最后执行:
sudo mount -a
可以用 df -h
来验证。
在 Beancount 使用的过程中难免会出现周期性重复的账单,比如房租,水电,网费,以及可能的分期付款账单,每个月的订阅费用等等。在之前我都是在 Vim 下复制粘贴然后改改,倒是没有那么麻烦,比如房租,基本上几秒钟就能搞定,但是长期下来我发现这样一来比较繁琐,因为我基本上每个月只对账一次,也不会一直开着 fava,所以有可能有遗漏,排查起来麻烦,二来为了管理方便我需要单独设立一个文件来管理,比如房租会单独有一个 rent.bean
文件,所以最后并没有向日常的账单那样是按照月份来管理的,如果要从时间上来统一管理就比较麻烦。
不过随手一查文档,发现 Beancount 提供了一个插件 plugin "beancount.plugins.forecast
专门用来处理周期性账单(交易),可以按照每月费用的自动生成。并且只需要记录一次,之后可以按照设定的周期截止时间,或者执行几次来自动完成对账。体验了一下感觉非常不错,下面就记录一下使用过程。
首先在 main.bean
中引入插件:
plugin "beancount.plugins.forecast"
然后就可以在 *.bean
文件中定义分期语法。
这里举一个例子,比如每个月3000的房租:
plugin "beancount.plugins.forecast"
2021-01-01 open Expenses:House:Rent
2021-01-01 open Assets:DebitCard:CMB
2021-05-17 # "House Rent [MONTHLY]"
Expenses:House:Rent 3000 CNY
Assets:DebitCard:CMB
使用上了上述的交易之后,Beancount 会每个月自动记录一笔,在 Fava 中查看的时候会默认把今年剩余的月份都补全。注意上面的语法中 #
是必须的,并且 [MONTHLY]
定义了记账周期。
同样的,这里的 MONTHLY
可以替换成
YEARLY
,每年WEEKLY
,每周DAILY
,每天更进一步,如果已知了账单的循环次数,比如分期付款的时候分了 12 期,那么可以在 MONTHLY
后面使用如下的语法:
2022-02-08 # "Computer [MONTHLY REPEAT 12 TIMES]"
Expenses:DigitalDevice 20000 USD
Liabilities:CreditCard:CMB
其中的 [MONTHLY REPEAT 12 TIMES]
就定义了循环的次数。
另外一种情况就是不清楚次数,但是知道账单结束的时间,那么可以使用 UNTIL
语法:
2022-02-08 # "Electricity bill [MONTHLY UNTIL 2022-12-31]"
Expenses:Electricity 50.10 USD
Assets:Checking -50.10 USD
交易按照年循环,循环 10 次:
2014-03-08 # "Electricity bill [YEARLY REPEAT 10 TIMES]"
Expenses:Electricity 50.10 USD
Assets:Checking -50.10 USD
SKIP
语法可以用来跳过某一次记账:
2014-03-08 # "Electricity bill [WEEKLY SKIP 1 TIME REPEAT 10 TIMES]"
Expenses:Electricity 50.10 USD
Assets:Checking -50.10 USD
2014-03-08 # "Electricity bill [DAILY SKIP 3 TIMES REPEAT 1 TIME]"
Expenses:Electricity 50.10 USD
Assets:Checking -50.10 USD
Playbook 是一个为创意人员而设计的文件管理器,同时强调了非常方便的在线协作能力。Playbook 称自己为 Dropbox for designers。
看其官网的描述就可以知道,Playbook 是为了 Designers,Artist,Photographers,marketer 而设计的。设计师可以用它来管理设计素材,交互设计;艺术家可以用它来管理灵感;摄影师可以用它来分享摄影作品。1
在 Playbook 中每一个资源都是使用 Board 来管理,每一个 Board 都在 Playbook 中,所以从上到下的层级关系大概是,Playbook,Board,Sub-Boards, 最后就是要管理的图片资源。
现在注册 Early Access Pro account,可以得到 4TB 的永久存储空间。当然前提是 Playbook 可以一直运营下去。
最后来总结一下 Playbook,一句话说就是产品目前都是在发展阶段,目前只有基于 Web 的管理端,也没有推出任何 Native 的客户端,这也就意味着所有的用户的数据都是在其服务器中,这也就违背了我个人选择软件的第一条原则,由我产生的数据都必须有非常方便可以导出及备份的能力。所以在我这里如果以后要重度使用 Playbook ,那么我也只能将 Playbook 作为一个分享工具来使用。而本地还是保留一个与 Playbook 相似的文件结构来备份这些内容。
初步的使用感受来说,Playbook 非常适合于分享,以及多人协作的场景,如果只是个人使用的话似乎只能用来它作为产出的展示页面。