Akawa

ETY001的博客

到今天,满打满算,已经远程工作了有四年了。写下这篇,算是总结,也算是给想要远程工作的人揭开这种工作模式的面纱,并给出建议和启示。

时间先倒退到大学。大学的时候,其实对于远程工作是没有什么理解的,当时只是觉得把部里(校会网络部)的工作拿回宿舍去完成,感觉很酷炫。毕竟那时候很多资料都是存储在部里办公室的电脑里的,在宿舍做一些东西,发现缺少资料了,还要从南区最南头奔到北区最北头去取,很是麻烦。于是我通过一些技术手段,把处于内网中的办公室电脑开放到公共网络中,于是在宿舍也可以远程去操作。

到了大三快结束的时候,需要考虑是实习还是准备考研的时候,选择了实习。那时也没有想到会要去找一份远程工作,因为那个时候,国内很少看到这样的概念。可能只有黑产和灰产是远程工作吧。

投实习简历的时候,一心想要去深圳的我,最终鬼使神差的去了北京,进到了中科院计算所“打杂”。大四一年实习,之后毕业也就留在了计算所继续“打杂”。在计算所的几年,浩哥(实验室小boss)也是给了我充分的时间自由,再加上经手的项目多为外地合作项目,倒是感觉像是在远程工作。

到后来跟来爷、海辰一块创业的时候,也基本上是一周碰面一到两次讨论重要的核心问题,而多数时间,我基本上都是宅在回龙观的某出租屋里,躺在懒人沙发上码代码。

创业失败后,让我决定回家的原因有很多,但是能够支撑我做出最终决定的最大动力,就是我这好几年的远程工作的经历了。正因为有这样的经历,让我相信,我可以找到一份薪资还算说得过去的远程岗位,然后回家生活。

这个决定也算是我人生几个重要转折点之一吧。这也算是我的性格使然,追求自己想要的生活。帝都这种高房价的生活一旦开始,就真的是没有了生活,人生苦短,我觉得我没有那个兴趣去做这样的事情。尽管帝都的资源很多,机会很多,但是作为一粒沙子的我,大概率的还是会随风飘扬,而不是成为小概率的钻石。

在把所有的行李打成几个包弄回家后,我也踏上了返家的行程。

回家后头几个月,在经历了两段不是很开心的短暂远程工作后,最终自己主动降薪一半,找到了一份还算不错的远程工作。之所以说还算不错,是因为给我的时间很灵活,基本上是任务多以结果为导向,时间节点会给一个比较远的设定,整体工作压力不大。

直到前年年底,我决定辞职,脱产一年专心搞一下我自己的产品。于是就这样,开始了终极道路–自由职业者。不过最终坚持大半年后,还是弹尽粮绝了。现在又回到了远程工作的状态。

流水账写完,就来说说远程工作的一些好处,基本上这些好处,也是显而易见的。远程工作的最大好处就是不受地点限制,你可以选择回老家,也可以选择去自己喜欢的城市,可以选择在家办公,也可以选择在咖啡馆之类的公共场所里搞事情,甚至你也可以一边旅行一边工作。所有的一切都是你自己说了算,只要你赚的钱足够多就好。

其次,就是时间相对灵活。这个也要看你找到的工作的老板的要求了。如果是按周或者月来安排计划的话,每天的时间其实就非常的灵活了。除了工作时间,你的生活时间也会增加很多,因为在家办公,没有大城市的通勤时间,每天至少是可以多出来一到两个小时的。这一两个小时的时间可以拿来做很多事情,比如健身。

再来说说缺点和问题。最大的问题,就是如何把工作和生活分开。目前看,想要彻底分开是不可能的,但是需要最大限度的分开,否则真的有被拖垮的危险。由于在家办公,所以家里人有时候有事情需要跑腿,那么基本上大概率的就会把这个任务落在你的身上。除此之外还有很多琐碎的事情,也会拜托到你的身上。因为你在家里办公,大家最大的感触就是你顺手就可以把这个事情给做了。但实际到底如何,也就只有当事人最清楚了。

除了无法把自己从琐碎的家务中剥离,还有就是要承受一定的孤单。很多时候,你可能是一个人在工作,一个人在家,一个人吃饭。因为远程,你基本上没有了跟同事聊天的机会。这就会带来很多问题。你只能自己想办法去解决,尽量给自己增加一些社交机会,否则过不了几年,你可能就脱离社会比较远了。也许你刚开始远程工作的时候,由于很兴奋,感受不到。但是时间一久,就会慢慢凸现出来了。

最后一个比较严重的问题,就是亲朋好友的认可问题了。尤其是对于老一些的长辈,这种新鲜的事物未必就是他们能够接受的。他们可能觉得你整天闷在家里不务正业。这种情况没有什么好办法,你只能用事实和时间来证明你自己不是在家里不务正业。

对于选择远程工作的人,我觉得有三类,一类是家里所迫,由于家里的问题,不得不回家工作,一类是喜欢相对自由的环境,不喜欢朝九晚五的束缚,最后一类就是介于二者之间了。我想我属于第三类,但是会偏向于追求自由的那类。

做出了怎样的选择,就会过上怎样的生活。仔细考虑好,再做选择就好。对于目前国内的情况,还是慎重选择远程工作为好。

最近接了一个运维 cybex 节点的任务,过程甚是坎坷。首先,我的客户提供的服务器上有一个在运行的 eos 侧链节点,其次,cybex 节点的搭建,官方要求要做严格的防火墙设置,即入站规则中只允许指定的 IP 访问本地的指定端口,出站规则中只允许本地访问指定IP的指定端口。

这样就防火墙要求就有些棘手了。由于目前服务器上的 eos 侧链节点并没有什么防火墙配置,所以要是把 cybex 的出站白名单机制做上了,eos 侧链要想正常工作,也需要配置一份出站白名单,否则 eos 侧链节点将拿不到其他节点的同步数据了。

思前想后,觉得可以把 cybex 节点使用 docker 容器运行,这样 cybex 节点就有独立的 IP 和隔离的环境了,我只需要在宿主机,使用 cybex 容器的 IP 作为标示来过滤进出站数据包即可。

思路定下来后,开始准备实施。

由于宿主机操作系统是 CentOS7,也就是系统默认安装的是 Firewalld。经过一番学习,写出了入站规则,如下:

1
2
firewall-cmd --zone=public --add-rich-rule 'rule family="ipv4" source address="IP1" forward-port port="5000" protocol="tcp" to-port="5000" to-addr="172.20.0.2"' --permanent
firewall-cmd --zone=public --add-rich-rule 'rule family="ipv4" source address="IP2" destination address="172.20.0.2" accept' --permanent

以上只是规则的一部分,重复语法的规则就不再写了。入站主要是两种规则,一种规则是指定来源IP,访问指定端口,就像第一条,来源 IPIP1,访问宿主机 5000 端口,直接转发到容器 172.20.0.25000 端口,即这是一条 FORWARD 链的规则。另外一种规则就是指定来源 IP 可以访问容器的所有端口,即这是一条 INPUT 链的规则。

这里需要注意,由于我们需要容器的高度隔离,所以,在启动容器的时候,就不再使用 -p 参数来绑定端口了。原因我们通过 iptables -L FORWORD --line-number 可以看到如下信息,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[root@li1677-102 ~]# iptables -L FORWARD --line-number
Chain FORWARD (policy ACCEPT)
num target prot opt source destination
1 DOCKER-USER all -- anywhere anywhere
2 DOCKER-ISOLATION-STAGE-1 all -- anywhere anywhere
3 ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
4 DOCKER all -- anywhere anywhere
5 ACCEPT all -- anywhere anywhere
6 ACCEPT all -- anywhere anywhere
7 ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
8 DOCKER all -- anywhere anywhere
9 ACCEPT all -- anywhere anywhere
10 ACCEPT all -- anywhere anywhere
11 ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
12 DOCKER all -- anywhere anywhere
13 ACCEPT all -- anywhere anywhere
14 ACCEPT all -- anywhere anywhere
15 ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED
16 ACCEPT all -- anywhere anywhere
17 FORWARD_direct all -- anywhere anywhere
18 FORWARD_IN_ZONES_SOURCE all -- anywhere anywhere
19 FORWARD_IN_ZONES all -- anywhere anywhere
20 FORWARD_OUT_ZONES_SOURCE all -- anywhere anywhere
21 FORWARD_OUT_ZONES all -- anywhere anywhere
22 DROP all -- anywhere anywhere ctstate INVALID
23 REJECT all -- anywhere anywhere reject-with icmp-host-prohibited

结合 iptables 的结构流程图(http://www.zsythink.net/archives/1199/),

所有 Docker 容器的流量都是通过 FORWARD 链转发的,我们能看到上面第1\2\4\8\12行都是 Docker 产生的自定义链,并且通过 Docker 的官方文档了解到,官方不建议用户去修改 DOCKER 命名的自定义链,而是把需要增加的自定义内容放到 DOCKER-USER 这个自定义链中。而我们平时启动容器的时候,使用 -p 参数的时候,其实是把过滤规则写入了 DOCKER 链中。这就意味着,使用 -p 参数设置的转发过滤规则的优先级最高,无法用 Firewalld 来限制(17–21行是 Firewalld 产生的自定义链)。这也是我最初尝试的方案,用 -p 把容器端口映射到宿主机,然后再在宿主机配置端口策略,这个方法是不行的

写完入站规则后,也想用入站的规则改一下成出站规则,发现 Firewalldrich rules 无法做到(至少目前我是绕不出来)。于是继续看文档,发现 Firewalld 有一个 –direct 的参数可以直接使用 iptables 的语法来写入规则到 iptables 中。

又经过若干时间的学习和本地虚拟机实验,最终总结出的出站规则如下:

1
2
3
firewall-cmd  --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -p tcp -s 172.20.0.2 -d IP1 --dport 5000 -j ACCEPT
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -s 172.20.0.2 -d IP2 -j ACCEPT
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -s 172.20.0.2 -j REJECT

同样,这里也是捡出来关键的几条来展示。

这里就涉及到刚才说的,如果你要添加针对 Docker 的防火墙规则,就要把规则写入到 DOCKER-USER 中,因为 DOCKER-USER 的优先级最高。这几条规则的思路就是写出要放行的是哪些,然后最后把其他的所有的出站数据都禁掉。

这个地方,我踩到了一个还没有填的坑。

由于 DockerFirewalld 都是通过修改 iptables 来实现其相应的功能的。而 DockerDOCKER-USERFirewalld 无法管理。具体表现就是上面的规则,你执行后,由于加了 --permanent 参数,需要重载配置才能生效,但是重载配置的时候,会报错,并且新增配置并不能生效。

经过两天的搜索,最终找到了一个临时的解决方案( https://github.com/moby/moby/issues/35043#issuecomment-356036671 ) 。

这个方案的原理大概是这样的:Docker 启动的时候会去检查有没有 DOCKER-USER 链,如果没有就创建一个,如果有就不再操作了,只是检查该链的最后,是不是放行所有规则。由于 Docker 的这个特性,那么我们就手动删掉 DOCKER-USER 链,用 Firewalld 来创建并管理这个链,这样并不影响 Docker 的正常使用,且 Firewalld 也可以正常重启或者重载配置。

1
2
3
4
5
6
7
8
# Removing DOCKER-USER CHAIN (it won't exist at first)
firewall-cmd --permanent --direct --remove-chain ipv4 filter DOCKER-USER

# Flush rules from DOCKER-USER chain (again, these won't exist at first; firewalld seems to remember these even if the chain is gone)
firewall-cmd --permanent --direct --remove-rules ipv4 filter DOCKER-USER

# Add the DOCKER-USER chain to firewalld
firewall-cmd --permanent --direct --add-chain ipv4 filter DOCKER-USER

所以,先执行上面这三条命令,用 Firewalld 重建 DOCKER-USER 链,然后再执行写好的出站规则,重载配置。进入容器测试了下,出站规则生效了。

到这里,我本来以为已经完美配置结束了。正要欢呼的时候,cybex 那边的技术小哥说,我的 5000 端口为啥不通?

WTF?!

考虑了下,感觉应该是后加的出站规则影响了入站规则。我也用 nc -zv server_ip 5000 测试了下,发现会一直卡到连接超时。我猜测可能是 firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -s 172.20.0.2 -j REJECT 这条规则影响的,于是手动删除掉这条规则,再用 nc 测试就正常了。

思考了下,这个过程应该是,白名单中的外部主机向我的服务器的 5000 端口发送了握手包要建立连接,而外部主机肯定是系统随机了一个高位的端口来连接我的 5000 端口,虽然入站规则符合了,但是我回应给他的握手包却不符合我设置的出站规则,导致握手不成功。

这个思路确定后,就继续去翻 iptables 的教程,去看看这种关联性的数据包怎么处理。最终还真让我发现了,http://www.zsythink.net/archives/1597

也就是,我可以在出站规则中先加上放行这些 state 的数据包的规则,所以修改后的出站规则如下:

1
2
3
firewall-cmd  --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -m state --state RELATED,ESTABLISHED -j ACCEPT
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -s 172.20.0.2 -d IP2 -j ACCEPT
firewall-cmd --permanent --direct --add-rule ipv4 filter DOCKER-USER 0 -s 172.20.0.2 -j REJECT

这样配置后,再测试,所有配置都已达到预期,完工!

这里再补充下这种出站策略对于节点的保护的意义。

很早之前,还是 webshell 盛行的时代,有见过类似这样配置的主机。由于 webshell 的运行权限一般不是 Administrator 或者 root,所以入侵者通过 web 网站拿到 webshell 后的权限比较低,会想通过上传本地溢出漏洞代码的方式,拿到最高权限。而有时候,管理员会配置上传文件的大小或者扩展名,所以直接通过 webshell 上传是受限的,所以入侵者想到了一个曲线救国的方案,就是用 webshell 指定一个下载路径,让服务器自己去指定的下载路径下载溢出漏洞的执行代码。这时候出站规则就排上用途了,如果管理员配置了禁止所有出站链接,除了 RELATED,ESTABLISHED 这样的数据包外,那么入侵者的这个方案就不可行了。另外还有就是如果入侵者通过 webshell 成功上传了被动链接式的木马程序,出站规则一样可以拦截掉。

这就是我之前对于出站规则的理解,就是防止内鬼从外部获取数据。

但是这次这种分布式节点,为什么还要这么设置呢?在咨询了 @abit 后豁然开朗。那就是这种配置,可以避免自己服务器的 IP 暴露给不信任的节点。不过我还是觉得这种规则的必要性并不是很强。

去年年初一直想修 btsbots,但是水平有限,在解析 BTS 区块数据那块,实在是搞不定,于是就放下了。前天,花了6个小时,按照之前 btsbots 的大致逻辑,实现了核心部分,现在已经封装成了 Docker 镜像。

拉取镜像

1
docker pull ety001/btsbots-cli

创建环境变量

1
vim env

环境变量的内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 节点地址
API_URL=wss://bts.open.icowallet.net/ws
# 账号名
ACCOUNT=
# 账号active私钥
PRIV_KEY=
# 钱包密码
PASSWD=
# 做市市场 Quote:Base
MARKET=BTS:CNY
# 买单价格比率
BUY_RATE=0.01
# 卖单价格比率
SELL_RATE=0.01
# 花费base的数量
BUY_AMOUNT=10
# 得到base的数量
SELL_AMOUNT=10
# base货币最大持有量
BASE_MAX=2000
# quote货币最大持有量
QUOTE_MAX=2000

启动

1
2
3
4
5
docker run -d\
--name btsbots\
--restart always\
--env-file env\
ety001/btsbots-cli

查看log

1
docker logs btsbots

查看代码

1
2
3
4
docker run -it\
--rm\
ety001/btsbots-cli\
cat /app/bots.py

解释

btsbots 的做市逻辑就是按照比当前最高买价低 BUY_RATE 的价格下买单,比当前最低卖价高 SELL_RATE 的价格下卖单。
举例说明一下,比如目前做 BTS:CNY 市场(quote:base),当前最高买入BTS价是 0.45 CNY,最低卖出价是 0.46 CNY
你的账号目前有 200 CNY500 BTSBUY_RATE = 0.01SELL_RATE = 0.02BUY_AMOUNT=10SELL_AMOUNT=20

市场中quote是要交易的对象,也就是如果你把 BTS 设置为 quote,那么就意味着你是在拿一种货币买入 BTS
或者卖出 BTS 获取某种其他货币。

机器人的下单数据计算如下:

1.计算买入价: buyPrice = 0.45 * (1-0.01)
2.计算卖出价: sellPrice = 0.46 * (1+0.02)
3.买单买入 BTS 数量: BUY_AMOUNT / buyPrice
4.卖单卖出 BTS 数量: SELL_AMOUNT / sellPrice

符合下买单的条件:

1.当前没有买单
2.当前持有的 quote币 数量小于 QUOTE_MAX (要买的币还没买够,继续买)
3.当前持有的 base币 数量大于 BUY_AMOUNT (拿来买币的币足以下买单)

符合下卖单的条件

1.当前没有卖单
2.当前持有的 base币 数量小于 BASE_MAX (要卖的币还没卖够,继续卖)
3.当前持有的 quote币 数量大于 SELL_AMOUNT / sellPrice (要卖的币足够下卖单)

在不考虑小白用户的无脑输入异常处理,不考虑用户体验的情况下,只开发核心真的是轻松加愉快,只用了6个小时就完工了。

该程序不提供任何支持、答疑,不提供任何担保,交易风险自行承担。

该程序不提供任何支持、答疑,不提供任何担保,交易风险自行承担。

该程序不提供任何支持、答疑,不提供任何担保,交易风险自行承担。

祝玩的愉快~

之前在开发 Steem LightDB 的时候,曾经总结过 SteemOperation ,当时只是基于从数据块中总结出来了一些常见的类型。

最近在用 PHP 去实现一些公私钥生成,签名操作这样的功能,在比对着 steem-js 库在一点点用 PHP 仿写,过程真的是蛋碎一地,几度要放弃。

目前公私钥可以用 PHP 去生成了,目前正在实现如何组装 Transaction 并签名发送。

组装部分已经实现,目前就是签名这里实现很复杂,需要先对 Transaction 的原始数据 —— JSON 对象,进行转换,转换为二进制的形式,然后才是签名。

在看 steem-js 如何把 Transaction 转二进制的时候,发现了这么一段代码,里面应该就是目前 Steem 的所有操作方法吧,记录一下,说不定以后有用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
operation.st_operations = [
vote,
comment,
transfer,
transfer_to_vesting,
withdraw_vesting,
limit_order_create,
limit_order_cancel,
feed_publish,
convert,
account_create,
account_update,
witness_update,
account_witness_vote,
account_witness_proxy,
pow,
custom,
report_over_production,
delete_comment,
custom_json,
comment_options,
set_withdraw_vesting_route,
limit_order_create2,
claim_account,
create_claimed_account,
request_account_recovery,
recover_account,
change_recovery_account,
escrow_transfer,
escrow_dispute,
escrow_release,
pow2,
escrow_approve,
transfer_to_savings,
transfer_from_savings,
cancel_transfer_from_savings,
custom_binary,
decline_voting_rights,
reset_account,
set_reset_account,
claim_reward_balance,
delegate_vesting_shares,
account_create_with_delegation,
fill_convert_request,
author_reward,
curation_reward,
comment_reward,
liquidity_reward,
interest,
fill_vesting_withdraw,
fill_order,
shutdown_witness,
fill_transfer_from_savings,
hardfork,
comment_payout_update,
return_vesting_delegation,
comment_benefactor_reward
];

准备

开始

  1. 手机连接电脑,使用adb调用数据备份功能

    1
    adb backup -noapk com.xiaomi.smarthome -f backup.ab

    执行这条命令后,手机上会显示一个界面,点击允许备份即可。

  2. 使用 ADB Backup Extractor 把备份下来的 backup.ab 转换为 backup.tar

    1
    java -jar abe.jar unpack backup.ab backup.tar
  3. 解压缩backup.tar 后,在 db 目录里就能找到 miio2.db 了。

注意

对于新版的米家不再把 token 存储在 miio2.db 中的解决方案,就是去下载米家 5.0.19,这里是下载地址:
https://www.apkmirror.com/apk/xiaomi-inc/mihome/mihome-5-0-19-release/mihome-5-0-19-android-apk-download/download/

直接上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
use BitWasp\Bitcoin\Key\Factory\PrivateKeyFactory;
use BitWasp\Buffertools\Buffer;
use BitWasp\Buffertools\Buffertools;
use BitWasp\Bitcoin\Crypto\Hash;
use BitWasp\Bitcoin\Base58;
use BitWasp\Bitcoin\Bitcoin;
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\PhpEcc\Key\PublicKey;

function getPubKeyFromPrivKeyWif($wif) {
$factory = new PrivateKeyFactory();
$privKey = $factory->fromWif($wif);
$publicKey = $privKey->getPublicKey();
$pubKeyBuff = doSerialize($publicKey);
$checkSum = Hash::ripemd160($pubKeyBuff);
$addy = Buffertools::concat($pubKeyBuff, $checkSum->slice(0, 4));
$pubdata = Base58::encode($addy);
$pubKeyStr = 'STM'.$pubdata;
return $pubKeyStr;
}

function doSerialize(PublicKey $pubKey) {
$point = $pubKey->getPoint();
$prefix = getPubKeyPrefix($pubKey);
$xBuff = Buffer::hex(gmp_strval($point->getX(), 16), 32);
$yBuff = Buffer::hex(gmp_strval($point->getY(), 16), 32);
$data = Buffertools::concat($prefix , $xBuff);
// steem的compress与btc相反
if ($pubKey->isCompressed()) {
$data = Buffertools::concat($data, $yBuff);
}
return $data;
}

function getPubKeyPrefix($pubKey) {
// steem的compress与btc相反
return !$pubKey->isCompressed() ?
Bitcoin::getEcAdapter()->getMath()->isEven($pubKey->getPoint()->getY())
? Buffer::hex('02', 1) : Buffer::hex('03', 1)
: Buffer::hex('04', 1);
}

之前一直搞不出来,就是因为不知道为什么 Steem 判断压缩的变量跟 BTC 的正好是反的。不懂,先略过吧。

今天老板新招了个前端来做 AppUE 方面的工作。结果小伙无法在他的 Windows 上运行开发环境,而我在我的 KVM 虚拟出的 Win10 里测试下,完全没有问题啊。。。。

貌似小伙一直在等我给他找解决方案,也真是日了个狗了,这个不应该是你自己的事情吗?

尝试了一些方法都失败了,一直报错 webpack 找不到,应该就是他本机的环境设置的问题,但是不知道具体是什么原因。理论上讲,nodejs 都应该优先在当前项目目录下面寻找依赖,而在他机器上则偏偏不这样,即使你在启动命令里指定了目录路径、使用绝对路径,都不好使。。。这两天也真是日了狗了。。。

最后,我决定封装个开发环境的 Docker 容器。

在我的 Mac 里封装个镜像还是很轻松的,但是为了在 Win10 下安装个 Docker Desktop 也真是废了个劲了。

我的 win10 安装的是老毛子精简过的版本,2.38G,去掉了好多东西,比如最重要的 Hyper-V(终于说到了主题)。于是换了另外一个 Ghost 版的,

最终找到了下面的脚本

1
2
3
4
5
6
7
8
9
pushd "%~dp0"

dir /b %SystemRoot%\servicing\Packages\*Hyper-V*.mum >hyper-v.txt

for /f %%i in ('findstr /i . hyper-v.txt 2^>nul') do dism /online /norestart /add-package:"%SystemRoot%\servicing\Packages\%%i"

del hyper-v.txt

Dism /online /enable-feature /featurename:Microsoft-Hyper-V-All /LimitAccess /ALL

保存为 .cmd 扩展名的批处理文件,然后用管理员权限执行下,之后重启机器就可以有 Hyper-V 了。

起因

因为最近要开发 AuthSteem(一个 SteemConnectPHP 版本的复制品),其中需要到生成私钥,验证私钥之类的操作,而目前没有找到一款包含这个功能的 PHP SDK,所以要自己来实现。

代码

虽然过程很艰辛,但是代码还是很简短的,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use BitWasp\Bitcoin\Key\Factory\PrivateKeyFactory;
function generatePrivateKeysFromMainPassword($username, $mainPassword) {
$roles = ['owner', 'active', 'posting', 'memo'];
$result = [];
$factory = new PrivateKeyFactory();
foreach ($roles as $role) {
$seed = $username.$role.$mainPassword;
$brainKey = implode(" ", explode("/[\t\n\v\f\r ]+/", trim($seed)));
$hashSha256 = hash('sha256', $brainKey);
$privKey = $factory->fromHexUncompressed($hashSha256);
$result[$role] = $privKey->toWif();
}
return $result;
}
$result = generatePrivateKeysFromMainPassword('ety001', '123456');

探索过程

过程真的是很艰辛,主要时间就是在读 steem-js 的源代码中度过的。另外有一篇关于比特币地址的文章也有指导意义,地址是:https://www.cnblogs.com/kumata/p/10477369.html

总结下,生成过程是 $username, $role, $mainPassword,三者连接后的字符串做 sha256 哈希,哈希的这一步的目的是产生一个 32-bytehex 字符串,这个字符串也就是你的私钥了,只不过这个私钥还需要经过一步处理,才是我们平时看到的样子。这个过程就是 Base58

Base58 的目的就是要把 256-bit 的二进制串(也就是 32-bytehex 字符串)转换为人类可读的、相对较短、不易写错的字符串。详细的内容在上面我给出的那个文章里有。

经过 Base58 处理后,就能得到我们平时看到的私钥字符串了。

这里我找到了一个库 bitwasp/bitcoin,可以帮助我更好的完成这个过程。通过使用这个库的 BitWasp\Bitcoin\Key\Factory\PrivateKeyFactory::fromHexUncompressed() 方法,可以快速的完成这个工作。

下一步

本计划等研究出如何把私钥转成公钥后再发文,但无奈花了一个下午,还是没有弄成功,于是下一步的工作就是需要能用 bitwasp/bitcoin 库已有的方法,实现通过私钥得到公钥。

目前尝试了 bitwasp/bitcoin 库里面自带的 BitWasp\Bitcoin\Address\PayToPubKeyHashAddress 类继承的 getAddress 方法,但是得到的地址并不是 Steem 格式的公钥地址。问题应该是 Steem 得到公钥的过程跟 BTC 还是有区别的。

另外仿照 Steem-js 的这段代码https://github.com/steemit/steem-js/blob/master/src/auth/ecc/src/key_public.js#L110 改写了一段 PHP 的版本,仿写的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
use BitWasp\Bitcoin\Key\Factory\PrivateKeyFactory;
use BitWasp\Buffertools\Buffertools;
use BitWasp\Bitcoin\Crypto\Hash;
use BitWasp\Bitcoin\Base58;
function getPubKeyFromPrivKeyWif($wif) {
$factory = new PrivateKeyFactory();
$privKey = $factory->fromWif($wif);
$pubKeyBuff = $privKey->getBuffer();
$checkSum = Hash::ripemd160($pubKeyBuff);
$addy = Buffertools::concat($pubKeyBuff, $checkSum->slice(0, 4));
$pubdata = Base58::encode($addy);
$pubKeyStr = 'STM'.$pubdata;
return $pubKeyStr;
}

但是,结果依旧不对。

所以还得继续折腾。。。

起因

最近一个项目用到了 Laravel 框架的 Task Schedule 功能。

这个功能其实就是把 Laravel 的一个 Command 加到 Crontab 中,即

1
* * * * *  php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1

然后你可以在 Laravel 中配置更多的详细的计划任务。

问题

由于当前生产环境,我使用的是 Docker 来部署的。

我的 Docker 部署方案思路是用 Docker 的容器来作为运行环境,所有的网站代码通过挂载的方式,让容器读取到。具体的配置方案可以参考我之前的一篇文章 《命令行版的docker化lnmp搭建》。

这样的生产环境部署 Crontab 就有些棘手了。因为容器最好是只跑一个进程,如果用 docker execPHP 容器里执行 php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1,那就违背了这个最佳实践。而在宿主机部署计划任务,宿主机又没有 PHP 执行环境。

解决

最终我想到的方案是,按照当前 PHP 容器的配置参数,再启动一个临时容器来执行 php /path-to-your-project/artisan schedule:run >> /dev/null 2>&1

比如说当前我的 PHP 容器的启动命令是:

1
2
3
4
5
6
7
8
9
docker run -itd --name php7 \
-v /etc/php7:/etc/php7 \
-v /data/wwwroot:/data/wwwroot \
-v /tmp:/tmp \
-v /data/logs/php7:/var/log/php7 \
--restart always \
--network lnmp \
--ip "172.20.0.4" \
ety001/php:7.2.14

那么我在宿主机创建一个脚本 schedule.sh,内容如下:

1
2
3
4
5
6
7
8
9
10
docker run -i \
--user 65534:65534 \
--rm \
-v /etc/php7:/etc/php7 \
-v /data/wwwroot:/data/wwwroot \
-v /tmp:/tmp \
-v /data/logs/php7:/var/log/php7 \
--network lnmp \
ety001/php:7.2.14 \
php /data/wwwroot/xiaoyugan-server/artisan schedule:run

然后在宿主机的计划任务中添加

1
* * * * * /your-project-path/schedule.sh

这里需要注意的是,去掉了 -t 参数,否则把 schedule.sh 加入到计划任务后,会执行失败,报 The input device is not a TTY
另外需要增加 --user 参数来指定你的计划任务的执行用户。这个配置很重要!!!由于我的网站运行在 nobody 用户下,如果不增加这个配置,那么计划任务里的写日志操作,很可能在每天0点的时候触发,进而导致日志文件的属主是 root 用户,进而网站再有写日志操作就会没有权限写入了,很尴尬!
--rm 参数可以使容器里的主进程执行结束后,自动删除该容器

总结

目前看,这个方案应该是优雅的吧。不过配置起来还是很复杂,且很多点有时候想不到。就像这个 --user 参数,也是在偶然的跨天测试中发现的。像这种跨天的bug真的是很坑。。。

起因

由于目前手头所有的本不是 MacOSX 就是LInux,再或者是ChromeOS系统。有时候想找个 Windows 系统测试东西,都找不到。老想着在淘一台性价比高的 Windows 的 VPS 用,今天才反应过来,我特么的见证人服务器那么多闲置资源为啥不拿来用?!!!轻轻松松 8 核 16G,硬盘任意用啊!!!

开工

安装KVM

我的宿主机是 Ubuntu16.04 不带桌面,安装很简单,只需要一条命令:

1
apt-get install qemu-kvm libvirt-bin virtinst bridge-utils cpu-checker

安装好以后,下载 Windows10 的 ISO 镜像。

启动

下载好以后,只需要两条命令就可以启动 KVM VPS 了。

先去创建虚拟盘

1
qemu-img create -f qcow2 /data/vm_images/win10/sys.img 50G

路径和大小根据自己的情况设定就好了。

然后一条命令创建并启动 VPS

1
virt-install --name win10 --ram 16384 --file /data/vm_images/win10/sys.img --cdrom /home/ety001/win10prox64.iso --os-type windows --vnc --vncport 5900 --vnclisten 0.0.0.0 --vcpus 8

里面的参数应该都看得懂,记得加上 –vnc 参数和配置,这样才能通过 VNC 远程来操作 VPS

使用

这里不得不吐槽苹果自带的 VNC 客户端,千万不要用这个去连,因为根本连不上!!!!一开始我还在怀疑是我的 frp 端口映射软件出了问题,折腾了一天,也不行,每次连接就是在那里转菊花。。。

今天才想到为什么我不换一个 VNC 客户端呢?!正好手机上有 VNC Viewer,赶快连一下服务器所在网络的 VPN,接入后,打开 VNC Viewer,连接服务器,瞬间登陆进去,画面早已经在安装界面等待,赶紧的安装了一下,快的飞起~~~

安装后进入系统

设置开机自启动

首先需要关闭虚拟机,然后执行下面的命令

1
virsh autostart win10

win10就是你的虚拟机的名字了

增加声卡

1
2
3
<sound model='ich6'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
</sound>

列出所有主机

1
virsh list

关机

1
virsh shutdown win10

强制关机

1
virsh destroy win10

删除主机

1
virsh undefined win10

总结

最近忙到脑子经常秀逗,思路跳跃比以前差了好多。总结一下就是身边自己的资源不要忘了,苹果自家的远程还是只跟自家的配套。


2021-05-28 补充

增加声卡支持

1
2
3
<sound model='ich6'>
<address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
</sound>

注意 slot 位不要冲突。

0%