Tokens, SSH keys, security settings
Intermediate GitLab UI
Why this matters
Section 8 covered what tokens and keys are. This lesson is the map: which of GitLab's six credential types to reach for, and the exact page each one lives on. Getting this right is what separates "I made a token" from "I made a token that can only read one project and expires in ninety days".
The choice, in one table
Pick the least powerful thing that works. It is the whole skill.
| You want to | Use | Created at |
|---|---|---|
| Push and pull over HTTPS as yourself | Personal access token, write_repository |
Avatar → Edit profile → Access → Personal access tokens |
| Push and pull over SSH as yourself | SSH key | Avatar → Edit profile → SSH Keys |
| Let a script or tool act on one project | Project access token | Project → Settings → Access tokens |
| Let a script act across a group's projects | Group access token | Group → Settings → Access tokens |
| Let a deploy system read the repository or registry | Deploy token, or a read-only deploy key | Project → Settings → Repository |
| Give a pipeline job a secret | CI/CD variable, masked and protected | Project → Settings → CI/CD → Variables |
Two rules follow from the table. Never use your personal token for automation: it carries all of your access, it dies when you leave, and its actions look like yours in the audit log. And never put a credential in the repository: that is what CI/CD variables exist for (lesson 8.6).
Personal credentials
Personal access tokens are yours and carry your permissions, limited by the scopes you tick. For Git over HTTPS, write_repository is enough; api is only needed by tools that read issues and merge requests, such as the VS Code and IntelliJ integrations. Set an expiry you can live with; GitLab emails you before a token expires, and that email is the only warning you get.
SSH keys live on the same profile menu, one public key per machine, optionally with an expiry (lesson 8.2).
The token value is shown once, at creation. If you did not copy it, revoke it and make another; there is no way to see it again.
Project and group access tokens
These belong to the project or the group rather than to a person, which is what makes them right for automation. Creating one gives you two choices beyond the scopes:
- A role. The token cannot do more than that role could, so a token for reading issues should be a Guest or Reporter, not a Maintainer (lesson 9.3).
- An expiry, which GitLab requires you to think about and which is the main defence against a forgotten credential.
Behind the scenes each one creates a bot user, which is why its activity appears in the project as a distinct name rather than as yours. Creating them needs the Maintainer or Owner role.
Deploy tokens and deploy keys
Both give a machine access to one project without a person's account.
| Deploy token | Deploy key | |
|---|---|---|
| Is | a username and password pair | an SSH public key |
| Can | read or write the repository, and read the registry | read, or read and write, the repository |
| Cannot | use the API | use the API or the registry |
| Good for | a server that pulls the code or an image | a build machine you already manage by SSH |
If you are choosing for a documentation build server that only needs to clone, a read-only deploy token or key is the correct answer and a personal token is not.
Secrets a pipeline needs
Settings → CI/CD → Variables holds values jobs read as environment variables. Three settings on each one matter:
| Setting | Effect |
|---|---|
| Masked | The value is replaced with [MASKED] in job logs |
| Protected | Only jobs on protected branches and tags can read it |
| Expanded / raw | Whether $OTHER inside the value is substituted |
Mask anything secret, and protect anything that can change the world: a deployment key, a publishing token. An unprotected variable is readable by any job on any branch, which means anyone who can push a branch can print it.
Reviewing what you have
Once every few months, or on the day someone leaves:
- Edit profile → Access → Personal access tokens: revoke what you no longer recognize, and note what expires soon.
- Edit profile → SSH Keys: remove keys for machines you no longer have.
- Edit profile → Authentication log: check where your account has been used from.
- Project Settings → Access tokens and Settings → Repository: revoke tokens and keys for retired systems.
- Project Settings → CI/CD → Variables: confirm the sensitive ones are masked and protected.
- Group or project Manage → Members: remove people who have left (lesson 9.3).
Revoking is instantaneous and reversible only by making a new credential, which is the right trade: a token you are unsure about should be revoked, not kept.
How to do it
Nothing here is a Git command, but two checks tell you which credential your clone is actually using:
$ git remote -vorigin git@gitlab.com:northwind-trails/trailguide.git (fetch)
origin git@gitlab.com:northwind-trails/trailguide.git (push)A git@ address uses your SSH key; an https:// address uses a token from the credential manager (lesson 8.4). To check which account your SSH key authenticates as:
$ ssh -T git@gitlab.comThe GitLab Workflow extension stores its token in the operating system keychain, and GitLab: Authenticate replaces it when it expires. Git credentials for HTTPS are handled by the credential manager, not by VS Code.
Settings → Version Control → GitLab holds the account and its token; Settings → Tools → Server Certificates and the keychain hold the rest. Expired tokens surface as a sign-in prompt in the Merge Requests window.
- Avatar → Edit profile → Access → Personal access tokens to create, review or revoke your own.
- Avatar → Edit profile → SSH Keys for keys, one per machine.
- Project Settings → Access tokens for a token scoped to that project, with a role and an expiry.
- Project Settings → Repository for deploy tokens and deploy keys.
- Project Settings → CI/CD → Variables for pipeline secrets, masked and protected.
- Avatar → Edit profile → Account for two-factor authentication (lesson 8.5).
The same six ideas with different names: fine-grained or classic personal access tokens, SSH keys, deploy keys, Actions secrets instead of CI/CD variables, and GitHub Apps in place of project access tokens. See lesson 10.12.
Common mistakes
- A personal token used by a server. It leaves with you and it carries all your access.
apiscope whenwrite_repositorywould do. Scope is the blast radius.- No expiry. A credential nobody remembers is a credential nobody revokes.
- An unmasked CI/CD variable, printed into a job log the first time someone debugs.
- An unprotected variable holding a deployment secret, readable from any branch anyone can push.
- Keeping tokens "in case". Revoke; making a new one takes a minute.
Try it yourself
Goal: audit your own credentials and replace one with something weaker.
- Open Edit profile → Access → Personal access tokens and list what you have, with scopes and expiry dates.
- Revoke anything you cannot explain in one sentence.
- On your practice project, open Settings → CI/CD → Variables and add a variable called
DEMO_SECRET, masked and protected. - Add a job to
.gitlab-ci.ymlthat runsecho "$DEMO_SECRET", push it on a branch, and read the job log. - Explain the result: the value is masked, and on an unprotected branch it is empty.
Expected result: the log shows [MASKED] rather than the value, and, because the variable is protected and your branch is not, the job prints nothing at all.
Show solution
Step 5 is the lesson. Protected is not about hiding the value in the log, which is what masked does; it is about which branches can read it at all. Together they mean a secret used for deployment cannot be exposed by anyone who can push a branch, which is the attack that catches teams out.