ブラウザだけで使えるClaude Code環境(EC2 + code-server + Bedrock)にCloudFrontを追加して、拡張機能もインストールできるようにしてみた

ブラウザだけで使えるClaude Code環境(EC2 + code-server + Bedrock)にCloudFrontを追加して、拡張機能もインストールできるようにしてみた

前回構築したEC2 + code-server + Bedrock環境の前段にCloudFrontを置き、自己署名証明書によるブラウザ警告と拡張機能のインストール制限を解消しました。証明書エラーに悩まされず拡張機能も使えるようになるので、研修やハンズオンでもそのまま配りやすい環境になります。
2026.08.25

こんにちは、林です。

前回、EC2上のcode-serverにBedrock経由のClaude Codeを載せた環境を構築しました。

https://dev.classmethod.jp/articles/202608-ec2-code-server-claude-code-bedrock/

この構成には制約がありました。code-server自身が生成する自己署名証明書でHTTPSを動かしているため、ブラウザに警告が出るほか、拡張機能のインストールにも失敗します。

今回はEC2の前段にCloudFrontを置いて、この制約を解消してみました。
CloudFrontのデフォルトドメイン(xxxxxxxx.cloudfront.net)にはブラウザに信頼された証明書が最初から付いているため、証明書を自前で用意する必要がありません。

構成

構成図

前回との違いは、EC2の前にCloudFrontが入り、TLSの終端がCloudFront側に移る点です。
EC2上のcode-serverは暗号化なしのHTTPで動かし、インターネットからEC2への直接アクセスは遮断します。

VPC・IAMロールは前回と同じです。
この記事では変更点だけを記載しているため、事前準備(Bedrockのモデルアクセス有効化・IAMロール作成・VPC作成)は前回記事を参照してください。

EC2のセキュリティグループを変更する

前回はcode-serverのTCP 50443番を、自分のアクセス元IPからのみ許可していました。
今回はこのルールを削除し、代わりにTCP 8080番を、CloudFrontのマネージドプレフィックスリストからのみ許可します。

タイプ ポート範囲 ソース
カスタムTCP 8080 com.amazonaws.global.cloudfront.origin-facing(プレフィックスリスト)

ソースで実際に指定するIDは pl-xxxxxxxx の形式で、リージョンごとに異なります。セキュリティグループのインバウンドルール編集画面で、ソース欄に com.amazonaws.global.cloudfront.origin-facing と入力すると候補として表示されるので、選択すればそのリージョンのIDが自動で入力されます。

このIDはリージョンごとに異なるため、後述のCloudFormationテンプレートではMappingsでリージョン別の対応表を用意しています。

CloudFrontを前に置いても、EC2に直接アクセスできる状態のままでは意味がありません。
オリジンへの経路をCloudFrontからのみに絞ることで、暗号化なしのHTTPで待ち受けていてもインターネットからは到達できなくなります。

code-serverの設定を変更する

TLSをCloudFront側で終端するので、EC2上のcode-serverは暗号化なしのHTTPで動かします。

ユーザーデータで作成する config.yaml のうち、変わるのは次の2行だけです。

bind-addr: 0.0.0.0:8080
cert: false

前回は bind-addr: 0.0.0.0:50443cert: true でした。
Claude Codeのインストール部分(~/.claude/settings.json の作成など)は前回と全く同じです。

EIPとCloudFrontディストリビューションを追加する

CloudFrontのオリジンには、EC2のパブリックDNS名を指定します。
EC2を停止・起動するとこのDNS名は変わってしまうため、先にEIPを紐付けてDNS名を固定してから、ディストリビューションを作成します。

EIPはEC2コンソールの Elastic IP から割り当て、対象のEC2インスタンスに関連付けてください。
これでインスタンスを停止・起動しても、パブリックIPとパブリックDNS名が変わらなくなります。

ディストリビューションの主な設定です。

項目 設定値
オリジンドメイン EC2のパブリックDNS名
オリジンプロトコルポリシー HTTP only
HTTPポート 8080
ビューワープロトコルポリシー Redirect HTTP to HTTPS
許可するHTTPメソッド GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE
キャッシュポリシー CachingDisabled
オリジンリクエストポリシー AllViewer

CachingDisabledAllViewer はどちらもCloudFrontの管理ポリシーです。
code-serverは操作のたびに応答が変わる動的なアプリケーションなので、キャッシュはせず、AllViewer でヘッダーやCookie、クエリ文字列をすべてオリジンに転送します。HTTPメソッドをGET/HEADだけに絞ると、ファイルの保存や拡張機能のインストールができなくなるので注意してください。

後述のCloudFormationテンプレートでは、これらのポリシーをIDで指定します。
CloudFrontはリージョンを持たないグローバルサービスなので、このIDはプレフィックスリストのIDと違ってリージョンやアカウントによらず固定です。


CloudFormationで自動構築する

ここまでの変更を反映したCloudFormationテンプレートです。

Ubuntu 24.04版 テンプレート全文
code-server-cloudfront-ubuntu.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: code-server + Claude Code (Amazon Bedrock) behind CloudFront on Ubuntu 24.04
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Set basic information.
        Parameters:
          - SystemName
      - Label:
          default: Set your EC2 instance settings.
        Parameters:
          - InstanceType
          - AMIID
Parameters:
  SystemName:
    Description: "Prefix of each resource name."
    Type: String
    Default: "code-server"
  InstanceType:
    Description: "EC2 instance type."
    Type: String
    Default: "t3.medium"
  AMIID:
    Description: "Ubuntu 24.04 AMI ID."
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/canonical/ubuntu/server/24.04/stable/current/amd64/hvm/ebs-gp3/ami-id
Mappings:
  # CloudFront managed prefix list ID (per region)
  # Reference: https://aws.amazon.com/jp/blogs/news/limit-access-to-your-origins-using-the-aws-managed-prefix-list-for-amazon-cloudfront/
  AWSRegions2PrefixListID:
    ap-northeast-1:
      PrefixList: pl-58a04531
    ap-northeast-2:
      PrefixList: pl-22a6434b
    ap-northeast-3:
      PrefixList: pl-31a14458
    ap-south-1:
      PrefixList: pl-9aa247f3
    ap-southeast-1:
      PrefixList: pl-31a34658
    ap-southeast-2:
      PrefixList: pl-b8a742d1
    ca-central-1:
      PrefixList: pl-38a64351
    eu-central-1:
      PrefixList: pl-a3a144ca
    eu-north-1:
      PrefixList: pl-fab65393
    eu-west-1:
      PrefixList: pl-4fa04526
    eu-west-2:
      PrefixList: pl-93a247fa
    eu-west-3:
      PrefixList: pl-75b1541c
    sa-east-1:
      PrefixList: pl-5da64334
    us-east-1:
      PrefixList: pl-3b927c52
    us-east-2:
      PrefixList: pl-b6a144df
    us-west-1:
      PrefixList: pl-4ea04527
    us-west-2:
      PrefixList: pl-82a045eb
Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-vpc"
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-igw"
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway
  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [0, !GetAZs ""]
      VpcId: !Ref VPC
      CidrBlock: 10.0.1.0/24
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-public-subnet"
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-public-rtb"
  PublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  PublicRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet

  CodeServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub "${SystemName}-sg"
      GroupDescription: "Security group for code-server (HTTP on 8080, from CloudFront only)"
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 8080
          ToPort: 8080
          SourcePrefixListId: !FindInMap [AWSRegions2PrefixListID, !Ref "AWS::Region", PrefixList]
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-sg"

  CodeServerRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub
        - "${SystemName}-role-${Suffix}"
        - Suffix: !Select [4, !Split ["-", !Select [2, !Split ["/", !Ref "AWS::StackId"]]]]
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "ec2.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
      Policies:
        - PolicyName: "BedrockInvokeAnthropicModels"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Sid: "BedrockInvokeAnthropicModels"
                Effect: "Allow"
                Action:
                  - "bedrock:InvokeModel"
                  - "bedrock:InvokeModelWithResponseStream"
                Resource:
                  - "arn:aws:bedrock:*::foundation-model/anthropic.*"
                  - !Sub "arn:aws:bedrock:*:${AWS::AccountId}:inference-profile/*.anthropic.*"
  CodeServerInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref CodeServerRole

  CodeServerEC2:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref AMIID
      InstanceType: !Ref InstanceType
      BlockDeviceMappings:
        - DeviceName: /dev/sda1
          Ebs:
            Encrypted: true
            VolumeSize: 16
            VolumeType: gp3
            DeleteOnTermination: true
      NetworkInterfaces:
        - AssociatePublicIpAddress: true
          DeviceIndex: "0"
          SubnetId: !Ref PublicSubnet
          GroupSet:
            - !Ref CodeServerSecurityGroup
      IamInstanceProfile: !Ref CodeServerInstanceProfile
      Tags:
        - Key: Name
          Value: !Ref SystemName
      UserData:
        Fn::Base64: |
          #!/bin/bash
          set -eux

          # Install code-server
          apt update
          apt install -y jq curl openssl

          CODER_VERSION=$(curl -s https://api.github.com/repos/coder/code-server/releases/latest | jq -r .tag_name | sed 's/v//')
          curl -fOL "https://github.com/coder/code-server/releases/download/v${CODER_VERSION}/code-server_${CODER_VERSION}_amd64.deb"
          apt install -y ./code-server_${CODER_VERSION}_amd64.deb
          rm -f code-server_${CODER_VERSION}_amd64.deb

          mkdir -p /home/ubuntu/.config/code-server/
          cat > /home/ubuntu/.config/code-server/config.yaml << EOF
          bind-addr: 0.0.0.0:8080
          auth: password
          password: $(openssl rand -hex 16)
          cert: false
          EOF
          chown -R ubuntu:ubuntu /home/ubuntu/.config
          systemctl enable --now code-server@ubuntu

          # Install Claude Code
          sudo -u ubuntu bash -c 'curl -fsSL https://claude.ai/install.sh | bash'

          mkdir -p /home/ubuntu/.claude
          cat > /home/ubuntu/.claude/settings.json << 'EOF'
          {
            "env": {
              "CLAUDE_CODE_USE_BEDROCK": "1",
              "AWS_REGION": "ap-northeast-1",
              "ANTHROPIC_MODEL": "jp.anthropic.claude-sonnet-4-6"
            }
          }
          EOF
          chown -R ubuntu:ubuntu /home/ubuntu/.claude

          # Add PATH
          sudo -u ubuntu bash -c 'grep -q ".local/bin" /home/ubuntu/.bashrc || echo "export PATH=\"$HOME/.local/bin:\$PATH\"" >> /home/ubuntu/.bashrc'

  CodeServerEIP:
    Type: AWS::EC2::EIP
    Properties:
      InstanceId: !Ref CodeServerEC2
      Tags:
        - Key: Name
          Value: !Ref SystemName

  CodeServerDistribution:
    Type: AWS::CloudFront::Distribution
    DependsOn: CodeServerEIP
    Properties:
      DistributionConfig:
        Enabled: true
        Comment: !Ref SystemName
        Origins:
          - Id: "code-server"
            DomainName: !GetAtt CodeServerEC2.PublicDnsName
            CustomOriginConfig:
              OriginProtocolPolicy: "http-only"
              HTTPPort: 8080
        DefaultCacheBehavior:
          TargetOriginId: "code-server"
          ViewerProtocolPolicy: "redirect-to-https"
          AllowedMethods:
            - "GET"
            - "HEAD"
            - "OPTIONS"
            - "PUT"
            - "POST"
            - "PATCH"
            - "DELETE"
          # CachingDisabled
          CachePolicyId: "4135ea2d-6df8-44a3-9df3-4b5a84be39ad"
          # AllViewer
          OriginRequestPolicyId: "216adef6-5c7f-47e4-b989-5492eafa07d3"

Outputs:
  CloudFrontDomain:
    Description: "code-server URL. Access it at https://<CloudFrontDomain>"
    Value: !GetAtt CodeServerDistribution.DomainName
Amazon Linux 2023版 テンプレート全文
code-server-cloudfront-al2023.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: code-server + Claude Code (Amazon Bedrock) behind CloudFront on Amazon Linux 2023
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Set basic information.
        Parameters:
          - SystemName
      - Label:
          default: Set your EC2 instance settings.
        Parameters:
          - InstanceType
          - AMIID
Parameters:
  SystemName:
    Description: "Prefix of each resource name."
    Type: String
    Default: "code-server"
  InstanceType:
    Description: "EC2 instance type."
    Type: String
    Default: "t3.medium"
  AMIID:
    Description: "Amazon Linux 2023 AMI ID."
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64
Mappings:
  # CloudFront managed prefix list ID (per region)
  # Reference: https://aws.amazon.com/jp/blogs/news/limit-access-to-your-origins-using-the-aws-managed-prefix-list-for-amazon-cloudfront/
  AWSRegions2PrefixListID:
    ap-northeast-1:
      PrefixList: pl-58a04531
    ap-northeast-2:
      PrefixList: pl-22a6434b
    ap-northeast-3:
      PrefixList: pl-31a14458
    ap-south-1:
      PrefixList: pl-9aa247f3
    ap-southeast-1:
      PrefixList: pl-31a34658
    ap-southeast-2:
      PrefixList: pl-b8a742d1
    ca-central-1:
      PrefixList: pl-38a64351
    eu-central-1:
      PrefixList: pl-a3a144ca
    eu-north-1:
      PrefixList: pl-fab65393
    eu-west-1:
      PrefixList: pl-4fa04526
    eu-west-2:
      PrefixList: pl-93a247fa
    eu-west-3:
      PrefixList: pl-75b1541c
    sa-east-1:
      PrefixList: pl-5da64334
    us-east-1:
      PrefixList: pl-3b927c52
    us-east-2:
      PrefixList: pl-b6a144df
    us-west-1:
      PrefixList: pl-4ea04527
    us-west-2:
      PrefixList: pl-82a045eb
Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-vpc"
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-igw"
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway
  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: !Select [0, !GetAZs ""]
      VpcId: !Ref VPC
      CidrBlock: 10.0.1.0/24
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-public-subnet"
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-public-rtb"
  PublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  PublicRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet

  CodeServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub "${SystemName}-sg"
      GroupDescription: "Security group for code-server (HTTP on 8080, from CloudFront only)"
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 8080
          ToPort: 8080
          SourcePrefixListId: !FindInMap [AWSRegions2PrefixListID, !Ref "AWS::Region", PrefixList]
      Tags:
        - Key: Name
          Value: !Sub "${SystemName}-sg"

  CodeServerRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub
        - "${SystemName}-role-${Suffix}"
        - Suffix: !Select [4, !Split ["-", !Select [2, !Split ["/", !Ref "AWS::StackId"]]]]
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "ec2.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
      Policies:
        - PolicyName: "BedrockInvokeAnthropicModels"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Sid: "BedrockInvokeAnthropicModels"
                Effect: "Allow"
                Action:
                  - "bedrock:InvokeModel"
                  - "bedrock:InvokeModelWithResponseStream"
                Resource:
                  - "arn:aws:bedrock:*::foundation-model/anthropic.*"
                  - !Sub "arn:aws:bedrock:*:${AWS::AccountId}:inference-profile/*.anthropic.*"
  CodeServerInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref CodeServerRole

  CodeServerEC2:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref AMIID
      InstanceType: !Ref InstanceType
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            Encrypted: true
            VolumeSize: 16
            VolumeType: gp3
            DeleteOnTermination: true
      NetworkInterfaces:
        - AssociatePublicIpAddress: true
          DeviceIndex: "0"
          SubnetId: !Ref PublicSubnet
          GroupSet:
            - !Ref CodeServerSecurityGroup
      IamInstanceProfile: !Ref CodeServerInstanceProfile
      Tags:
        - Key: Name
          Value: !Ref SystemName
      UserData:
        Fn::Base64: |
          #!/bin/bash
          set -eux

          # Install code-server
          dnf install -y jq openssl

          CODER_VERSION=$(curl -s https://api.github.com/repos/coder/code-server/releases/latest | jq -r .tag_name | sed 's/v//')
          curl -fOL "https://github.com/coder/code-server/releases/download/v${CODER_VERSION}/code-server-${CODER_VERSION}-amd64.rpm"
          dnf install -y ./code-server-${CODER_VERSION}-amd64.rpm
          rm -f code-server-${CODER_VERSION}-amd64.rpm

          mkdir -p /home/ec2-user/.config/code-server/
          cat > /home/ec2-user/.config/code-server/config.yaml << EOF
          bind-addr: 0.0.0.0:8080
          auth: password
          password: $(openssl rand -hex 16)
          cert: false
          EOF
          chown -R ec2-user:ec2-user /home/ec2-user/.config
          systemctl enable --now code-server@ec2-user

          # Install Claude Code
          sudo -u ec2-user bash -c 'curl -fsSL https://claude.ai/install.sh | bash'

          mkdir -p /home/ec2-user/.claude
          cat > /home/ec2-user/.claude/settings.json << 'EOF'
          {
            "env": {
              "CLAUDE_CODE_USE_BEDROCK": "1",
              "AWS_REGION": "ap-northeast-1",
              "ANTHROPIC_MODEL": "jp.anthropic.claude-sonnet-4-6"
            }
          }
          EOF
          chown -R ec2-user:ec2-user /home/ec2-user/.claude

          # Add PATH
          sudo -u ec2-user bash -c 'grep -q ".local/bin" /home/ec2-user/.bashrc || echo "export PATH=\"$HOME/.local/bin:\$PATH\"" >> /home/ec2-user/.bashrc'

  CodeServerEIP:
    Type: AWS::EC2::EIP
    Properties:
      InstanceId: !Ref CodeServerEC2
      Tags:
        - Key: Name
          Value: !Ref SystemName

  CodeServerDistribution:
    Type: AWS::CloudFront::Distribution
    DependsOn: CodeServerEIP
    Properties:
      DistributionConfig:
        Enabled: true
        Comment: !Ref SystemName
        Origins:
          - Id: "code-server"
            DomainName: !GetAtt CodeServerEC2.PublicDnsName
            CustomOriginConfig:
              OriginProtocolPolicy: "http-only"
              HTTPPort: 8080
        DefaultCacheBehavior:
          TargetOriginId: "code-server"
          ViewerProtocolPolicy: "redirect-to-https"
          AllowedMethods:
            - "GET"
            - "HEAD"
            - "OPTIONS"
            - "PUT"
            - "POST"
            - "PATCH"
            - "DELETE"
          # CachingDisabled
          CachePolicyId: "4135ea2d-6df8-44a3-9df3-4b5a84be39ad"
          # AllViewer
          OriginRequestPolicyId: "216adef6-5c7f-47e4-b989-5492eafa07d3"

Outputs:
  CloudFrontDomain:
    Description: "code-server URL. Access it at https://<CloudFrontDomain>"
    Value: !GetAtt CodeServerDistribution.DomainName

VPC・IAMロールの部分は前回と同一で、テンプレート上の差分は次の7箇所です。

  • AllowedIPパラメータを削除しました
    EC2へのアクセス経路がCloudFrontに限定されるため、自分のIPを指定する必要が無くなったためです
- AllowedIP:
-   Description: "Your access source IP in CIDR notation (e.g. 203.0.113.10/32)."
-   Type: String
  • CloudFrontのマネージドプレフィックスリストIDをリージョンごとに引くためのMappingsを追加しました
+ Mappings:
+   AWSRegions2PrefixListID:
+     ap-northeast-1:
+       PrefixList: pl-58a04531
+     ap-northeast-2:
+       PrefixList: pl-22a6434b
+     # ...(以下17リージョン分。全文は後述のテンプレートを参照)
  • CodeServerSecurityGroupは、ポートと許可元を変更しています
        SecurityGroupIngress:
          - IpProtocol: tcp
-           FromPort: 50443
-           ToPort: 50443
-           CidrIp: !Ref AllowedIP
+           FromPort: 8080
+           ToPort: 8080
+           SourcePrefixListId: !FindInMap [AWSRegions2PrefixListID, !Ref "AWS::Region", PrefixList]
  • CodeServerEC2のUserData内、config.yamlを生成する部分も変わります
          cat > /home/ubuntu/.config/code-server/config.yaml << EOF
-         bind-addr: 0.0.0.0:50443
+         bind-addr: 0.0.0.0:8080
          auth: password
          password: $(openssl rand -hex 16)
-         cert: true
+         cert: false
          EOF
  • CodeServerEIPCodeServerDistributionは新規追加のリソースです
  CodeServerEIP:
    Type: AWS::EC2::EIP
    Properties:
      InstanceId: !Ref CodeServerEC2
      Tags:
        - Key: Name
          Value: !Ref SystemName

  CodeServerDistribution:
    Type: AWS::CloudFront::Distribution
    DependsOn: CodeServerEIP
    Properties:
      DistributionConfig:
        Enabled: true
        Comment: !Ref SystemName
        Origins:
          - Id: "code-server"
            DomainName: !GetAtt CodeServerEC2.PublicDnsName
            CustomOriginConfig:
              OriginProtocolPolicy: "http-only"
              HTTPPort: 8080
        DefaultCacheBehavior:
          TargetOriginId: "code-server"
          ViewerProtocolPolicy: "redirect-to-https"
          AllowedMethods:
            - "GET"
            - "HEAD"
            - "OPTIONS"
            - "PUT"
            - "POST"
            - "PATCH"
            - "DELETE"
          # CachingDisabled
          CachePolicyId: "4135ea2d-6df8-44a3-9df3-4b5a84be39ad"
          # AllViewer
          OriginRequestPolicyId: "216adef6-5c7f-47e4-b989-5492eafa07d3"
  • OutputsをEC2のパブリックIPからCloudFrontのドメインに変更しました。
- PublicIP:
-   Description: "code-server public IP. Access it at https://<PublicIP>:50443"
-   Value: !GetAtt CodeServerEC2.PublicIp
+ CloudFrontDomain:
+   Description: "code-server URL. Access it at https://<CloudFrontDomain>"
+   Value: !GetAtt CodeServerDistribution.DomainName

デプロイと動作確認

デプロイコマンドは次のとおりです。
AllowedIPが無くなった以外は、前回と同じ形です。

aws cloudformation create-stack --stack-name temp-code-server-cloudfront \
    --template-body file://./code-server-cloudfront-ubuntu.yaml \
    --capabilities CAPABILITY_NAMED_IAM

Amazon Linux 2023版は --template-bodycode-server-cloudfront-al2023.yaml に変えてください。
使うモデルを変えたい場合は、テンプレートのユーザーデータ内、~/.claude/settings.jsonANTHROPIC_MODEL を書き換えてください。

デプロイが終わると、OutputsにCloudFrontのドメイン(xxxxxxxx.cloudfront.net)が出力されます。ディストリビューションの作成には数分かかるので、有効になるまで少し待ってから次に進んでください。

パスワードの確認方法はSession Manager経由で前回と同じです。

# Ubuntuの場合
sudo cat /home/ubuntu/.config/code-server/config.yaml | grep password:

# Amazon Linux 2023の場合
sudo cat /home/ec2-user/.config/code-server/config.yaml | grep password:

ブラウザでCloudFrontのドメインにアクセスします。前回と変わるのは次の2点です。

まず、証明書の警告が出ないことです。CloudFrontのデフォルトドメインを使うので、ブラウザに信頼された証明書でアクセスできます。

証明書の警告が出ずにcode-serverのログイン画面が開いた様子

そして本題の、拡張機能をインストールできるかどうかです。日本語化のための「Japanese Language Pack for Visual Studio Code」と、「Claude Code for VS Code」の2つをインストールしてみました。

Japanese Language Pack for Visual Studio Codeのインストール結果

Claude Code for VS Codeのインストール結果

どちらも問題なくインストールできました。日本語パックは、インストール後に表示される再起動の案内に従うと反映されます。実際にメニューが日本語化されていることも確認できています。

メニューが日本語化された状態でターミナルを開こうとしている画面

あわせて、ターミナルやファイル保存がCloudFront経由でも問題なく動くかも確認します。code-serverはWebSocketで通信するため、ここが動かないとエディタとして使えません。

ターミナルでclaude auth statusを実行し、Bedrock経由でログインできていることを確認した様子

ターミナルの起動・コマンド実行ともに問題なく動作し、claude auth statusでBedrock経由のログイン状態も確認できました。

運用上の注意

CloudFrontとEIPが増えるぶん、前回の構成より料金がかかります。
EIPはインスタンスに紐付けていても課金対象なので、使い終わったらスタックごと削除してください。
CloudFrontのデフォルトドメインは推測されにくい文字列ですが、URLを知っていれば誰でもアクセスできてしまいます。認証はcode-serverのパスワードのみなので、長期運用する場合はCognito認証などをあわせて検討してください。


さいごに

CloudFrontを前段に置くことで、code-serverの証明書まわりの制約を解消できました。
前回のコンソール手順・CloudFormationテンプレートに、セキュリティグループ・code-serverの設定・EIPとCloudFrontディストリビューションの3点を足すだけで済むので、既存環境への反映も難しくないはずです。

この記事がどなたかの参考になれば幸いです。最後までご覧いただきありがとうございました!

参考

https://dev.classmethod.jp/articles/202608-ec2-code-server-claude-code-bedrock/

https://aws.amazon.com/jp/blogs/news/limit-access-to-your-origins-using-the-aws-managed-prefix-list-for-amazon-cloudfront/


Claudeならクラスメソッドにお任せください

クラスメソッドは、Anthropic社とリセラー契約を締結しています。各種製品ガイドから、業種別の活用法、フェーズごとのお悩み解決などサービス支援ページにまとめております。まずはご覧いただき、お気軽にご相談ください。

サービス詳細を見る

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事