V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
nikoo
V2EX  ›  问与答

为什么修改端口号后仍可保持连接?

  •  
  •   nikoo · 2018-05-29 07:59:14 +08:00 · 3576 次点击
    这是一个创建于 2405 天前的主题,其中的信息可能已经有所发展或是发生改变。
    一台服务器仅开放 22 端口,屏蔽其他所有端口的访问权限
    通过 ssh ip:22 登录该服务器,修改 /etc/ssh/sshd_config 将 ssh 端口号改为例如 1234
    保存后重启 ssh 执行 /etc/init.d/ssh restart

    此时通过 netstat 查看 22 端口已经没有了监听,ssh 端口成功改为了 1234

    但神奇的是此时与服务器仍然保持着连接,只要不 exit,那么这个 ssh 客户端是一直可用的

    但是一旦 exit 后,无法再次通过 1234 或者 22 端口连接服务器

    好奇的是为什么在修改端口号并且 22 端口已经没有了监听的情况下,没有退出的那个客户端仍然可以与服务器通信
    因为 22 端口已没有了监听,其他端口都由防火墙屏蔽了,那这个通信是如何进行的?
    第 1 条附言  ·  2018-05-29 13:09:27 +08:00
    实际上条件中:"服务器端屏蔽除了 22 之外的所有端口" 如果更换为
    通过端口映射连接的内网机器,也同样有这个问题,

    比如在有公网 IP 路由器的路由器上,映射路由上一个端口(例如 888 )至内网的一台机器 22 端口,
    那么 SSH 公网 IP:888 连接这台内网机器,此时我在 SSH 中把该机器 SSH 端口改为 1234 并重启 SSH,在 22 端口失去监听的时候,仍然可以进行 SSH 操作,只要不 exit。

    这就很奇怪,好奇此时客户端是如何与这台内网机器通信的。
    31 条回复    2018-05-30 13:25:50 +08:00
    0312birdzhang
        1
    0312birdzhang  
       2018-05-29 08:32:17 +08:00 via iPhone
    长链接没释放,或许就是这个设定。万一你改错了然后断开了那岂不是永远连不上了?
    坐等楼下专业的说明😂
    princeofwales
        2
    princeofwales  
       2018-05-29 08:56:04 +08:00   ❤️ 4
    监听的端口就是提供个入口
    连接已经建立了 ,还要监听何用
    snnn
        3
    snnn  
       2018-05-29 09:00:22 +08:00
    嗯,是这样的。
    你可以强行把所有 ssh 的进程杀掉,以及它们的子进程。
    不过如果是我,我选 reboot。
    ThirdFlame
        4
    ThirdFlame  
       2018-05-29 09:14:58 +08:00
    kill -9 -1
    已经建立的连接保持不动,这时候可以试试 1234 能不能连上了。 要是连不上,而且你还断开了,那欢迎你用远程控制台 或者 跑现场吧。
    hilow
        5
    hilow  
       2018-05-29 12:26:14 +08:00 via Android   ❤️ 2
    ssh 使用 tcp 协议,可以了解一下 tcp 连接简历的过程就容易理解了。

    server 调用 listen 监听 22 端口的请求
    client 调用 connect 连接到 22 端口,此时 client 自动分配一个 port1
    server 调用 accept 接受 client 的连接请求,此时 server 自动分配一个新的 port2

    所以更改 ssh 的 listen port 是,对上面已经建立的连接 clientIP:port1<------>serverIP:port2
    是没有影响的
    nikoo
        6
    nikoo  
    OP
       2018-05-29 12:31:43 +08:00
    @hilow 谢谢回复,但问题是 server 屏蔽了除了 22 之外的所有端口连接,那么理论上 serverIP:port2 是不可能与 client 通信的,这个怎么理解呢?
    nikoo
        7
    nikoo  
    OP
       2018-05-29 12:34:25 +08:00
    @0312birdzhang @princeofwales 这个“长连接”是如何,通过什么方式( or 端口)与服务器建立的连接?(在服务器端屏蔽除了 22 之外的所有端口的前提条件下)
    hilow
        8
    hilow  
       2018-05-29 12:41:09 +08:00 via Android
    你的问题是 server 端的 port2 是被禁止访问的,为什么 clientIP:port1<------>serverIP:port2 之间能通信?

    这个我也得查资料确认下,猜测是因为防火墙禁止的只是发送到 非 22 端口的 syn 数据包吧。
    我这就确认下去
    hilow
        9
    hilow  
       2018-05-29 12:53:23 +08:00 via Android
    我说的有些地方不准确。晚上有空我再补充下。
    抱歉
    nikoo
        11
    nikoo  
    OP
       2018-05-29 13:03:02 +08:00
    @hilow 谢谢,实际上条件中:"服务器端屏蔽除了 22 之外的所有端口" 如果更换为
    通过端口映射连接的内网机器,也同样有这个问题,

    比如在有公网 IP 路由器的路由器上,映射路由上一个端口(例如 888 )至内网的一台机器 22 端口,
    那么 SSH 公网 IP:888 连接这台内网机器,此时我在 SSH 中把该机器 SSH 端口改为 1234 并重启 SSH,在 22 端口失去监听的时候,仍然可以进行 SSH 操作,只要不 exit。

    这就很奇怪,好奇此时客户端是如何与这台内网机器通信的。
    zst
        12
    zst  
       2018-05-29 13:08:21 +08:00 via Android
    是不是防火墙放行了 established 状态的包
    msg7086
        13
    msg7086  
       2018-05-29 15:20:18 +08:00   ❤️ 5
    监听影响的是新连接。已经建立的连接是不受监听影响的。

    我们举个栗子好了。
    比如你有 SSH-Server (PID=1000),监听 IP1 端口 22。
    现在你用 SSH 客户端从 IP2 连上去 22 端口,这会发生什么事呢。
    1. SSH-Server 接受了你的连接 (IP2 49123 IP1 22);
    2. SSH-Server 叉出一个新进程 SSH-Conn (PID=1096);
    3. SSH-Conn 接管 (IP2 49123 IP1 22) 连接,开始产生 SSH 通信;
    4. SSH-Server 继续摸鱼,等待下一个连接。

    现在你更改端口为 1234 并重启 SSH 以后,会发生什么事呢。
    1. SSH-Server 被谋杀;
    2. SSH-Conn 苟且偷生,继续 SSH 通信;
    3. SSH-Server 被复活 (PID=1140),监听 IP1 端口 1234。

    至于为什么还能进行 SSH 操作,当然是因为
    1. (IP2 49123 IP1 22) 这个连接还活着;
    2. SSH-Conn (PID=1096) 这个进程还活着。
    Tink
        14
    Tink  
       2018-05-29 15:25:57 +08:00
    你已经建立的连接没有影响
    linyinma
        15
    linyinma  
       2018-05-29 16:50:48 +08:00   ❤️ 2
    看了半天都没有一个回答对 涉及两个知识点:

    刚开始 SSHD 进程 22 端口处于 LISTENING 状态, 你 ssh 连接中完成 accept 后内核生产新的 socket fd 完成 5 元组绑定{ 协议, 目的地址、目的端口, 本地地址,本地端口},此过程本地地址端口被复用( SO_REUSEADDR ),并且 SSHD fork 产生协同进程( bash,由 /etc/passwd 配置),协同进程继承 SSHD accept 返回的 socket fd ;


    在此后的过程中都是和协同进程交换 和 SSHD 办毛子关系都没有
    nikoo
        16
    nikoo  
    OP
       2018-05-29 17:40:08 +08:00
    @linyinma 谢谢回复!这个协同进程是如何与服务器进行通信的?

    或者换个说法,在 SSH 客户端与服务器成功连接以后,客户端(或服务器)的防火墙如何屏蔽能立刻断开此时的 SSH 客户端与服务器的连接?
    再次感谢答疑解惑
    xmh51
        17
    xmh51  
       2018-05-29 17:42:45 +08:00
    修改 ssh 端口,和防火墙,还有内网端口映射,没半毛钱关系。单纯修改 ssh 端口生效后,已经建立的连接还是可以用的,新的连接必须连接新的 ssh 端口。你的之所以不能连接是因为虽然 ssh 端口监听了,但是被外部条件阻止了(没配置端口映射,防火墙没放开端口)
    nikoo
        18
    nikoo  
    OP
       2018-05-29 17:43:55 +08:00
    @linyinma 在实际测试里,SSH 登录并将端口 22 改为 1234 后,再在 22 端口开一个 nginx,并确定 22 端口已经是 nginx 的监听了,但此时 SSH 仍然与服务器连接的好好的
    xmh51
        19
    xmh51  
       2018-05-29 17:45:11 +08:00
    你这个是把两个事混在一起看了,应该分开看。
    nikoo
        20
    nikoo  
    OP
       2018-05-29 17:45:55 +08:00
    @xmh51 谢谢回复,问题是已经建立的 SSH 连接客户端与服务器之间是如何通信的?(通过什么方式通信的)
    nikoo
        21
    nikoo  
    OP
       2018-05-29 17:49:02 +08:00
    也就是一台屏蔽了所有端口仅开放 22 端口的服务器,
    SSH 登录上去把 22 端口改到 1234 并且在原 22 端口开一个 nginx,
    那么目前这台服务器理论上已和外接彻底隔绝了,用浏览器访问 22 端口也是 nginx 的页面了,
    那么为什么没有断开的 ssh 客户端仍然可以操作这台服务器,
    这个没有断开的客户端是通过什么方式与服务器进行通信的?
    xmh51
        22
    xmh51  
       2018-05-29 17:58:48 +08:00
    @nikoo 这个还真没深究过,先点个收藏,找到原理了 回复下。
    wly19960911
        23
    wly19960911  
       2018-05-29 18:37:45 +08:00
    [root@xxxxxxxxxxxx]~# netstat -ano|grep ':22'
    tcp 0 0 172.19.113.74:22 00.000.00.00:44947 ESTABLISHED keepalive (6983.97/0/0)

    刚刚检查了下,连接没有建立在 localhost 上,建立在内网的 ip 可以解释为什么 ssh-server 没有绑定在 22 端口上但是仍然可以工作。但是至于防火墙的问题我也不懂,抛砖引玉下
    hyq
        24
    hyq  
       2018-05-29 19:07:30 +08:00
    我猜是建立连接以后 客户端的 ip,端口,服务器 ip,端口这四个属性就被关联起来,操作系统根据这个把包发给对应的进程 /文件描述符
    qbqbqbqb
        25
    qbqbqbqb  
       2018-05-30 00:30:43 +08:00
    关闭监听是不会影响已经建立的连接的。sshd 监听对应的地址端口四元组是(ServerIP:22,0.0.0.0:0),协同进程建立的连接对应(ServerIP:22,ClientIP:port),两者是可以区分的,sshd 退出但协同进程仍然存活,监听“(ServerIP:22,0.0.0.0:0)”失效,连接“(ServerIP:22,ClientIP:port)”仍然有效。
    你可能是在想象“关闭在 22 端口上的监听”是“把 22 端口堵上”,但实际上 socket 的工作原理不是这样的。
    qbqbqbqb
        26
    qbqbqbqb  
       2018-05-30 00:40:45 +08:00
    ssh 不涉及防火墙屏蔽除 22 以外端口的问题,因为连接也是建立在 22 端口上的。

    但是即使要在其它端口上通信而不被防火墙屏蔽也是可能的(比如 FTP 协议就要建立额外的连接),因为 Linux 的 iptables 防火墙可以跟踪连接状态,可以(并且经常会)配置为直接放行已经建立的连接( ESTABLISHED )或和某个已经建立的连接相关的连接( RELATED,Linux 内核里有针对已知协议专门编写的模块判断数据包是否属于这个状态)。
    hilow
        27
    hilow  
       2018-05-30 00:42:21 +08:00 via Android   ❤️ 2
    server 调用 listen 监听 22 端口的请求
    client 调用 connect 连接到 22 端口,此时 client 自动分配一个 port1,并向 server 发送一个 syn 包,表示请求建立新的连接

    server 收到 syn 后,调用 accept 接受 client 的连接请求,并向 client 回复一个 syn+ack 包
    client 回复 ack 包后,连接正式建立完毕。后续双方发送 psh/ack 包交换数据。直到出现 fin 或 rst 包时,或者连接之间长时间无数据传递,即超时,连接才会关闭。

    题目所说防火墙禁用非 22 端口的数据,我猜很大可能,仅仅是允许 22 端口收到 syn 包,建立连接,非 22 端口收到 syn 会被防火墙丢弃。
    这个需要你来确认,具体使用的什么防火墙,配置的什么策略。


    所以能导致当前 ssh 连接失效断开的情况,有以下几种,
    1.sshd 进程关闭,导致 client 收到 fin 或者 rst 包
    2.防火墙丢弃 22 端口收到的 psh 或 ack 数据

    mario85 所贴链接说明,不会出现第 1 种情况,因为每个已建立的连接是新的进程。ssh 代码应该做了特殊处理,以保证不在重启配置时关闭之前启动的进程。
    第二种情况也没有出现,因为防火墙的配置中允许所有端口的 push/ack 包,禁止非 22 端口的 syn 包。



    tcp 连接建立过程
    https://en.m.wikiversity.org/wiki/Wireshark/TCP

    ssh 重启过程,链接由 mario85 提供
    https://unix.stackexchange.com/questions/27636/how-does-ssh-connection-survive-a-network-restart

    iptables 防火墙使用
    https://www.digitalocean.com/community/tutorials/iptables-essentials-common-firewall-rules-and-commands


    Allow All Incoming SSH To allow all incoming SSH connections run these commands:

    sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
    sudo iptables -A OUTPUT -p tcp --sport 22 -m conntrack --ctstate ESTABLISHED -j ACCEPT
    The second command, which allows the outgoing traffic of established SSH connections, is only necessary if the OUTPUT policy is not set to ACCEPT.
    GuuJiang
        28
    GuuJiang  
       2018-05-30 06:52:01 +08:00 via iPhone
    有一家大宝剑开在表♂世界 22 号
    楼主想要放♂松一下的时候就进入 22 号
    大宝剑为楼主分配了一个伺♂服♂器并且进入里♂世界 22 号
    一分钟以后由于外面风声紧,大宝剑只能搬到了表♂世界 1234 号
    后面的人想要放♂松只能到 1234 号,而楼主依然留在里♂世界 22 号不受影响
    三分钟以后楼主离开了里♂世界 22 号,下次还想再来就只能到 1234 号了
    linyinma
        29
    linyinma  
       2018-05-30 09:40:55 +08:00
    @nikoo Q1:也就是一台屏蔽了所有端口仅开放 22 端口的服务器,
    SSH 登录上去把 22 端口改到 1234 并且在原 22 端口开一个 nginx,
    那么目前这台服务器理论上已和外接彻底隔绝了,用浏览器访问 22 端口也是 nginx 的页面了,
    那么为什么没有断开的 ssh 客户端仍然可以操作这台服务器,
    这个没有断开的客户端是通过什么方式与服务器进行通信的?

    A1: 我不是回答的嘛,socket 通讯 sfd 绑定的一个 5 元组,为什么能绑定成功后因为 地址和端口被复用, 你还在纠结“ SSH 登录上去把 22 端口改到 1234 并且在原 22 端口开一个 nginx ” , 这会 nginx server 那个 socket fd 在内核中只是一个三元组,不影响原先通信;

    Q2: 你希望修改 sshd 监听端口后,能关闭已连接的客户端, 说白了就要在内核层面修改状态, 从用户层面么 kill 该链接对应的进程,发生中断
    Tyanboot
        30
    Tyanboot  
       2018-05-30 12:52:18 +08:00
    #5 已经明确的跟你说了建立的连接服务端用的都是新端口了, 你为啥还纠结通过什么方式通信的?

    就算你把 22 改成 nginx, 和 nginx 也没半毛钱关系.

    至于为什么"防火墙关闭除 22 之外的端口之外还能访问", 这应该是你防火墙还有放行 established 状态的规则. 因为我很久以前就试过 iptables -F 之后 只加一个 22 端口 accept 的规则. 再用-P DROP 设置默认规则之后. 当前的 ssh 链接立马就被中断了.
    hilow
        31
    hilow  
       2018-05-30 13:25:50 +08:00 via Android
    @Tyanboot 我在#5 说的不对。服务端是用 22 端口建立的连接。
    #27 是我更正后的准确说明
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2795 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 06:59 · PVG 14:59 · LAX 22:59 · JFK 01:59
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.