使用nginx或tomcat搭建https环境

前言

最近在做一个服务端和手机端相结合的项目,已接近尾声。

手机最初安装app是通过扫描二维码来下载安装的,这个方式在当下也较为流行。用此方法Android自然是没有问题,但是ios实现起来就没这么简单了。

因为是企业app使用的是企业证书不上appstore,所以只能使用safari来安装,步骤大致如下:

  • 安装证书,这里的证书必须安装,否则在连接下面的xxx.plist文件地址时会提示连不上服务器。

  • 访问xxx.plist文件,一般就是用户看到的安装按钮,地址就像这样:itms-services://?action=download-manifest&url=https://xxx.xxx/download/xxx.plist

  • 具体的安装操作是由xxx.plist文件中描述指定的,当然这步用户不会感觉到。xxx.plist文件ios开发人员会提供这里就不多说了。

我们要做的就是解决前面两步,本来是相当简单的事,但是苹果在ios7之后要求xxx.plist文件的地址必须是https的,这个就是麻烦的所在,因为我们原来没有https环境。

本来想着网上找个开放的https环境,把xxx.plist文件往上一放就解决了事,比如git.oschina.net实测就可以支持,但是领导要求这玩意不能放在别人那里,好吧,只能自己搭建一个了。

首先看了红薯的这篇: http://www.oschina.net/question/12_23148?fromerr=SUotMsG2

看来也挺简单的,按照说明搭建成功,浏览器也访问正常,可ios就是用不了。

后来折腾过才知道原来ios要求安装的证书是ssl的证书,并非ios开发者的证书,可上面的教程中并没有提供给ios安装的证书文件啊,看样子没这么简单搞定了,还是老老实实一步步来吧。

使用openssl生成证书

mac已经自带了openssl可以直接使用,如果没有请自行安装。

创建根证书

1、创建根证书私钥文件

openssl genrsa -out dexcoder.key 2048

2、创建根证书dexcoder.cer,机构名称为Dexcoder CA

openssl req -new -x509 -key dexcoder.key -out dexcoder.cer -days 3650 -subj /CN="Dexcoder CA"

创建自签名的ssl证书

1、创建一个私钥server.key

openssl genrsa -out server.key 2048

2、创建CSR,我这里是本地所以是:localhost,也可以改成IP,输出文件为server.csr

openssl req -new -out server.csr -key server.key -subj /CN=localhost

3、用CSR创建SSL证书,有效期为10年,输出文件为server.cer,序号文件为server.serial(撤销证书时使用)

openssl x509 -req -in server.csr -out server.cer -CAkey dexcoder.key  -CA dexcoder.cer -days 3650 -CAcreateserial -CAserial server.serial

配置nginx

到上面为止,证书就已经生成完成了,如果使用nginx,只需要进行如下配置:

server {
    listen       8443 ssl;
    server_name  localhost;
    ssl_certificate      /Users/liyd/testssl/server.cer;
    ssl_certificate_key  /Users/liyd/testssl/server.key;
}

重启nginx后就可以使用地址:https://localhost:8443 来访问了。

因为是自签名证书而非第三方认证机构签发,所以浏览器会出现以下提示:

未分类

但这并不影响我们ios应用的下载,就不管它了。

配置tomcat

上面nginx的配置已经完成了,但因为线上已经有一台tomcat,所以想着能不能并到一起,方便也节省资源。

tomcat要使用上面的证书,还需要做一下加工,因为tomcat使用的一般只有一个.keystore文件。

1、将.key 和.cer 文件导出为.p12 证书,需要输入证书密码。这里密码设为123456。输出文件名为server.p12。

selflydeMacBook-Pro:testssl liyd$ openssl pkcs12 -export -in server.cer -inkey server.key -out server.p12 -name "server"
Enter Export Password:
Verifying - Enter Export Password:
selflydeMacBook-Pro:testssl liyd$

2、用keytool将.p12文件导入到.keystore中,这里srcstorepass后面的123456为server.p12的密码,deststorepass后的12356为.keystore的密码。

selflydeMacBook-Pro:testssl liyd$ keytool -importkeystore -v -srckeystore server.p12 -srcstoretype pkcs12 -srcstorepass 123456 -destkeystore server.keystore -deststoretype jks -deststorepass 123456
已成功导入别名 server 的条目。
已完成导入命令: 1 个条目成功导入, 0 个条目失败或取消
[正在存储server.keystore]
selflydeMacBook-Pro:testssl liyd$

3、使用ssl证书

打开Tomcat安装目录,修改conf目录下的server.xml,增加下面内容,原来注释部分应该有类似标签内容,可以参考:

<Connector SSLEnabled="true" protocol="org.apache.coyote.http11.Http11Protocol"  
            scheme="https" secure="true" sslProtocol="TLS"
            keystoreFile="/Users/liyd/testssl/server.keystore" keystorePass="123456"
            maxThreads="150" port="8443" clientAuth="false" 
            />

这里设置了端口为8443,启动tomcat,打开浏览器访问:https://localhost:8443/ 成功打开tomcat主页,说明服务已经可用。

ios安装证书

上面做了这么多,现在才是我们的最终目的,将dexcoder.cer放到部署的tomcat中,供ios访问下载,例如:

https://localhost:8443/app/dexcoder.cer

这个证书下载不一定要是https的,在安装好证书之后,再访问plist文件安装:

itms-services://?action=download-manifest&url=https://xxx.xxx/download/xxx.plist

这里的xxx.plist文件地址才必须是https。

结束语

到这里我们要的功能就都已经搞定了。不足之处是证书是自签名的,浏览器在访问时会出现安全警告。

如果想要去掉这个警告,可以申请第三方CA机构的证书,这个可以自己网上找找。

以下是摘自网上的一段关于CA机构及证书的说明:

要获取受浏览器信任的证书,则需要到证书提供商处申请。证书授证中心,又叫做CA机构,为每个使用公开密钥的用户发放一个数字证书。浏览器在默认情况下内置了一些CA机构的证书,使得这些机构颁发的证书受到信任。VeriSign即是一个著名的国外CA机构,工行、建行、招行、支付宝、财付通等网站均使用VeriSign的证书,而网易邮箱等非金融网站采用的是中国互联网信息中心CNNIC颁发的SSL证书。一般来说,一个证书的价格不菲,以VeriSign的证书为例,价格在每年8000元人民币左右。

据说也有免费的证书可以申请。和VeriSign一样,StartSSL也是一家CA机构,它的根证书很久之前就被一些具有开源背景的浏览器支持(Firefox浏览器、谷歌Chrome浏览器、苹果Safari浏览器等)。后来StartSSL竟然搞定了微软:在升级补丁中,微软更新了通过Windows根证书认证(Windows Root Certificate Program)的厂商清单,并首次将StartCom公司列入了该认证清单。现在,在Windows 7或安装了升级补丁的Windows Vista或Windows XP操作系统中,系统会完全信任由StartCom这类免费数字认证机构认证的数字证书,从而使StartSSL也得到了IE浏览器的支持。

简单几步搭建一个基于 Docker 的 Tomcat 运行环境!

前言

Docker 旨在提供一种应用程序的自动化部署解决方案,在 Linux 系统上迅速创建一个容器(轻量级虚拟机)并部署和运行应用程序,并通过配置文件可以轻松实现应用程序的自动化安装、部署和升级,非常方便。因为使用了容器,所以可以很方便的把生产环境和开发环境分开,互不影响,这是 docker 最普遍的一个玩法。更多的玩法还有大规模 web 应用、数据库部署、持续部署、集群、测试环境、面向服务的云计算、虚拟桌面 VDI 等等。

主观的印象:Docker 使用 Go 语言编写,用 cgroup 实现资源隔离,容器技术采用 LXC. 提供了能够独立运行 Unix 进程的轻量级虚拟化解决方案。它提供了一种在安全、可重复的环境中自动部署软件的方式。 LXC 命令有些复杂,若感兴趣,这里有一篇我以前写的基于 LXC ,(从无到有,搭建一个简单版的 JAVA PAAS 云平台),可以提前复习一下。

有关实现原理、相关理论、运用场景等,会在本系列后面书写,这里先来一个浅尝辄止,完全手动,基于 Docker 搭建一个 Tomcat 运行环境。先出来一个像模像样 Demo,可以见到效果,可能会让我们走的更远一些。

环境

本文所有环境,VMwareWorkStation 上运行 ubuntu-13.10-server-amd64, 注意是 64 位系统,理论上其它虚拟机也是完全可行的。

安装 Docker

Docker 0.7 版本需要 Linux 内核 3.8 支持,同时需要 AUFS 文件系统。

# 检查一下AUFS是否已安装sudo apt-get updatesudo apt-get install linux-image-extra-`uname -r`# 添加Docker repository keysudo sh -c "wget -qO- https://get.docker.io/gpg | apt-key add -"# 添加Docker repository,并安装Dockersudo sh -c "echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list"sudo apt-get updatesudo apt-get install lxc-docker# 检查Docker是否已安装成功sudo docker version# 终端输出 Client version: 0.7.1Go version (client): go1.2Git commit (client): 88df052Server version: 0.7.1Git commit (server): 88df052Go version (server): go1.2Last stable version: 0.7.1

去除掉 sudo

在 Ubuntu 下,在执行 Docker 时,每次都要输入 sudo,同时输入密码,很累人的,这里微调一下,把当前用户执行权限添加到相应的 docker 用户组里面。

# 添加一个新的docker用户组sudo groupadd docker# 添加当前用户到docker用户组里,注意这里的yongboy为ubuntu server登录用户名sudo gpasswd -a yongboy docker# 重启Docker后台监护进程sudo service docker restart# 重启之后,尝试一下,是否生效docker version#若还未生效,则系统重启,则生效sudo reboot

安装一个 Docker 运行实例 -ubuntu 虚拟机

Docker 安装完毕,后台进程也自动启动了,可以安装虚拟机实例(这里直接拿官方演示使用的 learn/tutorial 镜像为例):

docker pull learn/tutorial

安装完成之后,看看效果

docker run learn/tutorial /bin/echo hello world

交互式进入新安装的虚拟机中

docker run -i -t learn/tutorial /bin/bash

会看到:

root@51774a81beb3:/#

说明已经进入交互式环境。

安装 SSH 终端服务器,便于我们外部使用 SSH 客户端登陆访问

apt-get updateapt-get install openssh-serverwhich sshd/usr/sbin/sshdmkdir /var/run/sshdpasswd #输入用户密码,我这里设置为123456,便于SSH客户端登陆使用exit #退出

获取到刚才操作的实例容器 ID

#docker ps -lCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES51774a81beb3 learn/tutorial:latest /bin/bash 3 minutes ago Exit 0 thirsty_pasteur

可以看到当前操作的容器 ID 为 51774a81beb3 。注意了,一旦进行所有操作,都需要提交保存,便于 SSH 登陆使用:

docker commit 51774a81beb3 learn/tutorial

以后台进程方式长期运行此镜像实例:

docker run -d -p 22 -p 80:8080 learn/tutorial /usr/sbin/sshd -D

ubuntu 容器内运行着的 SSH Server 占用 22 端口,-p 22 进行指定。-p 80:8080 指的是,我们 ubuntu 将会以 8080 端口运行 tomcat ,但对外(容器外)映射的端口为 80 。

这时,查看一下,是否成功运行。

#docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES871769a4f5ea learn/tutorial:latest /usr/sbin/sshd -D About a minute ago Up About a minute 0.0.0.0:49154->22/tcp, 0.0.0.0:80->8080/tcp focused_poincare

注意这里的分配随机的 SSH 连接端口号为 49154:

ssh [email protected] -p 49154

输入可以口令,是不是可以进入了?你一旦控制了 SSH ,剩下的事情就很简单了,安装 JDK ,安装 tomcat 等,随你所愿了。以下为安装脚本:

# 在ubuntu 12.04上安装oracle jdk 7apt-get install python-software-propertiesadd-apt-repository ppa:webupd8team/javaapt-get updateapt-get install -y wgetapt-get install oracle-java7-installerjava -version# 下载tomcat 7.0.47wget http://mirror.bit.edu.cn/apache/tomcat/tomcat-7/v7.0.47/bin/apache-tomcat-7.0.47.tar.gz# 解压,运行tar xvf apache-tomcat-7.0.47.tar.gzcd apache-tomcat-7.0.47bin/startup.sh

默认情况下,tomcat 会占用 8080 端口,刚才在启动镜像实例的时候,指定了 -p 80:8080 ,ubuntu 镜像实例/容器,开放 8080 端口,映射到宿主机端口就是 80 。知道宿主机 IP 地址,那就可以自由访问了。在宿主机上,通过 cur l测试一下即可:

curl http://192.168.190.131

当然,你也可以使用浏览器访问啦。

真实情况,可能不会让 tomcat 直接对外开放 80 端口,一般都会位于 nginx/apache 或者防火墙的后面,上面仅为演示。

小结

在 Docker 帮助下搭建一个 Tomcat 运行时环境,总体很简单,让我们看到了 PAAS 的身影。不错,使用 Docker 作为 PAAS 底层服务,本身就不复杂。以后有时间,会谈一谈如何使用脚本文件构建一个镜像实例,同时会谈一谈 Docker 的实现原理和机制等。

CentOS 7安装fail2ban+Firewalld防止SSH爆破与CC攻击

说明:差不多很多博主都会遇到被CC攻击和SSH爆破的情况,这个时候就需要做下防御措施了,前几天发过一个防CC脚本,参考: https://www.moerats.com/archives/484/ ,不过对于CentOS 7来说,由于防火墙的因素,配置起来还是有点麻烦的,这里争对CentOS 7再分享个简单防CC攻击和SSH爆破的方法。

前言

fail2ban可以监视你的系统日志,然后匹配日志的错误信息执行相应的屏蔽动作。网上大部分教程都是关于fail2ban+iptables组合,考虑到CentOS 7已经自带Firewalld,所以这里我们也可以利用fail2ban+Firewalld来防CC攻击和SSH爆破。

本教程编辑文件使用vi命令,如果不会用的,可以使用比较简单的nano,可以参考: https://www.moerats.com/archives/485/ ,或者使用FTP工具,如WinSCP登录VPS操作。

准备工作

1、检查Firewalld是否启用

#如果您已经安装iptables建议先关闭
service iptables stop
#查看Firewalld状态
firewall-cmd --state
#启动firewalld
systemctl start firewalld
#设置开机启动
systemctl enable firewalld.service

启用Firewalld后会禁止所有端口连接,因此请务必放行常用的端口,以免被阻挡在外,以下是放行SSH端口(22)示例,供参考:

#放行22端口
firewall-cmd --zone=public --add-port=80/tcp --permanent
#重载配置
firewall-cmd --reload
#查看已放行端口
firewall-cmd --zone=public --list-ports

2、安装fail2ban

fail2ban可以监控系统日志,并且根据一定规则匹配异常IP后使用Firewalld将其屏蔽,尤其是针对一些爆破/扫描等非常有效。

#CentOS内置源并未包含fail2ban,需要先安装epel源
yum -y install epel-release
#安装fial2ban
yum -y install fail2ban

安装成功后fail2ban配置文件位于/etc/fail2ban,其中jail.conf为主配置文件,相关的匹配规则位于filter.d目录,其它目录/文件一般很少用到,如果需要详细了解可自行搜索。

3、配置规则

新建jail.local来覆盖fail2ban的一些默认规则:

#新建配置
vi /etc/fail2ban/jail.local
#默认配置
[DEFAULT]
ignoreip = 127.0.0.1/8
bantime  = 86400
findtime = 600
maxretry = 5
#这里banaction必须用firewallcmd-ipset,这是fiewalll支持的关键,如果是用Iptables请不要这样填写
banaction = firewallcmd-ipset
action = %(action_mwl)s

参数说明:

ignoreip:IP白名单,白名单中的IP不会屏蔽,可填写多个以(,)分隔
bantime:屏蔽时间,单位为秒(s)
findtime:时间范围
maxretry:最大次数
banaction:屏蔽IP所使用的方法,上面使用firewalld屏蔽端口

防止SSH爆破

如果您还在使用默认SSH端口(22),可能每天都会被扫描,我们可以修改端口尽量避免被扫,参考: https://www.moerats.com/archives/394/ ,或者可以使用fail2ban将恶意IP屏蔽。

继续修改jail.local这个配置文件,在后面追加如下内容:

[sshd]
enabled = true
filter  = sshd
port    = 22
action = %(action_mwl)s
logpath = /var/log/secure

参数说明:

[sshd]:名称,可以随便填写
filter:规则名称,必须填写位于filter.d目录里面的规则,sshd是fail2ban内置规则
port:对应的端口
action:采取的行动
logpath:需要监视的日志路径

到这一步,我们jail.local的规则看起来可能像下面这样子:

[DEFAULT]
ignoreip = 127.0.0.1/8
bantime  = 86400
findtime = 600
maxretry = 5
banaction = firewallcmd-ipset
action = %(action_mwl)s

[sshd]
enabled = true
filter  = sshd
port    = 22
action = %(action_mwl)s
logpath = /var/log/secure

上面的配置意思是如果同一个IP,在10分钟内,如果连续超过5次错误,则使用Firewalld将他IP ban了。输入systemctl start fail2ban启动fail2ban来试试效果。

使用另一台服务器不断尝试连接SSH,并且不断的将密码输入错误,你会发现连续超过5次后直接连不上,说明IP被ban了,可以输入:fail2ban-client status sshd查看被ban的IP,如下截图。

未分类

防止CC攻击

这里仅以Nginx为例,使用fail2ban来监视nginx日志,匹配短时间内频繁请求的IP,并使用firewalld将其IP屏蔽,达到CC防护的作用。

#需要先新建一个nginx日志匹配规则
vi /etc/fail2ban/filter.d/nginx-cc.conf
#填写如下内容
[Definition]
failregex =  -.*- .*HTTP/1.* .* .*$
ignoreregex =

继续修改jail.local追加如下内容:

[nginx-cc]
enabled = true
port = http,https
filter = nginx-cc
action = %(action_mwl)s
maxretry = 20
findtime = 60
bantime = 3600
logpath = /usr/local/nginx/logs/access.log

上面的配置意思是如果在60s内,同一IP达到20次请求,则将其IP ban 1小时,上面只是为了测试,请根据自己的实际情况修改。logpath为nginx日志路径。

防止Wordpress爆破

如果您经常分析日志会发现有大量机器人在扫描wordpress登录页面wp-login.php,虽然对方可能没成功,但是为了避免万一还是将他IP干掉为好。

#需要先新建一个nginx日志匹配规则
vi /etc/fail2ban/filter.d/wordpress.conf
#填写如下内容
[Definition]
failregex = ^ -.* /wp-login.php.* HTTP/1.."
ignoreregex =

继续修改jail.local追加如下内容:

[wordpress]
enabled = true
port = http,https
filter = wordpress
action = %(action_mwl)s
maxretry = 20
findtime = 60
bantime = 3600
logpath = /usr/local/nginx/logs/access.log

当然,别忘记输入systemctl restart fail2ban重启fail2ban使其生效。

常用命令

#启动
systemctl start fail2ban
#停止
systemctl stop fail2ban
#开机启动
systemctl enable fail2ban
#查看被ban IP,其中sshd为名称,比如上面的[wordpress]
fail2ban-client status sshd
#删除被ban IP
fail2ban-client set sshd delignoreip 192.168.111.111
#查看日志
tail /var/log/fail2ban.log

总结

fail2ban已经内置很多匹配规则,位于filter.d目录下,包含了常见的SSH/FTP/Nginx/Apache等日志匹配,如果都还无法满足您的需求,您也可以自行新建规则来匹配异常IP。使用fail2ban+Firewalld来阻止恶意IP是行之有效的办法,可极大提高服务器安全。

提示:搬瓦工年付$18可换CN2机房的套餐已补货,7机房随意切换,支持支付宝,优惠码:BWH1ZBPVK,购买: https://bwh1.net/cart.php?a=confproduct&i=0

SSH-用screen恢复会话

你是不是经常需要 SSH 或者 telent 远程登录到 Linux 服务器?你是不是经常为一些长时间运行的任务而头疼,比如系统备份、ftp 传输等等。通常情况下我们都是为每一个这样的任务开一个远程终端窗口,因为他们执行的时间太长了。必须等待它执行完毕,在此期间可不能关掉窗口或者断开连接,否则这个任务就会被杀掉,一切半途而废了。

元凶:SIGHUP 信号

让我们来看看为什么关掉窗口/断开连接会使得正在运行的程序死掉。

在Linux/Unix中,有这样几个概念:

  • 进程组(process group):一个或多个进程的集合,每一个进程组有唯一一个进程组ID,即进程组长进程的ID。

  • 会话期(session):一个或多个进程组的集合,有唯一一个会话期首进程(session leader)。会话期ID为首进程的ID。

  • 会话期可以有一个单独的控制终端(controlling terminal)。与控制终端连接的会话期首进程叫做控制进程(controlling process)。当前与终端交互的进程称为前台进程组。其余进程组称为后台进程组。

根据POSIX.1定义:

  • 挂断信号(SIGHUP)默认的动作是终止程序。

  • 当终端接口检测到网络连接断开,将挂断信号发送给控制进程(会话期首进程)。

  • 如果会话期首进程终止,则该信号发送到该会话期前台进程组。

  • 一个进程退出导致一个孤儿进程组中产生时,如果任意一个孤儿进程组进程处于STOP状态,发送SIGHUP和SIGCONT信号到该进程组中所有进程。

因此当网络断开或终端窗口关闭后,控制进程收到SIGHUP信号退出,会导致该会话期内其他进程退出。

我们来看一个例子。打开两个SSH终端窗口,在其中一个运行top命令。

[root@tivf09 root]# top

在另一个终端窗口,找到top的进程ID为5180,其父进程ID为5128,即登录shell。

[root@tivf09 root]# ps -ef|grep top
root      5180  5128  0 01:03 pts/0    00:00:02 top
root      5857  3672  0 01:12 pts/2    00:00:00 grep top

使用pstree命令可以更清楚地看到这个关系:

[root@tivf09 root]# pstree -H 5180|grep top
|-sshd-+-sshd---bash---top

使用ps-xj命令可以看到,登录shell(PID 5128)和top在同一个会话期,shell为会话期首进程,所在进程组PGID为5128,top所在进程组PGID为5180,为前台进程组。

[root@tivf09 root]# ps -xj|grep 5128
 5126  5128  5128  5128 pts/0     5180 S        0   0:00 -bash
 5128  5180  5180  5128 pts/0     5180 S        0   0:50 top
 3672 18095 18094  3672 pts/2    18094 S        0   0:00 grep 5128

关闭第一个SSH窗口,在另一个窗口中可以看到top也被杀掉了。

[root@tivf09 root]# ps -ef|grep 5128
root     18699  3672  0 04:35 pts/2    00:00:00 grep 5128

如果我们可以忽略SIGHUP信号,关掉窗口应该就不会影响程序的运行了。nohup命令可以达到这个目的,如果程序的标准输出/标准错误是终端,nohup默认将其重定向到nohup.out文件。值得注意的是nohup命令只是使得程序忽略SIGHUP信号,还需要使用标记&把它放在后台运行。

nohup <command> [argument…] &

虽然nohup很容易使用,但还是比较“简陋”的,对于简单的命令能够应付过来,对于复杂的需要人机交互的任务就麻烦了。

其实我们可以使用一个更为强大的实用程序screen。流行的Linux发行版(例如Red Hat Enterprise Linux 4)通常会自带screen实用程序,如果没有的话,可以从GNU screen的官方网站下载。

[root@tivf06 ~]# rpm -qa|grep screen
xscreensaver-4.18-5.rhel4.11
screen-4.0.2-5

开始使用Screen

# 先在 服务器端 安装 screen
[root@ttsdy ~]# yum install screen

简单来说,Screen是一个可以在多个进程之间多路复用一个物理终端的窗口管理器。Screen中有会话的概念,用户可以在一个screen会话中创建多个screen窗口,在每一个screen窗口中就像操作一个真实的telnet/SSH连接窗口那样。在screen中创建一个新的窗口有这样几种方式:

1、直接在命令行键入screen命令

[root@tivf06 ~]# screen

Screen将创建一个执行shell的全屏窗口。你可以执行任意shell程序,就像在ssh窗口中那样。在该窗口中键入exit退出该窗口,如果这是该screen会话的唯一窗口,该screen会话退出,否则screen自动切换到前一个窗口。

2、Screen命令后跟你要执行的程序。

[root@tivf06 ~]# screen vi test.c

Screen创建一个执行vi test.c的单窗口会话,退出vi将退出该窗口/会话。

3、以上两种方式都创建新的screen会话。我们还可以在一个已有screen会话中创建新的窗口。在当前screen窗口中键入C-a c,即Ctrl键+a键,之后再按下c键,screen 在该会话内生成一个新的窗口并切换到该窗口。

screen还有更高级的功能。你可以不中断screen窗口中程序的运行而暂时断开(detach)screen会话,并在随后时间重新连接(attach)该会话,重新控制各窗口中运行的程序。例如,我们打开一个screen窗口编辑/tmp/abc文件:

[root@tivf06 ~]# screen vi /tmp/abc

之后我们想暂时退出做点别的事情,比如出去散散步,那么在screen窗口键入C-a d,Screen会给出detached提示:

暂时中断会话

未分类

半个小时之后回来了,找到该screen会话:

[root@tivf06 ~]# screen -ls
There is a screen on:
        16582.pts-1.tivf06      (Detached)
1 Socket in /tmp/screens/S-root.

重新连接会话:

[root@tivf06 ~]# screen -r 16582

看看出现什么了,太棒了,一切都在。继续干吧。

你可能注意到给screen发送命令使用了特殊的键组合C-a。这是因为我们在键盘上键入的信息是直接发送给当前screen窗口,必须用其他方式向screen窗口管理器发出命令,默认情况下,screen接收以C-a开始的命令。这种命令形式在screen中叫做键绑定(key binding),C-a叫做命令字符(command character)。

可以通过C-a ?来查看所有的键绑定,常用的键绑定有:

未分类

Screen常用选项

使用键绑定C-a ?命令可以看到, 默认的命令字符(Command key)为C-a,转义C-a(literal ^a)的字符为a:

Screen 常用选项

未分类

因为screen把C-a看作是screen命令的开始,所以如果你想要screen窗口接收到C-a字符,就要输入C-a a。Screen也允许你使用-e选项设置自己的命令字符和转义字符,其格式为:

-exy x为命令字符,y为转义命令字符的字符

下面命令启动的screen会话指定了命令字符为C-t,转义C-t的字符为t,通过C-t ?命令可以看到该变化。

[root@tivf18 root]# screen -e^tt

自定义命令字符和转义字符

未分类

其他常用的命令选项有:

未分类

下例显示当前有两个处于detached状态的screen会话,你可以使用screen -r 重新连接上:

[root@tivf18 root]# screen –ls
There are screens on:
        8736.pts-1.tivf18       (Detached)
        8462.pts-0.tivf18       (Detached)
2 Sockets in /root/.screen.

[root@tivf18 root]# screen –r 8736

如果由于某种原因其中一个会话死掉了(例如人为杀掉该会话),这时screen -list会显示该会话为dead状态。使用screen -wipe命令清除该会话:

[root@tivf18 root]# kill -9 8462
[root@tivf18 root]# screen -ls  
There are screens on:
        8736.pts-1.tivf18       (Detached)
        8462.pts-0.tivf18       (Dead ???)
Remove dead screens with 'screen -wipe'.
2 Sockets in /root/.screen.

[root@tivf18 root]# screen -wipe
There are screens on:
        8736.pts-1.tivf18       (Detached)
        8462.pts-0.tivf18       (Removed)
1 socket wiped out.
1 Socket in /root/.screen.

[root@tivf18 root]# screen -ls  
There is a screen on:
        8736.pts-1.tivf18       (Detached)
1 Socket in /root/.screen.

[root@tivf18 root]#

-d –m 选项是一对很有意思的搭档。他们启动一个开始就处于断开模式的会话。你可以在随后需要的时候连接上该会话。有时候这是一个很有用的功能,比如我们可以使用它调试后台程序。该选项一个更常用的搭配是:-dmS sessionname

启动一个初始状态断开的screen会话:

[root@tivf06 tianq]# screen -dmS mygdb gdb execlp_test

连接该会话:

[root@tivf06 tianq]# screen -r mygdb

管理你的远程会话

先来看看如何使用screen解决SIGHUP问题,比如现在我们要ftp传输一个大文件。如果按老的办法,SSH登录到系统,直接ftp命令开始传输,之后。。如果网络速度还可以,恭喜你,不用等太长时间了;如果网络不好,老老实实等着吧,只能传输完毕再断开SSH连接了。让我们使用screen来试试。

SSH登录到系统,在命令行键入screen。

[root@tivf18 root]# screen

在screen shell窗口中输入ftp命令,登录,开始传输。不愿意等了?OK,在窗口中键入C-a d:

管理你的远程会话

未分类

然后。。退出SSH登录?随你怎样,只要别杀掉screen会话。

是不是很方便?更进一步,其实我们可以利用screen这种功能来管理你的远程会话,保存你所有的工作内容。你是不是每次登录到系统都要开很多窗口,然后每天都要重复打开关闭这些窗口?让screen来帮你“保存”吧,你只需要打开一个ssh窗口,创建需要的screen窗口,退出的时候C-a d“保存”你的工作,下次登录后直接screen -r 就可以了。

最好能给每个窗口起一个名字,这样好记些。使用C-a A给窗口起名字。使用C-a w可以看到这些窗口名字,可能名字出现的位置不同。使用putty:

putty

未分类

使用telnet:

telnet

未分类

更多Screen功能

Screen提供了丰富强大的定制功能。你可以在Screen的默认两级配置文件/etc/screenrc和$HOME/.screenrc中指定更多,例如设定screen选项,定制绑定键,设定screen会话自启动窗口,启用多用户模式,定制用户访问权限控制等等。如果你愿意的话,也可以自己指定screen配置文件。

以多用户功能为例,screen默认是以单用户模式运行的,你需要在配置文件中指定multiuser on 来打开多用户模式,通过acl*(acladd,acldel,aclchg…)命令,你可以灵活配置其他用户访问你的screen会话。更多配置文件内容请参考screen的man页。

参考资料

  • “Advanced Programming in the UNIX® Environment: Second Edition” W. Richard Stevens, Stephen A. Rago 提供了更多关于Linux/Unix进程关系、信号的知识。

  • GNU Screen的官方网站:http://www.gnu.org/software/screen/

  • Screen的man page提供了最详细的信息:http://www.slac.stanford.edu/comp/unix/package/epics/extensions/iocConsole/screen.1.html

Linux ssh双向免密认证

一、实现原理

使用一种被称为”公私钥”认证的方式来进行ssh登录。”公私钥”认证方式简单的解释是:
首先在客户端上创建一对公私钥(公钥文件:~/.ssh/id_rsa.pub;私钥文件:~/.ssh/id_rsa),然后把公钥放到服务器上(~/.ssh/authorized_keys),自己保留好私钥。当ssh登录时,ssh程序会发送私钥去和服务器上的公钥做匹配。如果匹配成功就可以登录了。

二、实验环境

node1机:192.168.5.10
node2机:192.168.5.20

三、Linux/Unix双机建立信任

3.1 在node1机生成证书

在node1机root用户下执行ssh-keygen命令,在需要输入的地方,直接回车,生成建立安全信任关系的证书。

[root@node1 ~]# ssh-keygen -t rsa

未分类

注意:在程序提示输入passphrase时直接输入回车,表示无证书密码。
   
上述命令将生成私钥证书id_rsa和公钥证书id_rsa.pub,存放在用户家目录的.ssh子目录中。

3.2 查看生成密钥的文件

[root@node1 ~]# ll  /root/.ssh/

未分类

3.3 node1对node2建立信任关系

将公钥证书id_rsa.pub复制到机器node2的root家目录的.ssh子目录中,同时将文件名更换为authorized_keys,此时需要输入node2机的root用户密码(还未建立信任关系)。建立了客户端到服务器端的信任关系后,客户端就可以不用再输入密码,就可以从服务器端拷贝数据了。

[root@node1 ~]# scp -r /root/.ssh/id_rsa.pub 192.168.5.20:/root/.ssh/authorized_keys

未分类

3.4 node2对node1建立信任关系

在node2机上执行同样的操作,建立node2对node1的信任关系。

[root@node2 ~]# ssh-keygen -t rsa

未分类

查看文件

[root@node2 ~]# ll  /root/.ssh/

未分类

[root@node2 ~]# scp -r /root/.ssh/id_rsa.pub 192.168.5.10:/root/.ssh/authorized_keys

未分类

注意:记得修改authorized_keys权限

[root@node1 ~]# chmod 600 /root/.ssh/authorized_keys

四、测试

在node1机上:

[root@node1 ~]# scp /opt/test.txt 192.168.5.20:/opt/

未分类

在node2机上:

[root@node2 ~]# scp /opt/test22.txt 192.168.5.10:/opt/

未分类

五、远程执行命令

命令格式:ssh 远程用户名@远程主机IP地址 ‘远程命令或者脚本’

[root@node2 ~]# ssh [email protected] 'hostname'

未分类

用 Go 写一个轻量级的 ssh 批量操作工具

前言

这是一个轮子。

大家都知道 Ansible 是功能超级强大的自动化运维工具,十分的高大上。太高大上了以至于在低端运维有点水土不服,在于三点:

  1. Ansible 是基于 Python 的,而 Python 下的安装是有一堆依赖的。。。不要笑!对于很多使用 Win 的用户而言,光是装 Python, 装 pip 就够喝一壶的了。

  2. Ansible 的 paybook 所使用的 yaml 语法当然非常强大了。然而对于新人而言,刚入手是玩不转的,需要学习。虽然 Ansible 相比其他的自动化运维工具,它的学习曲线已经非常平易近人了,但毕竟还是要学一下的不是么

  3. Ansible 自动化运维 Linux 服务器得益于 Linux 上 python 的默认支持,功能非常强大。然而如果拿来跑交换机的话,因为交换机上通常没有 python 环境,功能就要打很多折扣了。基本上也就是执行一系列的命令组合。而我们这种有大片园区网的传统单位,运维的大头正式是交换机~

所以造这个轮子的出发点是基于以下考虑的:

  1. 要跨平台,木有依赖,开箱即用。用 Go 来撸一个就能很好的满足这个需求。你看 Open-Falcon 的 agent,ELK 的 beats ,都选择用 Go 来实现,就是这个原因。

  2. 简单无脑,无需学习。直接堆砌命令行就行,就像我们初始化交换机的那种命令行组合模板。只要 cli 会玩,直接照搬过来就行。

  3. 要支持并发。这个是 Go 的强项了,无需多言。

  4. 最后当然是学习 Go 啦。

一点都没有黑 Ansible 的意思。我们也有在用 Ansible 来做自动化运维的工作,我觉得所有运维最好都学习下 Ansible,将来总是要往自动化的方向走的。这个轮子的目的在于学习 Ansible 之前,先有个够简单无脑的工具解决下眼前的需求~

建立 ssh 会话

Go 自身不带 ssh 包。他的 ssh 包放在了 https://godoc.org/golang.org/x/crypto/ssh 这里。import 他就好

import "golang.org/x/crypto/ssh"

首先我们需要建立一个 ssh 会话,比如这样。

func connect(user, password, host, key string, port int, cipherList []string) (*ssh.Session, error) {
    var (
        auth         []ssh.AuthMethod
        addr         string
        clientConfig *ssh.ClientConfig
        client       *ssh.Client
        config       ssh.Config
        session      *ssh.Session
        err          error
    )
    // get auth method
    auth = make([]ssh.AuthMethod, 0)
    if key == "" {
        auth = append(auth, ssh.Password(password))
    } else {
        pemBytes, err := ioutil.ReadFile(key)
        if err != nil {
            return nil, err
        }

        var signer ssh.Signer
        if password == "" {
            signer, err = ssh.ParsePrivateKey(pemBytes)
        } else {
            signer, err = ssh.ParsePrivateKeyWithPassphrase(pemBytes, []byte(password))
        }
        if err != nil {
            return nil, err
        }
        auth = append(auth, ssh.PublicKeys(signer))
    }

    if len(cipherList) == 0 {
        config = ssh.Config{
            Ciphers: []string{"aes128-ctr", "aes192-ctr", "aes256-ctr", "[email protected]", "arcfour256", "arcfour128", "aes128-cbc", "3des-cbc", "aes192-cbc", "aes256-cbc"},
        }
    } else {
        config = ssh.Config{
            Ciphers: cipherList,
        }
    }

    clientConfig = &ssh.ClientConfig{
        User:    user,
        Auth:    auth,
        Timeout: 30 * time.Second,
        Config:  config,
        HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
            return nil
        },
    }

    // 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
    }

    modes := ssh.TerminalModes{
        ssh.ECHO:          0,     // disable echoing
        ssh.TTY_OP_ISPEED: 14400, // input speed = 14.4kbaud
        ssh.TTY_OP_OSPEED: 14400, // output speed = 14.4kbaud
    }

    if err := session.RequestPty("xterm", 80, 40, modes); err != nil {
        return nil, err
    }

    return session, nil
}

ssh.AuthMethod 里存放了 ssh 的认证方式。使用密码认证的话,就用 ssh.Password()来加载密码。使用密钥认证的话,就用 ssh.ParsePrivateKey() 或 ssh.ParsePrivateKeyWithPassphrase() 读取密钥,然后通过 ssh.PublicKeys() 加载进去。

ssh.config 这个 struct 存了 ssh 的配置参数,他有以下几个配置选项,以下引用自GoDoc 。

type Config struct {
    // Rand provides the source of entropy for cryptographic
    // primitives. If Rand is nil, the cryptographic random reader
    // in package crypto/rand will be used.
    // 加密时用的种子。默认就好
    Rand io.Reader

    // The maximum number of bytes sent or received after which a
    // new key is negotiated. It must be at least 256. If
    // unspecified, a size suitable for the chosen cipher is used.
    // 密钥协商后的最大传输字节,默认就好
    RekeyThreshold uint64

    // The allowed key exchanges algorithms. If unspecified then a
    // default set of algorithms is used.
    // 
    KeyExchanges []string

    // The allowed cipher algorithms. If unspecified then a sensible
    // default is used.
    // 连接所允许的加密算法
    Ciphers []string

    // The allowed MAC algorithms. If unspecified then a sensible default
    // is used.
    // 连接允许的 MAC (Message Authentication Code 消息摘要)算法,默认就好
    MACs []string
}

基本上默认的就好啦。但是 Ciphers 需要修改下,默认配置下 Go 的 SSH 包提供的 Ciphers 包含以下加密方式

aes128-ctr aes192-ctr aes256-ctr [email protected] arcfour256 arcfour128

连 linux 通常没有问题,但是很多交换机其实默认只提供 aes128-cbc 3des-cbc aes192-cbc aes256-cbc 这些。因此我们还是加全一点比较好。

这里有两个地方要提一下

1、在 clientConfig 里有这么一段

HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error {
    return nil
},

这是因为默认密钥不受信任时,Go 的 ssh 包会在 HostKeyCallback 里把连接干掉(1.8 之后加的应该)。但是我们使用用户名密码连接的时候,这个太正常了不是么,所以让他 return nil 就好了。

2、在 NewSession() 后,我们定义了 modes 和 RequestPty。这是因为为之后使用 session.Shell() 模拟终端时,所建立的终端参数。如果不配的话,默认值可能导致在某些终端上执行失败。例如一些 H3C 的交换机,连接建立后默认推出来的 Copyright 可能会导致 ssh 连接异常,然后超时或者直接断掉。例如这样:

******************************************************************************
* Copyright (c) 2004-2016 Hangzhou H3C Tech. Co., Ltd. All rights reserved.  *
* Without the owner's prior written consent,                                 *
* no decompiling or reverse-engineering shall be allowed.                    *
******************************************************************************

配置的参数照搬 GoDoc 上的示例就好了:

// Set up terminal modes
modes := ssh.TerminalModes{
    ssh.ECHO:          0,     // disable 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", 40, 80, modes); err != nil {
    log.Fatal("request for pseudo terminal failed: ", err)
}

执行命令

建立起 session 后,执行命令就很简单了,用 session.Run() 就可以执行我们的命令,结果则返回到 session.Studout 里。我们跑个简单的测试。

const (
    username = "admin"
    password = "password"
    ip       = "192.168.15.101"
    port     = 22
    cmd      = "show clock"
)

func Test_SSH_run(t *testing.T) {
    ciphers := []string{}
    session, err := connect(username, password, ip, port, ciphers)
    if err != nil {
        t.Error(err)
        return
    }
    defer session.Close()
    var stdoutBuf bytes.Buffer
    session.Stdout = &stdoutBuf
    session.Run(cmd)
    t.Log(session.Stdout)
    return
}

目标是一台交换机,测试一下

=== RUN   Test_SSH_run
--- PASS: Test_SSH_run (0.69s)
    ssh_test.go:30: 07:55:52.598 UTC Wed Jan 17 2018
PASS

可以看到 show clock 的命令已经成功执行了,并返回了结果。

session.Run() 仅限定执行单条命令,要执行若干命令组合就需要用到 session.Shell() 了。意思很明确,就是模拟一个终端去一条一条执行命令,并返回结果。就像我们用 Shell 一样,我们把整过过程打印出来输出就好了。从 session.StdinPipe() 逐个输入命令,从session.Stdout 和 session.Stderr 获取 Shell 上的输出。一样来做个测试。

const (
    username = "admin"
    password = "password"
    ip       = "192.168.15.101"
    port     = 22
    cmds     = "show clock;show env power;exit"
)
func Test_SSH(t *testing.T) {
    var cipherList []string
    session, err := connect(username, password, ip, key, port, cipherList)
    if err != nil {
        t.Error(err)
        return
    }
    defer session.Close()

    cmdlist := strings.Split(cmd, ";")
    stdinBuf, err := session.StdinPipe()
    if err != nil {
        t.Error(err)
        return
    }

    var outbt, errbt bytes.Buffer
    session.Stdout = &outbt

    session.Stderr = &errbt
    err = session.Shell()
    if err != nil {
        t.Error(err)
        return
    }
    for _, c := range cmdlist {
        c = c + "n"
        stdinBuf.Write([]byte(c))

    }
    session.Wait()
    t.Log((outbt.String() + errbt.String()))
    return
}

还是那台交换机,测试一下

=== RUN   Test_SSH
--- PASS: Test_SSH (0.69s)
    ssh_test.go:51: sw-1#show clock
        07:59:52.598 UTC Wed Jan 17 2018
        sw-1#show env power
        SW  PID                 Serial#     Status           Sys Pwr  PoE Pwr  Watts
        --  ------------------  ----------  ---------------  -------  -------  -----
         1  Built-in                                         Good

        sw-1#exit
PASS

可以看到,两个命令都得到执行了,并在执行完 exit 后退出连接。

比较一下和 session.Run() 的区别,可以发现在 session.Shell() 模式下,输出的内容包含了主机的名字,输入的命令等等。因为这是 tty 执行的结果嘛。如果我们只需要执行命令倒也无所谓,但是如果我们还需要从执行命令的结果中读取一些信息,这些内容就显得有些臃肿了。比如我们在一台 ubuntu 上跑一下看看

=== RUN   Test_SSH
--- PASS: Test_SSH (0.98s)
        ssh_test.go:50: Welcome to Ubuntu 16.04.3 LTS (GNU/Linux 4.4.0-98-generic x86_64)

                 * Documentation:  https://help.ubuntu.com
                 * Management:     https://landscape.canonical.com
                 * Support:        https://ubuntu.com/advantage

                  System information as of Thu Jan 18 16:34:56 CST 2018

                  System load:  0.0                Processes:              335
                  Usage of /:   10.0% of 90.18GB   Users logged in:        0
                  Memory usage: 2%                 IP address for eth0:    192.168.80.131
                  Swap usage:   0%                 IP address for docker0: 172.17.0.1

                  Graph this data and manage this system at:
                    https://landscape.canonical.com/

                16 个可升级软件包。
                16 个安全更新。

                New release '17.10' available.
                Run 'do-release-upgrade' to upgrade to it.

                You have new mail.
                Last login: Thu Jan 18 16:31:41 2018 from 192.168.95.104
                root@ubuntu-docker-node3:~# root@ubuntu-docker-node3:/opt# /opt
                root@ubuntu-docker-node3:/opt# 注销

最起码,上面那一堆 System information 就用不着嘛。交换机是没有办法,Linux 上能不能通过一条命令,也就是想办法 session.Run() 来执行命令组合呢?

答案是可以的,把命令通过 && 连接起来就好了嘛。LInux 的 Shell 会帮我们拆开来分别运行的,比如上面的这个命令我们就可以合并成一条命令 cd /opt&&pwd&&exit

=== RUN   Test_SSH_run
--- PASS: Test_SSH_run (0.91s)
    ssh_test.go:76: /opt

立马就简洁了对不对?

轮子

ssh 执行命令这样就差不多了。要变成一个可以用 ssh 批量操作工具,我们还要给他加上并发执行,并发限制,超时控制,输入参数解析,输出格式等等

这里就不展开了,最终这个造出来的轮子长这样:https://github.com/shanghai-edu/multissh

可以直接命令行来执行,通过 ; 号或者 , 号作为命令和主机的分隔符。

# ./multissh -cmds "show clock" -hosts "192.168.31.21;192.168.15.102" -u admin -p password

也可以通过文本来存放主机组和命令组,通过换行符分隔。

# ./multissh -cmdfile cmd1.txt.example -hostfile host.txt.example -u admin -p password

特别的,如果输入的是 IP (-ips 或 -ipfile),那么允许 IP 地址段方式的输入,例如 192.168.15.101-192.168.15.110 。(还记得 swcollector 么,类似的实现方式)

# ./multissh -cmds "show clock" -ips "192.168.15.101-192.168.15.110" -u admin -p password

支持使用 ssh 密钥认证,此时如果输入 password ,则为作为 key 的密码

# ./multissh -hosts "192.168.80.131" -cmds "date;cd /opt;ls" -u root -k "server.key"

对于 linux ,支持 linuxMode 模式,也就是将命令组合通过 && 连接后,使用 se
ssion.Run() 运行。

# ./multissh -hosts "192.168.80.131" -cmds "date;cd /opt;ls" -u root -k "server.key" -l

也可以为每个主机定义不同的配置参数,以 json 格式加载配置。

# ./multissh -c ssh.json.example

输出可以打成 json 格式,方便程序处理。

# ./multissh -c ssh.json.example -j

也可以把输出结果存到以主机名命名的文本中,比如用来做配置备份

# ./multissh -c ssh.json.example -outTxt

参考文档

golang.org/x/crypto/ssh

golang-ssh-how-to-run-multiple-commands-on-the-same-session

golang-enter-ssh-sudo-password-on-prompt-or-exit

git-ssh 配置和使用

1、设置Git的user name和email:(如果是第一次的话)

$ git config --global user.name "humingx"
$ git config --global user.email "[email protected]"

2、生成密钥

$ ssh-keygen -t rsa -C "[email protected]"

连续3个回车。如果不需要密码的话。
最后得到了两个文件:id_rsa和id_rsa.pub。

未分类

如果不是第一次,就选择overwrite.

未分类

3、添加密钥到ssh-agent

确保 ssh-agent 是可用的。ssh-agent是一种控制用来保存公钥身份验证所使用的私钥的程序,其实ssh-agent就是一个密钥管理器,运行ssh-agent以后,使用ssh-add将私钥交给ssh-agent保管,其他程序需要身份验证的时候可以将验证申请交给ssh-agent来完成整个认证过程。

# start the ssh-agent in the background
eval "$(ssh-agent -s)"
Agent pid 59566

添加生成的 SSH key 到 ssh-agent。

$ ssh-add ~/.ssh/id_rsa

未分类

4、登陆Github, 添加 ssh

把id_rsa.pub文件里的内容复制到这里

未分类

未分类

未分类

未分类

未分类

5、测试

$ ssh -T [email protected]

你将会看到:

The authenticity of host 'github.com (207.97.227.239)' can't be established.
RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48.
Are you sure you want to continue connecting (yes/no)?

选择 yes

Hi humingx! You've successfully authenticated, but GitHub does not provide shell access.

如果看到Hi后面是你的用户名,就说明成功了。

未分类

6、修改.git文件夹下config中的url

修改前

[remote "origin"]
url = https://github.com/humingx/humingx.github.io.git
fetch = +refs/heads/*:refs/remotes/origin/*

修改后

[remote "origin"]
url = [email protected]:humingx/humingx.github.io.git
fetch = +refs/heads/*:refs/remotes/origin/*

7、发布

未分类

stunnel+squid搭建代理服务器

一、网络环境

  • 主机A :192.168.0.11

  • 主机B:66.0.0.6

  • 主机C:4.2.2.2

主机A和B互通,B和C互通,A访问C网络较慢或不通,可以通过stunnel+squid代理跳转访问。

二、squid 安装配置

squid和stunnel可以在主机B上配置,也可在不同主机配置实现网络跳转。这里squid和stunnel server在主机B配置,stunnel client 在客户端主机A配置

  • 安装 yum install squid

  • 配置 vim /etc/squid/squid.conf,主要配置如下两处

acl localnet src 66.0.0.6/32  # 根据实际情况修改,添加允许 stunnel-client 的ip地址
http_port 3128  # squid监听端口

启动服务 service squid start

三、stunnel 配置

  • 安装yum -y install stunnel openssl openssl-devel

1、stunnel server 配置

  • 生成证书认证文件
openssl req -new -x509 -days 365 -nodes -out stunnel.pem -keyout stunnel.pem
openssl gendh 512>> stunnel.pem   #不是必须的
  • 配置

vim /etc/stunnel/stunnel_ser.conf (;;; 注释形式)

cert = /etc/stunnel/stunnel.pem   ;;;# 认证文件
CAfile = /etc/stunnel/stunnel.pem  ;;;# 认证文件
socket = l:TCP_NODELAY=1
socket = r:TCP_NODELAY=1
;;;chroot = /var/run/stunnel
pid = /tmp/stunnel_server.pid
verify = 3
;;; CApath = certs
;;; CRLpath = crls
;;; CRLfile = crls.pem
setuid = web
setgid = web
;;; client=yes
compression = zlib
;;; taskbar = no
delay = no
;;; failover = rr
;;; failover = prio
;;; sslVersion = TLSv1
;;; fips=no
sslVersion = all
;;; options = NO_SSLv2
;;; options = NO_SSLv3
debug = 7
syslog = no
output = /var/logs/stunnel_server.log
client = no  ;;;# 服务端
[sproxy]
accept = 44550  ;;;# 监听端口
connect = 66.0.0.6:3128  ;;;# squid服务连接端口
  • 启动服务 stunnel /etc/stunnel/stunnel_ser.conf

2、squid client 安装配置

yum -y install stunnel openssl openssl-devel
vim  /etc/stunnel/stunnel_cli.conf 

cert = /usr/local/etc/stunnel/stunnel_cli.pem  ;;;#步骤1中生成的stunnel.pem,改了名字而已
CAfile = /usr/local/etc/stunnel/stunnel_cli.pem
socket = l:TCP_NODELAY=1
socket = r:TCP_NODELAY=1

;;;chroot = /var/run/stunnel
pid = /tmp/stunnel.pid
verify = 3

;;; CApath = certs
;;; CRLpath = crls
;;; CRLfile = crls.pem

setuid = web
setgid = web

;;; client=yes
compression = zlib
;;; taskbar = no
delay = no
;;; failover = rr
;;; failover = prio
;;; fips=no
sslVersion = all
;;; options = NO_SSLv2
;;; options = NO_SSLv3

debug = 7
syslog = no
output = /data/logs/stunnel.log
client = yes   ;;;# 客户端

[sproxy]
accept = 0.0.0.0:44550  ;;;# 监听地址
connect = 66.0.0.6:44550  ;;;# stunnel 服务端地址

四、测试及错误解决

  • 测试:配置代理服务器地址:192.168.0.11,端口44550后,可以访问主机C
  • 错误解决:
stunnel 报错:CERT: Verification error: certificate has expired

stunnel客户端连不上服务端,连上几秒就断开了,具体报错信息如下

# stunnel 客户端:
2017.09.25 10:16:19 LOG7[13955:140155381970688]: Starting certificate verification: depth=0, /C=CN/L=Default City/O=Default Company Ltd
2017.09.25 10:16:19 LOG4[13955:140155381970688]: CERT: Verification error: certificate has expired
2017.09.25 10:16:19 LOG4[13955:140155381970688]: Certificate check failed: depth=0, /C=CN/L=Default City/O=Default Company Ltd
2017.09.25 10:16:19 LOG7[13955:140155381970688]: SSL alert (write): fatal: certificate expired
2017.09.25 10:16:19 LOG3[13955:140155381970688]: SSL_connect: 14090086: error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed
2017.09.25 10:16:19 LOG5[13955:140155381970688]: Connection reset: 0 byte(s) sent to SSL, 0 byte(s) sent to socket
2017.09.25 10:16:19 LOG7[13955:140155381970688]: Remote socket (FD=13) closed
2017.09.25 10:16:19 LOG7[13955:140155381970688]: Local socket (FD=3) closed
2017.09.25 10:16:19 LOG7[13955:140155381970688]: Service [sproxy] finished (0 left)

# stunnel 服务端:
2017.09.25 10:13:24 LOG7[15546:140344803059456]: SSL state (accept): SSLv3 flush data
2017.09.25 10:13:24 LOG7[15546:140344803059456]: SSL alert (read): fatal: certificate expired
2017.09.25 10:13:24 LOG3[15546:140344803059456]: SSL_accept: 14094415: error:14094415:SSL routines:SSL3_READ_BYTES:sslv3 alert certificate expired
2017.09.25 10:13:24 LOG5[15546:140344803059456]: Connection reset: 0 bytes sent to SSL, 0 bytes sent to socket
2017.09.25 10:13:24 LOG7[15546:140344803059456]: sproxy finished (0 left)

需要安装上面的证书生成命令,重新生成证书后手动更新

openssl req -new -x509 -days 365 -nodes -out stunnel.pem -keyout stunnel.pem

squid代理服务器

squid缓存代理概述

——–搭建代理服务器可以提高上网打开网页的速度,可以对非法网站进行屏蔽,限制文件下载,以及查看员工访问internet的情况,包括上网的地址,用户,时间等,是企业网络管理的常用手段。
———–代理服务器分为传统代理和透明代理,传统代理适用于浏览internet,需要在浏览器上手工指定服务器地址和端口,不是很方便,但是可以隐藏本机真实的ip地址,而且为下载工具使用多个代理可以规避服务器的并发连接显示。透明代理适用于共享上网网关,不需要指定服务器地址和端口,无需额外的设置即可上网,在实际工作中透明代理较多。

未分类

未分类

squid安装及运行控制

(一)传统代理

未分类

——配置并启用squid服务(主机B)

未分类

未分类

未分类

未分类

未分类

未分类

未分类

未分类

未分类

未分类

未分类

未分类

——–在web服务器打开网站(第一台)

未分类

——–设置代理客户端(主机C)
——– 测试代理Web访问(主机C)

未分类

未分类

—–关闭代理服务的时候

未分类

未分类

—-验证代理服务器
在客户机访问网站,然后查看web服务器的访问日志,发现客户机172.16.16.110访问网站172.16.16.172的记录,但是在web服务器中,查看网站日志文件,显示的访问者是代理服务器的地址172.16.16.22,不是客户端的地址。

未分类

未分类

(二)透明代理,常用。

未分类

未分类

未分类

未分类

未分类

—-或者vim /sysctl.conf sysctl -p —

未分类

未分类

未分类

未分类

—–验证透明代理—

未分类

未分类

设置ACL访问控制

未分类

未分类

未分类

未分类

未分类

未分类

未分类

未分类

squid日志分析

未分类

未分类

未分类

未分类

未分类

未分类

未分类

正则表达式sed

sed 能实现grep查找的功能,还可以替换指定的字符。

匹配查找文件中root字符(其中的 -n表示段落,p表示打印出来print)

[root@localhost sed]# sed -n '/root/'p test.txt 

root:x:0:0:root:/root:/bin/bash

operator:x:11:0:operator:/root:/sbin/nologin

. 点 (匹配任意一个字符)

[root@localhost sed]# sed  -n '/r.t/'p test.txt 

operator:x:11:0:operator:/root:/sbin/nologin

sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin

* (匹配一个或多个字符)

[root@localhost sed]# sed -n '/r*t/'p test.txt 

root:x:0:0:root:/root:/bin/bash

shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown

halt:x:7:0:halt:/sbin:/sbin/halt

operator:x:11:0:operator:/root:/sbin/nologin

ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin

systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin

dbus:x:81:81:System message bus:/:/sbin/nologin

polkitd:x:999:997:User for polkitd:/:/sbin/nologin

postfix:x:89:89::/var/spool/postfix:/sbin/n1ogin

sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin

+ 加号表示一个或者多个字符

[root@localhost sed]# sed -n '/r+t/'p test.txt 

不脱义,需要加一个-r

[root@localhost sed]# sed -nr '/r+t/'p test.txt 
[root@localhost sed]# sed -nr '/o+t/'p test.txt 

root:x:0:0:root:/root:/bin/bash

operator:x:11:0:operator:/root:/sbin/nologin

显示第2行内容

[root@localhost sed]# sed -n '2'p test.txt 

bin:x:1:1:bin:/bin:/sbin/NOLOGIN

显示2到5行

[root@localhost sed]# sed  -n '2,5'p test.txt 

bin:x:1:1:bin:/bin:/sbin/NOLOGIN

daemon:x:2:2:daemon:/sbin:/sbin/nologin

adm:x:3:4:adm:/var/adm:/sbin/nologin

lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

显示5到后面的行 5,;;表示后面的行

[root@localhost sed]# sed -n '5,$'p test.txt 

显示全部内容 1,$

[root@localhost sed]# sed -n '1,$'p test.txt 

显示第一行 和 匹配bus字符 用-e 可以执行多步操作 -n是显示段落 p是print打印输出

[root@localhost sed]# sed -e '1'p -e '/bus/'p  -n test.txt 

root:x:0:0:root:/root:/bin/bash

dbus:x:81:81:System message bus:/:/sbin/nologin

显示第一段,匹配root字符,匹配oo字符

[root@localhost sed]# sed -e '1'p  -e '/root/'p -e '/oo/'p -n test.txt 

root:x:0:0:root:/root:/bin/bash

root:x:0:0:root:/root:/bin/bash

root:x:0:0:root:/root:/bin/bash

lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

mail:x:8:12:mail:/var/spool/mail:/sbin/nologin

operator:x:11:0:operator:/root:/sbin/nologin

operator:x:11:0:operator:/root:/sbin/nologin

ooooqq

osdaoooo

postfix:x:89:89::/var/spool/postfix:/sbin/n1ogin

查找bus字符所在的行

[root@localhost sed]# sed -n '/bus/'p test.txt 

dbus:x:81:81:System message bus:/:/sbin/nologin

不区分大小写查找bus字符 (I 表示不区分大小写 grep -i 不区分大小写查找)

[root@localhost sed]# sed -n '/bus/'Ip test.txt 

dbus:x:81:81:System message bus:/:/sbin/nologin

polkitd:x:999:997:User for BUSpolkitd:/:/sbin/nologin

postfix:x:89:89::/var/spool/posBustfix:/sbin/nologin

删除1,10行,列出剩下的行(d 是delete删除) 不删除原来文件里面的内容,只是

显示的时候删除1到10行,显示剩下的内容。

[root@localhost sed]# sed '1,10'd test.txt 

games:x:12:100:games:/usr/games:/sbin/nologin

ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin

nobody:x:99:99:Nobody:/:/sbin/nologin

systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin

dbus:x:81:81:System message bus:/:/sbin/nologin

polkitd:x:999:997:User for BUSpolkitd:/:/sbin/nologin

postfix:x:89:89::/var/spool/posBustfix:/sbin/nologin

chrony:x:998:996::/var/lib/chrony:/sbin/nologin

sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin

xiaobo:x:1000:1000::/home/xiaobo:/bin/bash

删除1到25行内容,会删除原文件里面的内容! (-i)

[root@localhost sed]# sed  -i '1,25'd test.txt 

删除原文件里面的user2相关的行

[root@localhost sed]# sed -i '/user2/'d test.txt 

替换文件中1到10行 root字符替换成toor字符; s表示字符串;g(globle)全局

[root@localhost sed]# sed '1,10s/root/toor/g' test.txt 

可以使用正则表达式,将正则表达式的内容进行替换

替换 ro+ (正则表达式 加号表示1个或者多个字符) 把ro+ 替换为r

[root@localhost sed]# sed  -r '1,10s/ro+/r/g' test.txt |head 

rt:x:0:0:rt:/rt:/bin/bash

bin:x:1:1:bin:/bin:/sbin/nologin

daemon:x:2:2:daemon:/sbin:/sbin/nologin

adm:x:3:4:adm:/var/adm:/sbin/nologin

lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

sync:x:5:0:sync:/sbin:/bin/sync

shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown

halt:x:7:0:halt:/sbin:/sbin/halt

mail:x:8:12:mail:/var/spool/mail:/sbin/nologin

operator:x:11:0:operator:/rt:/sbin/nologin

head 显示

[root@localhost sed]# head test.txt 

root:x:0:0:root:/root:/bin/bash

bin:x:1:1:bin:/bin:/sbin/nologin

daemon:x:2:2:daemon:/sbin:/sbin/nologin

adm:x:3:4:adm:/var/adm:/sbin/nologin

lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

sync:x:5:0:sync:/sbin:/bin/sync

shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown

halt:x:7:0:halt:/sbin:/sbin/halt

mail:x:8:12:mail:/var/spool/mail:/sbin/nologin

operator:x:11:0:operator:/root:/sbin/nologin

把第一段跟最后一段替换 (第一段 冒号之前,最后一段 冒号之后)

[^:]表示非冒号字符,一个字符不是冒号

+号表示一个或多个字符

分成3部分

用反斜杠 3 :表示第3段 ([^:]+)

2 :第二段 (.*)

1 第一段([^:]+)

[root@localhost sed]# head test.txt |sed  -r 's/([^:]+):(.*):([^:]+)/3:2:1/'

/bin/bash:x:0:0:root:/root:root

/sbin/nologin:x:1:1:bin:/bin:bin

/sbin/nologin:x:2:2:daemon:/sbin:daemon

/sbin/nologin:x:3:4:adm:/var/adm:adm

/sbin/nologin:x:4:7:lp:/var/spool/lpd:lp

/bin/sync:x:5:0:sync:/sbin:sync

/sbin/shutdown:x:6:0:shutdown:/sbin:shutdown

/sbin/halt:x:7:0:halt:/sbin:halt

/sbin/nologin:x:8:12:mail:/var/spool/mail:mail

/sbin/nologin:x:11:0:operator:/root:operator

把 /root 替换成123,/root 之前加一个反斜杠 脱义

[root@localhost sed]# head test.txt  |sed 's//root/123/g'

root:x:0:0:root:123:/bin/bash

bin:x:1:1:bin:/bin:/sbin/nologin

daemon:x:2:2:daemon:/sbin:/sbin/nologin

adm:x:3:4:adm:/var/adm:/sbin/nologin

lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

sync:x:5:0:sync:/sbin:/bin/sync

shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown

halt:x:7:0:halt:/sbin:/sbin/halt

mail:x:8:12:mail:/var/spool/mail:/sbin/nologin

operator:x:11:0:operator:123:/sbin/nologin

加反斜杠 脱义

[root@localhost sed]# head test.txt  |sed 's//sbin/nologin/123/g'

root:x:0:0:root:/root:/bin/bash

bin:x:1:1:bin:/bin:123

daemon:x:2:2:daemon:/sbin:123

adm:x:3:4:adm:/var/adm:123

lp:x:4:7:lp:/var/spool/lpd:123

sync:x:5:0:sync:/sbin:/bin/sync

shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown

halt:x:7:0:halt:/sbin:/sbin/halt

mail:x:8:12:mail:/var/spool/mail:123

operator:x:11:0:operator:/root:123

也可以用@ 分隔开

[root@localhost sed]# head test.txt  |sed 's@/sbin/nologin@123@g'

root:x:0:0:root:/root:/bin/bash

bin:x:1:1:bin:/bin:123

daemon:x:2:2:daemon:/sbin:123

adm:x:3:4:adm:/var/adm:123

lp:x:4:7:lp:/var/spool/lpd:123

sync:x:5:0:sync:/sbin:/bin/sync

shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown

halt:x:7:0:halt:/sbin:/sbin/halt

mail:x:8:12:mail:/var/spool/mail:123

operator:x:11:0:operator:/root:123

删除文件中所有字母, 用[a-zA-Z]来表示字母

[root@localhost sed]# head  test.txt  |sed 's/[a-zA-Z]//g'

::0:0::/://

::1:1::/://

::2:2::/://

::3:4:://://

::4:7::///://

::5:0::/://

::6:0::/://

::7:0::/://

::8:12::///://

::11:0::/://

在所有的行前面加上aaa字符,(.*)表示一整行,&/(and符号表示前面的小括号),添加字符为aaa:

[root@localhost sed]# head test.txt  |sed -r 's/(.*)/aaa:&/'

aaa:root:x:0:0:root:/root:/bin/bash

aaa:bin:x:1:1:bin:/bin:/sbin/nologin

aaa:daemon:x:2:2:daemon:/sbin:/sbin/nologin

aaa:adm:x:3:4:adm:/var/adm:/sbin/nologin

aaa:lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin

aaa:sync:x:5:0:sync:/sbin:/bin/sync

aaa:shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown

aaa:halt:x:7:0:halt:/sbin:/sbin/halt

aaa:mail:x:8:12:mail:/var/spool/mail:/sbin/nologin

aaa:operator:x:11:0:operator:/root:/sbin/nologin