MSYS2 共用 Windows OpenSSH 的 SSH Agent
在有了 同 WSL 分享 SSH Agent 之后 Windows; WSL2; PuTTY / WinSCP 就都能用同一份 Windows 系统服务管理的 SSH Agent 了。添加一次私钥后再也不同重新输入密码。Git for Windows 也因为 PATH 中选用 /c/Program Files/OpenSSH/ssh-add 吃到了便利。
而 MSYS2 默认不添加来自 Windows 的 PATH,也不自带 OpenSSH。如果用 pacboy -S openssh 安装一份,它的 ssh-add 读取不到保存在 Windows Registry 的私钥。
好在 MSYS2 环境执行 .exe 文件还是很兼容的,ssh-agent 进程间访问通过标准输入输入通讯,直接删除 /usr/bin/ssh-add 让 PATH 自然会退到来自 Program Files 的即可。
也能用 MSYS2 的 ssh-add,见 想要实现 Windows to MSYS2 也是可以的
Windows OpenSSH 错误信息展示为八进制数字
在 Windows 上使用 SSH 一旦遇到 DNS 错误或者输入错误地址,按 SSH 连接失败的乱码究竟是什么? - 枫茶舍 解码,就会得到 不知道这样的主机。 错误提示,可它却以八进制数字的形式展示,难以接受。
ssh: Could not resolve hostname badname: \262\273\326\252\265\300\325\342\321\371\265\304\326\367\273\372\241\243好在有高人指点 Localized socket error messages are shown as octal numbers · 议题 #1654 · PowerShell/Win32-OpenSSH,本地化不行就切换会英语,总比乱码强。加一行自己编译
diff --git a/contrib/win32/win32compat/win32-utf8.c b/contrib/win32/win32compat/win32-utf8.c
index 30d9bc5d1..7105bf0d0 100644
--- a/contrib/win32/win32compat/win32-utf8.c
+++ b/contrib/win32/win32compat/win32-utf8.c
@@ -100,6 +100,8 @@ asmprintf(char **outp, size_t sz, int *written, const char *fmt, ...)
void
msetlocale(void)
{
+ SetThreadUILanguage(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
+
// save previous codepage
g_previous_codepage = GetConsoleOutputCP();
加一行自己按 Building OpenSSH for Windows (using LibreSSL crypto) · PowerShell/Win32-OpenSSH Wiki 的说明编译。注意不要贪速度用 shallow clone,编译过程会读取 Git 历史。
git clone https://github.com/PowerShell/openssh-portable
git clone https://github.com/Microsoft/Vcpkg
cd Vcpkg
./bootstrap-vcpkg.bat
./vcpkg.exe integrate install
cd openssh-portable
ipmo ./contrib/win32/openssh/OpenSSHBuildHelper.psm1 -Force
Start-OpenSSHBuild -Configuration Release -NativeHostArch x64
./bin/x64/Release/ssh -V在手动把二进制复制到安装位置,比如我是 C:/Program Files/OpenSSH,再测试就 OK 啦
ssh: Could not resolve hostname a.b.c.d.: No such host is known.Bash 的自动补全和语法提示
- akinomyoga/ble.sh: Bash Line Editor―a line editor written in pure Bash with syntax highlighting, auto suggestions, vim modes, etc. for Bash interactive sessions.
- ohmybash/oh-my-bash: A delightful community-driven framework for managing your bash configuration, and an auto-update tool so that makes it easy to keep up with the latest updates from the community.
- Bash-it/bash-it: A community Bash framework.
从 ble.sh 看到了 oh-my-bash,最后选择了 bash-it
同 WSL 分享 Git-Bash 的 GPG Agent
是 同 WSL 分享 SSH Agent 的后继
- mjg59 | Unix sockets, Cygwin, SSH agents, and sadness
- Sharing the ssh agent between Windows and WSL | sxda.io 建议阅读
网络上有许多能够实现将来自 Gpg4win 的 ~/AppData/Local/gnupg/S.gpg-agent Assuan Socket 转发到 WSL2 的方法,比如
- rupor-github/win-gpg-agent: [DEPRECATED] Windows helpers for GnuPG tools suite
sorelay.exe -a c\:/Users/mike0/AppData/Local/gnupg/S.gpg-agent - albertony/npiperelay: npiperelay allows you to access Windows named pipes from WSL
npiperelay.exe -ei -ep -a "C:/Users/<your-user-folder>/AppData/Local/gnupg/S.gpg-agent"
有很多介绍文章,但它们用的都是 Gpg4win,也都建议关闭 Windows 自带的 Win32-OpenSSH Agent,和我的使用场景不符。
- demonbane/wsl-gpg-systemd: Allow WSL instances in systemd mode to access GPG from Windows 整合到 systemd 中的方便脚本
- Sharing the ssh agent between Windows and WSL | sxda.io 详细说明了各种方法
- Using a Yubikey for GPG in WSL (Windows Subsystem for Linux) on Windows 10 和 Yubikey 结合的
- mjg59 | Unix sockets, Cygwin, SSH agents, and sadness 提到了 Cygwin 到 Windows Named Pipe 的转换思路
因为习惯在 Windows 上编程,由 Git for Windows 提供 Git & GPG,时不时还在 WSL2 做 Linux 开发。我是希望将来自 Git for Windows (MSYS2)的 GPG 创建的 ~/.gnupg/S.gpg-agent 伪 Unix Domain Socket 分享给 WSL2。整来整去
Easy workaround
[gpg]
program = gpg.exeProfile Script
#!/usr/bin/env sh
# Expose Git for Windows (Cygwin) GPG Agent to WSL environments
#
# This script targets the cygwin/msys2 GPG agent implementation,
# which use a cygwin emulated UNIX domain socket.
# NOT the Agent included with Gpg4win, or WSL2.
# Ensure the correct implementation is matched.
# - gpg-agent.exe C:\Program Files\Git\usr\bin\gpg-agent.exe typlically
# - nieperelay https://github.com/albertony/npiperelay the forked version
#
# Note that use of systemd unit files is recommended in WSL2 environments.
socat_gpg_agent_socket() {
local socket_path="$1"
local socket_name="${socket_path##*/}"
# Exit if socket already exists and is a valid socket
if ss -lxn | grep -q "$socket_path"; then
return
fi
# Remove the stale socket, if it exists
rm -f "$socket_path"
# Launch socat/npiperelay as a background process
(setsid socat \
UNIX-LISTEN:"$socket_path",umask=007,fork \
EXEC:"npiperelay.exe -ei -s -c C\:/Users/$USER/.gnupg/$socket_name",nofork \
&) >/dev/null 2>&1
}
# e.g. /run/user/1000/gnupg/S.gpg-agent
socat_gpg_agent_socket "$(gpgconf --list-dirs agent-socket)"
# e.g. /run/user/1000/gnupg/S.gpg-agent.ssh
socat_gpg_agent_socket "$(gpgconf --list-dirs agent-ssh-socket)"SystemD Socket
受 Sharing the ssh agent between Windows and WSL | sxda.io 的启发,感觉用 systemd 提供的 socket 触发启动进程的模式更加合理。也不用安装 socat 了,也不会有后台进程持续运行了。
我的版本相比链接中的,有一些调整
# systemd socket unit for named-pipe-gpg-agent
# which accept connections on a Unix socket
# and forward them to the Cygwin GPG agent.
#
# enable with:
# systemctl --user daemon-reload
# systemctl --user enable named-pipe-gpg-agent.socket --now
[Unit]
Description=GPG Agent provided by Cygwin emulated unix domain socket
Conflicts=gpg-agent.socket
[Socket]
ListenStream=%t/gnupg/S.gpg-agent
FileDescriptorName=std
SocketMode=0600
DirectoryMode=0700
Accept=yes
[Install]
WantedBy=sockets.targetConflicts=gpg-agent.socket因为 gpg-agent 一个进程就能创建 S.gpg-agent, S.gpg-agent.ssh 等多个 socket,用 apt 安装的 gpg 的 systemd 配置文件,自然是多个 socket 对应同一个 service 文件。这里 npiperelay 只能做到一个进程一个 socket,不得不拆分为多个 socket 和 service 来定义。所以和系统自带的 gpg 冲突,别一起用。Accept=yes很有必要,并且 service 文件名也带有@,否则会无限卡住。让每次请求启动新的进程,npiperelay 处理完一次请求就自动退出
# systemd service unit for npiperelay-gpg-agent.socket
# StandardInput can only handle one socket per service, thus multiple units.
# each service spawn its own npiperelay instance to serve one socket.
[Unit]
Description=Proxy to Windows GPG Agent, which provides the standard named pipe (Cygwin/MSYS2, etc.)
[Service]
Type=simple
ExecStart=/mnt/c/Users/%u/AppData/Local/Microsoft/WinGet/Links/npiperelay.exe -ei -s -c "C:/Users/%u/.gnupg/S.gpg-agent"
StandardInput=socketExecStart可执行文件路径要写绝对路径,放在哪里无所谓-c的参数在 shell 中要转义,但在这里放在引号内就不用
使用前需要先禁用系统自带的
systemctl --user daemon-reload
systemctl --user disable --now gpg-agent.socket在 WSL2 中可以这样测试
systemctl --user daemon-reload
systemctl --user disable --now named-pipe-gpg-agent.socket
systemctl --user enable --now named-pipe-gpg-agent.socket
ll /run/user/1000/gnupg/S.gpg-agent
gpg-connect-agent --no-autostart 'keyinfo --list' /bye如果正常输出类似你的 gpg -k 列表就说明成功了。
想要实现 Windows to MSYS2 也是可以的,Profile Script 通过在 MSYS2 的 /etc/profile 中运行 socat 后台的方式,不过需要用 MSYS2 中的 go 编译 npiperelay.exe 才行,否则会遇到 copy from stdin to pipe failed: read /dev/stdin: The parameter is incorrect. 的错误。
不过在 MSYS2 中跑后台进程感觉不是很合适,还是直接调用 ssh-add.exe 方便又直接,还免得设置 SSH_AUTH_SOCK 环境变量。
MSYS2 的 ssh.exe 会访问 SSH_AUTH_SOCK 环境变量,否则就尝试加载 ~/.ssh/id_ed25519 这种文件。
而且问题是 MSYS2 编译的 npiperelay 只能给 MSYS2 的 ssh 使用,Windows 环境编译的 npiperelay 又不能给 MSYS2 的 ssh 使用,因为它们的 stdio 不一样。只能在 socat 脚本里分开两个目标了。
所以需要为 MSYS2 编译一套 npiperelay,然后用 Profile 的方式启动。或者临时地,在两个窗口里
socat UNIX-LISTEN:/tmp/run/197608/ssh/ssh-agent.sock,umask=007,fork EXEC:"npiperelay-msys2.exe -v -ei -s //./pipe/openssh-ssh-agent",nofork
export SSH_AUTH_SOCK=/tmp/run/197608/ssh/ssh-agent.sock