Tomcat 7使用AJP协议导致AJP端口被意外暴露给外网

使用Ubuntu 13.10 Apache 2.2 通过 AJP 整合 Tomcat 7中的方法配置了通过AJP协议来通过Apache进行访问的代理。

但是最近发现Tomcat有时候会崩溃掉。刚刚开始以为是正常的OOM,后来分析日志,并没有找到相关的记录,反倒是发现如下内容:

Jan 26, 2016 5:06:47 PM org.apache.coyote.ajp.AjpMessage processHeader
SEVERE: Invalid message received with signature 18245
Jan 26, 2016 5:06:48 PM org.apache.coyote.ajp.AjpMessage processHeader
SEVERE: Invalid message received with signature 5635
Jan 26, 2016 5:06:48 PM org.apache.coyote.ajp.AjpMessage processHeader
SEVERE: Invalid message received with signature 18245
Jan 26, 2016 5:06:48 PM org.apache.coyote.ajp.AjpMessage processHeader
SEVERE: Invalid message received with signature 3338
Jan 26, 2016 5:06:48 PM org.apache.coyote.ajp.AjpMessage processHeader
SEVERE: Invalid message received with signature 20304
Jan 26, 2016 5:06:48 PM org.apache.coyote.ajp.AjpMessage processHeader
SEVERE: Invalid message received with signature 20304
Jan 26, 2016 5:06:48 PM org.apache.coyote.ajp.AjpMessage processHeader
SEVERE: Invalid message received with signature 32768
Jan 26, 2016 5:06:48 PM org.apache.coyote.ajp.AjpMessage processHeader
SEVERE: Invalid message received with signature 30

于是感觉有些奇怪,因为AJP协议应该不会发生非常频繁的通信协议错误问题。结果尝试从外网连接TomcatAjp端口8009,发现竟然可以通过telnet连接成功!!说明端口意外暴露给了外网。

那么根据The AJP Connector中的介绍说明(注意address部分),如果没有指定IP地址的话,默认是绑定任意地址,这样就导致外网也可以访问这个端口。因此出于安全考虑,我们需要增加这个address的设置,并且绑定到127.0.0.1。最终结果如下:

<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector port="8009" protocol="AJP/1.3" address="127.0.0.1" redirectPort="8443" />

而我在配置的时候,恰恰少设置了address="127.0.0.1".这个这种错误有些低级啊。

Linux入门-使用脚本

在linux上开发,经常有一些重复性劳动,建议写成脚本,省时省力还不出错。

1.新建一个文件

vi filename

如vi test.sh

2.编辑内容,linux上sh的形式比较一致

#!/bin/sh
#comments

然后跟上自己的命令就可以了,比如

mkdir tmp

cd tmp

3.最后chmod +x test.sh,可执行,就可以执行了

4.执行完毕后,你会发现确实创建了tmp,但当前目录没有变。

这是因为脚本执行时会创建一个子环境,执行完毕后回到当前环境,如果你想直接改变当前环境,使用source 来执行脚本就可以了

source test.sh

就可以看到目录切到tmp下。

Linux服务器-windows远程开发

最近项目需要在Linux环境下,本人的本是windows,怎么办么?

答:申请一台服务器,linux环境,用ssh工具就可以开发了

不过服务器上只能用vim开发,开发起来惨不忍睹,怎么办?

答:远程开发

远程开发的原理很简单,就是把服务器上的文件夹映射到windows上,就可以像windows本地开发一样了。

首先在Linux服务器上安装samba,装好后给自己配置一个samba用户,具体方法请自行百度,参考链接:http://blog.csdn.net/i_chips/article/details/19191957。

window端打开计算机,有一个映射网络驱动器选项

samba_3

打开后有两个选项

samba_4

第一个是盘符,默认就行,红框所选的地方 写上要映射的服务器文件夹地址。

填好之前在samba上配置的账号/密码,就可以用了。

openssl 如何查看 der 和 pem格式的证书

One way to verify if "keytool" did export my certificate using DER and PEM formats correctly or not is to use "OpenSSL" to view those certificate files. To do this, I used the "openssl x509" command to view keytool_crt.der and keytool_crt.pem:

>openssl x509 -in keytool_crt.pem -inform pem -noout -text

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 1185636568 (0x46ab60d8)
        Signature Algorithm: dsaWithSHA1
        Issuer: C=CA, ST=Herong State, L=Herong City, ...
        ...

>openssl x509 -in keytool_crt.der -inform der -noout -text

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 1185636568 (0x46ab60d8)
        Signature Algorithm: dsaWithSHA1
        Issuer: C=CA, ST=Herong State, L=Herong City, ...
        O=Herong Company, OU=Heron
        ...

原文链接:openssl 如何查看 der 和 pem格式的证书

如何在Ubuntu 12.04上部署免费的SSL证书(Let's Encrypt)

Let's Encrypt是国外一个公共的免费SSL项目,由 Linux 基金会托管,它的来头不小,由Mozilla、思科、Akamai、IdenTrust和EFF等组织发起,目的就是向网站自动签发和管理免费证书,以便加速互联网由HTTP过渡到HTTPS,目前Facebook等大公司开始加入赞助行列。

Let's Encrypt已经得了 IdenTrust 的交叉签名,这意味着其证书现在已经可以被Mozilla、Google、Microsoft和Apple等主流的浏览器所信任,你只需要在Web 服务器证书链中配置交叉签名,浏览器客户端会自动处理好其它的一切,Let's Encrypt安装简单,未来大规模采用可能性非常大。

Let's Encrypt虽然还在测试当中,但是市场需求非常大,已经有非常多的朋友迫不及待地安装并用上了Let's Encrypt。Let's Encrypt向广大的网站提供免费SSL证书,不管是对于网站站长、互联网用户,还是对整个Web互联网,都是非常有利的,它有利于整个互联网的安全。

本篇文章就来为大家讲解一下如何获取Let's Encrypt免费SSL证书,并附上Apache的SSL证书配置方法。

一、 安装Let's Encrypt免费SSL准备


1.Let's Encrypt官网:
官方网站:https://letsencrypt.org/
项目主页:https://github.com/letsencrypt/letsencrypt

2.安装Let's Encrypt脚本依赖环境:(这一部分可以跳过,因为官方提供的Let's Encrypt脚本会自动检测并安装)

$ apt-get install git

二、获取Let's Encrypt免费SSL证书


1.获取Let's Encrypt免费SSL证书很简单,你只需要执行以下命令,就会自动在你的VPS上生成SSL证书和私钥。

$ git clone https://github.com/letsencrypt/letsencrypt

$ cd letsencrypt

$ ./letsencrypt-auto

如果只想生成证书,则最后一句使用如下命令(貌似我目前只能用下面的命令,用上面的命令会报错):

$ ./letsencrypt-auto certonly

2.执行上述命令后,会弹出对话框,同意用户协议。

3.接着会提示让你关闭Nginx或者Apache。

4.Let's Encrypt需要用到80和443端口,所以你需要关闭那些占用这两个端口的应用。

5.当你看以下内容时,就表明你的Let's Encrypt免费SSL证书获取成功了。

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/www.mobibrw.com/fullchain.pem. Your cert will
   expire on 2016-04-25. To obtain a new version of the certificate in
   the future, simply run Let's Encrypt again.
 - If you like Let's Encrypt, please consider supporting our work by:

   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

注意: 目前阿里云的服务器域名解析不支持DNS Certification Authority Authorization (CAA) Resource Record,导致在进行签名的时候返回如下错误信息:

Failed authorization procedure. mobibrw.com (tls-sni-01): urn:acme:error:connection :: The server could not connect to the client to verify the domain :: DNS problem: query timed out looking up CAA for mobibrw.com

IMPORTANT NOTES:
 - The following errors were reported by the server:

   Domain: mobibrw.com
   Type:   urn:acme:error:connection
   Detail: DNS problem: query timed out looking up CAA for mobibrw.com

目前还没有解决方法,具体的讨论参考 DNS query timeout #1610

目前根据提交给阿里云的工单,今天2016年1月26日已经正式支持了Let's Encrypt,目前验证,一切正常了。

三、证书续期


Let's Encrypt有多种方法来验证你是否是域名的所有人,而acme-tiny这个小工具用的是最简单的方法,也就是利用http文件验证。
简单来说,就是生成一个验证文件,放在http://www.mobibrw.com/.well-known/acme-challenge/下,Let's Encrypt官方验证这个文件是否存在、内容是否正确。如果一切正确,说明域名是你所有。因此我们下面的脚本中会出现创建,删除.well-known/acme-challenge/这个目录的动作。

1.生成证书自动化请求脚本

$ sudo vim /root/renew-ssl.sh

里面内容如下:

#!/bin/bash

mkdir -p /var/www/wordpress/.well-known/acme-challenge

/root/letsencrypt/letsencrypt-auto --renew-by-default certonly --email wangqiang1588@sina.com  -d www.mobibrw.com -d mobibrw.com --webroot -w /var/www/wordpress --agree-tos

service apache2 reload

rm -rf /var/www/wordpress/.well-known

2.赋予脚本执行权限

$ sudo chmod +x /root/renew-ssl.sh

3.脚本添加到计划任务,每隔60天执行一次

$ sudo crontab -e

在文件的最后增加

0 3 */60 * * /root/renew-ssl.sh

4.查看是否添加成功

$ sudo crontab -l

5.重启cron服务

$ sudo service cron restart

四、需要注意的问题


目前(2022/02/27)最新的1.23.0版本,在生成/更新证书的时候,会提示用户是否全站都迁移到HTTPS。如果不小心同意了这个选项,对于Apache服务器来说,会在配置文件中增加如下内容:

RewriteEngine on
RewriteCond %{SERVER_NAME} =www.xxx.com [OR]
RewriteCond %{SERVER_NAME} =xxx.com [OR]
RewriteCond %{SERVER_NAME} =xxxxx.com [OR]
RewriteCond %{SERVER_NAME} =www.xxxxx.com
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]

这个配置会覆盖我们在网站目录下的HTTPS相关设置(.htaccess),如果我们配置网站某个目录下的文件可以不通过HTTPS进行访问,那么需要手工移除这些自动生成的配置信息,然后重启Apache服务。

参考链接


Ubuntu 12.04服务器版使用 cp -r -f 强制覆盖拷贝时仍需确认的问题

问题现象:


使用cp -r -f 强制覆盖拷贝命令时,每一个文件都需要认为的键入“Y”进行确认,甚是烦扰,难道要我点击一万下不成?

问题原因:


cp命令被设置了别名
alias cp='cp -i'
所以在每次执行cp命令是都会按照这个设置进行人为的确认(-i参数的含义)。

解决方法:


一.使用unalias cp命令 解除对cp的别名(仅本次终端连接会话临时取消),我们先输入alias命令,查看系统内部已经设置的别名:

[root@localhost ~]# alias
alias cp='cp -i'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --color=auto'
alias grep='grep --color=auto'
alias l='ls -CF'
alias la='ls -A'
alias ll='ls -alF'
alias ls='ls --color=auto'
alias mv='mv -i'
alias rm='rm -i'

输入unalias cp命令,取消cp命令的别名.

[root@localhost ~]# unalias cp

二.直接输入\cp命令,作用也是取消cp的别名

[root@localhost ~]# \cp filename new/filename
[root@localhost ~]#

三.使用管道的方式,自动输入yes

[root@localhost ~]# yes | cp filename new/filename

Linux中eventfd函数调用解析

eventfd 在内核版本,2.6.22以后有效。查看内核版本可以用命令 uname -r

#include<sys/eventfd.h>  
int eventfd(unsigned int initval,int flags);
这个函数会创建一个 事件对象 (eventfd object), 用来实现,进程(线程)间的等待/通知(wait/notify) 机制. 内核会为这个对象维护一个64位的计数器(uint64_t)。
并且使用第一个参数(initval)初始化这个计数器。调用这个函数就会返回一个新的文件描述符(event object)。2.6.27版本开始可以按位设置第二个参数(flags)。
有如下的一些宏可以使用:EFD_NONBLOCK , 功能同open(2)O_NONBLOCK,设置对象为非阻塞状态,如果没有设置这个状态的话,read(2)eventfd,并且计数器的值为0 就一直堵塞在read调用当中,要是设置了这个标志, 就会返回一个 EAGAIN 错误(errno = EAGAIN)。效果也如同 额外调用select(2)达到的效果。EFD_CLOEXEC 这个标识被设置的话,调用exec后子进程得不到这个句柄,而不影响fork产生的子进程,更多的是安全方面的考虑。

EFD_SEMAPHORE,这个标识(since Linux 2.6.30)开始,但是在Android的NDK中是没有这个定义的,因此不建议在Android中使用。他的功能完全可以用默认参数替换,而且更高效,因为如果是信号量模式,每次调用,只会减少1,导致重复进入内核,性能实际上是有影响的。

如果是2.6.26或之前版本的内核,flags 必须设置为0。

创建这个对象后,可以对其做如下操作。

write 将缓冲区写入的8字节整形值加到内核计数器上。

read 读取8字节值, 并把计数器重设为0. 如果调用read的时候计数器为0, 要是eventfd是阻塞的, read就一直阻塞在这里,否则就得到 一个EAGAIN错误。
如果buffer的长度小于8那么read会失败, 错误代码被设置成 EINVAL

poll select epoll

close 当不需要eventfd的时候可以调用close关闭, 当这个对象的所有句柄都被关闭的时候,内核会释放资源。 为什么不是close就直接释放呢, 如果调用fork 创建
进程的时候会复制这个句柄到新的进程,并继承所有的状态。

 程序实例:
#include <sys/eventfd.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>             /* Definition of uint64_t */

#define handle_error(msg) \
   do { perror(msg); exit(EXIT_FAILURE); } while (0)

int
main(int argc, char *argv[])
{
   uint64_t u;
	
   int efd = eventfd(10, 0);
   if (efd == -1)
       handle_error("eventfd");
   
   int ret = fork();
   if(ret == 0)
   {
       for (int j = 1; j < argc; j++) {
           printf("Child writing %s to efd\n", argv[j]);
           u = atoll(argv[j]);
           ssize_t s = write(efd, &u, sizeof(uint64_t));
           if (s != sizeof(uint64_t))
               handle_error("write");
       }
       printf("Child completed write loop\n");

       exit(EXIT_SUCCESS);
   }
   else
   {
       sleep(2);

       ssize_t s = read(efd, &u, sizeof(uint64_t));
       if (s != sizeof(uint64_t))
           handle_error("read");
       printf("Parent read %llu from efd\n",(unsigned long long)u);
       exit(EXIT_SUCCESS);
   }
}

运行结果:
比较简单,不做过解释。子进程写入命令行中传入的参数,父进程读取其中计数器的值。

运行结果:

./eventfd 10 20 30
Child writing 10 to efd
Child writing 20 to efd
Child writing 30 to efd
Child completed write loop
Parent read 70 from efd

命令行传入的是10、20、30其和应为60,为啥读取的是70呢?请看15行调用eventfd时第一个参数是10,这个参数是创建eventfd时初始化计数器的值。

参考链接:Linux中eventfd函数调用解析