Tines 감사 로그 수집
이 문서에서는 Amazon S3를 사용하여 Tines 감사 로그를 Google Security Operations에 수집하는 방법을 설명합니다.
시작하기 전에
다음 기본 요건이 충족되었는지 확인합니다.
- Google SecOps 인스턴스입니다.
- Tines에 대한 액세스 권한 관리
- AWS (S3, Identity and Access Management (IAM), Lambda, EventBridge)에 대한 권한 있는 액세스
Tines URL 가져오기
- 브라우저에서 테넌트의 Tines UI를 엽니다.
- 주소 표시줄에서 도메인을 복사합니다. 이 도메인은
TINES_BASE_URL
로 사용됩니다.- 형식:
https://<tenant-domain>
(예:https://<tenant-domain>.tines.com
)
- 형식:
Tines 서비스 API 키 (권장) 또는 개인 API 키 만들기
나중에 사용할 값을 저장합니다.
TINES_BASE_URL
(예:https://<domain>.tines.com
)TINES_API_KEY
- 다음 단계에서 만드는 토큰
옵션 1 - 서비스 API 키 (권장)
- 탐색 메뉴 > API 키로 이동합니다.
- + 새 키를 클릭합니다.
- 서비스 API 키를 선택합니다.
- 설명이 포함된 이름 (예:
SecOps Audit Logs
)을 입력합니다. - 만들기를 클릭합니다.
- 생성된 토큰을 즉시 복사하고 안전하게 저장합니다. 이 토큰은
TINES_API_KEY
로 사용됩니다.
옵션 2 - 개인 API 키 (서비스 키를 사용할 수 없는 경우)
- 탐색 메뉴 > API 키로 이동합니다.
- + 새 키를 클릭합니다.
- 개인 API 키를 선택합니다.
- 설명을 포함한 이름을 입력합니다.
- 만들기를 클릭합니다.
생성된 토큰을 복사하여 안전하게 저장합니다.
감사 로그 읽기 권한 부여
- 테넌트 소유자로 로그인합니다 (또는 테넌트 소유자에게 로그인하도록 요청).
- 설정 > 관리 > 사용자 관리로 이동합니다(또는 왼쪽 상단 메뉴에서 팀 이름을 클릭하고 사용자를 선택합니다).
- 서비스 API 키와 연결된 서비스 계정 사용자를 찾습니다 (API 키와 이름이 동일함).
- 개인 API 키를 사용하는 경우 대신 사용자 계정을 찾으세요.
- 사용자를 클릭하여 프로필을 엽니다.
- 테넌트 권한 섹션에서 AUDIT_LOG_READ를 사용 설정합니다.
- 저장을 클릭합니다.
(선택사항) API 액세스 확인
curl 또는 HTTP 클라이언트를 사용하여 엔드포인트를 테스트합니다.
curl -X GET "https://<tenant-domain>/api/v1/audit_logs?per_page=1" \ -H "Authorization: Bearer <TINES_API_KEY>" \ -H "Content-Type: application/json"
감사 로그 항목이 포함된 JSON 응답이 표시됩니다.
UI에서 설정> 모니터링> 감사 로그로 이동하여 감사 로그가 있는지 확인할 수도 있습니다 (AUDIT_LOG_READ 권한 필요).
AWS S3 버킷 구성
- 이 사용자 가이드(버킷 만들기)에 따라 Amazon S3 버킷을 만듭니다.
- 나중에 참조할 수 있도록 버킷 이름과 리전을 저장합니다 (예:
tines-audit-logs
).
Lambda S3 업로드의 IAM 정책 및 역할 구성
- AWS 콘솔에서 IAM > 정책 > 정책 만들기 > JSON 탭으로 이동합니다.
- 다음 정책을 복사하여 붙여넣습니다.
정책 JSON (다른 버킷 이름을 입력한 경우
tines-audit-logs
대체):{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowPutObjects", "Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::tines-audit-logs/*" }, { "Sid": "AllowGetStateObject", "Effect": "Allow", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::tines-audit-logs/tines/audit/state.json" } ] }
다음 > 정책 만들기를 클릭합니다.
정책 이름을
TinesLambdaS3Policy
로 지정합니다.IAM > 역할 > 역할 생성 > AWS 서비스 > Lambda로 이동합니다.
방금 만든
TinesLambdaS3Policy
를 첨부합니다.역할 이름을
TinesAuditToS3Role
로 지정하고 역할 만들기를 클릭합니다.
Lambda 함수 만들기
- AWS 콘솔에서 Lambda > 함수 > 함수 만들기로 이동합니다.
- 처음부터 작성을 클릭합니다.
다음 구성 세부정보를 제공합니다.
설정 값 이름 tines_audit_to_s3
런타임 Python 3.13 아키텍처 x86_64 실행 역할 TinesAuditToS3Role
함수가 생성되면 코드 탭을 열고 스텁을 삭제한 후 다음 코드 (
tines_audit_to_s3.py
)를 붙여넣습니다.#!/usr/bin/env python3 # Lambda: Pull Tines Audit Logs to S3 (no transform) import os, json, time, urllib.parse from urllib.request import Request, urlopen from urllib.error import HTTPError, URLError import boto3 S3_BUCKET = os.environ["S3_BUCKET"] S3_PREFIX = os.environ.get("S3_PREFIX", "tines/audit/") STATE_KEY = os.environ.get("STATE_KEY", "tines/audit/state.json") LOOKBACK_SEC = int(os.environ.get("LOOKBACK_SECONDS", "3600")) # default 1h PAGE_SIZE = int(os.environ.get("PAGE_SIZE", "500")) # Max is 500 for Tines MAX_PAGES = int(os.environ.get("MAX_PAGES", "20")) TIMEOUT = int(os.environ.get("HTTP_TIMEOUT", "60")) HTTP_RETRIES = int(os.environ.get("HTTP_RETRIES", "3")) TINES_BASE_URL = os.environ["TINES_BASE_URL"] TINES_API_KEY = os.environ["TINES_API_KEY"] s3 = boto3.client("s3") def _iso(ts: float) -> str: return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(ts)) def _load_state() -> dict: try: obj = s3.get_object(Bucket=S3_BUCKET, Key=STATE_KEY) b = obj["Body"].read() return json.loads(b) if b else {} except Exception: return {} def _save_state(st: dict) -> None: s3.put_object( Bucket=S3_BUCKET, Key=STATE_KEY, Body=json.dumps(st, separators=(",", ":")).encode("utf-8"), ContentType="application/json", ) def _req(url: str) -> dict: attempt = 0 while True: try: req = Request(url, method="GET") req.add_header("Authorization", f"Bearer {TINES_API_KEY}") req.add_header("Accept", "application/json") req.add_header("Content-Type", "application/json") with urlopen(req, timeout=TIMEOUT) as r: data = r.read() return json.loads(data.decode("utf-8")) except HTTPError as e: if e.code in (429, 500, 502, 503, 504) and attempt < HTTP_RETRIES: retry_after = 1 + attempt try: retry_after = int(e.headers.get("Retry-After", retry_after)) except Exception: pass time.sleep(max(1, retry_after)) attempt += 1 continue raise except URLError: if attempt < HTTP_RETRIES: time.sleep(1 + attempt) attempt += 1 continue raise def _write(payload, page: int) -> str: ts = time.gmtime() key = f"{S3_PREFIX}{time.strftime('%Y/%m/%d/%H%M%S', ts)}-tines-audit-{page:05d}.json" s3.put_object( Bucket=S3_BUCKET, Key=key, Body=json.dumps(payload, separators=(",", ":")).encode("utf-8"), ContentType="application/json", ) return key def _extract_items(payload) -> list: if isinstance(payload, list): return payload if isinstance(payload, dict): audit_logs = payload.get("audit_logs") if isinstance(audit_logs, list): return audit_logs return [] def _extract_newest_ts(items: list, current: str | None) -> str | None: newest = current for it in items: # Use created_at as the timestamp field t = it.get("created_at") if isinstance(t, str) and (newest is None or t > newest): newest = t return newest def lambda_handler(event=None, context=None): st = _load_state() since = st.get("since") or _iso(time.time() - LOOKBACK_SEC) page = 1 pages = 0 total = 0 newest_ts = since while pages < MAX_PAGES: # Build URL with query parameters # Note: Tines audit logs API uses 'after' parameter for filtering base_url = f"{TINES_BASE_URL.rstrip('/')}/api/v1/audit_logs" params = { "after": since, # Filter for logs created after this timestamp "page": page, "per_page": PAGE_SIZE } url = f"{base_url}?{urllib.parse.urlencode(params)}" payload = _req(url) _write(payload, page) items = _extract_items(payload) total += len(items) newest_ts = _extract_newest_ts(items, newest_ts) pages += 1 # Check if there's a next page using meta.next_page_number meta = payload.get("meta") or {} next_page = meta.get("next_page_number") if not next_page: break page = next_page if newest_ts and newest_ts != since: st["since"] = newest_ts _save_state(st) return {"ok": True, "pages": pages, "items": total, "since": st.get("since")} if __name__ == "__main__": print(lambda_handler())
구성 > 환경 변수로 이동합니다.
수정 > 새 환경 변수 추가를 클릭합니다.
다음 표에 제공된 환경 변수를 입력하고 예시 값을 실제 값으로 바꿉니다.
환경 변수
키 예시 값 S3_BUCKET
tines-audit-logs
S3_PREFIX
tines/audit/
STATE_KEY
tines/audit/state.json
TINES_BASE_URL
https://your-tenant.tines.com
TINES_API_KEY
your-tines-api-key
LOOKBACK_SECONDS
3600
PAGE_SIZE
500
MAX_PAGES
20
HTTP_TIMEOUT
60
HTTP_RETRIES
3
함수가 생성되면 해당 페이지에 머무르거나 Lambda > 함수 > your-function을 엽니다.
구성 탭을 선택합니다.
일반 구성 패널에서 수정을 클릭합니다.
제한 시간을 5분 (300초)으로 변경하고 저장을 클릭합니다.
EventBridge 일정 만들기
- Amazon EventBridge > 스케줄러 > 일정 만들기로 이동합니다.
- 다음 구성 세부정보를 제공합니다.
- 반복 일정: 요금 (
1 hour
) - 타겟: Lambda 함수
tines_audit_to_s3
- 이름:
tines-audit-1h
.
- 반복 일정: 요금 (
- 일정 만들기를 클릭합니다.
Google SecOps용 읽기 전용 IAM 사용자 및 키 만들기
- AWS 콘솔에서 IAM > 사용자로 이동합니다.
- Add users를 클릭합니다.
- 다음 구성 세부정보를 제공합니다.
- 사용자:
secops-reader
를 입력합니다. - 액세스 유형: 액세스 키 - 프로그래매틱 액세스를 선택합니다.
- 사용자:
- 사용자 만들기를 클릭합니다.
- 최소 읽기 정책 (맞춤) 연결: 사용자 > secops-reader > 권한 > 권한 추가 > 정책 직접 연결 > 정책 만들기
JSON:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:GetObject"], "Resource": "arn:aws:s3:::tines-audit-logs/*" }, { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": "arn:aws:s3:::tines-audit-logs" } ] }
이름 =
secops-reader-policy
정책 만들기 > 검색/선택 > 다음 > 권한 추가를 클릭합니다.
secops-reader
의 액세스 키를 만듭니다(보안 사용자 인증 정보 > 액세스 키).액세스 키 만들기를 클릭합니다.
.CSV
을 다운로드합니다. (이 값은 피드에 붙여넣습니다.)
Tines 감사 로그를 수집하도록 Google SecOps에서 피드 구성
- SIEM 설정> 피드로 이동합니다.
- + 새 피드 추가를 클릭합니다.
- 피드 이름 필드에 피드 이름을 입력합니다 (예:
Tines Audit Logs
). - 소스 유형으로 Amazon S3 V2를 선택합니다.
- 로그 유형으로 Tines를 선택합니다.
- 다음을 클릭합니다.
- 다음 입력 파라미터의 값을 지정합니다.
- S3 URI:
s3://tines-audit-logs/tines/audit/
- 소스 삭제 옵션: 환경설정에 따라 삭제 옵션을 선택합니다.
- 최대 파일 기간: 지난 일수 동안 수정된 파일을 포함합니다. 기본값은 180일입니다.
- 액세스 키 ID: S3 버킷에 대한 액세스 권한이 있는 사용자 액세스 키입니다.
- 보안 비밀 액세스 키: S3 버킷에 액세스할 수 있는 사용자 보안 비밀 키입니다.
- 애셋 네임스페이스: 애셋 네임스페이스입니다.
- 수집 라벨: 이 피드의 이벤트에 적용된 라벨입니다.
- S3 URI:
- 다음을 클릭합니다.
- 확정 화면에서 새 피드 구성을 검토한 다음 제출을 클릭합니다.
도움이 더 필요하신가요? 커뮤니티 회원 및 Google SecOps 전문가로부터 답변을 받으세요.