Maven jobs and Java versions compatibility

Maven jobs and Java versions compatibility

Issue

Maven jobs are reporting

java.lang.UnsupportedClassVersionError: Bad version number in .class file

or something like

ERROR: JENKINS-18403 JDK 5 not supported to run Maven; retrying with slave Java and setting compile/test properties to point to <path_to_the_java_vm-used_by_your_slave>

or something like

ERROR: Invalid project setup: jenkins/security/MasterToSlaveCallable : Unsupported major.minor version 52.0 
ERROR: [JENKINS-18403][JENKINS-28294] JDK 'XXXXXX' not supported to run Maven projects. 
ERROR: Maven projects have to be launched with a Java version greater or equal to the minimum version required by the master. 
ERROR: Use the Maven JDK Toolchains (plugin) to build your maven project with an older JDK. 
ERROR: Retrying with slave Java and setting compile/test properties to point to <path_to_the_java_vm-used_by_your_slave>.

Environment

Explanation

Because java serialized classes are exchanged between Jenkins master and Maven Jobs it is required that the JVM used to launch Maven is superior or equal to the version of Java for which Jenkins Master is built for.

Jenkins >= 1.520 (1.531.1 for LTS) requires Java 6 thus Maven jobs must be launched with a JDK >= 6.

Jenkins >= 1.612 (1.625.1 for LTS) requires Java 7 thus Maven jobs must be launched with a JDK >= 7.

Jenkins >= 2.54 (2.60.1 for LTS) requires Java 8 thus Maven jobs must be launched with a JDK >= 8.

This constraint was firstly reported for the Jenkins upgrade from Java 5 to Java 6 as JENKINS-18403 thus the error message

ERROR: JENKINS-18403 JDK 5 not supported to run Maven; retrying with slave Java and setting compile/test properties to point to <path_to_the_java_vm-used_by_your_slave>

This one was wrongly implemented thus for the upgrade to Java 7 you are receiving the same error about JDK 5 while it is JDK 6 (See JENKINS-28294).

Resolution

All Maven Jobs have to be configured to use at the minimum the version required by Jenkins like described above. To ease the change you can use the Configuration Slicing Plugin. This will allow your build to properly start but you may face various problems because you will build your project with a moe recent version of your JDK than what you are really targeting.

There are several solutions to avoid this problem.

On Jenkins side

The simplest workaround could be to define the properties -Dmaven.compiler.source=1.6 -Dmaven.compiler.target=1.6 locally in your maven job settings or globally in Jenkins global configuration for MAVEN_OPTSif you have only Java 6 maven jobs.

NOTE 1: This will work if your maven jobs are using the default Maven behavior to configure the java level rather than enforcing the java level configuration in compiler and other plugins.

NOTE 2: Same thing for Java 7 but you use -Dmaven.compiler.source=1.7 -Dmaven.compiler.target=1.7

In Jenkins, instead of using Maven Jobs you can use FreeStyle jobs with a Maven build step.
This solution requires a manual recreation of jobs.
FreeStyle jobs will offer less features than Maven jobs but they’ll support to launch Maven on any version of java.

At Apache Maven level

Before anything you need to configure the maven compiler plugin to target your oldest version of Java even if you are using a more recent JDK.
If you didn’t configure (directly or in a parent) the compiler plugin you can just add in your pom (for Java 6):

<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
...
  <properties>
    <maven.compiler.source>1.6</maven.compiler.source>
    <maven.compiler.target>1.6</maven.compiler.target>
  </properties>
...

If the compiler plugin is already reconfigured in your project or in a parent pom you may have to use another property or declare the configuration options of the plugin in the plugins or pluginManagement settings.

Sadly configuring the compiler options are often not enough to ensure that you will produce binaries compatible with your target JRE. For exemple, you can use APIs (methods) provided by the new JDK and which are not available in the older version.

To avoid this kind of issue there are 2 solutions at Apache Maven level which will allow you to launch Apache Maven with a Java version superior to the one targetted by your application but without risking to produce an incompatible binary.
With theses solutions you’ll have to update your build but you’ll be able to continue to use your Maven Jobs.

The animal-sniffer solution

In your build you add an additionnal control using the Animal Sniffer plugin to avoid to use in your code some APIs provided by the version of Java used to build.
This solution isn’t 100% safe (it controls only the singatures of methods not their semantics) but it covers a large part of classical errors to build an application for an older version of Java.

<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-enforcer-plugin</artifactId>
        <version>1.4</version>
        <executions>
          <execution>
            <id>check-java-compat</id>
            <goals>
              <goal>enforce</goal>
            </goals>
            <phase>process-classes</phase>
            <configuration>
              <rules>
                <checkSignatureRule implementation="org.codehaus.mojo.animal_sniffer.enforcer.CheckSignatureRule">
                  <signature>
                    <groupId>org.codehaus.mojo.signature</groupId>
                    <artifactId>java16</artifactId>
                    <version>1.0</version>
                  </signature>
                </checkSignatureRule>
              </rules>
            </configuration>
          </execution>
        </executions>
        <dependencies>
          <dependency>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>animal-sniffer-enforcer-rule</artifactId>
            <version>1.14</version>
          </dependency>
        </dependencies>
      </plugin>
...
    </plugins>
  </build>
</project>

A full sample is available here

The toolchains solution

About toolchains :

  • https://maven.apache.org/guides/mini/guide-using-toolchains.html
  • http://www.slideshare.net/aheritier/java-is-evolving-rapidly-maven-helps-you-staying-on-track
  • https://github.com/MavenDemo/java-evolving-en/tree/master/toolchains

With toolchains, Apache Maven will be able to run on a different (version) of the JVM than the JDK used to build your project.
It will allow to run Apache Maven on the same JVM version than your Jenkins Master (for exemple Java JRE 7) while it will use another JDK to build your application (for example Java JDK 5).
With this strategy the targeted version of the JDL is used

On Jenkins side you will need to perform the following actions:

  • Your Maven Job project will be configured to use a JDK 7. You will use the Tool Environment Plugin to install an additional JDK 6 on your slave.
  • You will use the Config File Provider Plugin to define and install a toolchain.xml file used by maven to define where the JDK6 is installed
<toolchains xmlns="http://maven.apache.org/TOOLCHAINS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/TOOLCHAINS/1.1.0 http://maven.apache.org/xsd/toolchains-1.1.0.xsd">
  <toolchain>
    <type>jdk</type>
    <provides>
      <version>1.6</version>
    </provides>
    <configuration>
      <jdkHome>/home/opt/jdk1.6</jdkHome>
    </configuration>
  </toolchain>
</toolchains>

On Maven side:

  • You will configure the maven-toolchain-plugin to tell to Maven to use a JDK 6 to perform all Java related tasks (javac, javadoc …)
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-toolchains-plugin</artifactId>
        <version>1.1</version>
        <executions>
          <execution>
            <goals>
              <goal>toolchain</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <toolchains>
            <jdk>
              <version>1.6</version>
            </jdk>
          </toolchains>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

 

持续集成 踩坑记录—jenkins 升级至 2.110 带来的编译问题

jenkins升级至高版本引发的编译问题

 

jenkins 下载地址:

https://updates.jenkins-ci.org/download/war/

背景:jenkins版本升级

近期公司内部使用jenkins进行了升级,设置中也增加了JDK8的环境选项

问题1:无法正常编译的job

使用jdk8 来lanch maven,执行job构建时,发现部分job无法正常通过编译,报错为:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-javadoc-plugin:2.9:jar (attach-javadocs) on project wycds-console: MavenReportException: Error while creating archive:

初步排查是jdk8中不符合doclint规范的javadoc无法正常生成。

将job设置中jdk切换成低版本jdk7后继续构建发现以下异常:

Exception in thread "main" java.lang.UnsupportedClassVersionError: hudson/remoting/Launcher : Unsupported major.minor version 52.0
    at java.lang.ClassLoader.defineClass1(Native Method)

从报错上看,应该是jenkins需要jdk8的支持。在jenkins官方文档jenkins maven构建中也找到了jenkins 中lauch maven 的jdk最低版本要求:

  • Jenkins >= 1.520 requires Java 6 thus Maven jobs must be launched with Java >= 6.
  • Jenkins >= 1.612 requires Java 7 thus Maven jobs must be launched with Java >= 7.
  • Jenkins >= 2.54 requires Java 8 thus Maven jobs must be launched with Java >= 8.

使用博客服务器上运行中的Jenkins版本检测的方法来查看机器jenkins版本情况

unzip -c jenkins.war META-INF/MANIFEST.MF | egrep ^Jenkins-Version: | awk '{print $2}' | tr -d '\r'
2.110

显然,本机运行jenkins是需要jdk8支持的。所以这个问题不能简单粗暴的通过降低jdk版本来解决。

解决

转向搜索如何禁用jdk8 doclint的相关问题,stackoverflow的问题因javac出错而无法编译的项目解答中给出了三种解决办法:

  • 修复javadoc,使其符合doclint的规范
  • 跳过严格的doclint检查,具体方案在项目pom文件的maven-javadoc-plugin插件中添加以下配置:
<plugins>
  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-javadoc-plugin</artifactId>
    <configuration>
      <additionalparam>-Xdoclint:none</additionalparam>
    </configuration>
  </plugin>
</plugins>
  • 跳过javadoc的编译 mvn -Dmaven.javadoc.skip=true

内部jenkins主要是测试部署和CI集成使用,线上环境均使用jdk7运行,为减少对源码的修改,这里选择第三种方案,即修改job build pom.xml的goals设置为:

mvn -clean package -Dmvn.javadoc.compiler.skip

设置成功后,重新构建job,项目可以使用jdk8 正常构建。

问题2:高编译低运行的异常

在测试环境B上执行相关测试时,client连接到server后未收到任何响应,客户端长时间处于等待状态。打开debug日志,发现相关异常信息

java.lang.NoSuchMethodError: java.util.concurrent.ConcurrentHashMap.keySet()Ljava/util/concurrent/ConcurrentHashMap$KeySetView;
    at xxx.xxx.xxx.xxx.xx.utils.ConcurrentHashSet.iterator(ConcurrentHashSet.java:33) ~[jar-name.jar:na]

NoSuchMethodError报错信息比较异常,因为项目正常编译通过了,而且本地debug时,能正常执行。
google 搜索,发现简书博客Java高编译低运行错误记录了相同的异常信息,结论为jdk8高版本编译,低版本执行的问题。

对应到本项目,该项目对应的job在jenkins升级之后执行过部署操作,使用的jdk 确为JDK8,导致基于JDK 8的bootstrap class编译而成的keySet()方法,其返回值是JDK 8中ConcurrentHashMap$KeySetView这个新增内部类,在jdk7上执行时加载不到该新方法而抛出异常,而开发代码中未捕到该异常并做异常处理,导致服务端逻辑无法走到response client这一步,又client端超时时间设置比较长,所以观察到client一直处于等待状态,服务端一直未响应。

所以,导致异常现象出现的原因其实有两个,第一是高版本编译低版本运行的问题,第二是开发代码异常处理不到位。第一个问题是根源,所以本文关注第一个问题。

值得关注的一点是,其实项目中maven compiler 中指定了source 和 target的编译级别都是jdk7的,但正如博客中所说,source参数指的是源代码级别的语法兼容,而target参数指的是生成release版本的兼容性的class文件,降低版本号来编译,会导致生成class文件被标识为较低版本以供指定的JVM加载,但基于bootstrap class 编译的class文件依然是基于默认jdk的,无法保证运行时的正确性。
这点在apache maven官网文档设置source和target编译级别 中有详细说明。

解决

在博客Java高编译低运行错误中,作者给出了两种解决办法:

  1. 在编译期,使用javac 来指定bootstrap class的路径
  2. 修改代码,使用父类/接口替换子类,即ConcurrentMap替换ConcurrentHashMap声明

第二种方案对代码有侵入,直接放弃掉。第一种方法对单个类的示例操作,不能直接使用于maven compiler项目中。于是还是转向官网寻找解决办法。

还好,apache maven 官网文档使用不同jdk编译很直接地给出了两种解决方案:

  • maven 底层其实也是调用的javac 来执行编译的,故可指定maven-compiler执行时所使用的javac版本,示例如下:
<project>
  [...]
  <build>
    [...]
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.7.0</version>
        <configuration>
          <verbose>true</verbose>
          <fork>true</fork>
          <executable><!-- path-to-javac --></executable>
          <compilerVersion>1.3</compilerVersion>
        </configuration>
      </plugin>
    </plugins>
    [...]
  </build>
  [...]
</project>

指定的javac仅作用于maven compiler插件,不作用于其他插件。 javac_path可以写成硬编码,不过建议最好做成可配置项,具体如下:

<executable>${JAVA_1_4_HOME}/bin/javac</executable>

而JAVA_1_4_HOME 这一属性可以配置在maven的setting.xml文件中,或者设置为系统变量。
具体setting.xml文件的设置可以参考如下示例:

<settings>
  [...]
  <profiles>
    [...]
    <profile>
      <id>compiler</id>
        <properties>
          <JAVA_1_4_HOME>C:\Program Files\Java\j2sdk1.4.2_09</JAVA_1_4_HOME>
        </properties>
    </profile>
  </profiles>
  [...]
  <activeProfiles>
    <activeProfile>compiler</activeProfile>
  </activeProfiles>
</settings>
  • 使用maven toolchains插件。maven构建项目分为以下几步: 使用jdk执行编译(javac) —->使用jdk来生成javadoc (javadoc) —-> 使用jdk来生成jar的签名信息(jarsigner)。在不使用toolchain的情况下,构建过程中的每个插件都需要一个jdk工具来运行。toolchains则提供了一种方式,以统一管理的方式指定构建过程时需要的jdk版本,而构建运行的jdk独立于运行maven的jdk。 前提是,maven版本需要在2.0.9及其以上,具体使用要求有以下两点:
    1. 在项目工程中依赖maven-toolchains-plugin插件
    2. toolchains.xml 需要在building 机器上。 以使用jdk 1.7为例,pom文件配置如下:
<plugins>
 ...
  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.1</version>
  </plugin>
  <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-toolchains-plugin</artifactId>
    <version>1.1</version>
    <executions>
      <execution>
        <goals>
          <goal>toolchain</goal>
        </goals>
      </execution>
    </executions>
    <configuration>
      <toolchains>
        <jdk>
          <version>1.7</version>
          <vendor>sun</vendor>
        </jdk>
      </toolchains>
    </configuration>
  </plugin>
  ...
</plugins>

jdk的具体信息则被放置在toolchains.xml文件中。从maven3.3.1开始,可以使配置项 –global-toolchains file 来执行toolchains.xml文件的位置,但是依然推荐将文件安置在 ${user.home}/.m2路径下。

<?xml version="1.0" encoding="UTF8"?>
<toolchains>
  <!-- JDK toolchains -->
  <toolchain>
    <type>jdk</type>
    <provides>
      <version>1.7</version>
      <vendor>sun</vendor>
    </provides>
    <configuration>
      <jdkHome>/path/to/jdk/1.7</jdkHome>
    </configuration>
  </toolchain>
  <toolchain>
    <type>jdk</type>
    <provides>
      <version>1.6</version>
      <vendor>sun</vendor>
    </provides>
    <configuration>
      <jdkHome>/path/to/jdk/1.6</jdkHome>
    </configuration>
  </toolchain>

  <!-- other toolchains -->
  <toolchain>
    <type>netbeans</type>
    <provides>
      <version>5.5</version>
    </provides>
    <configuration>
      <installDir>/path/to/netbeans/5.5</installDir>
    </configuration>
  </toolchain>
</toolchains>

个人更倾向方案1。
具体到当前的场景,解决方法为:

在jenkins所在机器上copy一份项目的pom.xml文件,路径为$path/pom.xml, $path为jenkins用户有权限访问路径即可。 在原文件的maven-compiler插件配置中添加如下属性:


<configuration>
  <verbose>true</verbose>
  <fork>true</fork>
  <executable>$jdk7_path/bin/javac</executable>
  <compilerVersion>1.7</compilerVersion>
</configuration>

$jdk7_path 为jenkins机器上jdk7的安装路径。

在对应jenkins job设置中添加build 的pre steps,勾选 execute shell。 添加脚本,在项目编译前替换掉pom.xml文件

cp -f $path/pom.xml  ./pom.xml
参考引用

Linux上配置http上网代理

Linux上配置http上网代理

  有些局域网环境上网需要使用代理上网,图形界面的很好解决就设置一下浏览器的代理就好了,但是Linux纯命令行的界面就需要手动配置了。

如果要全局用户使用应用于所有的Shell,就需要修改 /etc/profile 文件

1 # vi /etc/profile

在文件中添加以下配置

1 http_proxy=proxy.abc.com:8080  
2 https_proxy=$http_proxy  
3 ftp_proxy=user:password@proxy.abc.com:8080  
4 no_proxy=*.abc.com,10.*.*.*,192.168.*.*,*.local,localhost,127.0.0.1  
5 export http_proxy https_proxy ftp_proxy no_proxy

 

其中:
http_proxy:http协议使用代理服务器地址;
https_proxy:https协议使用安全代理地址;
ftp_proxy:ftp协议使用代理服务器地址;
user:代理使用的用户名;
password:代理使用用户名的密码;
proxy.abc.com:代理地址,可以是IP,也可以是域名;
8080:使用的端口;
no_proxy:不使用代理的主机或IP。

保存退出,注销重新登陆系统即可生效。

此方法只适合配置http代理,使用socket代理上网的另有其他配置方法。

解决Exchange 2016 Chrome浏览器无法登陆OWA以及ECP问题

很久没写博了,其实也积攒了一些可以写的东西,准备陆续拿出来分享一下,最近遇到一个很奇葩的问题,在Azure上搭了一套Exchange 2016的测试环境,搭建的过程就不说了,Exchange 2016基本和2013安装没什么太大区别。

 

安装的过程很顺利,但是安装完成之后,偶然间突然发现一个问题,那就是ECP和OWA在IE浏览器登陆一切正常,但是在Chrome上却发现ECP和OWA无法正常登陆,提示ERR_SPDY_INADEQUATE_TRANSPORT_SECURITY

 

我累个擦这啥玩意,IE能上,chrome却不能,这个问题确实有点怪,正常来讲,IE既然能连上,chrome应该也没什么问题,这是什么原因呢?

wKiom1m_kdWjrDteAADFT7P6_QE011.png-wh_50

 

IE既然可以正常连接,那么问题应该就出在chrome身上了,大家都知道google对安全性的重视是非常高的,一般来说在浏览器安全方面的一些革新和措施都是google先发起,然后各家先后跟风,比如之前对各种违规的CA厂商的不再信任的措施,而我用的一般又都是chrome比较新的版本,顺着这个思路,准备尝试着换一个比较旧的版本来试一下,下载了一个Chrome 42的版本之后,发现果然就没再遇到之前的问题

这样基本上就可以确认是Chrome版本的问题了,很有可能是google又出了什么安全措施,导致了这个问题,之后又google一下报错的代码,果然发现了不少事情

 

原来是Chrome已经停止了对很多过时的协议以及加密算法的支持,这也导致用这些加密算法的连接都不会被chrome接受,比如PCT 1.0 SSL 2.0 SSL 3.0 以及RC2 128/128  RC2 56/128等等

问题既然确认了,那么如何解决呢,首先先安利一些很好用的工具和网站

1.https://www.ssllabs.com/ssltest/

这个网站也可以查看网站的证书信息,使用的加密算法,并且还会对其进行评级,对于查看这种因为算法和协议引起的问题实在是再好不过了,有兴趣的可以试试,以下是测评的一个截图

wKiom1m_mIShgtA4AAHi1sZVquo938.png-wh_50

2.TestSSLServer

这是个本地的小工具,可以看到本地服务器使用的协议和算法

wKioL1m_l0XBEJjSAAG8wWjIu14478.png-wh_50

最后问题是怎么解决的,有两种办法

解决办法之一:禁用以下协议和加密算法,稍微麻烦点

MultiProtocol Unified Hello

PCT 1.0

SSL 2.0

SSL 3.0

preferred via registry like:

; Disable PCT 1.0

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\PCT 1.0\Server]

“DisabledByDefault”=dword:00000001

“Enabled”=dword:00000000

; Disable SSL 2.0

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0]

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Client]

“DisabledByDefault”=dword:00000001

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 2.0\Server]

“Enabled”=dword:00000000

; Disable SSL 3.0

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0]

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Client]

“DisabledByDefault”=dword:00000001

“Enabled”=dword:00000000

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\SSL 3.0\Server]

“DisabledByDefault”=dword:00000001

“Enabled”=dword:00000000

and

NULL Cipher

DES 56/56

RC2 (fully)

RC4 (fully)

via

; Disable weak ciphers

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\DES 56/56]

“Enabled”=dword:00000000

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\NULL]

“Enabled”=dword:00000000

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC2 128/128]

“Enabled”=dword:00000000

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC2 40/128]

“Enabled”=dword:00000000

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC2 56/128]

“Enabled”=dword:00000000

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC2 56/56]

“Enabled”=dword:00000000

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 128/128]

“Enabled”=dword:00000000

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 40/128]

“Enabled”=dword:00000000

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 56/128]

“Enabled”=dword:00000000

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\RC4 64/128]

“Enabled”=dword:00000000

 

解决办法之二,介绍一个很厉害的小工具,下边是下载地址,这个工具可以支持一键设置服务器,自动配置成最佳实践的状态,省去了不少时间

https://www.nartac.com/Products/IISCrypto

下载完成后,接受用户协议

wKiom1m_mW6yUhMNAAuaaXT0GZc776.png-wh_50

然后可以看到当前服务器使用的加密算法和协议

wKioL1m_mUWx2pgJAAd-vgBAVLQ671.png-wh_50

接下来直接点击左下角的best practices,然后重启服务器

wKiom1m_mXvw0U1zAAeRv4jJAHQ818.png-wh_50

重启之后chrome问题解决,我只想说,实在是太特么方便了

wKioL1m_mfSxHkUbAAHn3Oqb9LI206.png-wh_50

最后,问题解决,皆大欢喜。

Linux Kernel Crash–hung_task_timeout_secs

前一阵产品升级Linux kernel的版本,升级后版本号是2.6.32-100.24.1.el5。
QA在vmWare上运行扩充磁盘空间时,偶尔会发现console上打出一堆kernel的异常信息,如下图

关键信息就是“hung_task_timeout_secs”,第一次遇到kernel的问题,而且这个kernel是我们自己从源码build出来的,也拿不准是哪里的问题,于是开始google…
找了半天,终于大概弄明白了,这应该是Linux kernel的一个bug,在redhat和centos的bugzilla中都能找到跟踪号,而且很多人都在讨论,但是似乎Linux的人不把这个问题当回事,问题出了很久也没有修改,或许他们认为是用户的使用问题。
https://bugzilla.redhat.com/show_bug.cgi?id=605444#c43
http://bugs.centos.org/view.php?id=4515
http://www.centos.org/modules/newbb/viewtopic.php?topic_id=36408&forum=55

网上找的一段解释:
By default Linux uses up to 40% of the available memory for file system caching.
After this mark has been reached the file system flushes all outstanding data to disk causing all following IOs going synchronous.
For flushing out this data to disk this there is a time limit of 120 seconds by default.
In the case here the IO subsystem is not fast enough to flush the data withing 120 seconds.
This especially happens on systems with a lot of memory.
The problem is solved in later kernels
意思就是说,一般情况下Linux写磁盘时会用到缓存,这个缓存大概是内存的40%,只有当这个缓存差不多用光时,系统才会将缓存中的内容同步写到磁盘中。但是操作系统对这个同步过程有一个时间限制,就是120秒。如果系统IO比较慢,在120秒内搞不定,那就会出现这个异常。这通常发生在内存很大的系统上。
原因找到了,怎么解决呢?网上也有一些方案,比如
1. /sbin/sysctl -w vm.dirty_ratio=10
2. echo noop > /sys/block/sda/queue/scheduler
3. /sbin/sysctl -w kernel.hung_task_timeout_secs = 0
第一个方案是调整缓存占内存的比例,降到10%,这样的话较少的缓存内容会被比较频繁地写到硬盘上,IO写会比较平稳
第二个方案是修改系统的IO调度策略,使用noop的方式,这是一种基于FIFO的最简单的调度方式
第三个方案是不让系统有那个120秒的时间限制,希望就是我慢就慢点,你等着吧,实际上操作系统是将这个变量设为长整形的最大值。
这三个方案好像都很有道理,应该能搞定这个吧?但是,往往事与愿违,这三种方案经QA验证后一个也没发挥作用,问题依旧偶尔出现。

百思不得姐抓狂,但是肯定是因为磁盘IO繁忙导致的系统故障,因为故障出现时,JBoss正在启动,vmWare还在扩展磁盘空间。自己写个脚本跑跑看:

  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4. use POSIX qw(setsid);
  5. my $i = 0;
  6. for ($i=0; $i < 1000; $i++) {
  7. if (my $pid = fork()) {
  8. }else {
  9. setsid or die “can not start a new session: $!”;
  10. close (STDOUT);
  11. `nice -n -20 /bin/dd if=/dev/zero of=/var/iotest.”$i” bs=4M count=10 oflag=direct` ;
  12. exit 0;
  13. }
  14. }

发现如果dd命令带着”oflag=direct”参数,问题就会出现,去掉就没问题。JBoss启动时有大量的MySQL读写,检查了产品用的MySQL配置文件,发现有这么一个配置:innodb_flush_method=O_DIRECT。什么是Direct IO呢,这里就不多说了,自己上网搜吧。

让QA把这个参数改成默认参数,问题消失了,但是QA说不能改这个参数,会影响系统性能。。。无语。。。

最后的workaround是扩展磁盘空间时,先把JBoss、MySQL都停掉,扩展完后再启动

Linux上如何查看某个进程的线程

这里提供了在Linux上显示某个进程的线程的几种方式。

方法/步骤

  1. 问题: 我的程序在其内部创建并执行了多个线程,我怎样才能在该程序创建线程后监控其中单个线程?我想要看到带有它们名称的单个线程详细情况(如,CPU/内存使用率)。

    线程是现代操作系统上进行并行执行的一个流行的编程方面的抽象概念。当一个程序内有多个线程被叉分出用以执行多个流时,这些线程就会在它们之间共享 特定的资源(如,内存地址空间、打开的文件),以使叉分开销最小化,并避免大量高成本的IPC(进程间通信)通道。这些功能让线程在并发执行时成为一个高 效的机制。

    在Linux中,程序中创建的线程(也称为轻量级进程,LWP)会具有和程序的PID相同的“线程组ID”。然后,各个线程会获得其自身的线程 ID(TID)。对于Linux内核调度器而言,线程不过是恰好共享特定资源的标准的进程而已。经典的命令行工具,如ps或top,都可以用来显示线程级 别的信息,只是默认情况下它们显示进程级别的信息。

    这里提供了在Linux上显示某个进程的线程的几种方式。

     

    方法一:PS

    在ps命令中,“-T”选项可以开启线程查看。下面的命令列出了由进程号为<pid>的进程创建的所有线程。

    1.$ ps -T -p <pid>

    Linux上如何查看某个进程的线程
  2. “SID”栏表示线程ID,而“CMD”栏则显示了线程名称。

     

    方法二: Top

    top命令可以实时显示各个线程情况。要在top输出中开启线程查看,请调用top命令的“-H”选项,该选项会列出所有Linux线程。在top运行时,你也可以通过按“H”键将线程查看模式切换为开或关。

    1.$ top -H

    Linux上如何查看某个进程的线程
  3. 要让top输出某个特定进程<pid>并检查该进程内运行的线程状况:

    $ top -H -p <pid>

    Linux上如何查看某个进程的线程
  4. 方法三: Htop

    一个对用户更加友好的方式是,通过htop查看单个进程的线程,它是一个基于ncurses的交互进程查看器。该程序允许你在树状视图中监控单个独立线程。

    要在htop中启用线程查看,请开启htop,然后按<F2>来进入htop的设置菜单。选择“设置”栏下面的“显示选项”,然后开启“树状视图”和“显示自定义线程名”选项。按<F10>退出设置。

    Linux上如何查看某个进程的线程
  5. 现在,你就会看到下面这样单个进程的线程视图。

    Linux上如何查看某个进程的线程

Out of Socket memory 内存溢出解释

20120830发现日本服务器 27和28 有报错日志。详细如下:

Aug 31 18:25:36 collect-28 kernel: printk: 58 messages suppressed.
Aug 31 18:25:36 collect-28 kernel: Out of socket memory

故障排查分析:

第一条日志分析:

查找信息,Aug 31 18:25:36 collect-28 kernel: printk: 58 messages suppressed. 此报错需要修改内核信息如下;
(1) 加大 ip_conntrack_max 值:

查出原本的 ip_conntrack_max 值,指令: cat /proc/sys/net/ipv4/ip_conntrack_max
写入理想的数值 (每一个 ip_conntrack buffer 会占用 292 Bytes)
指令: echo “数值” > /proc/sys/net/ipv4/ip_conntrack_max
例如: echo “163840” >/proc/sys/net/ipv4/ip_conntrack_max

这个效果是暂时的, 如果要每次开机都使用新的数值, 需将上述指令写入 /etc/rc.d/rc.local
或是在 /etc/sysctl.conf 加入: net.ipv4.ip_conntrack_max = 数值
或使用指令: sysctl -w net.ipv4.ip_conntrack_max=数值

(2): 降低 ip_conntrack timeout 时间

重设:ip_conntrack_tcp_timeout_established (原值: 432000, 单位: 秒)
指令:echo “数值” > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_established
例如:echo “180” > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_established

开机自动设置的作法同方法(1).

(3):开启 tcp_syncookies

重设:tcp_syncookies (默认值 0)
例如:echo ‘1’> /proc/sys/net/ipv4/tcp_syncookies

修改内核配置文件,报错日志无效,

第二条报错日志分析:

查找信息,Aug 31 18:25:36 collect-28 kernel: Out of socket memory,此报错需要修改内核信息如下;

两种情况会出发 “Out of socket memory” 的信息:
1.有很多的孤儿套接字(orphan sockets)
2.tcp socket 用尽了给他分配的内存

首先看看情况 2。对于 TCP socket 来说,使用 pages 来计数的,而非 bytes,一般情况下 1 page = 4096 bytes。page 大小可以通过下面命令获得:
$ getconf PAGESIZE
4096

查看内核分配了多少的内存给 TCP:
$ cat /proc/sys/net/ipv4/tcp_mem
69618 92825 139236
第一个数字表示,当 tcp 使用的 page 少于 69618 时,kernel 不对其进行任何的干预
第二个数字表示,当 tcp 使用了超过 92825 的 pages 时,kernel 会进入 “memory pressure”
第三个数字表示,当 tcp 使用的 pages 超过 139236 时,我们就会看到题目中显示的信息

查看 tcp 实际用的内存:
$ cat /proc/net/sockstat
sockets: used 116
TCP: inuse 3 orphan 0 tw 4 alloc 4 mem 110
UDP: inuse 1 mem 1
UDPLITE: inuse 0
RAW: inuse 0
FRAG: inuse 0 memory 0
可以看到,实际使用的 mem(110) 远远小于 69618,所以,“Out of socket memory”的错误是由于第一种情况引起的。

关于 orphan socket 的解释,请看这里。orphan socket 对于应用程序来说,意义不大,这也是内核要限制被 orphan socket 消耗内存的原因。而对于 web server 来说,有大量的 orphan socket 也属正常,那么多的连接放在那儿了。
查看 orphan socket 限制:
$ cat /proc/sys/net/ipv4/tcp_max_orphans

对比当前系统中的:
$ cat /proc/net/sockstat
sockets: used 14565
TCP: inuse 35938 orphan 21564 tw 70529 alloc 35942 mem 1894

由于内核代码中有个位运算,所以实际的跟最大的是 2x 或者是 4x 的关系。现在根据实际情况,将 tcp_max_orphans 调到一个合理的值就可以了。原则上该值建议只增大,另外,每个 orphan 会消耗大概 64KB 的内存。

还有个叫 tcp_orphan_retries 参数,对于 web server,可以减小。

修改了上面的一些配置参数,报错日志还是在一直往外报。

####################################################################################################
net.ipv4.tcp_max_orphans = 18000

最后修改值到18000,Out of socket memory报错解决。

net.ipv4.tcp_max_tw_buckets = 10000

最后修改值到net.ipv4.tcp_max_tw_buckets = 10000,kernel: printk: xxx messages suppressed.报错解决。

记得参数修改完成执行 sysctl -p

Chrome+SwitchyOmega+Shadowsocks 图文教程完整篇

Chrome+SwitchyOmega+Shadowsocks 网上有很多教程,不过但是大部分其实忽略了细节,导致很多新用户按教程配置后,导致代理无法使用的情况。 其实这个问题的核心就是端口的配置出现了问题,本着那啥精神:我还是一次性全部贴出教程吧,希望对大家有用!

下面开始正题:

1:电脑端下载 shadowscoks(最新版下载链接),打开后开始配置,第一步选择 “编辑服务器

Ps:我一般都用“扫描屏幕上的二维码”功能,这样免得出错~~

2:在这里配置上你获得的 shadowsocks 的服务器 IP、服务器端口和密码;代理端口可以只有选择的,不过一般都选择 1080

3:点击下 “从 GFWLIST 更新本地 PAC“ 更新完 PAC 后选择 使用本地 PAC

4:系统代理模式 选择 PAC 模式

5: 正常来说运行了 ShadowsocksR 后就可以直接访问 Chrome 应用商店安装SwitchyOmega了(在 Github 上下载最新版安装包)。

https://github.com/FelisCatus/SwitchyOmega/releases

 

chrome 安装好 SwitchyOmega 后选择 新建情景模式情景模式名称 可以随便选择,模式类型选择第一个 代理服务器

6:代理服务器按下表配置就 okay 了,到此也就可以正常使用了 PAC 模式了。但是为了更进一步更方便的使用我们还可以增加一步。

7:选择右边的 自动切换/auto switch 最下面的 规则列表设置 按下表设置就好了 规则网址也是我图上给出的这个。

GFWList 规则列表地址更新
GFWList 项目现在已经迁移到 Github。因此, GFWList 规则列表的发布地址也发生了变化。请注意在自动切换中,将规则列表网址手动改为新地址! SwitchyOmega 不知道您在用 GFWList ,也不会自动更改列表的 URL 地址,所以只能请您手动改一下了。具体的要修改的位置在上面一张图上有标识,是深红色下划线标出的那个文本框(截图中仍然是旧地址,新地址见下方)。

新地址: https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt

旧地址: https://autoproxy-gfwlist.googlecode.com/svn/trunk/gfwlist.txt (请不要再使用这个地址了,已经很久不更新了。)

8:然后最关键的一步,在浏览器右上角点击插件按钮,然后选择自动切换规则模式,这样只要在规则列表里面的网站都会翻墻访问。

对于没在翻墻规则里面的网站,可以自己添加规则(无法访问的时候插件那图标会有显示,点击后就可以看到快速添加方法。)

SSL A+评分 最简配置

server {
listen 443;
server_name iws-test.xxx.com;
ssl on;
ssl_certificate /opt/nginx/certs/STAR.xxx.crt;
ssl_certificate_key /opt/nginx/certs/STAR.xxx.key;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
ssl_protocols TLSv1.2;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256;
ssl_prefer_server_ciphers on;
ssl_ecdh_curve secp384r1;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security “max-age=63072000; includeSubDomains; preload”;
location / {
proxy_pass http://124.204.50.194:30572/;
# proxy_pass https://123.57.158.240:3000/;
proxy_read_timeout 3600s;
proxy_set_header Host $host;
proxy_set_header X-Real-Ip $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
}
access_log /opt/nginx/log/iws8-test.xxx.com-3000.access.log;
error_log /opt/nginx/log/iws8-test.xxx.com-3000.error_log;
}

加强 Nginx 的 SSL 安全

本文向你们展示如何在nginx的web服务器上设置更强的SSL。我们是通过使SSL无效来减弱CRIME攻击的这种方法实现。不使用在协议中易受攻击的SSLv3以及以下版本并且我们会设置一个更强的密码套件为了在可能的情况下能够实现Forward Secrecy,同时我们还启用HSTS和HPKP。这样我们就有了一个更强、不过时的SSL配置并且我们在Qually Labs SSL 测试中得到了A等级。

TL:DR:Copy-pastable strong cipherssuites for NGINX, Apache and Lighttpd: https://cipherli.st

这个说明是在Digital Ocean VPS是测试过的。如果你喜欢这个说明指到并且支持我的网站,通过下面的链接预定一个Digital Ocean VPS。

https://www.digitalocean.com/?refcode=7435ae6b8212

这个说明是在21世纪2014年的1月份下颁布的更严格的要求(早就是这样了,如果你你按照这个标准,就能得到A等级)下工作的。

This tutorial is also available for Apache
This tutorial is also available for Lighttpd

This tutorial is also available for FreeBSD, NetBSD and OpenBSD over at the BSD Now podcasthttp://www.bsdnow.tv/tutorials/nginx

通过下面链接你能获得有关主题更多信息:

我们在nginx的设置文档中如下编辑

/etc/nginx/sited-enabled/yoursite.com (On Ubuntu/Debian)或者在

/etc/nginx/conf.d/nginx.conf (On RHEL/CentOS).

对于整个说明文档,你需要编辑服务器配置的服务器那块和443端口(SSL配置)。在说明文档的最后,你会发现实现了样例的配置。

确保在编辑之前做了备份!

dust_wang

dust_wang
翻译于 3年前
1人顶
 翻译得不错哦!

BEAST攻击和RC4算法

简言之,就是通过篡改加密算法CBC密码块的加密模式,部分加密流量可以被偷偷地解密。更多的信息请参照以上链接。

新版本的浏览器客户端可以缓解BEASE攻击。建议禁用所有的TLS 1.0密码并且只是用RC4。然而,[RC4有一个不断增加的列表来防止攻击],(http://www.isg.rhul.ac.uk/tls/)其中的很多都将理论和现实交叉在一起。而且,这就是为什么NSA已经破解了RC4,他们所谓的“重大的突破”。

禁用RC4有几个结果。一、使用差劲儿浏览器的用户将使用3DES来代替。3-DES比RC4更安全。但是就意味着更加昂贵。你的服务器会因为这样的用户开销更大。二、RC4可以缓解BEAST攻击。因此,禁用RC4使TLS 1用户容易受到攻击,通过移动他们AES-CBC(通常的服务器端的BEAST“修复”是优先考虑高于一切的RC4)。我很确信,在BEAST上RC4上的缺陷明显大于风险。确实,客户端的缓解(chrome和火狐都提供)BEAST已不再是个问题。但对于增长RC4的风险:随着时间的推移更多的密码分析将很表面化。

dust_wang

dust_wang
翻译于 3年前
1人顶
 翻译得不错哦!

FREAK攻击

FREAK是在密码专家小组在INRIA, Microsoft Research and IMDEA所发现的一种中间人攻击。FREAK就是“Factoring RSA-EXPORT Keys .”。这种攻击可以追溯到90世纪90年代,也就是在美国政府禁止出售加密软件到海外的时候,除非输出的密码套件中加密密钥的长度不超过512位。

被证明是一些先进的TLS客户端-包括苹果的SecureTransport和OpenSSL-有一个Bug在里面。这个Bug造成了它们接受了RSA密钥的输出等级甚至当客户端都不要求RSA的密钥输出等级。这个Bug造成的影响还是相当严重的:假如客户端是易受攻击的并且服务器支持输出RSA,它允许第三人通过一个活跃的攻击者来减弱连接的质量进行攻击。

这里是两部分服务器必须接受的“RSA输出等级”攻击。

MITM攻击过成如下:

  • 在客户端的Hello消息中,它请求一个标准的“RSA”密码套件。
  • MITM攻击者改变这个消息为了得到“RSA的输出”.
  • 服务器返回一个512位的RSA输出密钥,并用它的永久密钥签名。
  • 由于OpenSSL和SecureTransport存有bug,客户端就接受了这个弱密钥。
  • 攻击者分析RSA模块为了恢复正在通信时RSA的解密密钥。
  • 当客户端把加密的“预备主密钥”发送给服务器时,攻击者现在可以解密它从而得到TLS的“主密钥”。
  • 从现在起,攻击者就可以看到明文了,并且可以注入任何他想的东西。

这个页面上提供密码套件但是支持密码输出等级。确保你的OpenSSl是最近更新过的版本而且你的客户端也要使用最新的软件。

dust_wang

dust_wang
翻译于 3年前
1人顶
 翻译得不错哦!

Heartbleed(心脏出血)

Hearbleed是一个在2014年四月OpenSSL密码库里被发现的安全漏洞,它被广泛用在运输层(TLS)协议的实施中。Heartbleed可能被使用不管是否使用了一个易受攻击的OpenSSL,比如说在一个服务器或者客户端使用。它是在DTLS心跳扩展(RFC6520)由不合适的输入确认(因为没有边界检查)所造成,因此这个漏洞的名字为“心跳”.这个漏洞被划为一个重读的缓冲区,更多超出允许的数据被读出。

哪些版本的OpenSSL被Heartbleed影响?

不同版本的情况:

  • OpenSSL 1.0.1 到 1.0.1f (包括) 受攻击。
  • OpenSSL 1.0.1g不受攻击。
  • OpenSSL 1.0.0的分支不受攻击。
  • OpenSSL 0.9.8 的分支不受攻击。

OpenSSL在2011年12月发现这个漏洞而且在2012年3月14日发布OpenSSL1.0.1之前一直没有采取措施。2014年4月7号发布的OpenSSL1.0.1g修复了这个漏洞。

通过更新OpenSSL就可以免受这个漏洞带来的攻击。

dust_wang

dust_wang
翻译于 3年前
1人顶
 翻译得不错哦!

SSL 压缩(犯罪攻击)

通常来说,犯罪攻击使用 SSL 压缩来施展它的魔法。SSL 压缩在 nginx1.1.6+/1.0.9+ 中默认是关闭的(如果使用 openssl 1.0.0+).

如果你正在使用 nginx 或者 OpenSSL 其他早期版本,并且你的发行版并没有回迁此选项,那么你需要重新编译不支持 ZLIB 的 OpenSSL。这将禁止使用DEFLATE压缩方法来使用 OpenSSL。如果你这样做,那么你仍然可以使用常规的HTML DEFLATE压缩。

SSLV2 与 SSLv3

SSL v2 并不安全,因此我们需要禁用它。我们也可以禁用 SSL v3,当 TLS 1.0 遭受一个降级攻击时,可以允许一个攻击者强迫使用 SSL v3 来连接,因此禁用“向前保密”。

再次编辑此配置文件:

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
crossmix

crossmix
翻译于 3年前
2人顶
 翻译得不错哦!

贵宾犬攻击和TLS-FALLBACK-SCSV

SSLv3允许利用“贵宾犬 POODLE”漏洞,这是禁用它的一个主要原因。Google已经提议一种叫TLSFALLBACKSCSV的SSL/TLS的拓展,旨在防止强制SSL降级。以下是升级后自动启用的OpenSSL版本:

  • OpenSSL 1.0.1 有 TLSFALLBACKSCSV 在 1.0.1j 及更高的版本.
  • OpenSSL 1.0.0 有 TLSFALLBACKSCSV 在 1.0.0o 及更高的版本.
  • OpenSSL 0.9.8 有 TLSFALLBACKSCSV 在 0.9.8zc 及更高的版本.

更多的信息请参阅NGINX文档

密码套件

Forward Secrecy 确保了在永久密钥被泄漏的事件中,会话密钥的完整性。PFS 实现这些是通过执行推导每个会话的新密钥来完成。

这意味着当私有密钥被泄露不能用来解密SSL流量记录。

密码套件提供 Perfect Forward Secrecy 暂时使用 Diffie-Hellman 密钥交换的形式。他们的缺点是开销大,这可以通过使用椭圆曲线的变异的改进。

dust_wang

dust_wang
翻译于 3年前
1人顶
 翻译得不错哦!

我建议以下两个密码套件,后者来自 Mozilla 基金会。

推荐的密码套件:

ssl_ciphers 'AES128+EECDH:AES128+EDH';

推荐的密码套件向后兼容(IE6 / WinXP):

ssl_ciphers "ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";

如果您的 OpenSSL 是旧版本,不可用密码将被自动丢弃。总是使用完整的密码套件,让OpenSSL选它所支持的。

密码套件的顺序非常重要,因为它决定在优先级算法将被选中。上面的建议重视算法提供完美的向前保密。

老版本的 OpenSSL 可能不会返回算法的完整列表。AES-GCM 和一些 ECDHE 相当近,而不是出现在大多数版本的 Ubuntu OpenSSL 附带或 RHEL。

优先级逻辑

  • 首先选择 ECDHE + AESGCM 密码。这些都是 TLS 1.2 密码并没有受到广泛支持。这些密码目前没有已知的攻击目标。
  • PFS 密码套件是首选,ECDHE 第一,然后 DHE。
  • AES 128 更胜 AES 256。有讨论是否 AES256 额外的安全是值得的成本,结果远不明显。目前,AES128 是首选的,因为它提供了良好的安全,似乎真的是快,更耐时机攻击。
  • 向后兼容的密码套件,AES 优先 3DES。暴力攻击 AES 在 TLS1.1 及以上,减轻和 TLS1.0 中难以实现。向后不兼容的密码套件,3DES 不存在.
  • RC4 被完全移除. 3DES 用于向后兼容。 查看讨论 #RC4_weaknesses

强制性的丢弃

  • aNULL 包含未验证 diffie – hellman 密钥交换,受到中间人这个攻击
  • eNULL 包含未加密密码(明文)
  • EXPORT 被美国法律标记为遗留弱密码
  • RC4 包含了密码,使用废弃ARCFOUR算法
  • DES 包含了密码,使用弃用数据加密标准
  • SSLv2 包含所有密码,在旧版本中定义SSL的标准,现在弃用
  • MD5 包含所有的密码,使用过时的消息摘要5作为散列算法
超级大富

超级大富
翻译于 3年前
1人顶
 翻译得不错哦!

其它的设置

确保你已经添加了以下几行:

ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;

在SSLv3或这是TLSv1握手时选择一个密码,通常是使用客户端的偏好。如果这个指令是启用的,那么服务器反而是使用服务器的偏好。

更多关于SSL preferserver密码的信息

更多关于SSL密码的信息

向前保密(Forward Secrecy)与Diffie Hellman Ephemeral Parameters

向前保密的概念很简单:客户端和服务器协商一个可靠的密钥,并在会话结束后销毁。服务器中的RSA私钥用来签名客户端和服务器之间交换的Diffie-Hellman密钥。副主密钥从Diffie-Hellman握手中得到,并用于加密。由于副主密钥在客户端和服务器之间的连接中是明确具体的,并用于有限的时间,因此被叫作Ephemeral(短暂的)。

由于有Forward Secrecy,即使攻击者持有服务器的私钥,也不能够解密过去的会话。私钥仅仅用来签名DH(Diffie-Hellman)的握手,它并没有泄漏副主密钥。Diffie-Hellman确保了副主密钥不会离开客户端和服务器,也不会被中间人截获。

dust_wang

dust_wang
翻译于 3年前
1人顶
 翻译得不错哦!

1.4.4所有的nginx版本在往Diffiel-Hellman输入参数时依赖OpenSSL。不幸的时,这就意味着Ephemeral Diffiel-Hellman(DHE)会使用OpenSSL的这一缺陷,包括一个1024位的交换密钥。由于我们正在使用一个2048位的证书,DHE客户端比非ephemeral客户端将使用一个更弱的密钥交换。

我们需要产生一个更强的DHE参数:

cd /etc/ssl/certs
openssl dhparam -out dhparam.pem 4096

然后告诉nginx在DHE密钥交换的时候使用它:

ssl_dhparam /etc/ssl/certs/dhparam.pem;

OCSP 适用

在和服务器连接的时候,客户端通过使用证书撤销列表(CRL)来验证服务器证书的有效性,或者是使用在线证书状态协议(OCSP)记录。但是CRL的问题是:CRL的列表项不断增多,而且需要不断地下载。

dust_wang

dust_wang
翻译于 3年前
1人顶
 翻译得不错哦!

OCSP是更轻量级的,因为它一次只获取一条记录。但是副作用是,当连接到服务器的时候,OCSP请求必须发送到第三方响应者,这增加了延迟,以及失败的可能。实际上,OCSP响应者由CA操控,由于它常常不可靠,导致浏览器由于收不到适时的响应而失败。这减少了安全性,因为它允许攻击者对OCSP响应者进行DoS攻击来取消验证。

解决方案是在TLS握手期间,允许服务器发送缓存的OCSP记录,这样来绕过OCSP响应者。这个技术节省了在客户端和OCSP响应者之间的一个来回,称为OCSP闭合(OCSP Stapling)。

服务器只在客户端请求的时候,发送一个缓存的OCSP响应,通过对CLIENT HELLO的status_request TLS拓展来声明支持。

大多数服务器都会缓存OCSP响应到48小时。在常规间隔,服务器会连接到CA的OCSP响应者来获取最新的OCSP记录。OCSP响应者的位置是从签名证书的Authority Information Access 字段来获取。

请参阅我的关于在NGINX上增加OCSP闭合(OCSP stapling)的教程

HTTP Strict Transport Security

如果可能,你应该开启 HTTP Strict Transport Security (HSTS),它指示浏览器只通过HTTPS来访问你的站点。

请参阅我的文章关于如何配置HTST。 

HTTP Public Key Pinning Extension

你同样应该开启 HTTP Public Key Pinning Extension

Public Key Pinning 意味着证书链必须包含处于白名单之中的公钥。它确保只在白名单中的CA可以对*.example.com进行签名,而不是浏览器中保存的任何一个CA。

我已经写了关于它的一篇文章,包含背景理论和配置实例,针对 Apache, Lighttpd 以及 NGINX:https://raymii.org/s/articles/HTTPPublicKeyPinningExtension_HPKP.html

配置示例

server {

  listen [::]:443 default_server;

  ssl on;
  ssl_certificate_key /etc/ssl/cert/raymii_org.pem;
  ssl_certificate /etc/ssl/cert/ca-bundle.pem;

  ssl_ciphers 'AES128+EECDH:AES128+EDH:!aNULL';

  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_session_cache shared:SSL:10m;

  ssl_stapling on;
  ssl_stapling_verify on;
  resolver 8.8.4.4 8.8.8.8 valid=300s;
  resolver_timeout 10s;

  ssl_prefer_server_ciphers on;
  ssl_dhparam /etc/ssl/certs/dhparam.pem;

  add_header Strict-Transport-Security max-age=63072000;
  add_header X-Frame-Options DENY;
  add_header X-Content-Type-Options nosniff;

  root /var/www/;
  index index.html index.htm;
  server_name raymii.org;

}

结论

如果你应用了上面的配置文件,你需要重启nginx:

# Check the config first:
/etc/init.d/nginx configtest
# Then restart:
/etc/init.d/nginx restart

现在使用 SSL 实验室测试(SSL Labs tes)看看你是否得到一个漂亮的A。同时,当然,拥有一个安全的,牢靠的,作为未来样例的SSL配置。