【AWS＋GitLab＋閉域】パブリックアクセスを許可しないGitLabをEC2上に構築

## はじめに

GitLab を AWS EC2 上に構築する際、多くの例ではインターネット経由でのアクセスを前提とした構成が紹介されています。しかし、企業のセキュリティポリシーや特定のシステム要件においては「パブリックアクセスを完全に遮断した GitLab 環境」が求められるケースもあります。

本記事では、**パブリックアクセスを一切許可せず、AWS SSM のポートフォワーディング機能のみでアクセス可能な GitLab 環境**を EC2上に構築する手順を紹介します。

エンタープライズ利用を想定して、EC2はRHEL9で構築します。

## 構成概要

- EC2：RHEL 9 系、パブリック IP 無し  
- VPC：プライベートサブネットのみ  
- アクセス方法：AWS SSM セッションマネージャー + ポートフォワーディング  
- GitLab：gitlab-ce-16.10.3-ce  

下記に構成の概要と接続のイメージを載せます。
![20250415.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555997/ce643ba0-5334-41d3-8331-0774fbd81e98.jpeg)



## 1. EC2 環境と前提条件

- EC2 に SSM Agent がインストール済みであること  
- インスタンスに適切な IAM ロール（`AmazonSSMManagedInstanceCore`）が付与されていること  
- SSM 用のエンドポイントが構成されている（VPC エンドポイントまたは NAT 越し）
- マシンのメモリは8GB以上が望ましい（筆者の場合はt3.largeを使用・小さい場合はGitLabの初期構築途中にハングアップする可能性があります）

セキュリティグループでは、インバウンドルールをゼロに設定しても動作可能です。

VPCエンドポイントを経由し、閉域でSSMと疎通するには下記のVPCエンドポイントを作成しておく必要があります。STSはEC2がクレデンシャルを取得するために必要です。
```bash
com.amazonaws.${AWS::Region}.ssm
com.amazonaws.${AWS::Region}.ssmmessages
com.amazonaws.${AWS::Region}.ec2
com.amazonaws.${AWS::Region}.ec2messages
com.amazonaws.${AWS::Region}.sts
```


## 2. GitLab のインストール

```bash
sudo dnf update
sudo dnf install -y curl openssh-server ca-certificates tzdata perl
curl -LO https://packages.gitlab.com/gitlab/gitlab-ce/packages/el/9/gitlab-ce-16.10.3-ce.0.el9.x86_64.rpm/download.rpm
sudo dnf install -y ./download.rpm
```

上記はcurlを使って手動でパッケージをダウンロードしています。構成スクリプトを利用する方法もありますが、OSバージョンによっては起動に失敗します。

インストールが完了すると、下記のような画面になります。

![20250415_4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555997/c6d0a61d-83f5-4946-a2c4-3c79f4dec4fd.jpeg)




この時点ではまだ外部からのアクセス手段はありません。


## 3. GitLabの初期構成
ポートフォワードして接続するため、external_urlをlocalhostに変更します。

今回は下記のようにポートフォワードしたいため、次のような設定を行います。
ローカル端末 8080ポート　→ GitLab(nginx) 80ポート

```bash
# /etc/gitlab/gitlab.rb
external_url 'http://localhost:8080'
nginx['listen_port'] = 80
```

設定を行ったらGitLabの初期構成を実施しましょう。

```bash
sudo gitlab-ctl reconfigure
```

初期構成後にGitLabのrootパスワードが生成されるので、控えておきましょう。（パスワードファイルは24時間後に削除されます）

```bash
sudo cat /etc/gitlab/initial_root_password
```


## 4. ポートフォワーディングの構成（クライアント端末側）

ここからはクライアント端末側の設定です。

まずはAWS CLIとSSMセッションマネージャのプラグインをインストールしましょう。AWS Systems Manager Session Manager Plugin の公式ドキュメントは [こちら](https://docs.aws.amazon.com/systems-manager/latest/userguide/install-plugin-windows.html) を参照してください。（Windows端末向け）

SSMセッションマネージャへのアクセスは複数通りありますが、下記のようにセッションキーを取得して、AWS CLIから呼び出せるようにしておく方式が安全だと思います。

```bash
aws sts assume-role --role-arn arn:aws:iam::123456789012:role/sts:AssumeRoleを許可したロール --role-session-name AWSCLI
```
取得したセッションキーでAWS CLIのcredentialsを構成しておきましょう。
```
aws_access_key_id = （省略）
aws_secret_access_key = （省略）
aws_session_token =（省略）
```

SSMセッションマネージャでポートフォワーディングを開始するには、Windows 環境から PowerShell 経由で以下のように実行します。

```powershell
aws ssm start-session `
  --target i-xxxxxxxxxxxxxxxxx（EC2インスタンス） `
  --document-name AWS-StartPortForwardingSession `
  --parameters '{\"portNumber\":[\"80\"],\"localPortNumber\":[\"8080\"]}' `
  --region ap-northeast-1
```

> ⚠ 注意：10080 や 666 など一部ポートはブラウザでブロックされるため、8080 などのセーフなポート番号を選びましょう。

ローカルブラウザで `http://localhost:8080` にアクセスすると GitLab の画面が表示されます。
![20250415_3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555997/66b21fff-fe31-40f9-92ad-89a144420142.jpeg)


## 5. トラブルシューティング
下記のようにGitLabが起動待ちの画面が続く場合は、以下2点を確認しましょう。

①GitLabのステータス
②ステータスが正常でないプロセスのログ

![20250415_5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555997/7ba415f8-bc65-49b4-8945-6c871616db2e.jpeg)

①GitLabのステータス
下記の場合は一見正常に見えますが、pumaプロセスの経過時間が37sと短く、再起動を繰り返しているものと推定されます。

```bash
sudo gitlab-ctl status

run: alertmanager: (pid 54154) 6549s; run: log: (pid 53938) 6608s
run: gitaly: (pid 54112) 6553s; run: log: (pid 53247) 6740s
run: gitlab-exporter: (pid 54129) 6552s; run: log: (pid 53825) 6626s
run: gitlab-kas: (pid 53504) 6726s; run: log: (pid 53521) 6723s
run: gitlab-workhorse: (pid 54096) 6554s; run: log: (pid 53698) 6644s
run: logrotate: (pid 57057) 3155s; run: log: (pid 53144) 6754s
run: nginx: (pid 54104) 6553s; run: log: (pid 53729) 6638s
run: node-exporter: (pid 54121) 6553s; run: log: (pid 53791) 6632s
run: postgres-exporter: (pid 54162) 6549s; run: log: (pid 53975) 6600s
run: postgresql: (pid 53313) 6732s; run: log: (pid 53375) 6729s
run: prometheus: (pid 54139) 6551s; run: log: (pid 53898) 6614s
run: puma: (pid 59705) 37s; run: log: (pid 53615) 6656s
run: redis: (pid 53182) 6749s; run: log: (pid 53210) 6746s
run: redis-exporter: (pid 54131) 6551s; run: log: (pid 53860) 6620s
run: sidekiq: (pid 53631) 6653s; run: log: (pid 53641) 6649s
```

②ステータスが正常でないプロセスのログ
上記pumaプロセスが再起動を繰り返していた時に、pumaのログに出力されていたエラーは下記の通りです。

```bash
# /var/log/gitlab/puma/current
2025-xx-xx_xx:xx:xx.xxxxx /opt/gitlab/embedded/lib/ruby/gems/3.1.0/gems/puma-6.4.0/lib/puma/binder.rb:334:in `initialize': Address already in use - bind(2) for "127.0.0.1"port 8080 (Errno::EADDRINUSE)
```

ポート8080がサーバ内で既に使用中になっていたため、アプリの起動に失敗しています。「3. GitLabの初期構成」でnginxのポートを明示的に指定していない場合、nginxがexternal_urlに記載されたポート8080で立ち上がってくるため、上記のような衝突事象が発生していました。


## 6. まとめ

- GitLab は `external_url` に厳密に従ってリダイレクトされる  
- SSM セッションマネージャーとポートフォワーディングにより、完全に非公開の GitLab が構築可能  

SSM によるセキュアアクセスと合わせて、**閉域構成での GitLab 利用**を検討している方の参考になれば幸いです。