Dify on AWSのシンプル構成をHTTPSアクセス可能な構成にする

記事タイトルとURLをコピーする

はじめに

こんにちは、久保です。

AWSではBedrockをはじめとした様々な生成AIのサービスが提供されています。
生成AIアプリケーションを構築、利用したい場合に、フロントエンドとして何を利用するかについても様々な選択肢がありますが、
本記事ではDifyをお試しで利用したい場合の構築方法についてご紹介します。

AWSの構築ワークショップとして以下URLで提供されているCloudFormationテンプレートをベースに、HTTPSで利用できるようにした構成を紹介します。

Dify での生成 AI アプリケーション構築ワークショップ

本記事のCloudFormationテンプレートを利用することで、簡単にDifyをAWSで試してみたいが通信はHTTPSで暗号化したい というケースに対応可能となります。

2026/1/16 "Bedrockを利用するためのDifyの初期設定"を追記しました。

Dify とは

github.com

Dify is an open-source platform for developing LLM applications.

とあるとおり、LangGenius社が提供している、生成AIアプリケーションを作成するためのオープンソースプラットフォームであり、どなたでも利用することが可能なソフトウェアです。

GUIでアプリケーションの処理を定義することが可能で、非エンジニアの方であっても簡単に生成AIアプリケーションを構築することができます。

生成AIによる業務効率化などの課題解決を、より現場の業務に精通した現場の方々が主体となって実施可能とすることができます。

AWSワークショップのご紹介

先述のとおり、AWSではDifyを利用した生成AIアプリケーションを構築するためのワークショップが提供されています。
こちらを実施することでAWSでDifyを利用した生成AIアプリケーションの構築を簡単に体験することができます。

Dify での生成 AI アプリケーション構築ワークショップ

ただし注意事項に明記されておりますとおり、ワークショップで構築する環境では通信の暗号化や公開アプリの認証、冗長化は実施されないため、本番利用の際には必要なセキュリティを別途施す必要があります。

本記事で紹介する構成について

本記事では最低限、通信についてはCloudFrontを利用してHTTPSで暗号化通信が可能なようにしたCloudFormationテンプレートを紹介します。

注意事項として、PC→CloudFront間の通信はHTTPSで暗号化されますが、CloudFront→EC2間の通信はHTTP通信となります。
あくまでもインターネットを経由する通信を暗号化することを目的としていることをご理解ください。

本記事で紹介する構成

ベースのテンプレートとの違いは以下のとおりです。

  • EC2の前にCloudFrontが配置され、HTTPSでのアクセスが可能となります(CloudFrontのデフォルトドメイン名を利用します)
  • アクセスを許可するIPアドレスアドレスを指定した場合は、AWS WAFがCloudFrontにアタッチされ、指定したIPアドレスからのアクセスのみ許可されます
  • CloudFront用のAWS WAFはus-east-1で作成する必要があるため、IP制限を行う場合はCloudFormationスタックをus-east-1で作成する必要があります(IP制限しない場合は任意のリージョンで作成可能です)
  • EC2のセキュリティグループは、CloudFrontのIPアドレスレンジからのHTTPアクセスのみを許可します
  • DifyVersionというパラメータで利用するDifyのバージョンを指定可能です(デフォルトは1.11.2

なお、Amazon Bedrockが米国(オレゴン)となっているのはワークショップ内容をそのまま実施した場合の例です。実際には任意のリージョンでBedrockを利用可能です。

CloudFormationテンプレートの紹介

dify-self-deployment-with-ssl.ymlとして保存してご利用ください。

▼テンプレートを表示する
AWSTemplateFormatVersion: '2010-09-09'
Description: Dify CloudFormation Template with HTTPS support

Parameters:
  VpcCIDR:
    Type: String
    Default: 192.168.0.0/16
    Description: CIDR block for the VPC

  Subnet1CIDR:
    Type: String
    Default: 192.168.0.0/20
    Description: CIDR block for Subnet 1

  Subnet2CIDR:
    Type: String
    Default: 192.168.16.0/20
    Description: CIDR block for Subnet 2

  AllowedCIDR:
    Type: String
    Default: 0.0.0.0/0
    Description: CIDR block to allow HTTPS traffic from CloudFront. Set to 0.0.0.0/0 to allow all IPs, or specify a specific CIDR (e.g., 203.0.113.0/24) to restrict access via WAF

  DifyVersion:
    Type: String
    Default: 1.11.2
    Description: Dify version to deploy

  AmazonLinuxAMI:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64


Conditions:
  EnableIPRestriction: !Not [!Equals [!Ref AllowedCIDR, '0.0.0.0/0']]


Mappings:
  # CloudFront origin-facing managed prefix list IDs by region
  # aws ec2 describe-managed-prefix-lists --region <REGION> | jq -r '.PrefixLists[] | select (.PrefixListName == "com.amazonaws.global.cloudfront.origin-facing") | .PrefixListId'
  AWSRegions2PrefixListID:
    ap-northeast-1:
      PrefixList: pl-58a04531
    ap-northeast-2:
      PrefixList: pl-22a6434b
    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:
  DifyVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCIDR
      EnableDnsHostnames: true
      EnableDnsSupport: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: dify-vpc

  Subnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref DifyVPC
      AvailabilityZone: !Select [0, !GetAZs '']
      CidrBlock: !Ref Subnet1CIDR
      Tags:
        - Key: Name
          Value: dify-subnet-1

  Subnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref DifyVPC
      AvailabilityZone: !Select [1, !GetAZs '']
      CidrBlock: !Ref Subnet2CIDR
      Tags:
        - Key: Name
          Value: dify-subnet-2

  InternetGateway:
    Type: AWS::EC2::InternetGateway

  VPCGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref DifyVPC
      InternetGatewayId: !Ref InternetGateway

  RouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref DifyVPC

  Route:
    Type: AWS::EC2::Route
    DependsOn: VPCGatewayAttachment
    Properties:
      RouteTableId: !Ref RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  Subnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref Subnet1
      RouteTableId: !Ref RouteTable

  Subnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref Subnet2
      RouteTableId: !Ref RouteTable

  DifySecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow HTTP traffic from CloudFront only
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          SourcePrefixListId: !FindInMap [AWSRegions2PrefixListID, !Ref 'AWS::Region', PrefixList]
          Description: Allow HTTP traffic from CloudFront origin-facing
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0
          Description: Allow all outbound traffic
      VpcId: !Ref DifyVPC
      Tags:
        - Key: Name
          Value: dify-sg

  DifyWsInstanceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/AmazonBedrockFullAccess'
        - 'arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore'

  InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref DifyWsInstanceRole

  DifyWsInstance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref AmazonLinuxAMI
      InstanceType: t3.medium
      NetworkInterfaces:
        - AssociatePublicIpAddress: 'true'
          DeviceIndex: '0'
          GroupSet:
            - !Ref DifySecurityGroup
          SubnetId: !Ref Subnet1
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            VolumeSize: 20
            VolumeType: gp2
            Encrypted: 'true'
            
      Tags:
        - Key: Name
          Value: dify-ws
      IamInstanceProfile: !Ref InstanceProfile
      UserData:
        Fn::Base64: !Sub |
            #!/bin/bash
            max_attempts=5
            attempt_num=1
            success=false
            while [ $success = false ] && [ $attempt_num -le $max_attempts ]; do
              sudo dnf install -y git docker
              if [ $? -eq 0 ]; then
                echo "dnf install succeeded"
                success=true
              else
                echo "dnf install $attempt_num failed. trying again..."
                sleep 3
                ((attempt_num++))
              fi
            done

            sudo systemctl start docker
            sudo gpasswd -a ec2-user docker
            sudo gpasswd -a ssm-user docker
            sudo chgrp docker /var/run/docker.sock
            sudo service docker restart
            sudo systemctl enable docker
            sudo curl -L "https://github.com/docker/compose/releases/download/v2.28.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
            sudo chmod +x /usr/local/bin/docker-compose
            sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
            cd /opt
            sudo git clone https://github.com/langgenius/dify.git
            cd /opt/dify
            sudo git checkout ${DifyVersion}
            sudo git pull origin ${DifyVersion}
            cd /opt/dify/docker
            sudo cp .env.example .env
            docker-compose up -d

  WAFIPSet:
    Type: AWS::WAFv2::IPSet
    Condition: EnableIPRestriction
    Properties:
      Name: DifyAllowedIPs
      Scope: CLOUDFRONT
      IPAddressVersion: IPV4
      Addresses:
        - !Ref AllowedCIDR

  WAFWebACL:
    Type: AWS::WAFv2::WebACL
    Condition: EnableIPRestriction
    Properties:
      Name: DifyCloudFrontWAF
      Scope: CLOUDFRONT
      DefaultAction:
        Block: {}
      Rules:
        - Name: AllowSpecificIPs
          Priority: 0
          Statement:
            IPSetReferenceStatement:
              Arn: !GetAtt WAFIPSet.Arn
          Action:
            Allow: {}
          VisibilityConfig:
            SampledRequestsEnabled: true
            CloudWatchMetricsEnabled: true
            MetricName: AllowSpecificIPsRule
      VisibilityConfig:
        SampledRequestsEnabled: true
        CloudWatchMetricsEnabled: true
        MetricName: DifyCloudFrontWAF

  CloudFrontDistribution:
    Type: AWS::CloudFront::Distribution
    Properties:
      DistributionConfig:
        Enabled: true
        Comment: Dify CloudFront Distribution with HTTPS
        WebACLId: !If [EnableIPRestriction, !GetAtt WAFWebACL.Arn, !Ref 'AWS::NoValue']
        DefaultCacheBehavior:
          TargetOriginId: DifyEC2Origin
          ViewerProtocolPolicy: redirect-to-https
          AllowedMethods:
            - GET
            - HEAD
            - OPTIONS
            - PUT
            - POST
            - PATCH
            - DELETE
          CachedMethods:
            - GET
            - HEAD
            - OPTIONS
          ForwardedValues:
            QueryString: true
            Headers:
              - '*'
            Cookies:
              Forward: all
          MinTTL: 0
          DefaultTTL: 0
          MaxTTL: 0
        Origins:
          - Id: DifyEC2Origin
            DomainName: !GetAtt DifyWsInstance.PublicDnsName
            CustomOriginConfig:
              HTTPPort: 80
              OriginProtocolPolicy: http-only
        PriceClass: PriceClass_All

Outputs:
  CloudFrontURL:
    Description: CloudFront Distribution URL (HTTPS enabled)
    Value: !Sub 'https://${CloudFrontDistribution.DomainName}'
    Export:
      Name: DifyCloudFrontURL

  InstancePublicIP:
    Description: Public IP of the EC2 instance (Direct access is restricted to CloudFront)
    Value: !GetAtt DifyWsInstance.PublicIp
    Export:
      Name: DifyInstancePublicIP
  InstanceId:
    Description: InstanceId of the EC2 instance
    Value: !Ref DifyWsInstance
    Export:
      Name: DifyInstanceId

利用例

CloudFormationスタックの作成

AWSマネジメントコンソールにログインし、CloudFormationのサービスページに移動します。
構築したいリージョンを選択し、「スタックの作成」をクリックします。
以下ではIP制限も行うために米国(バージニア北部)リージョンを例にしています。

「テンプレートファイルのアップロード」を選択し、先ほど保存したdify-self-deployment-with-ssl.ymlファイルを選択します。 「次へ」をクリックします。

スタック名に任意の名前、例) "dify-on-aws"を入力し、必要に応じてパラメータを変更します。

パラメータ 説明 デフォルト値
AllowedCIDR CloudFront経由でのHTTPSアクセスを許可するCIDRブロック。例えば1.2.3.4/32といった形式で指定します。すべてのIPアドレスからのアクセスを許可する場合は0.0.0.0/0を指定します。 0.0.0.0/0
AmazonLinuxAMI EC2インスタンスに使用するAmazon Linux 2023のAMI ID。デフォルトでは最新のAMI IDがSSMパラメータストアから取得されます。 /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64
DifyVersion デプロイするDifyのバージョン。 1.11.2
Subnet1CIDR サブネット1のCIDRブロック。 192.168.0.0/20
Subnet2CIDR サブネット2のCIDRブロック。 192.168.16.0/20
VpcCIDR VPCのCIDRブロック。 192.168.0.0/16

通常、お試しで利用いただく場合は AllowedCIDR の設定のみ変更し、他のパラメータはデフォルト値のままで問題ありません。

本記事では AllowedCIDR に IPを指定した場合を例とします。

パラメータを入力しましたら「次へ」をクリックします。

"スタックオプションの設定"画面となります。最下部までスクロールし、AWS CloudFormation によって IAM リソースが作成される場合があることを承認します。にチェックを入れ、「次へ」をクリックします。

"確認して作成"の画面となります。内容を確認し、「送信」をクリックします。

スタックの作成が開始されます。
WAFの作成まで含めて概ね5分程度で完了します。
ステータスが"CREATE_COMPLETE"となれば完了です。
「出力」タブをクリックします。

CloudFrontURLの値をクリックします。

これで、HTTPSでDifyの初期画面にアクセスできることが確認できます。
管理者として登録するメールアドレス、任意のユーザ名、パスワードを設定します。

Bedrockを利用するためのDifyの初期設定

DifyでBedrockを利用するために、モデルの設定を行う必要があります。
画面右上のアイコンをクリックし、設定画面にアクセスします。

「モデルプロバイダー」をクリックし「Amazon Bedrock」の「インストール」をクリックします。

「インストール」をクリックします。

インストール後、「セットアップ」をクリックします。

リージョンをBedrockを利用したいリージョンに指定し、「保存」をクリックします。

「システムモデル設定」をクリックします。

システム推論モデル、埋め込みモデル、Rerankモデルをそれぞれ任意のモデルを指定し、保存します。
図は一例です。
これらはデフォルトでDifyで利用されたり、会話のタイトルの生成などのシステムでのLLM利用時に使用されます。
Bedrockの場合音声-to-テキストモデル、テキスト-to-音声モデル は指定できないため空欄とします。
Rerankモデルはリージョンによって利用できない場合があります。
例えば us-east-1 の場合は cohere.rerank-v3-5:0 が利用可能です。

最後に、表示するモデルの設定をご紹介します。
「モデルの表示」をクリックします。

こちらでモデルのオンオフが可能です。
普段利用しないモデルはオフにしておくことで、アプリを作成する際に非表示にすることができます。

その他Difyの設定についてはAWSワークショップの内容や、弊社の過去記事もご参照ください。

blog.serverworks.co.jp

blog.serverworks.co.jp

参考(Infrastructure Composerで確認)

本例で構築されるリソースをInfrastructure Composerで確認すると以下のようになります。

終了後のお掃除

Difyのお試しが終了しましたら、CloudFormationスタックを削除してリソースをクリーンアップしてください。

おわりに

本記事ではDifyをAWS上に構築する際に、HTTPSでアクセス可能とする構成をCloudFormationテンプレートで紹介しました。

あくまでシンプルにHTTPSでアクセス可能とすることを目的としているため、CloudFrontのオリジンは直接EC2を指定する形となっています。
EC2はプライベートサブネットに配置してVPCオリジンを利用する、もしくはALBを間に配置するなど、本番利用にあたってはよりセキュアな構成をご検討いただく必要がございます。

Dify on AWS を可能な限り簡単に、かつ最低限のセキュリティを確保して試したい場合に、本記事の内容がお役に立てば幸いです。

久保 賢二(執筆記事の一覧)

猫とAWSが好きです。