Quantcast
Channel: CodeSection,代码区,Python开发技术文章_教程 - CodeSec
Viewing all articles
Browse latest Browse all 9596

利用Python Fabric配置主机间SSH互信和添加公钥

$
0
0

本文主要讲述如何利用python的Fabric模块编写一个脚本用于配置多个主机间SSH互信以及如何将管理员自己的公钥批量添加到多个主机中。

脚本说明

该脚本只提供如题所述的少量功能,用于帮助熟悉Python的Fabric和SSH几项简单的基本配置,原本的目的是想通过Python和Fabric实现对主机进行一些批量操作,如完成主机的初始化等。因为SSH的配置具有通用性和必要性,所以便有了此文,希望对linux运维和使用Python、Fabric自动化部署感兴趣的人有所帮助。

该脚本将继续维护,直至使用Python和Fabric完成主机的初始化这个脚本在生产环境可用,可以关注GitHub上 LinuxBashShellScriptForOps 项目的 更新信息 。

说明: LinuxBashShellScriptForOps 是一个Linux运维脚本仓库,对在Linux运维工作所能用到的Shell脚本和Python脚本的归纳和总结。 绝大部分的源码均出自生产系统并经过比较严谨的测试,可以通过阅读该项目的 README 获得该项目的更多信息。

涉及的主要知识

其中涉及的知识:

Python编程知识

Fabric模块配置和使用相关知识

定义主机角色、用户名和密码、sudo用户名和密码、SSH配置和连接配置等

使用设置上下文 (with setting),用于一些需要Fabric特殊设置的执行过程,如在执行命令出错时是否该弹出警告并不退出当前任务

sudo执行命令

获取远程命令的返回结果(标准输出和标准错误输出,但不包括返回结果值0,1)

终端多颜色显示

特殊操作需要用户确认(confirm函数)

runs_once装饰器,注意runs_once装饰器不能与定义的角色中有多个主机的情况下联用

日志模块的配置已经隐含在脚本中,非常有用但没有使用,原因在本文第二段已经说明

使用Python判断当前系统是windows还是Linux,是Debian系还是RHEL系

使用Python判断IP是否合法

功能说明

此脚本以Ubuntu为例,主要有三个功能:

重置主机自动生成的SSH Key

将管理员自己的SSH 公钥注入到root用户的SSH认证文件,从而可以通过key和以root身份登录到主机

将其他(同一网段或者能访问到)主机的SSH 公钥添加到root用户的SSH认证文件

注:是否要重置主机自动生成的SSH Key取决于当前环境下主机是如何安装操作系统的,如果是通过模板的方式批量部署的虚拟机通常拥有相同的SSH配置,这就意味着它们的SSH配置完全相同,包括公钥、私钥、SSH配置和用户SSH配置等。如果这是公有云环境,可能导致安全问题,就像通过模板安装Windows需要sysprep安装删除唯一性信息一样,Linux中SSH也必须重置。这些key通常位于/etc/ssh目录下:

/etc/ssh/ssh_host_dsa_key
/etc/ssh/ssh_host_dsa_key.pub
/etc/ssh/ssh_host_ecdsa_key
/etc/ssh/ssh_host_ecdsa_key.pub
/etc/ssh/ssh_host_ed25519_key
/etc/ssh/ssh_host_ed25519_key.pub
/etc/ssh/ssh_host_rsa_key
/etc/ssh/ssh_host_rsa_key.pub 脚本内容

脚本内容如下,也可以从 GitHub 获取:

#!/usr/bin/python # encoding: utf-8 # -*- coding: utf8 -*- """ Created by PyCharm. File: LinuxBashShellScriptForOps:pyLinuxHostsSSHKeyInitialization.py User: Guodong Create Date: 2017/3/9 Create Time: 23:05 """ import time import os import logging import logging.handlers import sys from fabric.api import * from fabric.colors import red, green, yellow, blue, magenta, cyan, white from fabric.context_managers import * from fabric.contrib.console import confirm env.roledefs = { 'base': ['ubuntu@192.168.1.101:22', ], "bigData": ['ubuntu@192.168.100.122:22', 'ubuntu@192.168.100.123:22', 'ubuntu@192.168.100.124:22', ], "coreServices": ['ubuntu@192.168.100.127:22', 'ubuntu@192.168.100.128:22', 'ubuntu@192.168.100.129:22', 'ubuntu@192.168.100.130:22', ], "webAppFrontend": ['ubuntu@192.168.100.125:22', ], "webAppBackend": ['ubuntu@192.168.100.126:22', ], 'all': ['192.168.1.101', '192.168.100.122', '192.168.100.123', '192.168.100.124', '192.168.100.125', '192.168.100.126', '192.168.100.127', '192.168.100.128', '192.168.100.129', '192.168.100.130', ], 'db': ['ubuntu@192.168.100.127:22', ], 'nginx': ['ubuntu@192.168.100.128:22', ], } env.hosts = ['192.168.1.101', '192.168.100.122', '192.168.100.123', '192.168.100.124', '192.168.100.125', '192.168.100.126', '192.168.100.127', '192.168.100.128', '192.168.100.129', '192.168.100.130', ] env.port = '22' env.user = "ubuntu" env.password = "ubuntu" env.sudo_user = "root" # fixed setting, it must be 'root' env.sudo_password = "ubuntu" env.disable_known_hosts = True env.warn_only = False env.command_timeout = 15 env.connection_attempts = 2 def initLoggerWithRotate(logPath="/var/log", logName=None, singleLogFile=True): current_time = time.strftime("%Y%m%d%H") if logName is not None and not singleLogFile: logPath = os.path.join(logPath, logName) logFilename = logName + "_" + current_time + ".log" elif logName is not None and singleLogFile: logPath = os.path.join(logPath, logName) logFilename = logName + ".log" else: logName = "default" logFilename = logName + ".log" if not os.path.exists(logPath): os.makedirs(logPath) logFilename = os.path.join(logPath, logFilename) else: logFilename = os.path.join(logPath, logFilename) logger = logging.getLogger(logName) log_formatter = logging.Formatter("%(asctime)s %(filename)s:%(lineno)d %(name)s %(levelname)s: %(message)s", "%Y-%m-%d %H:%M:%S") file_handler = logging.handlers.RotatingFileHandler(logFilename, maxBytes=104857600, backupCount=5) file_handler.setFormatter(log_formatter) stream_handler = logging.StreamHandler(sys.stderr) logger.addHandler(file_handler) logger.addHandler(stream_handler) logger.setLevel(logging.DEBUG) return logger mswindows = (sys.platform == "win32") # learning from 'subprocess' module linux = (sys.platform == "linux2") def _win_or_linux(): # os.name ->(sames to) sys.builtin_module_names if 'posix' in sys.builtin_module_names: os_type = 'Linux' elif 'nt' in sys.builtin_module_names: os_type = 'Windows' return os_type def is_windows(): if "windows" in _win_or_linux().lower(): return True else: return False def is_linux(): if "linux" in _win_or_linux().lower(): return True else: return False def is_debian_family(): import platform # http://stackoverflow.com/questions/2988017/string-comparison-in-python-is-vs # http://stackoverflow.com/questions/1504717/why-does-comparing-strings-in-python-using-either-or-is-sometimes-produce if platform.system() == "Linux": distname = platform.linux_distribution() if "Ubuntu" in distname or "Debian" in distname: return True else: return False else: return False def is_rhel_family(): import platform if platform.system() == "Linux": distname = platform.linux_distribution() if "CentOS" in distname or "Debian" in distname: return True else: return False else: return False # log_path = "/var/log" if os.path.exists("/var/log") or os.makedirs("/var/log") else "/var/log" log_path = "/var/log" log_name = "." + os.path.splitext(os.path.basename(__file__))[0] log = initLoggerWithRotate(logPath="

Viewing all articles
Browse latest Browse all 9596

Trending Articles