プロジェクトアクセストークン、デプロイキー、デプロイトークンの使い分け
(トップページはこちら) - (プロジェクトで作業を整理する)
CI/CDパイプラインでコンテナイメージをプッシュしたい。外部サーバーからリポジトリをクローンしたい。APIを使ってIssueを自動作成したい。これらの自動化を実現する際、「どの認証方式を使えばいいのか」迷ったことはありませんか。
GitLab.comには3つの認証方式があり、それぞれ異なる目的に最適化されています。本記事では、プロジェクトアクセストークン、デプロイキー、デプロイトークンの特性を理解し、適切に使い分けるための実践的なガイドを提供します。
1. 3つの認証方式の基本
1.1 プロジェクトアクセストークン
プロジェクトアクセストークンは、GitLab APIとHTTP経由のGit操作に対応した、最も汎用性の高い認証方式です。
基本特性:
- 提供形式: GitLabが生成するトークン文字列(作成時のみ表示)
- プロトコル: HTTP/HTTPS
- 利用可能ティア: Premium/Ultimate(トライアルでは1つのみ)
- 有効期限: 必須(デフォルト30日、最大365日、GitLab 17.6以降は400日)
- スコープ: プロジェクト単位
技術的な仕組み:
トークン作成時、GitLabは自動的にボットユーザーを生成します。このボットユーザーは以下の特徴を持ちます。
- ユーザー名:
project_{project_id}_bot_{random_string} - メールアドレス:
project_{project_id}_bot_{random_string}@noreply.gitlab.com - 課金対象外(ライセンス数にカウントされない)
- プロジェクトメンバーとして表示される
利用可能なスコープ:
| スコープ | 用途 |
|---|---|
api |
GitLab API全般(Issue、MR、設定変更など) |
read_api |
GitLab APIの読み取り専用操作 |
read_repository |
Gitリポジトリのクローン、プル |
write_repository |
Gitリポジトリへのプッシュ |
read_registry |
コンテナイメージのプル |
write_registry |
コンテナイメージのプッシュ |
read_package_registry |
パッケージのダウンロード |
write_package_registry |
パッケージの公開 |
create_runner |
ランナーの作成 |
manage_runner |
ランナーの管理 |
ai_features |
GitLab Duo機能の利用 |
k8s_proxy |
Kubernetes Agent経由のAPI呼び出し |
self_rotate |
トークン自身のローテーション(GitLab 17.9以降) |
1.2 デプロイキー
デプロイキーは、SSH経由でGitリポジトリにアクセスするための公開鍵認証方式です。
基本特性:
- 提供形式: ユーザーが生成したSSH公開鍵をGitLabに登録
- プロトコル: SSH
- 利用可能ティア: 全ティア(Free、Premium、Ultimate)
- 有効期限: オプション
- スコープ: プロジェクト単位、または複数プロジェクト共有
2種類のデプロイキー:
権限レベル:
-
読み取り専用:
git clone、git pullのみ -
読み書き:
git clone、git pull、git push
重要な注意点:
デプロイキーは作成者のユーザーアカウントに紐付きます。作成者がプロジェクトから削除されたり、ブロックされたりすると、デプロイキーの動作に影響が出る可能性があります。
1.3 デプロイトークン
デプロイトークンは、個人アカウントに依存せず、HTTP経由で複数のリソースにアクセスできる認証方式です。
基本特性:
- 提供形式: GitLabが生成するユーザー名とトークンのペア(作成時のみ表示)
- プロトコル: HTTP/HTTPS
- 利用可能ティア: 全ティア(Free、Premium、Ultimate)
- 有効期限: オプション(デフォルトなし)
- スコープ: プロジェクト単位、またはグループ単位
デフォルトのユーザー名形式:
gitlab+deploy-token-{n}(カスタマイズ可能)
利用可能なスコープ:
| スコープ | 用途 |
|---|---|
read_repository |
Gitリポジトリのクローン |
read_registry |
コンテナイメージのプル |
write_registry |
コンテナイメージのプッシュ |
read_package_registry |
パッケージのダウンロード |
write_package_registry |
パッケージの公開 |
read_virtual_registry |
依存関係プロキシ経由のイメージプル |
write_virtual_registry |
依存関係プロキシ経由のイメージプッシュ |
特別な機能:gitlab-deploy-token
gitlab-deploy-tokenという名前でデプロイトークンを作成すると、CI/CDジョブで自動的に以下の環境変数として利用可能になります。
CI_DEPLOY_USERCI_DEPLOY_PASSWORD
これにより、CI/CD設定ファイルに認証情報を記述する必要がなくなります。
2. 比較と選択基準
2.1 機能比較表
| 項目 | プロジェクトアクセストークン | デプロイキー | デプロイトークン |
|---|---|---|---|
| プロトコル | HTTP/HTTPS | SSH | HTTP/HTTPS |
| GitLab API | ○ | × | × |
| Git操作 | ○ | ○ | ○ |
| コンテナレジストリ | ○ | × | ○ |
| パッケージレジストリ | ○ | × | ○ |
| 複数プロジェクト共有 | × | ○(公開キーのみ) | ○(グループレベル) |
| ユーザー依存 | ボットユーザー | 作成者に依存 | 非依存 |
| 利用可能ティア | Premium/Ultimate | 全ティア | 全ティア |
| 有効期限 | 必須 | オプション | オプション |
| CI/CD自動変数 | × | × | ○(特定名のみ) |
2.2 選択フローチャート
2.3 ティア別の利用可能性
Freeティア:
- デプロイキー: ○
- デプロイトークン: ○
- プロジェクトアクセストークン: ×
Premium/Ultimateティア:
- デプロイキー: ○
- デプロイトークン: ○
- プロジェクトアクセストークン: ○
トライアルライセンス:
- プロジェクトアクセストークン: 1つのみ作成可能
3. 実践的な使い分け
3.1 判断基準マトリクス
| やりたいこと | 推奨方式 | 理由 |
|---|---|---|
| GitLab APIでIssue/MRを操作 | プロジェクトアクセストークン | API操作に対応しているのはこれのみ |
| CI/CDでコンテナイメージをプッシュ | デプロイトークン(gitlab-deploy-token) | CI/CD変数として自動利用可能 |
| 外部サーバーからSSHでクローン | デプロイキー | SSHプロトコルに対応 |
| Kubernetesからイメージをプル | デプロイトークン | 長期的に安定、ユーザー非依存 |
| 複数プロジェクトに同じ鍵でアクセス | 公開デプロイキー | 一元管理が可能 |
| グループ全体のパッケージにアクセス | グループデプロイトークン | グループスコープで効率的 |
| ランナーの作成・管理 | プロジェクトアクセストークン | 専用スコープあり |
| 読み取り専用のミラーリング | デプロイキー(読み取り専用) | 最小権限の原則 |
3.2 アクセス範囲による選択
4. 具体的なユースケース
4.1 CI/CDパイプラインでのコンテナイメージ管理
シナリオ:
マイクロサービスアーキテクチャで、各サービスのDockerイメージをビルドし、GitLabコンテナレジストリにプッシュします。
要件:
- ソースコードの取得
- Dockerイメージのビルド
- コンテナレジストリへのプッシュ
- CI/CD設定の簡素化
推奨方式: デプロイトークン(gitlab-deploy-token)
実装:
# .gitlab-ci.yml
stages:
- build
- push
variables:
DOCKER_DRIVER: overlay2
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
build_image:
stage: build
image: docker:latest
services:
- docker:dind
before_script:
- echo "$CI_DEPLOY_PASSWORD" | docker login $CI_REGISTRY -u $CI_DEPLOY_USER --password-stdin
script:
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAG
only:
- main
- develop
メリット:
- 認証情報をCI/CD変数に手動設定する必要がない
- チームメンバーの変更に影響されない
- トークンの管理が一元化される
4.2 外部ビルドサーバーからの継続的デプロイ
シナリオ:
社内のビルドサーバー(Jenkins、TeamCityなど)から、複数のGitLabプロジェクトのソースコードを取得してビルドします。
要件:
- SSH経由でのアクセス
- 複数プロジェクトへのアクセス
- 読み取り専用で十分
- セキュリティの確保
推奨方式: 公開デプロイキー(読み取り専用)
実装手順:
# 1. ビルドサーバー上でSSHキーペアを生成
ssh-keygen -t ed25519 -C "ビルドサーバー用デプロイキー" -f ~/.ssh/gitlab_deploy_key
# 2. 公開鍵の内容を確認
cat ~/.ssh/gitlab_deploy_key.pub
# 3. GitLab管理者が公開デプロイキーとして登録
# Admin Area > Deploy Keys > New deploy key
# 4. 各プロジェクトのMaintainerが有効化
# Project Settings > Repository > Deploy keys > Publicly accessible deploy keys > Enable
# 5. ビルドスクリプト
#!/bin/bash
export GIT_SSH_COMMAND="ssh -i ~/.ssh/gitlab_deploy_key -o StrictHostKeyChecking=no"
# プロジェクトAのビルド
git clone git@gitlab.com:組織名/プロジェクトA.git
cd プロジェクトA
make build
cd ..
# プロジェクトBのビルド
git clone git@gitlab.com:組織名/プロジェクトB.git
cd プロジェクトB
make build
メリット:
- 1つのSSHキーで複数プロジェクトにアクセス
- 読み取り専用で最小権限を実現
- プロジェクトごとに個別に有効化/無効化可能
4.3 自動化スクリプトによるプロジェクト管理
シナリオ:
定期的に実行されるスクリプトで、Issueの自動作成、ラベルの整理、マイルストーンの管理を行います。
要件:
- GitLab APIへのアクセス
- Issue/MRの作成・更新
- プロジェクト設定の読み取り
- 定期実行(cron)
推奨方式: プロジェクトアクセストークン(apiスコープ)
実装:
#!/usr/bin/env python3
import requests
from datetime import datetime, timedelta
GITLAB_URL = "https://gitlab.com"
PROJECT_ID = "12345"
TOKEN = "glpat-xxxxxxxxxxxxxxxxxxxx"
headers = {"PRIVATE-TOKEN": TOKEN}
def create_weekly_maintenance_issue():
"""週次メンテナンスIssueを自動作成"""
next_week = datetime.now() + timedelta(days=7)
due_date = next_week.strftime("%Y-%m-%d")
issue_data = {
"title": f"週次メンテナンス {next_week.strftime('%Y年%m月%d日')}",
"description": """
## 実施項目
- [ ] 依存パッケージの更新確認
- [ ] セキュリティスキャン結果の確認
- [ ] ログの確認
- [ ] パフォーマンスメトリクスの確認
""",
"labels": "メンテナンス,定期作業",
"due_date": due_date,
"assignee_ids": [123] # 担当者ID
}
response = requests.post(
f"{GITLAB_URL}/api/v4/projects/{PROJECT_ID}/issues",
headers=headers,
json=issue_data
)
if response.status_code == 201:
issue = response.json()
print(f"Issue作成成功: {issue['web_url']}")
return issue
else:
print(f"エラー: {response.status_code} - {response.text}")
return None
def close_old_issues():
"""30日以上更新されていないIssueを自動クローズ"""
thirty_days_ago = (datetime.now() - timedelta(days=30)).strftime("%Y-%m-%dT%H:%M:%SZ")
# 古いIssueを検索
params = {
"state": "opened",
"updated_before": thirty_days_ago,
"labels": "自動クローズ対象"
}
response = requests.get(
f"{GITLAB_URL}/api/v4/projects/{PROJECT_ID}/issues",
headers=headers,
params=params
)
if response.status_code == 200:
issues = response.json()
for issue in issues:
# Issueをクローズ
close_response = requests.put(
f"{GITLAB_URL}/api/v4/projects/{PROJECT_ID}/issues/{issue['iid']}",
headers=headers,
json={"state_event": "close"}
)
if close_response.status_code == 200:
print(f"Issue #{issue['iid']} をクローズしました")
# クローズコメントを追加
comment_data = {
"body": "30日間更新がなかったため、自動的にクローズしました。"
}
requests.post(
f"{GITLAB_URL}/api/v4/projects/{PROJECT_ID}/issues/{issue['iid']}/notes",
headers=headers,
json=comment_data
)
if __name__ == "__main__":
create_weekly_maintenance_issue()
close_old_issues()
cron設定:
# 毎週月曜日の9時に実行
0 9 * * 1 /usr/local/bin/python3 /path/to/gitlab_automation.py
メリット:
- API操作に必要な権限を細かく制御
- ボットユーザーとして動作するため、人間のアクションと区別可能
- 有効期限があるため、定期的な見直しが促される
4.4 Kubernetesクラスタでのプライベートイメージ利用
シナリオ:
複数のKubernetes Namespaceで、GitLabコンテナレジストリのプライベートイメージを使用します。グループ内の複数プロジェクトのイメージにアクセスする必要があります。
要件:
- プライベートコンテナレジストリからのイメージpull
- 複数Namespaceでの利用
- グループ内の複数プロジェクトのイメージにアクセス
- 長期的に安定した認証
推奨方式: グループレベルのデプロイトークン(read_registryスコープ)
実装手順:
# 1. GitLab UIでグループレベルのデプロイトークンを作成
# Group Settings > Repository > Deploy tokens
# Name: k8s-image-pull
# Scopes: read_registry
# 2. トークン情報を環境変数に設定
DEPLOY_TOKEN_USERNAME="gitlab+deploy-token-123"
DEPLOY_TOKEN="xxxxxxxxxxxxxxxxxxxx"
# 3. 各Namespaceにdocker-registry Secretを作成
for namespace in production staging development; do
kubectl create secret docker-registry gitlab-registry \
--docker-server=registry.gitlab.com \
--docker-username=$DEPLOY_TOKEN_USERNAME \
--docker-password=$DEPLOY_TOKEN \
--namespace=$namespace
done
# 4. ServiceAccountにImagePullSecretを設定(オプション)
kubectl patch serviceaccount default \
-p '{"imagePullSecrets": [{"name": "gitlab-registry"}]}' \
--namespace=production
Deployment例:
apiVersion: apps/v1
kind: Deployment
metadata:
name: ウェブアプリ
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: ウェブアプリ
template:
metadata:
labels:
app: ウェブアプリ
spec:
# 方法1: Deployment内で明示的に指定
imagePullSecrets:
- name: gitlab-registry
containers:
- name: アプリ
image: registry.gitlab.com/組織名/プロジェクトA/ウェブアプリ:v1.2.3
ports:
- containerPort: 8080
- name: サイドカー
image: registry.gitlab.com/組織名/プロジェクトB/監視エージェント:latest
ports:
- containerPort: 9090
メリット:
- グループ内の全プロジェクトのイメージにアクセス可能
- ユーザーアカウントに依存しない
- 有効期限なしで長期運用可能(必要に応じて設定も可能)
- 複数Namespaceで同じSecretを再利用
4.5 パッケージレジストリへの自動公開
シナリオ:
CI/CDパイプラインで、NuGetパッケージをビルドし、GitLabパッケージレジストリに公開します。
要件:
- パッケージのビルド
- パッケージレジストリへの公開
- バージョン管理
- CI/CD統合
推奨方式: デプロイトークン(write_package_registryスコープ)
実装:
# .gitlab-ci.yml
stages:
- build
- publish
variables:
PACKAGE_VERSION: "1.0.$CI_PIPELINE_IID"
NUGET_SOURCE: "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/packages/nuget/index.json"
build_package:
stage: build
image: mcr.microsoft.com/dotnet/sdk:8.0
script:
- dotnet restore
- dotnet build --configuration Release
- dotnet pack --configuration Release -p:PackageVersion=$PACKAGE_VERSION --output ./packages
artifacts:
paths:
- packages/*.nupkg
expire_in: 1 week
publish_package:
stage: publish
image: mcr.microsoft.com/dotnet/sdk:8.0
dependencies:
- build_package
script:
# デプロイトークンを使用してNuGetソースを追加
- dotnet nuget add source $NUGET_SOURCE
--name GitLab
--username $DEPLOY_TOKEN_USER
--password $DEPLOY_TOKEN
--store-password-in-clear-text
# パッケージを公開
- dotnet nuget push "packages/*.nupkg" --source GitLab
only:
- tags
- main
CI/CD変数設定:
DEPLOY_TOKEN_USER: gitlab+deploy-token-456
DEPLOY_TOKEN: xxxxxxxxxxxxxxxxxxxx (Masked)
パッケージの利用側:
# 開発者のローカル環境でパッケージを利用
dotnet nuget add source https://gitlab.com/api/v4/projects/12345/packages/nuget/index.json \
--name GitLab \
--username gitlab+deploy-token-456 \
--password xxxxxxxxxxxxxxxxxxxx
dotnet add package MyCompany.MyPackage --version 1.0.42
メリット:
- パッケージの公開と取得に同じトークンを使用可能
- CI/CD変数として管理しやすい
- グループレベルで作成すれば、複数プロジェクトで共有可能
5. セキュリティベストプラクティス
5.1 共通の原則
5.1.1 最小権限の原則
必要最小限のスコープのみを付与します。
悪い例:
# すべての権限を持つトークン
スコープ: api, read_repository, write_repository, read_registry, write_registry
良い例:
# コンテナイメージのプルのみ
スコープ: read_registry
# パッケージの公開のみ
スコープ: write_package_registry
5.1.2 有効期限の設定
可能な限り有効期限を設定し、定期的にローテーションします。
推奨設定:
- 開発環境: 30日
- ステージング環境: 90日
- 本番環境: 180日(最大365日)
5.1.3 命名規則の統一
トークンの用途が一目でわかる名前を付けます。
推奨命名パターン:
{環境}_{用途}_{作成日}
例:
- prod_container_push_2024-01
- staging_api_automation_2024-01
- dev_package_publish_2024-01
5.2 プロジェクトアクセストークン固有の注意点
5.2.1 内部プロジェクトへのアクセス
内部ユーザーが作成したプロジェクトアクセストークンは、可視性レベルが「Internal」のすべてのプロジェクトにアクセス可能です。
対策:
- プロジェクトの可視性レベルを適切に設定
- 必要に応じてPrivateに変更
- トークンのスコープを厳密に制限
5.2.2 ボットユーザーの管理
ボットユーザーはプロジェクトメンバーとして表示されます。
確認方法:
Project Settings > Members
ボットユーザーは以下の形式で表示されます。
project_12345_bot_a1b2c3d4 (Bot)
5.2.3 有効期限通知の活用
GitLab 17.6以降、以下のタイミングでメール通知が送信されます。
- 60日前
- 30日前
- 7日前
通知先:
- プロジェクトのMaintainer以上のロール
- GitLab 17.7以降、継承されたOwner/Maintainerも対象(設定による)
5.3 デプロイキー固有の注意点
5.3.1 サービスアカウントの使用
個人アカウントではなく、サービスアカウントでデプロイキーを作成します。
理由:
- 個人の退職や異動による影響を回避
- 権限管理の明確化
- 監査ログの追跡が容易
実装手順:
API実行例:
# 1. グループサービスアカウントの作成
curl --request POST \
--header "PRIVATE-TOKEN: <管理者トークン>" \
--data "name=デプロイ用サービスアカウント&username=deploy_service" \
"https://gitlab.com/api/v4/groups/456/service_accounts"
# レスポンス例:
# {
# "id": 789,
# "username": "deploy_service",
# "name": "デプロイ用サービスアカウント"
# }
# 2. サービスアカウント用のパーソナルアクセストークンを作成
curl --request POST \
--header "PRIVATE-TOKEN: <管理者トークン>" \
--data "name=deploy_key_management&scopes[]=api" \
"https://gitlab.com/api/v4/service_accounts/789/personal_access_tokens"
# 3. サービスアカウントをプロジェクトに招待(UI操作)
# 4. サービスアカウントのトークンを使用してデプロイキーを作成
curl --request POST \
--header "PRIVATE-TOKEN: <サービスアカウントのトークン>" \
--header "Content-Type: application/json" \
--data '{
"title": "本番環境デプロイキー",
"key": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILx...",
"can_push": true
}' \
"https://gitlab.com/api/v4/projects/12345/deploy_keys"
5.3.2 保護ブランチへのプッシュ権限
デプロイキーで保護ブランチにプッシュするには、作成者が適切な権限を持っている必要があります。
確認項目:
- 作成者がプロジェクトメンバーである
- 作成者がMaintainer以上のロールを持つ
- デプロイキーに書き込み権限がある
- 保護ブランチ設定で「Allowed to push」が適切に設定されている
保護ブランチ設定例:
Settings > Repository > Protected branches
Branch: main
Allowed to merge: Maintainers
Allowed to push: Maintainers + Deploy keys with write access
5.4 デプロイトークン固有の注意点
5.4.1 有効期限なしトークンの管理
デプロイトークンはデフォルトで有効期限がありません。
推奨事項:
- 本番環境でも可能な限り有効期限を設定
- 有効期限なしの場合、定期的な棚卸しを実施(四半期ごとなど)
- 使用されていないトークンは即座にリボーク
棚卸しチェックリスト:
## デプロイトークン棚卸し(2024年Q1)
### プロジェクトA
- [ ] k8s-image-pull: 使用中(Kubernetes本番環境)
- [ ] ci-package-push: 使用中(CI/CDパイプライン)
- [ ] old-deploy-token: 未使用 → リボーク済み
### プロジェクトB
- [ ] docker-registry: 使用中(外部サーバー)
- [ ] test-token: 未使用 → リボーク済み
5.4.2 CI/CDランナーのセキュリティ
gitlab-deploy-tokenを使用する場合、ランナーのセキュリティ設定が重要です。
避けるべき設定:
# 悪い例: privilegedモードの使用
services:
- docker:dind
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: ""
build:
image: docker:latest
script:
- docker build --privileged -t $CI_REGISTRY_IMAGE . # 危険
推奨設定:
# 良い例: 必要最小限の権限
services:
- docker:dind
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: "/certs"
build:
image: docker:latest
script:
- docker build -t $CI_REGISTRY_IMAGE .
ランナー設定(config.toml):
[[runners]]
name = "secure-runner"
executor = "docker"
[runners.docker]
privileged = false # privilegedモードを無効化
volumes = ["/cache", "/certs/client"]
[runners.cache]
Type = "s3"
Shared = true
5.5 トークン漏洩時の対応手順
監査ログの確認方法:
Project Settings > Audit Events
確認項目:
- トークンを使用したAPI呼び出し
- Gitプッシュ/プル操作
- レジストリへのアクセス
- 設定変更
6. ライフサイクル管理
6.1 作成フェーズ
6.1.1 作成時のチェックリスト
プロジェクトアクセストークン:
- [ ] トークン名: {環境}_{用途}_{作成日}
- [ ] 説明: 用途、使用場所、担当者を記載
- [ ] 有効期限: 30〜365日(用途に応じて)
- [ ] ロール: 必要最小限(Guest/Reporter/Developer/Maintainer/Owner)
- [ ] スコープ: 必要最小限のみ選択
- [ ] 作成後: トークン値を安全に保管(1Password、Vault等)
- [ ] ドキュメント: 社内Wikiやドキュメントに記録
デプロイキー:
- [ ] キー名: {環境}_{用途}_{作成日}
- [ ] SSHキー: ed25519形式で生成
- [ ] 権限: 読み取り専用 or 読み書き
- [ ] 有効期限: 設定する場合は記載
- [ ] 作成者: サービスアカウント推奨
- [ ] 秘密鍵: 安全に保管(使用サーバーのみ)
- [ ] 公開鍵: GitLabに登録
デプロイトークン:
- [ ] トークン名: {環境}_{用途}_{作成日}
- [ ] ユーザー名: カスタマイズする場合は記載
- [ ] 有効期限: 可能な限り設定
- [ ] スコープ: 必要最小限のみ選択
- [ ] レベル: プロジェクト or グループ
- [ ] 作成後: ユーザー名とトークンを安全に保管
- [ ] CI/CD変数: 必要に応じて設定
6.2 運用フェーズ
6.2.1 定期的な棚卸し
月次チェック:
- 有効期限が30日以内のトークンを確認
- 更新が必要なトークンをリストアップ
四半期チェック:
- 全トークンの使用状況を確認
- 未使用トークンをリボーク
- トークン一覧をドキュメント更新
棚卸しスクリプト例:
#!/usr/bin/env python3
import requests
from datetime import datetime, timedelta
GITLAB_URL = "https://gitlab.com"
PROJECT_ID = "12345"
TOKEN = "glpat-xxxxxxxxxxxxxxxxxxxx"
headers = {"PRIVATE-TOKEN": TOKEN}
def check_project_access_tokens():
"""プロジェクトアクセストークンの有効期限をチェック"""
response = requests.get(
f"{GITLAB_URL}/api/v4/projects/{PROJECT_ID}/access_tokens",
headers=headers
)
if response.status_code == 200:
tokens = response.json()
now = datetime.now()
print("=== プロジェクトアクセストークン一覧 ===\n")
for token in tokens:
name = token['name']
expires_at = datetime.fromisoformat(token['expires_at'].replace('Z', '+00:00'))
days_until_expiry = (expires_at - now).days
status = "正常"
if days_until_expiry < 0:
status = "期限切れ"
elif days_until_expiry < 7:
status = "要更新(7日以内)"
elif days_until_expiry < 30:
status = "要注意(30日以内)"
print(f"名前: {name}")
print(f"有効期限: {expires_at.strftime('%Y-%m-%d')}")
print(f"残り日数: {days_until_expiry}日")
print(f"ステータス: {status}")
print(f"スコープ: {', '.join(token['scopes'])}")
print("-" * 50)
if __name__ == "__main__":
check_project_access_tokens()
6.2.2 監視とアラート
監視項目:
- トークンの使用頻度
- 異常なアクセスパターン
- 有効期限切れ間近のトークン
- 失敗したアクセス試行
GitLab監査イベントの活用:
Project Settings > Audit Events
フィルタ:
- Event Type: Token
- Date Range: 過去30日
6.3 更新・ローテーションフェーズ
6.3.1 プロジェクトアクセストークンのローテーション
GitLab 17.9以降、self_rotateスコープを持つトークンは自動ローテーション可能です。
手動ローテーション手順:
API経由のローテーション(GitLab 17.9以降):
# self_rotateスコープを持つトークンの自動ローテーション
curl --request POST \
--header "PRIVATE-TOKEN: glpat-xxxxxxxxxxxxxxxxxxxx" \
"https://gitlab.com/api/v4/personal_access_tokens/self/rotate"
# レスポンス例:
# {
# "id": 123,
# "name": "prod_api_automation_2024-01",
# "revoked": false,
# "created_at": "2024-01-15T10:00:00.000Z",
# "scopes": ["api", "self_rotate"],
# "user_id": 456,
# "last_used_at": "2024-03-10T14:30:00.000Z",
# "active": true,
# "expires_at": "2024-07-15",
# "token": "glpat-yyyyyyyyyyyyyyyyyyyy" # 新しいトークン
# }
6.3.2 デプロイキーのローテーション
手順:
# 1. 新しいSSHキーペアを生成
ssh-keygen -t ed25519 -C "デプロイキー_2024-04" -f ~/.ssh/gitlab_deploy_key_new
# 2. 新しい公開鍵をGitLabに登録(UI操作)
# 3. 使用箇所で新しい秘密鍵をテスト
export GIT_SSH_COMMAND="ssh -i ~/.ssh/gitlab_deploy_key_new"
git clone git@gitlab.com:組織名/プロジェクト名.git
# 4. 正常動作を確認後、すべての使用箇所を更新
# 5. 旧デプロイキーを無効化(UI操作)
# 6. 旧秘密鍵を削除
rm ~/.ssh/gitlab_deploy_key
6.3.3 デプロイトークンのローテーション
手順:
# 1. 新しいデプロイトークンを作成(UI操作)
# 2. CI/CD変数を更新
# Settings > CI/CD > Variables
# DEPLOY_TOKEN_USER: 新しいユーザー名
# DEPLOY_TOKEN: 新しいトークン
# 3. パイプラインを実行して動作確認
# 4. Kubernetes Secretを更新(該当する場合)
kubectl delete secret gitlab-registry --namespace=production
kubectl create secret docker-registry gitlab-registry \
--docker-server=registry.gitlab.com \
--docker-username=新しいユーザー名 \
--docker-password=新しいトークン \
--namespace=production
# 5. 旧デプロイトークンをリボーク(UI操作)
6.4 廃止フェーズ
6.4.1 リボーク手順
プロジェクトアクセストークン:
Project Settings > Access tokens > Active tokens > Revoke
リボーク後、30日間は非アクティブトークンとして保持され、その後自動削除されます。
デプロイキー:
Project Settings > Repository > Deploy keys > Disable
デプロイトークン:
Project Settings > Repository > Deploy tokens > Revoke
6.4.2 リボーク後の確認事項
- [ ] 監査ログで最終使用日時を確認
- [ ] 関連するCI/CD変数を削除
- [ ] Kubernetes Secretを削除(該当する場合)
- [ ] ドキュメントから削除
- [ ] 秘密鍵/トークン値を安全に削除
7. トラブルシューティング
7.1 プロジェクトアクセストークン関連
7.1.1 トークンが作成できない
症状:
「You don't have permission to create project access tokens」というエラーが表示される。
原因と対策:
| 原因 | 対策 |
|---|---|
| Freeティアを使用している | Premium/Ultimateにアップグレード |
| トライアルで既に1つ作成済み | 既存トークンを削除してから作成 |
| グループ設定で制限されている | グループオーナーに設定変更を依頼 |
| Maintainer権限がない | プロジェクトオーナーに権限付与を依頼 |
グループ設定の確認:
Group Settings > General > Permissions and group features
> Users can create project access tokens and group access tokens in this group
7.1.2 APIアクセスが403エラーになる
症状:
curl --header "PRIVATE-TOKEN: glpat-xxxxxxxxxxxxxxxxxxxx" \
"https://gitlab.com/api/v4/projects/12345/issues"
# レスポンス:
# {"message":"403 Forbidden"}
原因と対策:
| 原因 | 対策 |
|---|---|
apiスコープがない |
apiまたはread_apiスコープを持つトークンを作成 |
| トークンが期限切れ | 新しいトークンを作成 |
| プロジェクトIDが間違っている | 正しいプロジェクトIDを確認 |
| ボットユーザーの権限不足 | トークン作成時のロールを上げる |
スコープの確認:
Project Settings > Access tokens > Active tokens
7.1.3 内部プロジェクトに意図せずアクセスできる
症状:
作成したトークンで、他の内部プロジェクトにもアクセスできてしまう。
原因:
内部ユーザーが作成したプロジェクトアクセストークンは、可視性レベルが「Internal」のすべてのプロジェクトにアクセス可能です。
対策:
- プロジェクトの可視性を「Private」に変更
- トークンのスコープを最小限に制限
- 必要に応じてサービスアカウントを使用
7.2 デプロイキー関連
7.2.1 保護ブランチにプッシュできない
症状:
git push origin main
# エラー:
# remote: GitLab: You are not allowed to push code to protected branches on this project.
原因と対策:
| 原因 | 対策 |
|---|---|
| デプロイキーが読み取り専用 | 書き込み権限を付与 |
| 作成者がプロジェクトメンバーでない | サービスアカウントで再作成 |
| 作成者の権限が不足 | Maintainer以上の権限を付与 |
| 保護ブランチ設定で許可されていない | 「Allowed to push」にデプロイキーを追加 |
保護ブランチ設定の確認:
Settings > Repository > Protected branches
Branch: main
Allowed to push: Maintainers + Deploy keys with write access
サービスアカウントを使用した解決策:
# 1. グループサービスアカウントを作成(API)
curl --request POST \
--header "PRIVATE-TOKEN: <管理者トークン>" \
--data "name=デプロイ用SA&username=deploy_sa" \
"https://gitlab.com/api/v4/groups/456/service_accounts"
# 2. サービスアカウント用のPATを作成
# 3. サービスアカウントをプロジェクトにMaintainerとして招待
# 4. サービスアカウントのPATを使用してデプロイキーを作成
curl --request POST \
--header "PRIVATE-TOKEN: <サービスアカウントのPAT>" \
--header "Content-Type: application/json" \
--data '{
"title": "本番デプロイキー",
"key": "ssh-ed25519 AAAA...",
"can_push": true
}' \
"https://gitlab.com/api/v4/projects/12345/deploy_keys"
7.2.2 デプロイキーが突然使えなくなった
症状:
以前は動作していたデプロイキーで、突然アクセスできなくなった。
原因と対策:
| 原因 | 対策 |
|---|---|
| 作成者がブロックされた | サービスアカウントで再作成 |
| 作成者がプロジェクトから削除された | サービスアカウントで再作成 |
| デプロイキーが無効化された | 再度有効化、または新規作成 |
| 有効期限が切れた | 新しいデプロイキーを作成 |
使用できないデプロイキーの特定(Self-Managed限定):
Rails Consoleで以下のスクリプトを実行します。
DeployKeysProject.with_write_access.find_each do |deploy_key_mapping|
project = deploy_key_mapping.project
deploy_key = deploy_key_mapping.deploy_key
user = deploy_key.user
access_checker = Gitlab::DeployKeyAccess.new(deploy_key, container: project)
can_push = access_checker.can_do_action?(:push_code)
can_push_to_default = access_checker.can_push_for_ref?(project.repository.root_ref)
next if access_checker.allowed? && can_push && can_push_to_default
if user.nil? || user.ghost?
username = 'なし'
state = '-'
else
username = user.username
user_state = user.state
end
puts "デプロイキー: #{deploy_key.id}, プロジェクト: #{project.full_path}, " \
"プッシュ可能?: #{can_push ? 'はい' : 'いいえ'}, " \
"デフォルトブランチへのプッシュ可能?: #{can_push_to_default ? 'はい' : 'いいえ'}, " \
"ユーザー: #{username}, ユーザー状態: #{user_state}"
end
7.2.3 公開デプロイキーが有効化できない
症状:
公開デプロイキーを有効化しようとすると、エラーが表示される。
原因と対策:
| 原因 | 対策 |
|---|---|
| Maintainer権限がない | プロジェクトオーナーに依頼 |
| 同じ公開鍵が既に登録されている | 既存のデプロイキーを確認 |
| 公開デプロイキーが存在しない | 管理者に公開デプロイキーの作成を依頼 |
7.3 デプロイトークン関連
7.3.1 コンテナレジストリにログインできない
症状:
echo "$DEPLOY_TOKEN" | docker login -u $DEPLOY_TOKEN_USER --password-stdin registry.gitlab.com
# エラー:
# Error response from daemon: Get https://registry.gitlab.com/v2/: unauthorized
原因と対策:
| 原因 | 対策 |
|---|---|
read_registryスコープがない |
正しいスコープを持つトークンを作成 |
| ユーザー名が間違っている | 正しいユーザー名を確認(gitlab+deploy-token-{n}) |
| トークンが間違っている | トークン値を再確認(作成時のみ表示) |
| トークンがリボークされている | 新しいトークンを作成 |
| プロジェクトがプライベートでない | プロジェクトの可視性を確認 |
正しいユーザー名の確認:
Project Settings > Repository > Deploy tokens > Active Deploy Tokens
7.3.2 CI/CDで環境変数が利用できない
症状:
gitlab-deploy-tokenを作成したが、CI_DEPLOY_USERとCI_DEPLOY_PASSWORDが利用できない。
原因と対策:
| 原因 | 対策 |
|---|---|
トークン名が正確にgitlab-deploy-tokenでない |
正確な名前で再作成 |
| グループレベルで作成している | プロジェクトレベルで作成 |
| GitLabバージョンが古い | GitLab 15.1以降にアップグレード |
正しい作成方法:
Project Settings > Repository > Deploy tokens
Name: gitlab-deploy-token(正確にこの名前)
Username: (空欄またはカスタマイズ)
Scopes: read_registry, write_registry
7.3.3 パッケージレジストリにアクセスできない
症状:
nuget push mypkg.nupkg -Source GitLab
# エラー:
# Response status code does not indicate success: 401 (Unauthorized)
原因と対策:
| 原因 | 対策 |
|---|---|
write_package_registryスコープがない |
正しいスコープを持つトークンを作成 |
| NuGetソースの設定が間違っている | URLとプロジェクトIDを確認 |
| 認証情報が間違っている | ユーザー名とトークンを再確認 |
正しいNuGetソース設定:
# ソースの追加
nuget source Add \
-Name GitLab \
-Source "https://gitlab.com/api/v4/projects/12345/packages/nuget/index.json" \
-UserName gitlab+deploy-token-789 \
-Password xxxxxxxxxxxxxxxxxxxx \
-StorePasswordInClearText
# パッケージのプッシュ
nuget push mypkg.1.0.0.nupkg -Source GitLab
7.4 共通のトラブルシューティング
7.4.1 トークン/キーの値を紛失した
症状:
作成したトークンやデプロイトークンの値を記録し忘れた。
対策:
GitLabでは、セキュリティ上の理由から、作成後にトークン値を再表示することはできません。
解決方法:
- 既存のトークン/キーをリボーク
- 新しいトークン/キーを作成
- 今度は安全に保管(1Password、HashiCorp Vault等)
7.4.2 監査ログでトークンの使用状況を確認したい
手順:
Project Settings > Audit Events
フィルタ:
- Entity Type: Project Access Token / Deploy Key / Deploy Token
- Date Range: 確認したい期間
確認できる情報:
- トークンの作成日時
- 最終使用日時
- 使用したIPアドレス
- 実行された操作
7.4.3 大量のトークンを一括管理したい
推奨ツール:
- 1Password: チーム向けパスワード管理
- HashiCorp Vault: エンタープライズ向けシークレット管理
- AWS Secrets Manager: AWSインフラと統合
- Azure Key Vault: Azureインフラと統合
管理のベストプラクティス:
## トークン管理台帳
### プロジェクトA
| 名前 | 種類 | 用途 | 有効期限 | 保管場所 | 担当者 |
|------|------|------|---------|---------|--------|
| prod_api_2024-01 | PAT | API自動化 | 2024-07-01 | 1Password | 山田太郎 |
| k8s-deploy-key | Deploy Key | K8s デプロイ | なし | Vault | 佐藤花子 |
| gitlab-deploy-token | Deploy Token | CI/CD | なし | CI/CD変数 | 鈴木一郎 |
8. まとめ
8.1 選択の決定木
8.2 重要なポイント
1. 最小権限の原則
必要最小限のスコープと権限のみを付与します。
2. 有効期限の設定
可能な限り有効期限を設定し、定期的にローテーションします。
3. サービスアカウントの活用
デプロイキーは個人アカウントではなく、サービスアカウントで作成します。
4. 定期的な棚卸し
四半期ごとに全トークンの使用状況を確認し、未使用のものはリボークします。
5. 安全な保管
トークン値は1PasswordやVaultなどのシークレット管理ツールで保管します。
6. ドキュメント化
作成したトークンの用途、使用場所、担当者を必ず記録します。
8.3 クイックリファレンス
| やりたいこと | 推奨方式 | スコープ/権限 |
|---|---|---|
| GitLab APIでIssue作成 | プロジェクトアクセストークン | api |
| CI/CDでイメージプッシュ | デプロイトークン(gitlab-deploy-token) |
read_registry, write_registry
|
| 外部サーバーからクローン(SSH) | デプロイキー | 読み取り専用 |
| 外部サーバーからクローン(HTTP) | デプロイトークン | read_repository |
| Kubernetesからイメージプル | グループデプロイトークン | read_registry |
| パッケージの公開 | デプロイトークン | write_package_registry |
| 複数プロジェクトへのSSHアクセス | 公開デプロイキー | 読み取り専用/読み書き |
| ランナーの管理 | プロジェクトアクセストークン | manage_runner |
適切な認証方式を選択し、セキュリティベストプラクティスに従うことで、安全で効率的な自動化を実現できます。