LoginSignup
1
0

More than 3 years have passed since last update.

ElasticBeanstalk環境下のALBで301リダイレクトさせる

Last updated at Posted at 2019-07-23

本エントリは下書きにしたまま放置してあったんですが、ElasticBeanstalkとTerraformの記事って、ネットであまり見かけないしバッドノウハウは需要あると思い、掘り起こしてブラッシュアップしました。
https://youtu.be/e2W4BTR_9eA:embed:cite

ApplicationLoadBalancerのリスナーは下記をサポートしており、そのリスナーにはデフォルトのルールとオプションルールを定義することが可能です。
各ルールは優先度、1 つ以上のアクション、1 つ以上の条件で構成されます。

  • プロトコル: HTTP,HTTPS
  • ポート: 1 ~ 65535

ルールアクションの中にredirectというタイプがあるので、これを使用することでHTTP=>HTTPSへリダイレクトさせることが可能になります。
(今から1年前くらいに実装された機能です)
https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-listeners.html#redirect-actions:embed:cite

そもそもElasticBeanstalkの標準の名前空間においてALBのリスナーのルールでリダイレクトを設定する機能が存在しません。そのため、.ebextensionsでALBにリダイレクトルールを入れようとしました。下記がその時の設定です。

elastic_beanstalk_environment.tf
(snip)
/* Network Tier */
#
# Load Balancing
#
// EnvironmentType
setting {
  namespace = "aws:elasticbeanstalk:environment"
  name      = "EnvironmentType"
  value     = "LoadBalanced"
}

// 環境のロードバランサーのタイプ
setting {
  namespace = "aws:elasticbeanstalk:environment"
  name      = "LoadBalancerType"
  value     = "application"
}

#
# process:default
#
// Elastic Load Balancing がアプリケーションの Amazon EC2 インスタンスの状態をチェックする間隔 (秒)
setting {
  namespace = "aws:elasticbeanstalk:environment:process:default"
  name      = "HealthCheckInterval"
  value     = "15"
}

// ヘルスチェックの HTTP リクエストを送信するパス
setting {
  namespace = "aws:elasticbeanstalk:environment:process:default"
  name      = "HealthCheckPath"
  value     = "/healthcheck"
}

// ヘルスチェック中のレスポンスの待機時間 (秒)
setting {
  namespace = "aws:elasticbeanstalk:environment:process:default"
  name      = "HealthCheckTimeout"
  value     = "5"
}

// インスタンスのヘルスステータスを変更するために必要な、連続して成功したリクエストの数
setting {
  namespace = "aws:elasticbeanstalk:environment:process:default"
  name      = "HealthyThresholdCount"
  value     = "3"
}

// インスタンスが正常であることを示す HTTP コードのカンマ区切りのリスト
setting {
  namespace = "aws:elasticbeanstalk:environment:process:default"
  name      = "MatcherHTTPCode"
  value     = "200"
}

// プロセスがリッスンしているポート
setting {
  namespace = "aws:elasticbeanstalk:environment:process:default"
  name      = "Port"
  value     = "80"
}

// プロセスで使用するプロトコル
setting {
  namespace = "aws:elasticbeanstalk:environment:process:default"
  name      = "Protocol"
  value     = "HTTP"
}

// スティッキーセッション
setting {
  namespace = "aws:elasticbeanstalk:environment:process:default"
  name      = "StickinessEnabled"
  value     = "false"
}

// 内部ロードバランサーを作成する場合は internal を指定
setting {
  namespace = "aws:ec2:vpc"
  name      = "ELBScheme"
  value     = "external"
}

setting {
  namespace = "aws:elbv2:listener:80"
  name      = "ListenerEnabled"
  value     = "true"
}

// HTTP リスナーによって使用されるプロトコル
setting {
  namespace = "aws:elbv2:listener:80"
  name      = "Protocol"
  value     = "HTTP"
}

// HTTPS リスナーによって使用されるプロトコル
setting {
  namespace = "aws:elbv2:listener:443"
  name      = "Protocol"
  value     = "HTTPS"
}

// サーバ証明書
setting {
  namespace = "aws:elbv2:listener:443"
  name      = "SSLCertificateArns"
  value     = "arn:aws:acm:ap-northeast-1:xxxxxxxxx:certificate/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

// セキュリティポリシー
setting {
  namespace = "aws:elbv2:listener:443"
  name      = "SSLPolicy"
  value     = "ELBSecurityPolicy-2016-08"
}

// HTTP リスナーに適用するルールのリスト
setting {
  namespace = "aws:elbv2:listener:80"
  name      = "DefaultProcess"
  value     = "default"
}

// HTTPS リスナーに適用するルールのリスト
setting {
  namespace = "aws:elbv2:listener:443"
  name      = "DefaultProcess"
  value     = "default"
}

#
# default
#
// HTTP リスナーに適用するルールのリスト
setting {
  namespace = "aws:elbv2:listener:80"
  name      = "Rules"
  value     = "default"
}

// HTTPS リスナーに適用するルールのリスト
setting {
  namespace = "aws:elbv2:listener:443"
  name      = "Rules"
  value     = "default"
}

setting {
  namespace = "aws:elbv2:listenerrule:default"
  name      = "Process"
  value     = "default"
}

// 一致するホスト名のリスト
setting {
  namespace = "aws:elbv2:listenerrule:default"
  name      = "HostHeaders"
  value     = "xxxxx.jp"
}

// 複数のルールが一致する場合の、このルールの優先順位。低い番号が優先される
setting {
  namespace = "aws:elbv2:listenerrule:default"
  name      = "Priority"
  value     = "1"
}

// アクセスログを保存する Amazon S3 バケット
setting {
  namespace = "aws:elbv2:loadbalancer"
  name      = "AccessLogsS3Bucket"
  value     = "xxxxx"
}

// アクセスログストレージを有効
setting {
  namespace = "aws:elbv2:loadbalancer"
  name      = "AccessLogsS3Enabled"
  value     = "true"
}

// アクセスログ名に追加するプレフィックス
setting {
  namespace = "aws:elbv2:loadbalancer"
  name      = "AccessLogsS3Prefix"
  value     = "xxxx/production"
}

// クライアントとインスタンスへの接続を閉じる前に、リクエストの完了を待機する時間
setting {
  namespace = "aws:elbv2:loadbalancer"
  name      = "IdleTimeout"
  value     = "60"
}

// EBでSGを新規作成せずに、既存SGを充てる
setting {
  namespace = "aws:elbv2:loadbalancer"
  name      = "SecurityGroups"
  value     = aws_security_group.xxxxx_elb_sg.id
}

// EBでSGを新規作成せずに、既存SGを充てる
setting {
  namespace = "aws:elbv2:loadbalancer"
  name      = "ManagedSecurityGroup"
  value     = aws_security_group.xxxxx_elb_sg.id
}
(snip)

alb_redirect.config
Resources:
  AWSEBV2LoadBalancerListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      LoadBalancerArn:
        Ref: AWSEBV2LoadBalancer
      DefaultActions:
        - Type: redirect
          RedirectConfig:
            Protocol: HTTPS
            Port: 443
            StatusCode: 'HTTP_301'
      Port: 80
      Protocol: HTTP

結果、デプロイは正常に終了しましたがALBにルールが適用されませんでした...

原因は設定オプションの優先順位に起因するものでした。

ElasticBeanstalk環境では、環境作成時にCreateEnvironmentAPIのoptionSettingsパラメータにて指定した設定が最優先されます。
Terraformから実行されたCreateEnvironmentAPIのリクエストパラメータoptionSettingsにおいて、名前空間aws:elbv2:listener:80に対する設定が優先されたため、.ebextensions配下のconfigが反映されなかったのが原因でした。

以下の名前空間を削除して置き換えました。

setting {
  namespace = "aws:elbv2:listener:80"
  name      = "ListenerEnabled"
  value     = "true"
}

setting {
  namespace = "aws:elbv2:listener:80"
  name      = "Protocol"
  value     = "HTTP"
}

setting {
  namespace = "aws:elbv2:listener:80"
  name      = "DefaultProcess"
  value     = "default"
}

setting {
  namespace = "aws:elbv2:listener:80"
  name      = "Rules"
  value     = "default"
}

setting {
  namespace = "aws:elbv2:listener:443"
  name      = "Rules"
  value     = "default"
}

setting {
  namespace = "aws:elbv2:listenerrule:default"
  name      = "Process"
  value     = "default"
}

setting {
  namespace = "aws:elbv2:listenerrule:default"
  name      = "HostHeaders"
  value     = "xxxxx.jp"
}

setting {
  namespace = "aws:elbv2:listenerrule:default"
  name      = "Priority"
  value     = "1"
}
  • .ebextensionが優先されるように更新した設定ファイル

elastic_beanstalk_environment.tf
(snip)
   /* Network Tier */
  #
  # Load Balancing
  #
  // EnvironmentType
  setting {
    namespace = "aws:elasticbeanstalk:environment"
    name      = "EnvironmentType"
    value     = "LoadBalanced"
  }

  // 環境のロードバランサーのタイプ
  setting {
    namespace = "aws:elasticbeanstalk:environment"
    name      = "LoadBalancerType"
    value     = "application"
  }

  #
  # process:default
  #
  // Elastic Load Balancing がアプリケーションの Amazon EC2 インスタンスの状態をチェックする間隔 (秒)
  setting {
    namespace = "aws:elasticbeanstalk:environment:process:default"
    name      = "HealthCheckInterval"
    value     = "15"
  }

  // ヘルスチェックの HTTP リクエストを送信するパス
  setting {
    namespace = "aws:elasticbeanstalk:environment:process:default"
    name      = "HealthCheckPath"
    value     = "/healthcheck"
  }

  // ヘルスチェック中のレスポンスの待機時間 (秒)
  setting {
    namespace = "aws:elasticbeanstalk:environment:process:default"
    name      = "HealthCheckTimeout"
    value     = "5"
  }

  // インスタンスのヘルスステータスを変更するために必要な、連続して成功したリクエストの数
  setting {
    namespace = "aws:elasticbeanstalk:environment:process:default"
    name      = "HealthyThresholdCount"
    value     = "3"
  }

  // インスタンスが正常であることを示す HTTP コードのカンマ区切りのリスト
  setting {
    namespace = "aws:elasticbeanstalk:environment:process:default"
    name      = "MatcherHTTPCode"
    value     = "200"
  }

  // プロセスがリッスンしているポート
  setting {
    namespace = "aws:elasticbeanstalk:environment:process:default"
    name      = "Port"
    value     = "80"
  }

  // プロセスで使用するプロトコル
  setting {
    namespace = "aws:elasticbeanstalk:environment:process:default"
    name      = "Protocol"
    value     = "HTTP"
  }

  // スティッキーセッション
  setting {
    namespace = "aws:elasticbeanstalk:environment:process:default"
    name      = "StickinessEnabled"
    value     = "false"
  }

  // 内部ロードバランサーを作成する場合は internal を指定
  setting {
    namespace = "aws:ec2:vpc"
    name      = "ELBScheme"
    value     = "external"
  }

  // HTTPS リスナーによって使用されるプロトコル
  setting {
    namespace = "aws:elbv2:listener:443"
    name      = "Protocol"
    value     = "HTTPS"
  }

  // サーバ証明書
  setting {
    namespace = "aws:elbv2:listener:443"
    name      = "SSLCertificateArns"
    value     = "arn:aws:acm:ap-northeast-1:xxxxxxxxx:certificate/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
  }

  // セキュリティポリシー
  setting {
    namespace = "aws:elbv2:listener:443"
    name      = "SSLPolicy"
    value     = "ELBSecurityPolicy-2016-08"
  }

  // HTTPS リスナーに適用するルールのリスト
  setting {
    namespace = "aws:elbv2:listener:443"
    name      = "DefaultProcess"
    value     = "default"
  }

  #
  # default
  #
  setting {
    namespace = "aws:elbv2:listener:443"
    name      = "Rules"
    value     = "tls"
  }

  setting {
    namespace = "aws:elbv2:listenerrule:tls"
    name      = "HostHeaders"
    value     = "xxxxx.jp"
  }

  // アクセスログを保存する Amazon S3 バケット
  setting {
    namespace = "aws:elbv2:loadbalancer"
    name      = "AccessLogsS3Bucket"
    value     = "xxxxxx"
  }

  // アクセスログストレージを有効
  setting {
    namespace = "aws:elbv2:loadbalancer"
    name      = "AccessLogsS3Enabled"
    value     = "true"
  }

  // アクセスログ名に追加するプレフィックス
  setting {
    namespace = "aws:elbv2:loadbalancer"
    name      = "AccessLogsS3Prefix"
    value     = "xxxx/production"
  }

  // クライアントとインスタンスへの接続を閉じる前に、リクエストの完了を待機する時間
  setting {
    namespace = "aws:elbv2:loadbalancer"
    name      = "IdleTimeout"
    value     = "60"
  }

  // EBでSGを新規作成せずに、既存SGを充てる
  setting {
    namespace = "aws:elbv2:loadbalancer"
    name      = "SecurityGroups"
    value     = aws_security_group.xxxxx_elb.id
  }

  // EBでSGを新規作成せずに、既存SGを充てる
  setting {
    namespace = "aws:elbv2:loadbalancer"
    name      = "ManagedSecurityGroup"
    value     = aws_security_group.xxxxx_elb.id
  }
(snip)

これでリスナールールが更新されるはず!と思ってapplyするも、正常終了しても既存のルールがそのまま残っており、優先順位が変更できない状態に陥りました。
さんざん悩んだ結果、環境を再作成することで.ebextensions配下の設定ファイルを適用することができました。このあたりはTerraformとCloudFormationの問題だと思うのですが、
今現在も解決していないため、ISSUEを出したいと思っています。

image.png

301でリダイレクトされています。

$ curl --head xxxxx.jp
HTTP/1.1 301 Moved Permanently
Server: awselb/2.0
Date: Tue, 23 Jul 2019 03:23:34 GMT
Content-Type: text/html
Content-Length: 150
Connection: keep-alive
Location: https://xxxxx.jp:443/

ALBのログにはredirectされたことがわかるように出ています。

http 2019-07-23T04:31:30.708115Z app/awseb-AWSEB-************/*********** ***.***.***.***:49488 - -1 -1 -1 301 - 185 349 "GET http://***.***.***.***:80/ HTTP/1.1" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36" - - - "Root=1-5d368da2-89854ad4ee4a72105965a0c8" "-" "-" 0 2019-07-23T04:31:30.707000Z "redirect" "https://***.***.***.***:443/" "-"
1
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
1
0