🐙

GitLab環境で実践するAmazon Q DeveloperとInspectorによるシフトレフト

に公開

導入

背景・目的

2025年11月20日をもってCodeGuru Securityがサポート終了し、サービスアクセスが不可になることがアナウンスされました。代替サービスとしては、Amazon Q DeveloperとAmazon Inspectorが案内されています。
本記事では、セキュアソフトウェア開発ライフサイクル(SSDLC)の考え方に基づき、GitLab Community Edition(以降、GitLab)で管理しているアプリケーションコードに対する、Amazon Q DeveloperとInspectorを組み合わせたシフトレフトの実現パターンを解説します。各ツールの役割と設計判断を明確にしたうえで、実装例を紹介します。

対象読者

AWS Certified Security - Specialtyレベル以上の知識を想定し、AWSセキュリティサービスに対する詳細な説明は割愛します。

環境構成

SSDLCの背景

SDLCにおけるセキュリティの課題

ソフトウェア開発ライフサイクル(SDLC)は、計画・設計・実装・テスト・デプロイ・メンテナンスの各フェーズで構成されます。従来のSDLCでは、セキュリティテストはテストフェーズで初めて実施されるのが一般的でした。この場合、実装フェーズで混入した脆弱性がテストフェーズまで検出されず、手戻りが大きくなるという課題があります。
脆弱性の修正コストは、発見が遅れるほど増大します。設計段階で発見できれば設計変更で済むものが、テスト段階やリリース後に発見された場合は、コード修正・再テスト・再デプロイといった追加工数が発生します。こうした背景から、セキュリティ活動をSDLCの上流工程に移動させる「シフトレフト」の必要性が高まっています。

SSDLCとは

セキュアソフトウェア開発ライフサイクル(SSDLC)は、SDLCの各フェーズにセキュリティ活動を統合する方法論です。セキュリティテストをソフトウェア開発と分離するのではなく、DevSecOpsのプラクティスを通じてソフトウェア開発の各工程にセキュリティテストを統合することで、脆弱性の早期発見と修正コストの低減を実現します。

本記事では、SSDLCの中でも実装フェーズに焦点を当て、AWSのAIサービスを活用して、開発者のローカル環境からCI/CDパイプラインまでを対象としたシフトレフトの実現パターンを解説します。

AWSサービスによるシフトレフト実現パターン

AWS Well-Architected Frameworkにおけるアプリケーションセキュリティ

AWSのAIサービスを活用してシフトレフトを実現するにあたり、AWS Well-Architected Frameworkのセキュリティの柱の記載が参考になります。特に、アプリケーションセキュリティに関する以下の2つに目を通すのが良いでしょう。

SEC11-BP02 開発およびリリースライフサイクル全体を通じてテストを自動化する

開発およびリリースライフサイクル全体を通じて、セキュリティ特性のテストを自動化します。自動化により、リリース前にソフトウェアの潜在的な問題を一貫して繰り返し特定することが容易になります。これにより、提供されるソフトウェアのセキュリティ問題のリスクが低減されます。
(出典:https://docs.aws.amazon.com/ja_jp/wellarchitected/latest/framework/sec_appsec_automate_testing_throughout_lifecycle.html)

SEC11-BP04 コードレビューを実施する

コードレビューを実装して、開発中のソフトウェアの品質とセキュリティを検証します。コードレビューでは、元のコード作成者以外のチームメンバーがコードをレビューし、潜在的な問題、脆弱性、コーディング標準とベストプラクティスへの準拠を確認します。このプロセスは、元のデベロッパーが見落とした可能性のあるエラー、不整合、セキュリティ上の欠陥を検出するのに役立ちます。自動ツールを使用してコードレビューを支援します。
(出典:https://docs.aws.amazon.com/ja_jp/wellarchitected/latest/framework/sec_appsec_manual_code_reviews.html)

これらのベストプラクティスを踏まえ、SSDLCの実装フェーズにおいてセキュリティテストやコードレビューを組み込むような、AWSサービスの活用パターンを解説していきます。

2つのレイヤーでのAWSサービス活用

SSDLCの実装フェーズにおいて、SASTを単一のツールで完結させるのではなく、CodeGuru Securityの代替サービスとしてアナウンスされたAmazon Q DeveloperとInspectorを2つのレイヤーで利用することで、テスト自動化とコードレビューによるセキュリティ向上をそれぞれのツールの特性を生かして実現します。

レイヤー1:Amazon Q Developerによるローカル環境でのSAST/コードレビュー
Amazon Q Developerは、IDE上でコードレビュー機能を提供します。開発者がコーディング中にリアルタイムで脆弱性のフィードバックを受けることができ、SEC11-BP02が推奨する「セキュリティ評価の自動化・早期実施」や、SEC11-BP04が推奨する「コードレビュー実施」について、ローカル環境での開発時に実行することができます。
このような即時性の高いフィードバックを得られる一方で、レビュー実行が開発者個人の裁量に委ねられることから、実行・修正漏れのリスクがあります。セキュリティ品質向上の支援ツールとして非常に有用ではありつつ、リリース前にセキュリティ品質を組織的に担保するための仕組みが必要です。

レイヤー2:Inspectorによるリリース前のSAST
Inspectorのコードスキャン機能は、GitLabやGitHubなどのリポジトリと統合し、自動スキャンを実行します。SEC11-BP02が推奨する「開発・リリースライフサイクル全体を通じたテスト自動化」を実現し、検出結果はAWS Security Hubに自動集約されます。
リポジトリ更新時に自動スキャンが強制的に実行されることから、開発者個人の裁量に依存せず、スキャンの実行漏れを防ぐことが可能です。

レイヤー1の即時性とレイヤー2の強制性を組み合わせることで、開発者が自律的にセキュリティ品質を高めつつ、組織的に漏れなくセキュリティを担保する設計となり、SSDLC実装フェーズにおけるSASTの実効性を最大化することが可能です。
以降のセクションでは、このパターンの実装例として、GitLab CE環境におけるAmazon Q DeveloperとInspectorのセットアップおよび動作検証を紹介します。

Amazon Q DeveloperとInspectorのセットアップ~動作検証

[再掲]環境構成

Amazon Q Developerのセットアップ

GitLab実行基盤の構築

まずは、CDKを用いてGitLab向けにALB・EC2関連資源を構築します。

GitLab向けAWS資源構築用Stack

// ------------ Network ---------------
// ---- Route53
const myHostedZone = route53.HostedZone.fromLookup(this, "HostedZone", {
	domainName: props.domainName
})

// ---- VPC Resources
// Create VPC Resources by using Blea
// https://github.com/aws-samples/baseline-environment-on-aws/blob/main/usecases/blea-guest-ecs-app-sample/lib/construct/networking.ts
const networking = new Networking(this, "Networking", {
	vpcCidr: props.vpcCidr,
});

// ------------ GitLab Resources ---------------
// ---- Secrets Manager
const gitLabSecret = new secretsmanager.Secret(this, "GitLabSecret", {
	secretName: `${props.pathPrefix}/password/gitlab/root`,
	description: "GitLab Root User Password",
	generateSecretString: {
		secretStringTemplate: JSON.stringify({ username: "root" }),
		generateStringKey: "password",
		excludeCharacters: '"@/\\\"',
		passwordLength: 32,
	},
});

// ---- EC2
// Create IAM Role
const gitLabInstanceRole = new iam.Role(this, "GitLabInstanceRole", {
	assumedBy: new iam.ServicePrincipal("ec2.amazonaws.com"),
	managedPolicies: [
		iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonSSMManagedInstanceCore"),
	],
})
gitLabSecret.grantRead(gitLabInstanceRole)
gitLabSecret.grantWrite(gitLabInstanceRole)

// Config User Data
const gitLabSetUp = new GitLabSetUp(this, "GitLabSetUp", {
	domainName: `gitlab.${props.domainName}`,
	secretArn: gitLabSecret.secretArn,
})

// Create Instance
const gitLabInstance = new ec2.Instance(this, "GitLabInstance", {
	vpc: networking.vpc,
	instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.LARGE),
	machineImage: ec2.MachineImage.latestAmazonLinux2023(),
	vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
	role: gitLabInstanceRole,
	userData: gitLabSetUp.userData,
})

// ---- ALB
// Create Certificate
const gitLabCertificate = new acm.Certificate(this, 'GitLabCertificate', {
	domainName: `gitlab.${props.domainName}`,
	validation: acm.CertificateValidation.fromDns(myHostedZone),
});

// Create Load Balancer
const gitLabLb = new elbv2.ApplicationLoadBalancer(this, "GitLabLb", {
	vpc: networking.vpc,
	vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
	internetFacing: true,
})

// Create Target Group
const gitLabTg = new elbv2.ApplicationTargetGroup(this, "GitLabTg", {
	vpc: networking.vpc,
	port: 80,
	protocol: elbv2.ApplicationProtocol.HTTP,
	targetType: elbv2.TargetType.INSTANCE,
	targets: [new elbv2_targets.InstanceTarget(gitLabInstance)],
	healthCheck: {
		path: "/-/health",
		protocol: elbv2.Protocol.HTTP,
		healthyHttpCodes: "200",
		interval: cdk.Duration.seconds(60),
		timeout: cdk.Duration.seconds(10),
		healthyThresholdCount: 2,
		unhealthyThresholdCount: 10,
	}
})

// Add Listener
gitLabLb.addListener("HttpsListener", {
	port: 443,
	open: true,
	protocol: elbv2.ApplicationProtocol.HTTPS,
	certificates: [gitLabCertificate],
	defaultAction: elbv2.ListenerAction.forward([gitLabTg]),
})
gitLabLb.addListener("HttpListener", {
	port: 80,
	open: true,
	protocol: elbv2.ApplicationProtocol.HTTP,
	defaultAction: elbv2.ListenerAction.redirect({
		protocol: "HTTPS",
		port: "443",
		permanent: true,
	}),
})

// Allow Connection to EC2
gitLabLb.connections.allowTo(gitLabInstance, ec2.Port.HTTP)

// Add ARecord
new route53.ARecord(this, "GitLabARecord", {
	zone: myHostedZone,
	recordName: `gitlab.${props.domainName}`,
	target: route53.RecordTarget.fromAlias(new targets.LoadBalancerTarget(gitLabLb)),
});

UserData設定用Construct [1]

// Create Linux UserData
const userData = ec2.UserData.forLinux({ shebang: '#!/bin/bash' });

userData.addCommands(
	'set -e',
	`export DOMAIN_NAME="${props.domainName}"`,
	`export SECRET_ARN="${props.secretArn}"`,
	'',
	'# System update and basic dependency installation',
	'dnf update -y',
	'dnf install -y policycoreutils-python-utils openssh-server openssh-clients perl wget',
	'',
	'# Install GitLab CE',
	'curl "https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh" | bash',
	'dnf install -y gitlab-ce',
	'',
	'# Update GitLab configuration',
	'cat >> /etc/gitlab/gitlab.rb << EOF',
	'',
	'# Configuration for ALB environment',
	"external_url 'https://${DOMAIN_NAME}'",
	"nginx['listen_port'] = 80",
	"nginx['listen_https'] = false",
	'',
	'# Avoid systemd target conflicts in AWS EC2 UserData environment',
	"package['systemd_after'] = 'basic.target'",
	"package['systemd_wanted_by'] = 'basic.target'",
	'EOF',
	'',
	'# Apply GitLab configuration',
	'gitlab-ctl reconfigure',
	'',
	'# Retrieve initial GitLab root password and store it in AWS Secrets Manager',
	'if [ -f "/etc/gitlab/initial_root_password" ]; then',
	'  ROOT_PASSWORD=$(grep \'^Password:\' /etc/gitlab/initial_root_password | sed \'s/^Password: //\')',
	'  if [ -n "$ROOT_PASSWORD" ]; then',
	'    aws secretsmanager put-secret-value --secret-id "$SECRET_ARN" --secret-string "{\\"username\\":\\"root\\",\\"password\\":\\"$ROOT_PASSWORD\\"}" --region "ap-northeast-1"',
	'  fi',
	'fi'
);
this.userData = userData;

CDKデプロイ後にGitLabコンソールにアクセスし、Secrets Managerに格納されたパスワードを用いてログインします。[2]

ログイン成功後、多要素認証やサインアップ制限を設定しておきます。

  • User settings > Account から、多要素認証の有効化
  • Admin area > Settings > General > Sign-up restrictions > Sign-up enabledの無効化

上記設定完了後、検証用リポジトリを作成しておきます。

Access Tokenの発行

それでは、Inspectorとの統合に必要なAccess Tokenを発行していきます。
User settings > Access tokens > Personal access tokensセクションでボタン「Add new token」をクリックします。
api, read_api, read_repository, write_repositoryを許可するよう設定して、Access Tokenを発行します。

これでGitLab側の事前セットアップは完了です。

GitLabとInspectorの統合

Inspectorのコードスキャン機能とGitLabを統合し、リポジトリ更新時に自動スキャンを実行します。

AWSコンソールにログインし、Inspector>コードセキュリティ画面から、コードスキャンを有効化します。

有効化後、GitLabと接続するよう設定します。

AWS推奨設定に基づき、週次スキャン及びプルリクエスト/プッシュ時スキャンを有効化します。

エンドポイントURL及びAccess Tokenを登録し、GitLab側で接続を承認することで統合が有効化されます。

動作検証

サンプルコードの作成

SQLインジェクションやOSコマンドインジェクション等の脆弱性を含むPythonサンプルコードを作成します。

Amazon Q DeveloperでのSAST

Amazon Q Developerでは、自動レビューおよびチャットパネル経由のレビューが可能です。
今回はVSCodeのチャットパネルで Run a code review on this file を入力し、手動レビューを実行します。
スキャン完了後、Code Issuesパネルに検出結果が表示されます。
検出箇所をクリックすると、拡大鏡アイコンで解説確認、レンチアイコンで自動修正が可能です。

InspectorでのSAST

同一のPythonコードをGitLab上のリポジトリ(mainブランチ)にPushします。
Inspectorコンソールでスキャン結果を確認すると、ファイルパスや脆弱箇所が明示されています。

また、検出結果は AWS Security Hub にも自動的に集約されます。

まとめ

本記事では、SSDLCの考え方に基づき、GitLab CE環境においてAmazon Q DeveloperとInspectorを組み合わせたシフトレフトの実現パターン及びセットアップ方法について解説しました。
SSDLCの実装フェーズにおけるSASTを、ローカル開発環境(Amazon Q Developer)とCI/CDパイプライン(Inspector)の2つのレイヤーで実現し、即時性と強制性を両立する設計としています。
これにより、開発者個人の裁量に依存せず、組織的にセキュリティ品質を担保する仕組みを構築できます。
なお、InspectorはGitLabだけでなくGitHubとの統合も可能ですので、是非試してみてください。

参考資料

https://docs.aws.amazon.com/ja_jp/amazonq/latest/qdeveloper-ug/code-reviews.html
https://docs.aws.amazon.com/ja_jp/amazonq/latest/qdeveloper-ug/start-review.html
https://docs.aws.amazon.com/ja_jp/inspector/latest/user/code-security-assessments-connect-gitlab.html
https://docs.aws.amazon.com/ja_jp/inspector/latest/user/code-security-assessments-create-configuration.html
https://aws.amazon.com/jp/what-is/sdlc/
https://docs.aws.amazon.com/ja_jp/wellarchitected/latest/framework/sec-11.html
https://docs.gitlab.com/user/profile/personal_access_tokens/
https://dev.classmethod.jp/articles/2025-09-24-cdk-self-managed-gitlab-ce-demo/
https://github.com/aws-samples/baseline-environment-on-aws/blob/main/usecases/blea-guest-ecs-app-sample/lib/construct/networking.ts

注意事項

本記事は万全を期して作成していますが、お気づきの点がありましたら、ご連絡よろしくお願いします。
なお、本記事の内容を利用した結果及び影響について、筆者は一切の責任を負いませんので、予めご了承ください。

脚注
  1. Mixed Contentエラーを回避するために、external_urlのスキームにはHTTPSを指定してください。 ↩︎

  2. GitLabコンソールにアクセスできない場合は、gitlab-runsvdirサービスのステータスを確認してください。 ↩︎

Accenture Japan (有志)

Discussion