作者归档:Ferrets

DN42全攻略-part1-注册gitea

前言

最近在网上看到个很神奇的东西,叫做DN42,这是个实验性的网络,各个玩家通过VPN隧道在世界各地之间的节点建立链接,并通过BGP协议将各个玩家的网络连接到一起,模拟真实的互联网的环境。然后各个玩家可以在这个网络里面部署自己的服务,测试各种技术,实施各种一般情况下需要不少花费(比如说注册ASN,申请IP地址块,和其他人进行peer等)才能达成的操作。

这一下子就激起了我的兴趣,于是仔细阅读了官网的说明,并且根据指引,完成了注册,并peer了不少节点,完成了DNS服务的搭建,姑且算是完成了入门。于是在这里写以下文章,供后人参考。

官网的wiki关于开始的页面里面提供了相当详细的描述,这里就简单的用中文描述一下申请的流程,还有一些我踩过的坑。

准备材料

首先,你得有一些最基础的知识和材料,比如说你得知道什么是VPN、BGP等,还有需要支撑你参与到这个游戏里面的最基本的工具——一个能运行BGP的机器,你可以选择的机器有很多,从普通的x86电脑到ARM的单板机到一些家用的路由器(目前据我所知就只有Openwrt和RouterOS满足条件),都可以。主要能7*24的运行即可。

至于说其他企业级路由器就有点不幸了,因为现在网里面大家基本上都是用wireguard作为VPN隧道,而我所知的企业级路由器,绝大部分都不支持这个功能,所以强烈不推荐用企业级路由器和其他人进行互联,当然,用来做你自己的网络的核心是没问题的。

还有最后一个的话就是互联网链接,这个不多叙述,最好是有公网的ipv4和ipv6地址。

材料都准备好之后,就可以着手开搞了。

订阅消息

参考链接:https://dn42.dev/contact#contact_mailing-list

订阅的方法很简单,往dn42+subscribe /at/ groups.io发一封邮件就行,然后会有一封自动回复的邮件,让你确认是否要订阅。这时候再随便回复点什么就可以完成订阅了。

完成订阅之后就可以进行下一步,也就是正式开始申请资源了。

注册git.dn42.dev

dn42的整个registry信息都是gitea来存储的,所有人都可以pull来获取全部内容,如果想要申请点什么资源,添加点什么记录,方法就是通过PR(pull-request)来向管理员申请。在管理员审核之后,会将你的申请合并进去,就可以让大家都查看到你的信息了。一般来说,管理员每天做一次审核。

大神burble做了一个在线的浏览工具可以查看现在registry里面的内容

registry里面的结构是这样的,不同的文件夹里面以纯文本的方式记录着不同的内容。

.
├── data
│ ├── as-block
│ ├── as-set
│ ├── aut-num
│ ├── dns
│ ├── inet6num
│ ├── inetnum
│ ├── key-cert
│ ├── mntner
│ ├── organisation
│ ├── person
│ ├── registry
│ ├── role
│ ├── route
│ ├── route-set
│ ├── route6
│ ├── schema
│ └── tinc-key
└── utils
└── schema-check

了解到大概的流程之后,就可以着手开干了。

首先,打开https://git.dn42.dev/dn42/registry,会提示登录,这时候注册一个账号,注册完成之后重新打开链接,就能看到默认的readme了。

这时候先去创建一个PGPkey,因为你必须对你提交的内容进行一个签名,于此同时,PGPkey也可以用来证明你的身份,所以要好好保存,不要泄露。

我使用的密钥管理工具是kleopatra,如果你之前没有PGPkey的话,可以用这个工具创建一个新的PGP密钥对。注意,创建的时候需要保证密钥对里面的邮箱地址是你用来注册git.dn42.dev的那个。

在创建完密钥对之后,右键选择对应的key,选择导出,可以导出对应的公钥。

导出来的文件可以直接用文本编辑器打开,就是公钥的内容了。

然后全选,复制。再打开刚完成注册的gitea,进去个人设置,找到SSH/GPG密钥,然后将复制出来的内容填到内容框里面,然后点击“增加密钥”。

保存好之后,再点击增加的密钥旁边的“验证”

系统会生成一段随机的文字,复制出来,粘贴到klepatra的记事本标签页。然后点击“签名记事本”,会找你输入生成GPG密钥对的时候的密码。

输入正确之后,会对记事本的内容进行签名。

再将记事本里面的全部内容复制出来,粘贴到网页上的GPG增强签名文本框里面,点击验证。即可完成验证,之后你就能用这个GPG密钥来对git push进行签名了。

又或者直接复制页面上那段,直接在终端里面运行。会得到一样的签名信息。

echo "911ebb86da4234f38cb65f3d670798422a03205a5cbaba25fb8e6e82da5ab468" | gpg -a --default-key A67CBCEB66739B68 --detach-sig

到此就完成了前期的准备工作,下一步就是fork整个registry,修改本地内容,然后重新push并请求PR,这个下一篇再说。

Windows简易TCP端口转发

由于某些特殊的原因,我临时需要在一个windows的机器上做一个tcp转发。网上各种奇奇怪怪的软件要下载下来还需要额外担心一下是不是有病毒什么的。不过经过一番搜索,我找到windows自带端口转发的功能,是netsh命令的一部分。

微软有一个相关的说明文档,里面有具体的说明。根据网上搜索得到的结果来看,是从windows vista的年代开始内置,不需要额外的软件。

使用方法也很简单:

#新建一个转发,监听到192.168.1.111:4422,所有进来的流量,转发到192.168.0.33:80
netsh interface portproxy add v4tov4 listenport=4422 listenaddress=192.168.1.111 connectport=80 connectaddress=192.168.0.33
#列出当前所有转发
netsh interface portproxy show all
侦听 ipv4:                 连接到 ipv4:

地址            端口        地址            端口
--------------- ----------  --------------- ----------
192.168.1.111   4422        192.168.0.33    80
#删除转发
netsh interface portproxy delete v4tov4 listenport=4422 listenaddress=192.168.1.111

网上还有控制这个端口转发的图形界面软件,可以免除手动输入命令的功夫。

另外,这个转发收到防火墙的保护,意味着你打开端口需要手动调整防火墙。所以流程就是以下的二选一

  • 增加转发->打开防火墙->重启电脑->生效;
  • 打开防火墙->增加转发->生效;

所以建议先操作防火墙。

在Linux中为主机添加一个地址段

一般情况下,一台主机只会有一个,或者几个ip地址,这种情况下,配置ip地址的方法有非常多文章可以搜索到,这里不加赘述。但是如果我想给主机配置很多ip地址,类如ipv6配置一个/64的地址块呢,这时候一个一个的在配置文件里面写配置显得非常的不实际。

万幸,网上有人问过这个问题,里面提到一个叫做AnyIP的东西,可以让linux响应多个ip地址,配置方法也很简单,就写一条静态路由就行了。

ip route add local 2001:db8::/32 dev lo

和平时添加静态路由差不多,就是在目标地址前面加一个“local”,然后目标设备是lo即可。

Mikrotik动态更新从PPPOE获得的dns服务器

手里的那只RB750Gr3之前给几个出租屋的房间配置dhcp的时候,都是将客户端的dns指向RB750Gr3自己作为网关的ip地址,这样,RouterOS会作为一个DNS代理,查询和缓存DNS请求。这样设置其实没啥问题,只是这几天又突发奇想,想着要不直接分发运营商的DNS服务器地址给用户,如此一来,可以稍微的降低一下路由器的负载(其实没多少)。

Mikrotik的脚本功能其实比较强大,但是相关的文档就惨不忍睹,好在论坛上有好心人回答问题,给我提供了一些思路。以下是我写的脚本。

:local dynamic (([/ip dns print as-value]) -> "dynamic-servers" );
:local dns

:set dynamic [:tostr $dynamic]

:for i from=0 to=([:len $dynamic] - 1) do={ 
 :local char [:pick $dynamic $i]
 :if ($char = ";") do={
  :set $char ","
 }
 :set dns ($dns . $char)
}

/ip dhcp-server network set [ find comment=UserNet ] dns-server=$dns

简单的解释以下脚本内容:

  • 1.从/ip dns里面读取“dynamic-servers”的内容,这些ip地址是pppoe拨号之后,从pppoe服务器上获取到的。
  • 2.要将dynamic的内容填充到dhcp-server里面,但是因为读取到的内容是一个array,用”;”分隔,没法直接套用,所以,需要转换成string类型。
  • 3.然后将”;”替换成”,”,然后就可以在修改配置的命令行里面调用了。
  • 因为配置里面,包含了多个dhcp-server,而我不需要修改全部的network的配置,所以,我给需要修改配置的network,写了一个备注,内容是“UserNet”。这样脚本就会只修改备注内容是“UserNet”的network配置了。

脚本做好之后,就是调用了,可以选择定时执行脚本或者是在pppoe会话起来的时候执行,我选择的是在pppoe会话起来的时候执行。找到pppoe调用的ppp profile,在scripts里面调用就行了。

使用OPNsense来保护内网的服务

因为在家里部署了不少应用服务,所以,也想将一些服务暴露到公网上,但是呢,直接暴露的话,会有非常大的风险。全部走vpn也略微有点麻烦,虽然最近看到一些油管的UP主在推荐cloudflare的tunnel服务,但是我都自己托管了,还走cloudflare是不是有点……愚蠢呢。

因为工作的原因有接触过pfSense,然后也尝试过其分支OPNsense,一番测试之后,发现大有可为,不得不说真好使。

安装OPNsense

就是很普通的用iso引导,然后根据屏幕上的说明一路下一步就行。安装完毕之后,顺手更新一下系统。调整一下时区、默认语言等。

安装必要的插件

最主要的插件只有2个,一个是os-acme-client,另外一个是os-nginx。acme负责签署证书,而nginx,则负责做反向代理。

自动注册证书

从侧边栏访问“服务-ACME客户端”,默认的设置页面会报错,这时候,需要切换成英文,就能够正常打开页面了。跑去settings页面,打开启用插件和自动更新,应用。剩下的就可以切换回中文了。

下一步,就是创建一个账号,只要填一下邮箱地址,其他默认就行。

下一步是添加一个“挑战类型”,这里其实翻译成“验证类型”会比较好……

由于国内的环境,80和443端口都不用想了,所以只能走dns,具体用哪一家的dns,就根据对应的服务商的文档来配置就行。

下一步是创建一个自动操作,这里选择重启Nginx,这样证书在续签之后会自动重启nginx,并应用新的证书。

然后就是创建一个证书(申请)

注意这里的域名应该这样设计:一个通用域名(example.com),还有其下的通配符域名(*.example.com)。到时候,example.com应该返回一个404;具体对应的系统,则应该放在子域名来访问,例如nextcloud.example.com。另外,所有的应用都应该使用同一个监听端口,除非是由于某些原因,只能使用”流“(stream)代理。如此,可以最大限度地隐藏服务入口。

最后,自动操作里面选择一下刚创建的重启nginx的操作。保存。

保存之后出来可以手动触发一下证书签署,顺利的话就直接拿到一个可以用的证书了。

配置NGINX

OPNsense使得nginx的配置简单的很多,只要点点点就行,而且附加了很多安全方面的设置,有很多值得学习的内容。

创建错误提示页面

为了误导可能的攻击者,我们需要整点花活,因为默认情况下,被策略拦截之后,会向客户端返回一个页面,提醒被OPNsense上的策略拦截了。这可不行,怎么能让对方知道我们用了什么东西的呢对吧。我们来创建一个错误页面,整一个Nginx默认的500错误页面。并且匹配所有的状态码。

这样,无论是什么错误,用户都只能看到一个500的错误。(邪恶)

创建一个默认的服务器

设计的思路在证书那里已经说过了,首先,创建一个默认的服务器,在访问域名不正确的情况下,所有的访问都会跑到这里来:

位置

首先创建一个位置(HTTP(s)-位置),打开高级模式,修改以下的内容,确认保存:

  • 描述:随便填写
  • 网址格式:/
  • 强制HTTPS:勾选
  • honeypot:勾选

这里解释一下这个honeypot,在打开这个参数之后,如果访问了这个位置,就只会返回一个403,并且源地址将会被记录,然后屏蔽。具体屏蔽的时间,可以在“常规设置”的页面,打开高级选项,就可以自定义屏蔽的时间了。

HTTP服务器

然后创建一个服务器(HTTP(s)-HTTP服务器),修改这些内容:

  • HTTP监听地址:空
  • HTTPS监听地址:0.0.0.0:8443(你应该换个别的端口号)
  • TLS证书:(刚签署的那个)
  • 仅HTTPS:勾选
  • 错误页面:刚创建的糊弄人的默认错误页面

配置好DNS之后就可以测试一下是否生效了。尝试访问一下这个服务器,会得到一个空白页。然后在禁止的页面上就能看到这个ip进入了拦截清单了。

调整防火墙策略

最后一步就是调整防火墙策略,新增一个策略,拦截掉这些坏东西的访问:

  • 操作:阻止
  • 源:nginx_autoblock

这样,那些来源不明的访问就会被拦截。

应用保存之后,一个基本的拦截就做好了,攻击者在不知道你有什么域名的情况下,如果访问了OPNsense,就会被关小黑屋,被防火墙拦截,而被防火墙拦截的情况下,其他的端口也会无法访问。

另外,由于我们签署的证书包含2个域名,一个是example.com,另外一个是*.example.com。所以,即便是通过这次的访问,拿到了SSL证书,也不会知道我们用的是什么域名。这就能最大限度地隐藏访问入口。

添加需要暴露的服务器

基础搭建好了之后,就是增加需要通过nginx反向代理的服务了。一共有4个步骤,简单的说法:

  1. 添加一个“上游服务器”
  2. 添加一个“上游”
  3. 添加一个“位置”
  4. 添加一个“HTTP服务器”

上游服务器

需要填写的一共4项,描述、服务器、端口还有优先级。这里特别说明以下,因为设计上,是有多个后端的,所以必须要填上一个优先级。因为一般只有1个后端,所以……随便填一个数字就行。

上游

这里的话,只要填写描述,然后,服务器条目选择刚创建的上游服务器即可。值得注意的是,如果被反代的服务器是使用https的,则需要勾选上“启用TLS(HTTPS)”

位置

位置的话,刚才在创建一个默认的服务器的时候已经讲过了,这次稍有不同的地方,就是有一个额外的选项需要选择,就是“上游服务器”,这里选择刚才创建的“上游”即可。

HTTP服务器

可以参考上面的创建http服务器,和之前的主要差别,只有1个,那就是需要指定一个你自己才知道的属于*.example.com的子域名。由于证书是泛域名证书,所以所有子域名都能够使用。

应用配置

一切搞定之后,点击应用,保存并生效。

最后,在调整完dns之后,你就能用https://private.example.com:8443/来访问你需要暴露的服务了。有多个应用的情况下,重复创建上游服务器、上游、位置、http服务器,即可。

Mikrotik RB750Gr3优化配置,跑(差点)满千兆

开篇

故事背景是这样,出租屋那里有一只RB750G r3做出口路由器,之前由于互联网线路是300M的,所以在配置方面,是比较随意,就真把这小东西当成一个能5个口都跑满线速度的路由器来规划接口的链接方式了。

可是,最近由于电信将宽带免费(是的,只要满足月消费多少多少,就免费)提升到1000M下行还有50M的上行,这时候,之前那随心所以的配置方法,就会让这只路由器的转发能力下降。于是我废了相当大的功夫(这文档写的十分隐晦,非常难读懂),进行了一番优化,终于还是能发挥出其全部性能,跑到900M也不是什么大问题。(包含一定的防火墙策略,不是简单的裸奔)

首先,还是上相关资料:

这个路由器非常的奇怪,Mikrotik提供了2个block-diagram,具体怎么翻译这个不好说,但从结果来看的话,我觉得可以翻译成“硬件拓扑图”

根据说明,这是禁用了“交换”时候的分布
这个是启用了“交换”时候的硬件分布

根据分布图,明显是启用“交换”的时候,能够发挥最大的性能,1个上行口,其余4个交换,那么对于CPU来说就是1Gb/s入,1Gb/s出,非常合理。

但是要怎么打开这个“交换”呢?产品页面里面没有任何说明……

于是经过一番苦苦搜索,我找到了第二个链接,里面介绍了一位前辈的操作过程,里面提到了一个很重要步骤:

Make sure the bridge is created without spanning tree enabled (ie, no STP/RSTP/MSTP) as the switch chip cannot handle that, and so using those features will force slow software switching. The default mode is RSTP, which will turn off hardware switching on several chipsets, including the RB750Gr3/hEX one.

你的bridge需要关掉STP。哈啊?这玩意不是默认打开的吗?于是我自己看来一下:

我丢,这玩意用的MT7621,由于不支持这些那些功能,所以呢,必须要关掉,否则的话,就会变成内核转发。我想我大概找到问题所在了。

规划

首先,重新规划好端口,按照1上行,4下行的方式划分端口,下行的4个接口都划分到同一个bridge,多子网的情况下,可以用vlan接口或者是做静态路由。

规划过程中有这几点需要注意(仅对于Ros v6有效,Ros v7有改进,可能有更多功能,但是由于是生产环境我暂时还不敢升级):

  • 建议只使用一个bridge,因为只有1个bridge能off-load到硬件
  • 不支持给没vlan标签的包加上vlan标签,即所有access口都只能在同一个vlan,和bridge(无vlan标签)处于同一个子网
  • 不能使用链路聚合
  • 不能使用QinQ

打开“交换”

调整交换机的配置,检查一下多余的功能是否都已经关闭,必要的功能都打开:

  • bridge的protocol-mode必须是none
  • bridge的igmp-snooping必须是no
  • bridge的dhcp-snooping必须是no
  • bridge的vlan-filtering必须是no
  • /interface bridge settings里面allow-fast-path必须是yes
  • /ip settings里面allow-fast-path必须是yes

配置示例

下面是一个示例的命令行:

/interface bridge
add name=local protocol-mode=none vlan-filtering=no
/interface bridge port
add bridge=local interface=ether2
add bridge=local interface=ether3
add bridge=local interface=ether4
add bridge=local interface=ether5
/interface bridge vlan
add bridge=local untagged=local,ether2,ether3,ether4 vlan-ids=1
add bridge=local tagged=local,ether4,ether5 vlan-ids=2
add bridge=local tagged=local,ether4,ether5 vlan-ids=3
/interface vlan
add interface=local name=guest vlan-id=2
add interface=local name=server vlan-id=3
/ip address
add address=192.168.1.1/24 interface=local
add address=192.168.2.1/24 interface=guest
add address=192.168.3.1/24 interface=server

以上是一个简单的示例,创建了一个bridge,2口和3口都是Access模式,划分到一个子网里面。4口是hybrid模式,没有vlan标签的包,会和2、3口处于同一个子网,同时,能够接受带vlan-id=2的包。5口也是Trunk模式,分别接受vlan2和vlan3的包。然后创建了2个vlan接口,“guest”、“server”。然后local、guest、server3个接口都配置了ip地址。

顺利的话,就能在/interface bridge port print里面看到行的开头有个H的标识,代表已经使用了硬件卸载。同时,系统日志里面也会有相关提示。

测速结果

J4125的小主机上分了1核心+1G内存的XP,跑了以上的测速结果,期间基本上没其他人同时用网。也基本上差不多了吧。

Ros V7更新

自从RouterOS v7.1rc5之后,MT7621(就是RB750Gr3所使用的CPU/交换芯片),更新了驱动,支持了vlan表,所以,可以给access口的包打标签了。另外的话,也提供了STP的硬件支持。

对比v6的设置,有这么一些不同:

  • bridge的protocol-mode可以是STP/RSTP/MSTP
  • bridge的igmp-snooping必须是no
  • bridge的dhcp-snooping必须是no
  • bridge的vlan-filtering可以是yes
  • /interface bridge settings里面allow-fast-path必须是yes
  • /ip settings里面allow-fast-path必须是yes

重点注意

根据观察和mikrotik的论坛报告,系统更新到了Ros V7之后,转发性能有所下降,根据我观察所得,基本上从之前的约800+Mbit/s下降到了600~700Mbit/s,感觉有点难受。

不过偶然间,我发现了一个油管视频:https://www.youtube.com/watch?v=bllvDWEKgNA,还有论坛里面的一些帖子:https://forum.mikrotik.com/viewtopic.php?p=959710#p959710,里面提到,这机器刷了OpenWRT之后,可以打开硬件转发,可以在跑满950+Mbit/s转发的情况下,99% CPU idle。就是说,如果Mikrotik继续开发,是可以将l3hw下放到这个小机器上的。不过……鬼知道要等到什么时候。

配置示例.v7

/interface bridge
add name=local protocol-mode=RSTP vlan-filtering=yes
/interface bridge port
add bridge=local interface=ether2
add bridge=local interface=ether3
add bridge=local interface=ether4
add bridge=local interface=ether5
/interface bridge vlan
add bridge=local tagged=local,ether5 untagged=ether2 vlan-ids=1
add bridge=local tagged=local,ether5 untagged=ether3 vlan-ids=2
add bridge=local tagged=local,ether5 untagged=ether4 vlan-ids=3
/interface vlan
add interface=local name=lan vlan-id=1
add interface=local name=guest vlan-id=2
add interface=local name=server vlan-id=3
/ip address
add address=192.168.1.1/24 interface=lan
add address=192.168.2.1/24 interface=guest
add address=192.168.3.1/24 interface=server

例子中,ether2、ether3、ether4均为access口,ether5为trunk口。划分了3个vlan,分别名字为lan、guest、server。

用smartdns来防治DNS污染

一直以来,我对dns查询受到污染的情况一直都比较苦恼。国内的DNS服务器基本上都是受污染的,而通过VPN直接去从国外的DNS服务器查询,则会获得为国外优化的结果,时常莫名奇妙就访问国外的网站,特别是一些有中国版和国际版区别的站点,诸如京东淘宝之流。

今天和在和朋友讨论dns查询的问题的使用,对方提到用tcp来进行dns查询,这就让我感到一些神奇。在拿8.8.8.8来进行过测试之后,我有了一个重大的发现。

使用tcp向8.8.8.8进行dns查询

8.8.8.8的tcp/53端口是开放的!而且有个很重要的特征!对于受屏蔽的域名,dns查询的会话会收到tcp rst导致中断,而未受屏蔽的域名,则可以正常查询。

那好说!只要找到一个DNS转发器,能支持用TCP来进行上行查询的软件就行了(当然,还有一个能用的梯子)。

最开始,我是打算用dnsmasq的按顺序查询,不过dnsmasq似乎不支持tcp查询,不得不放弃。然后又想起另外一个东西叫做smartdns的,能够使用tcp进行dns查询。那就好办。

思路是这样,现调整好vpn的路由。8.8.8.8不走vpn,8.8.4.4走vpn,然后smartdns配置server-tcp 8.8.8.8和server-tcp 8.8.4.4。这种情况下可以同时获得国内优化版和国际优化查询结果,这两者都是未经污染的,无需额外过滤。再由smartdns进行探测,得到访问速度最快的服务器(一般都是国内的)。搞定。

Proxmox中LXC容器中docker调用宿主机的显卡加速

刚整了个AgentDVR,里面设置支持GPU解码,于是寻思着整一个,因为J4125的CPU带6路视频录像确实有点吃力,即使是使用了摄像头的stream2,用的是低分辨率的视频流来进行分析,对这个轻量级的CPU来说还是有点吃力。

于是进行了一番Google之后,顺利实现,下面记录一下过程。注意的是,我这里使用的是intel的核显,也就是vaapi,如果是AMD或者Nvidia的显卡的话,肯定会有点不一样,具体的差别需要继续搜索。

宿主机安装驱动

首先,安装显卡驱动和相关的查看工具

apt install i965-va-driver intel-media-va-driver vainfo

根据包的说明i965-va-driver是给Intel G45 & HD Graphics的显卡驱动,而intel-media-va-driver是8代之后的显卡驱动,可以根据实际情况酌情选择。至于vainfo,就是查看相关信息的工具。安装之后执行vainfo,可以看到显卡的加速信息:

root@pve4:~# vainfo
error: can't connect to X server!
libva info: VA-API version 1.10.0
libva info: Trying to open /usr/lib/x86_64-linux-gnu/dri/iHD_drv_video.so
libva info: Found init function __vaDriverInit_1_10
libva info: va_openDriver() returns 0
vainfo: VA-API version: 1.10 (libva 2.10.0)
vainfo: Driver version: Intel iHD driver for Intel(R) Gen Graphics - 21.1.1 ()
vainfo: Supported profile and entrypoints
      VAProfileMPEG2Simple            : VAEntrypointVLD
      VAProfileMPEG2Main              : VAEntrypointVLD
      VAProfileH264Main               : VAEntrypointVLD
      VAProfileH264Main               : VAEntrypointEncSliceLP
      VAProfileH264High               : VAEntrypointVLD
      VAProfileH264High               : VAEntrypointEncSliceLP
      VAProfileJPEGBaseline           : VAEntrypointVLD
      VAProfileJPEGBaseline           : VAEntrypointEncPicture
      VAProfileH264ConstrainedBaseline: VAEntrypointVLD
      VAProfileH264ConstrainedBaseline: VAEntrypointEncSliceLP
      VAProfileVP8Version0_3          : VAEntrypointVLD
      VAProfileHEVCMain               : VAEntrypointVLD
      VAProfileHEVCMain10             : VAEntrypointVLD
      VAProfileVP9Profile0            : VAEntrypointVLD
      VAProfileVP9Profile2            : VAEntrypointVLD

能看到显卡支持的编码,就算是成功了。这时候,可以在母机的/dev/dri/文件夹下面看到以下内容:

root@pve4:/etc/pve/lxc# ls -als /dev/dri
total 0
0 drwxr-xr-x  3 root root        100 Mar 12 16:22 .
0 drwxr-xr-x 23 root root       5140 Mar 17 07:40 ..
0 drwxr-xr-x  2 root root         80 Mar 12 16:22 by-path
0 crw-rw----  1 root video  226,   0 Mar 12 16:22 card0
0 crw-rw----  1 root render 226, 128 Mar 12 16:22 renderD128

将显卡透传给LXC容器

先给上两个参考文档:

简单来说,就是直接用文本编辑器修改lxc容器的配置文件,加上这么一块

lxc.cgroup2.devices.allow: c 226:0 rwm
lxc.cgroup2.devices.allow: c 226:128 rwm
lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file
lxc.mount.entry: /dev/dri/card0 dev/dri/card0 none bind,optional,create=file

根据proxmox论坛里面的说明,如果是proxmox 6或者之前的,就用lxc.cgroup。如果是Promox 7或者之后的,就用lxc.cgrpup2。保存退出。

启动LXC容器,再看看/dev/dri/就能看到已经挂载了:

root@ispy:~# ls -als /dev/dri
total 0
0 drwxr-xr-x 2 root   root          80 Mar 17 09:44 .
0 drwxr-xr-x 8 root   root         520 Mar 17 09:44 ..
0 crw-rw---- 1 nobody nogroup 226,   0 Mar 12 08:22 card0
0 crw-rw---- 1 nobody nogroup 226, 128 Mar 12 08:22 renderD128

然后,就是最后一个问题,权限问题,由于lxc容器有隔离,从上面的结果可以看到card0和renderD128被映射成了nobody:nogroup的ownership。这时候就有这么个几种解决办法:

简单粗暴,母机对两个设备给与666的权限

在母机上,直接运行

chmod 666 /dev/dri/card0 /dev/dri/renderD128

使得显卡所有人可以读写。但是这种方法存在一定的安全隐患,所以用来测试配置是否成功的时候可以用,测试OK的话,应该换个别的方法来赋予权限。

idmap+usermod

思路是这样,lxc容器有个设定,叫做idmap,用来控制容器内部的uid和gid如何映射到母机上对应的部分的。例如这个参考:https://bookstack.swigg.net/books/linux/page/lxc-gpu-access

首先,考虑到两个设备默认的权限是660,所以我们有几个方向:

  • 将LXC容器中需要调用显卡的用户映射为母机的root(明显有严重的安全隐患,而且无法处理多个用户需要调用显卡的情况,pass)
  • 修改/dev/dri/card0 /dev/dri/renderD128的ownership,然后和上面一样将LXC容器中需要调用显卡的用户映射为这个新的owner(也还行,但是同样无法处理多用户同时调用,而且母机系统的变动比较多)
  • 将LXC容器中的video组和render组映射到母机的video和render组,然后将需要调用显卡的用户都加入这两个组(不错,但是对于不同的系统,就需要检查对应的/etc/group,并进行对应的调整)

我选择第3种方法。

首先,要确认一下系统的video组和render组,首先是母机的,因为系统都是proxmox(debian),所以gid理论上都是一样的,video的gid是44,render是103:

root@pve4:/etc/pve/lxc# cat /etc/group
……
video:x:44:root
……
render:x:103:root
……

然后就是检查LXC容器的用户组,我用的是Ubuntu20.04,video是gid是44,render是107:

root@ispy:/# cat /etc/group
……
video:x:44:
……
render:x:107:

准备工作就做好了。接下来关掉容器,直接用文本编辑器修改LXC容器的配置文件,加入这么一块内容:

lxc.idmap: u 0 100000 65535
lxc.idmap: g 0 100000 44
lxc.idmap: g 44 44 1
lxc.idmap: g 45 100045 62
lxc.idmap: g 107 103 1
lxc.idmap: g 108 100108 65427

下面给一点解释:

lxc.idmap: u 0 100000 65535		//映射LXC容器中的uid,将容器中[0-65535)映射为母机的[100000-165535)
lxc.idmap: g 0 100000 44		//映射LXC容器中的gid,将容器中[0-44)映射为母机的[100000-100044)
lxc.idmap: g 44 44 1			//映射LXC容器中的gid,将容器中[44-45)映射为母机的[44-45)
lxc.idmap: g 45 100045 62		//映射LXC容器中的gid,将容器中[45-106)映射为母机的[100045-100106)
lxc.idmap: g 107 103 1			//映射LXC容器中的gid,将容器中[107-108)映射为母机的[103-104)
lxc.idmap: g 108 100108 65427	//映射LXC容器中的gid,将容器中[108-65535)映射为母机的[100108-165535)

然后就是要配置/etc/subgid文件,添加以下内容:

root:44:1
root:103:1

最后,把LXC容器开起来,将需要调用显卡的用户加入两个组:

root@ispy:/# usermod -G video root
root@ispy:/# usermod -G render root

执行测试

测试的方法和母机差不多,都是用vainfo来检查,或者使用ffmpeg来测试,但这两个都需要安装一堆软件包,所以这时候,先给容器打个快照。

vainfo的方法可以参考母机,不同的发行版有不同的包管理器,安装vainfo然后运行就能看到结果了。

另外一个方法是使用ffmpeg,这个更接近实际应用,毕竟很多应用也是通过调用ffmpeg来执行编解码的操作。具体ffmpeg的使用方法可以参考官方的文档

这里给出比较有用的一个参考:

ffmpeg -hwaccel vaapi -hwaccel_output_format vaapi -i input.mp4 -f null -

这样可以让ffmpeg调用vaapi来进行解码,并且不输出任何东西,如果能够顺利解码,那就算是成功了。

LXC透传给Docker

透传给Docker的部分不算非常困难,只需要在docker-compose文件里面加上这么一节就可以透进去了:


version: '2.4'
services:
  agentdvr:
    image: doitandbedone/ispyagentdvr
    restart: unless-stopped
    environment:
      - TZ=Asia/Shanghai
    ports:
      - 8090:8090
      - 3478:3478/udp
      - 50000-50010:50000-50010/udp
    volumes:
      - ./ispyagentdvr/config:/agent/Media/XML
      - ./ispyagentdvr/media:/agent/Media/WebServerRoot/Media
      - ./ispyagentdvr/commands:/agent/commands
    devices:
      - /dev/dri:/dev/dri

但是我们遇到了和LXC透传的时候一样的问题,文件权限的问题。ispy提供的docker镜像是基于Ubuntu18.04的,


root@34c54716eee7:/# ls -las /dev/dri
total 0
0 drwxr-xr-x 2 root   root        80 Mar 17 17:44 .
0 drwxr-xr-x 6 root   root       360 Mar 17 17:44 ..
0 crw-rw---- 1 nobody video 226,   0 Mar 12 16:22 card0
0 crw-rw---- 1 nobody   107 226, 128 Mar 12 16:22 renderD128
root@34c54716eee7:/# cat /etc/group 
root:x:0:
……
video:x:44:
……
messagebus:x:102:

容器里面就没有render组,root用户也不在两个用户组里面。于是还得自己build一下镜像……

root@ispy:~/agentdvr# cat build/Dockerfile 
FROM doitandbedone/ispyagentdvr
RUN     groupadd -g 107 render
RUN     usermod -g 44 root
RUN     usermod -g 107 root

完了之后就可以在docker 容器中调用GPU进行加速了。

Linux上iptables端口触发拦截

之前整过在RouterOS上异常的访问触发防火墙拦截ip地址访问,就一直想給VPS套上这种拦截策略,以提高安全性,拦截那些奇奇怪怪的扫描,最近偶然看到了iptables里面的一个模块——ip_recent。

这个模块现在已经改名了,叫做xt_recent,可以达到我所想要的目的,这里记录一下用法:

xt_recent

首先,检查一下模块的情况,用modinfo来看看模块的情况

root@ferrets:/proc/net/xt_recent# modinfo xt_recent
filename: /lib/modules/5.10.0-9-amd64/kernel/net/netfilter/xt_recent.ko
alias: ip6t_recent
alias: ipt_recent
license: GPL
description: Xtables: "recently-seen" host matching
author: Jan Engelhardt [email protected]
author: Patrick McHardy [email protected]
depends: x_tables
retpoline: Y
intree: Y
name: xt_recent
vermagic: 5.10.0-9-amd64 SMP mod_unload modversions
sig_id: PKCS#7
signer: Debian Secure Boot CA
sig_key: 4B:6E:F5:AB:CA:66:98:25:17:8E:05:2C:84:66:7C:CB:C0:53:1F:8C
sig_hashalgo: sha256
signature: 54:BB:49:31:44:96:65:97:63:67:BA:20:96:4A:85:29:C6:F2:0C:19:
69:FC:31:34:C7:1E:82:9C:13:90:80:E2:7C:9B:2E:46:1F:32:46:A3:
E0:29:55:01:E4:0E:24:5A:EE:67:C2:6D:45:16:ED:0A:51:AC:CE:A7:
66:F4:CE:3D:2E:F4:13:32:23:87:0C:A2:77:3D:72:99:96:CB:36:6E:
3C:43:15:52:8E:F5:59:31:1B:A1:4F:2D:B2:08:81:AC:F7:B3:0D:80:
D7:0E:DD:78:94:50:F9:8A:CA:3F:5C:6D:AB:4A:B9:E3:94:87:64:7F:
97:08:4F:4A:D3:7E:44:54:8F:F3:03:B8:A8:48:8E:5D:7C:5C:9F:FE:
4E:49:60:63:57:EC:62:57:0C:5C:9F:1F:36:FE:D3:E6:EF:F3:AB:C5:
D2:A4:0E:9C:B2:89:52:55:8A:1F:BF:92:DE:30:19:61:75:A6:22:BC:
A4:DD:2D:B7:8B:74:65:61:A6:4D:75:0E:9B:05:EB:33:79:46:88:13:
71:A3:3F:5C:4B:3C:11:85:EF:B0:F9:CD:45:DE:28:12:84:EB:D1:79:
32:38:19:D3:9F:55:7D:01:27:6C:A2:FB:96:18:7D:30:4E:F7:B4:4F:
33:DA:B6:5D:A2:F9:5E:B0:D2:17:4D:14:12:39:D7:00
parm: ip_list_tot:number of IPs to remember per list (uint)
parm: ip_list_hash_size:size of hash table used to look up IPs (uint)
parm: ip_list_perms:permissions on /proc/net/xt_recent/* files (uint)
parm: ip_list_uid:default owner of /proc/net/xt_recent/* files (uint)
parm: ip_list_gid:default owning group of /proc/net/xt_recent/* files (uint)
parm: ip_pkt_list_tot:number of packets per IP address to remember (max. 255) (uint)

xt_recent上记录ip地址和RouterOS的addresslist设定稍微有点不一样。xt_recent有个参数ip_list_tot,这里会指定单个列表中,能够记录的ip地址的最大的数量,只要列表没满,就会一直留在列表里面;而RouterOS的addresslist会有个timeout的设定,如果超时了就会自动从列表里面移除。这样就导致两者的记录和调用逻辑稍微有点不一样:

  • xt_recent是匹配策略就记录下ip,然后在拦截策略读取ip列表的时候再指定时间区间。
  • RouterOS是匹配的时候就定义一个timeout,然后拦截策略不需要考虑老旧数据的问题。

具体孰优孰劣这个不同人有不同看法,这里就不作太多比较。

要注意的是ip_list_tot默认大小是100,我个人感觉是不是非常够的,于是打算修改一下这个限制。

具体当前的值可以在/sys/module/xt_recent/parameters/ip_list_tot看到,直接用cat来读出来就行。

如果你也觉得默认的列表只有100的容量不是很够,可以参考下面的部分来修改ip_list_tot的值

首先把xt_recent这个mod卸载掉,如果出现提示说这个mod在用导致无法卸载的话,就先检查一下是不是iptables或者ip6tables已经存在调用,可以先把策略删掉,完了再加回来。

首先,把xt_recent停掉。

modprobe -r xt_recent

然后,在/etc/modprobe.d/下面新增一个xt_recent.conf,内容如下:

options xt_recent ip_list_tot=8192

新增完毕之后,再重新加载xt_recent

modprobe xt_recent

最后,检查一下是否已经生效

cat /sys/module/xt_recent/parameters/ip_list_tot

完毕之后,就可以着手开始调整iptables了。

iptables

设计思路可以参考之前RouterOS的部分,但是要作一些修改,因为拦截两种坏东西的时长是不一样的,在Linux上要拆分成两个list,所以,iptables里面INPUT chain可以这样设计:

  1. 允许RELATED,ESTABLISHED
  2. 允许受信任的源
  3. 拦截坏东西的ip地址
  4. 拦截未知访问的ip地址
  5. 标记扫描端口的坏东西的ip地址
  6. 放通正常的服务
  7. 标记其他未知访问的ip地址
  8. INPUT chain默认行为是DROP

这里拿未知访问的来做例子:

# 4.拦截未知访问的ip地址
iptables -A INPUT -m recent --update --seconds 86400 --name UnknownConnection --rsource -j DROP
……
……
……
# 7.标记其他未知访问的ip地址
iptables -A INPUT -m recent --set --name UnknownConnection --rsource -j DROP

这里简单介绍一下几个参数:

-A INPUT :这个策略添加在INPUT chain中
-j DROP :跳转至DROP,即直接丢弃包,不做任何回应
-m recent :调用xt_recent模块
--update :更新最后一次见到的时间
--seconds 86400:指定只检查86400秒就是24小时内的部分,这里的单位是秒
--name UnknownConnection :指定检查名称为UnknownConnection列表
--rsource :指定为源地址匹配
--set :将地址添加到列表中

其他用法的话,这里有篇文章,讲述的比较详细,可以作为参考。

配上之后,可以在/proc/net/xt_recent/文件夹里面看到所有的列表,可以直接用cat或者less等工具来读取。

列表里面的内容

结束

从此,服务器的安全性得到了一定的提升,至少能挡一下扫描什么的了。至于说WAF什么的……先靠Cloudflare吧。

哦,对了,ip6tables可以用一样的方法来加强抗扫描的能力。

追加1

上了这个拦截之后,发现邮箱被jetpack提醒网站下线的邮件填满了。花了一段时间检查之后,终于发现之前放通的部分是使用了ctstate NEW+limit这种,导致无法匹配到tcp reset的包,最后发来reset包的cloudflare服务器就被当作是坏家伙而屏蔽了。加上允许reset包的策略就可以解决这个问题。

iptables -A INPUT -p tcp -m tcp --tcp-flags RST,PSH,ACK RST -m limit --limit 64/sec -j ACCEPT

追加2

如果需要手动操作清单里面的ip地址,可以直接用echo来添加和删除:例如

echo +10.10.10.10 > UnknownConnection #将10.10.10.10添加到列表中
echo -10.10.10.10 > UnknownConnection #将10.10.10.10从列表中移除
echo / > UnknownConnection #清空整个列表

通过DHCP指定mtu

起因

家里的Proxmox母机开了VXLAN的SDN,但是由于vxlan隧道需要吃掉几十个字节作为包的头,导致vxlan的子网里面必须将mtu缩小至1450(母机之间互联用的是1500),但是每次开虚拟机就要手动改mtu这个非常的不友好。

在网上搜索手动指定mtu,文章全部都是使用静态的ip地址,在进一步搜索的时候,发现需要修改Linux的dhcpclient配置,在配置文件里面看到,在客户端发送dhcp请求的时候,是包含接口mtu信息的。

这样就有趣了。

那是不是DHCP服务器可以指定客户端的接口mtu呢?那就不需要我一个一个手动修改了。

进行一番搜索之后,果然发现DHCP里面指定了一个option 26,可以定义接口mtu。而RouterOS也支持自定义dhcp options,官方的wiki里面有关于option的写法。

开搞

配置过程中充满了曲折,我尝试在winbox里面添加option,根据教程,我尝试在value里面填入1400,报错,然后尝试填入0x5aa(1450的十六进制值),也不行。然后我发现,填值据然要加上单引号,于是填入‘1450’,过了。

然而事情并不顺利,我在客户端通过DHCP拿到一个奇怪的mtu,43525。当时我就蒙圈了,这啥情况?

于是我胡乱填了更多的值,得到了更多奇奇怪怪大的一批的mtu值。着实搞不明白,然后仔细看了一下winbox中给的rawvalue,看着怎么这么眼熟,好家伙,这不是把字节的顺序给反过来了吗!

本来应该是05 aa,结果变成了 aa 05

我再确认一下整个活

90000转成十六进制实际上是 01 5f 90,结果变成了 90 5f 01

确认之后,就可以根据需要的raw value反推需要填的值。我们需要 05 aa ,就将 aa 05转换成十进制,得出43525,填入之后得到正确的raw value。

得到正确的raw value。

经测试,能够正常获取到1450的mtu。

报告Bug

这明显是有问题啊,我就去给mikrotik提了个请求,得到回复说马上就修。

mikrotik确认了这个故障,说准备修复。

继续阅读官方wiki

后来仔细阅读了一下官方的wiki,里面提到确实可以用0x来输入十六进制的值,也不用用单引号括住。那为啥我输入0x5aa报错?再来一次的时候发现……0x05aa是完全没问题的,但是0x5aa就不行。

开头的0不能省略

看着顺眼多了。后续等官方修复这个Bug之后或许可以换回去’1450’,这样就更直观了。