首页 文章 精选 留言 我的

精选列表

搜索[手游加固],共7832篇文章
优秀的个人博客,低调大师

伸手党的容器镜像加固流程

人在江湖飘,哪能不挨刀 说了好几期 CIS 之类的运行时安全问题,K8s 在运行过程中,还有个常见的安全威胁就是镜像漏洞,不少同学都有在实施过程中因为镜像漏洞问题被吊打的经验,今天就结合个人经验,说说镜像漏洞修复的一般流程。 这里主要指的是第三方镜像的修复过程,Scratch 不在此列。 修复任务分析 通常扫描报告会明确指明确切的问题源头、相关软件包、问题版本、修复版本等,首先要根据报告判断修复的目标。修复目标并不一定是固定的,有些情况下可能仅需要修复公开的高危漏洞,有些可能要求更多。 我们都知道,容器镜像是个分层结构,底层通常由操作系统(例如 debian:buster-slim)或者特定运行时(例如 openjdk:16)构成;另外可能构建应用程序,或者直接通过 COPY/ADD 的方式加入应用程序;另外还会通过 CMD、ENV 等指令设置运行环境等。软件漏洞多发于底层和应用程序层,因此这里需要根据扫描报告,确认我们的修复目标。 软件配置问题也可能造成漏洞,可以另行讲解。 应用层漏洞 如果要修复的问题是处于应用层,就要判断该镜像是否为官方镜像,如果答案是肯定的,则可以直接更新官方镜像,通常同一个大版本下的小版本更新,都是兼容的,可以更新之后直接进行测试和复查。 如果官方没有针对性的修复,或者镜像并未更新,就可能需要自己构建镜像了。 如果是开源软件,强烈建议提交 Issue 或者 PR 基础层漏洞 如果是基础层漏洞,除了可以像应用层修复一样,检查版本更新之外,还有另一个选项,就是更换不同的基础层,例如从 Debian 更换为 Ubuntu,这种方式对于 all-in-one 形式的应用(例如大多数用 golang 构建的应用)尤其有效,如果应用程序依赖众多,就不合适了。另外众所周知,Alpine 和我们常用的发行版差异较大,因此也不太合适直接切换。 镜像构建 是不是就一个 docker build 就可以了?多数时候是的。不过要分成几种情况。 有 Dockerfile 的情况 官方已经发布二进制物料:这种情况通过修改 Dockerfile 加入更新的二进制文件之后,执行 docker build 即可。 官方未发布二进制物料:这种就需要根据源码进行构建,然后再生成 Docker 镜像。 没有 Dockerfile 的情况 有的软件源码中通过 Makefile 等方式提供了从二进制到镜像的构建方法,通常需要在 README.md 或者 BUILD.md 中查找线索。 更换底层的情况,通常需要自己照猫画虎,重新编写 Dockerfile。 还有一种比较尴尬的情况——有二进制物料,但是没 Dockerfile,这种我通常会使用 docker cp->docker commit 的不入流方式。 另外一种尴尬情况就是,官方只提供了 Docker 镜像,但是我们想要换掉基础层,这种情况和上面类似,用 docker cp 把官方镜像中的应用文件复制出来即可。 复测 在完成修复步骤之后,可以针对性地进行复测,查看修复情况,循环往复直到完成目标为止。 本文分享自微信公众号 - 伪架构师(fake-architect)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

分享自己的页运维架构

简单画了个图: 首先,后端程序及客户端都是分成三个版本:内部测试版,线上测试版,线上稳定版。线上测试版是小范围更新,经过一天测试没问题,然后再推到线上稳定版,更新其他服,一般游戏也都是按这个流程来更新的。 运维管理后台,记录了区服信息,提供各种简单API接口给各脚本使用。 然后批量维护脚本,create_list.py是根据运维管理后台提供的API,根据输入的参数(平台,区服范围)生成一份cqbyupdate.py需要使用的iplist文件,然后cqbyupdate.py根据这份ip文件执行相应的操作。 saltstack,是用于全服修改一些配置使用,例如批量修改zabbix的配置,批量修改nginx的配置 等等。 rsync,用于数据同步,例如给游戏服拉取最新版本。 游戏服最关键的只有一个control.py脚本,该脚本集成了管理单个游戏区服的所有操作,根据传进去的版本参数及动作参数执行对应的操作。 整套架构的优点是全服维护可用cqbyupdate.py脚本操作,如果临时游戏服上想做些什么更新,可用单服脚本control.py操作,比较灵活;缺点是对中心机依赖比较高,万一中心机岩了,就麻烦大了,所以搞了一台备份中心机。这套架构已经上线开服3000+ control.py单服维护脚本: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 #!/usr/bin/python #coding=utf-8 import subprocess import shutil import os import sys reload (sys) sys.setdefaultencoding( 'utf-8' ) import optparse import ConfigParser import time import jinja2 import urllib2 import json import socket try : import fcntl except : pass import struct import MySQLdb def get_ip_address(ifname): s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) return socket.inet_ntoa(fcntl.ioctl( s.fileno(), 0x8915 , #SIOCGIFADDR struct.pack( '256s' ,ifname[: 15 ]) )[ 20 : 24 ]) class Cqby: def __init__( self ,version,platform,platformid, id ): self .version = version self .platform = platform self .platformid = platformid self . id = id #工作目录: self .workdir = '/data/init' #定义游戏程序目录: self .gamedir = '/data/game/game%s' % self . id try : os.makedirs( '/data/game' ) except : print "目录已存在" #当前游戏聊天监控目录: self .chatdir = '/data/game/chat%s' % self . id #定义游戏端口: if int ( self . id )> 50000 : self .gameport = str ( self . id ) else : self .gameport = 20000 + int ( self . id ) self .gameport = str ( self .gameport) try : self .localip = get_ip_address( 'eth0' ) except : self .localip = get_ip_address( 'em1' ) #定义数据库名称: self .dbname = 'game%s' % self . id #定义管理员使用的数据库帐号密码: self .admindbuser = 'root' self .admindbpass = '123456' #定义备份目录: self .backup = '/data/backup' try : os.makedirs( self .backup) except : print "目录已经存在" #建立日志目录: self .gamelogdir = '/data/gamelogs/chuanqi/%s/S%s' % ( self .platform, self . id ) if not os.path.isdir( self .gamelogdir): os.makedirs( self .gamelogdir) subprocess.call( 'chownwww:www-R/data/gamelogs' ,shell = True ) #程序配置文件模板: self .binConfigDir = '%s/bin' % self .gamedir self .binConfigFiles = [ 'socket.jinja2' ] self .confConfigDir = '%s/conf' % self .gamedir self .confConfigFiles = [ 'jade.cfg.jinja2' ] self .independentConfigDir = '%s/conf/independent' % self .gamedir self .independentConfigFiles = [ 'auth.properties.jinja2' , 'debug.properties.jinja2' , 'fcm.properties.jinja2' , 'gm.properties.jinja2' , 'net.properties.jinja2' , 'server.properties.jinja2' , 'whiteList.properties.jinja2' , 'onlineLimit.properties.jinja2' , ] self .miscConfigDir = '%s/conf/config/common' % self .gamedir self .miscConfigFiles = [ 'misc.properties.jinja2' , ] #数据库权限: baselist = [ '127.0.0.1' ,] payIPListAll = { '37wan' :[], 'liebao' :[], '2345' :[], 'yilewan' :[], 'renrenwang' :[], '6711' :[], '1360' :[], 'duowan' :[], 'baidu' :[], 'lianyun' :[], 'tencent' :[] } try : self .platformPayList = payIPListAll[ self .platform] except : self .platformPayList = payIPListAll[ 'lianyun' ] self .payList = baselist + self .platformPayList self .mergelist = self .__getMerge() def __getMerge( self ): '''获取合服列表''' i = 0 while True : try : if i> = 3 : print "请求超时!!!!!!" sys.exit( 2 ) url = 'http://yw.admin.xxx.com/yunwei/api/getmergetarget/%s/%s/' % ( self .platform, self . id ) request = urllib2.urlopen(url) response = request.read().split( ',' ) except Exception,e: print "请求合服信息失败:" + str (e) print "正在重试。。。" i = i + 1 else : break return response def createDatabase( self ): '''创建数据库''' try : print "正在创建数据库:%s" % self .dbname cmd = '''/usr/local/mysql/bin/mysql-u'%s'-p'%s'-e"createdatabase%sDEFAULTCHARACTERSETutf8COLLATEutf8_general_ci"''' % ( self .admindbuser, self .admindbpass, self .dbname) ret = subprocess.call(cmd,shell = True ) print "执行状态:%s" % ret if ret: print "创建数据库失败,请确认!" sys.exit( 2 ) except Exception,e: print "捕捉到异常:" ,e sys.exit( 2 ) def updateDB( self ,filename): '''导入数据库文件''' try : print "正在导入SQL文件:%s" % filename cmd = '''/usr/local/mysql/bin/mysql-u'%s'-p'%s'%s<%s''' % ( self .admindbuser, self .admindbpass, self .dbname,filename) ret = subprocess.call(cmd,shell = True ) print "执行状态:%s" % ret except Exception,e: print "捕捉到异常:" ,e sys.exit( 2 ) def dumpDatabase( self ): '''备份数据库''' try : print "正在备份数据库:%s" % self .dbname curTime = time.strftime( '%Y%m%d%H%M%S' ,time.localtime(time.time())) cmd = '''/usr/local/mysql/bin/mysqldump-u'%s'-p'%s'%s>%s''' % ( self .admindbuser, self .admindbpass, self .dbname,' % s / % s - % s.sql' % ( self .backup,curTime, self .dbname)) ret = subprocess.call(cmd,shell = True ) print "执行状态:%s" % ret except Exception,e: print "捕捉到异常:" ,e def dropDatabase( self ): '''删除数据库''' try : print "正在删除数据库:%s" % self .dbname cmd = '''/usr/local/mysql/bin/mysql-u'%s'-p'%s'-e"dropdatabase%s"''' % ( self .admindbuser, self .admindbpass, self .dbname) ret = subprocess.call(cmd,shell = True ) print "执行状态:%s" % ret except Exception,e: print "捕捉到异常:" ,e def createGameDir( self ): '''创建游戏目录''' try : print "正在检测目录是否存在:%s" % self .gamedir if os.path.isdir( self .gamedir): print "目录已存在,请检查参数!" sys.exit( 2 ) else : print "正在复制程序文件至:%s" % self .gamedir shutil.copytree( '%s/%s/server' % ( self .workdir, self .version), self .gamedir) except Exception,e: print "捕捉到异常:" ,e sys.exit( 2 ) def dropGameDir( self ): '''清理游戏目录''' try : print "正在删除游戏目录:%s" % self .gamedir if os.path.isdir( self .gamedir): shutil.rmtree( self .gamedir) except Exception,e: print "遇到错误:" ,e def dropGameLogDir( self ): '''清理游戏日志目录''' try : print "正在删除日志目录:%s" % self .gamelogdir if os.path.isdir( self .gamelogdir): shutil.rmtree( self .gamelogdir) except Exception,e: print "遇到错误:" ,e def createConfig( self ,configdir,configlist): '''创建程序配置''' try : print "正在生成配置文件:%s" % configdir url = 'http://yw.admin.xxx.com/yunwei/api/getmem/%s/%s' % ( self .platform, self . id ) response = urllib2.urlopen(url) mem = response.read() env = jinja2.Environment(loader = jinja2.FileSystemLoader(configdir)) for gateconfig in configlist: print gateconfig template = env.get_template(gateconfig) f = open ( '%s/%s' % (configdir,gateconfig.rstrip( '.jinja2' )), 'w' ) f.write( template.render( version = self .version, platformid = self .platformid, platform = self .platform, gameid = self . id , gameport = self .gameport, gamedir = self .gamedir, dbuser = 'game' , dbpass = 'game123456' , dbname = self .dbname, paylist = self .platformPayList, mem = mem, mergelist = self .mergelist, ) ) f.close() except Exception,e: print "生成配置文件遇到错误:" ,e sys.exit( 2 ) def updateconfig( self ): self .createConfig( self .binConfigDir, self .binConfigFiles) os.chmod( '%s/bin/socket' % self .gamedir, 0755 ) self .createConfig( self .confConfigDir, self .confConfigFiles) self .createConfig( self .independentConfigDir, self .independentConfigFiles) #self.createConfig(self.miscConfigDir,self.miscConfigFiles) def updategame( self ): print "正在更新游戏程序。。。" cmd = '''rsync-avzP--exclude="socket"--exclude="log"--exclude="onlineLimit.properties"--exclude="jade.cfg"--exclude="auth.properties"--exclude="debug.properties"--exclude="fcm.properties"--exclude="gm.properties"--exclude="net.properties"--exclude="server.properties"--exclude="whiteList.properties"%s/%s/server/%s/''' % ( self .workdir, self .version, self .gamedir) print cmd result = subprocess.call(cmd,shell = True ) return result def start( self ): print "给JSVC添加执行权限:" os.chmod( '%s/bin/jsvc' % self .gamedir, 0755 ) print "正在启动服务:" cmd = '''cd%s/bin;./socketstart''' % self .gamedir result = subprocess.call(cmd,shell = True ) return result def stop( self ): print "正在关闭服务:" cmd = '''cd%s/bin;./socketstop''' % self .gamedir result = subprocess.call(cmd,shell = True ) return result def clearnow( self ): self .dumpDatabase() self .updateDB( '%s/%s/server/sql/database.sql' % ( self .workdir, self .version)) self .dropGameLogDir() def clear( self ): try : conn = MySQLdb.connect(user = self .admindbuser,passwd = self .admindbpass,host = 'localhost' ,db = self .dbname,unix_socket = '/tmp/mysql.sock' ) cursor = conn.cursor(cursorclass = MySQLdb.cursors.DictCursor) sql = '''select*fromPlayer''' sum = cursor.execute(sql) cursor.close() conn.close() print "数据库Player表有:%s" % sum if int ( sum )> 30 : print "Player表记录总数大于30!请确认后再执行清档操作!!!" sys.exit( 2 ) else : print "Player表记录总数小于30,可以执行清档操作!" self .stop() self .clearnow() self .start() except Exception,e: print "连接数据库错误:%s" % e sys.exit( 2 ) def create( self ): '''一键搭服''' self .createDatabase() self .updateDB( '%s/%s/server/sql/database.sql' % ( self .workdir, self .version)) self .mysqlgrant() self .createGameDir() self .updateconfig() self .createchat() self .nginxlogs() def drop( self ): self .dumpDatabase() self .dropDatabase() self .dropGameDir() self .dropGameLogDir() self .dropchat() def onekey( self ): '''一键更新''' self .stop() time.sleep( 10 ) self .updategame() self .start() def mysqlgrant( self ): '''添加数据库授权''' print "正在添加数据库授权:" for ip in self .payList: print "正在添加%s权限" % ip cmd = '''/usr/local/mysql/bin/mysql-u'%s'-p'%s'-e"grantallprivilegeson*.*togame@'%s'Identifiedby'cqbygame'"''' % ( self .admindbuser, self .admindbpass,ip) subprocess.call(cmd,shell = True ) cmd = '''/usr/local/mysql/bin/mysql-u'%s'-p'%s'-e"grantselecton*.*todb@'119.131.244.178'identifiedby'lizhenjie';"''' % ( self .admindbuser, self .admindbpass) subprocess.call(cmd,shell = True ) if __name__ = = "__main__" : active_list = [ 'create' , 'drop' , 'updateconfig' , 'start' , 'stop' , 'clear' , 'updategame' , 'updateDB' , 'onekey' , 'mysqlgrant' , 'clearnow' ] gamever_list = [ 'test' , '37dev' , '37stable' ] usage = '''usage:%prog-pplatform %prog-vversion-iid-aaction %prog-vversion-iid-aupdateDB-ssqlfile ''' parser = optparse.OptionParser( usage = usage, version = "%prog2.0" ) setplat_opts = optparse.OptionGroup( parser, '设置服务器平台标识' , '一台硬件服务器设置一次即可。' ) setplat_opts.add_option( '-p' , '--platform' , dest = "platform" , help = "平台名称" ) parser.add_option_group(setplat_opts) tools_opts = optparse.OptionGroup( parser, '服务器日常功能' , ) tools_opts.add_option( '-v' , '--ver' , dest = "ver" , help = "版本目录" , type = "choice" , choices = gamever_list, default = gamever_list[ 1 ] ) tools_opts.add_option( '-i' , '--id' , dest = 'id' , help = "服务器ID" ) tools_opts.add_option( '-a' , '--action' , dest = 'action' , help = "执行动作" , type = "choice" , choices = active_list ) tools_opts.add_option( '-s' , '--sql' , dest = 'sql' , help = "SQL文件(可选,配合updateDB使用)" ) parser.add_option_group(tools_opts) options,args = parser.parse_args() err_msg = '参数不对,请输--help查看详细说明!' ini = 'platform.ini' if options.platform: apiurl = 'http://yw.admin.xxx.com/yunwei/api/getplatforminfo/' ini = 'platform.ini' result = urllib2.urlopen(apiurl) response = json.loads(result.read()) for code, id in response.items(): if options.platform = = code: platformid = id print "正在设置服务器标识为:%s-%s" % (platformid,options.platform) cfd = open (ini, 'w' ) conf = ConfigParser.ConfigParser() conf.add_section( 'platforminfo' ) conf. set ( 'platforminfo' , 'name' ,options.platform) conf. set ( 'platforminfo' , 'id' ,platformid) conf.write(cfd) cfd.close() break sys.exit( 0 ) if options. id and options.ver and options.action: cf = ConfigParser.ConfigParser() cf.read(ini) platform = cf.get( 'platforminfo' , 'name' ) platformid = cf.get( 'platforminfo' , 'id' ) cqby = Cqby(options.ver,platform,platformid,options. id ) run_function = getattr (cqby,options.action) if options.action in [ 'updateDB' ,]: run_function( '%s/server/sql/%s' % (options.ver,options.sql)) else : run_function() else : parser.error(err_msg) cqbyupdate.py批量维护脚本: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 #!/usr/bin/python #coding:utf-8 import threading import Queue import subprocess import optparse import logging import logging.config import datetime import os import sys reload (sys) sys.setdefaultencoding( 'utf-8' ) #test: import time #logging.basicConfig(level=logging.DEBUG,format='(%(threadName)-10s)%(message)s',) logging.config.fileConfig( "logger.conf" ) logger = logging.getLogger( "root" ) logger2 = logging.getLogger( "file" ) queue = Queue.Queue() Failed_List = [] class Ahdts(threading.Thread): def __init__( self ,queue): super (Ahdts, self ).__init__() self .queue = queue self .workdir = '/data/init' #建立日志目录: log_path = 'updatelog' today = datetime.date.today() self .log_path_today = '%s/%s' % (log_path,today) if not os.path.isdir( self .log_path_today): try : os.makedirs( self .log_path_today) except Exception,e: print e sys.exit( 2 ) def run( self ): while True : global action global sqlfile item = self .queue.get() value = item.strip().split( ',' ) platform = value[ 0 ] id = value[ 1 ] ip = value[ 2 ] port = value[ 3 ] opentime = value[ 4 ] logging.debug( "%10s%6s%15s%15s%10sThreadingStart!" % (platform, id ,ip,action,ver)) if action = = 'rsync' : cmd = '''cd%s;./rsync''' % self .workdir elif action = = 'ntp' : cmd = '''cd%s;./TimeClient.py''' % self .workdir elif action in [ 'updateDB' ,]: cmd = '''cd%s;./control.py-i%s-a%s-v%s-s%s''' % ( self .workdir, id ,action,ver,sqlfile) elif action = = 'platform' : cmd = '''cd%s;./control.py-p%s''' % ( self .workdir,platform) else : cmd = '''cd%s;./control.py-i%s-a%s-v%s''' % ( self .workdir, id ,action,ver) sshcmd = '''sshroot@%s-n"%s"''' % (ip,cmd) with open ( '%s/%s-%s-%s-%s.log' % ( self .log_path_today,platform, id ,ver,action), 'a' )aslogfile: exitcode = subprocess.call(sshcmd,shell = True ,stdout = logfile,stderr = subprocess.STDOUT) if exitcode = = 0 : logger2.debug( '%10s%6s%15s%15s%10s%s' % (platform, id ,ip,action,ver,cmd)) rettxt = '%10s%6s%15s%15s%10sThreadingEnd!ExitCode:%s' % (platform, id ,ip,action,ver,exitcode) if exitcode: Failed_List.append(rettxt) logging.debug(rettxt) self .queue.task_done() if __name__ = = "__main__" : action_list = [ 'rsync' , 'create' , 'drop' , 'start' , 'stop' , 'clear' , 'updateconfig' , 'updategame' , 'updateDB' , 'onekey' ] gamever_list = [ 'test' , '37dev' , '37stable' ] usage = '''usage:%prog--file<file.ini>--action<action> Forexample:%prog-fgame-test.ini-acreate %prog-fgame-test.ini-aonekey %prog-fgame-test.ini-aupdateDB-stest.sql ''' parser = optparse.OptionParser( usage = usage, version = "%prog1.4" ) parser.add_option( '-f' , '--file' ,dest = "file" , help = "IP文件列表" ) parser.add_option( '-a' , '--action' ,dest = "action" , help = "执行动作" , type = "choice" ,choices = action_list) parser.add_option( '-v' , '--ver' ,dest = 'ver' , help = "版本目录标识" , type = "choice" ,choices = gamever_list) parser.add_option( '-s' , '--sql' ,dest = 'sql' , help = "待更新的SQL文件" ) options,args = parser.parse_args() err_msg = '参数不对,请输--help查看详细说明!' if options.action and options.ver and options. file : with open (options. file )as file : content = file .readlines() action = options.action ver = options.ver sqlfile = options.sql maxThreadNum = 200 if len (content)< 100 : maxThreadNum = len (content) for i in range (maxThreadNum): t = Ahdts(queue) t.setDaemon( True ) t.start() logging.debug( "%10s%6s%15s%15s%10s" % ( 'PlatForm' , 'ID' , 'IP' , 'Action' , 'Version' )) iplist = [] for i in content: ii = i.strip().split( ',' ) ip = ii[ 2 ] if action in [ 'rsync' , 'platform' ] and ip in iplist: continue queue.put(i) iplist.append(ip) queue.join() #打印执行失败列表: print '=' * 20 + '执行失败列表' + '=' * 20 if Failed_List: for i in Failed_List: print i else : print "None" print '=' * 52 logging.debug( "Done" ) else : print err_msg 批量维护脚本其实就是ssh远程过去游戏服执行control.py脚本,后面看能不能改成用socket的方式去连接,把socket的东西练练手,整套东西感觉还是比较简单。 本文转自运维笔记博客51CTO博客,原文链接http://blog.51cto.com/lihuipeng/1617958如需转载请自行联系原作者 lihuipeng

优秀的个人博客,低调大师

Linux服务器安全配置加固防护方法

本文详细总结了PHP网站在Linux服务器上面的安全配置,包含PHP安全、mysql数据库安全、web服务器安全、木马查杀和防范等,很好很强大很安全。(如果需要深入的安全部署建议找专业做安全的国内公司如:Sinesafe,绿盟,启明星辰等等都是比较不错的专业做网站安全的公司) PHP安全配置 1. 确保运行php的用户为一般用户,如www 2. php.ini参数设置 disable_functions = passthru,exec,system,chroot,chgrp,chown,shell_exec,proc_open,proc_get_status,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,stream_socket_server,fsocket,phpinfo #禁用的函数 expose_php = off #避免暴露PHP信息 display_errors = off #关闭错误信息提示 register_globals = off #关闭全局变量 enable_dl = off #不允许调用dl allow_url_include = off #避免远程调用文件 session.cookie_httponly = 1 #http only开启 upload_tmp_dir = /tmp#明确定义upload目录 open_basedir = ./:/tmp:/home/wwwroot/#限制用户访问的目录 open_basedir参数详解 open_basedir可将用户访问文件的活动范围限制在指定的区域,通常是其家目录的路径,也可用符号"."来代表当前目录。注意用open_basedir指定的限制实际上是前缀,而不是目录名。 举例来说: 若"open_basedir = /home/wwwroot", 那么目录"/home/wwwroot"和"/home/wwwroot1"都是可以访问的。所以如果要将访问限制在仅为指定的目录,请用斜线结束路径名。 注意: 从网上获取的资料来看,open_basedir会对php操作io的性能产生很大的影响。研究资料表明,配置了php_basedir的脚本io执行速度会比没有配置的慢10倍甚至更多,请大家自己衡量 open_basedir也可以同时设置多个目录, 在Windows中用分号分隔目录,在任何其它系统中用冒号分隔目录。当其作用于Apache模块时,父目录中的open_basedir路径自动被继承。 MySQL安全设置 1. MySQL版本的选择 在正式生产环境中,禁止使用4.1系列的MySQL数据库。至少需要使用5.1.39或以上版本。 2. 网络和端口的配置 在数据库只需供本机使用的情况下,使用–skip-networking参数禁止监听网络 。 3. 确保运行MySQL的用户为一般用户,如mysql,注意存放数据目录权限为mysql vi/etc/my.cnf user = mysql 4. 开启mysql二进制日志,在误删除数据的情况下,可以通过二进制日志恢复到某个时间点 vi/etc/my.cnf log_bin = mysql-bin expire_logs_days = 7 5. 认证和授权 (1) 禁止root账号从网络访问数据库,root账号只允许来自本地主机的登陆。 mysql>grantallprivilegeson*.* toroot @localhost identified by'password'withgrantoption; mysql>flush priveleges; (2) 删除匿名账号和空口令账号 mysql>USE mysql; mysql>deletefromuserwhereUser=; mysql>deletefromuserwherePassword=; mysql>deletefromdb whereUser=; web服务器安全 确保运行Nginx或者Apache的用户为一般用户,如www,注意存放数据目录权限为www 防止sql注入 if( $query_string ~* ".*[\;'\<\>].*"){ return404; } 关闭存放数据上传等目录的PHP解析 location ~* ^/(attachments|data)/.*\.(php|php5)${ deny all; } 针对Apache:关闭图片目录/上传等目录的PHP解析 order allow,deny Deny from all 木马查杀和防范 php木马快速查找命令 grep-r --include=*.php '[^a-z]eval($_POST'/home/wwwroot/ grep-r --include=*.php 'file_put_contents(.*$_POST\[.*\]);'/home/wwwroot/ 利用find mtime查找最近两天或者发现木马的这几天,有哪些PHP文件被修改 find-mtime -2 -typef -name \*.php 防范: 1. 做好之前的安全措施,比如禁用相关PHP函数等 2. 改变目录和文件属性 find-typef -name \*.php -execchomd 644 {} \; find-typed -execchmod755 {} \; chown-R www.www /home/wwwroot/www.waitalone.cn 3. 为防止跨站感染,需要做虚拟主机目录隔离 (1) nginx的简单实现方法 利用nginx跑多个虚拟主机,习惯的php.ini的open_basedir配置: open_basedir = ./:tmp:/home/wwwroot/ 注:/home/wwwroot/是放置所有虚拟主机的web路径 黑客可以利用任何一个站点的webshell进入到/home/wwwroot/目录下的任何地方,这样对各个虚拟主机的危害就很大 例如: /data/www/wwwroot目录下有2个虚拟主机 修改php.ini open_basedir = ./:/tmp:/home/wwwroot/www.sinesafe.com:/home/wwwroot/back.sinesafe.com 这样用户上传webshell就无法跨目录访问了。 (2) Apache的实现方法,控制跨目录访问 在虚拟机主机配置文件中加入 php_admin_value open_basedir "/tmp:/home/wwwroot/www.sinesafe.com"

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册