VNC还是RDP? 云上的远程桌面究竟该如何选

这里说的VNC是什么?

简单来说,所谓的 VNC(Virtual Network Computing)是一种图形化的桌面共享系统,它使用远程帧缓冲协议 (RFB) 来远程控制另一台计算机。它将键盘和鼠标事件从一台计算机传输到另一台计算机,通过网络向另一个方向转发图形屏幕更新。

类似这样的技术VNC不是绝无仅有,但VNC 的流行和普及却因为其具有的过人之处 –

  • VNC是平台无关的—— 有多种客户端和服务器的实现,几乎涵盖了所有的主流平台。甚至一些VNC的实现被称“无客户端”,这是因为不需要安装插件或客户端软件而,而是依靠HTML5技术,只需要一个浏览器就可以访问远程桌面了。
  • VNC是开源的—— VNC最初是在英国剑桥的Olivetti & Oracle研究实验室开发的。原始的VNC源代码和许多现代的衍生品在GNU通用公共许可证下是开放源码的。
  • VNC的协议是简单、普适的—— VNC使用的是 RFB(Remote Framebuffer) 协议。这是一个开放且简单的协议。因为它在framebuffer级别工作,协议是基于像素的所以适用于所有窗口系统和应用程序,包括Microsoft Windows、macOS和X Window系统。这个协议的性能表现是很出色的。

说起来满满的都是优点,那么

访问云上的实例,为什么不选择VNC呢?


VNC 的优点很多,很多场景下都能看到VNC。例如,访问树莓派的桌面,对 headless 服务器的管理等等。但是,对于云上实例的远程图形化的访问VNC却不是好的选择。考虑到我们的使用场景是通过互联网来访问云上的主机,这就引出了否定 VNC 最主要的原因– 安全性。

默认情况下,RFB并不是一个安全的协议。虽然这个协议下密码不以明文方式发送,但如果能从网络中嗅出加密密钥和已编码的密码,还是有可能破解成功的。因此,建议密码至少有8个字符。另一方面,VNC的一些版本也有8个字符的限制; 如果发送的密码超过8个字符,则删除多余的字符,并将截断的字符串与密码进行比较。

在VNC生态系统中,”Big Four” 指的是LibVNC、UltraVNC、Tight VNC和TurboVNC 这四家提供VNC 产品的厂商。2019年,Kaspersky Lab 的研究人员对这四家公司进行了审计,以了解它们的安全性。他们的发现是令人失望的。总的来说,研究人员发现这四个程序的客户端和服务器部分共有37个严重缺陷。其中22个在 UltraVNC,另外10个在 LibVNC, 4个在 TightVNC,还有一个在 TurboVNC,但这是一个严重的漏洞,它会让攻击者在服务器端远程执行代码。

有人会建议通过 SSH 或 VPN 连接进行 VNC 的隧道化,通过这种方法增加一个具有更强加密功能的额外安全层。但是这种方法并不完美,除了增加了复杂性也容易引起其它的一些安全问题,例如中间人攻击等。

否定一个技术是简单的,但我们是否有替代技术呢?答案就是 Remote Desktop Protocol (RDP)。

那么,RDP又是什么?

有过 Windows 使用经验的人对于远程桌面(Remote Desktop Protocol ,RDP)一定不会陌生。RDP 是由微软公司开发的一种专有协议,它为用户提供了通过网络连接到另一台计算机的图形界面。在使用上,用户需要使用 RDP 客户端软件,而在远程另一台计算机则需要运行 RDP 服务器软件。

微软的Windows、Linux、macOS、iOS、Android等操作系统都有客户端。Windows操作系统内置RDP服务器;Linux与 macOS 可以安装一个 RDP 服务器。缺省配置下,服务器监听 TCP 端口 3389 和 UDP 端口 3389。

微软目前把他们的官方RDP客户端软件称为Remote Desktop Connection,以前叫做 “Terminal Services client”

与VNC 相比,RDP的安全性有很大的提升。主要的安全特性包括了:

  • 128位加密,使用RC4加密算法(版本6加入)
  • 提供了对TLS的支持(版本2加入)

此外,正如前面提到的VNC协议是基于像素的。尽管这带来了极大的灵活性,可以显示任何类型的桌面,但它的效率往往不如那些更好地理解底层图形布局(例 如: X11)或桌面(例如:RDP )的解决方案。这些协议以更简单的形式(例如:打开窗口)发送图形原语或高级命令,而 VNC 的 RFB 协议尽管支持压缩但只能是发送原始像素数据。

如何使用RDP?

在 Windows 环境下使用RDP是再简单不过的事情。我想谈的是在 Linux 环境下RDP 的安装部署与使用。虽然微软公司没有为 Linux 提供 RDP 的软件,但是我们可以使用开源的xRDP,这是RDP协议在Linux平台 的实现。

xRDP是一个开源的远程桌面协议服务器,它用来实现Linux接受来自 Windows、Mac 或者 Linux 远程桌面客户端或的连接。这意味着你不需要在我们所使用的 Windows 或 macOS 机器上安装额外的第三方应用程序。

Linux 安装 xRDP的方法如下。这里我以 Ubuntu 20.04 为例 –

  • 安装Gnome

GNOME是一个Linux 操作系统下的桌面环境,完全由免费和开源软件组成。缺省情况下在EC2上安装的Linux 操作系统都不包含Gnome,需要额外安装。Ubuntu 缺省的桌面窗口管理器就是Gnome,用以下命令安装。

安装 xRDP除了Gnome 以外,我们还可以选择 LXDE、Xface 等等。相比之下,LXDE 是轻量级的窗口管理器,Xface 则具备类似 Windows 界面的风格。如果不在乎额外增加的大约 5GB 的磁盘存储的开销,我还是推荐使用Gnome。原因在于这与我们本地使用的 Ubuntu 具有一致性的体验。

输出结果如下:安装完成后,xRDP服务将自动启动。可以通过这个命令来检查其状态:

  • 接下来,要为Linux的用户(ubuntu)设置登录密码

此外,如果我们的 EC2 实例绑定了Elastics IP 并且拥有自己的域名,我推荐使用Let’s Encrypt 发出的免费SSL证书替换缺省的自签发的证书。需要注意的一点,Let’s Encrypt 证书的有效期是90天。可以考虑配置crontab 使用cerbot 自动更新证书。这里设置的密码将被用来登录到目标的 EC2 实例,出于安全的理由务必使其符合密码安全的策略。第二条命令是将 用户ubuntu 加入到 ssl-cert 用户组中。这是因为默认情况下,xRDP 使用的是自签发的证书,这个证书保存在 /etc/ssl/private/ssl-cert-snakeoil目录下。证书的密钥文件只能由 “ssl-cert” 用户组的成员读取。

  • 修改安全组 RDP 协议缺省使用3389端口。务必记得在EC2的安全组中打开TCP与UDP在这个端口上的访问许可。

  • 登录

在这里,username 输入ubuntu,password 输入刚刚修改过的用户密码。成功登录之后熟悉的Ubuntu 桌面就会立刻出现在眼前。

按照我的体验,网络延迟在35ms以内xRDP 的与本地Linux 桌面的操作体验几乎没有差别。实测之下,通过我所使用的100M联通宽带访问AWS 中国(北京)区域的EC2 实例,网络延迟大约在5ms左右。

单以鼠标、键盘的使用体验而论,本地桌面与远程桌面的差别已经微乎其微了,我终于可以放心的将许多工作移到云端。无论是通过我的笔记本电脑、iPad 甚至是一台树莓派都能够让我接入顺利的接入远程桌面。如此一来,开发在云端岂不是再简单不过的事情了。

参考链接


解决KiCAD(系统macOS Big Sur)关联封装(Footprint)报错“No PCB footprint libraries are listed in the current footprint library table.”

最近在把macOS Big Sur系统上的KiCAD5.x升级到6.x之后,新建的项目在进行关联封装操作的时候,报错

具体如下图:

造成这个问题的原因是KiCAD 6.x版本的全局封装库默认存储位置发生了变动,从低版本升级上来,还是会从以前的配置文件中读取原来配置的路径信息,导致找不到新版本的全局封装库路径。

解决方法是删除低版本配置的封装库相关的配置信息即可(删除之前,先关闭KiCAD软件,否则删除无效)。

macOS系统:

Windows系统:

Linux系统:

完成后,重启KiCAD软件即可。

参考链接


[华硕主板] 支持ECC内存的AMD Ryzen™处理器列表

下表列出了支持带ECC功能内存模组的Ryzen™处理器的列表;它们的官方名称;以及是否支持B550X570系列。

请注意,当涉及到APUsRyzen 3000/4000 G系列),只有PRO处理器(例如Ryzen 3 PRO 3200G)支持ECC内存。

官方名称

支持B550系列

支持X570系列

ECC功能

UDIMM ECC

REG ECC

AMD Ryzen™ 5000 Series Processors

V

V

支持

V

 

AMD Ryzen™ 4000 G-Series Processors

V

V

只有PRO支持

V

 

AMD Ryzen™ 3000 Series Processors

V

V

支持

V

 

AMD Ryzen™ 2000 Series Processors

 

V

支持

V

 

AMD Ryzen™ 3000 G-Series Processors

 

V

只有PRO支持

V

 

注意,Ryzen™处理器只支持二手UDIMM ECC,不支持更便宜的二手REG ECC

这个问题简单解释一下,新的REG ECC从性能到价格都是远远高于UDIMM ECC的,但是REG ECC不能用在家用主板上,导致大量淘汰的二手内存需求量不高,因而价格偏便宜。

继续阅读[华硕主板] 支持ECC内存的AMD Ryzen™处理器列表

运维中tcp_tw_recycle net.ipv4.tcp_timestamps引发的坑

NAT环境下,遇到因为tcp_tw_recycle=1和net.ipv4.tcp_timestamps=1引起 Nginx upstream timed out 后,一直没在遇见,今天在朋友的阿里云环境下又重新再一次出现;因此在这炒一次冷饭,让运维新手或者刚上云的朋友大概了解一下,避免再一次采坑。

故障情况:

阿里云账号A的A机房,内网里面部署两台Nginx,通过网络出口(NAT),代理用户访问到阿里云账号B的B机房服务。A机房的Nginx出现:upstream timed out 。

故障的诱因是:net.ipv4.tcp_timestamps=1

抓包图:

注意,这个选项生效的前提是,报文的发出方必须在TCP头部的可选项中增加时间戳字段,否则这个设置是不生效的。

直接上当年的笔记:

先看看TCP IP 对tw的一些解析:
RFC 1323里有这样的定义:

大概的中文意思就是:TCP协议中有一种机制,缓存了每个主机(即ip)过来的连接最新的timestamp值。这个缓存的值可以用于PAWS(Protect Against Wrapped Sequence numbers,是一个简单的防止重复报文的机制)中,来丢弃当前连接中可能的旧的重复报文。而Linux实现这个机制的方法就是同时启用net.ipv4.tcp_timestamps和net.ipv4.tcp_tw_recycle 这两个选项。
这种机制在 客户端-服务器 一对一的时候,没有任何问题,但是当服务器在负载均衡器后面时,由于负载均衡器不会修改包内部的timestamp值,而互联网上的机器又不可能保持时间的一致性,再加上负载均衡是会重复多次使用同一个tcp端口向内部服务器发起连接的,就会导致什么情况呢:

负载均衡通过某个端口向内部的某台服务器发起连接,源地址为负载均衡的内部地址——同一假如恰巧先后两次连接源端口相同,这台服务器先后收到两个包,第一个包的timestamp被服务器保存着,第二个包又来了,一对比,发现第二个包的timestamp比第一个还老——客户端时间不一致。服务器基于PAWS,判断第二个包是重复报文,丢弃之。

反映出来的情况就是在服务器上抓包,发现有SYN包,但服务器就是不回ACK包,因为SYN包已经被丢弃了。为了验证这一结果,可以执行netstat -s | grep timestamp 命令,看输出里面passive connections rejected by timestamp 一项的数字变化。

tcp_ipv4.c中,在接收SYN之前,如果符合如下两个条件,需要检查peer是不是proven,即per-host PAWS检查:

  • 收到的报文有TCP option timestamp时间戳
  • 本机开启了内核参数net.ipv4.tcp_tw_recycle

解决办法:

tcp_tw_recycle=0 或(和)net.ipv4.tcp_timestamps=0同时从4.10内核开始,官方修改了时间戳的生成机制,所以导致 tcp_tw_recycle 和新时间戳机制工作在一起不那么友好,同时 tcp_tw_recycle 帮助也不那么的大。

此处的时间戳并不是我们通常意义上面的绝对时间,而是一个相对时间。很多情况下,我们是没法保证时间戳单调递增的,比如业务服务器之前部署了NAT,LVS等情况。相信很多小伙伴上班的公司大概率实用实用各种公有云,而各种公有云的 LVS 网关都是 FullNAT 。所以可能导致在高并发的情况下,莫名其妙的 TCP 建联不是那么顺畅或者丢连接。

而这也是很多优化文章中并没有提及的一点,大部分文章都是简单的推荐将net.ipv4.tcp_tw_recycle设置为1,却忽略了该选项的局限性,最终造成严重的后果(比如我们之前就遇到过部署在nat后端的业务网站有的用户访问没有问题,但有的用户就是打不开网页)。

参考链接


树莓派固件更新 rpi-update

1 简介

rpi-update是一个用于更新树莓派固件的工具,可以通过apt get install rpi-update来安装

一般来说直接执行下面的命令就可以更新固件(扯淡,基本不可能好吗):

2 跳过自更新

rpi-update启动时会更新自己,如果报以下错误:

可以尝试一下他说的方法来解决:

如果都不行,直接跳过算了,反正也可以用apt来更新这个工具,没必要让他更新自己。

跳过自更新,直接更新固件的方法:

3 【究极方法】本地更新

即使跳过自更新,下载速度也太慢,还经常断流。可以考虑本地更新:

先在PC上下载固件(可以用一下魔法上网):

然后用scp传到树莓派上的/root目录下,之后ssh连上树莓派:

4 检查

对于树莓派4的一个检查方法:

看该目录下是否有libEGL.solibGLESv2.so这两个库,更新前这两个库都是没有的。

如果这两个库出现了,并且是真正的库而不是软链接,就说明更新大概可能也许成功了吧。

参考链接


树莓派固件更新(rpi-update)的那些坑

群晖DSM7通过Docker安装WordPress最新版

PHP 7.x连接MySQL 8.x报错“The server requested authentication method unknown to the client [caching_sha2_password]”

PHP 7.x使用如下代码连接MySQL 8.x

结果出现如下错误信息

原因为 MySQL 支持 caching_sha2_password  / 用户名密码 两种验证方式,但是从 MySQL 8 开始默认使用caching_sha2_password 的验证方式,导致以前版本的客户端不能成功连接。更详细的解释如下:

The server validates the user and returns the connection status. MySQL uses caching_sha2_password and auth_socket plugins for validation.

The caching_sha2_password plugin uses an SHA-2 algorithm with 256-bit password encryption. MySQL 8 prefers this auth method.

Whereas the auth_socket plugin checks if the socket username matches with the MySQL username. If the names don’t match, it checks for the socket username of the mysql.user. If a match is found, the plugin permits the connection.

But to serve the pre 8.0 clients and avoid compatibility errors, it is preferred to revert back the auth method. Older versions of MySQL use mysql_native_password plugin for validation.

解决方案为修改用户默认的认证方式,如下:

参考链接


[RESOLVED] MySQL the server requested authentication method unknown to the client

macOS Monterey (12.0.1)解决App Store无法下载APP的问题

最近,公司的一台电脑升级到 macOS Monterey (12.0.1),之后从进行软件更新,以及 XCode 的下载安装。由于网速的限制,临时把 XCode 的下载进行了暂停操作,之后忘记恢复就进行了重启。

结果第二天在App的安装界面上就看不到被暂停的任务了。然后在 App Store 中尝试继续进行下载,发现无法进行下载任务,下载按钮点击之后,状态一闪而过,又回到待下载状态。

更换账号/重启应用/重启系统之后,无法解决问题。

应该是 App Store 内部状态混乱了,导致无法进行下载任务,需要对 App Store 进行重置操作。

网上搜索很久,才找到解决方案,执行如下命令:

之后重启系统,重新点击下载应用,即可解决问题。

参考链接


群晖DSM7通过Docker安装MySQL最新版(8.x)

搜索并下载镜像,如下:

注意:此处搜索MySQL,会出现两个MySQL镜像,很容易让人不知道该如何选择。其实这两个镜像一个是社区维护,一个是Oracle维护的版本。两者对于普通用户来说,差别不大,选择任意一个都可以。这里,我们选择社区维护的版本。

配置必要的参数信息:

存储位置配置如下:

存储位置(/var/lib/mysql)是镜像中MySQL的默认数据存储目录,位置(/etc/mysql/conf.d)是镜像中MySQL的配置文件目录,这两个目录位置不要变更,如果需要修改配置,则在主机的对应目录上进行调整。

MySQL 服务启动时会以 /etc/mysql/my.cnf 为配置文件,之后会导入 /etc/mysql/conf.d 目录中所有以 .cnf 为后缀的文件。这些文件会拓展或覆盖 /etc/mysql/my.cnf 文件中的配置。

因此可以在挂载至 MySQL 容器中的 /etc/mysql/conf.d 目录创建需要的配置文件。

比如如下:

对于MySQL的日志目录,需要进行特殊授权,否则没办法启动成功。

权限授权方式如下:

配置MySQL的默认登陆密码:

MYSQL_ROOT_PASSWORD 为手工添加,其它是安装 MySQL 自带的。MYSQL_ROOT_PASSWORD 用于设置 root 的密码。

查看系统运行状态:

设置登录的SHELL

如果想替换掉群晖自带的MariaDB,实现主从同步,可以参考家里ADSL上网无固定外网IP的群晖NAS安全实现与公网MySQL服务器主从同步

默认Docker创建出来的MySQL是外部网络都可以访问的,这样不安全,我们可以通过群晖自带的防火墙禁止特定的端口访问来阻止外部IP的访问请求。

操作步骤如下:

参考链接


从Vue 2到Vue 3组织代码的思维转变

到了Vue 3,当我们看完组合式 API相关文档,蠢蠢欲动立马上手把业务代码挪到setup内时,竟发现无法在setup()内部通过 this 获取当前组件实例了(this 是 undefined)!

其实想要变相获取“this”可以用 getCurrentInstance ,比如像这样访问全局属性:

真正属于组件的内容是ctxproxyproxy就是对ctx包装了一层 Proxy。目前看来属实没用,就不展开来说了。

getCurrentInstance 只能setup生命周期钩子中调用。不仅使用起来麻烦,且只适用于开发环境。官方都告知我们日常开发中不要用:

貌似到头来this的问题还是无解。Vue 3 Composition API 的设计初衷也是为了减少对组件实例的依赖,避免this指向的困扰。包括一些全局方法也可以提取到 composables 组合函数中,无需再通过当前组件的原型链获得。我们动不动就通过this获取组件实例的思想该转变了

怎么个直接使用法?我们先看一眼简易版 选项式 API组合式 API 的对比。再用例子来捋:

API 对比

1. 读写数据

Vue 2的做法,或者说Option API更准确 (后面就简写成Vue 2)

data中定义,this.xxx获取
<template>模版中不需要用this

Vue 3 的 Composition API (后面就简写成 Vue 3)

ref reactive定义。
通过ref方法定义的属性在 setup 函数内需要通过.value去访问它的值 (template 模版内不用), reactive则不用。我们可以简单地把 ref(obj) 理解为 reactive({ value: obj })
详细请看 ➡️ 【Vue 3 之:弄清 ref reactive toRef toRefs】

如何选择 refreactive?建议:

  1. 基础类型值(StringNumberBoolean等) 或单值对象(类似{ count: 3 }这样只有一个属性值的对象) 使用 ref
  2. 引用类型值(ObjectArray)使用 reactive
  3. 对于 ref 对象可以使用 unref 语法糖来免去.value访问的困扰

2. 定义/调用方法

Vue 2:methods

继续上面的例子,我们把请求列表数据的操作提取成一个方法。(略去重复的代码)

Vue 3

3. 获取模版DOM元素/或组件实例的引用

Vue 2

直接整上 element-plus Form 表单 的的例子,<el-form>是我们写的DemoForm组件的一个子组件,通过为它添加一个refattribute,就能使用使用this.$refs[ref值]直接访问子组件以及它的所有属性/方法了。
这边只放相对实际的场景用例,给 DOM 元素添加 ref 引用的例子看这里:【模板引用】

Vue 3

我们来修改成 Composition API 版本:
别忘了 ref 创建的变量要用.value获取值
详细文档请阅:【在组合式 API 中使用 template refs】

4. 父组件向子组件传值

文档:Props
使用 DOM 模板时,camelCase (驼峰命名法) 的 props 需要使用等价的 kebab-case (短横线分隔命名) 命名。

说明:第 4-8 点 都会结合同一个 Tabs 标签页的例子,循序渐进梳理这几个属性或方法。依然用了element-plus 的组件(TabsTabPane)。

Vue 2

父组件:📃src/views/design/indexV2.vue

子组件 DesignTabs:📃src/views/design/components/DesignTabsV2.vue

Vue 3

转化成 Composition API 后的子组件:(一贯省略重复代码)
📃src/views/design/components/DesignTabsV3.vue

5. 计算属性computed

比如我们现在希望在点击 tab 标签的时候能获得 tab 的序号(index):

Vue 2

组件 DesignTabs:📃src/views/design/components/DesignTabsV2.vue

Vue 3

这里就比较不一样了,props 的值是不允许在子组件直接修改的。子组件的activeName状态初始值为传入的activeTab,如果activeTabtabOptions一样通过 toRefstoRef 包装,那么activeName的修改是和activeTab深度响应的,这样会报错;
如果要同步修改父组件的值,可以通过emit事件(后面会说)

组件 DesignTabs:📃src/views/design/components/DesignTabsV3.vue

这里想额外提一嘴computed可以包在reactive内使用,在组件数据比较简单的情况下甚至可以直接用reactive包起组件全部数据,就好像 Option Api 的 data 选项那样。这样做的目的是在setup()访问不用再带上.value
但这同时会带来一个问题,在模版需要使用state.xx去渲染,而且如果我们 return 的时候把state解构,包含的状态会失去响应性,那就得不偿失了。需要用toRefs包裹再传递,即可维持其响应性。

6. 侦听器watch

获取这个tabIndex有什么用呢,目的是在当前标签变化时根据index刷新对应TabPane的子组件的数据。但是如果在标签点击事件触发数据刷新也不合适,因为我们不希望重复点击相同tab时也去刷新。那么这个时候watch或者watchEffect就登场了。

Vue 2

组件 DesignTabs:📃src/views/design/components/DesignTabsV2.vue

Vue 3

这个写法可能不是很直观,可以看下 ➡️ 组合式 API 模板引用在 v-for 中的用法
组件 DesignTabs:📃src/views/design/components/DesignTabsV3.vue

watchwatchEffect 的功能是等效的,都是侦听其依赖,并在依赖值变更时重新运行定义的函数。两者区别:

watch

  • 必须在第一个参数明确指定跟踪的依赖
    侦听器数据源只能是getter/effect函数、refreactive对象,或者包含这些类型(的数据)的数组
    换句话说,只要侦听数据不是refreactive对象,就必须传入一个箭头函数
    打个比方,若要侦听reactive对象的某个属性(例:const state = reactive({ count: 0 })count),便不能像侦听单个ref或整个reactive对象那样直接传一个变量,而是必须在第一个参数传入一个回调函数,如() => state.count
  • 第二个参数是依赖值变更时执行的回调,函数内能访问被侦听状态的当前值和前一个值;
  • 组件初始化时不会执行回调。如果需要可在第三个参数(Object)中设置immediate: true
  • 如果要对多层嵌套状态深度侦听,在第三个参数中设置deep: true

watchEffect

  • 无需手动传入依赖项;
  • 只有一个参数,即侦听数据变更的回调函数,会自动跟踪所有函数中用到的变量;
  • 组件初始化时即会执行一次。

7. 子组件向父组件通信(触发父组件方法)

Vue 2

父组件:📃src/views/design/indexV2.vue

子组件 DesignTabs:📃src/views/design/components/DesignTabsV2.vue

Vue 3

子组件 DesignTabs:📃src/views/design/components/DesignTabsV3.vue

8. 使用 Vue Router

Vue 2

📃src/views/design/components/DesignTabsV2.vue

Vue 3

📃src/views/design/components/DesignTabsV3.vue

9. 获取 Vuex 对象

Vue 2

Vue 3

参考链接


从 Vue 2 到 Vue 3 组织代码的思维转变(一)- 获取组件实例