DB를 지키려고 붙인 모니터링 도구가 DB로 들어가는 문이 되면 곤란하겠죠. 2026년 8월 19일 Percona가 자사 모니터링 제품에서 그런 경로를 공개했습니다.

Percona 보안 공지의 대상은 Percona Monitoring and Management(PMM) 3.9.0 이하입니다. CVSS는 8.7, CVE 번호는 공지 시점에 아직 배정되지 않았고 배정되면 공지를 갱신한다고 적혀 있습니다.

문제의 모양

PMM은 Grafana를 화면으로 쓰고, 쿼리 분석 데이터를 ClickHouse에 담습니다. 문제는 그 둘을 잇는 데이터소스 설정에 있었습니다.

두 가지가 겹칩니다. 첫째, Grafana는 로그인한 사용자가 raw data source API에 접근하는 것을 허용합니다. Viewer 역할도 포함입니다. 대시보드만 볼 수 있는 계정이 데이터소스에 직접 쿼리를 보낼 수 있다는 뜻입니다. 둘째, ClickHouse 데이터소스가 연결할 때 쓰는 기본 신원이 전역 DDL, DML, SOURCES 권한을 가지고 있었습니다.

flowchart TD
    A[Grafana Viewer 계정] --> B[raw data source API]
    B --> C[ClickHouse 데이터소스]
    C --> D[기본 신원으로 연결
SOURCES 권한 보유] D --> E[임의 SQL 실행]

Grafana에서 anonymous access를 켜 두면 로그인하지 않은 사용자까지 이 경로에 닿습니다. 기본값은 꺼져 있습니다. 이 조건은 명확히 해 두는 게 좋습니다. 뒤에 나오는 전체 공격 체인은 anonymous access가 켜져 있고 AWS EC2에 올라간 배포에서 성립합니다.

SOURCES 권한이 실제로 무엇을 여는가

공지가 url() 함수를 언급하는데, ClickHouse를 자주 다루지 않으면 이게 왜 위험한지 감이 안 옵니다. 직접 확인해 봤습니다. 기본 설정으로 ClickHouse 컨테이너를 띄웠습니다.

$ docker run -d --name chtest -e CLICKHOUSE_PASSWORD=pw \
    clickhouse/clickhouse-server:latest
$ docker exec chtest clickhouse-client --password pw -q "select version()"
26.7.5.10

default 사용자가 가진 권한을 그대로 뽑아 봤습니다.

show grants for default;
GRANT SOURCES ON *.* TO default
GRANT TABLE ENGINE ON * TO default
GRANT CHECK, SHOW, SELECT, INSERT, ALTER, CREATE, DROP, UNDROP TABLE,
      TRUNCATE, OPTIMIZE, BACKUP, KILL QUERY, KILL TRANSACTION,
      MOVE PARTITION BETWEEN SHARDS, SYSTEM, dictGet,
      displaySecretsInShowAndSelect, INTROSPECTION, CLUSTER,
      FILE, URL, REMOTE, MONGO, REDIS, MYSQL, POSTGRES, SQLITE,
      ODBC, JDBC, HDFS, S3, HIVE, AZURE, KAFKA, NATS, RABBITMQ,
      YTSAURUS, ARROW FLIGHT, SOURCES ON *.* TO default

목록을 보면 성격이 드러납니다. FILE, URL, REMOTE, S3, MONGO, MYSQL, POSTGRES, HDFS가 전부 들어 있습니다. SOURCES는 이들을 묶은 상위 권한입니다.

이게 뜻하는 바는 ClickHouse의 쿼리가 데이터베이스 안에서 끝나지 않는다는 것입니다. url() 테이블 함수는 SELECT 문 안에서 HTTP 요청을 보냅니다. s3()는 S3 버킷을 읽습니다. file()은 서버 로컬 파일을 테이블처럼 읽습니다. SELECT 권한만 있는 계정이 아니라, HTTP 클라이언트를 겸하는 계정이 되는 셈입니다.

file()에는 별도 울타리가 하나 더 있습니다. 임의 경로는 막습니다.

$ clickhouse-client -q "select count() from file('/etc/hostname','LineAsString')"
Code: 291. DB::Exception: File `/etc/hostname` is not inside
`/var/lib/clickhouse/user_files`. (DATABASE_ACCESS_DENIED)

user_files 디렉터리 밖은 거부됩니다. 다만 그 안이라면 그대로 읽힙니다.

$ docker exec chtest sh -c 'echo secret-token-abc > /var/lib/clickhouse/user_files/t.txt'
$ clickhouse-client -q "select * from file('t.txt','LineAsString')"
secret-token-abc

네트워크 쪽은 울타리가 없습니다. 권한이 실제로 통과하는지 확인하려고 닫힌 포트를 겨냥해 봤습니다.

$ clickhouse-client -q "select * from url('http://127.0.0.1:1/x','LineAsString')"
Code: 1000. DB::Exception: Connection refused. (POCO_EXCEPTION)

돌아온 것이 권한 거부가 아니라 연결 거부입니다. 권한 검사를 통과해 실제로 소켓을 열려고 시도했다는 뜻입니다. 겨냥한 주소가 열려 있었다면 요청이 나갔을 것입니다. SELECT 문 하나가 DB 서버에서 출발하는 HTTP 요청이 되는 지점입니다.

공격 체인

공지가 설명하는 흐름은 네 단계입니다.

url() 함수로 AWS IMDSv1에 요청을 보냅니다. 인스턴스 메타데이터 서비스의 v1은 토큰 없이 GET 하나로 응답합니다. 여기서 EC2 인스턴스에 붙은 role의 임시 자격증명을 얻습니다.

그 자격증명으로 S3에 접근해 Terraform state 파일을 읽습니다. state 파일에는 인프라 구성이 그대로 들어 있고, 관리자 비밀번호 같은 값이 평문으로 남아 있는 경우가 흔합니다.

마지막으로 그 값으로 PMM 관리자로 인증합니다. 모니터링 시스템의 관리자가 되면 감시 대상 DB의 접속 정보를 그 안에서 찾을 수 있습니다.

단계마다 그 자체로는 알려진 문제입니다. IMDSv1이 토큰 없이 응답하는 것도, Terraform state에 비밀이 남는 것도 오래된 이야기입니다. 이 공지가 보여 주는 건 그 알려진 약점들이 SELECT 권한 하나로 연결된다는 점입니다.

수정과 완화

수정 버전은 PMM 3.9.1이고 2026년 8월 19일 나왔습니다. 근본 수정은 이쪽입니다.

바로 올릴 수 없는 환경을 위해 공지가 완화 스크립트를 함께 제공합니다. 방향은 단순합니다. 데이터소스가 쓰는 계정을 읽기 전용으로 새로 만들고, SOURCES 계열을 주지 않는 것입니다.

CREATE USER IF NOT EXISTS grafana_ro
  IDENTIFIED WITH plaintext_password BY '<password>'
  SETTINGS readonly = 1;
GRANT SELECT ON pmm.* TO grafana_ro;

readonly = 1을 걸고 pmm 데이터베이스에 SELECT만 줍니다. SOURCES를 주지 않으면 url(), s3(), mongodb(), remote(), file() 호출이 막힙니다. 대시보드가 필요한 것은 SELECT뿐이니 기능은 그대로 돕니다.

참고로 위 CREATE USER를 기본 default 계정으로 실행하면 거부될 수 있습니다. 제 컨테이너에서는 이렇게 나왔습니다.

Code: 497. DB::Exception: default: Not enough privileges.
To execute this query, it's necessary to have the grant
CREATE USER ON grafana_ro. (ACCESS_DENIED)

SOURCES는 넉넉히 주면서 사용자 관리 권한은 안 주는 기본 구성입니다. 이 스크립트를 돌리려면 ACCESS MANAGEMENT 권한이 있는 계정으로 접속해야 합니다.

관측 스택 계정 권한을 점검하는 자리

공지가 다루지 않는 부분을 하나 짚고 싶습니다. 이번 문제의 뼈대는 PMM에 국한되지 않습니다. “대시보드가 DB에 붙을 때 어떤 권한으로 붙는가"라는 질문입니다. 이 질문은 어느 관측 스택에나 유효합니다.

점검할 지점을 정리하면 이렇습니다.

확인할 것
데이터소스 계정의 권한 목록SELECT 외에 무엇이 붙어 있는지. ClickHouse면 SOURCES 유무
Grafana anonymous access 설정켜져 있으면 비인증 사용자가 데이터소스 API에 닿는다
Viewer 역할의 실제 범위대시보드 열람과 raw query 실행이 같은 등급인지
DB 서버에서 나가는 네트워크DB가 외부로 HTTP를 보낼 수 있는지
IMDS 버전v1이 열려 있으면 SSRF 한 방이 자격증명이 된다
Terraform state 저장소평문 비밀이 남아 있는지, 접근 주체가 누구인지

DB 계정 권한부터 보는 게 순서지만, 네트워크 쪽에서도 한 겹 막을 수 있습니다. DB 서버가 외부로 HTTP를 보낼 이유가 대개 없습니다. IMDSv2를 강제하는 것도 이 체인 두 번째 단계를 끊습니다.

남는 생각

Copy Fail이나 NGINX Rift 같은 사례는 코드 한 줄의 결함이었습니다. 이번 건은 성격이 다릅니다. ClickHouse가 url()을 제공하는 것도, Grafana가 데이터소스 API를 여는 것도 각각은 의도된 기능입니다. 두 기능이 만나는 자리에서 권한 설정이 넉넉했던 것이 문제였습니다.

그래서 패치만으로 끝나지 않습니다. 3.9.1로 올린 뒤에도 데이터소스 계정에 무엇이 붙어 있는지 한 번 확인해 볼 만합니다. 저는 이 공지를 읽고 나서 ClickHouse의 SOURCES 권한이 그렇게 많은 것을 묶고 있는 줄 몰랐다는 걸 알았어요.

참고