Хотя GitLab обычно не так общедоступен, как GitHub, здесь действуют общие правила, касающиеся данных: если вы передали конфиденциальные / секретные данные кому-то, кому нельзя доверять, ваш секрет уже раскрыт, и вам следует прекратить зависеть от него.
Это означает, что ключевой вопрос не в том, или, по крайней мере, пока, в том, «как убедить GitLab забыть свои секреты», а скорее «полностью ли я полностью доверяю как серверу (-ам) GitLab, так и всем остальным.еще, кто имел доступ к этим серверам все это время? "Если ответ «нет», вы все равно должны остановиться в зависимости от этого секрета.
Тем не менее, есть правила о том, как Git сам хранит данные.Предполагая, что ваши серверы GitLab используют / используют только Git (а не какие-то дополнительные элементы, построенные на них, которые могут добавить еще больше способов доступа к данным, которые предоставляют еще больше способов для ваших конфиденциальных / секретных данных длявсе, что вам нужно сделать, это убедить серверы GitLab сделать то же самое, что вы сделали бы в своем собственном Git.
Базовая модель хранения Git состоит в том, что хранилище представляет собой набор того, что Git называет объекты .Каждый объект имеет уникальный хэш-идентификатор и имеет один из четырех типов: blob , tree , commit и аннотированный тег .A blob - это, примерно, данные файла.Если конфиденциальные / секретные данные находятся внутри файла, они фактически находятся внутри объекта BLOB-объекта. дерево объединяется в пару - ну, больше, чем пара , но давайте пока воспользуемся этим 1 - имя каждого файла с его хэшем BLOB-объектаID, поэтому, если файл name является конфиденциальными / секретными данными, ваш секрет на самом деле находится внутри объекта дерева.Объект commit содержит ваше имя, адрес электронной почты, отметку времени, сообщение журнала и хеш-идентификатор некоторого предыдущего или parent commit (s), а также хеш-идентификатор дереваон содержит файлы, которые составляют моментальный снимок, который является , который фиксирует. аннотированный тег объект содержит почти то же самое, что и коммит, за исключением того, что вместо объекта дерева он обычно имеет хэш-идентификатор коммита;именно здесь обычно хранится подпись PGP, помечающая какой-то конкретный коммит как «благословенный» и, скажем, называемый версией 2.3.4 или чем-то другим.
Предполагается, что ваши секреты находятся в одном конкретном файле, само имя которого не является секретнымВаша цель на данный момент состоит в том, чтобы заставить ваш Git прекратить использовать большой двоичный объект, содержащий данные этого конкретного файла.Чтобы сделать это, вы должны заставить сам объект стать без ссылки , а затем использовать git gc
, чтобы Git физически удалил объект без ссылки.На данный момент полезно выделить достижимость в целом, но я перенесу его на Think Like (a) Git .Скажем так: в общем, сразу после того, как вы случайно зафиксировали какой-то секретный файл, способ, которым Git находит объект commit , использует имя ветви:
... <-F <-G <-H <--master
The name master
содержит идентификатор хеша коммита H
.Коммит H
содержит идентификатор хеша его родительского коммита, фиксацию G
, поэтому, чтобы Git нашел коммит G
, он начинает с чтения имени master
(которое создает хэш-идентификатор H
) и затем чтенияобъект commit из базы данных (который создает один объект tree и один parent commit хэш, G
вместе с сообщением журнала, вашим именем, адресом электронной почты и т. д.), выбрасываетвсе, кроме хэша G
, а затем считывает фактический объект фиксации G
из базы данных.Если вы попросили Git получить какой-то конкретный файл - или, точнее, содержимое этого файла - из commit G
, он затем использует дерево G
, чтобы найти идентификатор хэша большого двоичного объекта, содержащего этот файл.файл, затем получает объект blob из базы данных, и теперь Git имеет содержимое.
Итак, suppose ваши секретные данные находятся в большом двоичном объекте, прикрепленном к дереву, прикрепленному для фиксации H
, и эти же данные не находятся в любом другом файле - так что ни одно дерево other не присоединяется ни к одному деревуcommit будет иметь хэш-идентификатор этого BLOB-объекта.Затем, чтобы сделать H
самой ссылкой, просто сделайте имя master
точкой G
вместо H
:
git checkout master
git reset --hard HEAD~1
Теперь у вас есть:
...--E--F--G <-- master
\
H [abandoned]
Но хотя H
не имеет очевидного имени, содержащего его хэш-идентификатор, мы еще не закончили: git gc
не будет - по крайней мере, не пока - уберет H
,и вот тут все начинает усложняться.
Если в H
есть ценные файлы, мы можем отодвинуть H
в сторону, используя git commit --amend
, чтобы сделать новый коммит I
, чьим родителем является G
вместо H
и master
указывают на I
:
... edit files, git add, git commit --amend ...
, давая:
H [abandoned]
/
...--E--F--G--I <-- master
1 Техническикаждая запись дерева имеет:
- запись
mode
, текстовую строку, например 100755
или 100644
.Строка - 40000
, если запись предназначена для поддерева. - строка байтов, содержащая имя файла, обычно в кодировке UTF-8
- идентификатор хеша, который идет сзапись
(Режим и имя разделяются пробелом, а имя заканчивается ASCII NUL, а хэш-код кодируется в 20 двоичных байтах. Это придется изменить, когдаGit переключается на SHA-256. Я не думаю, что новый формат еще не решен, но это может быть так же просто, как, скажем, использовать режим 0n
, где n
- номер версии, как режимнаходится в восьмеричном виде с подавленными ведущими нулями, поэтому ни одно из существующих деревьев не будет иметь 01
в качестве режима. Или, возможно, это может быть байт NUL, за которым следует номер версии, поскольку в настоящее время это тоже недопустимая запись дерева.) Следовательно, для sub-директории, дерево просто перечисляет поддеревья, а для обычных файлов есть два значения плюс хеш.Для символических ссылок идентификатор хеша по-прежнему является идентификатором большого двоичного объекта, но содержимое большого двоичного объекта является target символической ссылки;а для gitlink для подмодулей идентификатор хеша - это идентификатор commit Git следует git checkout
в подмодуле.
Основным осложнением является reflogs
.часть Git, которую делает помнит H
для вас, даже после того, как вы git reset
ее удалили, это то, что Git называет reflogs .Reflog запоминает предыдущие значения ссылки.То есть имя ветви master
может указывать на H
прямо сейчас , прежде чем мы git reset
это.Затем он указывает на G
или I
прямо сейчас , после того как мы используем git reset --hard
или git commit --amend
для отмены коммита H
.Но он имел обыкновение указывать на H
, поэтому хэш-идентификатор H
находится в reflog для имени master
.
Синтаксис @{1}
или @{yesterday}
это то, как вы говорите Git искать эти значения reflog.Запись master@{1}
говорит вашему Git: посмотрите в моем master
reflog и получите сразу же предыдущее значение master
. Тот факт, что эта запись существует, заставит ваш Git сохранить коммит H
что заставит ваш Git сохранить большой двоичный объект, содержащий секрет.
На самом деле существует по крайней мере два reflogs, содержащих хэш-идентификатор commit H
: один для master
, в master@{1}
и один для HEAD
.Поэтому, если вы хотите убедить свой Git действительно отменить коммит H
и, следовательно, отбросить дерево для H
и, следовательно, отбросить любые уникальные для дерева BLOB-объекты для H
, вы должны заставить эти записи reflog исчезнуть.
Обычно они уходят сами по себе, как правило, примерно через 30 дней.Это происходит потому, что у каждой записи reflog также есть отметка времени, и git reflog expire
истечет и удалит старые записи reflog, основанные на этой отметке времени, по сравнению с текущим временем на вашем компьютере.Команда master git gc
запускает для вас git reflog expire
и настраивает ее на истечение срока действия недоступных коммитов 2 через 30 дней по умолчанию.(Достижимые коммиты получают по умолчанию 90 дней.) Таким образом, для вашего собственного Git вам нужно выполнить:
git reflog expire --expire-unreachable=now --all
to tell your Git: Найдите все недостижимые коммиты, такие как H
, и истекайте срок их записей reflog.
2 Технически, это недоступно изтекущее значение ссылки .То есть, Git не собирается тестировать глобальную достижимость здесь, а скорее делает несколько более простой тест: указывает ли эта точка входа reflog на коммит, который является предком коммита, на который ссылается сама ссылка сейчас?
Вторым осложнением является льготное время удаления объекта
Даже после истечения срока действия записей reflog как из HEAD
, так и из имени ветви, вы обнаружите, что ваше собственноеgit gc
не сразу удаляет объект BLOB-объекта.Причина в том, что все объекты Git имеют льготный период, в течение которого git gc
не удалит их.Льготный период по умолчанию составляет 14 дней.Это дает всем командам Git некоторое время, в течение которого они могут создавать объекты, не беспокоясь о них, при условии, что они заканчивают всю свою работу в течение этого 14-дневного периода, связывая все эти объекты вобъект коммита или тега или что-либо еще, и создание соответствующего ссылочного имени (такого как имя ветви или тега) записывает хэш-идентификатор этого объекта.
Чтобы блоб, который вы случайно зафиксировали с помощью H
, пропалзатем вам нужно не только истечь недостижимые записи reflog, но и сказать Git об удалении объектов, даже если им ноль дней:
git prune --expire=now
Этот шаг удаления - эточасть git gc
, которая фактически удаляет объект, поэтому, запустив git prune
, вы избавляетесь от необходимости запускать git gc
.(git gc
также запускает explog expire и т. Д., Но координирует все, чтобы убедиться, что Git имеет эти льготные периоды. Так как мы пропускаем все льготные периоды, мы также просто обходим git gc
.)
Убедитесь, что никакие другие команды Git не выполняются, когда вы делаете это, поскольку они могут создавать объекты, которые, как они ожидают, сохранятся в течение 14 дней, пока они выполняют свою работу.
Последнее осложнение - файлы пакета
Если ваш секрет хранится в том, что Git называет рыхлым объектом, то вышеуказанных шагов достаточно: объект полностью исчезнет, и:
git rev-parse <hash-ID>
больше не найдетобъект вообще.Он больше недоступен в этом репозитории Git.
Но не все объекты свободны.В конце концов, чтобы сэкономить место, Git упаковывает эти незакрепленные объекты в упаковывает файлы .Объекты, хранящиеся в файлах пакета, сжимаются относительно других объектов в том же файле пакета. 3 В этом случае, если ваши секретные данные упакованы, их можно извлечь из файла пакета.
Обычно это происходит не так быстро, поэтому редко можно найти только что совершенный секретный файл в файле пакета.Но если в произошло , единственный способ очистить его - заставить Git переупаковать все существующие файлы пакета.То есть вы должны были бы, чтобы Git взорвал пакеты в их составляющие свободные объекты, затем бросил бы нежелательный объект, затем создал новый (обычно один) файл пакета - или использовал бы процесс, который, по крайней мере, имеет такой эффект.Команда Git для перекомпоновки пакетов - git repack
и имеет много опций.Я не буду вдаваться в подробности, поскольку у меня нет времени.
3 В тонких упаковках объекты могут быть сжатыпо отношению к другим объектам в хранилище, которые не в файле пакета, но тонкие пакеты используются только для операций выборки и отправки, после чего они «откормляются» путем добавления отсутствующих баз обратно.
На серверах часто нет повторных журналов
Чтобы справиться со всем этим, вам необходимо иметь возможность войти на сервер (ы) GitLab, поскольку ни одна из этих команд обслуживания Git (ни BFG, см. ниже) не могут быть вызваны через выборку или pushВ частности, хотя вы можете использовать git push -f
на своем клиенте, чтобы имя master
на сервере больше не указывало на фиксацию H
, вы не можете вызвать git prune
, чтобы освободить свободный объект.
Если и когда вы делаете , войдя на сервер, вы можете проверить, включены ли в вашем репозитории reflogs.Если нет, то нет необходимости делать какие-либо истечения рефлога.Вы также можете увидеть, является ли ваш объект свободным или упакованным, заглянув в каталог .git/objects
.Если ваш хэш-идентификатор BLOB-объекта, скажем, 0123456789...
, он будет находиться в файле с именем .git/objects/01/23456789...
.Как только он не будет ссылаться и будет удален, файл исчезнет, и все будет готово.
Использование очистителя репозитория BFG
Вы можете избежать многих осложнений, используя очиститель репозитория BFG.BFG в любом случае не соблюдает льготный период, поскольку у него другое назначение.Это также решает любые проблемы с файлами пакета.Как и другой метод, он должен быть запущен на сервере и имеет свои особенности (см. Связанный вопрос и ответы).