从 1Password 到 Bitwarden

red padlock on black computer keyboard

近日,1Password 从原生开发变为了 Electron 开发,而我购买 1Password 家庭版也两年有余。虽然成本不高,但考虑到希望节省开支,便在考虑将一部分支出精简。可以自行托管的 Bitwarden 就成为了 1Password 的替代品。

借着国庆假期,将密码管理软件从 1Password 转换到了自建的 Bitwarden 上。

备份 1Password 的数据

既然要迁移,自然要先备份现有的数据,比如 1Password 中的数据。

在迁移方面,Bitwarden 官方提供了教程,你可以直接参考。

Import Data from 1Password | Bitwarden Help & Support

部署 Bitwarden

Bitwarden 官方的开源版本使用的是 C# 编写的,对于资源的消耗较大。而我是希望在 NAS 中运行,因此,就选择了一个对于资源消耗更少的版本 —— VaultWarden(之前的 bitwarden_rs 项目),一个基于 Rust 编写的 Bitwarden 服务端。

部署的过程也不复杂,使用 Docker 将镜像拉到本地,并复制官方的执行命令,稍做修改即可部署。

# <meta charset="utf-8">/vw-data/ 修改为你自己服务器上的数据存储位置。
docker run -d --name vaultwarden -v /vw-data/:/data/ -p 80:80 vaultwarden/server:latest
Code language: HTML, XML (xml)

不过,我并没有使用 Docker 部署,而是使用 Docker 的替代品 Podman 进行部署(主要是想试试新技术)。

整个的使用过程也很简单

# 拉取镜像
podman pull docker.io/vaultwarden/server
# 启动容器
podman run -d --name vaultwarden -v /vw-data/:/data/:Z -e ROCKET_PORT=8080 -p 8080:8080 vaultwarden/server:latest
# 进入 systemd 目录
cd /etc/systemd/system/
# 生成 service 文件
podman generate systemd --name vaultwarden --files
# 设置自启动,并启动服务
systemctl enable /etc/systemd/system/container-vaultwarden.service
systemctl start container-vaultwarden.service
Code language: PHP (php)

通过几行代码,就完成了 Bitwarden 的部署。

使用 Nginx 对 Vaultwarden 进行反向代理(非必需)

默认情况下,你配置的 Bitwarden 服务器会运行在 8080 端口下,如果你希望将其使用 Https 保护起来,或使用HTTP的 80/443 端口,一个比较简单易行的方式就是使用 Nginx 来进行反向代理。

fmphu

Vaultwarden 官方提供了 Nginx 的配置文件可供参考,其他的代理服务器也可以找到类似的说明,具体的配置你可以自行访问前面的连接进行查看。

注册账号,并导入数据

配置完成后,访问你的 Vaultwarden 地址,并注册一个新的账号,就可以在 Bitwarden Web 端的“工具”页面,进行数据导入了。

vc40l
导入界面

需要注意的是,如果你选择的是文件上传导入,则会使用 WebSocket 进行导入。这里如果你没有配置 WebSocket ,就无法通过文件进行导入,需要使用文本编辑器打开备份文件,复制到下方的输入框中进行恢复。

安装各个平台客户端

在完成了各项配置后,最后就是安装各平台的客户端,并配置服务器、账号密码等,登陆客户端,并同步账号密码了。

s4u5v
配置说明

总结

其实 Bitwarden 的部署不算复杂,不过,Bitwarden 的使用体验目前还是不够好的,在一些网页上,1Password 可以完成自动填充,但 Bitwarden 就需要自行复制粘贴,在用户体验方面还有待提升。

Rust + MicroBit 按钮调试代码

#![no_main]
#![no_std]
use panic_halt;
use microbit::hal::nrf51::{interrupt, GPIOTE, UART0};
use microbit::hal::prelude::*;
use microbit::hal::serial;
use microbit::hal::serial::BAUD115200;
use cortex_m::interrupt::Mutex;
use cortex_m::peripheral::Peripherals;
use cortex_m_rt::entry;
use core::cell::RefCell;
use core::fmt::Write;
use core::ops::DerefMut;
static GPIO: Mutex<RefCell<Option<GPIOTE>>> = Mutex::new(RefCell::new(None));
static TX: Mutex<RefCell<Option<serial::Tx<UART0>>>> = Mutex::new(RefCell::new(None));
#[entry]
fn main() -> ! {
    if let (Some(p), Some(mut cp)) = (microbit::Peripherals::take(), Peripherals::take()) {
        // 引入两个版本的外设
        cortex_m::interrupt::free(move |cs| {
            /* 开启外部设备的 GPIO 中断 */
            cp.NVIC.enable(microbit::Interrupt::GPIOTE);
            microbit::NVIC::unpend(microbit::Interrupt::GPIOTE);
            /* 切分 GPIO 口,方便使用 */
            let gpio = p.GPIO.split();
            /* 将 Button 的 IO 口作为输入 IO 口 */
            let _ = gpio.pin26.into_floating_input();
            let _ = gpio.pin17.into_floating_input();
            /* 当 GPIO 17 ( A 键)出现了下降沿的时候,触发中断 */
            p.GPIOTE.config[0]
                .write(|w| unsafe { w.mode().event().psel().bits(17).polarity().hi_to_lo() });
            p.GPIOTE.intenset.write(|w| w.in0().set_bit());
            p.GPIOTE.events_in[0].write(|w| unsafe { w.bits(0) });
            /* 当 GPIO 26 (B键)出现了下降沿的时候,触发中断 */
            p.GPIOTE.config[1]
                .write(|w| unsafe { w.mode().event().psel().bits(26).polarity().hi_to_lo() });
            p.GPIOTE.intenset.write(|w| w.in1().set_bit());
            p.GPIOTE.events_in[1].write(|w| unsafe { w.bits(0) });
            *GPIO.borrow(cs).borrow_mut() = Some(p.GPIOTE);
            /* 根据需要,设置 GPIO 口作为输入输出口 */
            let tx = gpio.pin24.into_push_pull_output().downgrade();
            let rx = gpio.pin25.into_floating_input().downgrade();
            /* 将准备的 GPIO 口作为串口来使用 */
            let (mut tx, _) = serial::Serial::uart0(p.UART0, tx, rx, BAUD115200).split();
            let _ = write!(
                tx,
                "\n\rWelcome to the buttons demo. Press buttons A and/or B for some action.\n\r",
            );
            *TX.borrow(cs).borrow_mut() = Some(tx);
        });
    }
    loop {
        continue;
    }
}
// 定义一个中断(如果函数出现了错误,就会触发中断),当我从按钮按下接收到了一个中断,这个函数就会被调用。
#[interrupt]
fn GPIOTE() {
    /* 进入中断内部的内容 */
    cortex_m::interrupt::free(|cs| {
        if let (Some(gpiote), &mut Some(ref mut tx)) = (
            GPIO.borrow(cs).borrow().as_ref(),
            TX.borrow(cs).borrow_mut().deref_mut(),
        ) {
            let buttonapressed = gpiote.events_in[0].read().bits() != 0; // 识别出 A 键按下
            let buttonbpressed = gpiote.events_in[1].read().bits() != 0; // 识别出 B 键按下
            /* 将按钮打印到串口中 */
            let _ = write!(
                tx,
                "Button pressed {}\n\r",
                match (buttonapressed, buttonbpressed) {
                    (false, false) => "",
                    (true, false) => "A",
                    (false, true) => "B",
                    (true, true) => "A + B",
                }
            );
            /* 清空事件 */
            gpiote.events_in[0].write(|w| unsafe { w.bits(0) });
            gpiote.events_in[1].write(|w| unsafe { w.bits(0) });
        }
    });
}

Code language: PHP (php)

Rust 学习资料推荐

首推

《The Rust Programming Language》:https://doc.rust-lang.org/stable/book/foreword.html 这本书虽然是纯英文写就,但是浅显易懂。非常不错。中文版

嵌入式相关

《Micro Rust》:https://droogmic.github.io/microrust/index.html 这本书是基于 MicroBit 这个硬件来写的课程,可以去淘宝上买个 Micro::Bit Go,一百多一点,然后自己实践一下。

《The Discovery Book》:https://docs.rust-embedded.org/discovery/index.html

《The Embedded Rust Book》:https://docs.rust-embedded.org/book/index.html

Rust Crates 使用国内镜像

我在使用 Rust 安装 rls 时,出现了下载报错,具体如下

info: caused by: error during download
info: caused by: [56] Failure when receiving data from the peer

一看到 from the peer ,我就明白了,大概率被墙了。所以,就要切换为国内镜像源。

目前只有 USTC 提供了 Rust Crates 的镜像源,接下来,我们就开始配置 Rust Crates 源。

https://servers.ustclug.org/2016/01/mirrors-add-rust-crates/
USTC Lug 的公告

配置 Mirror

使用 VSCode 打开配置文件 $HOME/.cargo/config

在其中加入如下代码

[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
replace-with = 'ustc'
[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"
Code language: JavaScript (javascript)

然后重新在命令行中执行安装代码,可以看到非常高效的完成了安装.

s2ym7

macOS 安装 Rust 开发环境

安装 Rust

Rust 在 macOS 上的安装非常的简单,只需要在终端执行如下代码

curl https://sh.rustup.rs -sSf | sh
Code language: JavaScript (javascript)

会自动进入到安装的状态,脚本会检测你的操作系统,然后提示你是否安装,作为初学者,直接用默认的安装吧。

zsh4o
提示

稍等片刻,就安装完成了。

e4po0
安装完成的提示

你可以重启终端来应用 Rust 的环境变量,或者直接执行如下命令来使环境变量生效。

source $HOME/.cargo/env
Code language: PHP (php)

执行命令 rustc --version 可以看到版本号信息:

wrl6o

这时,就说明你的安装完成了。

创建测试项目

接下来,在终端里执行如下命令,来创建一个测试项目。

cargo new hello-world
Code language: JavaScript (javascript)

并使用 cd 命令进入该目录,编译,并运行代码

cd hello-world
cargo run

你可以看到这样的输出,就说明你的 Rust 环境已经配置好了。

jkivy

Rust With VSCode

安装 Rust 代码支持

我一般习惯用 VSCode 写代码,所以依然这次继续使用 VSCode 来写 Rust。

在 VSCode 中的拓展管理器中搜索 Rust,可以看到一个名为 Rust(rls) 的拓展,安装这个拓展。

raqc5
Rust(rls)

此时使用 VSCode 打开刚刚创建的 Hello World 项目,就可以看到如下的效果。

84029

然后,打开终端,执行如下命令,来安装 Rust 拓展所需的一些 Rust 组件

rustup component add rls rust-analysis rust-src

安装 crates.io 的拓展支持

Crates.io 类似于我们在 Node.js 世界的 NPM,PHP 世界的 Composer。crates.io 的拓展可以帮助我们更好的管理我们的项目拓展。

在 VSCode 的拓展界面搜索 crates ,就可以看到由 Seray Uzgur 提交的 crates 插件,安装这个插件。

u2ru4

安装完成后,打开项目根目录的 cargo.toml 文件,然后你可以看到,在你的依赖项目后会提示你最新的版本。

a56cs

把你的鼠标放在"0.2.0"的后面,还会提示你所有的更新的版本,你可以通过一下点击,来升级依赖的版本。


或者执行命令来升级所有的依赖

7raj1

安装 TOML 的高亮

Toml 是 cargo 用来存储项目信息的文件格式,VSCode 本身不提供高亮,不过我们可以通过安装一些拓展来实现。

在 VSCode 的拓展管理器搜索 TOML,并安装由 bungcip 开发的 Better TOML,即可实现 toml 文件的高亮。

fqr13

安装完成后,重新打开 cargo.toml 文件,你就会发现其内容被语法高亮了。

ig8h9

这样,我们就完成了 Rust 开发环境的基本配置。后续可以根据你自己的需要,去配置其他拓展和依赖。