website: dbclient 감사로그 actor fingerprint 파싱 버그 수정
어제(#452) 배포한 dbclient 감사로그 기능을 실제 dbclient 세션으로 라이브 테스트해보니, 실행자(actor)가 계속 `unknown(등록되지 않은 키, fp=ssh-ed25519)`로만 찍혔다.
배포하고 바로 다음 날 문제가 드러난 셈이라, 로그부터 확인했다. fingerprint로 ssh-ed25519가 찍히고 있다는 게 이상했다. 이건 SSH 키 알고리즘 이름이지 지문(fingerprint)이 아니다. SHA256 해시 형태로 나와야 정상인데, 알고리즘 이름이 그 자리에 들어가고 있다는 건 파싱 로직이 잘못된 필드를 읽고 있다는 뜻이었다.
원인은 resolve_actor() 함수에서 SSH_USER_AUTH 파일을 읽는 부분에 있었다.
used_fp="1=="publickey"{print 2; exit}' "SSH_USER_AUTH")"
SSH_USER_AUTH 파일의 한 줄은 publickey <알고리즘> <SHA256:지문> 이렇게 3필드로 구성된다. 그런데 스크립트는 2번째 필드(2가 알고리즘 이름(ssh-ed25519 등)이고, 지문은 $3이다. 어제 코드를 짤 때 이 파일 포맷을 문서만 보고 짐작했던 게 실제 필드 순서와 어긋났던 것 같다.
고칠 때 단순히 3으로 바꾸는 방법도 있었지만, 그러면 알고리즘 이름 길이에 따라 필드 개수가 달라질 수 있는 경우(예를 들어 ecdsa-sha2-nistp256처럼 이름 자체에 공백이 섞이거나 포맷이 변형되는 케이스)에 다시 깨질 여지가 있다. 그래서 필드 순서에 기대는 대신, SHA256:로 시작하는 필드를 찾아서 뽑아내는 방식으로 바꿨다.
used_fp="1=="publickey"{for(i=2;i<=NF;i++) if (i ~ /^SHA256:/) {printi; exit}}' "$SSH_USER_AUTH")"
이렇게 하면 publickey 다음에 어떤 필드가 몇 개 오든 상관없이 SHA256:로 시작하는 값만 정확히 집어낸다. 필드 위치에 의존하지 않는 쪽이 이런 외부 포맷을 다룰 때 더 안전하다는 판단이었다.
검증은 세 단계로 했다. bash -n으로 문법 오류가 없는지 먼저 확인하고, awk 로직만 따로 떼어내서 임의의 입력으로 SHA256:... 값을 정확히 추출하는지 시뮬레이션했다. 그리고 실제 SSH_USER_AUTH 파일 포맷 자체는 어제 라이브 dbclient 세션에서 얻은 실측 로그(fp=ssh-ed25519)를 거꾸로 추적해서 확인한 것이라, 배포 후 다시 한 번 라이브로 재확인이 필요한 상태로 남겨뒀다.
PR은 생성 후 1분 만에 병합됐다. 감사로그가 실행자를 제대로 못 잡는 문제라 운영에 바로 영향이 있는 버그였고, 수정 자체도 스크립트 한 줄 로직 교체라 리뷰랄 것도 딱히 필요 없었다.
어제 짠 코드를 하루 만에 라이브로 검증하다가 바로 버그를 잡은 케이스다. 파일 포맷을 문서나 추측만으로 파싱 로직을 짤 때는 필드 순서보다 값의 패턴(여기서는 SHA256: 접두사)으로 찾는 게 더 견고하다는 걸 다시 확인했다.