Hadoop运维记录系列(二十一)
Zeppelin启用https过程和Hack内核以满足客户需求的记录。 原因是这客户很有意思,该客户中国分公司的人为了验证内网安全性,从国外找了一个***测试小组对Zeppelin和其他产品进行***测试,结果发现Zeppelin主要俩问题,一个是在内网没用https,一个是zeppelin里面可以执行shell命令和python语句。其实这不算大问题,zeppelin本来就是干这个用的。但是***小组不了解zeppelin是做什么的,认为即使在内网里,执行shell命令能查看操作系统的一些文件是大问题,然后发生的事就不说了,不是我们的问题了。 不过既然他们要求整改,我们也只好配合,虽然大家都觉得内网域名加https属于脱了裤子放屁,然后不让zeppelin干他本来应该干的事就更过分了,但鉴于客户是甲方,也只好hack源码了。 于是某个周末用了4个小时完成所有工作。 先记录下zeppelin加https访问,我们有自己的域名证书,所以直接用即可。如果没有域名证书,需要自签发,那么可以看第二部分,双向认证步骤。 https第一部分,已有域名添加jks: opensslpkcs12-export-inxxx.com.crt-inkeyxxx.com.key-outxxx.com.pkcs12 keytool-importkeystore-srckeystorexxx.com.pkcs12-destkeystorexxx.com.jks-srcstoretypepkcs12 https第二部分,自签发证书双向认证添加jks #生成root私钥和证书文件。 opensslgenrsa-outroot.key(pem)2048#Generaterootkeyfile opensslreq-x509-new-keyroot.key(pem)-outroot.crt#Generaterootcertfile #创建客户端私钥和证书以及证书请求文件csr opensslgenrsa-outclient.key(pem)2048#Generateclientkeyfile opensslreq-new-keyclient.key(pem)-outclient.csr#Generateclientcertrequestfile opensslx509-req-inclient.csr-CAroot.crt-CAkeyroot.key(pem)-CAcreateserial-days3650-outclient.crt#Userootcerttogenerateclientcertfile #生成服务器端私钥,证书和证书请求文件csr opensslgenrsa-outserver.key(pem)2048#Generateserverkeyfile,useinZeppelin opensslreq-new-keyserver.key(pem)outserver.csr@Generateservercertrequestfile opensslx509-req-inserver.csr-CAroot.crt-CAkeyroot.key(pem)-CAcreateserial-days3650-outserver.crt#Userootcerttogenerateservercertfile #生成客户端端jks文件 opensslpkcs12-export-inclient.crt-inkeyclient.key(pem)-outclient.pkcs12#Packagetopkcs12format,mustinputapassword,youshouldrememberthepassword keytool-importkeystore-srckeystoreclient.pkcs12-destkeystoreclient.jks-srcstoretypepkcs12#Theclientpasswordyoujustinputatlaststep #生成服务器端jks文件 opensslpkcs12-export-inserver.crt-inkeyserver.key(pem)-outserver.pkcs12@Packagetopkcs12format,mustinputapassword,youshouldrememberthepassword keytool-importkeystore-srckeystoreserver.pkcs12-destkeystoreserver.jks-srcstoretypepkcs12#Theserverpasswordyoujustinputatlaststep 如果是不需要双向认证,只要单向自签发,不创建客户端的各种就可以了。 然后找个地把这些文件放过去,再修改zeppelin配置即可。 mkdir-p/etc/zeppelin/conf/ssl cpserver.crtserver.jks/etc/zeppelin/conf/ssl <property> <name>zeppelin.server.ssl.port</name> <value>8443</value> <description>Serversslport.(usedwhensslpropertyissettotrue)</description> </property> <property> <name>zeppelin.ssl</name> <value>true</value> <description>ShouldSSLbeusedbytheservers?</description> </property> <property> <name>zeppelin.ssl.client.auth</name> <value>false</value> <description>ShouldclientauthenticationbeusedforSSLconnections?</description> </property> <property> <name>zeppelin.ssl.keystore.path</name> <value>/etc/zeppelin/conf/ssl/xxx.com.jks</value> <description>PathtokeystorerelativetoZeppelinconfigurationdirectory</description> </property> <property> <name>zeppelin.ssl.keystore.type</name> <value>JKS</value> <description>Theformatofthegivenkeystore(e.g.JKSorPKCS12)</description> </property> <property> <name>zeppelin.ssl.keystore.password</name> <value>passwordwhichyouinputongeneratingserverjksstep</value> <description>Keystorepassword.CanbeobfuscatedbytheJettyPasswordtool</description> </property> 然后反代那里也加上443的ssl证书以及443转8443的upstream即可。 然后是hack zeppelin源码加入关键字限制,这个确实找了一小会zeppelin发送执行源码给interpreter的地方,zeppelin架构比较清晰,但是代码挺复杂的,用到了很多小花活儿。比如thrift,interpreter脚本里建立nc监听。然后各个解释器插件用socket跟interpreter脚本通信,前端angular,后端jetty,还用shiro做验证和授权。回头可以单开好几篇说说zeppelin安装,使用和详细配置,做这项目基本把zeppelin摸透了。 找到发送前端编写内容给interpreter的java代码,然后用很生硬的办法限制执行命令。具体那个.java文件的名字我就不说了,有悬念有惊喜。我不写java,只负责读源码找到代码位置,hack的java是同事写的。然后编译,替换jar包,完成。后面改了改配置,后续的***测试顺利通过。 staticHashSet<String[]>blockedCodeString=newHashSet<>(); static{ blockedCodeString.add(newString[]{"import","os"}); blockedCodeString.add(newString[]{"import","sys"}); blockedCodeString.add(newString[]{"import","subprocess"}); blockedCodeString.add(newString[]{"import","pty"}); blockedCodeString.add(newString[]{"import","socket"}); blockedCodeString.add(newString[]{"import","commands"}); blockedCodeString.add(newString[]{"import","paramiko"}); blockedCodeString.add(newString[]{"import","pexpect"}); blockedCodeString.add(newString[]{"import","BaseHTTPServer"}); blockedCodeString.add(newString[]{"import","ConfigParser"}); blockedCodeString.add(newString[]{"import","platform"}); blockedCodeString.add(newString[]{"import","popen2"}); blockedCodeString.add(newString[]{"import","copy"}); blockedCodeString.add(newString[]{"import","SocketServer"}); blockedCodeString.add(newString[]{"import","sysconfig"}); blockedCodeString.add(newString[]{"import","tty"}); blockedCodeString.add(newString[]{"import","xmlrpmlib"}); blockedCodeString.add(newString[]{"etc"}); blockedCodeString.add(newString[]{"boot"}); blockedCodeString.add(newString[]{"dev"}); blockedCodeString.add(newString[]{"lib"}); blockedCodeString.add(newString[]{"lib64"}); blockedCodeString.add(newString[]{"lost+found"}); blockedCodeString.add(newString[]{"mnt"}); blockedCodeString.add(newString[]{"proc"}); blockedCodeString.add(newString[]{"root"}); blockedCodeString.add(newString[]{"sbin"}); blockedCodeString.add(newString[]{"selinux"}); blockedCodeString.add(newString[]{"usr"}); blockedCodeString.add(newString[]{"passwd"}); blockedCodeString.add(newString[]{"useradd"}); blockedCodeString.add(newString[]{"userdel"}); blockedCodeString.add(newString[]{"rm"}); blockedCodeString.add(newString[]{"akka"}); blockedCodeString.add(newString[]{"groupadd"}); blockedCodeString.add(newString[]{"groupdel"}); blockedCodeString.add(newString[]{"mkdir"}); blockedCodeString.add(newString[]{"rmdir"}); blockedCodeString.add(newString[]{"ping"}); blockedCodeString.add(newString[]{"nc"}); blockedCodeString.add(newString[]{"telnet"}); blockedCodeString.add(newString[]{"ftp"}); blockedCodeString.add(newString[]{"scp"}); blockedCodeString.add(newString[]{"ssh"}); blockedCodeString.add(newString[]{"ps"}); blockedCodeString.add(newString[]{"hostname"}); blockedCodeString.add(newString[]{"uname"}); blockedCodeString.add(newString[]{"vim"}); blockedCodeString.add(newString[]{"nano"}); blockedCodeString.add(newString[]{"top"}); blockedCodeString.add(newString[]{"cat"}); blockedCodeString.add(newString[]{"more"}); blockedCodeString.add(newString[]{"less"}); blockedCodeString.add(newString[]{"chkconfig"}); blockedCodeString.add(newString[]{"service"}); blockedCodeString.add(newString[]{"netstat"}); blockedCodeString.add(newString[]{"iptables"}); blockedCodeString.add(newString[]{"ip"}); blockedCodeString.add(newString[]{"route"}); blockedCodeString.add(newString[]{"curl"}); blockedCodeString.add(newString[]{"wget"}); blockedCodeString.add(newString[]{"sysctl"}); blockedCodeString.add(newString[]{"touch"}); blockedCodeString.add(newString[]{"scala.sys.process"}); blockedCodeString.add(newString[]{"0.0.0.0"}); blockedCodeString.add(newString[]{"git"}); blockedCodeString.add(newString[]{"svn"}); blockedCodeString.add(newString[]{"hg"}); blockedCodeString.add(newString[]{"cvs"}); blockedCodeString.add(newString[]{"exec"}); blockedCodeString.add(newString[]{"ln"}); blockedCodeString.add(newString[]{"kill"}); blockedCodeString.add(newString[]{"rsync"}); blockedCodeString.add(newString[]{"lsof"}); blockedCodeString.add(newString[]{"crontab"}); blockedCodeString.add(newString[]{"libtool"}); blockedCodeString.add(newString[]{"automake"}); blockedCodeString.add(newString[]{"autoconf"}); blockedCodeString.add(newString[]{"make"}); blockedCodeString.add(newString[]{"gcc"}); blockedCodeString.add(newString[]{"cc"}); } staticbooleanallMatch(Stringaim,String[]checker){ if(checker==null||checker.length<1){ returnfalse; }else{ //bydefault,treatasmatch,everynotmatchchangeit for(Stringi:checker){ if(!aim.matches(".*\\b"+i+"\\b.*")){ returnfalse; } } returntrue; } } staticStringanyMatch(Stringaim,HashSet<String[]>all)throwsException{ if(aim.contains("FUCKP&G")){ thrownewException("Howdoyouknowthis????"); }else{ for(String[]one:all){ if(allMatch(aim,one)){ StringBuildersb=newStringBuilder(); for(Strings:one){ sb.append(s+""); } returnsb.toString(); } } thrownewException("Noonematch"); } } //......此处是个public类 try{ StringmatchesStrings=anyMatch(st,blockedCodeString); result=newInterpreterResult(Code.ERROR,"Containsdangerouscode:"+matchesStrings); }catch(Exceptionme){//nomatchany scheduler.submit(job); while(!job.isTerminated()){ synchronized(jobListener){ try{ jobListener.wait(1000); }catch(InterruptedExceptione){ logger.info("ExceptioninRemoteInterpreterServerwhileinterpret,jobListener.wait",e); } } } if(job.getStatus()==Status.ERROR){ result=newInterpreterResult(Code.ERROR,Job.getStack(job.getException())); }else{ result=(InterpreterResult)job.getReturn(); //incaseofjobabortinPENDINGstatus,resultcanbenull if(result==null){ result=newInterpreterResult(Code.KEEP_PREVIOUS_RESULT); } } } //......直到该public类结束 因为客户有deadline限制,所以快速定位源码位置的过程还是挺有意思的,比较紧张刺激,在这个以小时计算deadline压力下,什么intelliJ, Eclipse都不好使啊,就grep和vi最好用,从找到到改完,比客户定的deadline提前了好几个小时。