apache反向代理tomcat获取用户真实IP

校内CAS单点登录的审计日志中,用户的IP地址一直是服务器地址。这个问题拖了好久,今天终于解决了。
通过追查日志的来源,发现是自己实现了一个appender配置在了log4j中。
从这个类入手开始追查代码,最终发现IP的来源是通过request.getRemoteAddr这个方法来获得的。
而这个方法是来源于tomcat自身的servlet-api提供,返回的向tomcat发起请求的客户端IP。

但是一般情况下tomcat都会用nginx或apache代理tomcat,所以这个方法得到的IP就是服务器自身的IP而不是真实的远程IP了
而我又不想更改CAS的源码重新部署,所以只能想办法更改tomcat,让tomcat得到真实的IP。
根据apache文档所述,proxy模块会自动添加这三个header。
但是还差两个header没有添加,于是引入header模块使用下面的指令设置剩下的信息。
RequestHeader set x-forwarded-by “IP”
RequestHeader set x-forwarded-proto “https”
由于我的服务器是https协议,所以写了https。

根据tomcat文档所述,在tomcat端的server.xml中
Engine级别下添加Value节点。

文档写的很清楚internalProxies指定的正则所匹配的地址不会在x-forwarded-by中出现,而trustedProxied正则匹配的地址会出现在x-forwarded-by中。
我想要的结果是真实IP为用户IP,同时添加校内服务器的ip地址进入x-forwarded-by中,所以理论上我值需要把服务器IP地址段的正则写入trustedProxied即可。但是实际测试结果依然不对。
getRemoteAddr方法返回的依然是服务器的IP而不是真实IP。

而如果我在internalProxies服务器IP地址段正则的话,getRemoteAddr返回的结果就是正常的,但是x-forwarded-by字段就看不见代理服务器的IP了。

同时更改tomcat日志的格式如下。

HTTPS证书制作和配置示例

今天在校内尝试从git.bistu.edu.cn克隆代码,使用https协议时候发现clone失败,于是开始实验,使用curl连接cas.bistu.edu.cn、x.bistu.edu.cn、chat.bistu.edu.cn、community.bistu.edu.cn、site.bistu.edu.cn
发现只有cas和site正常,其他的都报错了,但是浏览器访问并未发现错误。
联想之前ZFZ的safari访问git报错而我电脑的浏览器却没事,瞬间意识到之前配置的HTTPS可能很多都有问题。
经过一翻折腾,发现之前的补链不全导致有些浏览器无法识别完整的信任链,最终通过补链解决。
当Apache、IIS、Nginx都不报错之后,只有Tomcat还有问题。
Tomcat之前的jks使用的是http://www.agentbob.info/agentbob/79-AB.html这篇文章的工具生成的,说明这个工具有问题。
继续搜索发现可以使用
openssl s_client -msg -connect cas.bistu.edu.cn:8443
这个命令来调试详细的ssl连接过程
和正常的过程对比发现Tomcat的信任链依然不一样。遂放弃这个工具。
最终在文末的文章中找到了解决方案。
废话说完了,下面是完整操作记录。
—————————————————————–
拿到手的有bistu.crt证书一张、bistu.key私钥一个。
实际需要开启HTTPS的服务器若干,服务器种类为Tomcat、Nginx、Apache、IIS。
到证书签发机构官网下载root根证书和中间证书,在windows上另存为Base64PEM格式
分别为root.crt和intermediate.crt
拷贝到Linux下开始制作

补链后证书为bistu.chained.crt

生成bistu.jks为tomcat所需格式

openssl pkcs12 -export -out bistu.pfx -inkey bistu.key -in bistu.crt

生成bistu.pfx为IIS所需格式

至此所有文件如下
1.bistu.crt #原始证书
2.bistu.key #私钥
3.root.crt #根证书
4.intermediate.crt #中间证书
5.bistu.chained.crt #补链后证书
6.combined.crt #中间链+根证书
7.bistu.p12 #p12格式的证书库
8.bistu.jks #jks格式的证书库
9.bistu.pfx #pfx格式的证书库

Apache需要的:1、2、5
Nginx需要的:2、5
Tomcat需要的:8
IIS需要的:2、9

配置示例

Apache

Nginx配置示例

Tomcat配置示例

参考:
http://blog.csdn.net/jun55xiu/article/details/8980812
http://www.fourproc.com/2010/06/23/create-a-ssl-keystore-for-a-tomcat-server-using-openssl-.html
https://blogs.oracle.com/blogbypuneeth/entry/steps_to_create_a_jks

Apache+Tomcat8的HTTPS配置+反向代理

http://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html

1.生成tomcat自签名证书
keytool -genkey -alias tomcat -keyalg RSA -keystore self.jks

Enter keystore password: 设置keystore密码
Re-enter new password: 设置keystore密码
What is your first and last name?
[Unknown]: localhost 输入localhost
What is the name of your organizational unit?
[Unknown]:
What is the name of your organization?
[Unknown]:
What is the name of your City or Locality?
[Unknown]:
What is the name of your State or Province?
[Unknown]:
What is the two-letter country code for this unit?
[Unknown]:
Is CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown correct?
[no]: yes

Enter key password for
(RETURN if same as keystore password): 直接回车

直接生成了self.jks

2.让系统信任这个证书
keytool -exportcert -alias tomcat -keystore self.jks -file self.cer
sz self.cer
传到windows上打开self.cer,点击详细信息->复制到文件->Base64编码X.509->另存为self.crt
rz
回传self.crt
sudo mkdir -p /usr/share/ca-certificates/tomcat
sudo cp self.crt /usr/share/ca-certificates/tomcat
sudo dpkg-reconfigure ca-certificates
第一步选yes,然后按空格选中self.crt,回车。

3.tomcat开启SSL
打开/etc/tomcat8/server.xml,修改这段如下

4.apache开启SSL模块和proxy模块
cd /etc/apache2/mods-enabled
sudo ln -s ../mods-available/proxy.load .
sudo ln -s ../mods-available/proxy_http.load .
sudo ln -s ../mods-available/ssl.load .

5.Apache反向代理配置

重启apache和tomcat即可

Apache2.4中文配置文档1

本文主要翻译一部分常用的apache2.4配置文档,因为之前在搭建个人博客的时候发现2.4的中文文档还还不多。
在某个版本后apache本身的的目录结构有点变化,而网上能搜到的方法很多都是直接更改主配置文件,这样并不好。
以下所有例子均以本站实际需求为例,结合官方文档进行说明。
环境:
Ubuntu14.04 x64
apache2.4

参考内容:
官方文档:http://httpd.apache.org/docs/2.4/zh-cn/
本站配置参考:http://blog.hylstudio.cn/archives/383
相关概念:http://blog.hylstudio.cn/archives/380

apache的初步使用

安装:配置好apt源后,执行sudo apt-get install apache2即可
启动:sudo service apache2 start
重启:sudo service apache2 restart
关闭:sudo service apache2 stop
查看状态:sudo service apache2 status

默认目录

网站根目录:/var/www/html
日志目录:/var/log/apache2
配置文件目录:/etc/apache2
配置文件目录结构如下
├── apache2.conf
├── conf-available
├── conf-enabled
├── envvars
├── magic
├── mods-available
├── mods-enabled
├── ports.conf
├── sites-available
└── sites-enabled

其中可以看到成对出现的文件夹有三对儿,分别是站点、模块、配置。
顾名思义,available下是可用的、enabled下是启用的。
相比直接更改主配置文件apache2.conf来说,修改这些更加方便。
启用的方法为进入到enable文件夹中执行软连接命令,把要启用的内容做个软连接到enabled目录下即可。
比如ln -s ../sites-enabled/000-default.conf .

接下来是常用的功能文档翻译

首先,配置文件的语法描述如下(巴克斯范式)

expr ::= “true” | “false”
| “!” expr
| expr “&&” expr
| expr “||” expr
| “(” expr “)”
| comp

comp ::= stringcomp
| integercomp
| unaryop word
| word binaryop word
| word “in” “{” wordlist “}”
| word “in” listfunction
| word “=~” regex
| word “!~” regex

stringcomp ::= word “==” word
| word “!=” word
| word “<” word
| word “<=” word | word “>” word
| word “>=” word

integercomp ::= word “-eq” word | word “eq” word
| word “-ne” word | word “ne” word
| word “-lt” word | word “lt” word
| word “-le” word | word “le” word
| word “-gt” word | word “gt” word
| word “-ge” word | word “ge” word

wordlist ::= word
| wordlist “,” word

word ::= word “.” word
| digit
| “‘” string “‘”
| “”” string “””
| variable
| rebackref
| function

string ::= stringpart
| string stringpart

stringpart ::= cstring
| variable
| rebackref

cstring ::= …
digit ::= [0-9]+

variable ::= “%{” varname “}”
| “%{” funcname “:” funcargs “}”

rebackref ::= “$” [0-9]

function ::= funcname “(” word “)”

listfunction ::= listfuncname “(” word “)”

端口监听

当apache启动的时候会绑定在本地机器上的一些端口和地址等待请求。默认情况下,它会监听本地所有的地址,然而实际需求可能只需要监听某些特定的端口、特定的地址,或者特定地址上的特定端口。这经常和虚拟主机(Virtual Host)特性结合在一起使用,端口监听决定了apache服务器在不同的IP地址、hostname、端口上如何响应。

Listen指令告诉服务器在特定的地址、特定的端口、或者特定地址的特定端口上接受请求,如果Listen命令仅指定了一个特定的端口,那么服务器会监听所有地址上的这个端口,如果同时指定了IP地址和端口,那么服务器将会监听指定地址上的指定端口,多个Listen指令可以被用来监听指定的多个地址和端口。服务器将会在任意指定监听的地址和端口上响应请求。

例如,如果想让服务器同时在80和8000端口响应请求,使用如下写法
Listen 80
Listen 8000

让服务器在192.0.2.1:80和192.0.2.5:8000响应请求,使用如下写法
Listen 192.0.2.1:80
Listen 192.0.2.5:8000

IPv6的地址必需用方括号括起来,如下
Listen [2001:db8::a00:20ff:fea7:ccea]:80

应当避免重复监听同一个地方,否则服务器启动时会看到下面的错误
(48)Address already in use: make_sock: could not bind to address [::]:80

IPv6相关事宜

越来越多的平台已经实现了IPv6,并且在这些平台上也支持APR、允许服务器申请IPv6套接字连接、处理、发送请求。

对服务器管理员来说一个复杂的因素是apache是否可以同时处理IPv4和IPv6的连接,在IPv6下处理IPv4套接字使用IPv4-mapped IPv6地址,这在很多平台上默认是可以的,但是在FreeBSD、NetBSD、OpenBSD上默认是不可以的。为了遵循操作系统方面的原则,一个特别的配置参数可以更改apache服务器的行为。

另一方面,在某些平台上,比如Linux和Tru64,同时处理IPv6和IPv4的方法有且仅有使用地址匹配。如果你想让apache用最少的套接字链接处理IPv6和IPv4,这要求必需使用IPv4-mapped IPv6地址,使用–enable-v4-mapped 参数即可。

–enable-v4-mapped这个参数除了在FreeBSD、NetBSD、OpenBSD上都是可以的。

如果你只想让apache服务器处理IPv4连接,无论你的平台是什么都可以被支持。使用Listen指令指定一个IPv4地址即可,如下
Listen 0.0.0.0:80
Listen 192.0.2.1:80

如果你的平台支持并且你想用独立的套接字处理IPv4和IPv6连接(例如禁用掉IPv4-mapped addresses),指定–disable-v4-mapped这个配置选项即可。

指定监听协议

监听指令的第二个可选参数是协议,如果没有指定,https默认是443,其他的默认为http,协议是用来决定哪个模块应该处理请求并且可以通过AcceptFilter指令来应用最优化的协议。
只要当你没有使用默认端口的时候才需要指定协议,例如在8443端口使用https协议:

Listen 192.170.2.1:8443 https

怎么和虚拟主机同时工作

Listen指令并没有实现虚拟主机,它仅仅告诉主服务器应该监听什么地址和端口。如果没有使用指令,服务器用同样的方式对所有的请求进行相应。然而可以被用来为一个或多个不同的端口和地址指定不同的行为。为了实现VirtualHost,服务器必须先监听一个地址或端口。在接下来的章节将会介绍使用VirtualHost指定地址和端口来设置不同的行为。注意如果使用了但是没有监听任何端口,那么服务器将不能被访问。

虚拟主机

如果你要调试虚拟主机配置,Apache 的命令行参数 -S 非常有用。即输入以下命令:
/usr/local/apache2/bin/httpd -S
这个命令将会显示 Apache 是如何解析配置文件的。仔细检查 IP 地址与服务器名称可能会帮助你发现配置错误 (参见 httpd 程序文档,以便了解其它命令行选项)。

虚拟主机分为两种,一种是基于名称的,一种是基于IP的
基于IP的虚拟主机:
它使用连接的IP地址来决定正确的主机用来服务,所以你需要为每个主机分配不同的IP地址。

基于名称的虚拟主机:
服务器依赖于客户端在HTTP请求头部报告的hostname来使用正确的主机,使用这个技术,不同的站点可以使用相同的IP。例如本站只有一个公网IP地址,却可以使用blog.hylstudio.cn、files.hylstudio.cn、tomcat.hylstudio.cn等不同的域名来共享一个公网IP地址。

基于名称的虚拟主机通常来说更简单一些,因为你仅需要配置你的DNS服务器解析不同的hostname到正确的IP地址并且配置apache服务器区分这些不同的地址即可。例如本站的多个二级域名都通过A记录解析到了同一个公网IP地址。基于名称的虚拟主机更加适应稀缺IP地址的需求,所以除非你使用的设备确实需求基于IP的虚拟主机,那么就应该使用基于名称的虚拟主机。由于历史原因,基于IP的虚拟主机的需要客户端的支持,现在已经不再适应一般情况下的web服务器了。

基于名称的虚拟主机建立于基于IP的虚拟主机选择算法上,这意味着搜索合适的servername仅会发生在基于IP的主机之间。
在这里主要介绍基于名称的虚拟主机。

服务器如何选择合适的基于名称的虚拟主机

要认识到,很重要的一点的:基于名称的虚拟主机解析的第一步是基于IP的解析。基于名称的虚拟主机解析仅仅是在缩小基于IP的虚拟主机的候选范围之后再选择最合适主机,在基于IP的虚拟主机地址中使用通配符(*)是不合适的。

当一个请求到来之后,服务器会根据请求中使用的IP地址和端口根据虚拟主机的配置参数匹配最佳(最具体)的虚拟主机。如果有一个以上的最佳匹配组合出现,apache服务器会继续比较ServerName和ServerAlias命令指定的名称。

如果你在基于名称的虚拟主机配置中省略了ServerName指令,服务器默认会从hostname得到主机的FQDN(完全合格域名/全称域名)。这种隐式的设置servername可能会导致与预期相反的虚拟主机匹配匹配,并且不鼓励这样。

IP地址和端口的组合的基于名称的默认虚拟主机
如果没有在包括具体IP地址和端口组合的虚拟主机设置中匹配到ServerName或者ServerAlias,那么列表中的第一个虚拟主机将会被使用。

基于名称的虚拟主机 (每个 IP 多个站点)

第一部是在配置文件为每个虚拟主机添加一块,在每个内部,你至少需要ServerName和DocumentRoot命令。ServerName指定指明了服务哪个站点,DocumentRoot指明了这个站点默认展示本地文件系统哪个路径下的东西。

Main host goes away(我实在不知道这段怎么翻译合适了=-=)
任意一个未被存在的匹配的请求都由全局的服务器配置来处理,无论hostname和ServerName是什么。
当你添加一个基于名称的虚拟主机时,如果这个虚拟主机的参数和一个已存在的IP端口组合重复了,那么请求将会由一个具体的虚拟主机处理。在这种情况下,一个机智的做法是创建一个默认的虚拟主机指定一个ServerName来匹配基本的站点。新的域名在同样的接口和端口上,但是要求独立的设置,他们可以作为子虚拟主机(不是默认的)被加入。

ServerName的继承

在每一个基于名称的虚拟主机中最好永远有一个具体的ServerName的列表。
如果一个虚拟主机没有指定具体的ServerName,那么将会从服务器的全局配置文件继承下来。如果没有全局的ServernName,当启动的时候会从DNS反向解析第一个监听的地址。 在这两种情况下,这个继承下来的SserverName会影响基于名称的虚拟主机的解析。所以最好在每一个基于名称的虚拟主机中最好永远有一个具体的ServerName的列表。

例如,假设你已经有了www.example.com,然后你想添加other.example.com虚拟主机,它们解析到了相同的IP地址。那么你仅需要简单的在配置文件中添加以下内容:

# This first-listed virtual host is also the default for *:80
ServerName www.example.com
ServerAlias example.com
DocumentRoot “/www/domain”
ServerName other.example.com
DocumentRoot “/www/otherdomain”

你可以灵活的在指令中星号的位置指定一个具体的IP地址。For example, you might want to do this in order to run some name-based virtual hosts on one IP address, and either IP-based, or another set of name-based virtual hosts on another address.(这句没看懂=_=求大神翻译)
ZHRMoe的翻译(他的博客:http://zhrmoe.com/):
比如,你这么做可以在一个IP地址上运行一些基于域名的虚拟主机(也可以是基于IP的),或者是在其他的地址上运行其他基于域名的虚拟主机。

许多服务想要通过一个以上的名称被访问,这可以通过ServerAlias指令实现。例如在第一个中,ServerAlias指定了一个其他的名称,那么用户就可以使用这个地址来访问同样的网站。
ServerAlias example.com *.example.com

加上这行之后,所有在example.com下的请求都会由www.example.com这个虚拟主机处理。通配符星号和问号可以用来被匹配名称。当然你不能仅仅把名称放在ServerName和ServerAlias后面,首先你要先让你的DNS按正确的规则解析这些域名。

基于名称的虚拟主机的匹配按照在配置文件中的出现顺序处理,第一个匹配到的ServerName或SrverAlias将被使用,它们没有和带通配符的域名没有优先级之分,同样ServerName和ServerAlias之间也没有优先级。所有域名的列表会和ServerAlias同等对待。

最后,你可以其他指令自由设置不同的虚拟主机,大多数指令都可以被使用,这些指令仅改变相关的虚拟主机。要确定一个指令是否被允许只用,检查这个指令的上下文即可。在之外的指令当没有被覆盖的时候会生效。

本站站配置参考

Apache多站点及反向代理 配置

 

Apache多站点及反向代理配置

状态:只有一个服务器,需要多个站点,比如博客、文件、Android接口等。

最开始我的做法是用子文件夹来区分站点,所以本站的地址都是这样的http://hylstudio.cn/xxx

后来曾老师提醒可以使用下一级域名来区分不同的站点,于是研究了下。

首先,更改DNS,把需要的域名解析到这台服务器,然后更改apache配置如下

进入apache配置目录/etc/apache2其中目录结构如下
├── apache2.conf           主配置文件
├── conf-available         可用配置文件
├── conf-enabled           启用的配置文件
├── mods-available       可用的模块
├── mods-enabled         启用的模块
├── sites-available         可用的站点
└── sites-enabled           启用的站点

各个文件夹的功能如上,规则是在available中创建真实的配置、然后在enabled文件夹下创建软连接(ln -s命令)。

默认状态下sites-enabled下只有一个000-default,被我改成了999-default.conf,把内容改成下面这样

然后复制一份这个文件为001-wordpress.conf。内容如下

其他的同理,只需要更改ServerName、Alias、DocumentRoot即可。

需要说一下的是tomcat,因为tomcat在其他端口,需要做反向代理。内容如下

同时需要启用proxy模块
cd /etc/apache2/mods-enabled
sudo ln -s ../mods-available/proxy.load

这样做的好处是对外不暴露tomcat真实端口。

这样,就实现了通过不同域名访问不同站点。

配置default虚拟主机,rewrite含义为我没定义过的虚拟主机都会302重定向到blog.hylstudio.cn