Linux命令 ssh


原文链接: Linux命令 ssh

ssh的连接重用
牢记25个最佳的SSH命令
16条技巧让你更高效使用SSH
ssh通过代理连接服务器
SSH端口中转全攻略
Tunnel:论如何在内网中自由渗透
usage: ssh [-1246AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]

       [-D [bind_address:]port] [-E log_file] [-e escape_char]
       [-F configfile] [-I pkcs11] [-i identity_file] [-L address]
       [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]
       [-Q query_option] [-R address] [-S ctl_path] [-W host:port]
       [-w local_tun[:remote_tun]] [user@]hostname [command]

-b bind_address 当有多个网卡时,指定绑定的ip
-c cipher_spec 加密算法 “3des”, “blowfish”, and “des”.


ssh -f root@$ADMIN_SSH_HOST -p $ADMIN_SSH_PORT "sh $PROJECT_DIR/backend_deploy.sh $NAME $ADMIN_PORT $DB_HOST $DB_PORT $DB_USER $DB_PASSWORD $DB_DATABASE $REDIS_HOST $REDIS_PASSWORD";

scp nginx.conf root@peeper:/etc/nginx/
ssh -tt root@peeper <<'ENDSSH'
nginx -t && systemctl restart nginx
ENDSSH


RSA 公钥验证和匹配的已知可信远程主机是一种有效防止中间人攻击的手段。不过在批量 scp 或者 ssh 的时候,可能会出现一水儿的 RSA 公钥验证请求。这种时候,要不然需要手工输入一长串的 yes;要不然连输入的机会都没有,就被刷屏了。

限制IP访问 /etc/hosts.allow 立即生效

  1. 在ssh的配置文件:/etc/ssh/sshd_config中添加如下一行即可
    Allowusers username@192.168.1.100 #允许某个ip用什么帐户登陆 AllowUsers root@tardis root@tardis.drwho.com Allowusers root@123.132.226.66 root@123.132.252.42
  2. 修改/etc/hosts.allow文件可以控制允许访问本机的IP
    ```
    在这个文件中添加sshd: 允许访问的IP地址
    sshd:123.132.226.66,123.132.252.42:allow

1:修改“/etc/hosts.allow”文件,在最下面添加一行:
sshd:192.168.100.0/255.255.255.0 #允许局域网内所有机器访问服务器上的sshd进程
sshd:60.28.160.244 #允许外网的60.28.160.244访问这个服务器上的sshd进程
sshd:11.8.,12.1.,18.3.:allow
sshd:123.132.252.42,123.132.226.66,139.129.234.31,139.129.108.163:allow
2:修改“/etc/hosts.deny”文件,在最后一行添加:
sshd:all:Deny #禁止所有


# 1. ssh scp等消除每次问yes/no,避免需要手工输入一长串的 yes这种情况
可以在 scp 或者 ssh 的时候加上 -o StrictHostKeyChecking=no 即可。
  `ssh -o StrictHostKeyChecking=no `

  `vi /etc/ssh/ssh_config`
      StrictHostKeyChecking no

# 2. ssh保持连接 5分钟发送一个数据包 5*60
`ssh -o TCPKeepAlive=yes -o ServerAliveInterval=300 `

#3.
```bash

#!/bin/sh
for i in {2..18}
do
expect<<!
  spawn ssh-copy-id -i root@10.114.0.${i}
  expect "*yes/no)?*"
  send "yes\r"
  expect "*password:*"
  send "Lybb@2017\r"
  expect eof
!
done

-L 正向代理 端口转发 正向隧道

-L [bind_address:]port:host:hostport
-L [bind_address:]port:remote_socket
-L local_socket:host:hostport
-L local_socket:remote_socket

ssh -L 9999:localhost:8384 machine
This will bind to your local port 9999 and forward all connections from there to port 8384 on the target machine. This still works even if Syncthing is bound to listen on localhost only.

本机访问代理后面的内网机器,且中间没有防火墙; 执行点在本机localhost

LHOST -> proxy -> RHOST

本地访问127.0.0.1:port1就是host:port2(用的更多)
ssh -CfNg -L local_port:target_ip:target_port proxy_user@proxy_ip #本地转发
ssh -CNfg -L 0.0.0.0:2222:139.129.234.31:22 root@139.129.108.163 -p 222

可以将dmz_host的hostport端口通过proxy_ip转发到本地的port端口
ssh -qTfnN -L local_port:unreachable_ip:unreachable__port -l dmz_user dmz_ip -p 222 #正向隧道,监听本地port

dmz == proxy 代理机
target == Rhost==remote 目标机

-q Quiet mode.                    安静模式
-T Disable pseudo-tty allocation. 禁用伪终端,自动化时使用。不占用 shell 了
-C Enable compression.            压缩数据传输。
-N Do not execute a shell or command. 不执行远程命令,端口转发就用它了,通常与-f连用。
-f Fork into background after authentication.  后台运行 认证用户/密码,通常和-N连用,不用登录到远程主机。并推荐加上 -n 参数。
-n Redirects stdin from /dev/null (actually, prevents reading from stdin). -f 推荐的,不加这条参数应该也行。
-g Allow remote hosts to connect to forwarded ports. 在-L/-R/-D参数中,允许远程主机连接到建立的转发的端口,如果不加这个参数,只允许本地主机建立连接。

-R 反向代理 反向隧道

-R [bind_address:]port:host:hostport
-R [bind_address:]port:local_socket
-R remote_socket:host:hostport
-R remote_socket:local_socket

用于内网穿透防火墙限制;执行点在[RHOST]肉鸡target (RHOST)

LHOST<--->proxy<---firewall--->Rhost

ssh -CfNg -R port2:127.0.0.1:port1 user@host #远程转发
访问host:port2就是访问127.0.0.1:port1

  1. ssh -qTCNfg -R 8000:localhost:8080 root@139.129.108.163
    转发外网139.129.108.163:8000的流量->内网(RHOST) 127.0.0.1:8080 ((RHOST)内网执行)
  2. nc -l 8001 -c "nc localhost 8000" #外网执行 本机端口转发
    ssh -qTCNfL *:8001:localhost:8000 localhost #本机端口转发8001->8000

可以将dmz_host的hostport端口转发到remote_ip的port端口 dzm == local
ssh -qTCNfn -R remote_port:dmz_host:dmz_port -l user remote_ip #反向隧道,用于内网穿透防火墙限制之类

-D [bind_address:]port 动态映射 [做socks5代理使用]

ssh -qTfnN -D port remotehost

ssh方式使用google
ssh -qTfnN -D 7070 root@xx.xx.xx.xx -p3009

SOCKS主机:127.0.0.1 端口:7070
确定后就可以正常使用google

把socks代理转换成http代理:privoxy

5、自动连接和防断线脚本

```bat
:1
plink -pw “password” -D 7070 user@serverip
goto 1
```
apt-get install sshpass
`sshpass -p "password" ssh -D 7070 user@serverip`

然后写脚本autossh.sh,内容如下:
```bash
#!/bin/bash

while [ '' == '' ]
do
ssh_d_process_num=`ps aux|grep -E 'ssh \-' |grep -v grep |wc -l`
if [ "$ssh_d_process_num" == "0" ]; then
  /home/user/sshpass -p "password" ssh -D 7070 user@ServerIP &
fi

sleep 300
done
```

SSH打洞

使用场景

开发联调。别人只能访问到你的外网地址,但是你想在本地机器监测并调试
内网内容资源展示

基本流程就是别人访问外网,然后外网转发到内网,实际的内容是在内网。因为外网不能主动访问到内网,所以下面这条命令一定要从内网运行。
#暴露内网的3306给remotehost的9123,这样别人访问外网的9123实际就是访问的内网的3306
ssh -R 9123:127.0.0.1:3306 username@remotehost
流量转发
使用场景

本地不能直接连接处于内网的mysql,可以通过ssh forward上去

这条命令会转发本地3308的流程到mysqlhost的3306端口
ssh -L 3308:mysqlhost:3306 user@sshhost
还是比较好区分这两种命令的。R代表remote,L代表local,如果是-R表示remote -> local,-L标志local->remote

################################################################################
移除fingerprint
ssh-keygen -f "/home/ubuntu/.ssh/known_hosts" -R 47.90.57.192

允许密码登录

vi /etc/ssh/sshd_config

 PasswordAuthentication yes
 #PasswordAuthentication no

在服务器添加完公钥之后,ssh服务器然后报了错误 sign_and_send_pubkey: signing failed: agent refused operation
然后执行了以下命令才好 eval "$(ssh-agent -s)"
ssh-add

不输密码登录SSH

ssh-keygen
-b 密钥长度,默认为2048
-t 密钥类型, rsa|des... (默认类型为rsa)
SSH1: RSA
SSH2: RSA, DSA, ECDSA
-N 新密码
-f 指定密钥文件,创建时会同时生成一个.pub结尾的公钥文件。
-C
-c 修改公钥或私钥文件中的注释
-p 修改私钥文件密码
-P 旧密码
-e 导出为其它格式的密钥文件,可以转换密钥类型
-i 从其他格式的密钥文件导入,可以转换密钥类型
-m 与-e,-i配合使用,指明导出或导入的密钥文件格式
-y 读入密钥并显示公钥

生成密钥

ssh-keygen -t rsa -b 2048 -C "kig5.com"

复制密钥

  1. cat ~/.ssh/id_rsa.pub | ssh user@machine “mkdir ~/.ssh; cat >> ~/.ssh/authorized_keys”
  2. ssh-copy-id root@deedbeef.com

    .ssh目录权限

    0) /root目录用户必须 root:root
    1) .ssh目录的权限必须是700
    2) .ssh/authorized_keys文件权限必须大于400
    chown $(id -u):$(id -g) $HOME
    chmod 700 ~/.ssh
    chmod 600 ~/.ssh/id_rsa
    chmod 644 ~/.ssh/id_rsa.pub
    chmod 400 ~/.ssh/authorized_keys
    3) 编辑sshd_config配置文件
    vim /etc/ssh/sshd_config
    PubkeyAuthentication yes
    AuthorizedKeysFile %h/.ssh/authorized_keys
    useDNS no

找到#PubkeyAuthentication yes并把注释去掉
找到#AuthorizedKeysFile ~/.ssh/authorized_keys,去掉注释
找到useDNS yes --> 改为useDNS no #关闭DNS查找加速客户端连接 不改的话ssh客户端连接的时候会很慢。
这两行的意思是打开Pubkey认证方式,把pubkey放在~/.ssh/authorized_keys ,在我的机器上对应的是/root/.ssh/authorized_keys

远程执行命令

ssh 连接服务器时使自动启动 tmux

通过SSH连接屏幕
ssh -t root@keyicloud.com tmux a
ssh user@host -t 'tmux list-session; if [[ $? -eq 0 ]]; then tmux attach; else tmux;fi'

通过中间主机建立SSH连接

ssh -t reachable_host ssh unreachable_host
-t 提供一个远程服务器的虚拟tty终端

通过SSH运行复杂的远程shell命令

ssh host -l user 'cat cmd.txt'

ssh 安全与后门

这里的场景和前段时间的 redis + ssh 漏洞很相似,那么这里需要看一下服务器是否有ssh服务,如果有的话,那么直接把/root/.ssh目录挂载到container内,比如/tmp /.ssh,然后修改/tmp/.ssh/authorized_keys 文件,把自己的public key写进去,修改权限为600,然后就可以以root用户登录了。

注:有些服务器会配置不允许root用户直接登录,可以通过挂载 /etc/ssh/sshd_config 查看配置。这个时候,你换一个用户目录写入就行,并且挂载修改 /etc/sudoers 文件,直接配置成免密码,sudo切换root即可。

如果没有运行ssh服务,那么也可以利用挂载写crontab定时任务,比如ubuntu root用户的crontab文件在 /var/spool/cron/crontabs/root,反弹一个shell~

配置允许指定user访问

AllowUsers/AllowGroups与DenyUsers/DenyGroups
AllowUsers允许指定的用户可以通过ssh服务登录该服务器,如果使用了AllowUsers,则只有AllowUsers的用户可以登录服务器,其它用户不能登录服务器.
注意:
1)如果我们使用PermitRootLogin no(阻止Root用户登录),那样AllowUsers root是不会生效的.
2)如果我们使用DenyUsers阻止某用户登录,而AllowUsers开放某用户登录,则以DenyUsers为准.
3)如果我们使用DenyGroups阻止某用户组登录,而AllowUsers开放某用户登录,也以DenyGroups为准.
4)如果增加多个用户,各用户名之间用空格分隔,例如:AllowUsers root test test1
最后我们对AllowUsers进行测试:
1) ssh服务端:
vi /etc/ssh/sshd_config
AllowUsers test
存盘退出,并重启sshd服务
/etc/init.d/sshd restart

2) ssh客户端:
此时我们使用用户test1登录系统,发现无法登录,如下:
ssh test1@192.168.27.142
test1@192.168.27.142's password:
Permission denied, please try again.

转为使用test用户,登录成功,说明AllowUsers是起作用的,如下:
ssh test@192.168.27.142
test@192.168.27.142's password:
Last login: Sat Jun 4 18:50:52 2011 from 192.168.27.143

AllowGroups/DenyUsers/DenyGroups的用法都比较简单,我们在此不一一举例.

ssh中如何配置只允许某个IP以某个账号登录服务器?

使用TCP wrappers仅允许指定的主机连接

如果你想在你的网络上只允许特定的主机才能连接到你的SSH服务,但又不想使用或弄乱你的iptables配置,那这个方法非常有用,你可以使用TCP wrappers。在这个例子中对sshd进行TCP包裹,我将创建一条规则允许本地子网192.168.1.0/24和远程193.180.177.13的自己连接到我的SSH服务。
默认情况下,TCP wrappers首先在/etc/hosts.deny中查找看主机是否允许访问该服务,接下来,TCP wrappers查找/etc/hosts.allow看是否有规则允许该主机服务指定的服务,我将在/etc/hosts.deny中创建一个规则,如下:
sshd: ALL

这意味着默认情况下所有主机被拒绝访问SSH服务,这是应该的,否则所有主机都能访问SSH服务,因为TCP wrappers首先在hosts.deny中查找,如果这里没有关于阻止SSH服务的规则,任何主机都可以连接。

接下来,在/etc/hosts.allow中创建一个规则允许指定的主机使用SSH服务:
sshd: 192.168.1 193.180.177.13

现在,只有来自192.168.1.0/24和193.180.177.13的主机能够访问SSH服务了,其他主机在连接时还没有到登陆提示符时就被断开了,并收到错误提示,如下:
ssh_exchange_identification: Connection closed by remote host

7、使用iptables允许特定的主机连接
iptables -A INPUT -p tcp -m state --state NEW --source 193.180.177.13 --dport 22 -j ACCEPT
并确保没有其他的主机可以访问SSH服务:
iptables -A INPUT -p tcp --dport 22 -j DROP

8、SSH时间锁定技巧
你可以使用不同的iptables参数来限制到SSH服务的连接,让其在一个特定的时间范围内可以连接,其他时间不能连接。你可以在下面的任何例子中使用/second、/minute、/hour或/day开关。
第一个例子,如果一个用户输入了错误的密码,锁定一分钟内不允许在访问SSH服务,这样每个用户在一分钟内只能尝试一次登陆:
~# iptables -A INPUT -p tcp -m state --syn --state NEW --dport 22 -m limit --limit 1/minute --limit-burst 1 -j ACCEPT
~# iptables -A INPUT -p tcp -m state --syn --state NEW --dport 22 -j DROP
第二个例子,设置iptables只允许主机193.180.177.13连接到SSH服务,在尝试三次失败登陆后,iptables允许该主机每分钟尝试一次登陆:
~# iptables -A INPUT -p tcp -s 193.180.177.13 -m state --syn --state NEW --dport 22 -m limit --limit 1/minute --limit-burst 1 -j ACCEPT
~# iptables -A INPUT -p tcp -s 193.180.177.13 -m state --syn --state NEW --dport 22 -j DROP


##剪切板
sudo apt-get install xclip
xclip -sele clip ~/.ssh/id_rsa.pub

ssh root@1.0.9.8

#add a user for login as root, add -m to create home/tsnd folder
useradd sona -m
passwd sona

cat /etc/sudoers
visudo

add line

Ctrl + X , then y, remove ending .tmp in file name to quit nano and save settings

sona ALL=(ALL:ALL) ALL

Configure SSH server settings

vi /etc/ssh/sshd_config

Port 420
#2048 or 1024 both working
ServerKeyBits 2048
PermitRooLogin no
PasswordAuthentication no
RSAAuthentication yes
PubkeyAuthentication yes

add new lines

MaxStartups 5
AllowUsers aywusq

Set SSH key for new SSH user

su sona #swicth to sona and set the public key
mkdir ~/.ssh
touch ~/.ssh/authorized_keys
vi ~/.ssh/authorized_keys

paste the public key

chmod 400 ~/.ssh/authorized_keys
chmod 700 ~/.ssh

#one said: id_rsa is 600,id_rsa.pub is 644

service ssh restart
#/etc//init.d/ssh restart

Test the new SSH server

Please note: DON't close the terminal, create a new terminal window to test the new ssh_config, we need to change settings if we are unable log in using the rsa key

#log in using the rsa key on local machine
ssh -i path_to_id_rsa -p 420 sona@1.0.9.8
su
#enter root password to swich as root user
exit

16条技巧让你更高效使用SSH
SSH有很多非常酷的特性,如何它是你每天的工作伴侣,那么我想你有必要了解以下16条高效使用SSH的秘籍,它们帮你节省的时间肯定会远远大于你用来配置它们的时间。

  1. 多条连接共享

  如果你需要在多个窗口中打开到同一个服务器的连接,而不想每次都输入用户名,密码,或是等待连接建立,那么你可以配置SSH的连接共享选项,在本地打开你的SSH配置文件,通常它们位于~/.ssh/config,然后添加下面2行:

ControlMaster auto
ControlPath /tmp/sshmux%h%p%r

  现在试试断开你与服务器的连接,并建立一条新连接,然后打开一个新窗口,再创建一条连接,你会发现,第二条连接几乎是在瞬间就建立好了。

  Windows用户

  如果你是Windows用户,很不幸,最流行的开源SSH客户端Putty并不支持这个特性,但是Windows上也有OpenSSH的实现,比如这个Copssh,如果你觉得下面的一些技巧对你很有帮助,或许你应该试试Copssh。

  文件传输

  连接共享不止可以帮助你共享多个SSH连接,如果你需要通过SFTP与服务器传输文件,你会发现,它们使用的依然是同一条连接,如果你使用的Bash,你会发现,你甚至SSH甚至支持Tab对服务器端文件进行自动补全,共享连接选项对于那些需要借助SSH的工具,比如rsync,git等等也同样有效。

  2. 长连接

  如果你发现自己每天需要连接同一个服务器无数次,那么长连接选项就是为你准备的:

ControlPersist 4h

  现在你每次通过SSH与服务器建立连接之后,这条连接将被保持4个小时,即使在你退出服务器之后,这条连接依然可以重用,因此,在你下一次(4小时之内)登录服务器时,你会发现连接以闪电般的速度建立完成,这个选项对于通过scp拷贝多个文件提速尤其明显,因为你不在需要为每个文件做单独的认证了。

  3. 别再输入密码

  如果你还在通过密码方式登录SSH,那么你或许应该试试SSH Keys,首先使用OpenSSH为自己声称一对密钥:

$ ssh-keygen

  跟随指示,完成之后,你应该可以在你的.ssh目录下看到两个文件,id_rsa就是你的私钥,而id_ras.pub则是你的公钥,现在你需要将你的公钥拷贝到服务器上,如果你的系统有ssh-copy-id命令,拷贝会很简单:  

$ ssh-copy-id smylers@compo.example.org

  否则,你需要手动将你的私钥拷贝的服务器上的~/.ssh/authorized_keys文件中:

$ < ~/.ssh/id_rsa.pub ssh clegg.example.org ‘mkdir -p .ssh; cat >> .ssh/authorized_keys; chmod go-w .ssh .ssh/authorized_keys’

  现在试试重新连接到SSH服务器,或是拷贝文件,是不是已经不需要再输入密码了?

  相关链接:不输入密码ssh直接登录阿里云Linux主机

  为Putty配置SSH Key

  Putty也可以使用SSH Key,从Putty网站下载PuttyGen和Pageant,然后使用PuttyGen生成你的密钥,将公钥拷贝到服务器的’.ssh/authorized_keys’目录,然后运行Pageant,导入你的私钥,让它在后台运行,险隘你就可以使用Putty通过公钥直接登录服务器了,你可以在Putty手册的第8,9章了解关于这一特性的详细介绍。

  4. 连接中转

  有时候你可能需要从一个服务器连接另外一个服务器,比如在两个服务器之间直接传输数据,而不用通过本地电脑中转:

www1 $ scp -pr templates www2:$PWD

  (顺便说一下,当你需要在两台服务器间拷贝文件时,$PWD变量时非常有用的),因为即使你已经在两台服务器上添加了你本地电脑的公钥,scp默认仍然会提示你输入密码:这是因为你用来作为跳板的那台服务器上并没有你的私钥,所以,第二胎服务器会拒绝你的公钥,但是一定不要通过将你的私钥拷贝到中转服务器上来解决这个问题,你可以使用agent forwarding来解决这个问题,只要在你的.ssh/config文件中加入下面这行代码就可以了:

ForwardAgent yes

  或者是在Putty中勾上Allow agent forwarding选项,现在你的本地SSH就变成了第一台服务器的SSH代理,从第一台服务器在连接其它服务器就变和和在你本地一样简单,注意,如果要开启这个选项,前提是这个中间服务器值得你信任。

  5. 省略主机名

  输入服务器的完整主机名来建立一个新的SSH连接实在是太乏味无聊了,尤其是当你有一组拥有相同域名但是子域名不同的服务器需要管理时,比如下面这样:

  * www1.example.com
  * www2.example.com
  * mail.example.com
  * intranet.internal.example.com
  * backup.internal.example.com
  * dev.internal.example.com

  或许你的网络已经配置了可以直接使用短域名,比如intranet,但是如果你的网络不支持,实际上你可以自己搞定这个问题,而不用求助网络管理员。

  解决办法根据你用的操作系统而略有差异,下面是我的Ubuntu系统的配置:

prepend domain-search internal.example.com, example.com;

  然后你需要重启网络:

$ sudo restart network-manager

  不同的系统,这两条命令可能会略有差异。

  5. 主机别名

  你也可以在你的SSH配置中直接定义主机别名,就像下面这样:

Host dev
HostName dev.internal.example.com

  你还可以使用通配符来进行分组:

Host dev intranet backup
HostName %h.internal.example.com

Host www* mail
HostName %h.example.com

  在Putty中你可以为每个主机名保存单独的session,然后双击建立连接(但是它可能没办法支持通配符)。

  7. 省去用户名

  如果你在远程服务器上的用户名和你本地的用户名不同,你同样可以在SSH配置中进行设置:

Host www* mail
HostName %h.example.com
User simon

  现在就算我的本地用户名是 smylers,我仍然可以这样连接我的服务器:  

$ ssh www2

  SSH会使用simon账户连接你的服务器,同样,Putty可以保存这个信息在你的session中。

  8. 在服务器间跳转

  有些时候,你可能没法直接连接到某台服务器,而需要使用一台中间服务器进行中转,这个过程也可以自动化。首先确保你已经为服务器配置了公钥访问,并开启了agent forwarding,现在你就可以通过2条命令来连接目标服务器,不会有任何提示输入:

$ ssh gateway
gateway $ ssh db

  然后在你的本地SSH配置中,添加下面这条配置:

Host db
HostName db.internal.example.com
ProxyCommand ssh gateway netcat -q 600 %h %p

  现在你就可以通过一条命令来直接连接目标服务器了:

$ ssh db

  这里你可能会需要等待长一点的时间,因为SSH需要进行两次认证,注意netcat也有可能被写成nc或者ncat或者前面还需要加上g,你需要检查你的中间服务器来确定实际的参数。

  9. 突破网络端口限制

  有些时候,你使用的网络可能只开放了80端口,或者它们封锁了SSH端口(默认的22端口),这种情况下,你可以通过配置SSH服务器在80或者443端口进行监听来突破封锁,只需要编辑你的服务器的/etc/ssh/sshd_config文件:

  Port 443

  然后重启SSH服务器:

$ sudo reload ssh

  当然这样做的前提是你的服务器没有使用HTTPS服务,但是实际上你只需要设置一台服务器使用HTTPS端口就够了,你可以访问这台服务器,使用我们前面提到的技术利用它作为跳板来访问其它服务器。但是记住,你需要提前配置好这台服务器,这样万一当你身处一个只能访问Web的网络环境时,就可以省掉打电话让其他人帮你配置中间服务器的麻烦了。

  10. 穿越Web代理

  有些时候,你所在的网络不止封锁SSH端口,它们有可能更进一步,只让你通过Web代理来访问网络,幸运的是我们有一个叫做Corkscrew的程序可以通过Web代理在发送SSH数据。Corkscrew的使用非常简单,一般我都是在需要时搜索,然后直接下载,跟随网站上的指示,然后就搞定了,一般你需要这样一条配置:  

ProxyCommand corkscrew proxy.example.org 8080 %h %p

  11. 远程GUI

  有时候通过本地的GUI程序来访问远程服务器的文件会非常有用,比如,编辑一副图片,或者查看一个PDF文件,或者只是简单的通过一个非命令行的编辑器来修改代码,我发现GVim要比终端里的Vim更有用,因为我可以通过gvimopens打开一个新窗口来编辑文件,而用当前的SSH窗口继续执行其它操作,不要这样做,你需要先在你的SSH配置中开启一个叫做X forwarding的选项:

ForwardX11 yes

  这个选项需要服务器配置才能起作用,服务器也需要开启X forwarding,你可以在服务器的/etc/ssh/sshd_config中添加下面这个命令:  

X11Forwarding yes

  同时你还需要确保安装了xauth,编辑器,图片查看器以及其它的你需要运行的图形化程序,这种方式只有在支持本地X服务器的操作提供才可以工作,Mac和Windows上都有免费的X Server,你可能需要花些时间配置它们,相比之下,切换到Linux相对会更容易一下。

  12. 本地操作远程文件

  另一种让远程GUI程序显示在本地的替代方案就是让本地的GUI程序可以直接操作远程文件,你可以通过SSHFS来实现,只需要创建一个空目录,然后使用SSHFS将一个远程目录mount到这个目录就可以了:

$ mkdir gallery_src
$ sshfs dev:projects/gallery/src gallery_src
$ cd gallery_src
$ ls

  现在你就可以使用任何你喜欢的本地程序来便捷这个目录中的文件了,它们看起来是在你的本地,但其实时远程服务器上的文件,你可以使用fusermount命令来unmount这些文件,不要担心记不住,它们就在sshfs手册的顶上:  

$ cd ..
$ fusermount -u gallery_src

  SSHFS可以在Linux和OSX上工作,Windows用户我目前还没找到什么好办法。

  13. 通过Vim访问远程文件

  Vim有一个内置的功能可以直接编辑远程文件,需要借助SCP URL:

$ gvim scp://dev/projects/gallery/src/templates/search.html.tt

  这中方式明显不如SSHFS灵活,但是如果你只需要对远程服务器的1,2个文件进行编辑时,这条命令就要更灵活一些了,并且可以在Windows上你也可以这样做:  

:help netrw-problems

  14. 使用本地App连接远程服务器

  有时可能有些服务,比如数据库或是Web服务器,它们运行在远程服务器上,但是如果有用方式可以直接从本地程序连接它们,那会非常有用,要做到这一点,你需要用到端口转发(port forwarding),举个例子,如果你的服务器运行Postgres(并且只允许本地访问),那么你就可以在你的SSH配置中加入:

Host db
LocalForward 5433 localhost:5432

  现在当你连接你的SSH服务器时,它会在你本地电脑打开一个5433端口(我随便挑的),并将所有发送到这个端口的数据转发到服务器的5432端口(Postgres的默认端口),然后,只要你和服务器建立了连接,你就可以通过5433端口来访问服务器的Postgres了。

$ ssh db

  现在打开另外一个窗口,你就可以通过下面这条命令在本地连接你的Postgres数据库了:

$ psql -h localhost -p 5443 orders

  如果你想要使用服务器不支持的图形化Postgres客户端时,这条命令会显得尤其有用:

$ pgadmin3 &

  或者你有一个后台的Web服务器,你不希望直接通过Internet访问它,你也可以通过端口转发来访问它:

Host api
LocalForward 8080 localhost:80

  现在连接到服务器:  

$ ssh api

  然后将浏览器指向你选择的端口号:  

$ firefox http://localhost:8080/

  15. 减少延迟

  如果每次连接服务器都意味着你需要等待几十秒而无所事事,那么你或许应该试试在你的SSH配置中加入下面这条:

GSSAPIAuthentication no

  如果这条命令有效的话,你应该通知你的系统管理员让他在服务器上禁用这个选项,这样其他人就不用再分别添加这条配置到它们的本地配置了。

  16. 加速连接

  如果你确保你和某个服务器之间的连接是安全的(比如通过公司内网连接),那么你就可以通过选择arcfourencryption算法来让数据传输更快一些:

Host dev
Ciphers arcfour

  注意这个加速是以牺牲数据的加密性为代价的,所以如果你连接的是位于网上的服务器,千万不要打开这个选项,并且确保你是通过VPN建立的连接。

  开始行动吧!

  以上就是我收集的高效使用SSH的技巧合集了,如果你还有更多技巧想要分享,记得通过Smylers@cpan.org或是通过@Smylers2来和我联系。

  现在就赶紧行动试试通过这些技巧来让你的SSH更有效率吧!

  • ssh_config
    ** man
    *** PATTERNS
    模式(patterns)包含零个或多个非空字符, '*' (匹配零个或多个字符), '?' (匹配至少一个字符).
    例如, 对 ".co.uk" 这组域名下的所有主机指定一组说明, 下面的模式(pattern)能够被使用:

    Host *.co.uk
    

    下面的模式(pattern)能够匹配任何在 192.168.0.[0-9] 网段下的主机:

    Host 192.168.0.?
    

    模式列表(pattern-list)是逗号分隔的模式(pattern)的列表. 模式列表中的模式可能需要在它们前面
    使用感叹号来进行排除. 例如, 允许在一个组织内, 来自任何地方的密钥被使用, 除了来自 "dialup"
    部分, 下面的条目可以被使用(在 authorized_keys):

    from="!*.dialup.example.com,*.example.com"
    
  • sshd_config
    ** man
    *** keywords and their meanings

    • AcceptEnv :: 指定由用户发送过来的环境变量哪些可以被拷贝到任务 environ(7) 中
    • AddressFamily :: 指定 sshd(8) 可以使用哪些地址簇, 共三个参数可选 any, inet, inet6
      默认参数为 any
    • AllowAgentForwarding :: 指定 ssh-agent(1) 转发是否被认可. 默认为 yes . 需要指出的是
      仅用此选项并不能提高安全性, 除非用户被仅用 shell 访问, 否则用户总是可以安装自己的转发工具
    • AllowGroups :: 此关键词能够接受以空格分隔 group "名字" 组成的列表. 如果指定的话将只允许
      列表中的主组及所属组中的用户登录. 仅可以使用用户组的名字, 用户组 ID 是
      不允许使用的. 默认情况下所有用户组均可登录.(允许列出的用户组成员登录而拒绝
      其余的所有用户组成员)
    • AllowTcpForwarding :: 指定是否可以进行 TCP 转发. 可接收的参数是: yes/all, no, local,
      remote. 默认为 yes. 需要指出仅用此选项并不能提高安全性, 除非用户被仅用 shell 访问,
      否则用户总是可以安装自己的转发工具.
    • AllowUsers :: (允许列出的用户登录而拒绝其余的所有用户)这个关键词可以接收以用户名字的形式
      并以空格分隔的列表. 如果指定, 那么仅当与列表中指出的用户名匹配时才可登录.
      仅用户名被认可, 用户 ID 是不被认可的. 默认情况允许所有用户登录. 如果形式
      为 USER@HOST , 那么 USER 和 HOST 被分开检查, 限制来自特定主机去登录到
      特定用户. HOST 可以暂时理解为网关地址
    • Match :: 引入了一个条件块. 如果在 Match 行的所有条件都满足, 那么下一行的关键字会覆盖配置
      文件中的全局设置, 直到另外一个 Match , 或者文件结尾.

      Match 使用的参数是一个或者多个 "条件-模式(criteria-pattern)" 对. 可使用的条件
      是 User, Group, Host, LocalAddress, LocalPort, Address. 可匹配的模式包括
      单一条目或者用逗号分隔的列表或者可用的通配符或者是 "!" 进行否定.

      在 Address 条件的模式中, 可额外包含如下形式 192.0.2.0/24'' 3ffe:ffff::/32''

      只有部分关键字可以被用于 Match . 这些关键字包括:

       AcceptEnv, AllowAgentForwarding,
           AllowGroups, AllowTcpForwarding, AllowUsers, AuthenticationMethods,
           AuthorizedKeysCommand, AuthorizedKeysCommandUser,
           AuthorizedKeysFile, AuthorizedPrincipalsFile, Banner,
           ChrootDirectory, DenyGroups, DenyUsers, ForceCommand, GatewayPorts,
           GSSAPIAuthentication, HostbasedAuthentication,
           HostbasedUsesNameFromPacketOnly, KbdInteractiveAuthentication,
           KerberosAuthentication, MaxAuthTries, MaxSessions,
           PasswordAuthentication, PermitEmptyPasswords, PermitOpen,
           PermitRootLogin, PermitTunnel, PubkeyAuthentication,
           RhostsRSAAuthentication, RSAAuthentication, X11DisplayOffset,
           X11Forwarding and X11UseLocalHost.
      
    • AuthenticationMethods :: (目前不太清楚如何使用)指定一些必须成功的完成对一个用户授权访问
      的验证方法. 这个参数必须包含一个或多个由逗号分隔的验证方法的列表. 成功的验证需要完成

    • AuthorizedKeysCommand :: (我们熟悉的是使用公钥文件来验证用户, 但 man 手册中还提供了一
      种可以通过命令来验证公钥的方法)

    • AuthorizedKeysCommandUser :: (运行验证公钥命令的用户)

    • AuthorizedKeysFile :: 指定可以用来做用户验证的包含公钥的文件. 格式在 sshd(8) 中的
      AUTHORIZED_KEYS FILE FORMAT 一节描述. AuthorizedKeysFile 可以包含形如 %T 的标记,
      这些标记会在连接过程中被替换. 将这些标记扩展后, AuthorizedKeysFile 采取绝对路径或者
      相对与用户的相对路径. 多个文件可以被列出, 文件名由空格分隔
      如下一些标记被定义为:

      • %% :: 指代字面意义上的 '%'
      • %h :: 指代已经验证通过的用户的家目录
      • %u :: 指代已经验证通过的用户名
    • Banner :: 指定文件包含的内容将在验证前展示给远程用户. 如果参数为 'none' 则不会展示任何
      内容, 这个选项只在协议 2 中有效. 默认情况, 无任何显示.

    • ChallengeResponseAuthentication :: (没有看懂)

    • ChrootDirectory :: 指定验证后用来 chroot 用的目录的路径名. 组成路径名的所有部分必须是
      root 拥有的目录, 任何人或用户都不可以有写权限. 在 chroot 后, sshd 会切换工作目录到
      用户的家目录(即设定的目录)

    • Ciphers :: 指定哪些密码类型是被 协议2 允许的. 多个密码使用逗号分隔.

    • ClientAliveInterval :: 设置以秒为单位的超时间隔, 当没有接收到来自 client 端的数据后,
      sshd 将会通过加密通道请求 clinet 响应. 默认为 0 , 表示将没有任何消息发送到 client 端.
      这个选项仅应用在协议2.

    • ClientAliveCountMax :: 设置 client alive 消息的数量, 这些消息会在 sshd 无法接收到任何
      由 client 传回的消息时发送. 当 client alive 消息发送次数到达阀值上限, sshd 将会断开
      client 连接并终止会话. 这个选项与 TCPKeepAlive 是不同的
      默认值是 3. 如果 ClientAliveInterval 设置 15, 并且 ClientAliveCountMax 为预设
      值, 那么 cliant 端在 45 秒内无响应的话会断开连接. 仅用在协议 2.

    • Compression :: 指定是否压缩被允许或者延迟到用户成功验证后. 参数必须是 "yes", "delayed",

       "no". 默认是 "delayed"
      
    • DenyGroups :: 这个关键词可以接收一个列表, 由空格分隔的用户组名字的列表. 对于在主组及所属

      组匹配的用户登录是被禁止的. 只有组名字是被允许的, 组 ID 是不接受的.
      (拒绝一个所属组成员, 而允许其余的所有所属组成员登录)
      
    • DenyUsers :: (同DenyGroups)

    • ForceCommand :: 强制执行由 ForceCommand 指定的命令, 忽略任何由客户端以及 ~/.ssh/rc

        提供的命令. 命令会被以用户登录 shell 的 -c 选项引用. 适用于 shell,
        command 或者 subsystem execution. 多用于 Match 块.
      
    • GatewayPorts :: 指定远程主机是否被允许连接到由客户端转发的端口. 默认 sshd 绑定远程端口

        转发到回环地址. 这防止了其他远程主机连接到转发端口. GatewayPorts 能被
        用于指定 sshd 应该允许远程端口转发绑定到非回环地址. 来使其他主机连接.
        包含三个参数: no, yes, clientspecified.
      
    • GSSAPIAuthentication :: 指定基于 GSSAPI 的用户验证是否被允许

    • GSSAPIKeyExchange ::

    • GSSAPICleanupCredentials ::

    • GSSAPIStrictAcceptorCheck ::

    • GSSAPIStoreCredentialsOnRekey ::

    • HostbasedAuthentication ::

    • HostbasedUsesNameFromPacketOnly ::

    • HostCertificate ::

    • ListenAddress :: 指定 sshd 应该监听本地的哪个端口

    • LoginGraceTime :: 在本次用户失败登录服务器断开连接. 默认 120 秒. 如果值为 0 那么没有限制

    • LogLevel :: 日志等级

    • MACs ::

    • MaxAuthTries :: 指定每次连接最大尝试认证的次数. 一旦失败次数达到这个值的一半, 额外的失败

        会被记录. 默认值为 6.
      
    • MaxSessions :: 指定每个网络能够打开会话的最大数量, 当使用 inetd(8) 运行 sshd 时此选项

       是没有效果的
      
    • MaxStartups :: 当使用 inetd(8) 运行 sshd 时此选项是没有效果的.

    • PasswordAuthentication :: 指定密码验证是否允许. 默认是不允许的

    • PermitEmptyPasswords :: 当密码验证被允许时, 此选项指定是否服务器接受账号的空密码. 默认是
      "no"

    • PermitOpen :: 指定一个目标, 说明哪些 tcp 端口转发是被允许的. 转发规范必须是下列形式:

      PermitOpen host:port
              PermitOpen IPv4_addr:port
              PermitOpen [IPv6_addr]:port
      
      多个转发可以通过使用空格分隔来指定. "any" 允许所有转发, "none" 禁止所有转发
      
    • PermitRootLogin :: 指定 root 是否可以使用 ssh(1) 登录. 接受的参数必须是 "yes",
      "without-password", "forced-commands-only", or "no".
      如果选项被设置为 "without-password", 那么密码验证将会被禁止.
      如果选项被设置为 "forced-commands-only", root 使用公钥登录被允许, 但是只有当 command
      许昂想被指定时才生效果. 其他的任何验证方式都是不被允许的.

    • PermitTunnel :: 指定 tun(4) 设备转发是否被允许. 参数必须为 "yes", "point-to-point",

        "ethernet", "no"
      
    • PermitUserEnvironment ::

    • PidFile ::

    • PrintLastLog :: 指定 sshd(8) 是否应该打印出上次用户登录的日期时间.

    • PrintMotd :: 指定 sshd(8) 当用户登录交互界面时是否打印 /etc/motd 中的提示内容. 默认是 "yes"

    • Protocol :: 指定 sshd(8) 支持的协议版本. 可能的值为 "1" 或 "2"

    • PubkeyAuthentication :: 指定公钥验证是否被允许.

    • RevokedKeys :: 指定被撤销的公钥. 列在此文件中的密钥将会被拒绝公钥验证. 注意: 如果此文件不可

       读, 那么公钥验证将拒绝所有的用户. 密钥可以被指定为一个文本文件, 每行列出一个
       公钥, 或者or as an OpenSSH Key Revocation List (KRL) as generated by
               ssh-keygen(1).
      
    • RhostsRSAAuthentication ::

    • RSAAuthentication ::

    • SACLSupport ::

    • ServerKeyBits :: 指定在接受登录前 sshd 需要检查用户的文件及家目录的文件的属性和属主.

    • Subsystem :: (可以配置一个相对安全的 sftp 服务)

    • SyslogFacility :: 当 ssd 记录日志时提供的设备代码. 可能的值为:

      (这些值的含义可参考 rsyslog 手册, 会根据 rsyslog 的配置将日志记录到
                  对应的位置)
      DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, LOCAL3,
                  LOCAL4, LOCAL5, LOCAL6, LOCAL7.
      
    • TCPKeepAlive :: 指定系统是否应该发送 TCP keepalive 消息到另一端. 如果被发送, 连接中断

        或者机器中的一个断掉将会适当的注意到. 然而这以为如果路由临时掉线连接就会
        中断, 那么有些人就会觉得这让人很讨厌. 另一方面, TCP keepalive 没有发送,
        ssh 会话可能会无限期的存在与 server 中, 如同幽灵用户, 浪费服务器资源.
      
    • TrustedUserCAKeys ::

    • UseDNS ::

    • UsePrivilegeSeparation :: 指定 sshd 是否分离权限, 通过创建非特权子进程的方式处理网络传输
      在成功验证后, 额外的进行将会被创建, 此进程拥有被验证过的用户的权限. 这样做的目的是为了防止
      通过有缺陷的子进程提升权限,从而使系统更加安全。

  • ssh command
    ** -D [bind_address:]port
    Specifies a local ``dynamic'' application-level port forwarding.
    This works by allocating a socket to listen to port on the local
    side, optionally bound to the specified bind_address. Whenever a
    connection is made to this port, the connection is forwarded over
    the secure channel, and the application protocol is then used to
    determine where to connect to from the remote machine. Currently
    the SOCKS4 and SOCKS5 protocols are supported, and ssh will act
    as a SOCKS server. Only root can forward privileged ports.
    Dynamic port forwardings can also be specified in the
    configuration file.

IPv6 addresses can be specified by enclosing the address in
square brackets. Only the superuser can forward privileged
ports. By default, the local port is bound in accordance with
the GatewayPorts setting. However, an explicit bind_address may
be used to bind the connection to a specific address. The
bind_address of `localhost'' indicates that the listening port be bound for local use only, while an empty address or*'
indicates that the port should be available from all interfaces.

** -L [bind_address:]port:host:hostport
Specifies that the given port on the local (client) host is to be
forwarded to the given host and port on the remote side. This
works by allocating a socket to listen to port on the local side,
optionally bound to the specified bind_address. Whenever a
connection is made to this port, the connection is forwarded over
the secure channel, and a connection is made to host port
hostport from the remote machine. Port forwardings can also be
specified in the configuration file. IPv6 addresses can be
specified by enclosing the address in square brackets. Only the
superuser can forward privileged ports. By default, the local
port is bound in accordance with the GatewayPorts setting.
However, an explicit bind_address may be used to bind the
connection to a specific address. The bind_address of
`localhost'' indicates that the listening port be bound for local use only, while an empty address or*' indicates that the
port should be available from all interfaces.

** -R [bind_address:]port:host:hostport
Specifies that the given port on the remote (server) host is to
be forwarded to the given host and port on the local side. This
works by allocating a socket to listen to port on the remote
side, and whenever a connection is made to this port, the
connection is forwarded over the secure channel, and a connection
is made to host port hostport from the local machine.

Port forwardings can also be specified in the configuration file.
Privileged ports can be forwarded only when logging in as root on
the remote machine. IPv6 addresses can be specified by enclosing
the address in square brackets.

By default, the listening socket on the server will be bound to
the loopback interface only. This may be overridden by
specifying a bind_address. An empty bind_address, or the address
`*', indicates that the remote socket should listen on all
interfaces. Specifying a remote bind_address will only succeed
if the server's GatewayPorts option is enabled (see
sshd_config(5)).

If the port argument is `0', the listen port will be dynamically
allocated on the server and reported to the client at run time.
When used together with -O forward the allocated port will be
printed to the standard output.

  • experience
    ** 私钥转发
    1. $ ssh-agent
    2. $ ssh-add
    3. $ ssh -A user@host
      ** 远程端口转发
      *** 环境
      共涉及三台机器([[../proj_img/ssh_remote_port_forward.png][网络环境图片链接]]):
    4. puppet server :: (ubuntu, 192.168.24.16) 提供 puppet, openvpn 服务等
    5. Windows juniper vpn client :: (windows, 192.168.24.128) 提供访问
      application 所在服务器的网络环境
    6. application server :: (ubuntu, juniper vpn 10.192.168.16) 对外提供指定服务.
      服务器处在某局域网内, 无法访问任何外部网络, 且对外只提供 80 端口访问. 当
      管理员处在局域网以外时只能通过登录 windows 系统的 juniper vpn 网络访问服
      务器的 22 等端口.

*** 部署 application 过程

为了简便,快速,准确地部署 application, 必须使用 puppet 服务及外部软件源. 由于
在Windows 上存在模拟 Linux 系统的 cygwin 工具, 故可以利用此工具安装 sshd 服务.
当 Windows juniper vpn client 连接到 application 内部网络后, 利用 ssh 端口转
发即可将 Windows 系统当作跳板, 实现我们想要地一切. Windows 上执行的命令如下:

#+BEGIN_SRC SHELL
将 Windows 系统的 22 端口转发到 application 服务器的 2222 端口
ssh -R 2222:localhost:22 user@application.com
#+END_SRC

当我们将 Windows 的 22 端口转发到 application 内部时, 其实并未脱离 Windows的
束缚, 且意义不大. 我们希望将某台 Linux 的某个端口转发至 application 内部.
application 可以通过这个 Linux 端口通向外面的世界, 并且外面的世界也可以通过此
端口顺利进入 application 内部. 为了达到这样的效果可以考虑将 TCP 协议的
Openvpn 服务端口转发至 application 内部. 利用这个端口可将外部机器与
application 间组成局域网, 且 appliction 也可通过 Openvpn 访问外部网络.
Windows 上执行的命令为如下:

#+BEGIN_SRC SHELL
# Windows: 192.168.24.128
# openvpn server: 192.168.24.16
# 将 openvpn server 的 1194 端口转发至 application 的 1194 端口
ssh -R 1194:192.168.24.16:1194 user@application.com
#+END_SRC

再在 application 中添加相应的路由即可. 或者在 openvpn client 配置中设置
openvpn 地址为默认网关即可.
`