Available since 1.14.0 (EE), with the early access flag fe_security_scan_policies
Preliminary version. The functionality may change, and an upgrade may require manual migration actions.
Secret detection in Deckhouse Code looks for credentials committed to a project’s own source code. The scanner
behind it is Gitleaks, and the scan is called gitleaks_secret_detection.
The scan is mandated by a scan execution policy in a separate security policy project; the project’s own
.gitlab-ci.yml does not add it. The policy decides what fails the pipeline, and the scanned project cannot
override this from its own CI/CD settings.
What the scan does
The gitleaks_secret_detection action adds two jobs to the policy’s stage, fe-security-scanner unless the
policy’s “CI stage” field names another. Each job is named with a trailing :<policy slug>-p<policy project id>,
so a group policy and a project policy mandating the same scan get jobs of their own.
| Job | What it does |
|---|---|
gitleaks_secret_scan | Runs Gitleaks over the repository and publishes its JSON output as an artifact |
gitleaks_secret_junit | Builds a JUnit report and a security report from that JSON, and fails the job when a finding fails the gate |
Both jobs upload artifacts with when: always, so a failed pipeline still carries the findings that failed it.
gitleaks_secret_junit also keeps the findings in SARIF, security-reports/sarif/gitleaks.sarif, as a plain
artifact for tools that read that format.
The scan reads the working copy of the commit being built. A commit that removes a secret clears its finding, but
the secret stays in the repository history until the history is rewritten. The policy editor offers no other
choice. A policy written by hand can set FE_SCANS_GITLEAKS_SCOPE: history among the action’s variables to
read the whole history, and FE_SCANS_GITLEAKS_LOG_OPTS to narrow it to a range of commits in git log terms.
A file kept in Git LFS is scanned by its real content: the pointer that stands for it in the repository is not read. A submodule is not scanned: only what is checked out in the project itself is read.
The scanning job’s log states the Gitleaks version and, once the scan finishes, how many findings it made,
listed by rule. A run that finds nothing still publishes a report, [].
Prerequisites
Before the scan can run, make sure that:
- an administrator has turned on the
fe_security_scan_policiesfeature flag as described on the Early access page; - a security policy project is linked to the project or to a group above it, or the policy editor creates and links one when you save the first policy by the steps in Turning the scan on;
- a runner that runs jobs in container images is available and can pull both images the scan uses:
- the scanner image, chosen as Where the scanner image comes from describes; for an image in a private registry, the policy names the CI/CD variables that hold the login and the password in its registry variable fields;
- the image of
gitleaks_secret_junit,ruby:3.3.10-slimby default, redirected with the server environment variableFE_SCANS_REPORT_CONVERTER_IMAGE; the policy gives this job no registry credentials, so the runner pulls the image without them;
- a project that holds the configuration file or the ignore file named in the policy lets the scanned project’s jobs read it: in that project, “Settings” → “CI/CD” → “Job token permissions” lists the scanned project or its group.
Turning the scan on
To turn on the scan in the policy editor:
- In the project or group, select “Secure” → “Policies”.
- Click “New policy”.
- On “Select a policy type”, click “Scan execution policy”.
- Name the policy.
- Under “Security scans”, expand “Secret Detection (embedded Gitleaks)” and tick “Enable this scanner”.
- Set up the section’s groups as described in What the policy sets.
- Click “Configure with a merge request”.
This opens a merge request in the security policy project. If no security policy project is linked yet, the editor creates and links one first. The policy applies once that merge request is merged.
While the policy cannot be saved, “Configure with a merge request” stays unavailable and the editor lists the reasons under it, after “Saving is unavailable:”. The list is shown in “.yaml mode” too.
Under “Configure with a merge request” the editor names the policy project the merge request goes to, or the path of the project that saving creates. When saving cannot create it, a warning says why instead:
- Only an Owner can create or link a policy project.
- A project with that path or name already exists; the warning links to the page where it can be linked.
- The administrator restricted the visibility level the project would get (the visibility of the group or project the policy belongs to).
- You have reached your personal projects limit.
- You cannot create projects in the group.
- The generated name or path is not valid; the warning adds the validation error.
- A policy project was linked after the page opened; reload the page to see it.
The button stays available while the warning is shown; the server refuses the save for the same reason.
Alternatively, add the action directly to .deckhouse_code/security-policies/policy.yml in the security
policy project:
scan_execution_policy:
- name: Secret detection everywhere
enabled: true
rules:
- type: pipeline
branches: ["*"]
actions:
- scan: gitleaks_secret_detectionA policy can have only one action per scanner: a policy with a second scan: gitleaks_secret_detection action cannot
be saved. Put a second set of settings in a policy of its own.
What the policy sets
In the policy editor, gitleaks_secret_detection is the “Secret Detection (embedded Gitleaks)” section under
“Security scans”. The section has one checkbox, “Enable this scanner”; once it is ticked, the section shows all
its groups: the scanner image, whether a finding fails the pipeline, the configuration file, the ignore file and
additional arguments. Below the groups is the runner tags field for the scan’s two jobs, with the placeholder
“Runner tags, comma-separated”. A folded section shows “Scanner enabled” or “Scanner disabled” beside its
heading.
Scanner image
| Field | What it sets |
|---|---|
| “Scanner image” | The image reference the scan runs in: a path and a tag, a digest, or both |
| “Registry login variable” | The name of a CI/CD variable of the project or its group that holds the login for the image’s registry, as $NAME or ${NAME}. Empty by default |
| “Registry password variable” | The name of the CI/CD variable that holds the password or token for that registry, in the same form. Empty by default |
Left empty, “Scanner image” means the image Deckhouse Code is configured for, shown greyed out as the field’s
own placeholder; steps 2 and 3 of Where the scanner image comes from name
that image. The registry
host may be left out for an image on Docker Hub, as in zricethezav/gitleaks:v8.30.1. A reference that is not
whole, such as zricethezav/gitleaks with no tag or digest, does not stop the policy from being saved: the scan job stops
with this scan was not given what the policy asks for: scanner image: ….
The two variable fields are for an image in a private registry. The policy names the variables, never their
values. In the policy file they are FE_SCANS_REGISTRY_LOGIN_VARIABLE and FE_SCANS_REGISTRY_PASSWORD_VARIABLE
among the action’s variables. The policy is not saved when:
- only one of the two fields is filled;
- a value is not written as
$NAMEor${NAME}; - a name starts with
FE_,CI_orGITLAB_.
To give the scan access to a private registry:
- In the scanned project or its group, create two CI/CD variables of the type “Variable (default)”: one with the login, one with the password or token. Mask the password variable.
- Enter their names in “Registry login variable” and “Registry password variable”.
The variables are read from the scanned side: a group policy relies on a group variable, and a project variable
with the same name takes its place and makes the pull fail if its value is wrong. The credentials reach
gitleaks_secret_scan only, and are not used for the image shipped with the product.
Fail on findings
A checkbox, ticked by default.
| State | What it does |
|---|---|
| Ticked (default) | A finding fails the pipeline |
| Unticked | A finding is still reported and still reaches the vulnerability report; the pipeline is not stopped by it |
In the policy file the checkbox is FE_SECURITY_FAIL_ON: info when ticked, report_only when not.
Every finding is reported at severity CRITICAL, so there is no threshold to set between levels as in
SAST scanning with Opengrep: a finding either fails the pipeline or it does not.
Configuration file
| Field | What it sets |
|---|---|
| “File path” | Path to the scanner’s own TOML configuration, read by the scan job from the project named beside it. Empty by default; the scan then runs on the configuration built into the scanner image alone |
| “Project” | Repository the file is read from, as group/project, at its default branch. Needed once “File path” is filled |
The file may carry an [extend] section, allowlists of single rules, and rules it switches off. The set built
into the scanner image stays in force unless the file says otherwise; a file with no [extend] section is read
as extending it. An [extend] section is accepted only with useDefault = true and, beside it,
disabledRules. The job stops before scanning when the file carries anything else in [extend], a second
[extend], a top-level allowlist, or disabledRules outside [extend].
The scan job reads the file with its own job token, so the project named in “Project” must give the scanned project the access described in Prerequisites.
The policy is saved with “Project” empty or naming the scanned project itself, and the scan job then stops
before scanning. The scan never reads the scanned project’s own .gitleaks.toml, which stays in the checkout.
Ignore file
| Field | What it sets |
|---|---|
| “File path” | Path to a file of finding fingerprints, one per line, in the form the scanner prints them. Empty by default; no finding is suppressed |
| “Project” | Repository the file is read from, the same rule as “Configuration file” → “Project” |
The scanned project’s own .gitleaksignore is removed from the checkout before the scan runs, so the file
this field names is the only place a finding is suppressed. A gitleaks:allow comment on the line of a secret
is not honored either: the scan passes --ignore-gitleaks-allow, so a comment committed beside a secret does
not silence its own finding.
Additional arguments
| Field | What it sets |
|---|---|
| “Additional arguments” | Flags passed to the scan as written, for anything this form does not offer |
A note beside the field says that what the scan reports is published as a security report, so it reaches the vulnerability report and whatever the administrator has connected to it.
Flags the job sets itself are refused, with the flag named: those that move or reshape the report, replace the
configuration, suppress findings (--baseline-path, --ignore-gitleaks-allow, --enable-rule), set the exit
code, redaction, banner, colors or diagnostics, name the commits to read (--log-opts), switch to another
mode (--pre-commit, --staged), set the platform used for finding links (--platform), or print help instead
of scanning. The log levels warn, error, fatal, panic and disabled stop the job, which reads the
scanner’s own log back.
Where the scanner image comes from
The server resolves the image once, before the job starts, and writes it in as a literal value; the scanned project’s own CI/CD settings cannot redirect it. Resolution order:
- The policy’s own “Scanner image” field, if it is a whole reference: a path and a tag, a digest, or both.
- Otherwise, the image set in the
FE_SCANS_GITLEAKS_IMAGEenvironment variable of the Deckhouse Code server. - Otherwise, the image this release pins,
zricethezav/gitleaks:v8.30.1.
FE_SCANS_GITLEAKS_IMAGE is an environment variable of the server processes that render pipelines, the web
server and Sidekiq; an administrator sets it in their environment. The same name among the CI/CD variables of a
project or a group has no effect. In the policy file the “Scanner image” field is the FE_SCANS_GITLEAKS_IMAGE
key of the action’s variables.
A filled field that is not a whole reference does not fall back to steps 2–3 for the scan: the job stops with
this scan was not given what the policy asks for: scanner image: … before scanning. The job is still started in
the image of step 2 or 3, so that image must be pullable.
An installation with no route to the internet mirrors this image into its own registry and names the mirrored
reference in the server environment variable FE_SCANS_GITLEAKS_IMAGE or in the policy’s “Scanner image” field.
If that registry asks for a login, fill the registry variable fields of the policy.
Where results appear
| Report | Where to view it |
|---|---|
| Tests (JUnit) | “Tests” tab of the pipeline, and the test report widget of the merge request, titled “Security scan summary” or “Test and security scan summary” |
| Security report (secret detection) | “Vulnerability report” page |
| Findings forwarded to DefectDojo | DefectDojo, when the DefectDojo integration is configured |
Each test case is named CRITICAL <rule> <description> [<file>:<line>]. The description is the one Gitleaks gives
the rule; a rule without one is named by its identifier once. A repeated name gets a trailing #2, #3 and so
on. The finding’s description ends with Gitleaks rule: <rule> on its own line.
A passing test case named gitleaks scan states what was scanned (the working copy of the commit, or the
history), how many fingerprints the policy’s ignore file holds, and which additional arguments were passed, if
any.
Job names carry the policy’s own slug and its policy project’s id; DefectDojo reconciles tests by that whole name. Renaming the policy, or moving it to another policy project, starts a new test in DefectDojo.
Troubleshooting
| The job says | What it means |
|---|---|
gitleaks is missing from this scan job image | The resolved image carries no gitleaks on its PATH. Check Where the scanner image comes from |
this scan was not given what the policy asks for: <detail> | The server refused part of the policy before the job started. The detail names the field: scanner image (not a whole reference), blocking (a value other than info or report_only), what the scan reads (a scope other than working_copy or history), additional arguments (a flag the job sets itself), configuration file or ignore file (a path with no project, a project with no path, or a path or project written wrongly). Several are joined with semicolons |
this policy names a configuration file in the project being scanned, which this scan never reads | “Configuration file” → “Project” names the scanned project. Name the project the file is kept in |
this policy names an ignore file in the project being scanned, which is the file this scan removes from the checkout | “Ignore file” → “Project” names the scanned project. Name the project the file is kept in |
<project> does not let this project's jobs read it | The project holding the file does not list the scanned project under “Settings” → “CI/CD” → “Job token permissions” |
<path> is not on the default branch of <project>, or that project does not exist | The path or the project is wrong, or the job token permissions of that project do not let the scanned project in |
<path> in <project> is empty, and this policy names it | The named file has no content. Fill the file or clear “File path” |
this scanner's image carries no curl, python3 or wget, so a file in another project cannot be read | A custom scanner image lacks the tools that fetch the configuration or ignore file |
the configuration this policy names would switch off the set this scan is for: … | The file named in “Configuration file” carries a construct that would replace or empty the shipped rule set, or that the scanner would not read: a second [extend] section, a top-level allowlist section or key, an extend key written with a dot, an [extend] without useDefault = true or with a key other than useDefault and disabledRules, or disabledRules outside [extend] |
the policy names rules this scanner does not have: … | A disabledRules entry in the configuration file names a rule Gitleaks does not carry |
this scan was asked to log at <level>, which is quieter than this job can read back | “Additional arguments” sets the log level warn, error, fatal, panic or disabled |
gitleaks exited with status <n>: the scan did not complete | The scanner itself failed; the job’s log carries its error lines |
gitleaks exited with status 0 but wrote no report: the scan did not run | The scanner stopped without scanning and without an error status. Read the scanner’s lines in the job’s log |
ruby is missing from this converter job image | The resolved converter image carries no ruby on its PATH |
The scan did not run: it left no report behind (in the gitleaks scan test case) | gitleaks_secret_scan failed or was cancelled; gitleaks_secret_junit publishes no findings from a partial report and fails the job |