﻿---
title: Docker SSH Forwarding
date: 2025-04-25
excerpt: 🤖本文介绍了一种基于Docker的SSH转发解决方案，通过在Ubuntu服务器上运行容器并配置端口映射，解决了老旧系统（如Ubuntu 18.04）上的软件兼容性问题，实现了标准化的开发环境配置和便捷的远程访问。
tags:
  - Docker
  - SSH
  - VSCode
  - Linux
cover: https://assets.vluv.space/cover/ToolChain/docker_forward.avif
---

## Introduction

最近在老旧系统上经常会遇到软件兼容性问题，实习申请的开发机信息如下

- Remote Host：Ubuntu 18.04 LTS（glibc 2.27）

日常开发，存在以下 package 不兼容

- VSCode （Version >= 1.99）
- NodeJS：[node.js - Getting GLIBC_2.28 not found - Stack Overflow](https://stackoverflow.com/questions/72921215/getting-glibc-2-28-not-found)
- fastfetch，如果是 ubuntu18，需要自行编译得到可执行文件

## Solutions(TL;DR)

针对老旧系统的兼容性问题，实践了两种方案

1. 直接在远程主机上升级系统
2. 在远程主机上运行 Docker 容器，使用 SSH 直接连接到 Ubuntu 主机上运行的 Docker 容器

> [!INFO] 命令提示符说明
> `>` - LocalHost
> `$` - Remote Host(Ubuntu 18)
> `#` - Docker Container

Option 2 的优势和使用 docker 作为开发环境的优势差不多

- 环境配置标准化，易于共享和复制
- ...

### Option 1

参考如下命令升级 Ubuntu 系统，过程中有较详细的提示

```shell
# 更新软件包
$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get dist-upgrade
# 安装升级工具
$ sudo apt install update-manager-core
# 编辑升级配置文件,末行配置为lts，保存退出
$ sudo vim /etc/update-manager/release-upgrades
Prompt=lts
# 执行升级，执行后会询问重启，或自行重启
$ sudo do-release-upgrade
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.6 LTS
Release:        20.04
Codename:       focal
```

### Option 2

```mermaid
graph LR
    A[Windows 主机] -->|SSH 直接连接| B[Ubuntu 服务器]

    style A fill:#f5c2e7,stroke:#181825
    style B fill:#b2bcfb,stroke:#181825

    AA[Windows 主机] -->|SSH 连接| BB[Ubuntu 服务器]
    BB -->|启动容器，将容器22端口映射为Ubuntu 8033端口| CC[Docker 容器]
    AA -->|SSH ubuntu:8033端口| CC

    style AA fill:#f5c2e7,stroke:#181825
    style BB fill:#b2bcfb,stroke:#181825
    style CC fill:#a6e3a1,stroke:#181825
```

## Docker SSH Forwarding

### 创建 Dockerfile

创建一个目录，用于构建 Docker 镜像，可以将`.ssh`,`.config`,`.vscode-server`等目录拷贝在这，基本都可以复用

![SSH Forwarding](https://assets.vluv.space/ssh_forward_1.webp)

- 安装必要工具：vim、SSH 服务、fish shell 和 fzf 工具
- 配置 SSH，允许 root 登录，设置密码为 root(optional)
- 复制主机的 SSH 密钥到容器中，实现无密码登录
- 开放 22 端口

```dockerfile
FROM artifactory.momenta.works/docker/python:3.11

# 安装 vim 和其他必要软件包
RUN apt-get update && \
    apt update && \
    apt-get install -y vim && \
    apt-get install -y openssh-server -y && \
    apt install fish -y && \
    apt install fzf

# 拷贝ssh
COPY ./.ssh /root/.ssh/
COPY ./.config /root/.config/

# 免输入yes
RUN echo "Host *\n\tHostKeyAlgorithms +ssh-rsa\n\tPubkeyAcceptedKeyTypes +ssh-rsa\n\tStrictHostKeyChecking no" > /root/.ssh/config

RUN sed -i 's/^#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config

RUN echo 'root:root' | chpasswd
RUN chmod 600 /root/.ssh/id_rsa
RUN mkdir /var/run/sshd

EXPOSE 22

# 启动 SSH deamon service
ENTRYPOINT service ssh start && tail -f /dev/null
```

一次性在 dockerfile 中写完镜像的配置会比较麻烦，可以先在基于简单的 dockerfile 创建一个容器，进入 container 的 shell 交互环境，在容器内输入命令，安装好需要环境。

完成基础开发环境的配置后，再使用 docker commit 命令，将 docker container 打包成一个新的 imagemage 环境的配置一次在 dockerfile 中写完可能会比较麻烦，建议可以先在本地创建一个 docker container，安装好需要的环境后，commit 成一个新的 image

```shell
# 创建容器
$ docker run -it --name dev-image --hostname=dk --privileged=true --net=bridge -p 8033:22 debian:bookworm
# 进入容器
$ docker exec -it dev-image /bin/bash
# 安装需要的环境
$ apt-get update && \
    apt update && \
    apt install <xxx>
# 退出容器
$ exit
# 提交容器为新的镜像
$ docker commit dev-image dev-image:latest
```

### 构建 Docker 镜像

```shell
# 创建容器
$ docker build -t dev-image:latest .
```

### 运行 Docker 容器

```shell
# 运行容器
$ docker run -it --name ssh --hostname=dk --privileged=true --net=bridge -p 8033:22 dev-image:latest
```

参数说明：

- `--name ssh`: 设置容器名称为 ssh
- `--hostname=dk`: 设置容器主机名
- `--privileged=true`: 赋予容器特权模式
- `--net=bridge`: 使用桥接网络模式
- `-p 8033:22`: 将容器 22 端口映射到主机的 8033 端口

> [!NOTE] 挂载数据卷 & 端口映射
> 如果需要持久化数据，可以使用 `-v` 参数挂载数据卷，例如：`-v /path/on/host:/path/in/container`。

可以执行`docker exec -it <container-name> /bin/bash`进入容器，或者使用`docker exec -it <container-name> /bin/fish`进入 fish shell。

## LocalHost SSH Config

编辑 `~/.ssh/config` 文件

```shell
# Read more about SSH config files: https://linux.die.net/man/5/ssh_config
Host ubuntu
    HostName 10.21.163.77
    User root
    IdentityFile C:/Users/gjx/.ssh/id_rsa
    StrictHostKeyChecking no

Host docker
    HostName 10.21.163.77
    User root
    Port 8033
```

## 5. 工作原理

这种连接方式涉及两层转发：

1. Windows → Ubuntu 主机
2. Ubuntu 主机端口 8033 → Docker 容器 22 端口

```mermaid
sequenceDiagram
    participant W as Windows
    participant U as Ubuntu 主机
    participant D as Docker 容器

    W->>U: SSH 连接到端口 8033
    U->>D: 端口映射 8033->22
    D-->>W: SSH 会话建立

    Note over W,D: 直接连接的效果
```

当需要连接多个 Docker 容器时，可以为每个容器映射不同的端口，并在 SSH 配置中添加相应条目：

```shell
Host docker1
    HostName 10.21.163.77
    User root
    Port 8033

Host docker2
    HostName 10.21.163.77
    User root
    Port 8034
```

```mermaid
graph TB
    subgraph Windows
        ssh[SSH 客户端]
    end

    subgraph Ubuntu主机["Ubuntu 主机 (10.21.163.77)"]
        port1[端口 8033]
        port2[端口 8034]
    end

    subgraph Docker容器
        container1[容器1 - 端口22]
        container2[容器2 - 端口22]
    end

    ssh -->|ssh docker1| port1
    ssh -->|ssh docker2| port2
    port1 --> container1
    port2 --> container2

    style Windows fill:#f5c2e7,stroke:#181825
    style Ubuntu主机 fill:#b2bcfb,stroke:#181825
    style Docker容器 fill:#a6e3a1,stroke:#181825
    style ssh stroke:#181825,color:#11111e
    style port1 stroke:#181825,color:#11111e
    style port2 stroke:#181825,color:#11111e
    style container1 stroke:#181825,color:#11111e
    style container2 stroke:#181825,color:#11111e
```
