🐙

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

に公開

導入

背景・目的

  • 2025年11月20日をもってCodeGuru Securityがサポート終了し、サービスアクセスが不可になることがアナウンスされました。
  • 代替サービスとしては、Amazon Q DeveloperとInspectorが案内されています。
  • 両サービスを活用したSASTは、アプリケーションセキュリティの向上に有効ですが、それぞれの利用タイミングや特性が異なります。
  • 上記サービス群を組み合わせて、GitLab Community Edition(以降、GitLab)で管理しているアプリケーションコードのセキュリティを強化する仕組みについて、実装例を紹介します。

対象読者

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

環境構成

Amazon Q Developerのセットアップ

GitLabのセットアップ

GitLab向けAWS資源の構築

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の統合

Amazon 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 にも自動的に集約されます。

まとめ

本記事では、GitLab CE環境において Amazon Q Developer と Inspector とを利用することで、ローカル開発段階から継続的スキャンまでのシフトレフトによるセキュリティ検証の早期化を実現しました。
これにより、開発者個人の裁量に依存せず、組織的にセキュリティ品質を担保する仕組みを構築できます。
なお、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://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