JDBC 从 Dockerized SpringBoot 到外部 MariaDB 的身份验证失败
JDBC Authentication Failure from Dockerized SpringBoot to External MariaDB
当 SpringBoot 服务来自 Docker 容器内的 运行 时,我遇到了 SpringBoot Web 服务无法成功验证到裸机 MariaDB 10.3.8 数据库的问题。在命令行外部 运行 时,相同的 SpringBoot 服务 JAR 成功连接到数据库。详情如下:
- 服务器是 Fedora 30 运行ning on 192.168.99.10
- MariaDB 版本 10.3.8 绑定到 0.0.0.0(未 Docker 化)
- mysql-连接器-5.1.41
- Java OpenJDK 13 运行在 Fedora 30 上运行
- Docker 版本 19.03.0-rc3
- Docker 图片 openjdk:13-alpine
SpringBoot 应用程序被编码为使用环境变量来定义 MariaDB 数据库实例的位置和凭据。
Java 服务中建立 JDBC 池的 class 有额外的工具来确认环境值正在设置并且在 JDBC 池被实例化:
@Autowired
public void setDataSource(DataSource dataSource) {
// pull environment variables being used and log them for debugging
String dbhost = System.getenv("DOCKENV_MYSQL_HOST");
String dbport = System.getenv("DOCKENV_MYSQL_PORT");
String dbuser = System.getenv("DOCKENV_MYSQL_USERID");
String dbpw = System.getenv("DOCKENV_MYSQL_PASSWORD");
thisLog.info("DOCKENV_MYSQL_HOST=" + dbhost + " DOCKENV_MYSQL_PORT=" + dbport + " DOCKENV_MYSQL_USERID=" + dbuser
+ " DOCKENV_MYSQL_PASSWORD=" + dbpw);
jdbcTemplate = new JdbcTemplate(dataSource);
}
这是在 Linux 提示层使用 curl 命令执行和调用时 SpringBoot 服务记录的内容。
[mdh@fedora1 ~/gitwork/kuberdepends]$ printenv | grep DOCK
DOCKENV_MYSQL_PORT=3306
DOCKENV_MYSQL_PASSWORD=badpassword
DOCKENV_MYSQL_HOST=192.168.99.10
DOCKENV_MYSQL_USERID=dependsapp
[mdh@fedora1 ~/gitwork/kuberdepends]$ java -jar target/depends.jar 1>/dev/null 2>/dev/null&
[1] 7814
[mdh@fedora1 ~/gitwork/kuberdepends]$
[mdh@fedora1 ~/gitwork/kuberdepends]$ curl -H "Content-type: application/json" -X GET http://127.0.0.1:8080//depends/api/projects/34
{"project_id":34,"projectstatus_id":0,"clientbusunit_id":2,"clientbusdept_id":2,"factorybusunit_id":11,"factorybusdept_id":23,"projectname":"Ent Portal Unification","shortdescription":"Merge enterprise portals into spectrum.net","longdescription":"Longer desc here","hascapitalspend":"Y","hasexpensespend":"Y","capitalledger":"","expenseledger":"","clientpriority":1,"deliverypriority":1,"restricttodept":"N","restricttomembers":"N","createdatetime":"2018-05-20 20:51:22.0","updatedatetime":null}[mdh@fedora1 ~/gitwork/kuberdepends]$
[mdh@fedora1 ~/gitwork/kuberdepends]$
[mdh@fedora1 ~/gitwork/kuberdepends]$
[mdh@fedora1 ~/gitwork/kuberdepends]$ cat /logs/springboot/dependsLog.txt | grep dependsapp
2019-08-24 13:33:45.077 INFO 7814 --- [main] com.charter.depends.dao.ProjectsDAO : DOCKENV_MYSQL_HOST=192.168.99.10 DOCKENV_MYSQL_PORT=3306 DOCKENV_MYSQL_USERID=dependsapp DOCKENV_MYSQL_PASSWORD=badpassword
[mdh@fedora1 ~/gitwork/kuberdepends]$
JAR 的工作版本被包装为一个 Docker 容器,包含以下 Docker 文件:
[mdh@fedora1 ~/gitwork/kuberdepends]$ cat Dockerfile.openjdk13alpine
# Docker file -- use as micro-sized test image with basic Linux commands
# 1) use alpine image as starting slice
# 2) add packages: iputils, busybox-extras (for telnet), mariadb-client (testing MariaDB access)
# 3) starts the SpringBoot app in depends.jar as java -jar /opt/mdhlabs/depends.jar
FROM openjdk:13-alpine
RUN mkdir /opt/mdhlabs
COPY ./target/depends.jar /opt/mdhlabs/depends.jar
WORKDIR /opt/mdhlabs
RUN apk update && apk add iputils && apk add busybox-extras && apk add mariadb-client
CMD ["java", "-jar","/opt/mdhlabs/depends.jar"]
[mdh@fedora1 ~/gitwork/kuberdepends]$
[mdh@fedora1 ~/gitwork/kuberdepends]$ docker build -t kuberdepends-alp13 -f Dockerfile.openjdk13alpine .
(stuff omitted here for brevity...)
Successfully tagged kuberdepends-alp13:latest
[mdh@fedora1 ~/gitwork/kuberdepends]$
在停止该 Springboot 进程并运行将 Docker 化版本端口从主 Linux 环境映射内部 8080 到外部 7777 之后,这是记录的内容。
[mdh@fedora1 ~/gitwork/kuberdepends]$ docker run --network=host -p 7777:8080 -d -e DOCKENV_MYSQL_HOST='192.168.99.10' -e DOCKENV_MYSQL_PORT='3306' -e DOCKENV_MYSQL_USERID='dependsapp' -e DOCKENV_MYSQL_PASSWORD='badpassword' --name kuberdepends-container kuberdepends-alp13
WARNING: Published ports are discarded when using host network mode
293d36ed488ca076d050f9579bb54979ff9e469a9d4429ad58204f069dbfd358
[mdh@fedora1 ~/gitwork/kuberdepends]$
[mdh@fedora1 ~/gitwork/kuberdepends]$ curl -H "Content-type: application/json" -X GET http://127.0.0.1:7777//depends/api/projects/34
curl: (7) Failed to connect to 127.0.0.1 port 7777: Connection refused
[mdh@fedora1 ~/gitwork/kuberdepends]$
如果我访问 Docker 容器,对内部 8080 侦听器端口执行 wget 并检查 SpringBoot 服务生成的日志文件,这里是与尝试实例化 [=82] 相关的输出=] 池.
[mdh@fedora1 ~/gitwork/kuberdepends]$ docker exec -it kuberdepends-container sh
/opt/mdhlabs # wget --header "Content-type: application/json" http://127.0.0.1:8080//depends/api/projects/34
Connecting to 127.0.0.1:8080 (127.0.0.1:8080)
wget: server returned error: HTTP/1.1 500
/opt/mdhlabs #
/opt/mdhlabs # cat /logs/springboot/dependsLog.txt | grep dependsapp
2019-08-24 18:52:08.956 INFO 1 --- [main] com.charter.depends.dao.ProjectsDAO : DOCKENV_MYSQL_HOST=192.168.99.10 DOCKENV_MYSQL_PORT=3306 DOCKENV_MYSQL_USERID=dependsapp DOCKENV_MYSQL_PASSWORD=badpassword
java.sql.SQLException: Access denied for user 'dependsapp'@'127.0.0.1' (using password: YES)
2019-08-24 18:56:14.796 ERROR 1 --- [http-nio-8080-exec-1] o.a.c.c.C.[.[.[.[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [/depends/api] threw exception [Request processing failed; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is java.sql.SQLException: Access denied for user 'dependsapp'@'127.0.0.1' (using password: YES)] with root cause
java.sql.SQLException: Access denied for user 'dependsapp'@'127.0.0.1' (using password: YES)
/opt/mdhlabs #
这是让我难过的部分。这不是从容器内部到 运行ning MariaDB 的外部主机的连接问题,也不是 dependsapp/badpassword 的 userid/password 的身份验证问题。如果我使用安装在 Docker 容器中的 mysql 客户端和凭据,就可以访问数据库。
/opt/mdhlabs # mysql --user=dependsapp --password=badpassword --host=192.168.99.10 depends
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 45
Server version: 10.3.8-MariaDB MariaDB Server
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [depends]> select project_id, projectname from projects where project_id=34;
+------------+------------------------+
| project_id | projectname |
+------------+------------------------+
| 34 | Ent Portal Unification |
+------------+------------------------+
1 row in set (0.000 sec)
MariaDB [depends]>
以下是 Docker 化版本的 SpringBoot 应用在尝试连接时生成的异常日志的详细信息。
2019-08-24 18:56:14.605 INFO 1 --- [http-nio-8080-exec-1] c.c.depends.services.ProjectController : QUERY action=projectRe
2019-08-24 18:56:14.788 ERROR 1 --- [http-nio-8080-exec-1] o.a.tomcat.jdbc.pool.ConnectionPool : Unable to create initi
java.sql.SQLException: Access denied for user 'dependsapp'@'127.0.0.1' (using password: YES)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:964) ~[mysql-connector-java-5.1.41.jar!/:5.1.41]
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3973) ~[mysql-connector-java-5.1.41.jar!/:5.1.41]
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3909) ~[mysql-connector-java-5.1.41.jar!/:5.1.41]
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:873) ~[mysql-connector-java-5.1.41.jar!/:5.1.41]
at com.mysql.jdbc.MysqlIO.proceedHandshakeWithPluggableAuthentication(MysqlIO.java:1710) ~[mysql-connector-java-5.1.
at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1226) ~[mysql-connector-java-5.1.41.jar!/:5.1.41]
at com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:2205) ~[mysql-connector-java-5.1.41.jar!/:5.1.41]
at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2236) ~[mysql-connector-java-5.1.41.jar!/:5.1
at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2035) ~[mysql-connector-java-5.1.41.jar!/:5.1.41]
我假设 Docker 容器内的内部 SpringBoot JVM 和 Docker 容器内的内部 mysql 客户端二进制文件似乎都来自同一个源 IP当连接请求到达外部 MariaDB 数据库时。如果它从 Docker 容器内的 mysql 客户端运行,那么当在同一个 Docker 容器中的 SpringBoot JVM 中使用时,应该允许相同的 userid/password 连接。
OpenJDK 12 和 openjdk:12-alpine 也会出现同样的问题,所以我认为问题与 Java 中的版本无关。 mysql-connector JAR(版本 5.1.41)是否与更新版本的 Java 或 MariaDB 10.3.x 有问题?
如有任何帮助,我们将不胜感激。
找到根本原因...飞行员错误。
我的项目树在类路径中有两个单独的 application.properties 文件:
$PROJECT/application.properties
$PROJECT/src/main/resources/application.属性
树顶部的版本已用于快速覆盖一次性测试的某些值,但我忘记将该文件与
一起复制到 Docker 图像中
$PROJECT/target/depends.jar
当 depends.jar 在容器内执行时,只留下 src/main/resources/application.properties 的内部版本,留下旧的不正确值。
当 SpringBoot 服务来自 Docker 容器内的 运行 时,我遇到了 SpringBoot Web 服务无法成功验证到裸机 MariaDB 10.3.8 数据库的问题。在命令行外部 运行 时,相同的 SpringBoot 服务 JAR 成功连接到数据库。详情如下:
- 服务器是 Fedora 30 运行ning on 192.168.99.10
- MariaDB 版本 10.3.8 绑定到 0.0.0.0(未 Docker 化)
- mysql-连接器-5.1.41
- Java OpenJDK 13 运行在 Fedora 30 上运行
- Docker 版本 19.03.0-rc3
- Docker 图片 openjdk:13-alpine
SpringBoot 应用程序被编码为使用环境变量来定义 MariaDB 数据库实例的位置和凭据。
Java 服务中建立 JDBC 池的 class 有额外的工具来确认环境值正在设置并且在 JDBC 池被实例化:
@Autowired public void setDataSource(DataSource dataSource) { // pull environment variables being used and log them for debugging String dbhost = System.getenv("DOCKENV_MYSQL_HOST"); String dbport = System.getenv("DOCKENV_MYSQL_PORT"); String dbuser = System.getenv("DOCKENV_MYSQL_USERID"); String dbpw = System.getenv("DOCKENV_MYSQL_PASSWORD"); thisLog.info("DOCKENV_MYSQL_HOST=" + dbhost + " DOCKENV_MYSQL_PORT=" + dbport + " DOCKENV_MYSQL_USERID=" + dbuser + " DOCKENV_MYSQL_PASSWORD=" + dbpw); jdbcTemplate = new JdbcTemplate(dataSource); }
这是在 Linux 提示层使用 curl 命令执行和调用时 SpringBoot 服务记录的内容。
[mdh@fedora1 ~/gitwork/kuberdepends]$ printenv | grep DOCK DOCKENV_MYSQL_PORT=3306 DOCKENV_MYSQL_PASSWORD=badpassword DOCKENV_MYSQL_HOST=192.168.99.10 DOCKENV_MYSQL_USERID=dependsapp [mdh@fedora1 ~/gitwork/kuberdepends]$ java -jar target/depends.jar 1>/dev/null 2>/dev/null& [1] 7814 [mdh@fedora1 ~/gitwork/kuberdepends]$ [mdh@fedora1 ~/gitwork/kuberdepends]$ curl -H "Content-type: application/json" -X GET http://127.0.0.1:8080//depends/api/projects/34 {"project_id":34,"projectstatus_id":0,"clientbusunit_id":2,"clientbusdept_id":2,"factorybusunit_id":11,"factorybusdept_id":23,"projectname":"Ent Portal Unification","shortdescription":"Merge enterprise portals into spectrum.net","longdescription":"Longer desc here","hascapitalspend":"Y","hasexpensespend":"Y","capitalledger":"","expenseledger":"","clientpriority":1,"deliverypriority":1,"restricttodept":"N","restricttomembers":"N","createdatetime":"2018-05-20 20:51:22.0","updatedatetime":null}[mdh@fedora1 ~/gitwork/kuberdepends]$ [mdh@fedora1 ~/gitwork/kuberdepends]$ [mdh@fedora1 ~/gitwork/kuberdepends]$ [mdh@fedora1 ~/gitwork/kuberdepends]$ cat /logs/springboot/dependsLog.txt | grep dependsapp 2019-08-24 13:33:45.077 INFO 7814 --- [main] com.charter.depends.dao.ProjectsDAO : DOCKENV_MYSQL_HOST=192.168.99.10 DOCKENV_MYSQL_PORT=3306 DOCKENV_MYSQL_USERID=dependsapp DOCKENV_MYSQL_PASSWORD=badpassword [mdh@fedora1 ~/gitwork/kuberdepends]$
JAR 的工作版本被包装为一个 Docker 容器,包含以下 Docker 文件:
[mdh@fedora1 ~/gitwork/kuberdepends]$ cat Dockerfile.openjdk13alpine # Docker file -- use as micro-sized test image with basic Linux commands # 1) use alpine image as starting slice # 2) add packages: iputils, busybox-extras (for telnet), mariadb-client (testing MariaDB access) # 3) starts the SpringBoot app in depends.jar as java -jar /opt/mdhlabs/depends.jar FROM openjdk:13-alpine RUN mkdir /opt/mdhlabs COPY ./target/depends.jar /opt/mdhlabs/depends.jar WORKDIR /opt/mdhlabs RUN apk update && apk add iputils && apk add busybox-extras && apk add mariadb-client CMD ["java", "-jar","/opt/mdhlabs/depends.jar"] [mdh@fedora1 ~/gitwork/kuberdepends]$ [mdh@fedora1 ~/gitwork/kuberdepends]$ docker build -t kuberdepends-alp13 -f Dockerfile.openjdk13alpine . (stuff omitted here for brevity...) Successfully tagged kuberdepends-alp13:latest [mdh@fedora1 ~/gitwork/kuberdepends]$
在停止该 Springboot 进程并运行将 Docker 化版本端口从主 Linux 环境映射内部 8080 到外部 7777 之后,这是记录的内容。
[mdh@fedora1 ~/gitwork/kuberdepends]$ docker run --network=host -p 7777:8080 -d -e DOCKENV_MYSQL_HOST='192.168.99.10' -e DOCKENV_MYSQL_PORT='3306' -e DOCKENV_MYSQL_USERID='dependsapp' -e DOCKENV_MYSQL_PASSWORD='badpassword' --name kuberdepends-container kuberdepends-alp13 WARNING: Published ports are discarded when using host network mode 293d36ed488ca076d050f9579bb54979ff9e469a9d4429ad58204f069dbfd358 [mdh@fedora1 ~/gitwork/kuberdepends]$ [mdh@fedora1 ~/gitwork/kuberdepends]$ curl -H "Content-type: application/json" -X GET http://127.0.0.1:7777//depends/api/projects/34 curl: (7) Failed to connect to 127.0.0.1 port 7777: Connection refused [mdh@fedora1 ~/gitwork/kuberdepends]$
如果我访问 Docker 容器,对内部 8080 侦听器端口执行 wget 并检查 SpringBoot 服务生成的日志文件,这里是与尝试实例化 [=82] 相关的输出=] 池.
[mdh@fedora1 ~/gitwork/kuberdepends]$ docker exec -it kuberdepends-container sh /opt/mdhlabs # wget --header "Content-type: application/json" http://127.0.0.1:8080//depends/api/projects/34 Connecting to 127.0.0.1:8080 (127.0.0.1:8080) wget: server returned error: HTTP/1.1 500 /opt/mdhlabs # /opt/mdhlabs # cat /logs/springboot/dependsLog.txt | grep dependsapp 2019-08-24 18:52:08.956 INFO 1 --- [main] com.charter.depends.dao.ProjectsDAO : DOCKENV_MYSQL_HOST=192.168.99.10 DOCKENV_MYSQL_PORT=3306 DOCKENV_MYSQL_USERID=dependsapp DOCKENV_MYSQL_PASSWORD=badpassword java.sql.SQLException: Access denied for user 'dependsapp'@'127.0.0.1' (using password: YES) 2019-08-24 18:56:14.796 ERROR 1 --- [http-nio-8080-exec-1] o.a.c.c.C.[.[.[.[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [/depends/api] threw exception [Request processing failed; nested exception is org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is java.sql.SQLException: Access denied for user 'dependsapp'@'127.0.0.1' (using password: YES)] with root cause java.sql.SQLException: Access denied for user 'dependsapp'@'127.0.0.1' (using password: YES) /opt/mdhlabs #
这是让我难过的部分。这不是从容器内部到 运行ning MariaDB 的外部主机的连接问题,也不是 dependsapp/badpassword 的 userid/password 的身份验证问题。如果我使用安装在 Docker 容器中的 mysql 客户端和凭据,就可以访问数据库。
/opt/mdhlabs # mysql --user=dependsapp --password=badpassword --host=192.168.99.10 depends Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 45 Server version: 10.3.8-MariaDB MariaDB Server Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. MariaDB [depends]> select project_id, projectname from projects where project_id=34; +------------+------------------------+ | project_id | projectname | +------------+------------------------+ | 34 | Ent Portal Unification | +------------+------------------------+ 1 row in set (0.000 sec) MariaDB [depends]>
以下是 Docker 化版本的 SpringBoot 应用在尝试连接时生成的异常日志的详细信息。
2019-08-24 18:56:14.605 INFO 1 --- [http-nio-8080-exec-1] c.c.depends.services.ProjectController : QUERY action=projectRe 2019-08-24 18:56:14.788 ERROR 1 --- [http-nio-8080-exec-1] o.a.tomcat.jdbc.pool.ConnectionPool : Unable to create initi java.sql.SQLException: Access denied for user 'dependsapp'@'127.0.0.1' (using password: YES) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:964) ~[mysql-connector-java-5.1.41.jar!/:5.1.41] at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3973) ~[mysql-connector-java-5.1.41.jar!/:5.1.41] at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3909) ~[mysql-connector-java-5.1.41.jar!/:5.1.41] at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:873) ~[mysql-connector-java-5.1.41.jar!/:5.1.41] at com.mysql.jdbc.MysqlIO.proceedHandshakeWithPluggableAuthentication(MysqlIO.java:1710) ~[mysql-connector-java-5.1. at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:1226) ~[mysql-connector-java-5.1.41.jar!/:5.1.41] at com.mysql.jdbc.ConnectionImpl.coreConnect(ConnectionImpl.java:2205) ~[mysql-connector-java-5.1.41.jar!/:5.1.41] at com.mysql.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:2236) ~[mysql-connector-java-5.1.41.jar!/:5.1 at com.mysql.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:2035) ~[mysql-connector-java-5.1.41.jar!/:5.1.41]
我假设 Docker 容器内的内部 SpringBoot JVM 和 Docker 容器内的内部 mysql 客户端二进制文件似乎都来自同一个源 IP当连接请求到达外部 MariaDB 数据库时。如果它从 Docker 容器内的 mysql 客户端运行,那么当在同一个 Docker 容器中的 SpringBoot JVM 中使用时,应该允许相同的 userid/password 连接。
OpenJDK 12 和 openjdk:12-alpine 也会出现同样的问题,所以我认为问题与 Java 中的版本无关。 mysql-connector JAR(版本 5.1.41)是否与更新版本的 Java 或 MariaDB 10.3.x 有问题?
如有任何帮助,我们将不胜感激。
找到根本原因...飞行员错误。
我的项目树在类路径中有两个单独的 application.properties 文件:
$PROJECT/application.properties
$PROJECT/src/main/resources/application.属性
树顶部的版本已用于快速覆盖一次性测试的某些值,但我忘记将该文件与
一起复制到 Docker 图像中$PROJECT/target/depends.jar
当 depends.jar 在容器内执行时,只留下 src/main/resources/application.properties 的内部版本,留下旧的不正确值。