Checkpoint Lab — Accounts, Two-Factor Authentication, SSH Keys, Access Tokens, Service Accounts, and Credentials
Integrate web, SSH/HTTPS Git, glab/API, token-scope diagnostics, CI job identity, service-identity design, and complete credential cleanup in one free-compatible checkpoint.
Learning objectives
- Build and predict a complete authentication matrix before creating temporary credentials.
- Prove SSH account and repository access with a dedicated key while verifying GitLab's host identity.
- Demonstrate an intentionally under-privileged API request, diagnose it, and correct only the required scope.
- Explain HTTPS credential-helper, CI job-token, and service-identity boundaries without requiring paid features or runner compute.
- Revoke/remove all temporary credentials and independently prove cleanup without recording secret values.
1. Checkpoint mission and safety contract
Build an authentication matrix for one disposable GitLab.com project and prove each channel independently. The checkpoint is complete only when temporary credentials are removed and you can state what each observation proves.
2. Predict the authentication matrix before touching credentials
| Channel | Principal | Credential | Target | Predicted authority |
|---|---|---|---|---|
| Web | Your human GitLab user | Current sign-in method + 2FA/passkey/SSO as configured | Disposable project | Your project role only |
| Git SSH | Your human GitLab user | Dedicated lab SSH key | Repository | Git operations permitted by project/ref policy |
| Git HTTPS | Your human GitLab user | Credential helper + token/OAuth-capable workflow | Repository | Token permission + user role |
| glab | Your human GitLab user | OAuth on GitLab.com | CLI/API resources | OAuth permissions + user authorization |
| REST scope drill | Your human GitLab user | Temporary PAT A then PAT B | /user |
A denied as under-scoped; B succeeds with only user-read permission |
| CI identity | Future running job | CI_JOB_TOKEN |
Supported GitLab resources | Job-lifetime credential; not created manually here |
Prediction 1: registering the public SSH key changes GitLab account metadata but creates no Git commit. Prediction 2: revoking a PAT changes credential validity but does not alter repository history. Verify both.
3. Capture non-secret preflight evidence
git remote -v
git branch --show-current
git rev-parse HEAD
glab auth status --hostname gitlab.com
Record project path, visibility, default branch, your role if visible, current Git remote host/protocol, whether the dedicated lab key exists yet, and token names currently present. Do not export token values. If reusing the Chapter 01 project, confirm it contains only synthetic learning content.
4. Create, register, verify, and exercise the SSH lab key
ssh-keygen -t ed25519 -C "gitlab-ch02-checkpoint" \
-f ~/.ssh/id_ed25519_gitlab_ch02
ssh-keygen -lf ~/.ssh/id_ed25519_gitlab_ch02.pub
-
Register only the
.pubkey with a clearly disposable title. - Compare the first-connect server fingerprint with GitLab's current published GitLab.com host fingerprints.
-
Run
ssh -o IdentitiesOnly=yes -i ~/.ssh/id_ed25519_gitlab_ch02 -T git@gitlab.com. -
Use
GIT_SSH_COMMANDplusgit ls-remoteagainst the disposable project. - Confirm the default-branch SHA matches the hosted project.
Evidence: key fingerprint, GitLab key title/metadata, authenticated username, repository ref SHA. No private key contents.
5. Verify glab identity and context
If glab is not authenticated, use the current GitLab.com OAuth flow:
glab auth login --hostname gitlab.com --web --git-protocol ssh
glab auth status --hostname gitlab.com
glab repo view -R YOUR_NAMESPACE/credential-hygiene-lab -F json
For a headless environment, use the documented device flow instead. Record hostname, account, and repository metadata; never use token-display output as evidence.
6. Required under-privilege drill: fail, diagnose, minimally correct
Create PAT A named
ch02-under-scoped with the shortest practical expiry
and only read_repository. Securely load it without
echoing. Request the authenticated-user endpoint:
read -rsp "PAT A: " GITLAB_TOKEN; echo
STATUS=$(curl --silent --show-error \
--output pat-a-response.json --write-out '%{http_code}' \
--header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"https://gitlab.com/api/v4/user")
unset GITLAB_TOKEN
printf 'HTTP status: %s\n' "$STATUS"
python -m json.tool pat-a-response.json
rm -f pat-a-response.json
Diagnose the result from current GitLab documentation: the
credential is valid for repository-read use, but the
/user API call needs user/API read permission. Do not
“fix” it by adding full api.
Revoke PAT A. Create PAT B named
ch02-read-user with only read_user, repeat
the same request, and verify a successful authenticated profile
response. Then revoke PAT B immediately.
7. HTTPS transport design without putting a token in the URL
Inspect the project's HTTPS clone URL and explain how a Git
credential helper would supply a token as the password when HTTPS is
used. Do not replace your working remote unless you need to test it.
Never use a URL such as
https://token-value@gitlab.com/... in the lab because
URLs leak into history, process lists, logs, and configuration.
On Windows, Git Credential Manager is a common way to keep Git HTTPS credentials out of plain-text remote URLs. On other systems, use the supported credential helper/keychain for the environment. The exact helper is local-tooling policy, not a GitLab server feature.
8. Model CI identity without consuming runner compute
No pipeline is required. Use this synthetic job description to predict identity:
inspect_gitlab:
script:
- 'echo "job identity exists only while this job runs"'
# Do not echo $CI_JOB_TOKEN.
When GitLab actually runs a job, it provides
CI_JOB_TOKEN; the token is valid only during the job
and can authenticate only to supported GitLab resources/endpoints.
Cross-project access can require target allowlisting and sufficient
permissions from the user who triggered the job. This is a workload
identity, not a PAT you manually create or keep after the job.
9. Service identity design exercise
Your deployment service must survive employee turnover and needs only read access to a package/registry. Compare three candidates: a human PAT, service-account PAT, and deploy token. Choose one and state resource reach, protocol, owner, storage, rotation, revocation, and why a broader alternative is unnecessary. No service account or deploy token creation is required.
Then consider a GitLab CI job performing a supported GitLab
API/resource operation. Explain why CI_JOB_TOKEN may be
preferable to all three long-lived choices.
10. Cleanup and independent verification
- Confirm PAT A and PAT B are revoked/inactive in GitLab. No token value should remain in files or shell variables.
- Remove the lab SSH public key from GitLab.
- Retry the dedicated-key SSH test; it should no longer authenticate that identity.
-
Delete only
~/.ssh/id_ed25519_gitlab_ch02and its.pubfile after confirming the exact paths. - Remove any temporary response files. Search the disposable project and lab notes for accidental strings such as access-token prefixes or private-key headers.
- Keep or archive the disposable project for Chapter 03; deletion is optional and must target only the full verified lab path.
unset GITLAB_TOKEN
rm -f pat-a-response.json pat-b-response.json
# Before deleting the key files, print exact paths and verify they are the lab key.
printf '%s\n' ~/.ssh/id_ed25519_gitlab_ch02 ~/.ssh/id_ed25519_gitlab_ch02.pub
11. Verification checklist
- Web sign-in/2FA state is documented without recovery codes or session material.
- The dedicated SSH key fingerprint and target account were verified before removal.
-
Repository authorization was independently checked with
git ls-remoteor equivalent. - glab host/account context was verified without displaying a token.
- PAT A failed the user endpoint because it was intentionally under-scoped; PAT B used only the documented user-read permission and succeeded.
- Both temporary PATs are revoked; the dedicated SSH key is removed; temporary local files/variables are cleared.
-
The CI identity explanation correctly states that
CI_JOB_TOKENis generated for the running job and revoked afterward. - Paid/admin/SSO/service-account features were not required for completion.
12. What Chapter 02 adds to the production operating model
Chapter 01 taught you to name GitLab resources and scopes. Chapter 02 adds the principal and credential dimension: every action now has an authenticated identity, credential type, lifetime, storage boundary, and authorization intersection. This becomes the basis for safe merge governance, CI/CD variables, runner trust, registry authentication, environment deployment, API automation, security policy, and incident response later in the course.
Chapter 03 moves from identity to project configuration: visibility, repository settings, forks, protected resources, and project templates.
Checkpoint questions
PAT A has read_repository and receives an authorization/scope failure from /user. Why is api not the best first fix?
The endpoint needs user/API read permission, and read_user is narrower than full api. Least privilege corrects only the capability required.
After deleting the local SSH private key, the public key remains registered in GitLab. Is cleanup complete?
No. GitLab still trusts that public key if someone possesses a copy of the corresponding private key. Remove the registered public key at the server and verify authentication fails.
A CI job token worked during the job but fails after completion. Is that evidence of a GitLab outage?
No. That is expected lifecycle behavior: CI_JOB_TOKEN is revoked when the job finishes.
A project access token and a personal PAT both have read_api. Why can their reachable projects differ?
Token type defines the resource boundary: a project access token is project-scoped, while a personal token follows the user's accessible resources. Scope and reach are separate constraints.
What evidence is safe to attach to a credential review?
Non-secret metadata such as credential type/name, owner/service identity, scopes/permissions, expiry, revoked state, SSH public-key fingerprint, sanitized HTTP status/body, and policy context—not the secret value.
Chapter 02 summary
You built and verified a multi-channel GitLab authentication model, used a disposable SSH key, protected host identity, authenticated glab without exposing credentials, executed an intentionally under-privileged API request, corrected only the required scope, modeled CI and service identities, and proved cleanup. The production invariant is simple: credential choice follows principal + resource + protocol + minimum permission + lifetime, and revocation is always verified at the issuer.
Official references
- GitLab Docs — Token overview
- GitLab Docs — Access token scopes
- GitLab Docs — Two-factor authentication
- GitLab Docs — 2FA troubleshooting and recovery
- GitLab Docs — Passkeys
- GitLab Docs — SSH keys
- GitLab CLI — Authentication
- GitLab CLI — glab auth status
- GitLab Docs — Personal access tokens
- GitLab Docs — Project access tokens
- GitLab Docs — Group access tokens
- GitLab Docs — Deploy tokens
- GitLab Docs — CI/CD job token
- GitLab Docs — Service accounts
- GitLab Docs — REST API authentication
- GitLab Docs — Fine-grained personal access tokens
- GitLab Docs — Pipeline triggers
- GitLab Docs — SAML SSO for GitLab.com groups
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.