Linux小课堂: SSH 服务部署与客户端连接实战详解
服务器端:安装并配置 SSH 服务(基于 CentOS)
在 Linux 服务器环境中,SSH(Secure Shell) 是远程管理系统的标准协议
它通过加密通道实现安全的命令行访问和文件传输
本节以 CentOS Server 环境为例,完整演示 SSH 服务端的确认、启动与开机自启配置
尽管最小化安装的 CentOS 通常已默认预装并启用 openssh-server
,但仍需系统性验证其状态,确保服务可用
1 )安装 OpenSSH 服务端
使用 yum
包管理器检查并安装 openssh-server
:
sudo yum install -y openssh-server
若输出提示“Nothing to do”,表明该软件包已安装且为最新版本,无需重复操作。
2 )启动并重启 SSHD 服务
SSH 服务由守护进程 sshd
提供支持。可通过以下命令控制其运行状态:
启动 SSH 服务
sudo systemctl start sshd重启 SSH 服务(适用于配置变更后)
sudo systemctl restart sshd
3 )设置开机自启
为确保服务器重启后仍可远程接入,必须将 sshd
设为开机自启:
sudo systemctl enable sshd
执行后,系统会在 /etc/systemd/system/multi-user.target.wants/
目录下创建指向 sshd.service
的符号链接,完成注册。
4 )验证服务运行状态
使用 ps
与 grep
组合查询当前运行中的 SSH 进程:
ps -ef | grep sshd
预期输出中应包含类似:
root 1234 1 0 10:00 ? 00:00:00 /usr/sbin/sshd -D
此外,也可使用 systemctl status sshd
查看详细服务状态,表明服务已在后台监听默认端口 22
5 ) 查看激活的开机启动单元
列出所有已激活的 systemd 单元,确认 sshd.service
是否处于 enabled
状态:
systemctl list-unit-files --type=service | grep sshd
# 或
systemctl list-unit-files --type=service | grep enabled | grep ssh
# 或
systemctl list-units --type=service --state=active | grep ssh
若显示 sshd.service enabled
,表示已正确配置自启
获取服务器 IP 地址
在进行远程连接前,需明确服务器的网络地址。使用 ip addr
或传统 ifconfig
命令查看本机 IP:
ip addr show
# 或
ifconfig
# 或
ifconfig eth0
查找对应网卡(如 eth0
或 ens33
),记录其 IPv4 地址。示例输出中可能出现:
inet 172.20.10.2 netmask 255.255.255.0
常见内网地址形如 172.20.10.2
,具体值依网络环境而定
该地址即为客户端连接目标
客户端:安装 SSH 工具(跨平台支持)
不同操作系统平台对 SSH 客户端的支持情况各异,以下分别说明 Windows、Linux 与 macOS 平台的处理方式
1 ) Windows 客户端:安装第三方 SSH 工具
Windows 原生不自带完整 SSH 客户端(旧版除外),推荐使用以下工具之一:
- PuTTY(免费开源)
- Xshell
- SecureCRT
下载与使用 PuTTY 示例:
- 打开浏览器访问 https://www.putty.org
- 下载
putty.exe
(64位免安装版) - 将其复制至桌面或指定目录
- 双击运行,无需安装
推荐将 putty.exe
固定到任务栏以便快速启动
PuTTY 配置流程:
- 打开 PuTTY
- 在 Session 分类下:
- Host Name (or IP address): 输入服务器 IP,如
172.20.10.2
- Port:
22
(默认 SSH 端口) - Connection type: 选择
SSH
- Host Name (or IP address): 输入服务器 IP,如
- 可选:保存会话名称(如
centos-server
)便于复用 - 点击 Open 启动连接
首次连接时,PuTTY 会弹出安全警告,提示主机密钥未缓存。显示内容如下:
The server’s host key is not cached in the registry. Are you sure you want to continue connecting?
此时应核对指纹信息。若信任该主机,点击 是(Yes),密钥将被加入本地缓存,后续连接不再提示。
登录界面要求输入用户名与密码。例如:
login as: root
root@172.20.10.2's password:
成功登录后,即可执行任意命令,如:
ls # 查看文件列表
touch file1.txt # 创建测试文件
ip addr # 查看网络配置
# 或
ifconfig # 验证本地接口地址
exit # 退出会话
关闭窗口或输入 exit
/ Ctrl+D
即断开连接
2 ) Linux 客户端:安装 OpenSSH 客户端
大多数 Linux 发行版默认已安装 openssh-clients
,但桌面版可能未包含。可通过 yum
显式安装:
sudo yum install -y openssh-clients
验证是否已安装:
which ssh
预期返回路径 /usr/bin/ssh
。若未安装,YUM 将自动解析依赖并补全
安装完成后,直接使用 ssh
命令连接服务器:
ssh root@172.20.10.2
首次连接时,终端会提示:
The authenticity of host '172.20.10.2 (172.20.10.2)' can't be established.
RSA key fingerprint is SHA256:xxxxxxxxxxxxxxxxxxxxx.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
输入 yes
确认信任,系统自动将主机密钥写入 ~/.ssh/known_hosts
文件。
认证通过后,用户进入远程 shell 环境,主机名变为目标服务器名称(如 localhost.localdomain
)。执行命令如:
ls
touch file2.txt
exit
均可实时反映在服务器上
3 ) macOS 客户端:原生支持 SSH
macOS 内置 OpenSSH 客户端,无需额外安装。打开 终端(Terminal) 应用即可使用 ssh
命令:
ssh root@172.20.10.2
行为逻辑与 Linux 客户端完全一致:首次连接需确认主机密钥指纹,输入 yes
后永久记录;随后输入密码完成登录
同样支持 ~/.ssh/known_hosts
记录机制,且支持密钥对免密登录等高级特性
创建测试文件验证连通性:
可在 macOS 终端中执行:
ls
touch file3.txt
ifconfig # 或 ipconfig getifaddr en0 查看本地IP
验证文件是否同步出现在服务器端。
技术要点总结与关键细节凝练
1 ) SSH 架构核心理解
- 服务端(Server):
openssh-server
提供sshd
守护进程,监听 22 端口 - 客户端(Client): 使用
ssh [user@host]
发起连接请求 - 加密机制: 基于非对称密钥体系,保障通信安全
- 身份认证: 支持密码、公钥等多种方式
2 ) 关键命令归纳
操作 | 命令 |
---|---|
安装服务端 | yum install -y openssh-server |
安装客户端 | yum install -y openssh-clients |
启动服务 | systemctl start sshd |
重启服务 | systemctl restart sshd |
开机自启 | systemctl enable sshd |
查看状态 | systemctl status sshd |
连接服务器 | ssh root@172.20.10.2 |
查看进程 | ps -ef | grep sshd |
或换一种表达
步骤 | 操作内容 | 关键技术点 |
---|---|---|
1 | 服务端确认 sshd 运行状态 | 使用 systemctl status sshd , ps -ef | grep sshd |
2 | 获取服务器局域网 IP 地址 | 执行 ifconfig 或 ip addr |
3 | 客户端安装对应 SSH 工具 | Windows: PuTTY/Xshell; Linux/macOS: 原生命令行 |
4 | 首次连接处理主机密钥验证 | 接受指纹以建立信任链,防止中间人攻击 |
5 | 成功登录并执行命令验证 | 创建文件、查看网络配置,确认双向一致性 |
或
技术点 | 说明 |
---|---|
加密传输 | SSH采用非对称加密(RSA/DSA/ECDSA)协商会话密钥,后续通信使用对称加密(AES等)保障数据机密性与完整性 |
主机认证 | 客户端通过比对服务器公钥指纹防止中间人攻击,首次连接需手动确认 |
用户认证 | 支持密码认证与公钥认证(推荐后者以提升安全性) |
端口默认值 | SSH服务监听TCP 22端口,可通过 /etc/ssh/sshd_config 修改 |
配置文件路径 服务端 | /etc/ssh/sshd_config —— 控制登录权限、允许Root登录、密钥认证开关等 |
配置文件路径 客户端 | ~/.ssh/config —— 自定义别名、端口映射、跳板机配置 |
重点强调:
- SSH 默认端口为 22,除非特别配置防火墙规则或更改监听端口,否则不得修改
- 主机密钥验证机制至关重要,它是抵御 MITM(Man-in-the-Middle)攻击的第一道防线
- 所有客户端最终都通过 TCP/IP 协议栈连接至目标服务器的
0.0.0.0:22
监听套接字 - 登录账户权限直接影响操作范围,root 用户拥有最高权限,应谨慎使用
3 ) 安全注意事项
- 避免频繁使用 root 登录:建议创建普通用户并通过
sudo
提权 - 禁用密码认证,启用密钥登录:提升安全性
- 修改默认端口(非必要不改):防止自动化扫描攻击
- 定期更新 SSH 软件包:修复潜在漏洞
补充 NestJS + TypeScript 示例:构建 SSH 管理模块
1 ) 方案1
虽然实际 SSH 操作多在 Shell 层完成,但在某些自动化运维系统中,可通过 Node.js 调用底层 SSH 功能。以下是基于 NestJS 框架封装的一个轻量级 SSH 工具服务。
安装依赖
npm install ssh2
npm install @types/node --save-dev
创建 SSH 服务(ssh.service.ts
)
import { Injectable } from '@nestjs/common';
import * as Client from 'ssh2';@Injectable()
export class SshService {private readonly config = {host: '172.20.10.2',port: 22,username: 'root',password: 'your_root_password', // 生产环境应使用密钥或环境变量};async executeCommand(command: string): Promise<string> {return new Promise((resolve, reject) => {const conn = new Client();let output = '';conn.on('ready', () => {conn.exec(command, (err, stream) => {if (err) {conn.end();return reject(err);}stream.on('close', (code, signal) => {conn.end();resolve(output);}).on('data', (data) => {output += data.toString();}).stderr.on('data', (data) => {console.error('STDERR:', data.toString());});});}).connect(this.config);});}async connectAndCreateFile(filename: string): Promise<void> {const commands = [`touch ${filename}`,'ls -la',];for (const cmd of commands) {try {const result = await this.executeCommand(cmd);console.log(`[SSH] Executed: ${cmd}\nOutput:\n${result}`);} catch (error) {console.error(`[SSH] Error executing: ${cmd}`, error.message);}}}
}
注册模块(ssh.module.ts
)
import { Module } from '@nestjs/common';
import { SshService } from './ssh.service';@Module({providers: [SshService],exports: [SshService],
})
export class SshModule {}
控制器调用示例(app.controller.ts
)
import { Controller, Get } from '@nestjs/common';
import { SshService } from './ssh.service';@Controller()
export class AppController {constructor(private readonly sshService: SshService) {}@Get('test-ssh')async testSsh() {await this.sshService.connectAndCreateFile('nest_test_file.txt');return { message: 'SSH command executed via NestJS' };}
}
注意事项:
- 实际生产中禁止硬编码密码,应使用
dotenv
加载环境变量 - 推荐使用私钥认证方式替代密码
- 此方案适用于轻量级集成,大规模运维建议采用 Ansible、SaltStack 等专用工具
2 ) 方案2
虽然生产级 SSH 实现复杂,但可通过 Node.js 生态中的 ssh2
模块构建轻量级控制台工具
以下为一个基于 NestJS 架构理念封装的 SSH 连接服务示例
初始化项目结构
nest new ssh-client-demo
cd ssh-client-demo
npm install ssh2
npm install @types/ssh2 --save-dev
创建 SSH 服务类
// src/ssh/ssh.service.ts
import { Injectable, Logger } from '@nestjs/common';
import { Client } from 'ssh2';@Injectable()
export class SshService {private readonly logger = new Logger(SshService.name);async connectAndExecute(host: string,port: number,username: string,password: string,commands: string[],): Promise<string[]> {return new Promise((resolve, reject) => {const conn = new Client();const results: string[] = [];conn.on('ready', () => {this.logger.log('SSH connection established.');let completed = 0;commands.forEach((cmd) => {conn.exec(cmd, (err, stream) => {if (err) {this.logger.error(`Command failed: ${cmd}`, err.message);results.push(`ERROR: ${err.message}`);if (++completed === commands.length) {conn.end();resolve(results);}return;}let stdout = '';let stderr = '';stream.on('close', (code, signal) => {this.logger.debug(`Command exited [${cmd}] code: ${code}, signal: ${signal}`);if (stderr) {results.push(`[${cmd}] ERROR: ${stderr}`);} else {results.push(`[${cmd}] OUTPUT:\n${stdout}`);}if (++completed === commands.length) {conn.end();resolve(results);}});stream.on('data', (data) => {stdout += data.toString();});stream.stderr.on('data', (data) => {stderr += data.toString();});});});});conn.on('error', (err) => {this.logger.error('SSH connection error', err.message);reject(err);});conn.connect({host,port,username,password,readyTimeout: 10000,});});}
}
注册模块并暴露 CLI 脚本
// src/ssh/ssh.module.ts
import { Module } from '@nestjs/common';
import { SshService } from './ssh.service';@Module({providers: [SshService],exports: [SshService],
})
export class SshModule {}
// src/app.service.ts
import { Injectable } from '@nestjs/common';
import { SshService } from './ssh/ssh.service';@Injectable()
export class AppService {constructor(private readonly sshService: SshService) {}async runRemoteTasks() {const host = '172.20.10.2';const port = 22;const username = 'root';const password = 'your_root_password'; // In practice, use ENV or vaultconst commands = ['whoami', 'ls -la', 'touch nestjs_test.txt', 'echo "Hello from NestJS SSH" > hello.txt'];try {const outputs = await this.sshService.connectAndExecute(host, port, username, password, commands);outputs.forEach((output, index) => {console.log(`Step ${index + 1} Result:\n${output}\n---`);});} catch (error) {console.error('Failed to execute remote commands:', error.message);}}
}
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AppService } from './app.service';async function bootstrap() {const app = await NestFactory.create(AppModule);const appService = app.get(AppService);// 执行 SSH 任务await appService.runRemoteTasks();await app.close(); // 自动退出
}
bootstrap();
运行效果说明
执行 npm run start
后,程序将:
- 连接到指定服务器
- 依次执行
whoami
,ls
,touch
,echo
四条命令 - 收集每条命令的标准输出与错误信息
- 输出至本地控制台
- 自动关闭连接
注意:
- 此仅为演示用途,不可用于生产环境替代真实 SSH 客户端
- 实际场景中应结合密钥认证、连接池、日志审计等功能增强健壮性
3 )方案 3
为体现自动化运维场景下的编程整合能力,以下提供一个基于 NestJS 框架的SSH连接服务模拟实现,使用 node-libssh
或 ssh2
库进行底层调用。
安装依赖
npm install ssh2
npm install @nestjs/common @nestjs/core
创建SSH服务类
// ssh.service.ts
import { Injectable, Logger } from '@nestjs/common';
import { Client } from 'ssh2';interface SshConnectionOptions {host: string;port?: number;username: string;password?: string;privateKey?: string;
}@Injectable()
export class SshService {private readonly logger = new Logger(SshService.name);async executeCommand(options: SshConnectionOptions,command: string,): Promise<string> {return new Promise((resolve, reject) => {const conn = new Client();conn.on('ready', () => {this.logger.log('SSH connection established');conn.exec(command, (err, stream) => {if (err) {conn.end();return reject(err);}let output = '';let errorOutput = '';stream.on('data', (data) => {output += data.toString();}).stderr.on('data', (data) => {errorOutput += data.toString();}).on('close', (code, signal) => {conn.end();if (code === 0) {resolve(output);} else {reject(new Error(`Command failed with code ${code}: ${errorOutput}`));}});});});conn.on('error', (err) => {reject(new Error(`SSH connection error: ${err.message}`));});conn.connect({host: options.host,port: options.port || 22,username: options.username,...(options.password ? { password: options.password } : {}),...(options.privateKey ? { privateKey: options.privateKey } : {}),});});}async createTestFile(ip: string, user: string, pass: string): Promise<void> {const cmd = 'touch /tmp/test_nestjs_ssh.txt && ls /tmp/ | grep test_nestjs_ssh.txt';try {const result = await this.executeCommand({ host: ip, username: user, password: pass },cmd,);this.logger.log(`File creation verified on ${ip}: ${result.trim()}`);} catch (err) {this.logger.error(`Failed to create file via SSH: ${(err as Error).message}`);}}
}
注册模块并调用
// app.module.ts
import { Module } from '@nestjs/common';
import { SshService } from './ssh.service';@Module({providers: [SshService],exports: [SshService],
})
export class SshModule {}
在控制器中测试
// app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { SshService } from './ssh.service';@Controller()
export class AppController {constructor(private readonly sshService: SshService) {}@Get('ssh-test')async sshTest() {await this.sshService.createTestFile('172.20.10.2', 'root', 'your_password');return { status: 'SSH test initiated' };}
}
安全提醒:生产环境中应避免明文存储密码,建议结合Vault、环境变量或密钥对方式进行认证
SSH 构建安全运维基石
SSH 不仅是远程登录工具,更是现代 DevOps 流程的基础组件,支撑着自动化部署、配置管理(Ansible)、容器编排(Kubernetes 节点接入)等多种关键场景
通过对服务端 sshd
的正确配置与客户端多样化接入方式的理解,可构建稳定、安全、高效的服务器管理体系。无论是图形化工具还是命令行接口,亦或是程序化调用,底层均遵循相同的加密协议规范(SSH-2),保证了跨平台兼容性与安全性
掌握 SSH 全链路部署与调试能力,是每一位系统工程师、运维开发人员不可或缺的核心技能