堡垒机作用明显,其提供运维统一入口和安全审计功能,切断直接访问和事后审计定责,解决“运维混乱”变得“运维有序” 。
下面是三种方法总结。分别从服务端,系统端、防火墙端来完成只允许堡垒机SSH登录的功能。
1、/etc/ssh/sshd_config
修改添加AllowUsers到ssh配置文件/etc/ssh/sshd_config :
AllowUsers [email protected].*
然后重启 sshd服务:systemctl restart sshd
2、hosts.allow与hosts.deny
修改/etc/hosts.deny中添加设置 sshd : ALL ,拒绝所有的访问;
修改/etc/hosts.allow,添加设置sshd : 20.132.4.* ,单独开启某个IP地址 。
这两个文件优先级为先检查hosts.deny,再检查hosts.allow。
更加详细信息参考笔者的文章-Linux中hosts.allow与hosts.deny 。
3、iptables防火墙
tcp协议中,禁止所有的ip访问本机的22端口。
iptables -I INPUT -p tcp--dport 22 -j DROP
只允许20.132.4.* 访问本机的22端口
iptables -I INPUT -s 20.132.4.* -ptcp --dport 22 -j ACCEPT
另外/etc/pam.d/sshd也可以提供访问控制功能,调用的pam_access.so模块是根据主机名、IP地址和用户实现全面的访问控制,pam_access.so模块的具体工作行为根据配置文件/etc/security/access.conf来决定。但是囿于资料过少,待以后遇到再解决把。
一般命令所谓一般命令,就是在一定时间内会执行完的命令。比如 grep, cat 等等。 执行命令的步骤是:连接,执行,获取结果
连接
连接包含了认证,可以使用 password 或者 sshkey 2种方式来认证。下面的示例为了简单,使用了密码认证的方式来完成连接。
import (
"fmt"
"time"
"golang.org/x/crypto/ssh"
)
func connect(user, password, host string, port int) (*ssh.Session, error) {
var (
auth []ssh.AuthMethod
addr string
clientConfig *ssh.ClientConfig
client *ssh.Client
session *ssh.Session
err error
)
// get auth method
auth = make([]ssh.AuthMethod, 0)
auth = append(auth, ssh.Password(password))
clientConfig = &ssh.ClientConfig{
User: user,
Auth: auth,
Timeout: 30 * time.Second,
}
// connet to ssh
addr = fmt.Sprintf("%s:%d", host, port)
if client, err = ssh.Dial("tcp", addr, clientConfig)err != nil {
return nil, err
}
// create session
if session, err = client.NewSession()err != nil {
return nil, err
}
return session, nil
}
连接的方法很简单,只要提供登录主机的 用户*, *密码*, *主机名或者IP*, *SSH端口
执行,命令获取结果
连接成功后,执行命令很简单
import (
"fmt"
"log"
"os"
"time"
"golang.org/x/crypto/ssh"
)
func main() {
session, err := connect("root", "xxxxx", "127.0.0.1", 22)
if err != nil {
log.Fatal(err)
}
defer session.Close()
session.Run("ls /ls /abc")
}
上面代码运行之后,虽然命令正常执行了,但是没有正常输出的结果,也没有异常输出的结果。 要想显示结果,需要将 session 的 Stdout 和 Stderr 重定向 修改 func main 为如下:
func main() {
session, err := connect("root", "xxxxx", "127.0.0.1", 22)
if err != nil {
log.Fatal(err)
}
defer session.Close()
session.Stdout = os.Stdout
session.Stderr = os.Stderr
session.Run("ls /ls /abc")
}
这样就能在屏幕上显示正常,异常的信息了。
交互式命令
上面的方式无法远程执行交互式命令,比如 top , 远程编辑一个文件,比如 vi /etc/nginx/nginx.conf 如果要支持交互式的命令,需要当前的terminal来接管远程的 PTY。
func main() {
session, err := connect("root", "olordjesus", "dockers.iotalabs.io", 2210)
if err != nil {
log.Fatal(err)
}
defer session.Close()
fd := int(os.Stdin.Fd())
oldState, err := terminal.MakeRaw(fd)
if err != nil {
panic(err)
}
defer terminal.Restore(fd, oldState)
// excute command
session.Stdout = os.Stdout
session.Stderr = os.Stderr
session.Stdin = os.Stdin
termWidth, termHeight, err := terminal.GetSize(fd)
if err != nil {
panic(err)
}
// Set up terminal modes
modes := ssh.TerminalModes{
ssh.ECHO: 1, // enable echoing
ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
}
// Request pseudo terminal
if err := session.RequestPty("xterm-256color", termHeight, termWidth, modes)err != nil {
log.Fatal(err)
}
session.Run("top")
}
更多内容请点击 我的博客 查看,欢迎来访。
本教程基于《Django使用Channels实现WebSocket消息通知功能》
xterm.js : 前端模拟 shell 终端的一个库,当用户每输入一个键,就向后端发送该数据
paramiko : Python 下对 ssh2 封装的一个库,可以使用他来远程连接主机
创建app。名为 webssh
将应用添加到 settings.py
修改应用下的 apps.py
修改应用下的 __init__.py
访问 http://127.0.0.1/webssh/ 可以连接到主页
apps/webssh/consumers.py
apps/webssh/routing.py
合并多个应用的url
遇到的问题:用协作连接时,用户加入到一个通道组,往这个通道组发送命令,这个通道所有用户都能收到,来实现协作的功能,但是从 self.ssh_channel 接收返回的数据,可能会存在和预想的不同,特别是 top 命令。示例如下,不知道怎么解决了!!!
参考链接: https://github.com/huyuan1999/django-webssh
https://www.cnblogs.com/52op/articles/9327733.html 【gevent库】