[Gradle] SpringBootの組み込まれてるTomcatのバージョンを変更したい

こむろ@札幌です。

小ネタです。1月前ほどにTomcatの脆弱性がアナウンスされてました。それも相当広いバージョンで。

JVNVU#90211511 Apache Tomcat の複数の脆弱性に対するアップデート

  • Apache Tomcat 9.0.0.M1 から 9.0.0.M18 まで
  • Apache Tomcat 8.5.0 から 8.5.12 まで
  • Apache Tomcat 8.0.0.RC1 から 8.0.42 まで
  • Apache Tomcat 7.0.0 から 7.0.76 まで
  • Apache Tomcat 6.0.0 から 6.0.52 まで

SpringBootで組んだWebアプリケーションは、開発時のバージョンのまま止まっていることが多く、こういった脆弱性が発見されたりした時には、すでに現行と2バージョンも違うなんていうこともザラです。 本来FrameworkのバージョンアップでサクッとMigrationできれば問題ないんでしょうけど、なかなかそううまくはいきません。 1バージョン新しくなった時に非推奨となったクラスが、2バージョンあとでは跡形もなく消えているなんてこともよくあります。例: SpringApplicationConfiguration

さて自分が担当していたシステムのSpringBootのバージョンはと言うと1.3.2.RELEASE

確実に対象だろうな、と思いながらも一抹の希望を捨てずに組み込まれているTomcatのバージョンを確認します。 (/ω・\)チラッ

Spring Boot Reference Guide - Appendix E. Dependency versions

GroupId Artifact Id Version
org.apache.tomcat.embed tomcat-embed-core 8.0.30

対象でした。無念。対応としてTomcatを以下のバージョンへアップデートする必要があります。

  • Apache Tomcat 9.0.0.M19
  • Apache Tomcat 8.5.13
  • Apache Tomcat 8.0.43
  • Apache Tomcat 7.0.77
  • Apache Tomcat 6.0.53

対応としてすぐ考えられるのはSpringBootのバージョンアップです。フレームワークごとバージョンアップしてしまえば中に組み込まれているTomcatもバージョンアップしているはずなので、当然の結果です。それぞれのフレームワークに組み込まれているTomcatのバージョンは以下のリンクで確認できます。

各種SpringBootのTomcatバージョンを確認

現行バージョンまでの組み込みTomcatのバージョンを確認します。以下のとおりです。

SpringBoot 1.3.2

GroupId Artifact Id Version
org.apache.tomcat tomcat-jdbc 8.0.30
org.apache.tomcat tomcat-jsp-api 8.0.30
org.apache.tomcat.embed tomcat-embed-core 8.0.30
org.apache.tomcat.embed tomcat-embed-el 8.0.30
org.apache.tomcat.embed tomcat-embed-jasper 8.0.30
org.apache.tomcat.embed tomcat-embed-logging-juli 8.0.30
org.apache.tomcat.embed tomcat-embed-websocket 8.0.30

1.3.2 Appendix Dependency versions

SpringBoot 1.3.8

GroupId Artifact Id Version
org.apache.tomcat tomcat-jdbc 8.0.37
org.apache.tomcat tomcat-jsp-api 8.0.37
org.apache.tomcat.embed tomcat-embed-core 8.0.37
org.apache.tomcat.embed tomcat-embed-el 8.0.37
org.apache.tomcat.embed tomcat-embed-jasper 8.0.37
org.apache.tomcat.embed tomcat-embed-logging-juli 8.0.37
org.apache.tomcat.embed tomcat-embed-websocket 8.0.37

1.3.8 Appendix Dependency versions

SpringBoot 1.4.6

GroupId Artifact Id Version
org.apache.tomcat tomcat-jdbc 8.5.13
org.apache.tomcat tomcat-jsp-api 8.5.13
org.apache.tomcat.embed tomcat-embed-core 8.5.13
org.apache.tomcat.embed tomcat-embed-el 8.5.13
org.apache.tomcat.embed tomcat-embed-jasper 8.5.13
org.apache.tomcat.embed tomcat-embed-websocket 8.5.13

1.4.6 Appendix Dependency versions

SpringBoot 1.5.3

GroupId Artifact Id Version
org.apache.tomcat tomcat-jdbc 8.5.14
org.apache.tomcat tomcat-jsp-api 8.5.14
org.apache.tomcat.embed tomcat-embed-core 8.5.14
org.apache.tomcat.embed tomcat-embed-el 8.5.14
org.apache.tomcat.embed tomcat-embed-jasper 8.5.14
org.apache.tomcat.embed tomcat-embed-websocket 8.5.14

1.5.3 Appendix Dependency versions

Tomcat8.0系は8.0.43以降へのバージョンアップを推奨しています。そのため、SpringBoot1.3.2を1.3.8に変更するだけでは修正にならないようです。実際このフレームワークのバージョンアップを検討したのですが、大量のテストコードの修正や実装コードの修正、特にパッケージの移動が発生しているクラスなどがあり、そこそこの実装の修正量になります。また1.3系から1.5系へのバージョンアップを行う場合、テストケースを大幅に修正する必要があり、なかなかの労力が必要です。

SpringBootのバージョンアップをせずにTomcatだけ更新する

そこで、ひとまず脆弱性のあるTomcatのみは急ぎでバージョンアップを検討します。これは結構簡単で、gradleファイルで依存しているTomcatのモジュール設定を上書きすることで実現が可能です。

環境

  • Java 1.8.0_91
  • SpringBoot 1.4.6.RELEASE

SpringBootのアプリケーションを作成します。今回はGradleで作成しますが、mavenでも同じ設定が有効です。

build.gradle

Spring Initializrで作成できる素の雛形そのものです。

buildscript {
    ext {
        springBootVersion = '1.4.6.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

Application

@SpringBootApplication
public class TomcatVersionupDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(TomcatVersionupDemoApplication.class, args);
    }
}

実行結果

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.4.6.RELEASE)

2017-05-17 15:55:19.416  INFO 6072 --- [           main] j.c.s.d.TomcatVersionupDemoApplication   : Starting TomcatVersionupDemoApplication on komuroMBA.local with PID 6072 (/Users/komurohiraku/Develop/spring/Tomcatversionup/build/classes/main started by komurohiraku in /Users/komurohiraku/Develop/spring/Tomcatversionup)
2017-05-17 15:55:19.419  INFO 6072 --- [           main] j.c.s.d.TomcatVersionupDemoApplication   : No active profile set, falling back to default profiles: default
2017-05-17 15:55:19.532  INFO 6072 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@6b53e23f: startup date [Wed May 17 15:55:19 JST 2017]; root of context hierarchy
2017-05-17 15:55:20.937  INFO 6072 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2017-05-17 15:55:20.944  INFO 6072 --- [           main] o.apache.catalina.core.StandardService   : Starting service Tomcat
2017-05-17 15:55:20.944  INFO 6072 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.13

SpringBoot1.4.6なので組み込まれているTomcatは Apache Tomcat/8.5.13 です。これを 8.5.15 に変更してみます。

build.gradle修正

組み込まれているTomcatのバージョンをDependenciesの設定の中で上書きします。

buildscript {
    ext {
        springBootVersion = '1.4.6.RELEASE'
        tomcatVersion = '8.5.15'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

tomcatVersion = '8.5.15'

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    testCompile('org.springframework.boot:spring-boot-starter-test')
    compile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}")
    compile("org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}")
    compile("org.apache.tomcat.embed:tomcat-embed-el:${tomcatVersion}")
    compile("org.apache.tomcat.embed:tomcat-embed-websocket:${tomcatVersion}")
}

実行結果

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.4.6.RELEASE)

2017-05-17 16:56:35.541  INFO 8065 --- [           main] j.c.s.d.TomcatVersionupDemoApplication   : Starting TomcatVersionupDemoApplication on komuroMBA.local with PID 8065 (/Users/komurohiraku/Develop/spring/Tomcatversionup/build/classes/main started by komurohiraku in /Users/komurohiraku/Develop/spring/Tomcatversionup)
2017-05-17 16:56:35.546  INFO 8065 --- [           main] j.c.s.d.TomcatVersionupDemoApplication   : No active profile set, falling back to default profiles: default
2017-05-17 16:56:35.687  INFO 8065 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@3b2c72c2: startup date [Wed May 17 16:56:35 JST 2017]; root of context hierarchy
2017-05-17 16:56:37.323  INFO 8065 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2017-05-17 16:56:37.332  INFO 8065 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2017-05-17 16:56:37.333  INFO 8065 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.5.15

はい。これでSpringBootのバージョンはそのままに、Tomcatのみを Apache Tomcat/8.5.15 に変更しました。

おまけ

compile で指定するとjarの中に組み込まれてしまいます。他のjarに組み込まれるライブラリなどは、Compileのときのみ取り込まれjarには含まれない provided を利用したいところですが、あいにくgradleにはデフォルトで providedスコープがありません。そこでプラグインで解決します。

repositoriesの追加

repositories以下に設定を追加します。

maven {
    url 'http://repo.spring.io/plugins-release'
}

buildscript.dependenciesの追加

classpath 'org.springframework.build.gradle:propdeps-plugin:0.0.7'

apply plugin

apply plugin: 'propdeps'

変更したファイル

build.gradleは以下のようになります。

buildscript {
    ext {
        springBootVersion = '1.4.6.RELEASE'
        tomcatVersion = '8.5.15'
    }
    repositories {
        mavenCentral()
        maven {
            url 'http://repo.spring.io/plugins-release'
        }
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        classpath 'org.springframework.build.gradle:propdeps-plugin:0.0.7'
    }
}

apply plugin: 'propdeps'

(snip)

dependencies {

  (snip)

  provided("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}")
  provided("org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}")
  provided("org.apache.tomcat.embed:tomcat-embed-el:${tomcatVersion}")
  provided("org.apache.tomcat.embed:tomcat-embed-websocket:${tomcatVersion}")
}

これでprovided指定ができるようになりました。よかったですね。

参照