以前开发一直用的nginx,一直使用它的spdy支持,在SSL连接传输耗时上确实有很大的提升,近期开了个wp博客,就是当前这个,想在这上面开启H2,也是遇到挺多问题,这里总结一下Apache2.4.17开启http2.0的遇到的相关问题。

前言

  • Google官方提供的SPDY模块只能在Apache2.2.*上使用,也有同学自行在2.4.*下编译安装成功的,相关内容请自行Google,我就不守旧了直接用http2.0。
  • 最新版本Apache(截止目前2.4.17)才支持。
  • 我使用的是阿里云的Centos7(x64),使用yum安装的openssl是1.0.1*版本的,在这个版本的openssl下编译Apache,开启Http2.0失败,Apache可以正常启动访问,就是浏览器上显示的不是h2协议,怀疑是openssl版本太低,下文使用最新的openssl编译安装。
  • Apache2.4.17版本有bug,在编译前config的时候如果没指定nghttp2的目录可能会报错(忘了什么错误了,以后补充)。
  • Apache2.4.17如果使用H2协议,不支持客户端证书认证,浏览器报连接错误,目前不清楚是浏览器不支持还是Apache问题,测试环境: firefox(42.0) chrome(46.0.2490.86) IE11 with windows7 x64(IE11 目前不支持h2),但是nginx最新版开启H2却不会有此情况,个人怀疑应该是Apache的问题。
  • 相关资料

 

安装过程

  • 请自行准备以下组件:apr apr-util nghttp2 openssl pcre,截止目前,nghttp2版本为1.5.0,openssl版本1.0.2d,我目前的各组件版本如下:
    QQ截图20151201161051
  • 编译安装以上组件(略)
  • 编译openssl,config配置:
    ./config -fPIC --prefix=/usr/local/openssl/ enable-shared
    
  • 如果编译完成之后version显示的还是系统之前的旧版本:

    # 备份原来的旧的openssl bin文件(移动到其他目录)
    mv /usr/bin/openssl /root/
    # 为新编译的openssl在bin目录中新建一个链接
    ln -s /usr/local/ssl/bin/openssl /usr/bin/openssl
    

    export LD_LIBRARY_PATH=/usr/local/openssl/lib

  • 编译Apache, config配置:
    ./configure 
    --prefix=/usr/local/apache 
    --enable-so 
    --enable-ssl 
    --enable-cgi 
    --enable-rewrite 
    --with-zlib 
    --with-pcre=/usr/local/pcre 
    --with-apr=/usr/local/apr 
    --with-apr-util=/usr/local/apr-util 
    --enable-modules=most 
    --enable-mpms-shared=all 
    --with-mpm=event 
    --enable-proxy 
    --enable-http2 
    --with-nghttp2=/usr/local/nghttp2 
    --enable-nghttp2-staticlib-deps 
    --enable-proxy-fcgi 
    --with-ssl=/usr/local/openssl
    

    有变化的就是以下配置:

    --enable-http2 
    --with-nghttp2=/usr/local/nghttp2 
    --enable-nghttp2-staticlib-deps
  • 编译php, config配置:
    ./configure 
    --prefix=/usr/local/php 
    --with-apxs2=/usr/local/apache/bin/apxs 
    --enable-shared 
    --with-libxml-dir 
    --with-gd 
    --with-openssl 
    --enable-mbstring 
    --with-mcrypt 
    --with-mysqli 
    --with-mysql 
    --enable-opcache 
    --enable-mysqlnd 
    --enable-zip 
    --with-zlib-dir 
    --with-pdo-mysql 
    --with-jpeg-dir 
    --with-freetype-dir 
    --with-curl 
    --without-pdo-sqlite 
    --without-sqlite3 
    --enable-maintainer-zts
    
  • Apache添加或者取消注释 加载H2模块的配置:
    LoadModule http2_module modules/mod_http2.so
    
  • 在Apache配置的根配置或者vHost配置下添加以下配置:
    ProtocolsHonorOrder On //启用协议顺序适配
    Protocols h2 http/1.1  //优先h2协议,否则h1.1
    
  • 打开Chrome,访问:chrome://net-internals/#http2 即可看到效果(点击图片放大):QQ截图20151201160442由于我的wp控制台用了证书登录,目前只能用H1.1了,上述只是做个测试以及演示,本站协议已经切换回来

 

关于HTTP2.0的Renegotiations问题

经过一顿排查和Google,终于发现了相关的问题和资料:

Renegotiations on a https: connection means that certain TLS parameters are changed on the running connection. In Apache httpd you can change TLS parameters in directory configurations. If a request arrives for a resource in a certain location, configured TLS parameter are compared to the current TLS parameters. If they differ, renegotiation is triggered.

Most common use cases for this are cipher changes and client certificates. You can require clients to meet authentication only for special locations, or you might enable more secure, but CPU intensive ciphers for specific resources.

Whatever your good use cases are, renegotiation are a MUST NOT in HTTP/2. With 100s of requests ongoing on the same connection, which renegotiation would otherwise occur when?

The current mod_h[ttp]2 does not protect you from such configuration. If you have a site which uses TLS renegotiation, DO NOT enable h2 on it!

Again, we will address that in future releases so that you can enable it safely.

 

在一个https连接中如果需要重新协商,意味着当前连接有部分TLS参数改变了。在Apache中可以配置目录不同的TLS参数(比如部分目录 SSLVerifyClient require,部分目录 SSLVerifyClient none)。当某些位置(目录)的资源被请求,这个位置的TLS参数将会与当前连接的TLS参数进行对比,如果不相同,将会进行重新协商。

最常见的例子是加密套件变更和客户端证书验证。你可能在某些特殊路径要求验证客户端证书,或者你可能需要为某些资源启用更加安全,但CPU占用更大的加密套件来传输。

重新协商不能用于HTTP/2。 With 100s of requests ongoing on the same connection, which renegotiation would otherwise occur when?(跪了,翻译不过来)

当前的mod_http2模块没有保护你的这种特殊配置(我配置了部分路径开启客户端证书认证,而Apache却正常启动,没有在启动时报错,指的是这个)。如果您的站点使用了TLS重新协商,请不要开启H2!

我们将在未来的版本中让您可以安全的开启它。

 

以上引用自: https://icing.github.io/mod_h2/howto.html

 

HTTP/2加密建立在TLS基础,关于TLS,维基百科上有解释:http://zh.wikipedia.org/wiki/%E5%82%B3%E8%BC%B8%E5%B1%A4%E5%AE%89%E5%85%A8%E5%8D%94%E8%AD%B0

摘取一张图,可说明基于ALPN协议扩展定义的协商流程:

image_thumb_14

其它要求:

  • 只能基于TLS >= 1.2版本。目前TLS 1.3为草案版本,正式版本目前尚未可知。目前只有TLS 1.2可选。
  • 必须支持Server Name Indication (SNI) [TLS-EXT]扩展,客户端在连接协商阶段需要携带上域名
  • 基于TLS 1.3或更高版本构建,仅需要支持SNI扩展。TLS 1.2要求较多
  • 基于TLS 1.2构建
    • 必须禁用压缩机制。不恰当压缩机制会导致信息外露,HTTP/2报头有压缩机制
    • 必须禁用重新协商机制。终端对待TLS 1.2重新协商作为PROTOCOL_ERROR类型连接错误对待;密码套件加密次数限制导致连接一直挂起等待不可用
    • 终端可以通过重新协商提供对客户端凭证保护功能在握手期间,重新协商必须发生在发送连接序言之前进行。服务器当看到重新协商请求时应该请求客户端证书在连接建立后
    • 当客户端请求受保护的特定资源时,服务器可以响应HTTP_1_1_REQUIRED错误,可有效阻止重新协商机制

 

以上引用自: http://www.blogjava.net/yongboy/archive/2015/03/24/423791.html

至此,关于我前两天提到的客户端证书认证问题就明了了,我在Apache中设置了wp的管理员相关后台链接需要验证客户端证书,而其余前台链接不需要客户端证书验证。使用H1.1,在切换到管理员相关后台链接的时候,TLS参数改变了,浏览器与服务器进行了重新协商,然而,H2不支持重新协商,浏览器直接丢给了我一个SSL连接错误。至于我前两天提到的nginx可以支持这个,是个失误…因为nginx不支持设置特定的目录或链接不同的TLS参数,所以我当时开启H2之后全站开启了客户端证书认证进行测试,而没测试目前的这种情形。

自建DNS递归服务器防污染

114DNS在最近,实在是抽的不成样,TTL缓存的时间无比之长,导致我的DNS记录在dnspod上修改完(TTL 600)半小时后获取的dns记录依然没变化,此时 8.8.8.8 早已...

阅读全文

openssl生成证书签名请求(CSR)

本文介绍了如何在Linux上使用openssl生成证书签名请求和证书私钥 输入以下指令生成一个加密的私钥文件,指令中的2048为加密位数。你会被要求为该私钥设置一...

阅读全文

linux上编译curl出现 undefined reference to ‘SSL_CTX_set_alpn_protos’

linux上编译curl出现 undefined reference to `SSL_CTX_set_alpn_protos' 或 undefined reference to `SSL_get0_alpn_selected' 解决方案。 最近编译Apache...

阅读全文

欢迎留言