├── .gitignore
├── .header.md
├── .pre-commit-config.yaml
├── .terraform-docs.yaml
├── .tflint.hcl
├── .tfsec
├── launch_configuration_imdsv2_tfchecks.json
├── launch_template_imdsv2_tfchecks.json
├── no_launch_config_tfchecks.json
├── sg_no_embedded_egress_rules_tfchecks.json
└── sg_no_embedded_ingress_rules_tfchecks.json
├── CODEOWNERS
├── LICENSE
├── NOTICE.txt
├── README.md
├── data.tf
├── examples
└── basic
│ ├── .header.md
│ ├── README.md
│ ├── main.tf
│ ├── outputs.tf
│ ├── providers.tf
│ └── variables.tf
├── images
├── polygon_diagram.png
└── polygon_diagram.pptx
├── main.tf
├── modules
├── alb
│ ├── main.tf
│ ├── outputs.tf
│ ├── variables.tf
│ └── versions.tf
├── instances
│ ├── data-sources.tf
│ ├── main.tf
│ ├── outputs.tf
│ ├── variables.tf
│ └── versions.tf
├── security
│ ├── data-sources.tf
│ ├── iam_roles.tf
│ ├── main.tf
│ ├── outputs.tf
│ ├── providers.tf
│ └── variables.tf
└── user-data
│ ├── main.tf
│ ├── outputs.tf
│ ├── scripts
│ ├── polygon_edge_node.tfpl
│ └── polygon_edge_server.tfpl
│ ├── variables.tf
│ └── versions.tf
├── outputs.tf
├── providers.tf
├── test
└── examples_basic_test.go
└── variables.tf
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | plan.out
3 | plan.out.json
4 |
5 | # Local .terraform directories
6 | .terraform/
7 |
8 | # .tfstate files
9 | *.tfstate
10 | *.tfstate.*
11 |
12 | # Crash log files
13 | crash.log
14 |
15 | # Exclude all .tfvars files, which are likely to contain sentitive data, such as
16 | # password, private keys, and other secrets. These should not be part of version
17 | # control as they are data points which are potentially sensitive and subject
18 | # to change depending on the environment.
19 | #
20 | *.tfvars
21 |
22 | # Ignore override files as they are usually used to override resources locally and so
23 | # are not checked in
24 | override.tf
25 | override.tf.json
26 | *_override.tf
27 | *_override.tf.json
28 |
29 | # Include override files you do wish to add to version control using negated pattern
30 | #
31 | # !example_override.tf
32 |
33 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
34 | # example: *tfplan*
35 |
36 | # Ignore CLI configuration files
37 | .terraformrc
38 | terraform.rc
39 | .terraform.lock.hcl
40 |
41 | # Ignore custom aws profile file
42 | aws.profile.tf
43 |
44 | .idea
45 | main.zip
--------------------------------------------------------------------------------
/.header.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # Polygon Edge AWS Terraform
6 |
7 | Polygon Edge is a modular and extensible framework for building Ethereum-compatible blockchain networks.
8 |
9 | To find out more about Polygon, visit the [official website](https://polygon.technology/).
10 |
11 | ### Documentation 📝
12 |
13 | If you'd like to learn more about the Polygon Edge, how it works and how you can use it for your project,
14 | please check out the **[Polygon Edge Documentation](https://docs.polygon.technology/docs/edge/overview/)**.
15 |
16 | ## Terraform deployment
17 |
18 | This is a fully automated Polygon Edge blockchain infrastructure deployment for AWS cloud provider.
19 |
20 | High level overview of the resources that will be deployed:
21 | * Dedicated VPC
22 | * 4 validator nodes (which are also boot nodes)
23 | * 4 NAT gateways to allow nodes outbound internet traffic
24 | * Lambda function used for generating the first (`genesis`) block and starting the chain
25 | * Dedicated security groups and IAM roles
26 | * S3 bucket used for storing `genesis.json` file
27 | * Application Load Balancer used for exposing the `JSON-RPC` endpoint
28 |
29 | ### Prerequisites
30 |
31 | Three variables that must be provided, before running the deployment:
32 |
33 | * `account_id` - the AWS account ID that the Polygon Edge blockchain cluster will be deployed on.
34 | * `alb_ssl_certificate` - the ARN of the certificate from AWS Certificate Manager to be used by ALB for https protocol.
35 | The certificate must be generated before starting the deployment, and it must have **Issued** status.
36 | * `premine` - the account/s that will receive pre mined native currency.
37 | Value must follow the official [CLI](https://docs.polygon.technology/docs/edge/get-started/cli-commands#genesis-flags) flag specification.
38 |
39 | ### Fault tolerance
40 |
41 | Only regions that have 4 availability zones are required for this deployment. Each node is deployed in a single AZ.
42 |
43 | By placing each node in a single AZ, the whole blockchain cluster is fault-tolerant to a single node (AZ) failure, as Polygon Edge implements IBFT
44 | consensus which allows a single node to fail in a 4 validator node cluster.
45 |
46 | ### Command line access
47 |
48 | Validator nodes are not exposed in any way to the public internet (JSON-PRC is accessed only via ALB)
49 | and they don't even have public IP addresses attached to them.
50 | Nodes command line access is possible only via ***AWS Systems Manager - Session Manager***.
51 |
52 | ### Base AMI upgrade
53 |
54 | This deployment uses `ubuntu-focal-20.04-amd64-server` AWS AMI. It will **not** trigger EC2 *redeployment* if the AWS AMI gets updated.
55 |
56 | If, for some reason, base AMI is required to get updated,
57 | it can be achieved by running `terraform taint` command for each instance, before `terraform apply`.
58 | Instances can be tainted by running the `terraform taint module.instances[].aws_instance.polygon_edge_instance` command.
59 |
60 | Example:
61 | ```shell
62 | terraform taint module.instances[0].aws_instance.polygon_edge_instance
63 | terraform taint module.instances[1].aws_instance.polygon_edge_instance
64 | terraform taint module.instances[2].aws_instance.polygon_edge_instance
65 | terraform taint module.instances[3].aws_instance.polygon_edge_instance
66 | terraform apply
67 | ```
68 |
69 | ### Resources cleanup
70 |
71 | When cleaning up all resources by running `terraform destory`, the only thing that needs to be manually deleted
72 | are **validator keys** from **AWS SSM Parameter Store** as they are not stored via Terraform, but with `polygon-edge`
73 | process itself.
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | fail_fast: false
3 | minimum_pre_commit_version: "2.6.0"
4 | repos:
5 | -
6 | repo: https://github.com/aws-ia/pre-commit-configs
7 | # To update run:
8 | # pre-commit autoupdate --freeze
9 | rev: e2be6eb118666030010fdeb0edaba9db669619d1 # frozen: v1.3.2
10 | hooks:
11 | - id: aws-ia-meta-hook
12 |
--------------------------------------------------------------------------------
/.terraform-docs.yaml:
--------------------------------------------------------------------------------
1 | formatter: markdown
2 | header-from: .header.md
3 | settings:
4 | anchor: true
5 | color: true
6 | default: true
7 | escape: true
8 | html: true
9 | indent: 2
10 | required: true
11 | sensitive: true
12 | type: true
13 | lockfile: false
14 |
15 | sort:
16 | enabled: true
17 | by: required
18 |
19 | output:
20 | file: README.md
21 | mode: replace
22 |
--------------------------------------------------------------------------------
/.tflint.hcl:
--------------------------------------------------------------------------------
1 | # https://github.com/terraform-linters/tflint/blob/master/docs/user-guide/module-inspection.md
2 | # borrowed & modified indefinitely from https://github.com/ksatirli/building-infrastructure-you-can-mostly-trust/blob/main/.tflint.hcl
3 |
4 | plugin "aws" {
5 | enabled = true
6 | version = "0.21.2"
7 | source = "github.com/terraform-linters/tflint-ruleset-aws"
8 | }
9 |
10 | config {
11 | module = true
12 | force = false
13 | }
14 |
15 | rule "terraform_required_providers" {
16 | enabled = true
17 | }
18 |
19 | rule "terraform_required_version" {
20 | enabled = true
21 | }
22 |
23 | rule "terraform_naming_convention" {
24 | enabled = true
25 | format = "snake_case"
26 | }
27 |
28 | rule "terraform_typed_variables" {
29 | enabled = true
30 | }
31 |
32 | rule "terraform_unused_declarations" {
33 | enabled = true
34 | }
35 |
36 | rule "terraform_comment_syntax" {
37 | enabled = true
38 | }
39 |
40 | rule "terraform_deprecated_index" {
41 | enabled = true
42 | }
43 |
44 | rule "terraform_deprecated_interpolation" {
45 | enabled = true
46 | }
47 |
48 | rule "terraform_documented_outputs" {
49 | enabled = true
50 | }
51 |
52 | rule "terraform_documented_variables" {
53 | enabled = true
54 | }
55 |
56 | rule "terraform_module_pinned_source" {
57 | enabled = true
58 | }
59 |
60 | rule "terraform_standard_module_structure" {
61 | enabled = true
62 | }
63 |
64 | rule "terraform_workspace_remote" {
65 | enabled = true
66 | }
67 |
--------------------------------------------------------------------------------
/.tfsec/launch_configuration_imdsv2_tfchecks.json:
--------------------------------------------------------------------------------
1 | {
2 | "checks": [
3 | {
4 | "code": "CUS002",
5 | "description": "Check to IMDSv2 is required on EC2 instances created by this Launch Template",
6 | "impact": "Instance metadata service can be interacted with freely",
7 | "resolution": "Enable HTTP token requirement for IMDS",
8 | "requiredTypes": [
9 | "resource"
10 | ],
11 | "requiredLabels": [
12 | "aws_launch_configuration"
13 | ],
14 | "severity": "CRITICAL",
15 | "matchSpec": {
16 | "action": "isPresent",
17 | "name": "metadata_options",
18 | "subMatch": {
19 | "action": "and",
20 | "predicateMatchSpec": [
21 | {
22 | "action": "equals",
23 | "name": "http_tokens",
24 | "value": "required"
25 |
26 | }
27 | ]
28 | }
29 | },
30 |
31 | "errorMessage": "is missing `metadata_options` block - it is required with `http_tokens` set to `required` to make Instance Metadata Service more secure.",
32 | "relatedLinks": [
33 | "https://tfsec.dev/docs/aws/ec2/enforce-http-token-imds#aws/ec2",
34 | "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/launch_configuration#metadata-options",
35 | "https://aws.amazon.com/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service"
36 | ]
37 | }
38 | ]
39 | }
40 |
--------------------------------------------------------------------------------
/.tfsec/launch_template_imdsv2_tfchecks.json:
--------------------------------------------------------------------------------
1 | {
2 | "checks": [
3 | {
4 | "code": "CUS001",
5 | "description": "Check to IMDSv2 is required on EC2 instances created by this Launch Template",
6 | "impact": "Instance metadata service can be interacted with freely",
7 | "resolution": "Enable HTTP token requirement for IMDS",
8 | "requiredTypes": [
9 | "resource"
10 | ],
11 | "requiredLabels": [
12 | "aws_launch_template"
13 | ],
14 | "severity": "CRITICAL",
15 | "matchSpec": {
16 | "action": "isPresent",
17 | "name": "metadata_options",
18 | "subMatch": {
19 | "action": "and",
20 | "predicateMatchSpec": [
21 | {
22 | "action": "equals",
23 | "name": "http_tokens",
24 | "value": "required"
25 |
26 | }
27 | ]
28 | }
29 | },
30 |
31 | "errorMessage": "is missing `metadata_options` block - it is required with `http_tokens` set to `required` to make Instance Metadata Service more secure.",
32 | "relatedLinks": [
33 | "https://tfsec.dev/docs/aws/ec2/enforce-http-token-imds#aws/ec2",
34 | "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/launch_template#metadata-options",
35 | "https://aws.amazon.com/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service"
36 | ]
37 | }
38 | ]
39 | }
40 |
--------------------------------------------------------------------------------
/.tfsec/no_launch_config_tfchecks.json:
--------------------------------------------------------------------------------
1 | {
2 | "checks": [
3 | {
4 | "code": "CUS003",
5 | "description": "Use `aws_launch_template` over `aws_launch_configuration",
6 | "impact": "Launch configurations are not capable of versions",
7 | "resolution": "Convert resource type and attributes to `aws_launch_template`",
8 | "requiredTypes": [
9 | "resource"
10 | ],
11 | "requiredLabels": [
12 | "aws_launch_configuration"
13 | ],
14 | "severity": "MEDIUM",
15 | "matchSpec": {
16 | "action": "notPresent",
17 | "name": "image_id"
18 | },
19 |
20 | "errorMessage": "should be changed to `aws_launch_template` since the functionality is the same but templates can be versioned.",
21 | "relatedLinks": [
22 | "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/launch_template",
23 | "https://aws.amazon.com/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service"
24 | ]
25 | }
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/.tfsec/sg_no_embedded_egress_rules_tfchecks.json:
--------------------------------------------------------------------------------
1 | {
2 | "checks": [
3 | {
4 | "code": "CUS005",
5 | "description": "Security group rules should be defined with `aws_security_group_rule` instead of embedded.",
6 | "impact": "Embedded security group rules can cause issues during configuration updates.",
7 | "resolution": "Move `egress` rules to `aws_security_group_rule` and attach to `aws_security_group`.",
8 | "requiredTypes": [
9 | "resource"
10 | ],
11 | "requiredLabels": [
12 | "aws_security_group"
13 | ],
14 | "severity": "MEDIUM",
15 | "matchSpec": {
16 | "action": "notPresent",
17 | "name": "egress"
18 | },
19 |
20 | "errorMessage": "`egress` rules should be moved to `aws_security_group_rule` and attached to `aws_security_group` instead of embedded.",
21 | "relatedLinks": [
22 | "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule",
23 | "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group"
24 | ]
25 | }
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/.tfsec/sg_no_embedded_ingress_rules_tfchecks.json:
--------------------------------------------------------------------------------
1 | {
2 | "checks": [
3 | {
4 | "code": "CUS004",
5 | "description": "Security group rules should be defined with `aws_security_group_rule` instead of embedded.",
6 | "impact": "Embedded security group rules can cause issues during configuration updates.",
7 | "resolution": "Move `ingress` rules to `aws_security_group_rule` and attach to `aws_security_group`.",
8 | "requiredTypes": [
9 | "resource"
10 | ],
11 | "requiredLabels": [
12 | "aws_security_group"
13 | ],
14 | "severity": "MEDIUM",
15 | "matchSpec": {
16 | "action": "notPresent",
17 | "name": "ingress"
18 | },
19 |
20 | "errorMessage": "`ingress` rules should be moved to `aws_security_group_rule` and attached to `aws_security_group` instead of embedded.",
21 | "relatedLinks": [
22 | "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule",
23 | "https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group"
24 | ]
25 | }
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @sshvans @aws-ia/aws-ia
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/NOTICE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2016-2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at
4 |
5 | http://aws.amazon.com/apache2.0/
6 |
7 | or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | # Polygon Edge AWS Terraform
7 |
8 | Polygon Edge is a modular and extensible framework for building Ethereum-compatible blockchain networks.
9 |
10 | To find out more about Polygon, visit the [official website](https://polygon.technology/).
11 |
12 | ### Documentation 📝
13 |
14 | If you'd like to learn more about the Polygon Edge, how it works and how you can use it for your project,
15 | please check out the **[Polygon Edge Documentation](https://docs.polygon.technology/docs/edge/overview/)**.
16 |
17 | ## Terraform deployment
18 |
19 | This is a fully automated Polygon Edge blockchain infrastructure deployment for AWS cloud provider.
20 |
21 | High level overview of the resources that will be deployed:
22 | * Dedicated VPC
23 | * 4 validator nodes (which are also boot nodes)
24 | * 4 NAT gateways to allow nodes outbound internet traffic
25 | * Lambda function used for generating the first (`genesis`) block and starting the chain
26 | * Dedicated security groups and IAM roles
27 | * S3 bucket used for storing `genesis.json` file
28 | * Application Load Balancer used for exposing the `JSON-RPC` endpoint
29 |
30 | ### Prerequisites
31 |
32 | Two variables that must be provided, before running the deployment:
33 |
34 | * `alb_ssl_certificate` - the ARN of the certificate from AWS Certificate Manager to be used by ALB for https protocol.
35 | The certificate must be generated before starting the deployment, and it must have **Issued** status.
36 | * `premine` - the account/s that will receive pre mined native currency.
37 | Value must follow the official [CLI](https://docs.polygon.technology/docs/edge/get-started/cli-commands#genesis-flags) flag specification.
38 |
39 | ### Fault tolerance
40 |
41 | Only regions that have 4 availability zones are required for this deployment. Each node is deployed in a single AZ.
42 |
43 | By placing each node in a single AZ, the whole blockchain cluster is fault-tolerant to a single node (AZ) failure, as Polygon Edge implements IBFT
44 | consensus which allows a single node to fail in a 4 validator node cluster.
45 |
46 | ### Command line access
47 |
48 | Validator nodes are not exposed in any way to the public internet (JSON-PRC is accessed only via ALB)
49 | and they don't even have public IP addresses attached to them.
50 | Nodes command line access is possible only via ***AWS Systems Manager - Session Manager***.
51 |
52 | ### Base AMI upgrade
53 |
54 | This deployment uses `ubuntu-focal-20.04-amd64-server` AWS AMI. It will **not** trigger EC2 *redeployment* if the AWS AMI gets updated.
55 |
56 | If, for some reason, base AMI is required to get updated,
57 | it can be achieved by running `terraform taint` command for each instance, before `terraform apply`.
58 | Instances can be tainted by running the `terraform taint module.instances[].aws_instance.polygon_edge_instance` command.
59 |
60 | Example:
61 | ```shell
62 | terraform taint module.instances[0].aws_instance.polygon_edge_instance
63 | terraform taint module.instances[1].aws_instance.polygon_edge_instance
64 | terraform taint module.instances[2].aws_instance.polygon_edge_instance
65 | terraform taint module.instances[3].aws_instance.polygon_edge_instance
66 | terraform apply
67 | ```
68 |
69 | ### Resources cleanup
70 |
71 | When cleaning up all resources by running `terraform destory`, the only thing that needs to be manually deleted
72 | are **validator keys** from **AWS SSM Parameter Store** as they are not stored via Terraform, but with `polygon-edge`
73 | process itself.
74 |
75 | ## Requirements
76 |
77 | | Name | Version |
78 | |------|---------|
79 | | [terraform](#requirement\_terraform) | >= 1.3.0 |
80 | | [aws](#requirement\_aws) | >= 4.22.0 |
81 | | [awscc](#requirement\_awscc) | >= 0.27.0 |
82 | | [external](#requirement\_external) | >= 2.2.2 |
83 | | [local](#requirement\_local) | >= 2.2.3 |
84 | | [null](#requirement\_null) | >=3.1.1 |
85 |
86 | ## Providers
87 |
88 | | Name | Version |
89 | |------|---------|
90 | | [aws](#provider\_aws) | >= 4.22.0 |
91 | | [null](#provider\_null) | >=3.1.1 |
92 |
93 | ## Modules
94 |
95 | | Name | Source | Version |
96 | |------|--------|---------|
97 | | [alb](#module\_alb) | ./modules/alb | n/a |
98 | | [instances](#module\_instances) | ./modules/instances | n/a |
99 | | [lambda](#module\_lambda) | terraform-aws-modules/lambda/aws | >=3.3.1 |
100 | | [s3](#module\_s3) | terraform-aws-modules/s3-bucket/aws | >= 3.3.0 |
101 | | [security](#module\_security) | ./modules/security | n/a |
102 | | [user\_data](#module\_user\_data) | ./modules/user-data | n/a |
103 | | [vpc](#module\_vpc) | aws-ia/vpc/aws | >= 3.0.1 |
104 |
105 | ## Resources
106 |
107 | | Name | Type |
108 | |------|------|
109 | | [null_resource.download_package](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource |
110 | | [aws_availability_zones.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source |
111 | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
112 | | [aws_iam_policy_document.genesis_s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
113 | | [aws_iam_policy_document.genesis_ssm](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
114 | | [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
115 | | [null_data_source.downloaded_package](https://registry.terraform.io/providers/hashicorp/null/latest/docs/data-sources/data_source) | data source |
116 |
117 | ## Inputs
118 |
119 | | Name | Description | Type | Default | Required |
120 | |------|-------------|------|---------|:--------:|
121 | | [alb\_ssl\_certificate](#input\_alb\_ssl\_certificate) | SSL certificate ARN for JSON-RPC loadblancer | `string` | n/a | yes |
122 | | [premine](#input\_premine) | Premine the accounts with the specified ammount. Format: account:ammount,account:ammount | `string` | n/a | yes |
123 | | [alb\_sec\_gr\_name\_tag](#input\_alb\_sec\_gr\_name\_tag) | External security group name tag | `string` | `"Polygon Edge External"` | no |
124 | | [block\_gas\_limit](#input\_block\_gas\_limit) | Set the block gas limit | `string` | `""` | no |
125 | | [block\_gas\_target](#input\_block\_gas\_target) | Sets the target block gas limit for the chain | `string` | `""` | no |
126 | | [block\_time](#input\_block\_time) | Set block production time in seconds | `string` | `""` | no |
127 | | [chain\_data\_ebs\_name\_tag](#input\_chain\_data\_ebs\_name\_tag) | The name of the chain data EBS volume. | `string` | `"Polygon_Edge_chain_data_volume"` | no |
128 | | [chain\_data\_ebs\_volume\_size](#input\_chain\_data\_ebs\_volume\_size) | The size of the chain data EBS volume. | `number` | `30` | no |
129 | | [chain\_id](#input\_chain\_id) | Set the Chain ID | `string` | `""` | no |
130 | | [chain\_name](#input\_chain\_name) | Set the name of chain | `string` | `""` | no |
131 | | [consensus](#input\_consensus) | Sets consensus protocol. | `string` | `""` | no |
132 | | [dns\_name](#input\_dns\_name) | Sets the DNS name for the network package | `string` | `""` | no |
133 | | [ebs\_device](#input\_ebs\_device) | The ebs device path. Defined when creating EBS volume. | `string` | `"/dev/nvme1n1"` | no |
134 | | [ebs\_root\_name\_tag](#input\_ebs\_root\_name\_tag) | The name tag for the Polygon Edge instance root volume. | `string` | `"Polygon_Edge_Root_Volume"` | no |
135 | | [epoch\_size](#input\_epoch\_size) | Set the epoch size | `string` | `""` | no |
136 | | [instance\_interface\_name\_tag](#input\_instance\_interface\_name\_tag) | The name of the instance interface. | `string` | `"Polygon_Edge_Instance_Interface"` | no |
137 | | [instance\_name](#input\_instance\_name) | The name of Polygon Edge instance | `string` | `"Polygon_Edge_Node"` | no |
138 | | [instance\_type](#input\_instance\_type) | Polygon Edge nodes instance type. | `string` | `"t3.medium"` | no |
139 | | [internal\_sec\_gr\_name\_tag](#input\_internal\_sec\_gr\_name\_tag) | Internal security group name tag | `string` | `"Polygon Edge Internal"` | no |
140 | | [lambda\_function\_name](#input\_lambda\_function\_name) | The name of the Lambda function used for chain init | `string` | `"polygon-edge-init"` | no |
141 | | [lambda\_function\_zip](#input\_lambda\_function\_zip) | The lambda function code in zip archive | `string` | `"https://raw.githubusercontent.com/Trapesys/polygon-edge-assm/aws-lambda/artifacts/main.zip"` | no |
142 | | [max\_slots](#input\_max\_slots) | Sets maximum slots in the pool | `string` | `""` | no |
143 | | [max\_validator\_count](#input\_max\_validator\_count) | The maximum number of stakers able to join the validator set in a PoS consensus. | `string` | `""` | no |
144 | | [min\_validator\_count](#input\_min\_validator\_count) | The minimum number of stakers needed to join the validator set in a PoS consensus. | `string` | `""` | no |
145 | | [nat\_address](#input\_nat\_address) | Sets the NAT address for the networking package | `string` | `""` | no |
146 | | [node\_name\_prefix](#input\_node\_name\_prefix) | The name prefix that will be used to store secrets | `string` | `"node"` | no |
147 | | [nodes\_alb\_name\_prefix](#input\_nodes\_alb\_name\_prefix) | ALB name | `string` | `"jrpc-"` | no |
148 | | [nodes\_alb\_name\_tag](#input\_nodes\_alb\_name\_tag) | ALB name tag | `string` | `"Polygon Edge JSON-RPC ALB"` | no |
149 | | [nodes\_alb\_targetgroup\_name\_prefix](#input\_nodes\_alb\_targetgroup\_name\_prefix) | ALB target group name | `string` | `"jrpc-"` | no |
150 | | [polygon\_edge\_dir](#input\_polygon\_edge\_dir) | The directory to place all polygon-edge data and logs | `string` | `"/home/ubuntu/polygon"` | no |
151 | | [pos](#input\_pos) | Use PoS IBFT consensus | `bool` | `false` | no |
152 | | [price\_limit](#input\_price\_limit) | Sets minimum gas price limit to enforce for acceptance into the pool | `string` | `""` | no |
153 | | [prometheus\_address](#input\_prometheus\_address) | Enable Prometheus API | `string` | `""` | no |
154 | | [s3\_bucket\_prefix](#input\_s3\_bucket\_prefix) | Name prefix for new S3 bucket | `string` | `"polygon-edge-shared-"` | no |
155 | | [s3\_force\_destroy](#input\_s3\_force\_destroy) | Delete S3 bucket on destroy, even if the bucket is not empty | `bool` | `true` | no |
156 | | [s3\_key\_name](#input\_s3\_key\_name) | Name of the file in S3 that will hold configuration | `string` | `"chain-config"` | no |
157 | | [ssm\_parameter\_id](#input\_ssm\_parameter\_id) | The id that will be used for storing and fetching from SSM Parameter Store | `string` | `"polygon-edge-validators"` | no |
158 | | [vpc\_cidr\_block](#input\_vpc\_cidr\_block) | CIDR block for VPC | `string` | `"10.250.0.0/16"` | no |
159 | | [vpc\_name](#input\_vpc\_name) | Name of the VPC | `string` | `"polygon-edge-vpc"` | no |
160 |
161 | ## Outputs
162 |
163 | | Name | Description |
164 | |------|-------------|
165 | | [jsonrpc\_dns\_name](#output\_jsonrpc\_dns\_name) | The dns name for the JSON-RPC API |
166 |
167 |
--------------------------------------------------------------------------------
/data.tf:
--------------------------------------------------------------------------------
1 | data "null_data_source" "downloaded_package" {
2 | inputs = {
3 | id = null_resource.download_package.id
4 | filename = local.downloaded
5 | }
6 | }
7 |
8 | data "aws_availability_zones" "current" {}
9 | data "aws_region" "current" {}
10 | data "aws_caller_identity" "current" {}
11 |
12 |
13 | data "aws_iam_policy_document" "genesis_s3" {
14 | version = "2012-10-17"
15 | statement {
16 | actions = [
17 | "s3:PutObject",
18 | "s3:GetObject",
19 | "s3:ListBucket"
20 | ]
21 | resources = [
22 | module.s3.s3_bucket_arn,
23 | "${module.s3.s3_bucket_arn}/*"
24 | ]
25 | }
26 | }
27 |
28 | data "aws_iam_policy_document" "genesis_ssm" {
29 | version = "2012-10-17"
30 | statement {
31 | actions = [
32 | "ssm:GetParameter",
33 | "ssm:GetParameters",
34 | "ssm:GetParametersByPath"
35 | ]
36 | resources = [
37 | "arn:aws:ssm:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:parameter/${var.ssm_parameter_id}/*"
38 | ]
39 |
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/examples/basic/.header.md:
--------------------------------------------------------------------------------
1 | # Polygon Edge simple deployment on AWS
2 |
3 | ## Prerequisites
4 |
5 | Three variables that must be provided, before running the deployment:
6 |
7 | * `account_id` - the AWS account ID that the Polygon Edge blockchain cluster will be deployed on.
8 | * `alb_ssl_certificate` - the ARN of the certificate from AWS Certificate Manager to be used by ALB for https protocol.
9 | The certificate must be generated before starting the deployment, and it must have **Issued** status.
10 | * `premine` - the account/s that will receive pre mined native currency.
11 | Value must follow the official [CLI](https://docs.polygon.technology/docs/edge/get-started/cli-commands#genesis-flags) flag specification.
12 |
13 | ## Deployment
14 | To get Polygon Edge cluster quickly up and running default values:
15 | * include this module
16 | * define mandatory variables or provide them at cli prompt
17 | * `terraform init` - to initialize modules
18 | * `terraform apply` - to deploy the infrastructure
19 |
20 | After everything is deployed the JSON-RPC URL should be outputted in the CLI, which needs to be set as a CNAME target
21 | for a domain that you've created the certificate for.
--------------------------------------------------------------------------------
/examples/basic/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Polygon Edge simple deployment on AWS
3 |
4 | ## Prerequisites
5 |
6 | Three variables that must be provided, before running the deployment:
7 |
8 | * `account_id` - the AWS account ID that the Polygon Edge blockchain cluster will be deployed on.
9 | * `alb_ssl_certificate` - the ARN of the certificate from AWS Certificate Manager to be used by ALB for https protocol.
10 | The certificate must be generated before starting the deployment, and it must have **Issued** status.
11 | * `premine` - the account/s that will receive pre mined native currency.
12 | Value must follow the official [CLI](https://docs.polygon.technology/docs/edge/get-started/cli-commands#genesis-flags) flag specification.
13 |
14 | ## Deployment
15 | To get Polygon Edge cluster quickly up and running default values:
16 | * include this module
17 | * define mandatory variables or provide them at cli prompt
18 | * `terraform init` - to initialize modules
19 | * `terraform apply` - to deploy the infrastructure
20 |
21 | After everything is deployed the JSON-RPC URL should be outputted in the CLI, which needs to be set as a CNAME target
22 | for a domain that you've created the certificate for.
23 |
24 | ## Requirements
25 |
26 | No requirements.
27 |
28 | ## Providers
29 |
30 | No providers.
31 |
32 | ## Modules
33 |
34 | | Name | Source | Version |
35 | |------|--------|---------|
36 | | [polygon-edge](#module\_polygon-edge) | aws-ia/polygon-technology-edge/aws | >=0.0.1 |
37 |
38 | ## Resources
39 |
40 | No resources.
41 |
42 | ## Inputs
43 |
44 | | Name | Description | Type | Default | Required |
45 | |------|-------------|------|---------|:--------:|
46 | | [alb\_ssl\_certificate](#input\_alb\_ssl\_certificate) | The ARN of SSL certificate that will be placed on JSON-RPC ALB | `string` | n/a | yes |
47 | | [premine](#input\_premine) | Public account that will receive premined native currency | `string` | n/a | yes |
48 |
49 | ## Outputs
50 |
51 | | Name | Description |
52 | |------|-------------|
53 | | [json\_rpc\_dns\_name](#output\_json\_rpc\_dns\_name) | The dns name for the JSON-RPC API |
54 |
--------------------------------------------------------------------------------
/examples/basic/main.tf:
--------------------------------------------------------------------------------
1 | module "polygon-edge" {
2 | source = "aws-ia/polygon-technology-edge/aws"
3 | version = ">=0.0.1"
4 |
5 | premine = var.premine
6 | alb_ssl_certificate = var.alb_ssl_certificate
7 | }
8 |
--------------------------------------------------------------------------------
/examples/basic/outputs.tf:
--------------------------------------------------------------------------------
1 | output "json_rpc_dns_name" {
2 | value = module.polygon-edge.jsonrpc_dns_name
3 | description = "The dns name for the JSON-RPC API"
4 | }
--------------------------------------------------------------------------------
/examples/basic/providers.tf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-ia/terraform-aws-polygon-technology-edge/ac610d5b2fbde2406b0827a69e10b6795ff7f6c2/examples/basic/providers.tf
--------------------------------------------------------------------------------
/examples/basic/variables.tf:
--------------------------------------------------------------------------------
1 | variable "premine" {
2 | type = string
3 | description = "Public account that will receive premined native currency"
4 | }
5 |
6 | variable "alb_ssl_certificate" {
7 | type = string
8 | description = "The ARN of SSL certificate that will be placed on JSON-RPC ALB"
9 | }
10 |
--------------------------------------------------------------------------------
/images/polygon_diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-ia/terraform-aws-polygon-technology-edge/ac610d5b2fbde2406b0827a69e10b6795ff7f6c2/images/polygon_diagram.png
--------------------------------------------------------------------------------
/images/polygon_diagram.pptx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/aws-ia/terraform-aws-polygon-technology-edge/ac610d5b2fbde2406b0827a69e10b6795ff7f6c2/images/polygon_diagram.pptx
--------------------------------------------------------------------------------
/main.tf:
--------------------------------------------------------------------------------
1 | module "vpc" {
2 | source = "aws-ia/vpc/aws"
3 | version = ">= 3.0.1"
4 |
5 | name = var.vpc_name
6 | cidr_block = var.vpc_cidr_block
7 | az_count = 4
8 |
9 | subnets = {
10 | public = {
11 | netmask = 24
12 | nat_gateway_configuration = "all_azs"
13 | }
14 |
15 | private = {
16 | netmask = 24
17 | connect_to_public_natgw = true
18 | }
19 | }
20 |
21 | vpc_flow_logs = {
22 | log_destination_type = "cloud-watch-logs"
23 | retention_in_days = 180
24 | }
25 | }
26 |
27 | locals {
28 | package_url = var.lambda_function_zip
29 | downloaded = basename(var.lambda_function_zip)
30 | azs = slice(data.aws_availability_zones.current.names, 0, 4)
31 | private_subnets = [for _, value in module.vpc.private_subnet_attributes_by_az : value.id]
32 | private_azs = {
33 | for idx, az_name in local.azs : idx => az_name
34 | }
35 |
36 | }
37 |
38 | module "s3" {
39 | source = "terraform-aws-modules/s3-bucket/aws"
40 | version = ">= 3.3.0"
41 |
42 | bucket_prefix = var.s3_bucket_prefix
43 | acl = "private"
44 | force_destroy = var.s3_force_destroy
45 | }
46 |
47 | module "security" {
48 | source = "./modules/security"
49 |
50 | vpc_id = module.vpc.vpc_attributes.id
51 | account_id = data.aws_caller_identity.current.account_id
52 | s3_shared_bucket_name = module.s3.s3_bucket_id
53 | ssm_parameter_id = var.ssm_parameter_id
54 | region = data.aws_region.current.name
55 | internal_sec_gr_name_tag = var.internal_sec_gr_name_tag
56 | alb_sec_gr_name_tag = var.alb_sec_gr_name_tag
57 | lambda_function_name = var.lambda_function_name
58 | }
59 |
60 | module "instances" {
61 | source = "./modules/instances"
62 |
63 | for_each = local.private_azs
64 |
65 | internal_subnet = local.private_subnets[each.key]
66 | internal_sec_groups = [module.security.internal_sec_group_id]
67 | user_data_base64 = module.user_data[each.key].polygon_edge_node
68 | instance_iam_role = module.security.ec2_to_assm_iam_policy_id
69 | az = each.value
70 | instance_type = var.instance_type
71 | instance_name = var.instance_name
72 | ebs_root_name_tag = var.ebs_root_name_tag
73 | instance_interface_name_tag = var.instance_interface_name_tag
74 | chain_data_ebs_volume_size = var.chain_data_ebs_volume_size
75 | chain_data_ebs_name_tag = var.chain_data_ebs_name_tag
76 |
77 | depends_on = [module.lambda]
78 | }
79 |
80 | module "user_data" {
81 | source = "./modules/user-data"
82 |
83 | for_each = local.private_azs
84 | node_name = "${var.node_name_prefix}-${each.value}"
85 |
86 | assm_path = var.ssm_parameter_id
87 | assm_region = data.aws_region.current.name
88 | s3_bucket_name = module.s3.s3_bucket_id
89 | s3_key_name = var.s3_key_name
90 | total_nodes = length(module.vpc.private_subnet_attributes_by_az)
91 |
92 | polygon_edge_dir = var.polygon_edge_dir
93 | ebs_device = var.ebs_device
94 |
95 | # Server configuration
96 |
97 | max_slots = var.max_slots
98 | block_time = var.block_time
99 | prometheus_address = var.prometheus_address
100 | block_gas_target = var.block_gas_target
101 | nat_address = var.nat_address
102 | dns_name = var.dns_name
103 | price_limit = var.price_limit
104 |
105 | # # Chain configuration
106 | premine = var.premine
107 | chain_name = var.chain_name
108 | chain_id = var.chain_id
109 | block_gas_limit = var.block_gas_limit
110 | epoch_size = var.epoch_size
111 | consensus = var.consensus
112 | lambda_function_name = var.lambda_function_name
113 | max_validator_count = var.max_validator_count
114 | min_validator_count = var.min_validator_count
115 | pos = var.pos
116 |
117 | }
118 |
119 | module "alb" {
120 | source = "./modules/alb"
121 |
122 | public_subnets = [for _, value in module.vpc.public_subnet_attributes_by_az : value.id]
123 | alb_sec_group = module.security.jsonrpc_sec_group_id
124 | vpc_id = module.vpc.vpc_attributes.id
125 | node_ids = [for _, instance in module.instances : instance.instance_id]
126 | alb_ssl_certificate = var.alb_ssl_certificate
127 |
128 | nodes_alb_name_prefix = var.nodes_alb_name_prefix
129 | nodes_alb_name_tag = var.nodes_alb_name_tag
130 | nodes_alb_targetgroup_name_prefix = var.nodes_alb_targetgroup_name_prefix
131 | }
132 |
133 |
134 |
135 | resource "null_resource" "download_package" {
136 | triggers = {
137 | downloaded = local.downloaded
138 | }
139 |
140 | provisioner "local-exec" {
141 | command = "curl -L -o ${local.downloaded} ${local.package_url}"
142 | }
143 | }
144 |
145 | module "lambda" {
146 | source = "terraform-aws-modules/lambda/aws"
147 | version = ">=3.3.1"
148 |
149 | function_name = var.lambda_function_name
150 | description = "Lambda function used to initialize chain and generate genesis.json"
151 | handler = "main"
152 | runtime = "go1.x"
153 | timeout = 20
154 | memory_size = 256
155 |
156 | create_package = false
157 | local_existing_package = data.null_data_source.downloaded_package.outputs["filename"]
158 |
159 | attach_policy_jsons = true
160 | number_of_policy_jsons = 2
161 | policy_jsons = [data.aws_iam_policy_document.genesis_s3.json, data.aws_iam_policy_document.genesis_ssm.json]
162 | }
163 |
--------------------------------------------------------------------------------
/modules/alb/main.tf:
--------------------------------------------------------------------------------
1 | # Create new ALB
2 | resource "aws_lb" "polygon_nodes" {
3 | name_prefix = var.nodes_alb_name_prefix
4 | #tfsec:ignore:aws-elb-alb-not-public
5 | internal = false
6 | load_balancer_type = "application"
7 | subnets = var.public_subnets
8 | security_groups = [var.alb_sec_group]
9 | drop_invalid_header_fields = true
10 |
11 |
12 | tags = {
13 | Name = var.nodes_alb_name_tag
14 | }
15 | }
16 | # Create new ALB Target Group
17 | resource "aws_lb_target_group" "polygon_nodes" {
18 | name_prefix = var.nodes_alb_targetgroup_name_prefix
19 | port = 8545
20 | protocol = "HTTP"
21 | vpc_id = var.vpc_id
22 | }
23 |
24 | # Set http listener on ALB
25 | resource "aws_lb_listener" "polygon_nodes_http" {
26 | load_balancer_arn = aws_lb.polygon_nodes.arn
27 | port = 80
28 | protocol = "HTTP"
29 |
30 | default_action {
31 | type = "redirect"
32 |
33 | redirect {
34 | status_code = "HTTP_301"
35 | port = 443
36 | protocol = "HTTPS"
37 | }
38 | }
39 | }
40 |
41 | # Set https listener on ALB
42 | resource "aws_lb_listener" "polygon_nodes_https" {
43 | load_balancer_arn = aws_lb.polygon_nodes.arn
44 | port = 443
45 | protocol = "HTTPS"
46 | ssl_policy = "ELBSecurityPolicy-TLS-1-2-2017-01"
47 | certificate_arn = var.alb_ssl_certificate
48 |
49 | default_action {
50 | type = "forward"
51 | target_group_arn = aws_lb_target_group.polygon_nodes.arn
52 | }
53 | }
54 |
55 | # Attach instances to ALB
56 | resource "aws_lb_target_group_attachment" "polygon_nodes" {
57 | count = length(var.node_ids)
58 |
59 | target_group_arn = aws_lb_target_group.polygon_nodes.arn
60 | target_id = var.node_ids[count.index]
61 | port = 8545
62 | }
63 |
--------------------------------------------------------------------------------
/modules/alb/outputs.tf:
--------------------------------------------------------------------------------
1 | output "dns_name" {
2 | value = aws_lb.polygon_nodes.dns_name
3 | description = "The dns name for the JSON-RPC"
4 | }
--------------------------------------------------------------------------------
/modules/alb/variables.tf:
--------------------------------------------------------------------------------
1 | variable "nodes_alb_name_prefix" {
2 | type = string
3 | description = "ALB name"
4 | }
5 | variable "nodes_alb_name_tag" {
6 | type = string
7 | description = "ALB name tag"
8 | }
9 | variable "public_subnets" {
10 | type = list(string)
11 | description = "The list of public subnet IDs"
12 | }
13 | variable "alb_sec_group" {
14 | type = string
15 | description = "The security group to place the ALB in"
16 | }
17 | variable "nodes_alb_targetgroup_name_prefix" {
18 | type = string
19 | description = "ALB target group name"
20 | }
21 | variable "vpc_id" {
22 | type = string
23 | description = "VPC id"
24 | }
25 | variable "node_ids" {
26 | type = list(string)
27 | description = "The ids of the nodes to place in targetgroup"
28 | }
29 | variable "alb_ssl_certificate" {
30 | type = string
31 | description = "The SSL certificate ARN for JSON-RPC load balancer"
32 | }
--------------------------------------------------------------------------------
/modules/alb/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.0.0"
3 |
4 | required_providers {
5 | aws = {
6 | source = "hashicorp/aws"
7 | version = ">= 4.22.0"
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/modules/instances/data-sources.tf:
--------------------------------------------------------------------------------
1 | data "aws_ami" "ubuntu_20_04" {
2 | most_recent = true
3 |
4 | filter {
5 | name = "name"
6 | values = ["ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"]
7 | }
8 |
9 | filter {
10 | name = "virtualization-type"
11 | values = ["hvm"]
12 | }
13 |
14 | owners = ["099720109477"]
15 | }
--------------------------------------------------------------------------------
/modules/instances/main.tf:
--------------------------------------------------------------------------------
1 | # create the instance
2 | resource "aws_instance" "polygon_edge_instance" {
3 | ami = data.aws_ami.ubuntu_20_04.id
4 | instance_type = var.instance_type
5 | user_data_base64 = var.user_data_base64
6 | availability_zone = var.az
7 | iam_instance_profile = var.instance_iam_role
8 |
9 | metadata_options {
10 | http_tokens = "required"
11 | http_endpoint = "enabled"
12 | }
13 |
14 | root_block_device {
15 | encrypted = true
16 | tags = {
17 | Name = var.ebs_root_name_tag
18 | }
19 | }
20 |
21 | lifecycle {
22 | ignore_changes = [ami]
23 | }
24 |
25 | tags = {
26 | Name = var.instance_name
27 | }
28 |
29 | # attach the network interface
30 | network_interface {
31 | network_interface_id = aws_network_interface.instance_interface.id
32 | device_index = 0
33 | }
34 | }
35 |
36 | # create the instance network interface
37 | resource "aws_network_interface" "instance_interface" {
38 | subnet_id = var.internal_subnet
39 | security_groups = var.internal_sec_groups
40 |
41 | tags = {
42 | Name = var.instance_interface_name_tag
43 | }
44 | }
45 |
46 | # create the EBS volume to hold the chain data
47 | #tfsec:ignore:aws-ebs-encryption-customer-key
48 | resource "aws_ebs_volume" "chain_data" {
49 |
50 | availability_zone = var.az
51 | size = var.chain_data_ebs_volume_size
52 | encrypted = true
53 |
54 | tags = {
55 | Name = var.chain_data_ebs_name_tag
56 | }
57 | }
58 |
59 | resource "aws_volume_attachment" "attach_chain_data" {
60 |
61 | device_name = "/dev/sdf"
62 | volume_id = aws_ebs_volume.chain_data.id
63 | instance_id = aws_instance.polygon_edge_instance.id
64 | force_detach = true
65 | }
66 |
--------------------------------------------------------------------------------
/modules/instances/outputs.tf:
--------------------------------------------------------------------------------
1 | output "instance_dns_name" {
2 | value = aws_instance.polygon_edge_instance.private_dns
3 | description = "Private dns name of the instance"
4 | }
5 |
6 | output "instance_id" {
7 | value = aws_instance.polygon_edge_instance.id
8 | description = "Instance id"
9 | }
--------------------------------------------------------------------------------
/modules/instances/variables.tf:
--------------------------------------------------------------------------------
1 | variable "instance_type" {
2 | type = string
3 | description = "Polygon Edge nodes instance type. Default: t3.medium"
4 | }
5 | variable "user_data_base64" {
6 | type = string
7 | description = "The base64 encoded data of user data ( cloud-init script )"
8 | }
9 | variable "az" {
10 | type = string
11 | description = "The availability zone of the instance."
12 | }
13 | variable "ebs_root_name_tag" {
14 | type = string
15 | description = "The name tag for the Polygon Edge instance root volume."
16 | }
17 |
18 | variable "instance_name" {
19 | type = string
20 | description = "The name of Polygon Edge instance"
21 | }
22 |
23 | variable "internal_subnet" {
24 | description = "The subnet id in which to place the instance."
25 | type = string
26 | }
27 |
28 | variable "internal_sec_groups" {
29 | type = list(string)
30 | description = "The list of security groups to attach to the instance."
31 | }
32 |
33 | variable "instance_interface_name_tag" {
34 | type = string
35 | description = "The name of the instance interface. Default: Polygon_Edge_Instance_Interface"
36 | }
37 |
38 | variable "chain_data_ebs_volume_size" {
39 | type = number
40 | description = "The size of the chain data EBS volume. Default: 30"
41 | }
42 |
43 | variable "chain_data_ebs_name_tag" {
44 | type = string
45 | description = "The name of the chain data EBS volume. Default: Polygon_Edge_chain_data_ebs_volume"
46 | }
47 |
48 |
49 | variable "instance_iam_role" {
50 | type = string
51 | description = "The IAM role to attach to the instance"
52 | }
--------------------------------------------------------------------------------
/modules/instances/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.0.0"
3 |
4 | required_providers {
5 | aws = {
6 | source = "hashicorp/aws"
7 | version = ">= 4.22.0"
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/modules/security/data-sources.tf:
--------------------------------------------------------------------------------
1 | data "aws_partition" "current" {}
2 |
3 | data "aws_iam_policy" "amazon_ssm_managed_instance_core" {
4 | arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/AmazonSSMManagedInstanceCore"
5 | }
6 |
7 | data "aws_iam_policy_document" "polygon_edge_node" {
8 | version = "2012-10-17"
9 | statement {
10 | actions = [
11 | "ssm:PutParameter",
12 | "ssm:DeleteParameter",
13 | "ssm:GetParameter"
14 | ]
15 | resources = [
16 | "arn:aws:ssm:${var.region}:${var.account_id}:parameter/${var.ssm_parameter_id}/*"
17 | ]
18 | }
19 | statement {
20 | actions = [
21 | "s3:PutObject",
22 | "s3:GetObject",
23 | "s3:DeleteObject"
24 | ]
25 | resources = [
26 | "arn:aws:s3:::${var.s3_shared_bucket_name}/*"
27 | ]
28 | }
29 | statement {
30 | actions = [
31 | "s3:ListBucket"
32 | ]
33 | resources = [
34 | "arn:aws:s3:::${var.s3_shared_bucket_name}"
35 | ]
36 | }
37 | statement {
38 | actions = [
39 | "lambda:InvokeFunction"
40 | ]
41 | resources = [
42 | "arn:aws:lambda:${var.region}:${var.account_id}:function:${var.lambda_function_name}"
43 | ]
44 | }
45 | }
46 |
47 | data "aws_iam_policy_document" "ec2_trust" {
48 | version = "2012-10-17"
49 | statement {
50 | actions = [
51 | "sts:AssumeRole"
52 | ]
53 | principals {
54 | type = "Service"
55 | identifiers = [
56 | "ec2.amazonaws.com"
57 | ]
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/modules/security/iam_roles.tf:
--------------------------------------------------------------------------------
1 | resource "aws_iam_role_policy" "polygon_edge_node" {
2 | name_prefix = "polygon-edge-node-"
3 | role = aws_iam_role.polygon_edge_node.id
4 |
5 | policy = data.aws_iam_policy_document.polygon_edge_node.json
6 | }
7 | resource "aws_iam_role" "polygon_edge_node" {
8 | name_prefix = "polygon-edge-node-"
9 |
10 | assume_role_policy = data.aws_iam_policy_document.ec2_trust.json
11 |
12 | }
13 |
14 | resource "aws_iam_role_policy_attachment" "ssm_role_policy_attach" {
15 | role = aws_iam_role.polygon_edge_node.name
16 | policy_arn = data.aws_iam_policy.amazon_ssm_managed_instance_core.arn
17 | }
18 |
19 | resource "aws_iam_instance_profile" "polygon_edge_node" {
20 | name_prefix = "polygon-edge-node-"
21 | role = aws_iam_role.polygon_edge_node.name
22 | }
23 |
--------------------------------------------------------------------------------
/modules/security/main.tf:
--------------------------------------------------------------------------------
1 | resource "aws_security_group" "polygon_internal" {
2 | name_prefix = "Polygon_Edge_Node_Internal_"
3 | description = "Allow Internal Traffic on Polygon Edge Nodes"
4 | vpc_id = var.vpc_id
5 |
6 | tags = {
7 | Name = var.internal_sec_gr_name_tag
8 | }
9 | }
10 |
11 | resource "aws_security_group" "json_rpc_alb" {
12 | name_prefix = "Poligon_Edge_JSONRPC_ALB_"
13 | description = "Allow External Traffic to ALB"
14 | vpc_id = var.vpc_id
15 |
16 | tags = {
17 | Name = var.alb_sec_gr_name_tag
18 | }
19 | }
20 |
21 | #tfsec:ignore:aws-vpc-no-public-egress-sgr
22 | resource "aws_security_group_rule" "allow_nodes_egress" {
23 | description = "Allow all outbound"
24 | from_port = 0
25 | to_port = 0
26 | protocol = "-1"
27 | cidr_blocks = ["0.0.0.0/0"]
28 | ipv6_cidr_blocks = ["::/0"]
29 | security_group_id = aws_security_group.polygon_internal.id
30 | type = "egress"
31 | }
32 |
33 | resource "aws_security_group_rule" "allow_jsonrpc" {
34 | description = "Allow Public Access to nodes JSON-RPC"
35 | from_port = 8545
36 | to_port = 8545
37 | protocol = "tcp"
38 | source_security_group_id = aws_security_group.json_rpc_alb.id
39 | security_group_id = aws_security_group.polygon_internal.id
40 | type = "ingress"
41 | }
42 |
43 | resource "aws_security_group_rule" "allow_libp2p" {
44 | description = "LibP2P Allow"
45 | from_port = 1478
46 | to_port = 1478
47 | protocol = "tcp"
48 | self = true
49 | security_group_id = aws_security_group.polygon_internal.id
50 | type = "ingress"
51 | }
52 | #tfsec:ignore:aws-vpc-no-public-egress-sgr
53 | resource "aws_security_group_rule" "allow_alb_egress" {
54 | description = "Allow all outbound"
55 | from_port = 0
56 | to_port = 0
57 | protocol = "-1"
58 | cidr_blocks = ["0.0.0.0/0"]
59 | ipv6_cidr_blocks = ["::/0"]
60 | security_group_id = aws_security_group.json_rpc_alb.id
61 | type = "egress"
62 | }
63 | #tfsec:ignore:aws-vpc-no-public-ingress-sgr
64 | resource "aws_security_group_rule" "allow_alb_http" {
65 | description = "Allow HTTP traffic for ALB"
66 | from_port = 80
67 | protocol = "tcp"
68 | security_group_id = aws_security_group.json_rpc_alb.id
69 | to_port = 80
70 | type = "ingress"
71 | cidr_blocks = ["0.0.0.0/0"]
72 | }
73 | #tfsec:ignore:aws-vpc-no-public-ingress-sgr
74 | resource "aws_security_group_rule" "allow_alb_https" {
75 | description = "Allow HTTPS traffic for ALB"
76 | from_port = 443
77 | protocol = "tcp"
78 | security_group_id = aws_security_group.json_rpc_alb.id
79 | to_port = 443
80 | type = "ingress"
81 | cidr_blocks = ["0.0.0.0/0"]
82 | }
83 |
--------------------------------------------------------------------------------
/modules/security/outputs.tf:
--------------------------------------------------------------------------------
1 | output "internal_sec_group_id" {
2 | value = aws_security_group.polygon_internal.id
3 | description = "Internal security group. Should be attached to Polygon Edge nodes only."
4 | }
5 |
6 | output "jsonrpc_sec_group_id" {
7 | value = aws_security_group.json_rpc_alb.id
8 | description = "Jsonrpc instance security group. Should be attached to ALB"
9 | }
10 |
11 | output "ec2_to_assm_iam_policy_id" {
12 | value = aws_iam_instance_profile.polygon_edge_node.id
13 | description = "IAM policy that allows communication between EC2 and ASSM"
14 | }
--------------------------------------------------------------------------------
/modules/security/providers.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.1.0"
3 | required_providers {
4 | aws = {
5 | source = "hashicorp/aws"
6 | version = ">= 4.22.0"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/modules/security/variables.tf:
--------------------------------------------------------------------------------
1 | variable "vpc_id" {
2 | type = string
3 | description = "VPC ID"
4 | }
5 |
6 | variable "internal_sec_gr_name_tag" {
7 | type = string
8 | description = "Internal security group name tag"
9 | }
10 |
11 | variable "s3_shared_bucket_name" {
12 | type = string
13 | description = "The name of the S3 bucket that holds shared polygon-edge nodes data"
14 | }
15 |
16 | variable "ssm_parameter_id" {
17 | description = "The id that will be used for storing and fetching from SSM Parameter Store"
18 | type = string
19 | }
20 |
21 | variable "region" {
22 | type = string
23 | description = "The region in which instances reside. Default: us-west-2"
24 | }
25 |
26 | variable "account_id" {
27 | type = string
28 | description = "The AWS account ID"
29 | }
30 |
31 | variable "alb_sec_gr_name_tag" {
32 | type = string
33 | description = "The name tag for ALB security group"
34 | }
35 |
36 | variable "lambda_function_name" {
37 | type = string
38 | description = "The name of the chain initialization Lambda function"
39 | }
--------------------------------------------------------------------------------
/modules/user-data/main.tf:
--------------------------------------------------------------------------------
1 | locals {
2 | polygon_edge_node = templatefile("${path.module}/scripts/polygon_edge_node.tfpl", {
3 | "polygon_edge_dir" = var.polygon_edge_dir
4 | "ebs_device" = var.ebs_device
5 | "node_name" = var.node_name
6 | "assm_path" = var.assm_path
7 | "assm_region" = var.assm_region
8 | "total_nodes" = var.total_nodes
9 | "s3_bucket_name" = var.s3_bucket_name
10 | "s3_key_name" = var.s3_key_name
11 | "lambda_function_name" = var.lambda_function_name
12 |
13 | "premine" = var.premine
14 | "chain_name" = var.chain_name
15 | "chain_id" = var.chain_id
16 | "pos" = var.pos
17 | "epoch_size" = var.epoch_size
18 | "block_gas_limit" = var.block_gas_limit
19 | "max_validator_count" = var.max_validator_count
20 | "min_validator_count" = var.min_validator_count
21 | "consensus" = var.consensus
22 | })
23 | polygon_edge_server = templatefile("${path.module}/scripts/polygon_edge_server.tfpl", {
24 | "polygon_edge_dir" = var.polygon_edge_dir
25 | "s3_bucket_name" = var.s3_bucket_name
26 | "prometheus_address" = var.prometheus_address
27 | "block_gas_target" = var.block_gas_target
28 | "nat_address" = var.nat_address
29 | "dns_name" = var.dns_name
30 | "price_limit" = var.price_limit
31 | "max_slots" = var.max_slots
32 | "block_time" = var.block_time
33 | })
34 | }
35 |
36 | data "cloudinit_config" "polygon_edge" {
37 | gzip = true
38 | base64_encode = true
39 |
40 | part {
41 | content_type = "text/x-shellscript"
42 | content = local.polygon_edge_node
43 | filename = "01-polygon-edge-node.sh"
44 | }
45 |
46 | part {
47 | content_type = "text/x-shellscript"
48 | content = local.polygon_edge_server
49 | filename = "02-polygon-edge-server.sh"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/modules/user-data/outputs.tf:
--------------------------------------------------------------------------------
1 | output "polygon_edge_node" {
2 | value = data.cloudinit_config.polygon_edge.rendered
3 | description = "Base64 rendered polygon edge node user-init data"
4 | }
5 |
--------------------------------------------------------------------------------
/modules/user-data/scripts/polygon_edge_node.tfpl:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | POLYGON_FOLDER="${polygon_edge_dir}"
3 | LOGS_FOLDER="${polygon_edge_dir}/logs"
4 | DATA_FOLDER="${polygon_edge_dir}/data"
5 | EBS_DEVICE="${ebs_device}"
6 | LOG_FILE="$LOGS_FOLDER/init.log"
7 | NODE_INIT_FILE="$LOGS_FOLDER/node_init.json"
8 | LINUX_USER="ubuntu"
9 | NODE_NAME="${node_name}"
10 | ASSM_PARAM_PATH="/${assm_path}"
11 | ASSM_REGION="${assm_region}"
12 | IP="$(hostname -I)"
13 |
14 | TOTAL_NODES="${total_nodes}"
15 |
16 | # create central dir that will hold chain data and logs
17 | if [ ! -d "$POLYGON_FOLDER" ];
18 | then
19 | mkdir "$POLYGON_FOLDER"
20 | fi
21 | # crate logs dir
22 | if [ ! -d "$LOGS_FOLDER" ];
23 | then
24 | mkdir "$LOGS_FOLDER"
25 | fi
26 |
27 | echo "----------- Script starting on :$(date) ----------------" >> "$LOG_FILE"
28 | ## Check if data directory already exists
29 | if [ -d "$DATA_FOLDER" ];
30 | then
31 | echo "$DATA_FOLDER direcotory is already created. Skipping directory creation..." >> "$LOG_FILE"
32 | else
33 | ERR=$(mkdir "$DATA_FOLDER" 2>&1 > /dev/null)
34 | [ $? -eq 0 ] && echo "Data directory created!" >> "$LOG_FILE" || echo "Directory creation failed! Error: $ERR" >> "$LOG_FILE"
35 | fi
36 |
37 | # Wait for the system to fully boot before attemting to mount the EBS
38 | counter=0
39 | while [ ! -b "$EBS_DEVICE" ];
40 | do
41 | sleep 10
42 | counter=$((counter + 1))
43 | if [ $counter -ge 30 ];
44 | then
45 | echo "Could not initialize $EBS_DEVICE for more than 5 minutes. Exiting ...">> "$LOG_FILE"
46 | exit 1
47 | fi
48 | done
49 |
50 | # Check if EBS storage exists
51 | if [ -b "$EBS_DEVICE" ];
52 | then
53 | # Check if we already have a filesystem on $EBS_DEVICE. If not create one.
54 | if [[ $(file -s "$EBS_DEVICE") == "$EBS_DEVICE: data" ]];
55 | then
56 | ERR=$(mkfs -t xfs "$EBS_DEVICE" 2>&1 > /dev/null)
57 | [ $? -eq 0 ] && echo "EBS volume successfuly formated" >> "$LOG_FILE" || echo "Error formating EBS volume. ERR: $ERR" >> "$LOG_FILE"
58 | else
59 | echo "$EBS_DEVICE already has a file system! Skipping... ">> "$LOG_FILE"
60 | fi
61 | # If block ebs device exists and it has a filesystem, check if it is mounted and mount if it is not already mounted
62 | if grep -qs "$EBS_DEVICE" /proc/mounts;
63 | then
64 | echo "EBS Volume already mounted. Skipping..." >> "$LOG_FILE"
65 | else
66 | # Mount EBS volume
67 | mount "$EBS_DEVICE" "$DATA_FOLDER"
68 | [ $? -eq 0 ] && echo "EBS Volume successfuly mounted!" >> "$LOG_FILE"
69 | ## Edit fstab
70 | echo "$EBS_DEVICE $DATA_FOLDER xfs defaults,nofail 0 2" >> /etc/fstab
71 | [ $? -eq 0 ] && echo "Fstab successfuly edited. EBS Volume added." >> "$LOG_FILE" || echo "Fstab EBS volume edit failed!" >> "$LOG_FILE"
72 | ## Set right permissions for ubuntu user
73 | chown -R "$LINUX_USER". "$DATA_FOLDER"
74 | [ $? -eq 0 ] && echo "User permissions on $DATA_FOLDER set..." >> $LOG_FILE || echo "Could not set $DATA_FOLDER permissions!"
75 | fi
76 | else
77 | echo "No EBS Volume detected! Check if it is attached and/or if it is mounted on $EBS_DEVICE" >> "$LOG_FILE"
78 | fi
79 |
80 | echo "--------------------------------------------------------------" >> "$LOG_FILE"
81 | echo "Getting polygon-edge binary ..." >> "$LOG_FILE"
82 |
83 | # wait for system to fully boot
84 | sleep 60
85 |
86 | # install packages
87 | sudo apt update && sudo apt install -y jq awscli
88 |
89 | # get polygon-edge binary from github releases #77894422 - v0.5.1
90 | mkdir /tmp/polygon-edge
91 | wget -q -O /tmp/polygon-edge/polygon-edge.tar.gz "$(curl -s https://api.github.com/repos/0xPolygon/polygon-edge/releases/77894422 | jq .assets[3].browser_download_url | tr -d '"')"
92 | tar -xvf /tmp/polygon-edge/polygon-edge.tar.gz -C /tmp/polygon-edge
93 | sudo mv /tmp/polygon-edge/polygon-edge /usr/local/bin/
94 | rm -R /tmp/polygon-edge
95 |
96 |
97 | # create json lambda query
98 | jsonLambda=$(cat <> "$LOG_FILE"
128 | echo "System init complete. Starting node initialization ..." >> "$LOG_FILE"
129 |
130 | # Check if data folder is empty
131 | if [ "$(ls -A "$DATA_FOLDER")" ]; then
132 | echo "Files in data dir found. Skipping Polygon edge initialization..." >> "$LOG_FILE"
133 | else
134 | echo "Starting node initialization" >> "$LOG_FILE"
135 | /usr/local/bin/polygon-edge secrets generate --type aws-ssm --name "$NODE_NAME" --extra region="$ASSM_REGION",ssm-parameter-path="$ASSM_PARAM_PATH" --dir "$DATA_FOLDER/secretsConfig.json"
136 | POLYGON=$(/usr/local/bin/polygon-edge secrets init --config "$DATA_FOLDER"/secretsConfig.json --json 2>&1)
137 | # Check if everything went ok
138 | if [ $? -eq 0 ]; then
139 | aws lambda invoke --function-name "${lambda_function_name}" --region "$ASSM_REGION" --payload "$jsonLambda" "$LOGS_FOLDER/lambda.json"
140 | else
141 | echo "The initialization of polygon edge failed. Check log @ $LOG_FILE" >> "$LOG_FILE"
142 | echo "$POLYGON" >> "$LOG_FILE"
143 | fi
144 | fi
145 |
146 | chown -R "$LINUX_USER". "$POLYGON_FOLDER"
147 |
148 | echo "ALL DONE!" >> "$LOG_FILE"
149 | echo "-------- Finished on: $(date) ----------" >> "$LOG_FILE"
150 | exit 0
151 |
--------------------------------------------------------------------------------
/modules/user-data/scripts/polygon_edge_server.tfpl:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | DATA_DIR="${polygon_edge_dir}/data"
3 | SECRETS_FILE="$DATA_DIR/secretsConfig.json"
4 | GENESIS_FILE="$DATA_DIR/genesis.json"
5 | S3_GENESIS="${s3_bucket_name}/genesis.json"
6 | LOG_FILE="${polygon_edge_dir}/logs/edge-server.log"
7 |
8 | # install awscli so that we can fetch genesis.json
9 | sudo apt update && sudo apt install -y awscli
10 |
11 | # wait untill genesis.json is found in the S3 bucket
12 | while ! aws s3 ls ${s3_bucket_name} | grep genesis.json > /dev/null;
13 | do
14 | echo "Waiting for genesis.json to appear in S3 bucket..."
15 | sleep 10
16 | done
17 |
18 | # we found genesis.json, now we download it to data folder
19 | aws s3 cp s3://$S3_GENESIS $DATA_DIR
20 |
21 |
22 | # Create polygon-edge service and start it after genesis.json file is detected
23 | cat > /etc/systemd/system/polygon_genesis.target << EOF
24 | # check if genesys.json exists
25 | [Unit]
26 | TimeoutStartSec=infinity
27 | ConditionPathExists=$GENESIS_FILE
28 | ExecStart=/usr/bin/sleep 5
29 | RemainAfterExit=yes
30 | EOF
31 |
32 | ## TODO make server options customisable
33 | cat > /etc/systemd/system/polygon-edge.service << EOF
34 | [Unit]
35 | Description=Polygon Edge Server
36 | After=network.target polygon_genesis.target
37 | Wants=polygon_genesis.target
38 | StartLimitIntervalSec=0
39 |
40 | [Service]
41 | Type=simple
42 | Restart=always
43 | RestartSec=10
44 | User=ubuntu
45 | StandardOutput=syslog
46 | StandardError=file:$LOG_FILE
47 | ExecStartPre=/bin/bash -c "sudo rm $LOG_FILE"
48 | ExecStart=/usr/local/bin/polygon-edge server --data-dir $DATA_DIR --chain $GENESIS_FILE --libp2p 0.0.0.0:1478 --grpc-address 0.0.0.0:9632 --secrets-config $SECRETS_FILE --seal SERVER_OPTIONS
49 |
50 | [Install]
51 | WantedBy=multi-user.target
52 | EOF
53 |
54 | #### customise server options
55 |
56 | # enable Prometheus API
57 | if [ "${prometheus_address}" != "" ]; then
58 | prometheus="--prometheus ${prometheus_address}"
59 | fi
60 |
61 | # set block gas limit
62 | if [ "${block_gas_target}" != "" ]; then
63 | block_gas_target="--block-gas-target ${block_gas_target}"
64 | fi
65 |
66 | # set nated address
67 | if [ "${nat_address}" != "" ]; then
68 | nat="--nat ${nat_address}"
69 | fi
70 |
71 | # set dns name
72 | if [ "${dns_name}" != "" ]; then
73 | dns_name="--dns ${dns_name}"
74 | fi
75 |
76 | # set price limit
77 | if [ "${price_limit}" != "" ]; then
78 | price_limit="--price-limit ${price_limit}"
79 | fi
80 |
81 | # set max slots
82 | if [ "${max_slots}" != "" ]; then
83 | max_slots="--max-slots ${max_slots}"
84 | fi
85 |
86 | # set block time in seconds
87 | if [ "${block_time}" != "" ]; then
88 | block_time="--block-time ${block_time}"
89 | fi
90 |
91 | # set these parameters in service file
92 | sed -i "s/SERVER_OPTIONS/$prometheus $block_gas_target $nat $dns $price_limit $max_slots $block_time/g" /etc/systemd/system/polygon-edge.service
93 |
94 | # change ownership of the polygon folder to ubuntu user
95 | sudo chown -R ubuntu. ${polygon_edge_dir}
96 |
97 | # Enable polygon-edge on startup
98 | sudo /usr/bin/systemctl enable polygon-edge.service
99 |
100 | # Start polygon-edge service
101 | sudo /usr/bin/systemctl start polygon-edge.service
102 |
--------------------------------------------------------------------------------
/modules/user-data/variables.tf:
--------------------------------------------------------------------------------
1 | variable "polygon_edge_dir" {
2 | type = string
3 | description = "The directory to place all polygon-edge data and logs"
4 | }
5 | variable "ebs_device" {
6 | type = string
7 | description = "The ebs device path. Defined when creating EBS volume."
8 | }
9 | variable "assm_region" {
10 | type = string
11 | description = "The region for AWS SSM service."
12 | }
13 | variable "assm_path" {
14 | type = string
15 | description = "The SSM paramter path."
16 | }
17 | variable "node_name" {
18 | type = string
19 | description = "The name of the node that will be different for every node and stored in AWS SSM"
20 | }
21 | variable "total_nodes" {
22 | type = string
23 | description = "The total number of validator nodes."
24 | }
25 | variable "s3_bucket_name" {
26 | type = string
27 | description = "The name of the S3 bucket that holds genesis.json."
28 | }
29 |
30 | ## genesis options
31 | variable "chain_name" {
32 | type = string
33 | description = "Set the name of chain"
34 | }
35 | variable "chain_id" {
36 | type = string
37 | description = "Set the Chain ID"
38 | }
39 | variable "block_gas_limit" {
40 | type = string
41 | description = "Set the block gas limit"
42 | }
43 | variable "premine" {
44 | type = string
45 | description = "Premine the accounts with the specified ammount."
46 | }
47 | variable "epoch_size" {
48 | type = string
49 | description = "Set the epoch size"
50 | }
51 | variable "pos" {
52 | type = bool
53 | description = "Deploy with PoS consensus"
54 | }
55 | variable "max_validator_count" {
56 | type = string
57 | description = "Maximum number of stakers able to join the validator set in a PoS consensus."
58 | }
59 | variable "min_validator_count" {
60 | type = string
61 | description = "Minimum number of stakers needed to join the validator set in a PoS consensus."
62 | }
63 | variable "consensus" {
64 | type = string
65 | description = "Sets the consensus protocol"
66 | }
67 |
68 | # server options
69 | variable "prometheus_address" {
70 | type = string
71 | description = "Enable Prometheus API"
72 | }
73 | variable "block_gas_target" {
74 | type = string
75 | description = "Sets the target block gas limit for the chain"
76 | }
77 | variable "nat_address" {
78 | type = string
79 | description = "Sets the NAT address for the networking package"
80 | }
81 | variable "dns_name" {
82 | type = string
83 | description = "Sets the DNS name for the network package"
84 | }
85 | variable "price_limit" {
86 | type = string
87 | description = "Sets minimum gas price limit to enforce for acceptance into the pool"
88 | }
89 | variable "max_slots" {
90 | type = string
91 | description = "Sets maximum slots in the pool"
92 | }
93 | variable "block_time" {
94 | type = string
95 | description = "Set block production time in seconds"
96 | }
97 |
98 | variable "lambda_function_name" {
99 | type = string
100 | description = "The name of the chain init Lambda function"
101 | }
102 |
103 | variable "s3_key_name" {
104 | type = string
105 | description = "Name for the config file stored in S3"
106 | }
--------------------------------------------------------------------------------
/modules/user-data/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.0.0"
3 |
4 | required_providers {
5 | aws = {
6 | source = "hashicorp/aws"
7 | version = ">= 4.22.0"
8 | }
9 | cloudinit = {
10 | source = "hashicorp/cloudinit"
11 | version = "2.2.0"
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/outputs.tf:
--------------------------------------------------------------------------------
1 | output "jsonrpc_dns_name" {
2 | value = module.alb.dns_name
3 | description = "The dns name for the JSON-RPC API"
4 | }
--------------------------------------------------------------------------------
/providers.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_version = ">= 1.3.0"
3 | required_providers {
4 | aws = {
5 | source = "hashicorp/aws"
6 | version = ">= 4.22.0"
7 | }
8 | awscc = {
9 | source = "hashicorp/awscc"
10 | version = ">= 0.27.0"
11 | }
12 | local = {
13 | source = "hashicorp/local"
14 | version = ">= 2.2.3"
15 | }
16 | external = {
17 | source = "hashicorp/external"
18 | version = ">= 2.2.2"
19 | }
20 | null = {
21 | source = "hashicorp/null"
22 | version = ">=3.1.1"
23 | }
24 | }
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/test/examples_basic_test.go:
--------------------------------------------------------------------------------
1 | package test
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/gruntwork-io/terratest/modules/terraform"
7 | )
8 |
9 | func TestExamplesBasic(t *testing.T) {
10 |
11 | terraformOptions := &terraform.Options{
12 | TerraformDir: "../examples/basic",
13 | // Vars: map[string]interface{}{
14 | // "myvar": "test",
15 | // "mylistvar": []string{"list_item_1"},
16 | // },
17 | }
18 |
19 | defer terraform.Destroy(t, terraformOptions)
20 | terraform.InitAndApply(t, terraformOptions)
21 | }
22 |
--------------------------------------------------------------------------------
/variables.tf:
--------------------------------------------------------------------------------
1 | # VPC
2 | variable "vpc_name" {
3 | description = "Name of the VPC"
4 | type = string
5 | default = "polygon-edge-vpc"
6 | }
7 | variable "vpc_cidr_block" {
8 | description = "CIDR block for VPC"
9 | type = string
10 | default = "10.250.0.0/16"
11 | }
12 |
13 | # S3
14 | variable "s3_bucket_prefix" {
15 | description = "Name prefix for new S3 bucket"
16 | type = string
17 | default = "polygon-edge-shared-"
18 | }
19 | variable "s3_force_destroy" {
20 | type = bool
21 | default = true
22 | description = "Delete S3 bucket on destroy, even if the bucket is not empty"
23 | }
24 | variable "s3_key_name" {
25 | type = string
26 | description = "Name of the file in S3 that will hold configuration"
27 | default = "chain-config"
28 | }
29 |
30 | # SECURITY
31 | variable "ssm_parameter_id" {
32 | description = "The id that will be used for storing and fetching from SSM Parameter Store"
33 | type = string
34 | default = "polygon-edge-validators"
35 | }
36 | variable "internal_sec_gr_name_tag" {
37 | type = string
38 | description = "Internal security group name tag"
39 | default = "Polygon Edge Internal"
40 | }
41 | variable "alb_sec_gr_name_tag" {
42 | type = string
43 | description = "External security group name tag"
44 | default = "Polygon Edge External"
45 | }
46 |
47 | # EC2
48 | variable "instance_type" {
49 | default = "t3.medium"
50 | type = string
51 | description = "Polygon Edge nodes instance type."
52 | }
53 | variable "ebs_root_name_tag" {
54 | default = "Polygon_Edge_Root_Volume"
55 | type = string
56 | description = "The name tag for the Polygon Edge instance root volume."
57 | }
58 | variable "instance_name" {
59 | default = "Polygon_Edge_Node"
60 | type = string
61 | description = "The name of Polygon Edge instance"
62 | }
63 | variable "instance_interface_name_tag" {
64 | default = "Polygon_Edge_Instance_Interface"
65 | type = string
66 | description = "The name of the instance interface."
67 | }
68 | variable "chain_data_ebs_volume_size" {
69 | default = 30
70 | type = number
71 | description = "The size of the chain data EBS volume."
72 | }
73 | variable "chain_data_ebs_name_tag" {
74 | default = "Polygon_Edge_chain_data_volume"
75 | type = string
76 | description = "The name of the chain data EBS volume."
77 | }
78 |
79 | #CHAIN
80 | variable "polygon_edge_dir" {
81 | default = "/home/ubuntu/polygon"
82 | type = string
83 | description = "The directory to place all polygon-edge data and logs"
84 | }
85 |
86 | variable "ebs_device" {
87 | default = "/dev/nvme1n1"
88 | type = string
89 | description = "The ebs device path. Defined when creating EBS volume."
90 | }
91 |
92 | variable "node_name_prefix" {
93 | type = string
94 | description = "The name prefix that will be used to store secrets"
95 | default = "node"
96 | }
97 |
98 | ## GENESIS
99 | variable "chain_name" {
100 | type = string
101 | description = "Set the name of chain"
102 | default = ""
103 | }
104 |
105 | variable "chain_id" {
106 | type = string
107 | description = "Set the Chain ID"
108 | default = ""
109 | }
110 |
111 | variable "block_gas_limit" {
112 | type = string
113 | description = "Set the block gas limit"
114 | default = ""
115 | }
116 |
117 | variable "premine" {
118 | type = string
119 | description = "Premine the accounts with the specified ammount. Format: account:ammount,account:ammount"
120 | }
121 |
122 | variable "epoch_size" {
123 | type = string
124 | description = "Set the epoch size"
125 | default = ""
126 | }
127 | variable "consensus" {
128 | type = string
129 | description = "Sets consensus protocol."
130 | default = ""
131 | }
132 | variable "max_validator_count" {
133 | type = string
134 | description = "The maximum number of stakers able to join the validator set in a PoS consensus."
135 | default = ""
136 | }
137 | variable "min_validator_count" {
138 | type = string
139 | description = "The minimum number of stakers needed to join the validator set in a PoS consensus."
140 | default = ""
141 | }
142 | variable "pos" {
143 | type = bool
144 | description = "Use PoS IBFT consensus"
145 | default = false
146 | }
147 | # server options
148 | variable "prometheus_address" {
149 | type = string
150 | description = "Enable Prometheus API"
151 | default = ""
152 | }
153 | variable "block_gas_target" {
154 | type = string
155 | description = "Sets the target block gas limit for the chain"
156 | default = ""
157 | }
158 | variable "nat_address" {
159 | type = string
160 | description = "Sets the NAT address for the networking package"
161 | default = ""
162 | }
163 | variable "dns_name" {
164 | type = string
165 | description = "Sets the DNS name for the network package"
166 | default = ""
167 | }
168 | variable "price_limit" {
169 | type = string
170 | description = "Sets minimum gas price limit to enforce for acceptance into the pool"
171 | default = ""
172 | }
173 | variable "max_slots" {
174 | type = string
175 | description = "Sets maximum slots in the pool"
176 | default = ""
177 | }
178 | variable "block_time" {
179 | type = string
180 | description = "Set block production time in seconds"
181 | default = ""
182 | }
183 |
184 | #ALB
185 | variable "alb_ssl_certificate" {
186 | type = string
187 | description = "SSL certificate ARN for JSON-RPC loadblancer"
188 | }
189 | variable "nodes_alb_name_prefix" {
190 | type = string
191 | description = "ALB name"
192 | default = "jrpc-"
193 | }
194 | variable "nodes_alb_name_tag" {
195 | type = string
196 | description = "ALB name tag"
197 | default = "Polygon Edge JSON-RPC ALB"
198 | }
199 | variable "nodes_alb_targetgroup_name_prefix" {
200 | type = string
201 | description = "ALB target group name"
202 | default = "jrpc-"
203 | }
204 | # LAMBDA
205 | variable "lambda_function_name" {
206 | type = string
207 | description = "The name of the Lambda function used for chain init"
208 | default = "polygon-edge-init"
209 | }
210 | variable "lambda_function_zip" {
211 | type = string
212 | description = "The lambda function code in zip archive"
213 | default = "https://raw.githubusercontent.com/Trapesys/polygon-edge-assm/aws-lambda/artifacts/main.zip"
214 | }
215 |
--------------------------------------------------------------------------------