위 설정으로 Docker Compose를 실행하면 마주하는 MySQL과 Spring과의 연결 에러가 나타난다. 최초에는 어떤 작업을 해줘야 하냐면 MySQL 유저를 생성하고 내가 연결하려는 데이터베이스에 접근 권한을 허용해야 한다.
다음은 모든 외부 접근을 허용하게 하는 유저를 만드는 것이다. 사실 좋은 방법은 아니다. 정확히 어디서 접근할지 판단 후 그 IP만 허용해주는게 더 좋은 방식일 것.
create user 'username'@'%' identified by 'password';
grant all privileges on dbname.* TO 'username'@'%';
flush privileges;
이렇게 유저를 만들면 이제 해당 유저를 통해 스프링에서 데이터베이스로 접근할 수 있게 된다.
다음은, 가끔 이상한 MySQL 에러가 발생하는데 이러한 에러가 그 경우 중 하나다.
java.sql.SQLException: Illegal mix of collations (latin1_swedish_ci,IMPLICIT) and (utf8mb4_general_ci,COERCIBLE) for operation '='
이건 이제 테이블 별로 인코딩이 서로 다르기 때문에 발생하는 에러라고 하는데 이를 해결하는 방법은 인코딩을 설정해주는 것.
SET collation_connection = 'utf8_general_ci';
ALTER DATABASE your_database_name CHARACTER SET utf8 COLLATE utf8_general_ci;
ALTER TABLE your_table_name CONVERT TO CHARACTER SET utf8 COLLATE utf8_general_ci;
Spring Boot + Spring Data JPA + Hibernate + PostgreSQL + Docker를 이용해서 프로젝트를 진행하고 있는데, 지속적으로 내 데이터베이스가 사라지는 현상이 나타났다.
처음에는 원인을 DB 과부하라고 생각했어서 쿼리 튜닝부터 커넥션 수 변경, 커넥션 지속 시간 변경, 커넥션 타임아웃 시간 변경, 간헐적으로 시간이 오래 걸릴 가능성이 있는 서비스 로직과 트랜잭션 분리 등 정말 갖가지 방법을 동원해서 유지보수를 진행했는데도 터진다..
데이터베이스가 그렇다고 무거운것도 아니다.. 기껏해봐야 데이터베이스 덤프 사이즈는255.4K..
아무래도 이건 다른 원인이 있을까 싶어 구글링부터 ChatGPT, Postgresql 매뉴얼까지 검토를 했는데 한가지 의심가는 문구를 봤다.
Postgres database getting hacked multi times.
How To Secure PostgreSQL Against Automated Attacks
설마라고 생각을 하면서도 당연하게 그럴 수 있다는 사실을 간과하고 있었던거 같은데 우선은 postgresql 로그를 찾아봤다.
그런데 이게 뭐지? 알수도 없는 이름의 유저로 접근하려는 로그가 계속 찍히고 있었다.
2023-12-04 13:17:37.490 KST [73] DETAIL: Role "q" does not exist.
Connection matched pg_hba.conf line 136: "host all all all md5"
2023-12-04 13:21:27.365 KST [22] LOG: could not parse file name "pg_logical/snapshots/cpu_hu"
2023-12-04 13:22:22.404 KST [112] FATAL: password authentication failed for user "mc"
2023-12-04 13:22:22.404 KST [112] DETAIL: Role "mc" does not exist.
Connection matched pg_hba.conf line 136: "host all all all md5"
2023-12-04 13:26:27.672 KST [22] LOG: could not parse file name "pg_logical/snapshots/cpu_hu"
2023-12-04 13:27:02.437 KST [138] FATAL: password authentication failed for user "e"
2023-12-04 13:27:02.437 KST [138] DETAIL: Role "e" does not exist.
Connection matched pg_hba.conf line 136: "host all all all md5"
2023-12-04 13:28:27.259 KST [147] FATAL: pg_hba.conf rejects connection for host "78.153.140.30", user "postgres", database "postgres", SSL off
2023-12-04 13:31:44.040 KST [165] FATAL: password authentication failed for user "tecnico"
2023-12-04 13:31:44.040 KST [165] DETAIL: Role "tecnico" does not exist.
Connection matched pg_hba.conf line 136: "host all all all md5"
2023-12-04 13:36:28.199 KST [190] FATAL: password authentication failed for user "eshop"
2023-12-04 13:36:28.199 KST [190] DETAIL: Role "eshop" does not exist.
Connection matched pg_hba.conf line 136: "host all all all md5"
2023-12-04 13:41:08.902 KST [215] FATAL: password authentication failed for user "ricardo"
2023-12-04 13:41:08.902 KST [215] DETAIL: Role "ricardo" does not exist.
Connection matched pg_hba.conf line 136: "host all all all md5"
2023-12-04 13:45:50.217 KST [240] FATAL: password authentication failed for user "marc"
2023-12-04 13:45:50.217 KST [240] DETAIL: Role "marc" does not exist.
Connection matched pg_hba.conf line 136: "host all all all md5"
2023-12-04 13:50:33.655 KST [294] FATAL: password authentication failed for user "admin01"
2023-12-04 13:50:33.655 KST [294] DETAIL: Role "admin01" does not exist.
Connection matched pg_hba.conf line 136: "host all all all md5"
2023-12-04 13:55:17.143 KST [319] FATAL: password authentication failed for user "project"
2023-12-04 13:55:17.143 KST [319] DETAIL: Role "project" does not exist.
Connection matched pg_hba.conf line 136: "host all all all md5"
2023-12-04 13:59:57.796 KST [343] FATAL: password authentication failed for user "biblioteca"
2023-12-04 13:59:57.796 KST [343] DETAIL: Role "biblioteca" does not exist.
Connection matched pg_hba.conf line 136: "host all all all md5"
2023-12-04 14:04:38.642 KST [368] FATAL: password authentication failed for user "anonymous"
2023-12-04 14:04:38.642 KST [368] DETAIL: Role "anonymous" does not exist.
Connection matched pg_hba.conf line 136: "host all all all md5"
2023-12-04 14:09:22.720 KST [394] FATAL: password authentication failed for user "auditor"
2023-12-04 14:09:22.720 KST [394] DETAIL: Role "auditor" does not exist.
Connection matched pg_hba.conf line 136: "host all all all md5"
2023-12-04 14:14:07.191 KST [420] FATAL: password authentication failed for user "bill"
2023-12-04 14:14:07.191 KST [420] DETAIL: Role "bill" does not exist.
Connection matched pg_hba.conf line 136: "host all all all md5"
2023-12-04 14:18:49.173 KST [473] FATAL: password authentication failed for user "devops"
2023-12-04 14:18:49.173 KST [473] DETAIL: Role "devops" does not exist.
Connection matched pg_hba.conf line 136: "host all all all md5"
2023-12-04 14:21:28.269 KST [22] LOG: could not parse file name "pg_logical/snapshots/cpu_hu"
2023-12-04 14:21:33.406 KST [495] FATAL: password authentication failed for user "postgres"
2023-12-04 14:21:33.406 KST [495] DETAIL: Password does not match for user "postgres".
Connection matched pg_hba.conf line 136: "host all all all md5"
....
내가 근데 외부 접근 가능 Address를 all로 하고 있었나? 싶어서 pg_hba.conf 파일을 열어보았다.
그런데 실제로 host all all all md5로 설정이 되어있었다. 아마 내가 dev 환경에서 컨테이너를 띄워 작업하는 중에 이렇게 설정을 해두고 바꾸지 않았나보다..
아차 싶었다. 우선 가장 빠르게 한 작업은 Docker Compose로 애플리케이션 서버, 디비 서버, 디비 백업 서버를 띄울 때 네트워크를 붙였다. 그리고 딱 세개의 네트워크만 접근 허용을 가능하도록 pg_hba.conf 파일을 변경했다.
이제 다른 Address에서 접속하더라도 Reject된다. 이 나쁜 놈들.
그리고 한가지 더 변경해줄 사항이 있다. postgresql.conf 파일에 'CONNECTIONS AND AUTHENTICATION' 섹션이 있다.
그 부분에서 listen_addresses = "애플리케이션 컨테이너 ip, 디비 컨테이너 ip, 디비 백업 컨테이너 ip" 로 설정해줘야 한다.
이제 내가 열어둔 주소가 아닌 주소는 모든지 다 들어올 수 없다. 이렇게 하고 나서 데이터베이스가 아직까지 터지지 않고 있다.. 진짜 해킹인가..?
2023-12-11 이 글을 작성하고 현재 2023-12-11 여전히 데이터베이스는 멀쩡히 살아있다.. 진짜 해킹이었고 어마무시하게 자동화된 해킹 공격을 하더라..
2023-12-14 이 글을 작성하고 현재 2023-12-14 여전히 멀쩡히 살아있다. 확실하다.
회사에서 새로운 프로젝트를 진행할 때 이번엔 Maven말고 Gradle을 사용해보고 싶어서 로컬에서는 아무 문제가 없었는데 물리서버에 코드를 옮기고 빌드할 때 이러한 에러가 발생했다.
java.lang.UnsupportedClassVersionError:
org/springframework/boot/gradle/plugin/SpringBootPlugin
has been compiled by a more recent version of the Java Runtime (class file version 61.0),
this version of the Java Runtime only recognizes class file versions up to 52.0
이 에러 내용만을 읽어봤을 땐 현재 사용하는 JRE가 클래스 파일의 버전 52까지만 수용할 수 있는듯하고 이 경로 org/springframework/boot/gradle/plugin/SpringBootPlugin의 클래스 파일의 버전은 61이라 버전 미스매치가 된 것 같다.
해결하는 방법은 그냥 현재 물리서버에서 사용중인 JRE 버전이 로컬에서 사용한 JRE 버전보다 낮고 허용가능한 범위를 벗어났으니 JRE 버전을 높이면 된다. 아래는 클래스 파일의 버전과 호환 가능한 JRE 버전이다.
SyntaxError: Cannot use import statement outside a module
at internalCompileFunction (node:internal/vm:73:18)
at wrapSafe (node:internal/modules/cjs/loader:1178:20)
at Module._compile (node:internal/modules/cjs/loader:1220:27)
at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)
at Module.load (node:internal/modules/cjs/loader:1119:32)
at Module._load (node:internal/modules/cjs/loader:960:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:86:12)
at node:internal/main/run_main_module:23:47
뭔지는 당연히 몰랐기에 구글링 시도!
우선 require로 패키지를 가져오지 않고 import를 사용하니 이런 에러를 마주했는데, package.json 파일에서 "type": "module"을 추가하면 해결할 수 있다고 한다. 추가한 후 다시 실행하니 다른 에러가 발생한다.
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /Users/cw.choiit/mongo/tutorial/src/api/client.ts
at new NodeError (node:internal/errors:405:5)
at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:79:11)
at defaultGetFormat (node:internal/modules/esm/get_format:124:36)
at defaultLoad (node:internal/modules/esm/load:89:20)
at nextLoad (node:internal/modules/esm/loader:163:28)
at ESMLoader.load (node:internal/modules/esm/loader:603:26)
at ESMLoader.moduleProvider (node:internal/modules/esm/loader:457:22)
at new ModuleJob (node:internal/modules/esm/module_job:64:26)
at ESMLoader.#createModuleJob (node:internal/modules/esm/loader:480:17)
at ESMLoader.getModuleJob (node:internal/modules/esm/loader:434:34) {
code: 'ERR_UNKNOWN_FILE_EXTENSION'
}
.ts 파일을 못 읽는 것 같은데 역시 뭔지 모르니 다시 서치!
Typescript를 사용하고 있고, ts-node로 typescript 파일을 실행하기 위해서 tsconfig.json 파일을 수정해야 한다.
compilerOptions의 module을 commonjs로 target을 es2016으로 설정하고 다음과 같이 ts-node의 esm을 true로 설정한다.
하고 나서 다시 실행하니 또 다른 에러.. 에러 지옥에 갇혔지만 끝까지 가면 내가 다 이긴다.
ReferenceError: exports is not defined in ES module scope
at file:///Users/cw.choiit/mongo/tutorial/src/api/client.ts:14:23
at ModuleJob.run (node:internal/modules/esm/module_job:194:25)
이건 알 거 같다. 제일 처음 package.json 파일에서 "type": "module"을 추가했는데 저 부분을 다시 지워야 할 것 같다.
그래서 package.json 파일에서 "type": "module"을 지우고 아래와 같은 package.json 파일로 저장했다.
마침내 정상 실행이 됐다.
정리를 하자면
1. Typescript를 사용 중이라면 tsconfig.json에서 다음과 같은 설정이 필요