리포지토리에서 중요한 데이터 제거 정보
git filter-repo
와 같은 도구를 사용하여 리포지토리의 기록을 변경할 때는 그 의미를 이해하는 것이 중요합니다. 기록을 다시 작성하려면 공동 작업자와 신중하게 조정하여 성공적으로 실행해야 하며 관리해야 하는 여러 가지 부작용이 있습니다.
제거해야 하는 중요한 데이터가 비밀(예: 암호/토큰/자격 증명)인 경우와 마찬가지로 첫 번째 단계로 해당 비밀을 해지 및/또는 회전해야 합니다. 비밀이 해지되거나 회전되면 더 이상 액세스에 사용할 수 없으며 문제를 해결하는 데 충분할 수 있습니다. 기록을 다시 작성하고 비밀을 제거하는 추가 단계를 거치는 것은 보증되지 않을 수 있습니다.
다시 쓰기 기록의 부작용
기록을 다시 작성하면 다음과 같은 부작용이 발생할 수 있습니다.
- 재확산 위험 높음: 안타깝지만, 중요한 데이터를 리포지토리에 다시 푸시하여 더 큰 혼란을 만들기 쉽습니다. 동료 개발자가 다시 쓰기 전의 복제본을 가지고 있고 다시 쓰기한 후 단순히
git pull
을 실행된 후git push
를 실행하면 중요한 데이터가 반환됩니다. 복제본을 삭제하고 다시 복제하거나 여러 단계를 신중하게 진행하여 먼저 복제본을 정리해야 합니다. - 다른 개발자의 작업 손실 위험: 다른 개발자가 정리를 시도하는 동안 중요한 데이터가 포함된 분기를 계속 업데이트하는 경우 정리를 다시 실행하거나 작업을 취소해야 합니다.
- 변경된 커밋 해시: 기록을 다시 작성하면 중요한 데이터 및 이후에 제공된 모든 커밋을 도입한 커밋의 해시가 변경됩니다. 변경되지 않는 커밋 해시에 의존하는 모든 도구나 자동화가 손상되거나 문제가 발생합니다.
- 분기 보호 문제: 강제 푸시를 방지하는 분기 보호가 있는 경우 중요한 데이터를 제거하려면 해당 보호를 해제(적어도 일시적으로)해야 합니다.
- B닫힌 끌어오기 요청에 대한 끊어진 Diff 뷰: 중요한 데이터를 제거하려면 끌어오기 요청에서 diff 뷰를 표시하는 데 사용되는 내부 참조를 제거해야 하므로 이러한 diff는 더 이상 볼 수 없습니다. 이는 중요한 데이터를 도입한 PR뿐만 아니라 중요한 데이터 PR이 병합된 후 기록 버전에서 빌드되는 PR에도 해당됩니다(이후 PR이 중요한 데이터를 사용하여 파일을 추가하거나 수정하지 않은 경우에도 마찬가지).
- 열린 끌어오기 요청과의 잘못된 상호 작용: 변경된 커밋 SHA는 다른 PR diff로 인해 발생하며, 이전 PR diff에 대한 주석이 무효화되고 손실되어 작성자와 검토자에게 혼동을 줄 수 있습니다. 리포지토리에서 파일을 제거하기 전에 모든 열려 있는 끌어오기 요청을 병합하거나 닫는 것이 좋습니다.
- 커밋 및 태그에 대한 서명 손실: 커밋이나 태그에 대한 서명은 커밋 해시에 따라 달라집니다. 커밋 해시는 기록 다시 쓰기에 의해 수정되므로 서명은 더 이상 유효하지 않으며 많은 기록 다시 쓰기 도구(
git filter-repo
포함)는 단순히 서명을 제거합니다. 실제로git filter-repo
는 중요한 데이터 제거 이전의 커밋에 대한 커밋 서명과 태그 서명을 제거합니다. (기술적으로 필요한 경우--refs
옵션을git filter-repo
로 변경하여 이 문제를 해결할 수 있지만, 기록에 중요한 데이터가 있고 범위에서 중요한 데이터를 도입한 커밋을 포함하는 모든 참조를 지정하도록 주의해야 합니다.) - 다른 사용자를 중요한 데이터로 직접 연결: Git은 커밋 식별자에 기본 제공된 암호화 검사를 사용하여 설계되었으므로 악의적인 개인이 서버에 침입하여 기록을 수정할 수 없습니다. 이는 보안 관점에서 유용하지만 중요한 데이터 관점에서 중요한 데이터를 삭제하는 것은 매우 관련된 조정 프로세스라는 것을 의미합니다. 또한 기록을 수정할 때 기존 클론이 있는 단서가 있는 사용자는 기록 차이를 확인하고 이를 사용하여 중앙 리포지토리에서 제거한 복제에서 중요한 데이터를 빠르고 쉽게 찾을 수 있습니다.
중요한 데이터 노출 정보
리포지토리에서 중요한 데이터를 제거하려면 다음 네 가지 고급 단계가 포함됩니다.
- git-filter-repo를 사용하여 리포지토리를 로컬로 다시 작성
- 로컬로 다시 작성된 기록을 사용하여 GitHub에서 리포지토리 업데이트
- 동료와 협력하여 존재하는 다른 클론을 정리
- 반복 방지 및 향후 중요한 데이터 유출 방지
기록을 다시 작성하고 강제로 푸시하는 경우 중요한 데이터가 있는 커밋은 다른 곳에서도 계속 액세스할 수 있습니다.
- 리포지토리의 모든 클론 또는 포크에서
- GitHub Enterprise Server의 캐시된 뷰에서 SHA-1 해시를 통해 직접
- 이를 참조하는 끌어오기 요청을 통해
다른 사용자의 리포지토리 복제본에서는 중요한 데이터를 제거할 수 없습니다. 대신 git filter-repo
매뉴얼의 다른 복사본이 정리되었는지 확인하기: 동료의 클론에 나오는 지침을 참고하여 다른 사용자들이 직접 정리하도록 해야 합니다. 그러나 사이트 관리자에게 문의에 문의하여 GitHub Enterprise Server의 끌어오기 요청에서 중요한 데이터에 대한 캐시된 보기 및 영구적으로 제거할 수 있습니다.
중요한 데이터를 도입한 커밋이 모든 포크에 있는 경우 계속 액세스할 수 있습니다. 포크 소유자와 조정하여 중요한 데이터를 제거하거나 포크를 완전히 삭제하도록 요청해야 합니다.
리포지토리의 기록을 다시 작성하기로 결정할 때 이러한 제한 사항을 고려하세요.
git-filter-repo를 사용하여 로컬 리포지토리의 기록에서 파일 제거
-
git filter-repo
도구의 최신 릴리스를 설치합니다. 적어도 버전 2.47을 의미하는--sensitive-data-removal
플래그가 있는 버전이 필요합니다.git filter-repo
는 수동으로 설치하거나 패키지 관리자를 사용하여 설치할 수도 있습니다. 예를 들어, HomeBrew를 사용하여 도구를 설치하려면brew install
명령을 사용하세요.brew install git-filter-repo
자세한 내용은
newren/git-filter-repo
리포지토리의 INSTALL.md를 참조하세요. -
로컬 컴퓨터에 리포지토리를 복제합니다. 리포지토리 복제을(를) 참조하세요.
git clone https://HOSTNAME/YOUR-USERNAME/YOUR-REPOSITORY
-
리포지토리의 작업 디렉터리로 이동합니다.
cd YOUR-REPOSITORY
-
git filter-repo
명령을 실행하여 중요한 데이터를 정리합니다.모든 분기/태그/참조에서 특정 파일을 삭제하려면
PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA
를 파일 이름뿐만 아니라 제거하려는 파일의 git 경로로 바꾸는 다음 명령을 실행합니다(예:src/module/phone-numbers.txt
).git filter-repo --sensitive-data-removal --invert-paths --path PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA
Important
중요한 데이터가 있는 파일이 다른 경로에 있는 경우(이동했거나 또는 이름이 변경되었기 때문에) 해당 파일에 대한 추가
--path
인수를 추가하거나 이 명령을 두 번째로 실행하여 대체 경로의 이름을 지정해야 합니다.리포지토리의 기록에서 발견된 바이너리가 아닌 파일에서
../passwords.txt
에 나열된 모든 텍스트를 바꾸려면 다음 명령을 실행합니다.git filter-repo --sensitive-data-removal --replace-text ../passwords.txt
-
리포지토리의 기록에서 원하는 모든 항목을 제거했는지 다시 확인합니다.
-
이 기록 다시 쓰기로 인해 부정적인 영향을 받는 끌어오기 요청 수를 확인합니다. 아래 정보가 필요합니다.
$ grep -c '^refs/pull/.*/head$' .git/filter-repo/changed-refs 4
-c
를 삭제하여 영향을 받는 끌어오기 요청을 확인할 수 있습니다.$ grep '^refs/pull/.*/head$' .git/filter-repo/changed-refs refs/pull/589/head refs/pull/602/head refs/pull/604/head refs/pull/605/head
이 출력에는 두 번째 슬래시와 세 번째 슬래시 사이에 끌어오기 요청 번호가 포함됩니다. 영향을 받는 끌어오기 요청 수가 예상보다 많은 경우, 잘못된 효과 없이 이 클론을 삭제하고 다시 쓰기를 다시 실행하거나 중요한 데이터 제거를 중단하면 됩니다. 다음 단계로 이동하면 다시 쓰기는 되돌릴 수 없습니다.
-
리포지토리의 상태에 만족했다면 로컬 변경 내용을 강제로 푸시하여 GitHub Enterprise Server 인스턴스에 리포지토리를 덮어쓰세요.
--mirror
에 의해--force
가 암시적으로 포함되기는 하지만, 아래에서는 리포지토리를 정리하는 동안 모든 분기, 태그, 참조를 강제로 업데이트하고, 다른 사용자가 해당 참조에서 수행한 변경 내용을 모두 폐기한다는 점을 상기시키기 위해 명시적으로 포함하였습니다.git push --force --mirror origin
GitHub Enterprise Server가 관련 참조를 읽기 전용으로 표시하기 때문에 이 명령은
refs/pull/
로 시작하는 모든 참조를 푸시하지 못합니다. 이러한 푸시 오류는 다음 섹션에서 다룰 예정입니다. 다른 참조에 대해서도 푸시 오류가 발생했다면 해당 분기에 분기 보호 기능이 활성화되어 있을 가능성이 높습니다. 일시적으로 보호 기능을 해제하고 푸시를 다시 실행해야 합니다. 업데이트에 대한 유일한 실패가refs/pull/
로 시작하는 참조일 때까지 반복합니다.
GitHub에서 데이터 완전히 제거
git filter-repo
를 사용하여 중요한 데이터를 제거하고 변경 내용을 GitHub Enterprise Server에 푸시한 후에는 GitHub Enterprise Server에서 데이터를 완전히 제거하려면 몇 가지 단계를 더 수행해야 합니다.
-
사이트 관리자에게 문의에 문의하고 다음 정보를 제공합니다.
- 문제의 소유자 및 리포지토리 이름(예: YOUR-USERNAME/YOUR-REPOSITORY)
- 이전 단계에서 찾은 영향을 받는 끌어오기 요청 수. 이는 영향을 받는 정도를 고객 지원팀이 확인하는 데 사용됩니다.
git filter-repo
에서 보고된 첫 번째 변경된 커밋(출력에서NOTE: First Changed Commit(s)
를 찾아보세요.)- git-filter-repo 출력에서
NOTE: There were LFS Objects Orphaned by this rewrite
가 나타나는 경우(첫 번째 변경된 커밋 바로 뒤), 분리된 LFS 개체를 언급하고 명명된 파일을 티켓에 업로드
PR 이외의 모든 참조를 성공적으로 정리하고 포크에 중요한 데이터에 대한 참조가 없는 경우 고객 지원팀에서 다음을 수행합니다.
- GitHub Enterprise Server에서 영향을 받는 모든 PR을 역참조하거나 삭제합니다.
- 서버에서 가비지 수집을 실행하여 스토리지에서 중요한 데이터를 제거합니다.
- 캐시된 뷰를 제거합니다.
- LFS 개체가 관련된 경우 분리된 LFS 개체를 삭제 및/또는 제거합니다.
사이트 관리자가 연결할 수 없는 Git 개체를 제거하는 방법에 대한 자세한 내용은 "명령줄 유틸리티"을(를) 참조하세요. 사이트 관리자가 도달 가능한 커밋을 식별하는 방법에 대한 자세한 내용은 "도달 가능한 커밋 식별하기"를 참조하세요.
-
협력자는 이전(오염된) 리포지토리 기록에서 만든 분기를 병합하지 않고 다시 지정해야 합니다. 하나의 병합 커밋은 방금 제거했던 오염된 기록의 일부 또는 전부를 다시 도입할 수 있습니다. 추가 단계를 수행해야 할 수도 있습니다.
git filter-repo
매뉴얼의 다른 복사본이 정리되었는지 확인하기: 동료의 클론을 참조하세요.
연결할 수 있는 커밋 식별
리포지토리에서 원치 않거나 민감한 데이터를 완전히 제거하려면 데이터를 처음 도입한 커밋이 분기, 태그, 당겨받기 요청 및 포크에서 완전히 참조되지 않도록 해야 합니다. 어디서든 단일 참조가 있으면 가비지 컬렉션이 데이터를 완전히 제거하지 못합니다.
SSH를 통해 어플라이언스에 연결할 때 다음 명령을 사용하여 기존 참조를 확인할 수 있습니다. 민감한 데이터를 처음 도입한 커밋의 SHA가 필요합니다.
ghe-repo OWNER/REPOSITORY -c 'git ref-contains COMMIT_SHA_NUMBER'
ghe-repo OWNER/REPOSITORY -c 'cd ../network.git && git ref-contains COMMIT_SHA_NUMBER'
이러한 명령 중 하나라도 결과를 반환하면 커밋이 성공적으로 가비지 컬렉션되기 전에 해당 참조를 제거해야 합니다. 두 번째 명령은 리포지토리의 포크에 존재하는 참조를 식별합니다(리포지토리에 포크가 없는 경우 실행을 건너뛸 수 있음).
refs/heads/
또는refs/tags/
(으)로 시작하는 결과는 각각 문제가 있는 커밋에 대한 참조가 여전히 포함된 분기 및 태그를 나타내며, 수정된 리포지토리에서 커밋이 완전히 정리되지 않았거나 강제 푸시되지 않았음을 의미합니다.refs/pull/
또는refs/__gh__/pull
(으)로 시작하는 결과는 문제가 있는 커밋을 참조하는 당겨받기 요청을 나타냅니다. 커밋이 가비지 컬렉션되도록 허용하려면 이러한 당겨받기 요청을 삭제해야 합니다. 사이트 관리자 대시보드의https://HOSTNAME/stafftools/repositories/OWNER/REPOSITORY/PULL_REQUESTS/<PULL-REQUEST-NUMBER>
서 당겨받기 요청을 삭제할 수 있으며, 풀 리퀘스트 번호를<PULL-REQUEST-NUMBER>
(으)로 바꿀 수 있습니다.
어떤 포크에서든 참조가 발견되면 결과는 비슷하게 보이지만 refs/remotes/NWO/
(으)로 시작됩니다. 이름으로 포크를 식별하려면 다음 명령을 실행하면 됩니다.
ghe-nwo NWO
중요한 데이터는 하나의 복제본으로 이동하여 정리된 리포지토리에서 가져온 다음, 정리된 리포지토리에서 관련 분기나 태그 위에 있는 중요한 데이터가 포함된 모든 분기와 태그를 다시 적용하여 리포지토리의 포크에서 제거할 수 있습니다. 또는 포크를 모두 삭제할 수 있으며 필요한 경우 루트 리포지토리 정리가 완료되면 리포지토리를 다시 포크할 수 있습니다.
커밋의 참조를 제거한 후 명령을 다시 실행하여 다시 확인합니다.
ref-contains
명령 중 하나에서 결과가 없는 경우 다음 명령을 실행하여 --prune
플래그를 사용하여 가비지 컬렉션을 실행하여 참조되지 않은 커밋을 제거할 수 있습니다.
ghe-repo-gc -v --prune OWNER/REPOSITORY
가비지 컬렉션으로 커밋이 성공적으로 제거되면 https://HOSTNAME/stafftools/repositories/OWNER/REPOSITORY
에서 리포지토리의 사이트 관리자 대시보드로 이동하여 네트워크를 선택한 다음 Git 캐시 무효화를 클릭하여 캐시된 데이터를 제거할 수 있습니다.
향후 실수로 인한 커밋 방지
기여자가 실수로 커밋하지 못하게 하면 중요한 정보가 노출되지 않도록 방지할 수 있습니다. 자세한 내용은 조직에서 데이터 유출을 방지하기 위한 모범 사례을(를) 참조하세요.
공유해서는 안 되는 항목을 커밋하거나 푸시하지 않도록 하기 위해 수행할 수 있는 몇 가지 작업은 다음과 같습니다.
- git에서 추적하지 않아야 하는 파일에서 중요한 데이터를 찾을 수 있는 경우 해당 파일 이름을
.gitignore
에 추가합니다(그리고 다른 개발자가 보호되도록 해당 변경 내용을.gitignore
에 커밋하고 푸시해야 함). - 코드에서 비밀 하드코딩을 방지합니다. 환경 변수 또는 Azure Key Vault, AWS Secrets Manager, HashiCorp Vault와 같은 비밀 관리 서비스를 사용하여 런타임에 비밀을 관리하고 삽입합니다.
- 커밋되거나 다른 곳으로 푸시되기 전에 중요한 데이터를 확인하는 사전 커밋 후크를 만들거나 git-secrets나 gitleaks와 같은 사전 커밋 후크에서 잘 알려진 도구를 사용합니다. (선택한 사전 커밋 후크를 설정하도록 각 공동 작업자에게 요청해야 합니다.)
- GitHub Desktop 또는 gitk와 같은 시각적 프로그램을 사용하여 변경 내용을 커밋합니다. 시각적 프로그램을 사용하면 일반적으로 각 커밋을 통해 추가, 삭제 및 수정할 파일을 정확하고 쉽게 확인할 수 있습니다.
- 대신 catch-all 명령
git add .
와 명령줄의git commit -a
을 사용하지 말고git add filename
과git rm filename
을 사용하여 파일을 개별적으로 스테이징하세요. - 각 파일 내에서 변경 내용을 개별적으로 검토하고 스테이징하는 데
git add --interactive
를 사용합니다. - 커밋을 위해 스테이징한 변경 내용을 검토하는 데
git diff --cached
를 사용합니다.-a
플래그를 사용하지 않는 한git commit
에서 생성되는 정확한 차이입니다. - 리포지토리에 대한 푸시 보호를 사용하도록 설정하여 하드 코딩된 비밀이 포함된 푸시가 코드베이스에 커밋되지 않도록 탐지하고 방지합니다. 자세한 내용은 푸시 보호 정보을(를) 참조하세요.
추가 참고 자료
git filter-repo
매뉴얼 페이지, 특히 "토론" 섹션의 "중요한 데이터 제거" 하위 섹션- Pro Git: Git 도구 - 기록 다시 쓰기
- 비밀 검사 정보