diff --git a/README.md b/README.md index dbb15d0b..e4daa7d0 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,8 @@ jobs: 1. [EC2](#ec2-inputs) 1. [VPC](#vpc-inputs) 1. [AWS Route53 Domains and Certificates](#aws-route53-domains-and-certificate-inputs) -1. [Load Balancer](#load-balancer-inputs) +1. [Load Balancer](#load-balancer-inputs-classic-elb) +1. [Application Load Balancer Inputs (ALB)](#application-load-balancer-inputs-alb) 1. [WAF](#waf) 1. [EFS](#efs-inputs) 1. [RDS](#rds-inputs) @@ -195,7 +196,7 @@ The following inputs can be used as `step.with` keys

-#### **Load Balancer Inputs** +#### **Load Balancer Inputs (Classic ELB)** | Name | Type | Description | |------------------|---------|------------------------------------| | `aws_elb_create` | Boolean | Toggles the creation of a load balancer and map ports to the EC2 instance. Defaults to `false`.| @@ -211,25 +212,56 @@ The following inputs can be used as `step.with` keys

+#### **Application Load Balancer Inputs (ALB)** +| Name | Type | Description | +|------------------|---------|------------------------------------| +| `aws_alb_create` | Boolean | Global toggle for ALB creation. Defaults to `false` | +| `aws_alb_security_group_name` | String | Name of the security group to use for ALB. Defaults to `SG for ${aws_resource_identifier} - ALB`| +| `aws_alb_app_port` | String | Comma-separated list of application ports for ALB target group. If none defined, will use `aws_alb_listen_port` ones. | +| `aws_alb_app_protocol` | String | Comma-separated list of protocols for ALB target group (HTTP/HTTPS). Defaults to `HTTP`. | +| `aws_alb_listen_port` | String | Comma-separated list of listener ports for ALB. Depending on certificate, defaults to `80` or `443`. | +| `aws_alb_listen_protocol` | String | Comma-separated list of listener protocols for ALB (HTTP/HTTPS). Defaults to Depending on certificate, defaults to `HTTP` or `HTTPS`. | +| `aws_alb_redirect_enable` | Boolean | Enable HTTP to HTTPS redirection on ALB. Defaults to `false` | +| `aws_alb_www_to_apex_redirect` | Boolean | Enable www to apex domain redirection on ALB. Defaults to `false` | +| `aws_alb_healthcheck_path` | String | Health check path for ALB target group. Defaults to `"/"` | +| `aws_alb_healthcheck_protocol` | String | Health check protocol for ALB target group. Defaults to `"HTTP"` | +| `aws_alb_ssl_policy` | String | SSL policy for HTTPS listeners. More [here](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/describe-ssl-policies.html) | +| `aws_alb_additional_tags`| String | A list of strings that will be added to created resources. Example: `{"key1": "value1", "key2": "value2"}`. Default `"{}"` | +
+
+ #### **WAF** | Name | Type | Description | |------------------|---------|------------------------------------| | `aws_waf_enable` | Boolean | Enable WAF for load balancer (LB only - NOT ELB). Default is `false` | | `aws_waf_logging_enable`| Boolean | Enable WAF logging to CloudWatch. Default `false` | | `aws_waf_log_retention_days`| Number | CloudWatch log retention period for WAF logs. Default `30` | -| `aws_waf_rule_rate_limit`| String | Rate limit for WAF rules. Default is `2000` | -| `aws_waf_rule_managed_rules`| Boolean | Enable common managed rule groups to use. Default `false` | -| `aws_waf_rule_managed_bad_inputs`| Boolean | Enable managed rule for bad inputs. Default `false` | -| `aws_waf_rule_ip_reputation`| Boolean | Enable managed rule for IP reputation. Default `false` | -| `aws_waf_rule_anonymous_ip`| Boolean | Enable managed rule for anonymous IP. Default `false` | -| `aws_waf_rule_bot_control`| Boolean | Enable managed rule for bot control (costs extra). Default `false` | -| `aws_waf_rule_geo_block_countries`| String | Comma separated list of countries to block. | -| `aws_waf_rule_geo_allow_only_countries`| String | Comma separated list of countries to allow. | -| `aws_waf_rule_sqli`| Boolean | Enable managed rule for SQL injection. Default `false` | -| `aws_waf_rule_linux`| Boolean | Enable managed rule for Linux. Default `false` | -| `aws_waf_rule_unix`| Boolean | Enable managed rule for Unix. Default `false` | -| `aws_waf_rule_admin_protection`| Boolean | Enable managed rule for admin protection. Default `false` | -| `aws_waf_rule_user_arn`| String | String of the user created ARN set of rules. | +| `aws_waf_rule_rate_limit`| String | Rate limit for WAF rules. Default is `2000`. | +| `aws_waf_rule_rate_limit_priority` | Number | Priority for rate limit rule. Defaults to `10`. | +| `aws_waf_rule_managed_rules` | Boolean | Enable common managed rule groups to use. Defaults to `false`. | +| `aws_waf_rule_managed_rules_priority` | Number | Priority for managed rules. Defaults to `20`. | +| `aws_waf_rule_managed_bad_inputs` | Boolean | Enable managed rule for bad inputs. Defaults to `false`. | +| `aws_waf_rule_managed_bad_inputs_priority` | Number | Priority for bad inputs rule. Defaults to `30`. | +| `aws_waf_rule_ip_reputation` | Boolean | Enable managed rule for IP reputation. Defaults to `false`. | +| `aws_waf_rule_ip_reputation_priority` | Number | Priority for IP reputation rule. Defaults to `40`. | +| `aws_waf_rule_anonymous_ip` | Boolean | Enable managed rule for anonymous IP. Defaults to `false`. | +| `aws_waf_rule_anonymous_ip_priority` | Number | Priority for anonymous IP rule. Defaults to `50`. | +| `aws_waf_rule_bot_control` | Boolean | Enable managed rule for bot control (costs extra). Defaults to `false`. | +| `aws_waf_rule_bot_control_priority` | Number | Priority for bot control rule. Defaults to `60`. | +| `aws_waf_rule_geo_block_countries` | String | Comma separated list of countries to block. Defaults to ``. | +| `aws_waf_rule_geo_block_countries_priority` | Number | Priority for geo block countries rule. Defaults to `70`. | +| `aws_waf_rule_geo_allow_only_countries` | String | Comma separated list of countries to allow. Defaults to ``. | +| `aws_waf_rule_geo_allow_only_countries_priority` | Number | Priority for geo allow only countries rule. Defaults to `75`. | +| `aws_waf_rule_sqli` | Boolean | Enable managed rule for SQL injection. Defaults to `false`. | +| `aws_waf_rule_sqli_priority` | Number | Priority for SQL injection rule. Defaults to `85`. | +| `aws_waf_rule_linux` | Boolean | Enable managed rule for Linux. Defaults to `false`. | +| `aws_waf_rule_linux_priority` | Number | Priority for Linux rule. Defaults to `90`. | +| `aws_waf_rule_unix` | Boolean | Enable managed rule for Unix. Defaults to `false`. | +| `aws_waf_rule_unix_priority` | Number | Priority for Unix rule. Defaults to `95`. | +| `aws_waf_rule_admin_protection` | Boolean | Enable managed rule for admin protection. Defaults to `false`. | +| `aws_waf_rule_admin_protection_priority` | Number | Priority for admin protection rule. Defaults to `100`. | +| `aws_waf_rule_user_arn` | String | ARN of the user rule. Defaults to ``. | +| `aws_waf_rule_user_arn_priority` | Number | Priority for user ARN rule. Defaults to `80`. | | `aws_waf_additional_tags`| String | A list of strings that will be added to created resources. Default `"{}"` |

diff --git a/action.yaml b/action.yaml index 7caf8b74..839bdec4 100644 --- a/action.yaml +++ b/action.yaml @@ -280,6 +280,53 @@ inputs: description: 'A JSON object of additional tags that will be included on created resources. Example: `{"key1": "value1", "key2": "value2"}`' required: false + # AWS ALB + aws_alb_create: + description: "Global toggle for ALB creation" + required: false + aws_alb_security_group_name: + description: "Name of the security group to use for ALB" + required: false + aws_alb_app_port: + description: "Comma-separated list of application ports for ALB target group" + required: false + aws_alb_app_protocol: + description: "Comma-separated list of protocols for ALB target group (HTTP/HTTPS)" + required: false + aws_alb_listen_port: + description: "Comma-separated list of listener ports for ALB" + required: false + aws_alb_listen_protocol: + description: "Comma-separated list of listener protocols for ALB (HTTP/HTTPS)" + required: false + aws_alb_redirect_enable: + description: "Enable HTTP to HTTPS redirection on ALB" + required: false + aws_alb_www_to_apex_redirect: + description: 'Enable www to apex domain redirection on ALB' + required: false + aws_alb_healthcheck_path: + description: "Health check path for ALB target group" + required: false + aws_alb_healthcheck_protocol: + description: "Health check protocol for ALB target group" + required: false + aws_alb_ssl_policy: + description: "SSL policy for HTTPS listeners" + required: false + aws_alb_access_log_enabled: + description: "Enable ALB access logs" + required: false + aws_alb_access_log_bucket_name: + description: "S3 bucket name to store the ALB access logs" + required: false + aws_alb_access_log_expire: + description: "Delete the access logs after this amount of days" + required: false + aws_alb_additional_tags: + description: 'A JSON object of additional tags that will be included on created resources. Example: `{"key1": "value1", "key2": "value2"}`' + required: false + # AWS WAF aws_waf_enable: description: 'Enable WAF for load balancer.' @@ -293,42 +340,81 @@ inputs: aws_waf_rule_rate_limit: description: 'Rate limit for WAF rules.' required: false + aws_waf_rule_rate_limit_priority: + description: 'Priority for rate limit rule.' + required: false aws_waf_rule_managed_rules: description: 'Enable common managed rule groups to use.' required: false + aws_waf_rule_managed_rules_priority: + description: 'Priority for managed rules group.' + required: false aws_waf_rule_managed_bad_inputs: description: 'Enable managed rule for bad inputs.' required: false + aws_waf_rule_managed_bad_inputs_priority: + description: 'Priority for bad inputs managed rule.' + required: false aws_waf_rule_ip_reputation: description: 'Enable managed rule for IP reputation.' required: false + aws_waf_rule_ip_reputation_priority: + description: 'Priority for IP reputation managed rule.' + required: false aws_waf_rule_anonymous_ip: description: 'Enable managed rule for anonymous IP.' required: false + aws_waf_rule_anonymous_ip_priority: + description: 'Priority for anonymous IP managed rule.' + required: false aws_waf_rule_bot_control: description: 'Enable managed rule for bot control (costs extra).' required: false + aws_waf_rule_bot_control_priority: + description: 'Priority for bot control managed rule.' + required: false aws_waf_rule_geo_block_countries: description: 'Comma separated list of countries to block.' required: false + aws_waf_rule_geo_block_countries_priority: + description: 'Priority for geo block countries managed rule.' + required: false aws_waf_rule_geo_allow_only_countries: description: 'Comma separated list of countries to allow.' required: false + aws_waf_rule_geo_allow_only_countries_priority: + description: 'Priority for geo allow only countries managed rule.' + required: false aws_waf_rule_sqli: description: 'Enable managed rule for SQL injection.' required: false + aws_waf_rule_sqli_priority: + description: 'Priority for SQL injection managed rule.' + required: false aws_waf_rule_linux: description: 'Enable managed rule for Linux.' required: false + aws_waf_rule_linux_priority: + description: 'Priority for Linux managed rule.' + required: false aws_waf_rule_unix: description: 'Enable managed rule for Unix.' required: false + aws_waf_rule_unix_priority: + description: 'Priority for Unix managed rule.' + required: false aws_waf_rule_admin_protection: description: 'Enable managed rule for admin protection.' required: false + aws_waf_rule_admin_protection_priority: + description: 'Priority for admin protection managed rule.' + required: false aws_waf_rule_user_arn: description: 'ARN of the user rule.' required: false + aws_waf_rule_user_arn_priority: + description: 'Priority for user defined rule.' + required: false aws_waf_additional_tags: description: 'A JSON object of additional tags that will be included on created resources. Example: `{"key1": "value1", "key2": "value2"}`' required: false @@ -1307,24 +1393,54 @@ runs: AWS_ELB_ACCESS_LOG_EXPIRE: ${{ inputs.aws_elb_access_log_expire }} AWS_ELB_ADDITIONAL_TAGS: ${{ inputs.aws_elb_additional_tags }} + # AWS ALB + AWS_ALB_CREATE: ${{ inputs.aws_alb_create }} + AWS_ALB_SECURITY_GROUP_NAME: ${{ inputs.aws_alb_security_group_name }} + AWS_ALB_APP_PORT: ${{ inputs.aws_alb_app_port }} + AWS_ALB_APP_PROTOCOL: ${{ inputs.aws_alb_app_protocol }} + AWS_ALB_LISTEN_PORT: ${{ inputs.aws_alb_listen_port }} + AWS_ALB_LISTEN_PROTOCOL: ${{ inputs.aws_alb_listen_protocol }} + AWS_ALB_REDIRECT_ENABLE: ${{ inputs.aws_alb_redirect_enable }} + AWS_ALB_WWW_TO_APEX_REDIRECT: ${{ inputs.aws_alb_www_to_apex_redirect }} + AWS_ALB_HEALTHCHECK_PATH: ${{ inputs.aws_alb_healthcheck_path }} + AWS_ALB_HEALTHCHECK_PROTOCOL: ${{ inputs.aws_alb_healthcheck_protocol }} + AWS_ALB_SSL_POLICY: ${{ inputs.aws_alb_ssl_policy }} + AWS_ALB_ACCESS_LOG_ENABLED: ${{ inputs.aws_alb_access_log_enabled }} + AWS_ALB_ACCESS_LOG_BUCKET_NAME: ${{ inputs.aws_alb_access_log_bucket_name }} + AWS_ALB_ACCESS_LOG_EXPIRE: ${{ inputs.aws_alb_access_log_expire }} + AWS_ALB_ADDITIONAL_TAGS: ${{ inputs.aws_alb_additional_tags }} + # AWS WAF AWS_WAF_ENABLE: ${{ inputs.aws_waf_enable }} AWS_WAF_LOGGING_ENABLE: ${{ inputs.aws_waf_logging_enable }} AWS_WAF_LOG_RETENTION_DAYS: ${{ inputs.aws_waf_log_retention_days }} AWS_WAF_ADDITIONAL_TAGS: ${{ inputs.aws_waf_additional_tags }} AWS_WAF_RULE_RATE_LIMIT: ${{ inputs.aws_waf_rule_rate_limit }} + AWS_WAF_RULE_RATE_LIMIT_PRIORITY: ${{ inputs.aws_waf_rule_rate_limit_priority }} AWS_WAF_RULE_MANAGED_RULES: ${{ inputs.aws_waf_rule_managed_rules }} + AWS_WAF_RULE_MANAGED_RULES_PRIORITY: ${{ inputs.aws_waf_rule_managed_rules_priority }} AWS_WAF_RULE_MANAGED_BAD_INPUTS: ${{ inputs.aws_waf_rule_managed_bad_inputs }} + AWS_WAF_RULE_MANAGED_BAD_INPUTS_PRIORITY: ${{ inputs.aws_waf_rule_managed_bad_inputs_priority }} AWS_WAF_RULE_IP_REPUTATION: ${{ inputs.aws_waf_rule_ip_reputation }} + AWS_WAF_RULE_IP_REPUTATION_PRIORITY: ${{ inputs.aws_waf_rule_ip_reputation_priority }} AWS_WAF_RULE_ANONYMOUS_IP: ${{ inputs.aws_waf_rule_anonymous_ip }} + AWS_WAF_RULE_ANONYMOUS_IP_PRIORITY: ${{ inputs.aws_waf_rule_anonymous_ip_priority }} AWS_WAF_RULE_BOT_CONTROL: ${{ inputs.aws_waf_rule_bot_control }} + AWS_WAF_RULE_BOT_CONTROL_PRIORITY: ${{ inputs.aws_waf_rule_bot_control_priority }} AWS_WAF_RULE_GEO_BLOCK_COUNTRIES: ${{ inputs.aws_waf_rule_geo_block_countries }} + AWS_WAF_RULE_GEO_BLOCK_COUNTRIES_PRIORITY: ${{ inputs.aws_waf_rule_geo_block_countries_priority }} AWS_WAF_RULE_GEO_ALLOW_ONLY_COUNTRIES: ${{ inputs.aws_waf_rule_geo_allow_only_countries }} + AWS_WAF_RULE_GEO_ALLOW_ONLY_COUNTRIES_PRIORITY: ${{ inputs.aws_waf_rule_geo_allow_only_countries_priority }} AWS_WAF_RULE_USER_ARN: ${{ inputs.aws_waf_rule_user_arn }} + AWS_WAF_RULE_USER_ARN_PRIORITY: ${{ inputs.aws_waf_rule_user_arn_priority }} AWS_WAF_RULE_SQLI: ${{ inputs.aws_waf_rule_sqli }} + AWS_WAF_RULE_SQLI_PRIORITY: ${{ inputs.aws_waf_rule_sqli_priority }} AWS_WAF_RULE_LINUX: ${{ inputs.aws_waf_rule_linux }} + AWS_WAF_RULE_LINUX_PRIORITY: ${{ inputs.aws_waf_rule_linux_priority }} AWS_WAF_RULE_UNIX: ${{ inputs.aws_waf_rule_unix }} + AWS_WAF_RULE_UNIX_PRIORITY: ${{ inputs.aws_waf_rule_unix_priority }} AWS_WAF_RULE_ADMIN_PROTECTION: ${{ inputs.aws_waf_rule_admin_protection }} + AWS_WAF_RULE_ADMIN_PROTECTION_PRIORITY: ${{ inputs.aws_waf_rule_admin_protection_priority }} # AWS EFS AWS_EFS_CREATE: ${{ inputs.aws_efs_create }} diff --git a/operations/_scripts/generate/generate_provider.sh b/operations/_scripts/generate/generate_provider.sh index 783f2f6e..0220f142 100644 --- a/operations/_scripts/generate/generate_provider.sh +++ b/operations/_scripts/generate/generate_provider.sh @@ -74,6 +74,6 @@ provider \"kubernetes\" { }" >> "${GITHUB_ACTION_PATH}/operations/deployment/terraform/$1/bitovi_provider.tf" } -generate_provider_aws aws ec2,r53,elb,efs,vpc,rds,aurora,ecs,db_proxy,redis,eks,ecr,waf +generate_provider_aws aws ec2,r53,elb,efs,vpc,rds,aurora,ecs,db_proxy,redis,eks,ecr,waf,lb echo "Done with generate_provider.sh" \ No newline at end of file diff --git a/operations/_scripts/generate/generate_vars_terraform.sh b/operations/_scripts/generate/generate_vars_terraform.sh index f36fd40e..26a329c3 100644 --- a/operations/_scripts/generate/generate_vars_terraform.sh +++ b/operations/_scripts/generate/generate_vars_terraform.sh @@ -132,6 +132,25 @@ if [[ $(alpha_only "$AWS_ELB_CREATE") == true ]]; then aws_elb_additional_tags=$(generate_var aws_elb_additional_tags $AWS_ELB_ADDITIONAL_TAGS) fi +#-- AWS ALB --# +if [[ $(alpha_only "$AWS_ALB_CREATE") == true ]]; then + aws_alb_create=$(generate_var aws_alb_create $AWS_ALB_CREATE) + aws_alb_security_group_name=$(generate_var aws_alb_security_group_name $AWS_ALB_SECURITY_GROUP_NAME) + aws_alb_app_port=$(generate_var aws_alb_app_port $AWS_ALB_APP_PORT) + aws_alb_app_protocol=$(generate_var aws_alb_app_protocol $AWS_ALB_APP_PROTOCOL) + aws_alb_listen_port=$(generate_var aws_alb_listen_port $AWS_ALB_LISTEN_PORT) + aws_alb_listen_protocol=$(generate_var aws_alb_listen_protocol $AWS_ALB_LISTEN_PROTOCOL) + aws_alb_redirect_enable=$(generate_var aws_alb_redirect_enable $AWS_ALB_REDIRECT_ENABLE) + aws_alb_www_to_apex_redirect=$(generate_var aws_alb_www_to_apex_redirect $AWS_ALB_WWW_TO_APEX_REDIRECT) + aws_alb_healthcheck_path=$(generate_var aws_alb_healthcheck_path $AWS_ALB_HEALTHCHECK_PATH) + aws_alb_healthcheck_protocol=$(generate_var aws_alb_healthcheck_protocol $AWS_ALB_HEALTHCHECK_PROTOCOL) + aws_alb_ssl_policy=$(generate_var aws_alb_ssl_policy $AWS_ALB_SSL_POLICY) + aws_alb_access_log_enabled=$(generate_var aws_alb_access_log_enabled $AWS_ALB_ACCESS_LOG_ENABLED) + aws_alb_access_log_bucket_name=$(generate_var aws_alb_access_log_bucket_name $AWS_ALB_ACCESS_LOG_BUCKET_NAME) + aws_alb_access_log_expire=$(generate_var aws_alb_access_log_expire $AWS_ALB_ACCESS_LOG_EXPIRE) + aws_alb_additional_tags=$(generate_var aws_alb_additional_tags $AWS_ALB_ADDITIONAL_TAGS) +fi + #-- AWS WAF --# if [[ $(alpha_only "$AWS_WAF_ENABLE") == true ]]; then aws_waf_enable=$(generate_var aws_waf_enable $AWS_WAF_ENABLE) @@ -139,18 +158,31 @@ if [[ $(alpha_only "$AWS_WAF_ENABLE") == true ]]; then aws_waf_log_retention_days=$(generate_var aws_waf_log_retention_days $AWS_WAF_LOG_RETENTION_DAYS) aws_waf_additional_tags=$(generate_var aws_waf_additional_tags $AWS_WAF_ADDITIONAL_TAGS) aws_waf_rule_rate_limit=$(generate_var aws_waf_rule_rate_limit $AWS_WAF_RULE_RATE_LIMIT) + aws_waf_rule_rate_limit_priority=$(generate_var aws_waf_rule_rate_limit_priority $AWS_WAF_RULE_RATE_LIMIT_PRIORITY) aws_waf_rule_managed_rules=$(generate_var aws_waf_rule_managed_rules $AWS_WAF_RULE_MANAGED_RULES) + aws_waf_rule_managed_rules_priority=$(generate_var aws_waf_rule_managed_rules_priority $AWS_WAF_RULE_MANAGED_RULES_PRIORITY) aws_waf_rule_managed_bad_inputs=$(generate_var aws_waf_rule_managed_bad_inputs $AWS_WAF_RULE_MANAGED_BAD_INPUTS) + aws_waf_rule_managed_bad_inputs_priority=$(generate_var aws_waf_rule_managed_bad_inputs_priority $AWS_WAF_RULE_MANAGED_BAD_INPUTS_PRIORITY) aws_waf_rule_ip_reputation=$(generate_var aws_waf_rule_ip_reputation $AWS_WAF_RULE_IP_REPUTATION) + aws_waf_rule_ip_reputation_priority=$(generate_var aws_waf_rule_ip_reputation_priority $AWS_WAF_RULE_IP_REPUTATION_PRIORITY) aws_waf_rule_anonymous_ip=$(generate_var aws_waf_rule_anonymous_ip $AWS_WAF_RULE_ANONYMOUS_IP) + aws_waf_rule_anonymous_ip_priority=$(generate_var aws_waf_rule_anonymous_ip_priority $AWS_WAF_RULE_ANONYMOUS_IP_PRIORITY) aws_waf_rule_bot_control=$(generate_var aws_waf_rule_bot_control $AWS_WAF_RULE_BOT_CONTROL) + aws_waf_rule_bot_control_priority=$(generate_var aws_waf_rule_bot_control_priority $AWS_WAF_RULE_BOT_CONTROL_PRIORITY) aws_waf_rule_geo_block_countries=$(generate_var aws_waf_rule_geo_block_countries $AWS_WAF_RULE_GEO_BLOCK_COUNTRIES) + aws_waf_rule_geo_block_countries_priority=$(generate_var aws_waf_rule_geo_block_countries_priority $AWS_WAF_RULE_GEO_BLOCK_COUNTRIES_PRIORITY) aws_waf_rule_geo_allow_only_countries=$(generate_var aws_waf_rule_geo_allow_only_countries $AWS_WAF_RULE_GEO_ALLOW_ONLY_COUNTRIES) + aws_waf_rule_geo_allow_only_countries_priority=$(generate_var aws_waf_rule_geo_allow_only_countries_priority $AWS_WAF_RULE_GEO_ALLOW_ONLY_COUNTRIES_PRIORITY) aws_waf_rule_user_arn=$(generate_var aws_waf_rule_user_arn $AWS_WAF_RULE_USER_ARN) + aws_waf_rule_user_arn_priority=$(generate_var aws_waf_rule_user_arn_priority $AWS_WAF_RULE_USER_ARN_PRIORITY) aws_waf_rule_sqli=$(generate_var aws_waf_rule_sqli $AWS_WAF_RULE_SQLI) + aws_waf_rule_sqli_priority=$(generate_var aws_waf_rule_sqli_priority $AWS_WAF_RULE_SQLI_PRIORITY) aws_waf_rule_linux=$(generate_var aws_waf_rule_linux $AWS_WAF_RULE_LINUX) + aws_waf_rule_linux_priority=$(generate_var aws_waf_rule_linux_priority $AWS_WAF_RULE_LINUX_PRIORITY) aws_waf_rule_unix=$(generate_var aws_waf_rule_unix $AWS_WAF_RULE_UNIX) + aws_waf_rule_unix_priority=$(generate_var aws_waf_rule_unix_priority $AWS_WAF_RULE_UNIX_PRIORITY) aws_waf_rule_admin_protection=$(generate_var aws_waf_rule_admin_protection $AWS_WAF_RULE_ADMIN_PROTECTION) + aws_waf_rule_admin_protection_priority=$(generate_var aws_waf_rule_admin_protection_priority $AWS_WAF_RULE_ADMIN_PROTECTION_PRIORITY) fi #-- AWS EFS --# @@ -509,24 +541,54 @@ $aws_elb_access_log_expire $aws_elb_access_log_bucket_name $aws_elb_additional_tags +#-- ALB --# +$aws_alb_create +$aws_alb_security_group_name +$aws_alb_app_port +$aws_alb_app_protocol +$aws_alb_listen_port +$aws_alb_listen_protocol +$aws_alb_redirect_enable +$aws_alb_www_to_apex_redirect +$aws_alb_healthcheck_path +$aws_alb_healthcheck_protocol +$aws_alb_ssl_policy +$aws_alb_access_log_enabled +$aws_alb_access_log_bucket_name +$aws_alb_access_log_expire +$aws_alb_additional_tags + #-- WAF --# $aws_waf_enable $aws_waf_logging_enable $aws_waf_log_retention_days $aws_waf_additional_tags $aws_waf_rule_rate_limit +$aws_waf_rule_rate_limit_priority $aws_waf_rule_managed_rules +$aws_waf_rule_managed_rules_priority $aws_waf_rule_managed_bad_inputs +$aws_waf_rule_managed_bad_inputs_priority $aws_waf_rule_ip_reputation +$aws_waf_rule_ip_reputation_priority $aws_waf_rule_anonymous_ip +$aws_waf_rule_anonymous_ip_priority $aws_waf_rule_bot_control +$aws_waf_rule_bot_control_priority $aws_waf_rule_geo_block_countries +$aws_waf_rule_geo_block_countries_priority $aws_waf_rule_geo_allow_only_countries +$aws_waf_rule_geo_allow_only_countries_priority $aws_waf_rule_user_arn +$aws_waf_rule_user_arn_priority $aws_waf_rule_sqli +$aws_waf_rule_sqli_priority $aws_waf_rule_linux +$aws_waf_rule_linux_priority $aws_waf_rule_unix +$aws_waf_rule_unix_priority $aws_waf_rule_admin_protection +$aws_waf_rule_admin_protection_priority #-- EFS --# $aws_efs_enable diff --git a/operations/deployment/terraform/aws/aws_variables.tf b/operations/deployment/terraform/aws/aws_variables.tf index 21a0a079..1ed68368 100644 --- a/operations/deployment/terraform/aws/aws_variables.tf +++ b/operations/deployment/terraform/aws/aws_variables.tf @@ -327,7 +327,98 @@ variable "aws_elb_additional_tags" { default = "{}" } -# AWS LB +# AWS ALB +variable "aws_alb_create" { + type = bool + description = "Global toggle for ALB creation" + default = false +} + +variable "aws_alb_security_group_name" { + type = string + description = "Name of the security group to use for ALB" + default = "" +} + +variable "aws_alb_app_port" { + type = string + description = "Comma-separated list of application ports for ALB target group" + default = "" +} + +variable "aws_alb_app_protocol" { + type = string + description = "Comma-separated list of protocols for ALB target group (HTTP/HTTPS)" + default = "" +} + +variable "aws_alb_listen_port" { + type = string + description = "Comma-separated list of listener ports for ALB" + default = "" +} + +variable "aws_alb_listen_protocol" { + type = string + description = "Comma-separated list of listener protocols for ALB (HTTP/HTTPS)" + default = "" +} + +variable "aws_alb_redirect_enable" { + type = bool + description = "Enable HTTP to HTTPS redirection on ALB" + default = false +} + +variable "aws_alb_www_to_apex_redirect" { + type = bool + description = "Enable www to apex domain redirection on ALB" + default = false +} + +# Healthcheck +variable "aws_alb_healthcheck_path" { + type = string + description = "Health check path for ALB target group" + default = "/" +} + +variable "aws_alb_healthcheck_protocol" { + type = string + description = "Health check protocol for ALB target group" + default = "HTTP" +} + +variable "aws_alb_ssl_policy" { + type = string + description = "SSL policy for HTTPS listeners" + default = null +} + +# Logging +variable "aws_alb_access_log_enabled" { + type = bool + description = "Enable ALB access logs" + default = false +} + +variable "aws_alb_access_log_bucket_name" { + type = string + description = "S3 bucket name to store the ALB access logs" + default = "" +} + +variable "aws_alb_access_log_expire" { + type = string + description = "Delete the access logs after this amount of days" + default = "90" +} + +variable "aws_alb_additional_tags" { + type = string + description = "A list of strings that will be added to created resources" + default = "{}" +} # AWS WAF variable "aws_waf_enable" { @@ -360,78 +451,156 @@ variable "aws_waf_rule_rate_limit" { default = "2000" } +variable "aws_waf_rule_rate_limit_priority" { + type = number + description = "Priority for rate limit rule" + default = 10 +} + variable "aws_waf_rule_managed_rules" { type = bool description = "Enable common managed rule groups to use" default = false } +variable "aws_waf_rule_managed_rules_priority" { + type = number + description = "Priority for managed rules" + default = 20 +} + variable "aws_waf_rule_managed_bad_inputs" { type = bool description = "Enable managed rule for bad inputs" default = false } +variable "aws_waf_rule_managed_bad_inputs_priority" { + type = number + description = "Priority for bad inputs rule" + default = 30 +} + variable "aws_waf_rule_ip_reputation" { type = bool description = "Enable managed rule for IP reputation" default = false } +variable "aws_waf_rule_ip_reputation_priority" { + type = number + description = "Priority for IP reputation rule" + default = 40 +} + variable "aws_waf_rule_anonymous_ip" { type = bool description = "Enable managed rule for anonymous IP" default = false } +variable "aws_waf_rule_anonymous_ip_priority" { + type = number + description = "Priority for anonymous IP rule" + default = 50 +} + variable "aws_waf_rule_bot_control" { type = bool description = "Enable managed rule for bot control (costs extra)" default = false } +variable "aws_waf_rule_bot_control_priority" { + type = number + description = "Priority for bot control rule" + default = 60 +} + variable "aws_waf_rule_geo_block_countries" { type = string description = "Comma separated list of countries to block" default = "" } +variable "aws_waf_rule_geo_block_countries_priority" { + type = number + description = "Priority for geo block countries rule" + default = 70 +} + variable "aws_waf_rule_geo_allow_only_countries" { type = string description = "Comma separated list of countries to allow" default = "" } +variable "aws_waf_rule_geo_allow_only_countries_priority" { + type = number + description = "Priority for geo allow only countries rule" + default = 75 +} + variable "aws_waf_rule_sqli" { type = bool description = "Enable managed rule for SQL injection" default = false } +variable "aws_waf_rule_sqli_priority" { + type = number + description = "Priority for SQL injection rule" + default = 85 +} + variable "aws_waf_rule_linux" { type = bool description = "Enable managed rule for Linux" default = false } +variable "aws_waf_rule_linux_priority" { + type = number + description = "Priority for Linux rule" + default = 90 +} + variable "aws_waf_rule_unix" { type = bool description = "Enable managed rule for Unix" default = false } +variable "aws_waf_rule_unix_priority" { + type = number + description = "Priority for Unix rule" + default = 95 +} + variable "aws_waf_rule_admin_protection" { type = bool description = "Enable managed rule for admin protection" default = false } +variable "aws_waf_rule_admin_protection_priority" { + type = number + description = "Priority for admin protection rule" + default = 100 +} + variable "aws_waf_rule_user_arn" { type = string description = "ARN of the user rule" default = "" } +variable "aws_waf_rule_user_arn_priority" { + type = number + description = "Priority for user ARN rule" + default = 80 +} + # AWS EFS ### This variable is hidden for the end user. Is built in deploy.sh based on the next 3 variables. diff --git a/operations/deployment/terraform/aws/bitovi_main.tf b/operations/deployment/terraform/aws/bitovi_main.tf index 628705b0..4501de1c 100644 --- a/operations/deployment/terraform/aws/bitovi_main.tf +++ b/operations/deployment/terraform/aws/bitovi_main.tf @@ -62,7 +62,7 @@ module "efs_to_ec2_sg" { module "aws_certificates" { source = "../modules/aws/certificates" - count = (var.aws_ec2_instance_create || var.aws_ecs_enable) && var.aws_r53_enable && var.aws_r53_domain_name != "" ? 1 : 0 + count = (var.aws_ec2_instance_create || var.aws_ecs_enable) && var.aws_r53_enable_cert && var.aws_r53_cert_arn == "" && var.aws_r53_domain_name != "" ? 1 : 0 # Cert aws_r53_cert_arn = var.aws_r53_cert_arn aws_r53_create_root_cert = var.aws_r53_create_root_cert @@ -85,12 +85,12 @@ module "aws_route53" { aws_r53_domain_name = var.aws_r53_domain_name aws_r53_sub_domain_name = var.aws_r53_sub_domain_name aws_r53_root_domain_deploy = var.aws_r53_root_domain_deploy - aws_r53_enable_cert = var.aws_r53_enable_cert + aws_r53_enable_cert = var.aws_r53_enable_cert ? var.aws_r53_cert_arn != "" ? true : try(module.aws_certificates[0].selected_arn, "") != "" ? true : false : false # ELB - aws_elb_dns_name = try(module.aws_elb[0].aws_elb_dns_name, "") - aws_elb_zone_id = try(module.aws_elb[0].aws_elb_zone_id, "") + aws_elb_dns_name = try(module.aws_lb[0].aws_alb_dns_name, module.aws_elb[0].aws_elb_dns_name, module.ec2[0].instance_public_ip, "") + aws_elb_zone_id = try(module.aws_lb[0].aws_alb_zone_id, module.aws_elb[0].aws_elb_zone_id, "", "") # Certs - aws_certificates_selected_arn = var.aws_r53_enable_cert && var.aws_r53_domain_name != "" ? module.aws_certificates[0].selected_arn : "" + aws_certificates_selected_arn = var.aws_r53_enable_cert ? try(module.aws_certificates[0].selected_arn, var.aws_r53_cert_arn) : "" # Others fqdn_provided = local.fqdn_provided @@ -118,18 +118,100 @@ module "aws_elb" { aws_instance_server_id = module.ec2[0].aws_instance_server_id aws_elb_target_sg_id = module.ec2[0].aws_security_group_ec2_sg_id # Certs - aws_certificates_selected_arn = var.aws_r53_enable_cert && var.aws_r53_domain_name != "" ? module.aws_certificates[0].selected_arn : "" + aws_certificates_selected_arn = var.aws_r53_enable_cert ? try(module.aws_certificates[0].selected_arn, var.aws_r53_cert_arn) : "" # Others aws_resource_identifier = var.aws_resource_identifier aws_resource_identifier_supershort = var.aws_resource_identifier_supershort # Module dependencies - depends_on = [module.vpc, module.ec2] + #depends_on = [module.vpc, module.ec2] providers = { aws = aws.elb } } +module "aws_lb" { + source = "../modules/aws/lb" + count = var.aws_ec2_instance_create && var.aws_alb_create ? 1 : 0 + # ALB Values + aws_alb_security_group_name = var.aws_alb_security_group_name + aws_alb_app_port = var.aws_alb_app_port + aws_alb_app_protocol = var.aws_alb_app_protocol + aws_alb_listen_port = var.aws_alb_listen_port + aws_alb_listen_protocol = var.aws_alb_listen_protocol + aws_alb_redirect_enable = var.aws_alb_redirect_enable + aws_alb_www_to_apex_redirect = var.aws_alb_www_to_apex_redirect + aws_alb_healthcheck_path = var.aws_alb_healthcheck_path + aws_alb_healthcheck_protocol = var.aws_alb_healthcheck_protocol + aws_alb_ssl_policy = var.aws_alb_ssl_policy + # Logging + aws_alb_access_log_enabled = var.aws_alb_access_log_enabled + aws_alb_access_log_bucket_name = var.aws_alb_access_log_bucket_name + aws_alb_access_log_expire = var.aws_alb_access_log_expire + # EC2 + aws_vpc_selected_id = module.vpc.aws_selected_vpc_id + aws_vpc_subnet_selected = module.vpc.aws_selected_vpc_subnets #module.vpc.aws_vpc_subnet_selected + aws_instance_server_id = module.ec2[0].aws_instance_server_id + aws_alb_target_sg_id = module.ec2[0].aws_security_group_ec2_sg_id + aws_r53_domain_name = var.aws_r53_domain_name + # Certs + aws_certificate_enabled = var.aws_ec2_instance_create && var.aws_r53_enable_cert && (var.aws_r53_cert_arn != "" || var.aws_r53_domain_name != "") ? true : false #var.aws_r53_enable_cert ? var.aws_r53_cert_arn != "" ? true : try(module.aws_certificates[0].selected_arn, "") != "" ? true : false : false + aws_certificates_selected_arn = try(module.aws_certificates[0].selected_arn, var.aws_r53_cert_arn, "") + + # Others + aws_resource_identifier = var.aws_resource_identifier + aws_resource_identifier_supershort = var.aws_resource_identifier_supershort + # Module dependencies + depends_on = [module.aws_certificates] + + providers = { + aws = aws.lb + } +} + +module "aws_waf_ec2_alb" { + source = "../modules/aws/waf" + count = var.aws_waf_enable && var.aws_ec2_instance_create && var.aws_alb_create ? 1 : 0 + aws_waf_enable = var.aws_waf_enable + aws_waf_logging_enable = var.aws_waf_logging_enable + aws_waf_log_retention_days = var.aws_waf_log_retention_days + aws_resource_identifier = var.aws_resource_identifier + # Rules + aws_waf_rule_rate_limit = var.aws_waf_rule_rate_limit + aws_waf_rule_rate_limit_priority = var.aws_waf_rule_rate_limit_priority + aws_waf_rule_managed_rules = var.aws_waf_rule_managed_rules + aws_waf_rule_managed_rules_priority = var.aws_waf_rule_managed_rules_priority + aws_waf_rule_managed_bad_inputs = var.aws_waf_rule_managed_bad_inputs + aws_waf_rule_managed_bad_inputs_priority = var.aws_waf_rule_managed_bad_inputs_priority + aws_waf_rule_ip_reputation = var.aws_waf_rule_ip_reputation + aws_waf_rule_ip_reputation_priority = var.aws_waf_rule_ip_reputation_priority + aws_waf_rule_anonymous_ip = var.aws_waf_rule_anonymous_ip + aws_waf_rule_anonymous_ip_priority = var.aws_waf_rule_anonymous_ip_priority + aws_waf_rule_bot_control = var.aws_waf_rule_bot_control + aws_waf_rule_bot_control_priority = var.aws_waf_rule_bot_control_priority + aws_waf_rule_geo_block_countries = var.aws_waf_rule_geo_block_countries + aws_waf_rule_geo_block_countries_priority = var.aws_waf_rule_geo_block_countries_priority + aws_waf_rule_geo_allow_only_countries = var.aws_waf_rule_geo_allow_only_countries + aws_waf_rule_geo_allow_only_countries_priority = var.aws_waf_rule_geo_allow_only_countries_priority + aws_waf_rule_user_arn = var.aws_waf_rule_user_arn + aws_waf_rule_user_arn_priority = var.aws_waf_rule_user_arn_priority + aws_waf_rule_sqli = var.aws_waf_rule_sqli + aws_waf_rule_sqli_priority = var.aws_waf_rule_sqli_priority + aws_waf_rule_linux = var.aws_waf_rule_linux + aws_waf_rule_linux_priority = var.aws_waf_rule_linux_priority + aws_waf_rule_unix = var.aws_waf_rule_unix + aws_waf_rule_unix_priority = var.aws_waf_rule_unix_priority + aws_waf_rule_admin_protection = var.aws_waf_rule_admin_protection + aws_waf_rule_admin_protection_priority = var.aws_waf_rule_admin_protection_priority + # Incoming + aws_lb_resource_arn = module.aws_lb[0].aws_lb_resource_arn + # Others + #depends_on = [module.aws_lb] + providers = { + aws = aws.waf + } +} + module "efs" { source = "../modules/aws/efs" count = var.aws_efs_enable ? 1 : 0 @@ -516,8 +598,8 @@ module "aws_ecs" { aws_selected_subnets = module.vpc.aws_selected_vpc_subnets # Others aws_r53_domain_name = var.aws_r53_enable && var.aws_r53_domain_name != "" ? var.aws_r53_domain_name : "" - aws_certificate_enabled = var.aws_r53_enable_cert && length(module.aws_certificates) > 0 ? true : false - aws_certificates_selected_arn = var.aws_r53_enable_cert && var.aws_r53_domain_name != "" ? module.aws_certificates[0].selected_arn : "" + aws_certificate_enabled = var.aws_r53_enable_cert + aws_certificates_selected_arn = var.aws_r53_enable_cert ? try(module.aws_certificates[0].selected_arn, var.aws_r53_cert_arn) : "" aws_resource_identifier = var.aws_resource_identifier aws_resource_identifier_supershort = var.aws_resource_identifier_supershort app_repo_name = var.app_repo_name @@ -540,7 +622,7 @@ module "aws_route53_ecs" { aws_elb_dns_name = module.aws_ecs[0].load_balancer_dns aws_elb_zone_id = module.aws_ecs[0].load_balancer_zone_id # Certs - aws_certificates_selected_arn = var.aws_r53_enable_cert && var.aws_r53_domain_name != "" ? module.aws_certificates[0].selected_arn : "" + aws_certificates_selected_arn = var.aws_r53_enable_cert ? try(module.aws_certificates[0].selected_arn, var.aws_r53_cert_arn) : "" # Others fqdn_provided = local.fqdn_provided depends_on = [module.aws_certificates] @@ -557,19 +639,32 @@ module "aws_waf_ecs" { aws_waf_log_retention_days = var.aws_waf_log_retention_days aws_resource_identifier = var.aws_resource_identifier # Rules - aws_waf_rule_rate_limit = var.aws_waf_rule_rate_limit - aws_waf_rule_managed_rules = var.aws_waf_rule_managed_rules - aws_waf_rule_managed_bad_inputs = var.aws_waf_rule_managed_bad_inputs - aws_waf_rule_ip_reputation = var.aws_waf_rule_ip_reputation - aws_waf_rule_anonymous_ip = var.aws_waf_rule_anonymous_ip - aws_waf_rule_bot_control = var.aws_waf_rule_bot_control - aws_waf_rule_geo_block_countries = var.aws_waf_rule_geo_block_countries - aws_waf_rule_geo_allow_only_countries = var.aws_waf_rule_geo_allow_only_countries - aws_waf_rule_user_arn = var.aws_waf_rule_user_arn - aws_waf_rule_sqli = var.aws_waf_rule_sqli - aws_waf_rule_linux = var.aws_waf_rule_linux - aws_waf_rule_unix = var.aws_waf_rule_unix - aws_waf_rule_admin_protection = var.aws_waf_rule_admin_protection + aws_waf_rule_rate_limit = var.aws_waf_rule_rate_limit + aws_waf_rule_rate_limit_priority = var.aws_waf_rule_rate_limit_priority + aws_waf_rule_managed_rules = var.aws_waf_rule_managed_rules + aws_waf_rule_managed_rules_priority = var.aws_waf_rule_managed_rules_priority + aws_waf_rule_managed_bad_inputs = var.aws_waf_rule_managed_bad_inputs + aws_waf_rule_managed_bad_inputs_priority = var.aws_waf_rule_managed_bad_inputs_priority + aws_waf_rule_ip_reputation = var.aws_waf_rule_ip_reputation + aws_waf_rule_ip_reputation_priority = var.aws_waf_rule_ip_reputation_priority + aws_waf_rule_anonymous_ip = var.aws_waf_rule_anonymous_ip + aws_waf_rule_anonymous_ip_priority = var.aws_waf_rule_anonymous_ip_priority + aws_waf_rule_bot_control = var.aws_waf_rule_bot_control + aws_waf_rule_bot_control_priority = var.aws_waf_rule_bot_control_priority + aws_waf_rule_geo_block_countries = var.aws_waf_rule_geo_block_countries + aws_waf_rule_geo_block_countries_priority = var.aws_waf_rule_geo_block_countries_priority + aws_waf_rule_geo_allow_only_countries = var.aws_waf_rule_geo_allow_only_countries + aws_waf_rule_geo_allow_only_countries_priority = var.aws_waf_rule_geo_allow_only_countries_priority + aws_waf_rule_user_arn = var.aws_waf_rule_user_arn + aws_waf_rule_user_arn_priority = var.aws_waf_rule_user_arn_priority + aws_waf_rule_sqli = var.aws_waf_rule_sqli + aws_waf_rule_sqli_priority = var.aws_waf_rule_sqli_priority + aws_waf_rule_linux = var.aws_waf_rule_linux + aws_waf_rule_linux_priority = var.aws_waf_rule_linux_priority + aws_waf_rule_unix = var.aws_waf_rule_unix + aws_waf_rule_unix_priority = var.aws_waf_rule_unix_priority + aws_waf_rule_admin_protection = var.aws_waf_rule_admin_protection + aws_waf_rule_admin_protection_priority = var.aws_waf_rule_admin_protection_priority # Incoming aws_lb_resource_arn = module.aws_ecs[0].load_balancer_arn # Others @@ -702,6 +797,7 @@ locals { db_proxy_tags = merge(local.default_tags, jsondecode(var.aws_db_proxy_additional_tags)) redis_tags = merge(local.default_tags, jsondecode(var.aws_redis_additional_tags)) waf_tags = merge(local.default_tags, jsondecode(var.aws_waf_additional_tags)) + lb_tags = merge(local.default_tags, jsondecode(var.aws_alb_additional_tags)) eks_vpc_tags = { // This is needed for k8s to use VPC resources @@ -717,13 +813,14 @@ locals { ) : false ) + protocol = var.aws_r53_enable_cert ? var.aws_r53_cert_arn != "" ? "https://" : try(module.aws_certificates[0].selected_arn, "") != "" ? "https://" : "http://" : "http://" create_efs = var.aws_efs_create == true ? true : (var.aws_efs_create_ha == true ? true : false) ec2_public_endpoint = var.aws_ec2_instance_create ? (module.ec2[0].instance_public_dns != null ? module.ec2[0].instance_public_dns : module.ec2[0].instance_public_ip) : null ec2_private_endpoint = var.aws_ec2_instance_create ? (module.ec2[0].instance_private_dns != null ? module.ec2[0].instance_private_dns : module.ec2[0].instance_private_ip) : null - ec2_endpoint = var.aws_ec2_instance_create ? (local.ec2_public_endpoint != null ? "http://${local.ec2_public_endpoint}" : "http://${local.ec2_private_endpoint}") : null - elb_url = try(module.aws_elb[0].aws_elb_dns_name, null) != null ? "http://${module.aws_elb[0].aws_elb_dns_name}" : null + ec2_endpoint = var.aws_ec2_instance_create ? (local.ec2_public_endpoint != null ? "${local.protocol}${local.ec2_public_endpoint}" : "${local.protocol}${local.ec2_private_endpoint}") : null + elb_url = try(module.aws_elb[0].aws_elb_dns_name, null) != null ? "${local.protocol}${module.aws_elb[0].aws_elb_dns_name}" : null + alb_url = try(module.aws_lb[0].aws_alb_dns_name, null) != null ? "${local.protocol}${module.aws_lb[0].aws_alb_dns_name}" : null } - # VPC output "aws_vpc_id" { value = module.vpc.aws_selected_vpc_id @@ -769,13 +866,18 @@ output "aws_elb_dns_name" { value = try(module.aws_elb[0].aws_elb_dns_name, null) } +output "aws_alb_dns_name" { + description = "Public DNS address of the ALB" + value = try(module.aws_lb[0].aws_alb_dns_name, null) +} + output "application_public_dns" { description = "Public DNS address for the application or load balancer public DNS" value = try(module.aws_route53[0].vm_url, null) } output "vm_url" { - value = try(module.aws_route53[0].vm_url, local.elb_url) + value = try(module.aws_route53[0].vm_url, local.alb_url, local.elb_url, local.ec2_endpoint) } # EFS diff --git a/operations/deployment/terraform/modules/aws/certificates/aws_certificates.tf b/operations/deployment/terraform/modules/aws/certificates/aws_certificates.tf index 96e20e67..c1f2d6b6 100644 --- a/operations/deployment/terraform/modules/aws/certificates/aws_certificates.tf +++ b/operations/deployment/terraform/modules/aws/certificates/aws_certificates.tf @@ -5,25 +5,28 @@ data "aws_route53_zone" "selected" { } data "aws_acm_certificate" "issued" { - #count = local.is_enabled_and_valid ? (!var.aws_r53_create_root_cert ? (!var.aws_r53_create_sub_cert ? (var.fqdn_provided ? 1 : 0) : 0) : 0) :0 - for_each = local.is_enabled_and_valid ? { - "domain" : var.aws_r53_domain_name, - "wildcard" : "*.${var.aws_r53_domain_name}" - "sub" : "${var.aws_r53_sub_domain_name}.${var.aws_r53_domain_name}" + for_each = (!var.aws_r53_create_root_cert && !var.aws_r53_create_sub_cert && var.aws_r53_domain_name != "") ? { + "domain" = var.aws_r53_domain_name, + "wildcard" = "*.${var.aws_r53_domain_name}", + "sub" = "${var.aws_r53_sub_domain_name}.${var.aws_r53_domain_name}" } : {} - domain = var.aws_r53_domain_name + domain = each.value + #domain = var.aws_r53_domain_name } # This block will create and validate the root domain and www cert resource "aws_acm_certificate" "root_domain" { - count = local.is_enabled_and_valid ? (var.aws_r53_create_root_cert ? (var.aws_r53_domain_name != "" ? 1 : 0) : 0) : 0 + count = var.aws_r53_domain_name != "" && var.aws_r53_create_root_cert ? 1 : 0 domain_name = var.aws_r53_domain_name subject_alternative_names = ["*.${var.aws_r53_domain_name}", "${var.aws_r53_domain_name}"] validation_method = "DNS" + lifecycle { + create_before_destroy = true + } } resource "aws_route53_record" "root_domain" { - count = local.is_enabled_and_valid ? (var.aws_r53_create_root_cert ? (var.aws_r53_domain_name != "" ? 1 : 0) : 0) : 0 + count = var.aws_r53_domain_name != "" && var.aws_r53_create_root_cert ? 1 : 0 allow_overwrite = true name = tolist(aws_acm_certificate.root_domain[0].domain_validation_options)[0].resource_record_name records = [tolist(aws_acm_certificate.root_domain[0].domain_validation_options)[0].resource_record_value] @@ -33,7 +36,7 @@ resource "aws_route53_record" "root_domain" { } resource "aws_acm_certificate_validation" "root_domain" { - count = local.is_enabled_and_valid ? (var.aws_r53_create_root_cert ? (var.aws_r53_domain_name != "" ? 1 : 0) : 0) : 0 + count = var.aws_r53_domain_name != "" && var.aws_r53_create_root_cert ? 1 : 0 certificate_arn = aws_acm_certificate.root_domain[0].arn validation_record_fqdns = [for record in aws_route53_record.root_domain : record.fqdn] } @@ -41,13 +44,16 @@ resource "aws_acm_certificate_validation" "root_domain" { # This block will create and validate the sub domain cert ONLY resource "aws_acm_certificate" "sub_domain" { - count = local.is_enabled_and_valid ? (var.aws_r53_create_sub_cert ? (var.aws_r53_domain_name != "" ? (var.aws_r53_sub_domain_name != "" ? (var.aws_r53_create_root_cert ? 0 : 1) : 0) : 0) : 0) : 0 + count = var.aws_r53_create_sub_cert && !var.aws_r53_create_root_cert && var.aws_r53_domain_name != "" && var.aws_r53_sub_domain_name != "" ? 1 : 0 domain_name = "${var.aws_r53_sub_domain_name}.${var.aws_r53_domain_name}" validation_method = "DNS" + lifecycle { + create_before_destroy = true + } } resource "aws_route53_record" "sub_domain" { - count = local.is_enabled_and_valid ? (var.aws_r53_create_sub_cert ? (var.aws_r53_domain_name != "" ? (var.aws_r53_sub_domain_name != "" ? (var.aws_r53_create_root_cert ? 0 : 1) : 0) : 0) : 0) : 0 + count = var.aws_r53_create_sub_cert && !var.aws_r53_create_root_cert && var.aws_r53_domain_name != "" && var.aws_r53_sub_domain_name != "" ? 1 : 0 allow_overwrite = true name = tolist(aws_acm_certificate.sub_domain[0].domain_validation_options)[0].resource_record_name records = [tolist(aws_acm_certificate.sub_domain[0].domain_validation_options)[0].resource_record_value] @@ -57,36 +63,21 @@ resource "aws_route53_record" "sub_domain" { } resource "aws_acm_certificate_validation" "sub_domain" { - count = local.is_enabled_and_valid ? (var.aws_r53_create_sub_cert ? (var.aws_r53_domain_name != "" ? (var.aws_r53_create_root_cert ? 0 : 1) : 0) : 0) : 0 + count = var.aws_r53_create_sub_cert && !var.aws_r53_create_root_cert && var.aws_r53_domain_name != "" && var.aws_r53_sub_domain_name != "" ? 1 : 0 certificate_arn = aws_acm_certificate.sub_domain[0].arn validation_record_fqdns = [for record in aws_route53_record.sub_domain : record.fqdn] } locals { - is_enabled_and_valid = var.aws_r53_domain_name != "" ? true : false + acm_arn = try(data.aws_acm_certificate.issued["domain"].arn, try(data.aws_acm_certificate.issued["wildcard"].arn, data.aws_acm_certificate.issued["sub"].arn, "")) + selected_arn = ( - local.is_enabled_and_valid ? - (var.aws_r53_cert_arn != "" ? var.aws_r53_cert_arn : - (!var.aws_r53_create_root_cert ? - (!var.aws_r53_create_sub_cert ? - (var.fqdn_provided ? local.acm_arn : "") - : aws_acm_certificate.sub_domain[0].arn - ) : aws_acm_certificate.root_domain[0].arn - ) - ) : "" + var.aws_r53_cert_arn != "" ? var.aws_r53_cert_arn : + var.aws_r53_create_root_cert ? aws_acm_certificate.root_domain[0].arn : + var.aws_r53_create_sub_cert ? aws_acm_certificate.sub_domain[0].arn : + var.fqdn_provided ? local.acm_arn : + "" ) - cert_available = ( - local.is_enabled_and_valid ? - (var.aws_r53_cert_arn != "" ? true : - (!var.aws_r53_create_root_cert ? - (!var.aws_r53_create_sub_cert ? - (var.fqdn_provided ? true : false) - : true - ) : true - ) - ) : false - ) - acm_arn = try(data.aws_acm_certificate.issued["domain"].arn, try(data.aws_acm_certificate.issued["wildcard"].arn, data.aws_acm_certificate.issued["sub"].arn, "")) } output "selected_arn" { diff --git a/operations/deployment/terraform/modules/aws/lb/aws_lb.tf b/operations/deployment/terraform/modules/aws/lb/aws_lb.tf new file mode 100644 index 00000000..b956d8e1 --- /dev/null +++ b/operations/deployment/terraform/modules/aws/lb/aws_lb.tf @@ -0,0 +1,343 @@ +# Locals for ALB +locals { + alb_ssl_available = var.aws_certificates_selected_arn != "" ? true : false + + alb_listen_port_list = var.aws_alb_listen_port != "" ? [for n in split(",", var.aws_alb_listen_port) : tonumber(n)] : [] + + alb_listen_port = var.aws_alb_listen_port != "" ? [for n in split(",", var.aws_alb_listen_port) : tonumber(n)] : var.aws_certificate_enabled ? [443] : [80] + alb_listen_protocol = var.aws_alb_listen_protocol != "" ? [for n in split(",", var.aws_alb_listen_protocol) : n] : var.aws_certificate_enabled ? ["HTTPS"] : ["HTTP"] + alb_app_port = var.aws_alb_app_port != "" ? [for n in split(",", var.aws_alb_app_port) : tonumber(n)] : local.alb_listen_port + alb_app_protocol = var.aws_alb_app_protocol != "" ? [for n in split(",", var.aws_alb_app_protocol) : n] : [for _ in local.alb_app_port : "HTTP"] + + # Ensure all arrays have the same length + alb_ports_ammount = min( + length(local.alb_listen_port), + length(local.alb_app_port), + length(local.alb_listen_protocol), + length(local.alb_app_protocol) + ) + + http_listener_action = var.aws_certificate_enabled ? "redirect" : var.aws_alb_www_to_apex_redirect ? "fixed-response" : "forward" +} + + +# Security group for ALB +resource "aws_security_group" "alb_security_group" { + name = var.aws_alb_security_group_name != "" ? var.aws_alb_security_group_name : "SG for ${var.aws_resource_identifier} - ALB" + description = "SG for ${var.aws_resource_identifier} - ALB" + vpc_id = var.aws_vpc_selected_id + egress { + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = ["0.0.0.0/0"] + } + tags = { + Name = "${var.aws_resource_identifier}-alb-sg" + } +} + +# Allow all from ALB to target SG +resource "aws_security_group_rule" "incoming_alb" { + type = "ingress" + from_port = 0 + to_port = 0 + protocol = -1 + source_security_group_id = aws_security_group.alb_security_group.id + security_group_id = var.aws_alb_target_sg_id +} + +# Allow incoming connections to the ALB +resource "aws_security_group_rule" "incoming_alb_ports" { + count = local.alb_ports_ammount + type = "ingress" + from_port = local.alb_listen_port[count.index] + to_port = local.alb_listen_port[count.index] + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + security_group_id = aws_security_group.alb_security_group.id +} + +# ALB resource (conditionally enable access logs) +resource "aws_lb" "vm_alb" { + name = var.aws_resource_identifier_supershort + internal = false + load_balancer_type = "application" + security_groups = [aws_security_group.alb_security_group.id] + subnets = var.aws_vpc_subnet_selected + + dynamic "access_logs" { + for_each = var.aws_alb_access_log_enabled ? [1] : [] + content { + bucket = aws_s3_bucket.alb_access_logs[0].id + enabled = true + } + } + + idle_timeout = 400 + + tags = { + Name = "${var.aws_resource_identifier_supershort}-alb" + } +} + +# Target groups for ALB +resource "aws_lb_target_group" "vm_alb_tg" { + count = local.alb_ports_ammount + name = "${var.aws_resource_identifier_supershort}${count.index}" + port = local.alb_app_port[count.index] + protocol = local.alb_app_protocol[count.index] + vpc_id = var.aws_vpc_selected_id + + health_check { + healthy_threshold = 2 + unhealthy_threshold = 2 + timeout = 3 + path = var.aws_alb_healthcheck_path + protocol = var.aws_alb_healthcheck_protocol + interval = 30 + } + + lifecycle { + replace_triggered_by = [aws_security_group.alb_security_group.id] + } + + tags = { + Name = "${var.aws_resource_identifier_supershort}-${count.index}-${local.alb_app_port[count.index]}" + } +} + +# Always exists, acts as a safe dependency wrapper +resource "null_resource" "http_redirect_dep" { + triggers = { + id = ( + length(aws_alb_listener.http_redirect) > 0 + ) ? aws_alb_listener.http_redirect[0].id : "none" + } +} + +# Listeners for ALB +resource "aws_alb_listener" "lb_listener_ssl" { + count = var.aws_certificate_enabled ? local.alb_ports_ammount : 0 + load_balancer_arn = aws_lb.vm_alb.arn + port = local.alb_listen_port[count.index] + protocol = local.alb_listen_protocol[count.index] + + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.vm_alb_tg[count.index].arn + } + # https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html + ssl_policy = var.aws_alb_ssl_policy + certificate_arn = var.aws_certificates_selected_arn + lifecycle { + replace_triggered_by = [null_resource.http_redirect_dep.id] + } + depends_on = [aws_alb_listener.http_redirect] +} + +resource "aws_alb_listener" "lb_listener" { + count = var.aws_certificate_enabled ? 0 : local.alb_ports_ammount + load_balancer_arn = aws_lb.vm_alb.arn + port = local.alb_listen_port[count.index] + protocol = local.alb_listen_protocol[count.index] + default_action { + type = "forward" + target_group_arn = aws_lb_target_group.vm_alb_tg[count.index].arn + } + lifecycle { + replace_triggered_by = [null_resource.http_redirect_dep.id] + } +} + +locals { +} + +resource "aws_alb_listener" "http_redirect" { + count = var.aws_alb_redirect_enable && !contains(local.alb_listen_port, 80) ? 1 : 0 + load_balancer_arn = aws_lb.vm_alb.arn + port = "80" + protocol = "HTTP" + + default_action { + type = local.http_listener_action + + dynamic "redirect" { + for_each = local.http_listener_action == "redirect" ? [1] : [] + content { + port = "443" + protocol = "HTTPS" + status_code = "HTTP_301" + } + } + + dynamic "fixed_response" { + for_each = local.http_listener_action == "fixed-response" ? [1] : [] + content { + content_type = "text/plain" + message_body = "Not Found" + status_code = "404" + } + } + target_group_arn = local.http_listener_action == "forward" ? aws_lb_target_group.vm_alb_tg[0].arn : null + } + + depends_on = [ + aws_lb.vm_alb, + aws_lb_target_group.vm_alb_tg, + ] + + lifecycle { + replace_triggered_by = [aws_security_group.alb_security_group.id] + } +} + +resource "aws_lb_listener_rule" "http_forward_apex" { + count = var.aws_alb_www_to_apex_redirect && var.aws_r53_domain_name != "" && !var.aws_certificate_enabled && var.aws_alb_www_to_apex_redirect ? 1 : 0 + listener_arn = aws_alb_listener.http_redirect[0].arn + priority = 20 + + condition { + host_header { + values = [var.aws_r53_domain_name] + } + } + + action { + type = "forward" + target_group_arn = aws_lb_target_group.vm_alb_tg[0].id + } +} + +resource "aws_lb_listener_rule" "redirect_www_to_apex" { + count = var.aws_alb_www_to_apex_redirect && var.aws_r53_domain_name != "" && (var.aws_certificate_enabled ? length(aws_alb_listener.https_redirect) > 0 : var.aws_alb_www_to_apex_redirect) ? 1 : 0 + listener_arn = aws_alb_listener.http_redirect[0].arn + priority = 10 + + condition { + host_header { + values = ["www.${var.aws_r53_domain_name}"] + } + } + + action { + type = "redirect" + + redirect { + port = local.alb_ssl_available ? "443" : "80" + protocol = local.alb_ssl_available ? "HTTPS" : "HTTP" + status_code = "HTTP_301" + host = var.aws_r53_domain_name + path = "/#{path}" + query = "#{query}" + } + } +} + +resource "aws_security_group_rule" "incoming_alb_http" { + count = !contains(local.alb_listen_port, 80) ? length(aws_alb_listener.http_redirect) : 0 + type = "ingress" + from_port = 80 + to_port = 80 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + security_group_id = aws_security_group.alb_security_group.id +} + +resource "aws_security_group_rule" "incoming_alb_https" { + count = !contains(local.alb_listen_port, 443) ? length(aws_alb_listener.https_redirect) : 0 + type = "ingress" + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_blocks = ["0.0.0.0/0"] + security_group_id = aws_security_group.alb_security_group.id +} +### + +resource "aws_alb_listener" "https_redirect" { + count = var.aws_alb_redirect_enable && var.aws_certificate_enabled && !contains(local.alb_listen_port, 443) ? 1 : 0 + load_balancer_arn = aws_lb.vm_alb.arn + port = "443" + protocol = "HTTPS" + certificate_arn = var.aws_certificates_selected_arn + ssl_policy = var.aws_certificates_selected_arn != "" ? var.aws_alb_ssl_policy : "" # https://docs.aws.amazon.com/elasticloadbalancing/latest/application/create-https-listener.html + + default_action { + target_group_arn = aws_lb_target_group.vm_alb_tg[0].arn + type = "forward" + } +} + +# Attach EC2 instance(s) to target group(s) +resource "aws_lb_target_group_attachment" "vm_alb_attachment" { + count = local.alb_ports_ammount + target_group_arn = aws_lb_target_group.vm_alb_tg[count.index].arn + target_id = var.aws_instance_server_id + port = local.alb_app_port[count.index] +} + + +# S3 bucket for ALB access logs (created only if logging is enabled) +resource "aws_s3_bucket" "alb_access_logs" { + count = var.aws_alb_access_log_enabled ? 1 : 0 + bucket = var.aws_alb_access_log_bucket_name + force_destroy = true + tags = { + Name = var.aws_alb_access_log_bucket_name + } +} + +resource "aws_s3_bucket_lifecycle_configuration" "alb_access_logs_lifecycle" { + count = var.aws_alb_access_log_enabled && tonumber(var.aws_alb_access_log_expire) > 0 ? 1 : 0 + bucket = aws_s3_bucket.alb_access_logs[0].id + rule { + id = "ExpirationRule" + status = "Enabled" + filter { + prefix = "" + } + expiration { + days = tonumber(var.aws_alb_access_log_expire) + } + } +} + +data "aws_elb_service_account" "main" { + count = var.aws_alb_access_log_enabled ? 1 : 0 +} + +resource "aws_s3_bucket_policy" "allow_access_from_another_account" { + count = var.aws_alb_access_log_enabled ? 1 : 0 + bucket = aws_s3_bucket.alb_access_logs[0].id + policy = < 0 ? [1] : [] content { name = "GeoBlockRule" - priority = 70 + priority = var.aws_waf_rule_geo_block_countries_priority action { block {} @@ -205,7 +205,7 @@ resource "aws_wafv2_web_acl" "waf" { for_each = length(local.aws_waf_rule_geo_allow_only_countries) > 0 ? [1] : [] content { name = "GeoAllowOnlyRule" - priority = 75 + priority = var.aws_waf_rule_geo_allow_only_countries_priority action { block {} @@ -234,7 +234,7 @@ resource "aws_wafv2_web_acl" "waf" { for_each = var.aws_waf_rule_user_arn != "" ? [1] : [] content { name = "UserDefinedRuleGroup" - priority = 80 + priority = var.aws_waf_rule_user_arn_priority override_action { none {} @@ -259,7 +259,7 @@ resource "aws_wafv2_web_acl" "waf" { for_each = var.aws_waf_rule_sqli ? [1] : [] content { name = "AWSManagedRulesSQLiRuleSet" - priority = 85 + priority = var.aws_waf_rule_sqli_priority override_action { none {} @@ -285,7 +285,7 @@ resource "aws_wafv2_web_acl" "waf" { for_each = var.aws_waf_rule_linux ? [1] : [] content { name = "AWSManagedRulesLinuxRuleSet" - priority = 90 + priority = var.aws_waf_rule_linux_priority override_action { none {} @@ -311,7 +311,7 @@ resource "aws_wafv2_web_acl" "waf" { for_each = var.aws_waf_rule_unix ? [1] : [] content { name = "AWSManagedRulesUnixRuleSet" - priority = 95 + priority = var.aws_waf_rule_unix_priority override_action { none {} @@ -337,7 +337,7 @@ resource "aws_wafv2_web_acl" "waf" { for_each = var.aws_waf_rule_admin_protection ? [1] : [] content { name = "AWSManagedRulesAdminProtectionRuleSet" - priority = 100 + priority = var.aws_waf_rule_admin_protection_priority override_action { none {} diff --git a/operations/deployment/terraform/modules/aws/waf/aws_waf_vars.tf b/operations/deployment/terraform/modules/aws/waf/aws_waf_vars.tf index 4dbbea14..5f187813 100644 --- a/operations/deployment/terraform/modules/aws/waf/aws_waf_vars.tf +++ b/operations/deployment/terraform/modules/aws/waf/aws_waf_vars.tf @@ -5,16 +5,29 @@ variable "aws_waf_logging_enable" {} variable "aws_waf_log_retention_days" {} variable "aws_resource_identifier" {} -variable "aws_waf_rule_rate_limit" {} # - Rate limit (requests per 5 minutes) -variable "aws_waf_rule_managed_rules" {} # - Managed rule groups -variable "aws_waf_rule_managed_bad_inputs" {} # - Known bad inputs rule -variable "aws_waf_rule_ip_reputation" {} # - IP reputation rule -variable "aws_waf_rule_anonymous_ip" {} # - Anonymous IPs rule -variable "aws_waf_rule_bot_control" {} # - Bot control rule -variable "aws_waf_rule_geo_block_countries" {} # - List of countries to block -variable "aws_waf_rule_geo_allow_only_countries" {} # - List of countries to allow only -variable "aws_waf_rule_user_arn" {} # - ARN of the user-defined rule group -variable "aws_waf_rule_sqli" {} # - SQL injection rule -variable "aws_waf_rule_linux" {} # - Linux rule -variable "aws_waf_rule_unix" {} # - Unix rule -variable "aws_waf_rule_admin_protection" {} # - Admin protection rule \ No newline at end of file +variable "aws_waf_rule_rate_limit" {} # - Rate limit (requests per 5 minutes) +variable "aws_waf_rule_rate_limit_priority" {} # - Priority for rate limit rule +variable "aws_waf_rule_managed_rules" {} # - Managed rule groups +variable "aws_waf_rule_managed_rules_priority" {} # - Priority for managed rules +variable "aws_waf_rule_managed_bad_inputs" {} # - Known bad inputs rule +variable "aws_waf_rule_managed_bad_inputs_priority" {} # - Priority for known bad inputs rule +variable "aws_waf_rule_ip_reputation" {} # - IP reputation rule +variable "aws_waf_rule_ip_reputation_priority" {} # - Priority for IP reputation rule +variable "aws_waf_rule_anonymous_ip" {} # - Anonymous IPs rule +variable "aws_waf_rule_anonymous_ip_priority" {} # - Priority for anonymous IPs rule +variable "aws_waf_rule_bot_control" {} # - Bot control rule +variable "aws_waf_rule_bot_control_priority" {} # - Priority for bot control rule +variable "aws_waf_rule_geo_block_countries" {} # - List of countries to block +variable "aws_waf_rule_geo_block_countries_priority" {} # - Priority for geo block rule +variable "aws_waf_rule_geo_allow_only_countries" {} # - List of countries to allow only +variable "aws_waf_rule_geo_allow_only_countries_priority" {} # - Priority for geo allow only rule +variable "aws_waf_rule_user_arn" {} # - ARN of the user-defined rule group +variable "aws_waf_rule_user_arn_priority" {} # - Priority for user-defined rule group +variable "aws_waf_rule_sqli" {} # - SQL injection rule +variable "aws_waf_rule_sqli_priority" {} # - Priority for SQL injection rule +variable "aws_waf_rule_linux" {} # - Linux rule +variable "aws_waf_rule_linux_priority" {} # - Priority for Linux rule +variable "aws_waf_rule_unix" {} # - Unix rule +variable "aws_waf_rule_unix_priority" {} # - Priority for Unix rule +variable "aws_waf_rule_admin_protection" {} # - Admin protection rule +variable "aws_waf_rule_admin_protection_priority" {} # - Priority for admin protection rule \ No newline at end of file