Windows VS Code Laravel开发容器连接Git远程SSH配置问题
我在Windows系统的Visual Studio Code中使用devcontainer,该容器通过Laravel Sail辅助工具构建,运行于Docker Desktop与WSL2环境。
对应的devcontainer.json配置如下:
{ "name": "Existing Docker Compose (Extend)", "dockerComposeFile": [ "../docker-compose.yml" ], "service": "laravel.test", "workspaceFolder": "/var/www/html", "settings": {}, "extensions": [ // "mikestead.dotenv", // "amiralizadeh9480.laravel-extra-intellisense", // "ryannaddy.laravel-artisan", // "onecentlin.laravel5-snippets", // "onecentlin.laravel-blade" ], "remoteUser": "sail", // "forwardPorts": [], // "runServices": [], // "postCreateCommand": "apt-get update && apt-get install -y curl", // "shutdownAction": "none", }
对应的docker-compose.yml配置如下:
version: '3' services: laravel.test: build: context: ./docker/8.1 dockerfile: Dockerfile args: WWWGROUP: '${WWWGROUP}' XDEBUG: ${SAIL_DEBUG} image: sail-8.1/app extra_hosts: - 'host.docker.internal:host-gateway' ports: - '${APP_PORT:-80}:80' environment: WWWUSER: '${WWWUSER}' LARAVEL_SAIL: 1 XDEBUG_MODE: '${SAIL_XDEBUG_MODE:-off}' XDEBUG_CONFIG: '${SAIL_XDEBUG_CONFIG:-client_host=host.docker.internal}' volumes: - '.:/var/www/html' networks: - sail depends_on: - mariadb mariadb: image: 'mariadb:10' ports: - '${FORWARD_DB_PORT:-3306}:3306' environment: MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}' MYSQL_ROOT_HOST: "%" MYSQL_DATABASE: '${DB_DATABASE}' MYSQL_USER: '${DB_USERNAME}' MYSQL_PASSWORD: '${DB_PASSWORD}' MYSQL_ALLOW_EMPTY_PASSWORD: 'no' volumes: - 'sail-mariadb:/var/lib/mysql' - './vendor/laravel/sail/database/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh' networks: - sail healthcheck: test: ["CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}"] retries: 3 timeout: 5s networks: sail: driver: bridge volumes: sail-mariadb: driver: local
此前本地开发、推送代码至GitHub仓库的功能均运行正常,当前需要推送代码到生产站点,执行命令后出现SSH协商报错:
git remote add namecheap xx.xx.xx.xx:xxxx/home/user git push namecheap dev Unable to negotiate with xx.xx.xx.xx port xxxx: no matching host key type found. Their offer: ssh-rsa,ssh-dss fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists.
尝试多种解决方法均未生效,需要在该devcontainer中添加SSH密钥或配置SSH agent转发。当前容器内缺少vim、sudo等基础Linux工具,不清楚工具安装方法,也不知道超级用户密码。
报错根因是新版OpenSSH默认禁用了ssh-rsa、ssh-dss两类存在安全风险的老旧主机密钥算法,而生产服务器仅支持这两类算法,所以协商失败。以下操作全程不需要安装vim、sudo,不需要输入root密码,所有操作都在sail用户权限范围内完成。
1. 修复SSH算法协商问题
不需要使用文本编辑器,直接在容器终端执行以下命令生成SSH配置,仅对生产服务器单独开启旧算法兼容,不影响GitHub等其他站点的连接安全:
mkdir -p ~/.ssh chmod 700 ~/.ssh cat >> ~/.ssh/config << EOF Host xx.xx.xx.xx Port xxxx HostkeyAlgorithms +ssh-rsa PubkeyAcceptedAlgorithms +ssh-rsa EOF chmod 600 ~/.ssh/config
执行前把命令里的xx.xx.xx.xx替换为生产服务器实际IP,xxxx替换为实际SSH端口。
2. 配置SSH Agent转发(推荐方案,无需在容器内存储私钥)
该方案直接复用Windows/WSL本地存储的SSH密钥,没有私钥泄露风险,配置一次永久生效:
- 第一步:修改本地项目里的
.devcontainer/devcontainer.json文件,添加agent转发和自动信任主机的配置,新增forwardAgent: true字段,替换原有的postCreateCommand注释项,修改后核心配置如下:
{ "name": "Existing Docker Compose (Extend)", "dockerComposeFile": [ "../docker-compose.yml" ], "service": "laravel.test", "workspaceFolder": "/var/www/html", "settings": {}, "extensions": [], "remoteUser": "sail", "forwardAgent": true, "postCreateCommand": "mkdir -p ~/.ssh && chmod 700 ~/.ssh && ssh-keyscan -p xxxx xx.xx.xx.xx >> ~/.ssh/known_hosts" }
同样把postCreateCommand里的IP和端口替换为生产服务器实际值,该命令会在容器创建时自动把生产服务器的主机指纹加入信任列表,避免首次连接弹出确认提示。
- 第二步:在本地系统启动SSH Agent并添加私钥
- 如果密钥存在Windows系统:打开管理员权限PowerShell,依次执行
Set-Service ssh-agent -StartupType Automatic、Start-Service ssh-agent设置agent开机自启,再执行ssh-add 你的私钥本地路径(默认路径一般为C:\Users\你的用户名\.ssh\id_rsa)添加私钥。 - 如果密钥存在WSL2环境:在WSL终端执行
eval $(ssh-agent -s)启动agent,再执行ssh-add ~/.ssh/id_rsa添加私钥,可以把启动agent的命令写入WSL的~/.bashrc文件实现开机自动加载。
- 如果密钥存在Windows系统:打开管理员权限PowerShell,依次执行
- 第三步:在VS Code中按F1,选择
Dev Containers: Rebuild Container重建容器,所有配置会自动生效。
3. 验证配置
容器重建完成后,在容器终端执行ssh -T 生产服务器用户名@xx.xx.xx.xx -p xxxx,如果不需要输入密码就能正常连接,直接执行git push即可正常推送代码。
临时备选方案(不推荐)
如果不需要长期配置,也可以直接把本地私钥写入容器,全程不需要编辑器:
cat > ~/.ssh/id_rsa << EOF -----BEGIN OPENSSH PRIVATE KEY----- 粘贴你本地私钥的全部文本内容 -----END OPENSSH PRIVATE KEY----- EOF chmod 600 ~/.ssh/id_rsa
该方案私钥会存储在容器持久化卷中,存在泄露风险,仅适合临时调试使用。
内容的提问来源于stack exchange,提问作者Valentino

