Redis 에이전트는 배포 옆에서 실행되는 서비스입니다. Redis는 노드마다 ACL을 따로 두므로, 에이전트는 복제본과 클러스터의 모든 노드까지 각 노드에서 Redis 자체의 ACL을 redis 데이터 서비스의 서명된 정책과 일치시킵니다. 그러면 Redis가 모든 명령을 직접 검사하며, 에이전트는 어떤 클라이언트 앞에도 끼지 않습니다. 배포판은 실행 가능한 JAR agents/redis/grantforge-agent-redis-<version>.jar로 제공하며, Java 17 이상이 필요합니다.
저장소의 Testcontainers 통합 테스트는 에이전트와 배포되는 JAR를 Redis 7.2, 7.4, 8.2, 8.10과 Valkey 8.1, 9.1의 복제본 하나를 둔 프라이머리, 그리고 Redis 8.10 클러스터에서 실행합니다. 이것이 다른 모든 버전을 인증하는 것은 아니며, 에이전트는 ACL 로그 항목에 번호가 없는 7.2보다 오래된 Redis를 거부합니다. 서비스 플러그인은 Redis를 참고하세요.
동작 방식
- 셀렉터는 GrantForge의 것, 루트 권한은 운영자의 것입니다. 사용자의 루트 권한이나 셀렉터 중 하나가 허용하면 Redis는 명령을 허용합니다. 에이전트는 자신이 관리하는 사용자의 모든 셀렉터를 유지하며 루트 권한과 비밀번호는 절대 건드리지 않습니다: 셀렉터 하나는 사용자가 읽거나 쓰거나 둘 다 할 수 있는 키를, 하나는 구독할 수 있는 채널을, 하나는 발행할 수 있는 채널을 담고, 모니터링할 수 있는 노드에는 하나가 더 있습니다.
- 변경은 한 명령으로 이루어집니다.
ACL SETUSER user clearselectors (…)는 한 노드에서 사용자의 모든 셀렉터를 한꺼번에 교체하므로, 사용자가 이전과 새 권한을 함께 갖거나 둘 다 잃는 일이 없습니다. 수동으로 바뀐 셀렉터도 다음 동기화 때 같은 방식으로 바로잡습니다. - 패턴은 성립하는 곳에만 부여합니다. 정책의 키나 채널 패턴은 정책이 그 패턴 전체에서 사용자를 똑같이 결정할 때만 Redis 자체의 패턴으로 부여됩니다. 에이전트는 겹치는 모든 정책을 따라 패턴을 잘라 각 부분을 결정하므로, 다른 사용자의 정책이 이를 막지 않습니다. 정책이 그 안에서 다르게 결정하는 패턴은 보류되고 한 번 로그에 남습니다.
- 위험한 것은 주지 않고, 남의 키를 나열하지 않습니다. 셀렉터는 Redis가 위험하다고 보는 것을 절대 주지 않으며,
SCAN과RANDOMKEY는 모든 키를 읽을 수 있는 사용자에게만 줍니다. - 모든 노드에, 가능하면 저장합니다. 에이전트는 주어진 노드에서 복제본과 클러스터 노드를 찾아 각각을 일치시킵니다. ACL을 파일에 두는 노드는
ACL SAVE로 저장하고, 그렇지 않은 노드는 재시작하면 셀렉터를 잃었다가 다음 동기화 때 다시 받습니다. - 무엇을 허용하는지 Redis에 묻습니다. 루트 권한은 에이전트의 셀렉터와 나란히 결정합니다. 에이전트는 그 규칙을 읽는 대신 정책이 거부하는 키, 채널이나 노드에 사용자가 명령을 실행할 수 있는지
ACL DRYRUN으로 Redis에 묻고, 사용자, 접근과 정책을 밝혀 한 번씩 경고합니다. 어떤 셀렉터도 묶지 않는, 모든 키에 모든 명령을 가진 사용자와, 누구나 비밀번호 없이 로그인할 수 있는 사용자도 경고합니다. - 거부는 각 노드의 ACL 로그에서 옵니다. 아래 거부 보고를 참고하세요.
- 현재 시점의 정책. 각 동기화는 그 시점에 적용되는 정책과 Redis에 있는 사용자와 노드로 계획을 세우므로, 유효 기간이 제때 시작하고 끝납니다.
배포
-
redis데이터 서비스를 추가하고 정책을 구성합니다. 데이터 권한 → 에이전트를 열어 서비스를 선택하고 에이전트 토큰을 발급합니다. 토큰은 한 번만 표시되니 에이전트 계정만 읽을 수 있는 파일에 저장하세요. -
모든 노드에 에이전트의 ACL 사용자를 만듭니다. 이 사용자는 사용자를 읽고, 셀렉터를 설정하고, ACL 파일을 저장하고, ACL 로그를 읽고, 드라이런을 실행하고, 클러스터 노드를 찾습니다.
+acl|setuser는 자신에게 무엇이든 줄 수 있으므로 이 사용자를 관리자처럼 보호하세요.ACL SETUSER grantforge on >… -@all +@connection +acl|whoami +acl|users +acl|getuser +acl|setuser +acl|log +acl|dryrun +acl|save +config|get +info +cluster|nodes +command|info -
에이전트가 관리하는 사용자의 루트 권한은 로그인처럼 모든 사용자에게 필요한 것으로 제한하세요. 나머지는 에이전트가 줍니다. 그보다 많이 주는 루트 권한은 발견되어 경고됩니다.
ACL SETUSER alice on >… resetkeys resetchannels -@all +@connection -
예시 주소, 경로와 이름을 바꿔 설정 파일을 씁니다:
grantforge.server.url=https://grantforge.example.com grantforge.token.file=/etc/grantforge/redis-agent.token grantforge.instance=redis-agent-1 grantforge.cache.dir=/var/lib/grantforge/redis-agent grantforge.redis.nodes=redis1.internal:6379,redis2.internal:6379 grantforge.redis.user=grantforge grantforge.redis.password.file=/etc/grantforge/redis-agent.password이 경로는 에이전트 머신에 있으며, 캐시 디렉터리는 쓰기 가능해야 합니다. 비밀번호 파일은 UTF-8로 읽고 앞뒤 공백을 지웁니다.
-
예를 들어 systemd 서비스로 에이전트를 시작합니다:
java -jar agents/redis/grantforge-agent-redis-<version>.jar /etc/grantforge/redis-agent.properties<version>을 배포 버전으로 바꾸세요.java -Dgrantforge.…=… -jar …로 준 설정이 파일보다 우선합니다. 에이전트는 SIGTERM에 깔끔하게 멈추고 대기 중인 이벤트를 보내거나 스풀링하며, Redis는 받은 것을 유지합니다.
| 설정 | 용도 |
|---|---|
grantforge.server.url, grantforge.token.file, grantforge.instance, grantforge.cache.dir |
필수: GrantForge 서버, 에이전트 토큰 파일, 이 에이전트의 이름, 그리고 서명된 스냅샷, 스풀된 이벤트와 감사 로그 읽기 위치를 둘 쓰기 가능한 디렉터리 |
grantforge.signing.key.file |
스냅샷 서명 키를 고정하는 선택 파일. 에이전트 페이지에서 Base64로 인코딩된 X.509 Ed25519 공개 키를 복사합니다. 생략하면 처음 연결할 때 서버에서 가져옵니다 |
grantforge.redis.nodes |
필수: 노드 주소 host:port, 포트를 쓰지 않으면 6379 |
grantforge.redis.discover |
기본값 true: 주어진 노드에서 클러스터의 나머지 노드와 프라이머리의 복제본을 찾습니다. 노드가 알리는 주소에 에이전트가 닿을 수 없으면 false로 주어진 노드만 사용합니다 |
grantforge.redis.user, grantforge.redis.password.file |
에이전트의 ACL 사용자(비우면 기본 사용자)와 그 비밀번호 |
grantforge.redis.tls.mode, grantforge.redis.tls.ca.file, grantforge.redis.tls.certificate.file, grantforge.redis.tls.key.file, grantforge.redis.tls.key.password.file |
disable(기본값), verify-ca 또는 verify-full; 서버를 검증하는 PEM CA 인증서, 비우면 Java가 신뢰하는 것; PEM 클라이언트 인증서, 별도 파일일 때의 키, 키의 비밀번호 |
grantforge.redis.timeout.ms |
연결과 명령 하나에 허용되는 시간, 기본값 10000, 범위 1..600000 밀리초 |
grantforge.redis.unmanaged.users |
에이전트가 건드리지 않는 사용자, 쉼표로 구분, 예: 서비스 계정. 자신의 사용자와 모든 키에 모든 명령을 가진 사용자는 항상 건드리지 않습니다 |
grantforge.sync.interval.ms |
에이전트가 Redis를 맞추는 주기, 기본값 10000, 범위 1000..3600000 밀리초 |
grantforge.audit.log, grantforge.audit.log.interval.ms |
기본값 true; false면 ACL 로그를 읽지 않습니다. ACL 로그를 읽는 주기, 기본값 5000 밀리초 |
grantforge.connect.timeout.ms, grantforge.read.timeout.ms |
GrantForge 연결·읽기 시간 초과, 기본값 5000 / 8000 밀리초 |
grantforge.refresh.interval.ms |
에이전트가 새 정책을 요청하는 주기, 기본값 30000, 최소 1000 밀리초 |
grantforge.audit.batch.size, grantforge.audit.queue.capacity, grantforge.audit.flush.interval.ms, grantforge.audit.spool.limit.bytes |
접근 이벤트: 배치 크기(기본값 500), 큐 크기(기본값 10000), 전송 주기(기본값 5000 밀리초)와 디스크 스풀 한도(기본값 64 MiB) |
grantforge.audit.fold.window.ms |
같은 허용 접근을 이벤트 하나로 묶는 시간, 기본값 10000, 최대 3600000 밀리초. 0은 하나씩 보냅니다 |
거부 보고
Redis는 무엇을 허용했는지 기록하지 않으므로, 에이전트는 각 노드의 ACL 로그에서 Redis가 거부한 것을 보고합니다: 누가 무엇을 언제 어디서 거부당했는지, 그리고 정책이 Redis와 같이 결정하는지입니다. Redis는 1분 안에 반복된 거부를 이전 항목에 합치며, 에이전트는 항목이 갱신될 때마다 다시 보고합니다.
| 로그의 이유 | 접근 이벤트 |
|---|---|
key |
Redis가 명령에 붙인 표시에 따라 키의 read 또는 write |
channel |
채널의 subscribe 또는 publish |
command |
모니터링 명령은 노드의 monitor; 다른 명령은 키 없이 기록되므로 보고하지 않습니다 |
auth |
보고하지 않음: 거부된 로그인은 어떤 접근도 가리키지 않습니다 |
사용자, 그룹과 역할
정책은 Redis의 ACL 사용자를 이름으로 지정합니다. Redis에는 자체 역할이나 그룹이 없으므로, 그룹은 GrantForge가 사용자를 넣은 그룹입니다.
결정과 제한 사항
- 스냅샷이 없으면 에이전트는 아무것도 바꾸지 않습니다. 사용하지 않는 서비스나 유형이
redis가 아닌 서비스에 묶인 토큰은 아무것도 부여하지 않습니다: 에이전트는 관리하는 사용자의 셀렉터를 비웁니다. - 에이전트가 실행되지 않는 동안 Redis는 마지막으로 받은 것을 유지하고, 정책 변경은 에이전트를 기다립니다. 변경은 갱신 주기와 동기화 주기 안에 Redis에 반영됩니다.
- 에이전트는 관리하는 사용자의 모든 셀렉터를 유지합니다: 운영자가 수동으로 추가한 셀렉터는 다음 동기화 때 제거됩니다. 추가 권한은 루트 권한에 주거나 그 사용자를 운영자에게 맡기세요.
- Redis의 ACL은 데이터베이스가 아니라 키와 채널을 지정하므로, 패턴은 노드의 모든 데이터베이스에서 해당 키를 포함합니다.
- 사용자가 아예 실행할 수 없는 명령이 거부되면 Redis는 키 없이 기록하므로 보고하지 않습니다.
- Redis는 값 안을 필터링하지도 마스킹하지도 않습니다.
