http://blog.robertelder.org/what-is-ssh/
什么是ssh?
ssh是一个在计算机之间实现安全通信的网络协议。通常人们在谈论使用ssh时,更多的是在说为了在另外一台计算机上执行一些命令而使用ssh client来连接到那台ssh服务器上。现代的计算机通常都既可以运行ssh client也可以跑ssh server.比如,执行以下命令:
ssh robert@192.168.0.123 "ls"
这条命令将试图使用'robert'这个username去远程登录192.168.0.123这个机器。一旦登录成功,它便试图去运行命令"ls",并且随后直接退出ssh session.为了这个过程能够工作,你可能需要键入远程机器的robert密码,或者使用其他的鉴权机制。
如果你在后面不加"ls"的命令,你将获得一个交互的session,你可以在远程机器上执行无限多的命令,直到你敲"exit"退出session.
在计算机之间拷贝文件
你可以通过‘scp’命令使用ssh协议在计算机之间拷贝文件:
scp /tmp/my_file robert@192.168.0.123:/mnt/my_file # From your computer to 192.168.0.123 scp robert@192.168.0.123:/mnt/my_file /tmp/my_file # From 192.168.0.123 to your computer
为其他服务提供隧道服务tunneling
这实际上是ssh真正强大的地方。你可以使用ssh经由ssh链接来为上层应用提供安全的数据传输隧道。比如,你可以在你自己家里搭建一个git repo作为远程备份用机。你可以使用下面的命令来clone repo:
git clone my-server:~/my_git_repo.git
最后,你甚至可以tunnel traffic on a port by port basis. 这允许你将远程服务当成本地服务一样来使用。比如,你可以允许将本地的一个web server或者database server用于接收来自任何地方的connections,只要使用一个有公网ip的proxy server即可。
为什么我们要用ssh?
在ssh之前,有一些更老也不安全的替代方案,比如telnent, ftp.这些老的协议之所以不安全,是因为login的信息明码传输,而ssh由于密码仅在安全通道建立后才会传输出去。ssh也支持公钥密码加密的方式,这比传统的基于password的认证更加安全。
ssh public and private keys
ssh可以以passowrd认证方式工作,但更加现代的方式是使用public key cryptography认证(鉴真)方式,而不是password方式。这对于初学者往往容易犯晕。实际上没有那么复杂,只要你多做几次,你就会觉得一切很自然。
大多数人已经习惯于传统密码认证方式:人们输入用户名和密码并且发往服务器鉴权。server然后检查password是否匹配以便决定是否允许你访问。而public key cryptography认证机制则有些不同,它需要人们创建一个'公钥私钥密码键值对public/private key pair':
1. 公钥,你可以分发给任何人
2.私钥,你应该对该文件保密,由创建该文件的人保管
这里不想对公钥加密机制做过多探讨(实际上需要非常多的数学知识),但是你需要了解以下概念和知识点:
1. 公钥和私钥之间有着非常复杂的关联关系;
2. 公钥用于加密消息,但却不能用公钥来解密消息;
3. 私钥可以解密由公钥加密的消息
ssh认证过程
具体流程为:
- Negotiating the version of the protocol to use
- Negotiating cryptographic algorithms and other options to use。
- Negotiating a one-time session key for encrypting the rest of the session
- Authenticating the server host using its host key
- Authenticating the user using a password, public key authentication, or other means.
注意server向user proves its identity或者反过来user向server证明它的身份都是通过拥有对应的private key来实现的。比如user指定了-i xxxprivatekey启动ssh session,而server上authorized_keys文件中拥有user的public key,这两者能够对应起来的话,server就确信user是那个user。反之亦然.
多个key pair的管理
默认情况下sshkey_gen工具产生的私钥为id_rsa,对应公钥为id_rsa.pub,而你可能会有多个key pair的需求,比如一个用于github,一个用于bitbucket,一个用于你自己的server登录,这时就需要分别创建多个key pair,保持文件名对应即可,比如id_rsa_github/id_rsa_bitbucket及对应的_pub
非对称加密
在1976年以前,所有的加密都采用对称加密,既A使用某种加密规则对信息加密,B收到信息后逆向加密规则解密数据。这通信方式产生了一个难以解决的问题:A如何安全的把加密规则通知B?
在1976年有三位数学家提出了一个崭新的非对加密的概念(RSA):
1.A生成一对两把密钥(公钥和私钥)。公钥是公开的,任何人都可以获得,私钥则是保密的。
2.B获取A生成的公钥,然后用它对信息加密。
3.A得到加密后的信息,用私钥解密。
虽然非对称加密很安全很强大,但是它也有缺点,相对于对称加密它计算量更大,计算时间更长。所以在大规模数据的安全通信场景中,普遍采用非对称加密技术来交换对称加密密钥,之后的通信都采用对称加密技术加密。
https://www.jianshu.com/p/5e3f9dfd2cb4
ssh keys in server and client
https://www.ssh.com/ssh/public-key-authentication
如何创建公钥私钥对?
在linux中,你可以使用ssh-keygen命令来创建密钥对
ssh-keygen
运行这条命令后,你需要回答以下问题完成密钥对的创建:
1. 输入保存密钥的文件名(默认为/home/your_home/.ssh/id_rsa):
2. 输入passphrase(可以为空,主要用于二次安全认证):
3.输入相同的passphrase确认
上述命令执行后,将生成两个文件: id_rsa为私钥文件,id_rsa.pub为公钥文件.以下为公钥的例子:
ssh-rsa AAAAB3Nz4C1yc2EAAAADAQABAAABAQDG3eIHvpiK2At0G+e3Y0vgo0o3aZHM8rJLXMMsGxC5kCorySKb2qtvsSVVm+3KverdalhhuJdLHf1PmVfd+kGgglAYyos21eKevM6Syub4k1r6qvBe/jqwigI1kwr3cL6mU4ifIpUN1eddrcnRVo3F2zdtBXML5Ty+PZ4Hd2/nQKApzohIHDph9wxUMgRA+cevPQpYslyLsP1Bef5ZOlY7GrFwqxNJV5li0tMG5GI+kQ7lUuySkv5Wjqbu/NqHb1OmH++jWJdAZ8BtNUuKjlD9r7lfvzIInX4CNs7KYY/USfL5ZuL/yOIGjjIiY0UMZJkJebiT/CLN/pXkw8ZlDikz robert@ubuntu
该公钥对应的私钥id_rsa文件内容为:
-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAxt3iB76YitgLdBvnt2NL4KNKN2mRzPKyS1zDLBsQuZAqK8ki m9qrb7ElVZvtyr3q3WpYYbiXSx39T5lX3fpBoIJQGMqLNtXinrzOksrm+JNa+qrw Xv46sIoCNZMK93C+plOInyKVDdXnXa3J0VaNxds3bQVzC+U8vj2eB3dv50CgKc6I SBw6YfcMVDIEQPnHrz0KWLJci7D9QXn+WTpWOxqxcKsTSVeZYtLTBuRiPpEO5VLs kpL+Vo6m7vzKh29Tph/vo1iXQGfAbTVLio5Q/a+5X78yCJ1+AjbOymGP70ny+Wbi /8jiCo4yImNFDGSZCXm4k/wizf6V5MPGZQ4pMwIDAQABAoIBAGavS3cUQ0/uHnvl rNBUxWlI55mVOWPKLaYcT+sGTqyCdEQHp4cycjNKFS0PRsnZJt0NfHV5CyYOZi4j z+sevaRJpWgnrZqy9kFg3ImPm5PfAqtMeLGUNFnT6TAgrRj3bnoTyAfjo3Nxb/Y/ NmaResMfXo88sRsDU0ooJuFUGsQdAUmYSDJ0wKkuCytW05hYx/sQS8fElhKg3c0f CG/MQwBxOYWOelLBwE76D1RIWDAA9l4+Ol48lE+b5Ltv2dZFVKZht/oFNa/egvMw YTikekakwfkg5MTaDyldD/idet2ZRoLllyObLsEH8c6y9oXVhkZsu6aQ02KEWx0W iD0+IQECgYEA7RzbS3YbJ/Ds+zrf/Czy1j3B+h7x+w67kojpD2kxpS9BWB6957ST ozCGx8ysdJ9kwtGTOe+MTykG0JHyQhV5qv2zIols/2EA2HIQFXUZDNqbfr9Hvekc vRctd60H/jMAMR5DLHu0OorNoY0AA/HATAYQsovkDnlL8IRr3V17uAsCgYEA1rUf pVFcjgoLPon+Zp3rV+9rwrMo8hOZ6EE+lX9uWBtPaKQNXiqkls+xIA8R8sNqdrB2 CkvyFpMz7HVV5rBsMhIqe8ti8Ot5Z2xp7cErxc7XpMuJBTTvmJX5Sti9R7I14CUf 4oFHeSdWJiOS55a85sPXZYHjHA1YuycUNWCcBHkCgYA6yBeZaosq6LBnS94xTxdY g3DuR+PJoIphtm1Is8Rp9gAWD3D22y5qm2IecCAkvUsmfPwptbgr+7jDxhqvxVEn UcOyAS2zVeH2xrg0CZaPODaqQlNPwlWsju1nqM69dvlKM/1lLrmsdbKqpSDm2WzZ q/tBuCpuaBWqV7nB5CYCpwKBgHT2BAA1uzqxJAD0gT57ZnnntgdBO9vra5sG98XO vliGwBJb0+BpUHHLQE0biIZ7h6KSbCsdxgogNFfqb1oU30vDc5suZ36gd+ksOORI p8TA8d4W9lR8ysyPXlc0jJ/i59BryNvF2x6XnCl4lY1NIyh+pPbp88MTTjPdjPeq 4jLZAoGAVuHcLyNV0ZUxTmFZovXfxisc+zdJV3eSaVD7/c7v/sA35cQyojFZEi/l JDJYIq5T4Mo5NnKb+mms3n34ehH50i8BTGeJfnlg63MIrd5fE7jZeFYX2cvx6jD5 mvPGakcGN6ftUaCbEfJfDjf85WjKwiVi52oY1wb/91OrIpoP3io= -----END RSA PRIVATE KEY-----
一旦你创建好了这两个文件,你就可以通过将公钥文件分发到server上这种方式来允许你登录到远程server上去.私钥文件永远只在你的客户端机器上,每一次你需要远程登录到保存了对应公钥的远程机器时,你都需要使用它。
如果你正在手工设置你所管理的两台机器,以便他们之间可以ssh,你需要做的是将你的public公钥存入你希望实现远程登录的remote远程机器中的'~/.ssh/authorized_keys'文件中.这个文件中可以包含多个pub keys以便运行多人通过ssh来登录该remote machine.
在Github上使用SSH
一个非常常见的SSH用例场景为:授权允许访问Github repo.本质原理上讲,在github上使用和其他任何场景下使用ssh并没有什么不同.你可以使用上面讲的方法来创建一个密钥对.这里是github关于创建密钥对的详细说明。需要说明的是github网站说明中也提到了将密钥通过ssh-agent这个小工具添加给你的authentication agent。通常情况下使用ssh-agent并不是必须的,但它确实是当你试图创建一个实际的remote connection时指定使用哪一个key的几种方法之一。
一旦你完成了密钥对创建,你可以接着浏览github网页:将你的key添加到特定的repo和帐号下 你只需将你的public key内容复制黏贴到github网站上就好了,这样任何能够证明它拥有该public key对应的私钥的人都是可信的,因此私钥保持私密非常重要。同时通过下面的ssh -i xxxxprivatekey来指定使用哪个私钥文件.在你创建好密钥对并且将公钥添加到你的github帐号上去之后,你就可以以下面的方式来登录到github上
ssh -i <path to your private key file> git@github.com : #注意,如果使用的是你的默认private key,比如id_rsa则无需使用-i来特别指定
ssh -i ~/.ssh/id_rsa git@github.com
如果前面你的设置正确,那么将会有以下打印:
PTY allocation request failed on channel 0 Hi <Your Repo Name>! You've successfully authenticated, but GitHub does not provide shell access. Connection to github.com closed.
上面的消息本身并不是错误消息,它告诉你你已经通过ssh pub key的方式正确认证了,但是github服务器本身并不提供ssh的shell access,它只允许你通过git client和github服务器进行通信.反过来,
如果你看到
Permission denied (publickey).
则表示你的配置还不正确.需要检查你的私钥目录是否正确,并检查确认你是否正确上传了公钥.并且确认你使用了'git'这个username,否则默认情况下username会使用你本机的当前用户.
为了clone或者push到你的repo中,你需要使用各种git commands,你需要理解的是所有这些git commands的运行都是在底层建立好的ssh tunnel隧道中传递信息的.
通过ssh来连接AWS EC2 instance
启动aws ec2时,当你被要求创建和下载一个key pair时你就获得了允许你访问那个ec2 instance的私钥. amazon会有一份public key的拷贝.你一旦使用那个key pair来launch一个新的ec2实例时,这个ec2实例就将在该机器实例的~/ssh/authorized_keys文件中添加public key.这也是为什么你不用手工将public key手工添加到~/.ssh/authorized_keys文件中去的原因。感兴趣的话可以点击 这里 了解
ssh config file
当你使用ssh时有一种可以大大提高你使用ssh效率的方法,这就是config文件.通过这种机制,config可以记住你的所有连接的参数,存储在~/.ssh/config文件中.如果你还没有的话,建议你创建开始使用.用它的好处是,你不用敲下面的长命令,你只用敲对应的短命令:
ssh robert@192.168.0.123 -p 1800 -i ~/.ssh/my_private_key ssh robert-server
只要你的~/.ssh/config文件中包含以下信息就可以了:
Host robert-server HostName 192.168.0.123 Port 1800 User robert IdentityFile ~/.ssh/my_private_key
有了这样的config后还有个好处是,其他使用ssh的地方依然有效,比如git clone或者scp:
git clone robert-server:/mnt/my_repo.git scp /tmp/my_file robert-server:/tmp/new_file
关于ssh config文件还有一点需要说明: 有些isp会关闭idle的连接,这意味着如果你打开了一个ssh connection,但是不做任何事情,你的isp将会将这条ssh connection timeout掉,你的终端将不再可用.
你可以通过在ssh config文件中每一个host connection参数区域添加 ServerAliveInterval参数:
ServerAliveInterval 60
通过ssh做远程管理
如前面所说,你可以使用ssh来远程登录,只要远程服务器运行着ssh server软件组,并且在远程服务器上已经正确配置了为incoming user的认证的方式
通常当你远程登录到remote server上,如果你执行一个长期运行的任务并且关闭terminal,有的ssh连接就将被关闭,这样你远程执行的命令也将在terminal关闭时被killed掉.这往往并不是你所想要的,你可能希望的是远程登录一下,执行对应命令关闭本地机器,并希望远程的命令能够一直运行.为解决这个terminal关闭后remotely执行的命令进程被killed的问题,可以有以下方案:
while true; do sleep 1; done # This will cause the terminal to hang until the command is killed by the user while true; do sleep 1; done & # This will run forever in the background
另外一个办法是使用一个terminal multiplexer , 这是一个能够增加更多功能到你的terminal session的软件.比较出名的有GUN Screen, tmux
无论你用哪一个multiplexer,都会提供允许你创建新的terminal session的命令.如果在新的session中执行了相关命令,该命令可以以background方式运行.下一次你登录到server上时,你可以再次运行multiplexer程序并且连接到前面使用过的terminal sessions.
使用ssh执行端口转发
使用SSH你可以做的真正很cool的事情是捕获你本地port的信息并且经由已经搭建起来的ssh隧道转发出去,并在tunnel的另一端forward给其他的应用.比如:
ssh -L 4005:127.0.0.1:80 robert-server
当上述命令执行时,将会创建一个在~/.ssh/config文件中定义的参数创建一条正常的ssh链接到'robert-server'远程服务器上去.而且任何本来将在本地机器的4005端口上接收到的traffic将被截流(traffic that would otherwise be receieved on the local port 4005)送进该tunnel(will instead be sent into the tunnel), 然后在另外一端将被forwarded到IP 127.0.0.1主机的80端口.如果‘robert-server'是一个web-server,那么在执行了上述命令的本地主机上浏览127.0.0.1则显示的是web-server上的page内容,这一切就像是该web-server运行在本地上一样. 这就是类似nginx的web proxy的基本原理!
你也可以对database server做相同的事情,该database可能放置在防火墙的背后因此外部无法访问,而通过这种方式,你的机器(放在互联网上的本地主机)在执行类似上述命令后就能够代理外部对数据库的访问,但同时却不允许外部的直接访问请求!
通常会碰到的共性问题
在下面的几个section,我们将来看看ssh相关的几个常见问题
SSH: Connect to Host: Connection Refused
如果你要ssh到的host本身并没有运行ssh server软件则会导致该错误.当然也有可能你指定了错误的端口号,ssh通常在port 22上.
Permission Denied(publickey)
如果你正在使用public key认证方式去登录ssh server而被服务器器拒绝的话就会有这个错误信息.通常情况下原因是你没有指定正确的private key导致的.
The authenticity of host 'hostname' can't be established
这个信息往往在你首次连接到一个ssh server时出现,因为每次你试图连接到server时,你的local client将会到本地保存的ssh信息文件(通常是在~/.ssh/known_hosts文件中)去找一点信息,该known_hosts文件会记住你信任的机器的id(identity of machines that you trust). 通常你只要敲'yes'就好了,当然如果你是个安全洁癖者,你可以打电话给那台你将登录的system administrator索取那台机器的RSA key fingerprint.然后你可以手工地将该RSA key fingerprint加到~/.ssh/know_hosts中,你就不会再见到这个warning了.
WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED
有时候,你可能看到类似下面的消息:
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone could be eavesdropping on you right now (man-in-the-middle attack)! It is also possible that a host key has just been changed. The fingerprint for the RSA key sent by the remote host is AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA:AA. Please contact your system administrator. Add correct host key in /home/robert/.ssh/known_hosts to get rid of this message. Offending RSA key in /home/robert/.ssh/known_hosts:1 RSA host key for 192.168.0.1 has changed and you have requested strict checking. Host key verification failed.
这条消息是说你试图连接的ssh server的identity发生了改变。这有可能说明:如果别人试图创建一个他们自己的ssh server并且欺骗你(很有可能通过建立他们自己的DNS server)登录到他们的ssh server上去而不是你真正希望登录的那台机器.
当然,以下几个场景也会出现这种消息: 如果你在使用AWS,EC2,如果你通过使用一个elastic IP去连接的话,ssh将会保存那个server的RSA key fingerprint并且将此fingerprint绑定到那个静态IP地址.随后,如果你又启动了一个不同的server而又绑定了那个静态IP,你将得到该错误,因为在你本地机器的~/.ssh/known_hosts文件中该IP对应着老EC2机器的RSA fingerprint key
122.32.210.134 ssh-rsa AAAAB3zew1yc2DAQABAAABAQDHOWdwLpkos2CLli6DFvQ36yQE6Pe/PtFp3XwyirfZCIoGWnedaWI8zkJWVCs0wgOB9/urFepTDfV2wN49KGy1sl2/CCDEH2K/zeoEAZlTcBrhU17bwg1yMHCyJ7IM+zdLzItDEKYjg1dXQQlwt7GP4W7HqffelQQoVxOMoZ5N50MzD+nvV4y8iq0KwDQNy62iU4hui9ajCSVUDLu/06ucd5IojSI9keRIYAXvQf52TJ5EbvoRhjuWNEG8IhnPP6rzPS11Ocmwg/xxxxxOKL28AeDBAh6B6MEBDtlyp5Yfu9cwZJ9CFtU/x5fHFPtAyyyyyhAfwN1
如果你确认这不是什么问题,你访问的机器就是你要访问的机器,则可以通过以下命令来清除对应的警告信息:
ssh-keygen -R <Whatever the IP address in the message was>
什么是known_hosts中的RSA Key fingerprint(指纹),怎么创建,有啥用?
https://superuser.com/questions/421997/what-is-a-ssh-key-fingerprint-and-how-is-it-generated
fingerprint基于主机的public key,通常就保存在~/.ssh/id_rsa.pub,通常是用做简单地identification/verification of the host your are connecting to.你要连的是你想连的.
如果fingerprint变化了,那么意味着你正在连接的那台机器已经变更了他们的public key。这有可能并不是什么大的问题,但是至少说明和上一次连接相比,现在你连接的domain/ip本身最终连接到了不同的机器上(这种情况可能会在load balancer的场景上经常发生),当然也有可能你正在被man-in-the-middle攻击,这种攻击有可能骗你把你的ssh登录请求引导到他们自己的ssh server上去,从而偷了你的username/password.
ssh-keygen -E md5 -lf ~/.ssh/id_dsa.pub 2048 MD5:4d:5b:97:19:8c:fe:06:f0:29:e7:f5:96:77:cb:3c:71 (DSA) ssh-keygen -lf ~/.ssh/id_dsa.pub 1024 SHA256:19n6fkdz0qqmowiBy6XEaA87EuG/jgWUr44ZSBhJl6Y (DSA)
WARNING: UNPROTECTED PRIVATE KEY FILE!
如果你的private key文件放开的权限太多,你将会看到这条消息
调试SSH的connections
如果你试图连接的ssh无法工作,那么有一些调试方法:
ssh -vvv <...arguments...> debug1: Reading configuration data /home/robert/.ssh/config debug1: /home/robert/.ssh/config line 12: Applying options for hostname debug1: Reading configuration data /etc/ssh/ssh_config debug1: /etc/ssh/ssh_config line 13: Applying options for host debug2: resolving "hostname.com" port 123 debug2: ssh_connect_direct: needpriv 0 debug1: Connecting to hostname.com [192.168.0.1] port 123. debug1: Connection established. ... debug1: kex: client->server cipher: chacha20-poly1305@openssh.com MAC: <implicit> compression: none debug3: send packet: type 30 ...