0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【AWS】CloudFormtionでブログサービスを構築してみた(02.EC2,RDS,ALB編)

Last updated at Posted at 2023-02-16

01.はじめに

【AWS】CloudFormtionでブログサービスを構築してみた(概要編)の続きである。
過去に公開した記事は下記の通りである。
【AWS】CloudFormtionでブログサービスを構築してみた(概要編)
【AWS】CloudFormtionでブログサービスを構築してみた(01.Network編)

この記事で扱うものはコンポーネントは下記である。
1.EC2
2.ALB
3.RDS(スナップショットを復元する)

また、テンプレートを投入後、手作業が発生するのでこちらも対応する。
SSM経由でEC2にログインして実施する
1.こちらでスナップショットを復元したRDSの接続先が
http://localhost:500になっている(SSHポートフォワーディングした影響)のでALBのドメイン名に変更する。

2.wp-config.phpに下記を追記する(https通信するとフォントが崩れてしまうため)

if($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
  $_SERVER['HTTPS'] = 'on';
  $_ENV['HTTPS'] = 'on';
}

02.構成図

下記の通りである。
構成図2‐EC2,ALB.png

03.事前準備

事前準備として下記をしておく
1.Route53で登録したゾーンのNSレコードをお名前ドットコムのネームサーバー情報に記載する。
 公式サイトによると設定変更が反映されるまで数時間~72時間かかるとのことだが、
 筆者の環境では1時間かからず反映された。
 設定変更方法

2.Wordpressの初期設定済のEC2を構築してAMI化しておく。
image.png
3.RDSのスナップショットを取得しておく。
こちらで取得したものを復元する
image.png

04.リスナールールの設計

リスナールールで下記アクセスになるように制御する
1.wp-adminは管理画面のログインURLなので、「自宅のIP/32」以外接続許可しない

2.httpアクセスはhttpsへリダイレクトする

表にすると下記。
◆自宅からのアクセス

アクセスURL アクセス結果
https://<ALBのドメイン名> 許可(200)
https://<ALBのドメイン名>/wp-admin 許可(302)(認証画面にリダイレクトされるため)
http://<ALBのドメイン名> httpsにリダイレクト(301)
http://<ALBのドメイン名>/wp-admin 拒否(403)

※自分からわざわざwp-adminにhttpアクセスしないので拒否にしている

◆自宅以外からのアクセス

アクセスURL アクセス結果
https://<ALBのドメイン名> 許可(200)
https://<ALBのドメイン名>/wp-admin 拒否(403)
http://<ALBのドメイン名> httpsにリダイレクト(301)
http://<ALBのドメイン名>/wp-admin 拒否(403)

05.テスト方法

下記観点で確認する
1.通常アクセス
 ターゲットグループのテストもかねて1台EC2をシャットダウンした状態でアクセスする。

2.ヘルスチェックが想定通りになっているか確認する

3.リスナールールの機能テスト(curlでレスポンスコードをとるのみにする)
 自宅のコマンドプロンプトと稼働中のEC2からそれぞれ実施する。

06.テンプレートファイル

Outputs情報

Outputs:

#サブネット
  PublicSubnetA:
      Value: !Ref PublicSubnetA
      Export:
        Name: PublicSubnetA-Outputs
  PublicSubnetB:
      Value: !Ref PublicSubnetB
      Export:
        Name: PublicSubnetB-Outputs
  PrivateSubnetA1:
      Value: !Ref PrivateSubnetA1
      Export:
        Name: PrivateSubnetA1-Outputs
  PrivateSubnetA2:
      Value: !Ref PrivateSubnetA2
      Export:
        Name: PrivateSubnetA2-Outputs
  PrivateSubnetC1:
      Value: !Ref PrivateSubnetC1
      Export:
        Name: PrivateSubnetC1-Outputs
  PrivateSubnetC2:
      Value: !Ref PrivateSubnetC2
      Export:
        Name: PrivateSubnetC2-Outputs
#セキュリティグループ
  EC2SecurityGroup:
      Value: !Ref EC2SecurityGroup
      Export:
        Name: EC2SecurityGroup-Outputs
  ALBSecurityGroup:
      Value: !Ref ALBSecurityGroup
      Export:
        Name: ALBSecurityGroup-Outputs
  RDSSecurityGroup:
      Value: !Ref RDSSecurityGroup
      Export:
        Name: RDSSecurityGroup-Outputs
  #VPC
  CFVPC:
      Value: !Ref CFVPC
      Export:
        Name: CFVPC-Outputs
  #Route53HostZone(ACMの証明書発行で使う)
  HostZone:
      Value: !Ref Route53HostedZone
      Export:
        Name: CFHostZone-Outputs

テンプレートファイル

EC2-ALB.yml
AWSTemplateFormatVersion: 2010-09-09
Description: EC2-ALB

# ------------------------------------------------------------#
#  パラメータ
# ------------------------------------------------------------#
Parameters:
#取得する証明書のドメイン
  ACMDomainName:
    Type: String
    Description: Type of this ACM Domain
#事前に取得しているEC2のAMI
  EC2AMI:
    Type: String
    Description: Type of this EC2AMI.
#自宅のグローバルIP(リスナールールで使用する)
  MYip:
    Type: String
    Description: Type of this MYip.
#管理画面のURLパス(リスナールールで使用する)
  URLPath:
    Type: String
    Description: URLPath.
    Default: "/wp-admin/*"

# ------------------------------------------------------------#
#  リソース
# ------------------------------------------------------------#
Resources:

# ------------------------------------------------------------#
#  証明書取得
# ------------------------------------------------------------#
  ACM:
    Type: AWS::CertificateManager::Certificate
    Properties: 
      DomainName: !Sub "${ACMDomainName}"
      DomainValidationOptions:
        - DomainName: !Sub "${ACMDomainName}"
          HostedZoneId: !ImportValue "CFHostZone-Outputs"
      Tags: 
        - Key: Name
          Value: CF-ACM
      ValidationMethod: DNS
# ------------------------------------------------------------#
#  EC2
# ------------------------------------------------------------#

  myEC2Instance1:
    Type: AWS::EC2::Instance
    Properties:
      KeyName: <事前に取得しているキーペア名>
      #取得したAMI 
      ImageId: !Sub ${EC2AMI}
      InstanceType: t2.micro
      Monitoring: true
      IamInstanceProfile: !Ref EC2InstanceProfile
      SecurityGroupIds:
        - !ImportValue EC2SecurityGroup-Outputs
      SubnetId: !ImportValue PrivateSubnetA1-Outputs
      Tags:
        - Key: Name
          Value: CF-EC2-1

  myEC2Instance2:
    Type: AWS::EC2::Instance
    Properties:
      KeyName: <事前に取得しているキーペア名>
      #取得したAMI 
      ImageId: !Sub ${EC2AMI}
      InstanceType: t2.micro
      Monitoring: true
      IamInstanceProfile: !Ref EC2InstanceProfile
      SecurityGroupIds:
        - !ImportValue EC2SecurityGroup-Outputs
      SubnetId: !ImportValue PrivateSubnetC1-Outputs
      Tags:
        - Key: Name
          Value: CF-EC2-2
# ------------------------------------------------------------#
#  セッションマネージャー許可のIAMロールを付与
# ------------------------------------------------------------#
  EC2IAMRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: EC2-SSM-role
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
# ------------------------------------------------------------#
#  セッションマネージャー許可のIAMロールをアタッチ
# ------------------------------------------------------------#
  EC2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: /
      Roles:
        - Ref: EC2IAMRole
      InstanceProfileName: EC2InstanceProfile


# ------------------------------------------------------------#
#  ALB
# ------------------------------------------------------------#

# ------------------------------------------------------------#
#  ALB本体
# ------------------------------------------------------------#
  CFALB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      IpAddressType: ipv4
      Name: CF-ALB
      Scheme: internet-facing
      SecurityGroups:
        - !ImportValue ALBSecurityGroup-Outputs
      Subnets:
        - !ImportValue PublicSubnetA-Outputs
        - !ImportValue PublicSubnetB-Outputs
      Tags:
        - Key: Name
          Value: CF-ALB
      Type: application

# ------------------------------------------------------------#
#  ターゲットグループ
# ------------------------------------------------------------#
  CFTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: CF-TargetGroup
      VpcId: !ImportValue CFVPC-Outputs
      IpAddressType: "ipv4"
      Protocol: HTTP
      ProtocolVersion: HTTP1
      Port: 80
      TargetType: instance
      Targets:
      - Id: !Ref myEC2Instance1
        Port: 80
      - Id: !Ref myEC2Instance2
        Port: 80
      #httpアクセスはリダイレクトされるので301にしている
      Matcher:
        HttpCode: "301"
      HealthCheckEnabled: true
      HealthCheckIntervalSeconds: 10
      HealthCheckPath: /index.php
      HealthCheckPort: traffic-port
      HealthCheckProtocol: HTTP
      HealthCheckTimeoutSeconds: 5
      HealthyThresholdCount: 2
      UnhealthyThresholdCount: 2

# ------------------------------------------------------------#
#  リスナー(https)    
# ------------------------------------------------------------#
#https用
  CFhttpsListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    DependsOn: ACM
    Properties:
      LoadBalancerArn: !Ref CFALB
      Certificates:
        - !Ref ACM
      DefaultActions:
      - TargetGroupArn: !Ref CFTargetGroup
        Type: forward
      Port: 443
      Protocol: HTTPS
      SslPolicy: "ELBSecurityPolicy-2016-08"
    
#リスナールール
#wp-adminのアクセスを特定IPのみ許可する設定
  CFhttpsListenerRule1:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties: 
      Actions: 
        - Type: forward
          TargetGroupArn: !Ref CFTargetGroup
      Conditions: 
        - Field: source-ip
          SourceIpConfig:
            Values:
              - !Sub ${MYip}
        - Field: path-pattern
          PathPatternConfig:
            Values:
              - !Sub ${URLPath}
      ListenerArn: !Ref CFhttpsListener
      Priority: 1

#wp-adminのアクセスを拒否する設定
  CFhttpsListenerRule2:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties: 
      Actions: 
        - Type: fixed-response
          FixedResponseConfig:
              ContentType: text/plain
              MessageBody: "403 Forbideen"
              StatusCode: 403
      Conditions: 
        - Field: path-pattern
          PathPatternConfig:
            Values:
              - !Sub ${URLPath}
      ListenerArn: !Ref CFhttpsListener
      Priority: 2

# ------------------------------------------------------------#
#  リスナー(http)    
# ------------------------------------------------------------#
#http用
  CFhttpListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn: !Ref CFALB
      DefaultActions:
      - Type: redirect
        RedirectConfig: 
          Protocol: "HTTPS"
          Port: 443
          Host: "#{host}"
          Path: "/#{path}"
          Query: "#{query}"
          StatusCode: "HTTP_301"
      Port: 80
      Protocol: HTTP

#リスナールール
#wp-adminのアクセスを拒否する設定
  CFhttpListenerRule1:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties: 
      Actions: 
        - Type: fixed-response
          FixedResponseConfig:
              ContentType: text/plain
              MessageBody: "403 Forbideen"
              StatusCode: 403
      Conditions: 
        - Field: path-pattern
          PathPatternConfig:
            Values:
              - !Sub ${URLPath}
      ListenerArn: !Ref CFhttpListener
      Priority: 1

# ------------------------------------------------------------#
#  RDS SubnetGroup
# ------------------------------------------------------------#
  CFDBSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription: CF-DB-SubnetGroup
      SubnetIds:
        - !ImportValue PrivateSubnetA2-Outputs
        - !ImportValue PrivateSubnetC2-Outputs
      Tags:
      - Key: Name
        Value: CFDBSubnetGroup

Outputs:
#ALBのDNS名の定義(Route53で使う)
  ALBDomainName:
      Value: !GetAtt CFALB.DNSName
      Export:
        Name: ALB-DNS-Outputs
#ALBのゾーンIDの定義(Route53で使う)
  ALBZoneId:
      Value: !GetAtt CFALB.CanonicalHostedZoneID
      Export:
        Name: ALB-CanonicalHostedZoneID-Outputs
#ALBのARNの定義(WAFで使う)
  ALBArn:
      Value: !Ref CFALB
      Export:
        Name: ALB-ARN-Outputs 

07.RDSのスナップショット復元

RDSのスナップショットを復元する。

08.修正作業

1.RDSの接続ポイント修正
EC2にセッションマネージャーでログイン、RDSに接続して下記コマンドを実行

$ mysql -h <データベースのエンドポイント名> -u wordpress -p
MySQL [(none)]> use wordpress
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MySQL [wordpress]> SELECT * FROM wp_options WHERE option_name IN ('siteurl', 'home');
+-----------+-------------+----------------------+----------+
| option_id | option_name | option_value         | autoload |
+-----------+-------------+----------------------+----------+
|         2 | home        | http://localhost:500 | yes      |
|         1 | siteurl     | http://localhost:500 | yes      |
+-----------+-------------+----------------------+----------+
2 rows in set (0.00 sec)

→下記コマンドで修正する

MySQL [wordpress]> UPDATE wp_options SET option_value = 'http://<ALBのドメイン名>' WHERE option_name IN ('siteurl', 'home');

2.EC2のwp-config.phpに下記を追記する(https通信するとフォントが崩れてしまうため)

if($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
  $_SERVER['HTTPS'] = 'on';
  $_ENV['HTTPS'] = 'on';
}

09.テスト

1.通常アクセス
ターゲットグループのテストもかねて1台EC2をシャットダウンした状態でアクセスする。

image.png
ALBのDNS名に対してhttpsアクセスする。
証明書はRoute53のドメインに充てているため、警告は出るがhttpsアクセス出来ている。
image.png
image.png
→想定通り

2.ヘルスチェックが想定通りになっているか確認する
image.png
→想定通り

3.リスナールールの機能テスト(curlでレスポンスコードをとるのみにする)
 自宅のコマンドプロンプトと稼働中のEC2からそれぞれ実施する。
 この表通りになるか確認する。

◆自宅からのアクセス

アクセスURL アクセス結果
https://<ALBのドメイン名> 許可(200)
https://<ALBのドメイン名>/wp-admin 許可(302)(認証画面にリダイレクトされるため)
http://<ALBのドメイン名> httpsにリダイレクト(301)
http://<ALBのドメイン名>/wp-admin 拒否(403)

※自分からわざわざwp-adminにhttpアクセスしないので拒否にしている

◆自宅以外からのアクセス

アクセスURL アクセス結果
https://<ALBのドメイン名> 許可(200)
https://<ALBのドメイン名>/wp-admin 拒否(403)
http://<ALBのドメイン名> httpsにリダイレクト(301)
http://<ALBのドメイン名>/wp-admin 拒否(403)

・自宅のコマンドプロンプトから

>curl -k https://CF-ALB-156526098.ap-northeast-1.elb.amazonaws.com -o /dev/null -w '%{http_code}\n' -s
'200
'
>
→通常アクセスなので想定通り

>curl -k https://CF-ALB-156526098.ap-northeast-1.elb.amazonaws.com/wp-admin/ -o /dev/null -w '%{http_code}\n' -s
'302
'
>
→ログインの認証画面に遷移するので問題なし

>curl http://CF-ALB-156526098.ap-northeast-1.elb.amazonaws.com -o /dev/null -w '%{http_code}\n' -s
'301
'
>
→httpsにリダイレクトされるので問題なし

>curl http://CF-ALB-156526098.ap-northeast-1.elb.amazonaws.com/wp-admin/ -o /dev/null -w '%{http_code}\n' -s
'403
'
>
→wp-adminへのアクセスなので403で問題なし

・EC2から

$ whoami 
ec2-user
$ 

$ curl -k https://CF-ALB-156526098.ap-northeast-1.elb.amazonaws.com -o /dev/null -w '%{http_code}\n' -s
200
$ 
→通常アクセスなので想定通り

$ curl -k https://CF-ALB-156526098.ap-northeast-1.elb.amazonaws.com/wp-admin/ -o /dev/null -w '%{http_code}\n' -s
403
$ 
→wp-adminへのアクセスなので403で問題なし

$ curl http://CF-ALB-156526098.ap-northeast-1.elb.amazonaws.com -o /dev/null -w '%{http_code}\n' -s
301
$ 
→httpsへリダイレクトされるので問題なし

$ curl http://CF-ALB-156526098.ap-northeast-1.elb.amazonaws.com/wp-admin/ -o /dev/null -w '%{http_code}\n' -s
403
$ 
→wp-adminへのアクセスなので403で問題なし

参考文献

★次回はこちら
【AWS】CloudFormtionでブログサービスを構築してみた(03.S3,CloudFront編)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?