├── .gitignore
├── .terraform.lock.hcl
├── README.md
├── flow_rules.tpl
├── init-demolab.tpl
├── main.tf
├── modules
├── ali
│ ├── main.tf
│ ├── variables.tf
│ └── versions.tf
├── aws
│ ├── main.tf
│ ├── variables.tf
│ └── versions.tf
├── azu
│ ├── main.tf
│ ├── variables.tf
│ └── versions.tf
├── do
│ ├── main.tf
│ ├── variables.tf
│ └── versions.tf
├── eqx
│ ├── main.tf
│ ├── variables.tf
│ └── versions.tf
├── gcp
│ ├── main.tf
│ ├── variables.tf
│ └── versions.tf
├── ibm
│ ├── main.tf
│ ├── variables.tf
│ └── versions.tf
├── oci
│ ├── main.tf
│ └── variables.tf
└── vul
│ ├── main.tf
│ ├── variables.tf
│ └── versions.tf
├── outputs.tf
├── provider.tf
├── variables.tf
└── versions.tf
/.gitignore:
--------------------------------------------------------------------------------
1 | # Local .terraform directories
2 | **/.terraform/*
3 | .terraform
4 |
5 | .tfstate files
6 | *.tfstate
7 | *.tfstate.*
8 |
9 | # Crash log files
10 | crash.log
11 |
12 | # Exclude all .tfvars files, which are likely to contain sentitive data, such as
13 | # password, private keys, and other secrets. These should not be part of version
14 | # control as they are data points which are potentially sensitive and subject
15 | # to change depending on the environment.
16 | #
17 | # *.tfvars
18 |
19 | # Ignore override files as they are usually used to override resources locally and so
20 | # are not checked in
21 | override.tf
22 | override.tf.json
23 | *_override.tf
24 | *_override.tf.json
25 |
26 | # Include override files you do wish to add to version control using negated pattern
27 | #
28 | # !example_override.tf
29 |
30 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
31 | # example: *tfplan*
32 |
33 | # Ignore CLI configuration files
34 | .terraformrc
35 | terraform.rc
36 |
--------------------------------------------------------------------------------
/.terraform.lock.hcl:
--------------------------------------------------------------------------------
1 | # This file is maintained automatically by "terraform init".
2 | # Manual edits may be lost in future updates.
3 |
4 | provider "registry.terraform.io/aliyun/alicloud" {
5 | version = "1.133.0"
6 | hashes = [
7 | "h1:X4RyQI3IHTXbYksMCpCo7kPxU2IZEC2XnOjq8AgOdDQ=",
8 | "zh:037ad5d1ce60b349b3ff96586b121e13f71c047994c40ee3a6938e7bb2ff31a2",
9 | "zh:0aac42cc554ed85018facd2a8f740e8bebea9cbfc27bc2eee431be12bd777f8d",
10 | "zh:199f0fdda1b83a29a5bd24522830ac4363a1771c2a2ce48f2fa48c8935748701",
11 | "zh:5cdfda23a8ae6e3795bbef660e7f2f0e4d6c3dc4248a4fbf5934a4118fbaa62f",
12 | "zh:6c4d7993fb5b6e61cdafbbeb037bd19d02ffb23328464b5fa9e7daa65cefd716",
13 | "zh:a3a4238929a55ee63c0bd1578cdb92072e60665c6e6d0fb7a3a6279087a8ec98",
14 | "zh:a6ef973052babaf84469671eca2d33856dc7605f24996d5cd1c99cdb9b22bac5",
15 | "zh:b11c79e4a359fc825271ba64cd38e36a7041bcb9078cbcec3580cdce010588a6",
16 | "zh:b2cfd2f9afba6631db02611ae8ff37cf23513669d3e7d0e20b8187fddbf5be94",
17 | "zh:b4b789d735b7d87264853b86f072370b6ac5d21f87877732b2ff0c8c3e014190",
18 | "zh:c5dd6e519fd98d3305971b9f0ec95e99d5c17b305ec4379e5a75a7fd903ca21e",
19 | "zh:c753f0d18038ec3c7ca3e95a07cbba8b1e99ba67f3645e1df0f862e2951b10fb",
20 | "zh:ee07b5508a9423e53dc9f28f4e57abb80c3eeffbf609f63b63190b3f04e3d79f",
21 | ]
22 | }
23 |
24 | provider "registry.terraform.io/digitalocean/digitalocean" {
25 | version = "2.11.1"
26 | hashes = [
27 | "h1:/vPcLUkdXevyzqVi2KdygASF2PhzCq+/R49rgoz164U=",
28 | "zh:0b379be98de2927eb751db4e88e69b14c979b97f33ff5b3f7840e27bbb3b10c4",
29 | "zh:28cdc581a684681eee6c871da76f0a6e9a99c4ae7dfabb0ffbc62c2566299a3b",
30 | "zh:41c332b015af4b0b08c7f34c57bd1f6e687ab78134d8fa891aba2357e2b0ced5",
31 | "zh:537da9f53842fe9c3ee3d8ae7ff8ee5a847a879ec881a9cf087e677d36e642c0",
32 | "zh:621782735bb0fa0e9b12750a6e91c2ef0d144185201435f0f70a95b66aa08dd1",
33 | "zh:7f59913181a0fb6d78ccfdac19f17d4b693f67776baf8ffb6a9cd07088804be8",
34 | "zh:9b164e204085691876535233eb57cd6fdf223a0eff529ceb072b61f4fea2f950",
35 | "zh:9d3c87e40b11f19bb0e59b369663d05b03e37d3095200a7b40e7bc7e04e3a303",
36 | "zh:9fe3ec149fcbe25f987b02e59d810a8fd836a9f4b4b7a9ed7273800c8020cc87",
37 | "zh:a426e765a4df74b21196d2faf4786bec2a7fdc2c21bf1137a7fb9a1c7e1cf1fc",
38 | "zh:aae6361881b90802d5fad20ca2cd7330e118be09cf583b79b9276e033b09beb4",
39 | "zh:c760535090ec85194e8248c823439a1692be72e4f337916f63c742ee8cb1f7cd",
40 | "zh:eca7a79f7c3611b79a1013b3390ac917043ac19cd648bda8c87e4b7714b27b00",
41 | "zh:f221a20df002cffac09dba1734a1f77217dd546705fd1b123951d03c0fff6592",
42 | "zh:f62b7120420cc0d3ff9d291aa1aae20b5868dd47d453cb3a33b915615f5cdb29",
43 | ]
44 | }
45 |
46 | provider "registry.terraform.io/equinix/metal" {
47 | version = "3.1.0"
48 | hashes = [
49 | "h1:9qdLMOS8tATAqb9IDjP7tfaC0hpWU7dJquvha84Basc=",
50 | "zh:070b77456a3de02b623be29f1752b48c17100533a98661b7c6555ad2b60aeb1b",
51 | "zh:140b4bfb72810b2e977e7fe442a5b69096db93fe3daff6e307574a541079eb42",
52 | "zh:1d9bf5ba1e05b55af6f5d3e37fa9c0dd4c9fed02e0d47112945c16561f10fe38",
53 | "zh:2b380fb077bb21296e1560c37efd051a7bb31fa488fbe525f54a166ace8cccc8",
54 | "zh:5120fd9f3164e06473d813206e4f06d3b330a351ef6f59114546e0170a49f542",
55 | "zh:5b131619662a36af7f0691c9505d24462533965391039b5664855e50b64db7c4",
56 | "zh:7d3b2f2e2d1a0073ae54070e5dee37713726f6401f26d046db8a5cf58c80cebc",
57 | "zh:7d40ec64e8a3f77c272c9e0cfc8bb6fc148e11a130ef0f022291947313798ec1",
58 | "zh:7fe35422891a14ed233080303468221d8a9f98cd74b0fe73688569cbfc19ff4c",
59 | "zh:a9bb9bfe0928bbc15c132004b3ef573983bb14cc59af9fb492b4f59f2d159054",
60 | "zh:bd7000a809e2e96f87ac7ef90c953a05a8813feb220f27cccc50232f1ae02a50",
61 | "zh:c0656d79d71b893fabcc8420774b8c2d5e2c77feb2b7fecd5dfb9be24228d392",
62 | "zh:f1c755853d4c988fc616e8b1cf8407ed425a0e9ef35a441c0e9eb37657aa9907",
63 | ]
64 | }
65 |
66 | provider "registry.terraform.io/hashicorp/aws" {
67 | version = "3.56.0"
68 | hashes = [
69 | "h1:XuiEcFvwm+GkRpt4MqfwJpfdU2BssSjpyqMkqrI3Pjs=",
70 | "zh:001373be6fbc5738bf8c3aa8688b248ba5f99b04174310c0efcbbf23e6c4dc29",
71 | "zh:0d4af59266668089790f5a7bdeb25642ba750fb5dc7934fe28d1cc36310ba495",
72 | "zh:1413ff4d445678c096d46e8957e27320df94561354955d7bc5d8054b6df7b299",
73 | "zh:19d614259f7ce16b50ec07868404b58749702baaf86bcd14fbaea2756e1c9f25",
74 | "zh:2d148ff632da25852622b06b5be9f5a0b6d509621a002a47338f96509021945b",
75 | "zh:3959a1d989c99f3e7cdd5de07eb3e7df7a85e19677488278c77ab753dd7127e6",
76 | "zh:5d8d65b458a8934dc67d22904da368b5bc3a77fb9c900ac89c54e736a221b76f",
77 | "zh:94d5660e56118fcaa40fccaff960a9bf4166b7b0e7fedeb21b2402c8fc7b4cb1",
78 | "zh:a6002ecc23ebc468ccac6f36c0ed7cc95de3223ef6b100e6c81762d22cc14077",
79 | "zh:b0880c82bc2ad395ef3dbf5a592a23e65bf943df8995d5d4238740f96a02f529",
80 | "zh:ee65f3d2c13653e0828222a63fb832f98e9835b84443eeca00ce36ae39783c08",
81 | ]
82 | }
83 |
84 | provider "registry.terraform.io/hashicorp/azurerm" {
85 | version = "2.78.0"
86 | hashes = [
87 | "h1:IDrQ8EzMT+GtLBo+8JWR45kVDCldVZ0Ty2cFXBBuu2A=",
88 | "zh:044cd4bdefca17e90e887ae7180ab5eef41a46e919fba44541560c03be09b39f",
89 | "zh:065fa80ed6bcadbe1c439b0b57ed60847402dd75b12c02f3c09792775753049d",
90 | "zh:2b7f0e8ca7078b3ed229bad2fd054b16d7b70fb6ee7c6e9b6a48d49d8d374cbb",
91 | "zh:50d2f733803affbdcc5583ad47050e38ad9407e2f712cd747d80c1e2de154609",
92 | "zh:6272819858557c239f3b058fd36228c7ed57a4c88f066de5994cfb2d2bca2732",
93 | "zh:76f92d1d4381ce565f50c3315cf9a8bf1236ae117abb68ee60dc62d86d9c59b3",
94 | "zh:bd6d9d77689bfb047c91a034fef701f71b567ec8645183b2f92a486984e46a2f",
95 | "zh:c9613c480efbf1c9388e89f81f35e38aa589dc18b3156b6154c06b3118320cdc",
96 | "zh:e0117a4c429dce0169280d352444fb6e38abcfe4d5dbdb18e33f6a1439b893a2",
97 | "zh:e71b48f08f25c8089b38038b02a2d8ab3cb755fed992666a0826c764faa2f193",
98 | "zh:eff6420f774247e2fdc8576e82f62d8d6d1312985fba89fe37616fa61a6fcf75",
99 | ]
100 | }
101 |
102 | provider "registry.terraform.io/hashicorp/cloudinit" {
103 | version = "2.2.0"
104 | hashes = [
105 | "h1:siiI0wK6/jUDdA5P8ifTO0yc9YmXHml4hz5K9I9N+MA=",
106 | "zh:76825122171f9ea2287fd27e23e80a7eb482f6491a4f41a096d77b666896ee96",
107 | "zh:795a36dee548e30ca9c9d474af9ad6d29290e0a9816154ad38d55381cd0ab12d",
108 | "zh:9200f02cb917fb99e44b40a68936fd60d338e4d30a718b7e2e48024a795a61b9",
109 | "zh:a33cf255dc670c20678063aa84218e2c1b7a67d557f480d8ec0f68bc428ed472",
110 | "zh:ba3c1b2cd0879286c1f531862c027ec04783ece81de67c9a3b97076f1ce7f58f",
111 | "zh:bd575456394428a1a02191d2e46af0c00e41fd4f28cfe117d57b6aeb5154a0fb",
112 | "zh:c68dd1db83d8437c36c92dc3fc11d71ced9def3483dd28c45f8640cfcd59de9a",
113 | "zh:cbfe34a90852ed03cc074601527bb580a648127255c08589bc3ef4bf4f2e7e0c",
114 | "zh:d6ffd7398c6d1f359b96f5b757e77b99b339fbb91df1b96ac974fe71bc87695c",
115 | "zh:d9c15285f847d7a52df59e044184fb3ba1b7679fd0386291ed183782683d9517",
116 | "zh:f7dd02f6d36844da23c9a27bb084503812c29c1aec4aba97237fec16860fdc8c",
117 | ]
118 | }
119 |
120 | provider "registry.terraform.io/hashicorp/google" {
121 | version = "3.81.0"
122 | hashes = [
123 | "h1:Ev/yvXGStJUYib3P+/FzEl7owd9vj+3Plwb++96a0H4=",
124 | "zh:2f0678805d7f062571a8adb5171ff284fa96fb0dbc7973677a7172346840c50e",
125 | "zh:3519a3cd4cbe9f8e8bc476ddb7dd2402c21c094c9edf2b648a08a65e52e654ec",
126 | "zh:3d84e4a033a5bd43e661b6e2f167d88454628cea98c07a7e32e7656571c67b6d",
127 | "zh:3ffe0015881d549ca0c9a1a03f51ddfc09e0e34e68b63692a7fd491fb38a82f1",
128 | "zh:56df6ac67407e79fe59fc0c4c0fc6514e8691a976deda3350879c2a53f77c2c8",
129 | "zh:6cc64fdadc0875e83d2fb01a4905f70ac21976e36a1300112791db2f6284e9cd",
130 | "zh:6e0c4ce6cc080a259c442a0aa3aee032c9a69b08bff766013b55dc60e125e775",
131 | "zh:743d2c3928a34abb3dd5e4042ec872450dedeb26800192a6bdb1a8f68f471857",
132 | "zh:de58d266faf4bdd06414a117a6c9a0b36e3cd39d43e30b92762172d0b7924b63",
133 | "zh:eabae93a170dd0e5d0a229bc7945960cde68880544e51fb2f6a9bd90251820f5",
134 | "zh:eb5a06a3a7d22f60354c3d639667234e6f02c1c140ca69969007c1a315cf594b",
135 | ]
136 | }
137 |
138 | provider "registry.terraform.io/hashicorp/oci" {
139 | version = "4.41.0"
140 | hashes = [
141 | "h1:DC6r7BHQ277FCkPoAPBNJDqZxZZ6PacXoHWLovbU7l4=",
142 | "zh:03ed0ba0eeb7cf926b407c575c285ffc2c45edcb36154116a59d443b16738e92",
143 | "zh:074a02105ef2988c9646b21f793030a805aefcefe273822d5c41285dcaddf956",
144 | "zh:3a6e091fccd3e27040b36bd1ac5d0b47485174667218c6af47802b184d1d3fdc",
145 | "zh:3def971d5d5dbff33c1b7264654378f143d76d5de7430c187710607a0c8e0b22",
146 | "zh:3f343b4f87716f6269b16d7f71eeb8c66db8e6764e25071a9b288daeb1f9464d",
147 | "zh:52a8c88ea9c96022dfb60fe6973cb6b94aac6fc2f922dcc444bf1925dc3db7fd",
148 | "zh:7bd3ed0c4ffcee1dd280104ee79d7a6f2b513b8f4e4a6395f1bbf3894b41527a",
149 | "zh:a116e61180a033a5804d4f82e31d066f340c00029716aa3b4c0af6e098fb6826",
150 | "zh:b2c94b1f25c8a65e6254a9b2602996963a1a22cba6703ec7803faefc2997fcc6",
151 | "zh:ca562056592f610d93c8bde80f14188a82bcdf09be91a28d24c838cec5bc947e",
152 | ]
153 | }
154 |
155 | provider "registry.terraform.io/hashicorp/template" {
156 | version = "2.2.0"
157 | hashes = [
158 | "h1:0wlehNaxBX7GJQnPfQwTNvvAf38Jm0Nv7ssKGMaG6Og=",
159 | "zh:01702196f0a0492ec07917db7aaa595843d8f171dc195f4c988d2ffca2a06386",
160 | "zh:09aae3da826ba3d7df69efeb25d146a1de0d03e951d35019a0f80e4f58c89b53",
161 | "zh:09ba83c0625b6fe0a954da6fbd0c355ac0b7f07f86c91a2a97849140fea49603",
162 | "zh:0e3a6c8e16f17f19010accd0844187d524580d9fdb0731f675ffcf4afba03d16",
163 | "zh:45f2c594b6f2f34ea663704cc72048b212fe7d16fb4cfd959365fa997228a776",
164 | "zh:77ea3e5a0446784d77114b5e851c970a3dde1e08fa6de38210b8385d7605d451",
165 | "zh:8a154388f3708e3df5a69122a23bdfaf760a523788a5081976b3d5616f7d30ae",
166 | "zh:992843002f2db5a11e626b3fc23dc0c87ad3729b3b3cff08e32ffb3df97edbde",
167 | "zh:ad906f4cebd3ec5e43d5cd6dc8f4c5c9cc3b33d2243c89c5fc18f97f7277b51d",
168 | "zh:c979425ddb256511137ecd093e23283234da0154b7fa8b21c2687182d9aea8b2",
169 | ]
170 | }
171 |
172 | provider "registry.terraform.io/hashicorp/tls" {
173 | version = "3.1.0"
174 | hashes = [
175 | "h1:XTU9f6sGMZHOT8r/+LWCz2BZOPH127FBTPjMMEAAu1U=",
176 | "zh:3d46616b41fea215566f4a957b6d3a1aa43f1f75c26776d72a98bdba79439db6",
177 | "zh:623a203817a6dafa86f1b4141b645159e07ec418c82fe40acd4d2a27543cbaa2",
178 | "zh:668217e78b210a6572e7b0ecb4134a6781cc4d738f4f5d09eb756085b082592e",
179 | "zh:95354df03710691773c8f50a32e31fca25f124b7f3d6078265fdf3c4e1384dca",
180 | "zh:9f97ab190380430d57392303e3f36f4f7835c74ea83276baa98d6b9a997c3698",
181 | "zh:a16f0bab665f8d933e95ca055b9c8d5707f1a0dd8c8ecca6c13091f40dc1e99d",
182 | "zh:be274d5008c24dc0d6540c19e22dbb31ee6bfdd0b2cddd4d97f3cd8a8d657841",
183 | "zh:d5faa9dce0a5fc9d26b2463cea5be35f8586ab75030e7fa4d4920cd73ee26989",
184 | "zh:e9b672210b7fb410780e7b429975adcc76dd557738ecc7c890ea18942eb321a5",
185 | "zh:eb1f8368573d2370605d6dbf60f9aaa5b64e55741d96b5fb026dbfe91de67c0d",
186 | "zh:fc1e12b713837b85daf6c3bb703d7795eaf1c5177aebae1afcf811dd7009f4b0",
187 | ]
188 | }
189 |
190 | provider "registry.terraform.io/ibm-cloud/ibm" {
191 | version = "1.30.1"
192 | hashes = [
193 | "h1:7n0oM83OHoHVS/hFOZ8qUmz54GgN++4TGLISkf00TTs=",
194 | "zh:3b66cc88d4314b2bbbdd80b08f09fd70289d58f58bd397a07e8694d00bb7ee13",
195 | "zh:9f94128c3655f12929666c3962500d16c2ccb0f5253de81e1f6257918e15e5f3",
196 | "zh:b864fc89d256d85f2ffeca1035233229be85ae916b38119a2079ce78a1fa0984",
197 | ]
198 | }
199 |
200 | provider "registry.terraform.io/vultr/vultr" {
201 | version = "2.4.1"
202 | hashes = [
203 | "h1:mn/4NSuoRE3BZIYSIw6y7ueOeqgkZP/Fp50fj8W/8ls=",
204 | "zh:02a2f0bc80bb29793ca7a0ac3b775c70df8b21bd86a068823598f9762a9e13bc",
205 | "zh:1762b2da68090c71ac79f9d07c5d3acbb6ff10f69743993b15f7e30253bad6f7",
206 | "zh:253874ec22f70187a9bf0acf55feb60928e0467141b07a7a3615a39a9615472b",
207 | "zh:256f4840063cb89c3d1964c8ee648dc5aac5afa11d797368005e3ea3a8b8b896",
208 | "zh:3cec15f8d6ef35920c2b21367f6dad9f836508701e899e2dd04b238c6471f73e",
209 | "zh:541ae4d83a1411859d35b1cbf352315ba1bbfa6c184ec1c7b9a3a463428aa87f",
210 | "zh:57afc66e9a9c46a4aa237705ed80030dbdb5475001c24ef8f1b99156c56225da",
211 | "zh:5d90b5d491de540f633d2077bdbf28c7eca7c23cc01463a9985f5031d1bd309e",
212 | "zh:602a217f3e381061e4234dee2b187960705fdbf78a91ea89ac560cfe428d1c93",
213 | "zh:7e6ef6d9caa97b36e2143dfb55836914c1beba1a7a311b536db4f5cd222ac88c",
214 | "zh:ae9ee1dfa92f3c50877998580f885c2caf7c06e33f142635fac42bf5cdb0ad88",
215 | "zh:c7b1cb8e4245d961b08e46c33cd57d76fc5fe2dda16148bac2a4db7a4dd6b253",
216 | "zh:cdec2fafdccc4a1af394983eca50281a5a28c6509bbd5532e1253ebab5788ff6",
217 | "zh:e0c0e9492d1e57d091150b1565fe73ba59c9a24fe00b4134af21a20e3eddf270",
218 | "zh:fd3ecd73a2d28392ae61bf218aceceaec7cc395be2d25dd925e32d66bf3c7514",
219 | ]
220 | }
221 |
222 | provider "registry.terraform.io/zerotier/zerotier" {
223 | version = "1.2.0"
224 | hashes = [
225 | "h1:GCRYFum1Ym1mPUQYo2ykYfDDSqgktpNdRFbOuyNs4wY=",
226 | "zh:2cc205bfbb42534fd6f629c58b350b63e6b82d60438f0385266fcf62f0035b39",
227 | "zh:4b0eef9be114283ddc2677bb6e5b99a0c148ac5f7fb1b7208c62c2874cf7518a",
228 | "zh:50b6c922cf73a63ee29272b8b5865735d4e71e5854d63ab19eafe901572f48d2",
229 | "zh:61d9540f076f8e1ebcb694028175942d737448c4aa04c94c888b8c01a66411e7",
230 | "zh:877a932c2500c0c09f9e920620a8f027704c808c5f02198ad729d47f1ccfab28",
231 | "zh:91fc66d7defa4e11f4aeba658451f069f6cc97c65148848127676063a5ffe36c",
232 | "zh:9fd311fcfd1cfbf78b8c3c7aec22f0a089801b135d6d9303a51158ffa9456815",
233 | "zh:b4a288175e81aaa5c4bb31ed05e90528d5920f8f5d1644148991a4d7c13a7f03",
234 | "zh:d65b7cfa8e1b16cee4330c1560540593832368eaa7069fd1cdc26fe66a78c3d1",
235 | "zh:d8f10be33837fbf28560b9ce21babd1fea206b1753b8e7e73e44896cd77d6d25",
236 | "zh:ebdb5a10e17bd376e7d4d155c708de1842c2c1837dfb8770c2e66b297893f148",
237 | "zh:f488867d43529392c03921f32ffb5d408dc75dcaf0c1d4639901199c4a94ac85",
238 | "zh:fcf8eed0a504106cc18d0850577b2cf30a2f5f7d5d7962cd299cb429d6c59523",
239 | ]
240 | }
241 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Terraform Multicloud Quickstart
2 |
3 |
4 | 
5 |
6 | We are living in an ephemeral world
7 | And I am an ephemeral girl
8 |
9 |
10 |
11 | ## Welcome!
12 |
13 | This quickstart tutorial creates a lab environment for using ZeroTier
14 | in combination with multiple Terraform cloud providers. If you're a
15 | ZeroTier user that's new to Terraform, You might be looking for the [Terraform Quickstart](https://docs.zerotier.com/terraform/quickstart) instead.
16 |
17 | If you're a Terraform user that's new to ZeroTier, you're in the right place. Make yourself a coffee a and buckle up.
18 |
19 | ## Prerequisites
20 |
21 | To follow along step by step, you will need:
22 |
23 | - A [Github](https://github.com) account,
24 | - A [ZeroTier Central](https://my.zerotier.com) account,
25 | - A [Terraform Cloud](https://app.terraform.io) account.
26 | - Accounts on multiple cloud providers
27 |
28 | The full-blown multicloud demo uses:
29 |
30 | - [Digital Ocean](https://www.digitalocean.com/)
31 | - [Amazon Web Services](https://aws.amazon.com/)
32 | - [Google Compute Engine](https://cloud.google.com/compute)
33 | - [Microsoft Azure](https://azure.microsoft.com)
34 | - [Oracle Cloud Infrastructure](https://www.oracle.com/cloud)
35 | - [Alibaba Cloud](https://alibabacloud.com)
36 | - [IBM Cloud](https://www.ibm.com/cloud)
37 | - [Vultr](https://www.vultr.com)
38 | - [Equinix Metal](https://metal.equinix.com)
39 |
40 | That's a lot of service providers. You'll need at least two for
41 | demonstration purposes, but I recommend using them all for dramatic
42 | effect. Digital Ocean was chosen at random to provide DNS service for
43 | the lab.
44 |
45 | The first time through, you will encounter a few hurdles. Each cloud
46 | vendor brings their own special brand of pain. For example, on AWS,
47 | you will need to accept the Marketplace agreement for the Ubuntu
48 | AMI. On GCP, you will be prompted to enable Cloud APIs. Others have
49 | stringent account verification procedures.
50 |
51 |
52 | 
53 |
54 |
55 | To lower the bar of entry, you can toggle which clouds are
56 | enabled `variables.tf`. The process for creating service accounts and
57 | gathering credentials is outside the scope of this document, but
58 | should be pretty straight forward.
59 |
60 | You can do this. We believe in you.
61 |
62 | ## Import repo
63 |
64 | [Import](https://github.com/new/import) the
65 | [Terraform Multicloud Quickstart](https://github.com/zerotier/terraform-multicloud-quickstart)
66 | to your Github account. We are "importing" instead of "cloning" so
67 | that we can set the repository as private. If you're comforable on
68 | the command line, feel free to clone to your laptop and commits from
69 | there, otherwise, we will use Github's in-browser editing feature to
70 | drive the tutorial.
71 |
72 | 
73 |
74 | ## Create a Terraform workspace
75 |
76 | Next, we create a Terraform workspace and attach it to our private
77 | Github repository. Be sure to select version control
78 | workflow, select the correct Github account, (we want the
79 | private copy, not the original), and give it a unique name.
80 |
81 | 
82 | 
83 | 
84 | 
85 | 
86 |
87 | ## Create ZeroTier Central variables
88 |
89 | Next, we will use Terraform to create some resources in the ZeroTier
90 | Central API. Before we can do this, we need to give Terraform
91 | credentials as Environment Variables.
92 |
93 | 
94 | 
95 |
96 | ## Create ZeroTier Central resources
97 |
98 | Examine `main.tf` At the top, you will see Terraform resources for
99 | creating [Identities](https://github.com/zerotier/terraform-provider-zerotier#identities),
100 | [Networks](https://github.com/zerotier/terraform-provider-zerotier#networks),
101 | and [Members](https://github.com/zerotier/terraform-provider-zerotier#members). There
102 | is also a [Token](https://github.com/zerotier/terraform-provider-zerotier#tokens)
103 | that we will use later.
104 |
105 | 
106 |
107 | ```jsx
108 | resource "zerotier_identity" "instances" {
109 | for_each = { for k, v in var.instances : k => (v) if v.enabled }
110 | }
111 |
112 | resource "zerotier_network" "demolab" {
113 | name = "demo.lab"
114 | description = "ZeroTier Terraform Demolab"
115 | assign_ipv6 {
116 | zerotier = true
117 | sixplane = true
118 | rfc4193 = true
119 | }
120 | assignment_pool {
121 | start = "10.0.0.1"
122 | end = "10.0.0.254"
123 | }
124 | route {
125 | target = "10.0.0.0/16"
126 | }
127 | }
128 |
129 | resource "zerotier_member" "devices" {
130 | for_each = var.devices
131 | name = each.key
132 | member_id = each.value.member_id
133 | description = each.value.description
134 | network_id = zerotier_network.demolab.id
135 | }
136 |
137 | resource "zerotier_member" "instances" {
138 | for_each = { for k, v in var.instances : k => (v) if v.enabled }
139 | name = each.key
140 | member_id = zerotier_identity.instances[each.key].id
141 | description = each.value.description
142 | network_id = zerotier_network.demolab.id
143 | no_auto_assign_ips = false
144 | ip_assignments = [each.value.ip_assignment]
145 | }
146 |
147 | resource "zerotier_token" "this" {
148 | name = "demolab"
149 | }
150 | ```
151 |
152 | Normally, to kick off a Terraform plan, we would make commits to our
153 | repository. However, since we have a fresh workspace and nothing to
154 | change, we'll need to manually queue our first plan in the Terraform
155 | webUI.
156 |
157 | 
158 | 
159 |
160 | Confirm the plan by clicking "Confirm & Apply"
161 |
162 |
163 | 
164 |
165 | Congratulations! You have just succesfully created your first ZeroTier
166 | network using Terraform! Go over to
167 | [ZeroTier Central](https://my.zerotier.com) and check out your new
168 | network. Alice and Bob are both authorized onto the network, but don't
169 | worry, they aren't real. We will replace them shortly.
170 |
171 | 
172 |
173 | ## Edit variables.tf
174 |
175 | Terraform has two kinds of variables. We have already seen some
176 | Environmet Variables, which we used to make credentials available to
177 | the [ZeroTier Terraform Provider](https://github.com/zerotier/terraform-provider-zerotier). The
178 | other kinds of variables are known as [Input Variables](https://www.terraform.io/docs/language/values/variables.html). We
179 | will use these to supply some usernames and SSH keys, as well as toggle which clouds we want to use.
180 |
181 | 
182 |
183 | Use Github's editor to set the `users`, `devices`, and `instances`
184 | variables. Replace Alice and Bob's information with your own SSH keys
185 | and ZeroTier Node ID's. In the `instances` variable, toggle the clouds
186 | you plan on using to `enabled`.
187 |
188 | 
189 |
190 | Save your work by clicking "Commit changes" at the bottom of the page.
191 |
192 |
193 | Go back to your workspace and see that it now says "Planned". Every
194 | time a commit is pushed to the repo, Terraform will queue a plan. This
195 | is the essence of the "Version control workflow" we selected earlier.
196 |
197 | 
198 |
199 | Navigate through "Runs" and then "confirm and apply". There is a
200 | setting to make this step automatic, but we will leave it manual for
201 | now.
202 |
203 | 
204 | 
205 |
206 | We now have pre-generated ZeroTier Identities that we will inject into
207 | our cloud instances when we bring them up. They are stored in the
208 | workspace's Terraform State on Terraform Cloud. Be careful about who
209 | has access to your account, as well as source repository that drives it.
210 |
211 | ## Create Digital Ocean resources
212 |
213 | Add your your `DIGITALOCEAN_TOKEN` to the worksace's Environmet Variables using the same procedure as before.
214 |
215 | 
216 |
217 |
218 | Next, edit main.tf and uncomment the Digital Ocean module.
219 |
220 |
221 |
222 | 
223 | 
224 | 
225 | 
226 | 
227 | 
228 |
229 | ## Join laptop to Network
230 |
231 | The ZeroTier Network can be found in the Terraform output. Find it by
232 | nagivating to the "Outputs" tab of the latest run.
233 | 
234 |
235 |
236 | You can also find it in the ZeroTier Central webUI.
237 |
238 |
239 |
240 | Join your laptop to the network. Make sure to check "Allow DNS"
241 | 
242 |
243 | You will be able to SSH into the box. If this does not work, make sure
244 | `username`, `ssh_pubkey` and `member_id` are correct in `variables.tf`.
245 | 
246 |
247 | ## Spin up Multiple Clouds
248 |
249 |
250 | 
251 | Baton Bunny - Warner Bros. 1959
252 |
253 |
254 | Next, spin up the rest of the cloud instances. Go through each cloud provider,
255 | one by one, adding Environment Variables to the Terraform workspace,
256 | then uncommenting out the corresponding module in `main.tf`.
257 |
258 | Here's a complete list of Environment Variables to set if you plan on
259 | spinning up every cloud the tutorial supports.
260 |
261 | ```bash
262 | # ZeroTier Central
263 | export ZEROTIER_CENTRAL_TOKEN="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
264 | export ZEROTIER_CENTRAL_URL="https://my.zerotier.com/api"
265 |
266 | # Digital Ocean
267 | export DIGITALOCEAN_TOKEN="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
268 |
269 | # Amazon Web Services
270 | export AWS_ACCESS_KEY_ID="XXXXXXXXXXXXXXXXXXXX"
271 | export AWS_SECRET_ACCESS_KEY="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
272 | export AWS_REGION="us-east-1"
273 |
274 | # Google Compute Platform
275 | export GOOGLE_CREDENTIALS="$(cat key-downloaded-from-gcp-console.json)"
276 | export GOOGLE_CLOUD_PROJECT="XXX-XXXXXX"
277 | export GOOGLE_REGION="us-east4"
278 | export GOOGLE_ZONE="us-east4-a"
279 |
280 | # Microsoft Azure
281 | export ARM_SUBSCRIPTION_ID="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
282 | export ARM_TENANT_ID="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
283 | export ARM_CLIENT_ID="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
284 | export ARM_CLIENT_SECRET="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
285 |
286 | # IBM Cloud
287 | export IBMCLOUD_API_KEY="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
288 | export IBMCLOUD_REGION="us-east"
289 |
290 | # Oracle Cloud Infrastructure
291 | export TF_VAR_compartment_id="ocid1.tenancy.oc1..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
292 | # please configure ~/.oci/config
293 |
294 | # Alibaba Cloud
295 | export ALICLOUD_ACCESS_KEY="XXXXXXXXXXXXXXXXXXXXXXXX"
296 | export ALICLOUD_SECRET_KEY="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
297 | export ALICLOUD_REGION="us-east-1"
298 |
299 | # Vultr
300 | export VULTR_API_KEY="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
301 |
302 | # Equinix Metal
303 | export METAL_AUTH_TOKEN="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
304 | ```
305 |
306 | ## Hit the web servers
307 |
308 | Each node is running a web server with an example nginx page,
309 | accessible with an internal DNS address.
310 |
311 | For example, [http://aws.demo.lab](http://aws.demo.lab/).
312 |
313 |
314 | 
315 |
316 |
317 | ## Understanding ZeroTier VL2
318 |
319 | ZeroTier networks are virtual Ethernet switches. This means that
320 | anything you can do on a physical LAN segment, ZeroTier can over the
321 | Internet, securely, across clouds, and through NAT devices.
322 |
323 |
324 | 
325 | Down the Rabbit Hole - Valerie Hinojosa 2006
326 |
327 |
328 | ```bash
329 | laptop:~$ ssh do.demo.lab
330 | ```
331 |
332 | ## Ping all the boxen (v4)
333 |
334 | ```bash
335 | alice@do:~$ for i in laptop aws gcp azu oci ali ibm vul eqx ; do ping -4 -c 1 $i.demo.lab ; done &>/dev/null
336 | ```
337 |
338 | ## Examine the ARP cache
339 |
340 | ```bash
341 | alice@do:~$ arp -a | grep demo | sort
342 | ali.demo.lab (10.0.8.1) at 5e:1e:72:fb:14:e4 [ether] on ztyqb6mebi
343 | aws.demo.lab (10.0.2.1) at 5e:6c:4b:3a:05:4f [ether] on ztyqb6mebi
344 | azu.demo.lab (10.0.4.1) at 5e:d5:43:77:15:62 [ether] on ztyqb6mebi
345 | eqx.demo.lab (10.0.9.1) at 5e:11:0c:5d:cd:44 [ether] on ztyqb6mebi
346 | gcp.demo.lab (10.0.3.1) at 5e:5f:43:6c:9a:58 [ether] on ztyqb6mebi
347 | ibm.demo.lab (10.0.6.1) at 5e:38:83:97:55:1a [ether] on ztyqb6mebi
348 | laptop.demo.lab (10.0.0.83) at 5e:27:8a:8d:21:51 [ether] on ztyqb6mebi
349 | oci.demo.lab (10.0.5.1) at 5e:19:d5:76:be:24 [ether] on ztyqb6mebi
350 | vul.demo.lab (10.0.7.1) at 5e:3c:36:a8:9f:9d [ether] on ztyqb6mebi
351 | ```
352 |
353 | As you can see, the ARP table now contains an entry for each node on
354 | our network, just as it would on a local ethernet network.
355 |
356 | ## Examine the interfaces
357 |
358 | Run the `ip link` command to examine the interfaces on each box.
359 |
360 | ```bash
361 | alice@do:~$ ip link | grep -A1 zt
362 | 4: ztyqb6mebi: mtu 2800 qdisc fq_codel state UNKNOWN mode DEFAULT group default qlen 1000
363 | link/ether 5e:56:14:d3:25:ed brd ff:ff:ff:ff:ff:ff
364 | ```
365 |
366 | You'll see a virtual ethernet interface for each ZeroTier network the node is joined to. (in this case, one)
367 |
368 | ```bash
369 | alice@aws:~$ ip link | grep -A1 zt
370 | 3: ztyqb6mebi: mtu 2800 qdisc fq_codel state UNKNOWN mode DEFAULT group default qlen 1000
371 | link/ether 5e:6c:4b:3a:05:4f brd ff:ff:ff:ff:ff:ff
372 | ```
373 |
374 | The name of the interface is derived from the network ID it is joined
375 | to. Note that the name of the interface is the same on each machine.
376 |
377 | ```bash
378 | alice@oci:~$ ip link | grep -A1 zt
379 | 3: ztyqb6mebi: mtu 2800 qdisc fq_codel state UNKNOWN mode DEFAULT group default qlen 1000
380 | link/ether 5e:19:d5:76:be:24 brd ff:ff:ff:ff:ff:ff
381 | ```
382 |
383 | ## Ethernet Tapping
384 |
385 | You may have noticed the [flow_rules](https://github.com/zerotier/zerotier-terraform-quickstart/blob/main/flow_rules.tpl)
386 | section in the `zerotier_network` while examining [main.tf](https://github.com/zerotier/zerotier-terraform-quickstart/blob/main/main.tf)
387 | earlier.
388 |
389 | ```jsx
390 | resource "zerotier_network" "demolab" {
391 | name = "demo.lab"
392 | description = "ZeroTier Terraform Demolab"
393 | assign_ipv6 {
394 | zerotier = true
395 | sixplane = true
396 | rfc4193 = true
397 | }
398 | assignment_pool {
399 | start = "10.0.0.1"
400 | end = "10.0.0.254"
401 | }
402 | route {
403 | target = "10.0.0.0/16"
404 | }
405 | flow_rules = templatefile("${path.module}/flow_rules.tpl", {
406 | ethertap = zerotier_identity.instances["do"].id
407 | })
408 | }
409 | ```
410 |
411 | We will use these to gain visibility into our network with tshark. You
412 | can see them reflected in the Central WebUI under the "Flow Rules"
413 | section for the `demo.lab` network. They are documented in in-depth in
414 | chapter 3 of the [ZeroTier Manual](https://www.zerotier.com/manual/#3).
415 |
416 | Edit `flow_rules.tpl`, uncommenting the `tee` rule.
417 |
418 | ```
419 | # drop not ethertype ipv4 and not ethertype arp and not ethertype ipv6;
420 | tee -1 ${ethertap};
421 | # watch -1 ${ethertap} chr inbound;
422 | accept;
423 | ```
424 |
425 | Flow Rules are applied to every member of the network. `tee` tells
426 | ZeroTier to mirror a copy of every packet to Digital Ocean. Apply the
427 | rule set by saving the file and running Terraform.
428 |
429 | ```bash
430 | terraform apply -target 'zerotier_network.demolab' -auto-approve
431 | ```
432 |
433 | ## Watching traffic with tshark
434 |
435 | On the Digital Ocean machine, view traffic by running tshark on your network's ZeroTier interface.
436 |
437 | ```
438 | sudo tshark -i ztyqb6mebi not port ssh
439 | ```
440 |
441 | Open another terminal window, log into AWS, and ping GCP.
442 |
443 | ```
444 | alice@aws:~$ ping -4 -c 1 gcp.demo.lab
445 | PING gcp.demo.lab (10.0.3.1) 56(84) bytes of data.
446 | 64 bytes from gcp.demo.lab (10.0.3.1): icmp_seq=1 ttl=64 time=2.02 ms
447 |
448 | --- gcp.demo.lab ping statistics ---
449 | 1 packets transmitted, 1 received, 0% packet loss, time 0ms
450 | rtt min/avg/max/mdev = 2.016/2.016/2.016/0.000 ms
451 | ```
452 |
453 | You will be able to observe the traffic from Digital Ocean.
454 |
455 | ```
456 |
457 | 37 67.550026693 10.0.2.1 → 10.0.3.1 ICMP 98 Echo (ping) request id=0x0005, seq=1/256, ttl=64
458 | 38 67.551676229 10.0.2.1 → 10.0.3.1 ICMP 98 Echo (ping) request id=0x0005, seq=1/256, ttl=64
459 | 39 67.551728848 10.0.3.1 → 10.0.2.1 ICMP 98 Echo (ping) reply id=0x0005, seq=1/256, ttl=64 (request in 38)
460 | 40 67.551933296 10.0.3.1 → 10.0.2.1 ICMP 98 Echo (ping) reply id=0x0005, seq=1/256, ttl=64
461 |
462 | ```
463 |
464 | You'll see duplicates, as the `tee` is picking up both the incoming and outgoing packets from both nodes.
465 | The `watch` rule, combined with the `inbound` characteristic is a
466 | little friendlier.
467 |
468 | Edit `flow_rules.tpl`, this time using the `watch` rule.
469 |
470 | ```
471 | # drop not ethertype ipv4 and not ethertype arp and not ethertype ipv6;
472 | # tee -1 ${ethertap};
473 | watch -1 ${ethertap} chr inbound;
474 | accept;
475 | ```
476 |
477 | Apply the rule set again with Terraform.
478 |
479 | ```bash
480 | terraform apply -target 'zerotier_network.demolab' -auto-approve
481 | ```
482 |
483 | You can also see the the traffic from your laptop when hitting the web
484 | servers. Load the page on IBM Cloud by visiting http://ibm.demo.lab, and
485 | observe the traffic in your Digital Ocean terminal.
486 |
487 | ```
488 |
489 | 486 1416.628490335 10.0.0.83 → 10.0.6.1 HTTP 541 GET / HTTP/1.1
490 | 487 1416.745168511 10.0.6.1 → 10.0.0.83 TCP 66 80 → 56084 [ACK] Seq=7441 Ack=925 Win=62848 Len=0 TSval=2811045625 TSecr=2751470539
491 | 488 1416.745410648 10.0.6.1 → 10.0.0.83 TCP 292 HTTP/1.1 200 OK [TCP segment of a reassembled PDU]
492 | 489 1416.746737900 10.0.6.1 → 10.0.0.83 TCP 1514 HTTP/1.1 200 OK [TCP segment of a reassembled PDU]
493 | 490 1416.747687877 10.0.6.1 → 10.0.0.83 TCP 1514 HTTP/1.1 200 OK [TCP segment of a reassembled PDU]
494 | 491 1416.748400578 10.0.6.1 → 10.0.0.83 TCP 1514 HTTP/1.1 200 OK [TCP segment of a reassembled PDU]
495 | 492 1416.749430863 10.0.6.1 → 10.0.0.83 TCP 1514 HTTP/1.1 200 OK [TCP segment of a reassembled PDU]
496 | 493 1416.750215893 10.0.6.1 → 10.0.0.83 TCP 955 HTTP/1.1 200 OK [TCP segment of a reassembled PDU]
497 | 494 1416.750237332 10.0.6.1 → 10.0.0.83 TCP 77 HTTP/1.1 200 OK [TCP segment of a reassembled PDU]
498 | 495 1416.750362231 10.0.6.1 → 10.0.0.83 TCP 118 HTTP/1.1 200 OK [TCP segment of a reassembled PDU]
499 | 496 1416.750636517 10.0.6.1 → 10.0.0.83 TCP 69 HTTP/1.1 200 OK [TCP segment of a reassembled PDU]
500 | 497 1416.750650316 10.0.6.1 → 10.0.0.83 TCP 122 HTTP/1.1 200 OK [TCP segment of a reassembled PDU]
501 |
502 | ```
503 |
504 | ## Manually manipulate IP addresses
505 |
506 | Because ZeroTier behaves like ethernet, we can assign multiple IP addresses to an interface, just like on a physical network.
507 |
508 | ```bash
509 | alice@aws:~$ ip -4 addr show ztyqb6mebi
510 | 3: ztyqb6mebi: mtu 2800 qdisc fq_codel state UNKNOWN group default qlen 1000
511 | inet 10.0.2.1/16 brd 10.0.255.255 scope global ztyqb6mebi
512 | valid_lft forever preferred_lft forever
513 | ```
514 |
515 | Our network is configured as a `/16`, which means we can add any of
516 | the 65,536 available IPv4 addresses to any zerotier interface, on any
517 | machine, and it will work as expected via ARP resolution.
518 |
519 | Experiment with this by adding ip addresses from the command line.
520 |
521 | ```bash
522 | # Amazon Web Services
523 | alice@aws:$ sudo ip addr add 10.0.2.2/24 dev ztyqb6mebi
524 | alice@aws:$ sudo ip addr add 10.0.2.3/24 dev ztyqb6mebi
525 | alice@aws:$ sudo ip addr add 10.0.2.4/24 dev ztyqb6mebi
526 |
527 | # Google Compute Engine
528 | alice@gcp:$ sudo ip addr add 10.0.3.2/24 dev ztyqb6mebi
529 | alice@gcp:$ sudo ip addr add 10.0.3.3/24 dev ztyqb6mebi
530 | alice@gcp:$ sudo ip addr add 10.0.3.4/24 dev ztyqb6mebi
531 | ```
532 |
533 | Clean up after yourself by deleting them.
534 |
535 | ```bash
536 | # Amazon Web Services
537 | alice@aws:$ sudo ip addr del 10.0.2.2/24 dev ztyqb6mebi
538 | alice@aws:$ sudo ip addr del 10.0.2.3/24 dev ztyqb6mebi
539 | alice@aws:$ sudo ip addr del 10.0.2.4/24 dev ztyqb6mebi
540 |
541 | # Google Compute Engine
542 | alice@gcp:$ sudo ip addr del 10.0.3.2/24 dev ztyqb6mebi
543 | alice@gcp:$ sudo ip addr del 10.0.3.3/24 dev ztyqb6mebi
544 | alice@gcp:$ sudo ip addr del 10.0.3.4/24 dev ztyqb6mebi
545 | ```
546 |
547 | ## Native Container Routing
548 |
549 |
550 | 
551 | Amy Gizienski - whale
552 |
553 |
554 | We would be remiss not to mention containers in the year 2021. A great
555 | attribute of Layer 2 networks is that containers can talk directly to
556 | each other using native routing.
557 |
558 | No really.
559 |
560 | Pick a box, any box, and start a shell in Docker.
561 |
562 | ```bash
563 | alice@ibm:~$ docker run -it alpine:latest /bin/sh
564 | alice@ibm:~$ docker run -it alpine:latest /bin/sh
565 | / # ip addr
566 | 1: lo: mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
567 | link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
568 | inet 127.0.0.1/8 scope host lo
569 | valid_lft forever preferred_lft forever
570 | inet6 ::1/128 scope host
571 | valid_lft forever preferred_lft forever
572 | 7: eth0@if8: mtu 1500 qdisc noqueue state UP
573 | link/ether 02:42:0a:2a:06:02 brd ff:ff:ff:ff:ff:ff
574 | inet 10.42.6.2/24 brd 10.42.6.255 scope global eth0
575 | valid_lft forever preferred_lft forever
576 | inet6 fcfb:88ae:e176:cdbb:4cc4:242:a2a:602/80 scope global flags 02
577 | valid_lft forever preferred_lft forever
578 | inet6 fe80::42:aff:fe2a:602/64 scope link
579 | valid_lft forever preferred_lft forever
580 | / #
581 | ```
582 |
583 | Then, pick another random box and do the same.
584 |
585 | ```
586 | alice@oci:~$ docker run -it alpine:latest /bin/sh
587 | Unable to find image 'alpine:latest' locally
588 | latest: Pulling from library/alpine
589 | a0d0a0d46f8b: Already exists
590 | Digest: sha256:e1c082e3d3c45cccac829840a25941e679c25d438cc8412c2fa221cf1a824e6a
591 | Status: Downloaded newer image for alpine:latest
592 | / # ip addr
593 | 1: lo: mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
594 | link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
595 | inet 127.0.0.1/8 scope host lo
596 | valid_lft forever preferred_lft forever
597 | inet6 ::1/128 scope host
598 | valid_lft forever preferred_lft forever
599 | 5: eth0@if6: mtu 1500 qdisc noqueue state UP
600 | link/ether 02:42:0a:2a:05:02 brd ff:ff:ff:ff:ff:ff
601 | inet 10.42.5.2/24 brd 10.42.5.255 scope global eth0
602 | valid_lft forever preferred_lft forever
603 | inet6 fcfb:88ae:e1b8:5eb5:963e:242:a2a:502/80 scope global flags 02
604 | valid_lft forever preferred_lft forever
605 | inet6 fe80::42:aff:fe2a:502/64 scope link
606 | valid_lft forever preferred_lft forever
607 | / #
608 | ```
609 |
610 | Ping the IPv4 and IPv6 addresses of the container, from the other
611 | container.
612 |
613 | ```
614 | / # ping 10.42.6.2
615 | PING 10.42.6.2 (10.42.6.2): 56 data bytes
616 | 64 bytes from 10.42.6.2: seq=0 ttl=62 time=5.992 ms
617 | 64 bytes from 10.42.6.2: seq=1 ttl=62 time=1.441 ms
618 | 64 bytes from 10.42.6.2: seq=2 ttl=62 time=1.710 ms
619 | 64 bytes from 10.42.6.2: seq=3 ttl=62 time=1.391 ms
620 | 64 bytes from 10.42.6.2: seq=4 ttl=62 time=1.520 ms
621 | ^C
622 | --- 10.42.6.2 ping statistics ---
623 | 5 packets transmitted, 5 packets received, 0% packet loss
624 | round-trip min/avg/max = 1.391/2.410/5.992 ms
625 | / #
626 | / # ping fcfb:88ae:e176:cdbb:4cc4:242:a2a:602
627 | PING fcfb:88ae:e176:cdbb:4cc4:242:a2a:602 (fcfb:88ae:e176:cdbb:4cc4:242:a2a:602): 56 data bytes
628 | 64 bytes from fcfb:88ae:e176:cdbb:4cc4:242:a2a:602: seq=0 ttl=62 time=1.810 ms
629 | 64 bytes from fcfb:88ae:e176:cdbb:4cc4:242:a2a:602: seq=1 ttl=62 time=2.103 ms
630 | 64 bytes from fcfb:88ae:e176:cdbb:4cc4:242:a2a:602: seq=2 ttl=62 time=1.388 ms
631 | 64 bytes from fcfb:88ae:e176:cdbb:4cc4:242:a2a:602: seq=3 ttl=62 time=1.403 ms
632 | ^C
633 | --- fcfb:88ae:e176:cdbb:4cc4:242:a2a:602 ping statistics ---
634 | 4 packets transmitted, 4 packets received, 0% packet loss
635 | round-trip min/avg/max = 1.388/1.676/2.103 ms
636 | / #
637 | ```
638 |
639 | What black magic is this? Let's examine the routing table.
640 |
641 | ```
642 | alice@eqx:~$ ip route | grep 42
643 | 10.42.1.0/24 via 10.0.1.1 dev ztly57gs2e proto bird metric 64
644 | 10.42.2.0/24 via 10.0.2.1 dev ztly57gs2e proto bird metric 64
645 | 10.42.3.0/24 via 10.0.3.1 dev ztly57gs2e proto bird metric 64
646 | 10.42.4.0/24 via 10.0.4.1 dev ztly57gs2e proto bird metric 64
647 | 10.42.5.0/24 via 10.0.5.1 dev ztly57gs2e proto bird metric 64
648 | 10.42.6.0/24 via 10.0.6.1 dev ztly57gs2e proto bird metric 64
649 | 10.42.7.0/24 via 10.0.7.1 dev ztly57gs2e proto bird metric 64
650 | 10.42.8.0/24 via 10.0.8.1 dev ztly57gs2e proto bird metric 64
651 | 10.42.9.0/24 dev docker0 proto kernel scope link src 10.42.9.1 linkdown
652 | ```
653 |
654 | At the bottom of the lab [boot script](https://github.com/zerotier/zerotier-terraform-quickstart/blob/main/init-demolab.tpl)
655 | we've installed a [routing daemon](https://bird.network.cz/) and
656 | gave it a simple OSPF configuration. This propigates the routes
657 | of the Docker networks among all the instances so they can talk over
658 | the ZeroTier network.
659 |
660 | But what about IPv6? For that, we've enabled the
661 | [ZeroTier 6PLANE](https://zerotier.atlassian.net/wiki/spaces/SD/pages/7274520/Using+NDP+Emulated+6PLANE+Addressing+With+Docker).
662 |
663 | ZeroTier 6PLANE encodes the network's name (8bd5124fd6f45ffe) into
664 | IPv6 addresses, and emulates
665 | [NDP](https://datatracker.ietf.org/doc/html/rfc4861). This allows for
666 | private IPv6 networking to work at massive scales, without actually
667 | having to send the discovery traffic.
668 |
669 | ## Tear it all down
670 |
671 | When you're done experimenting with the lab, tear everything down by
672 | queueing a destroy plan.
673 |
674 | 
675 | 
676 |
--------------------------------------------------------------------------------
/flow_rules.tpl:
--------------------------------------------------------------------------------
1 |
2 | # drop not ethertype ipv4 and not ethertype arp and not ethertype ipv6;
3 | # tee -1 ${ethertap};
4 | # watch -1 ${ethertap} chr inbound;
5 | accept;
6 |
--------------------------------------------------------------------------------
/init-demolab.tpl:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -euo pipefail
3 | IFS=$'\n\t'
4 |
5 | echo "-- functions and variables --"
6 |
7 | function apt-get() {
8 | while fuser -s /var/lib/dpkg/lock || fuser -s /var/lib/apt/lists/lock || fuser -s /var/lib/dpkg/lock-frontend ; do
9 | echo 'waiting for dpkg lock release' ;
10 | sleep 1 ;
11 | done ; /usr/bin/apt-get "$@"
12 | }
13 | export DEBIAN_FRONTEND=noninteractive
14 |
15 | echo "-- hostname --"
16 |
17 | hostname ${hostname}
18 | echo "${hostname}" > /etc/hostname
19 | sed -i "s/^127.0.1.1*/127.0.1.1 ${hostname}.${dnsdomain} ${hostname}/" /etc/hosts
20 |
21 | echo "-- users --"
22 |
23 | %{ for user in svc }
24 | useradd ${user.username} -c ${user.username} -m -U -s /bin/bash
25 | mkdir -p /home/${user.username}/.ssh
26 |
27 | echo "${user.ssh_pubkey}" > /home/${user.username}/.ssh/authorized_keys
28 | chmod 0600 /home/${user.username}/.ssh/authorized_keys
29 | chmod 0700 /home/${user.username}/.ssh
30 | chown -R ${user.username} ~${user.username}
31 |
32 | echo "${user.username} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${user.username}
33 | chmod 440 /etc/sudoers.d/${user.username}
34 | %{ endfor ~}
35 |
36 | echo "-- iptables --"
37 | iptables -I INPUT -p udp --dport 9993 -j ACCEPT
38 | iptables -I INPUT -p udp --dport 53 -j ACCEPT
39 | iptables -I INPUT -p tcp --dport 53 -j ACCEPT
40 | iptables -I INPUT -p tcp --dport 22 -j ACCEPT
41 | iptables -I INPUT -p tcp --dport 80 -j ACCEPT
42 | iptables -I INPUT -p tcp --dport 443 -j ACCEPT
43 |
44 | echo "-- ZeroTier identity --"
45 | mkdir -p /var/lib/zerotier-one/
46 | echo ${zt_identity.public_key} > /var/lib/zerotier-one/identity.public
47 | chmod 0644 /var/lib/zerotier-one/identity.public
48 | echo ${zt_identity.private_key} > /var/lib/zerotier-one/identity.secret
49 | chmod 0600 /var/lib/zerotier-one/identity.secret
50 |
51 | echo "-- ZeroTier --"
52 | curl -s https://install.zerotier.com | bash
53 |
54 | zerotier-cli join ${zt_network}
55 | while ! zerotier-cli listnetworks | grep ${zt_network} | grep OK ;
56 | do
57 | sleep 1
58 | done
59 |
60 | echo "-- ZeroTier Systemd Manager --"
61 | wget -q https://github.com/zerotier/zerotier-systemd-manager/releases/download/v0.2.1/zerotier-systemd-manager_0.2.1_linux_amd64.deb
62 | dpkg -i zerotier-systemd-manager_0.2.1_linux_amd64.deb
63 |
64 | %{ if zeronsd ~}
65 | echo "-- ZeroTier Central Token --"
66 |
67 | bash -c "echo ${zt_token} > /var/lib/zerotier-one/token"
68 | chown zerotier-one:zerotier-one /var/lib/zerotier-one/token
69 | chmod 600 /var/lib/zerotier-one/token
70 |
71 | echo "-- ZeroNSD --"
72 |
73 | wget -q https://github.com/zerotier/zeronsd/releases/download/v0.2.4/zeronsd_0.2.4_amd64.deb
74 | dpkg -i zeronsd_0.2.4_amd64.deb
75 |
76 | echo "zeronsd supervise -t /var/lib/zerotier-one/token -d ${dnsdomain} ${zt_network}"
77 | zeronsd supervise -t /var/lib/zerotier-one/token -d ${dnsdomain} ${zt_network}
78 |
79 | echo "systemctl enable zeronsd-${zt_network}"
80 | systemctl enable zeronsd-${zt_network}
81 |
82 | systemctl daemon-reload
83 |
84 | echo "systemctl restart zeronsd-${zt_network}"
85 | systemctl restart zeronsd-${zt_network}
86 | %{ endif ~}
87 |
88 | echo "-- Update Apt Cache --"
89 |
90 | apt-get -qq update &>/dev/null
91 |
92 | echo "-- Nginx Hello --"
93 | apt-get -qq install docker.io
94 |
95 | %{ for user in svc }
96 | usermod -a -G docker ${user.username}
97 | %{ endfor ~}
98 |
99 | docker run -d -it --restart always --network host nginxdemos/hello
100 |
101 | echo "-- Various Packages --"
102 |
103 | export DEBIAN_FRONTEND=noninteractive
104 | apt-get -qq install \
105 | emacs-nox \
106 | net-tools \
107 | iproute2 \
108 | iputils-ping \
109 | libndp-tools \
110 | tshark \
111 | nmap \
112 | avahi-utils \
113 | speedtest-cli \
114 | &>/dev/null
115 |
116 | echo "-- ZeroTier 6PLANE Docker Networks --"
117 |
118 | ZT_IDENT="$(cat /var/lib/zerotier-one/identity.public | cut -f 1 -d :)"
119 | LOWER=$(echo ${zt_network} | cut -c 1-8)
120 | UPPER=$(echo ${zt_network} | cut -c 9-16)
121 | PREFIX=$(printf 'fc%x\n' $(( 0x$LOWER ^ 0x$UPPER )))
122 | SIXPLANE=$(echo "$${PREFIX}$${ZT_IDENT}" | sed 's/.\{4\}/&:/g' | awk -F":" '{ print $1":"$2":"$3":"$4":"$5"::/80" }')
123 |
124 | cat < /etc/docker/daemon.json
125 | {
126 | "ipv6": true,
127 | "bip": "${pod_cidr}",
128 | "fixed-cidr": "${pod_cidr}",
129 | "fixed-cidr-v6": "$${SIXPLANE}",
130 | "ip-forward": true
131 | }
132 | EOF
133 |
134 | systemctl restart docker
135 | ip6tables -t nat -A POSTROUTING -s $${SIXPLANE} ! -o docker0 -j MASQUERADE
136 | iptables --policy FORWARD ACCEPT
137 |
138 | echo "-- docker container routes --"
139 |
140 | ROUTER_ID=$(echo ${pod_cidr} | cut -f 1 -d '/')
141 | apt-get -qq install bird
142 |
143 | cat < /etc/bird/bird.conf
144 | router id $${ROUTER_ID};
145 |
146 | protocol kernel {
147 | metric 64;
148 | import none;
149 | export all;
150 | }
151 |
152 | protocol device { }
153 |
154 | filter ospf_in {
155 | if net = 0.0.0.0/0 then reject; else accept;
156 | }
157 |
158 | filter ospf_out {
159 | if net = 0.0.0.0/8 then {
160 | reject;
161 | } else {
162 | ospf_metric1 = 1000;
163 | accept;
164 | }
165 | }
166 |
167 | protocol ospf ospf1 {
168 | debug { states, routes, interfaces };
169 | import filter ospf_in;
170 | export filter ospf_out;
171 |
172 | area 0 {
173 | interface "*" { };
174 | };
175 | }
176 | EOF
177 |
178 | systemctl enable bird
179 | systemctl restart bird
180 |
181 | iptables -I INPUT -p ospf -j ACCEPT
182 | iptables -I OUTPUT -p ospf -j ACCEPT
183 |
184 | echo "-- script finished! --"
185 |
--------------------------------------------------------------------------------
/main.tf:
--------------------------------------------------------------------------------
1 | #
2 | # ZeroTier Central
3 | #
4 |
5 | resource "zerotier_identity" "instances" {
6 | for_each = { for k, v in var.instances : k => (v) if v.enabled }
7 | }
8 |
9 | resource "zerotier_network" "demolab" {
10 | name = "demo.lab"
11 | description = "ZeroTier Terraform Demolab"
12 | assign_ipv6 {
13 | zerotier = true
14 | sixplane = true
15 | rfc4193 = true
16 | }
17 | assignment_pool {
18 | start = "10.0.0.1"
19 | end = "10.0.0.254"
20 | }
21 | route {
22 | target = "10.0.0.0/16"
23 | }
24 | # flow_rules = templatefile("${path.module}/flow_rules.tpl", {
25 | # ethertap = zerotier_identity.instances["do"].id
26 | # })
27 | }
28 |
29 | resource "zerotier_member" "devices" {
30 | for_each = var.devices
31 | name = each.key
32 | member_id = each.value.member_id
33 | description = each.value.description
34 | network_id = zerotier_network.demolab.id
35 | }
36 |
37 | resource "zerotier_member" "instances" {
38 | for_each = { for k, v in var.instances : k => (v) if v.enabled }
39 | name = each.key
40 | member_id = zerotier_identity.instances[each.key].id
41 | description = each.value.description
42 | network_id = zerotier_network.demolab.id
43 | no_auto_assign_ips = false
44 | ip_assignments = [each.value.ip_assignment]
45 | }
46 |
47 | resource "zerotier_token" "this" {
48 | name = "demolab"
49 | }
50 |
51 | # #
52 | # # Digital Ocean
53 | # #
54 |
55 | # module "do" {
56 | # source = "./modules/do"
57 | # for_each = { for k, v in var.instances : k => v if k == "do" && v.enabled }
58 | # name = "do"
59 | # image = "ubuntu-20-04-x64"
60 | # region = "nyc1"
61 | # size = "s-1vcpu-1gb-amd"
62 | # dnsdomain = zerotier_network.demolab.name
63 | # pod_cidr = "10.42.1.1/24"
64 | # script = "init-demolab.tpl"
65 | # svc = var.users
66 | # zeronsd = true
67 | # zt_identity = zerotier_identity.instances["do"]
68 | # zt_network = zerotier_network.demolab.id
69 | # zt_token = zerotier_token.this.token
70 | # }
71 |
72 | # #
73 | # # Amazon Web Services
74 | # #
75 |
76 | # module "aws" {
77 | # source = "./modules/aws"
78 | # for_each = { for k, v in var.instances : k => v if k == "aws" && v.enabled }
79 | # name = "aws"
80 | # cidr_block = "192.168.0.0/16"
81 | # availability_zone = "us-east-1a"
82 | # instance_type = "t3.micro"
83 | # dnsdomain = zerotier_network.demolab.name
84 | # pod_cidr = "10.42.2.1/24"
85 | # script = "init-demolab.tpl"
86 | # svc = var.users
87 | # zt_identity = zerotier_identity.instances["aws"]
88 | # zt_network = zerotier_network.demolab.id
89 | # }
90 |
91 | # #
92 | # # Google Compute Platform
93 | # #
94 |
95 | # module "gcp" {
96 | # source = "./modules/gcp"
97 | # for_each = { for k, v in var.instances : k => v if k == "gcp" && v.enabled }
98 | # name = "gcp"
99 | # ip_cidr_range = "192.168.0.0/16"
100 | # region = "us-east4"
101 | # zone = "us-east4-a"
102 | # dnsdomain = zerotier_network.demolab.name
103 | # pod_cidr = "10.42.3.1/24"
104 | # script = "init-demolab.tpl"
105 | # svc = var.users
106 | # zt_identity = zerotier_identity.instances["gcp"]
107 | # zt_network = zerotier_network.demolab.id
108 | # }
109 |
110 | # #
111 | # # Microsoft Azure
112 | # #
113 |
114 | # module "azu" {
115 | # source = "./modules/azu"
116 | # for_each = { for k, v in var.instances : k => v if k == "azu" && v.enabled }
117 | # name = "azu"
118 | # address_space = ["192.168.0.0/16", "ace:cab:deca::/48"]
119 | # v4_address_prefixes = ["192.168.1.0/24"]
120 | # v6_address_prefixes = ["ace:cab:deca:deed::/64"]
121 | # location = "eastus"
122 | # dnsdomain = zerotier_network.demolab.name
123 | # pod_cidr = "10.42.4.1/24"
124 | # script = "init-demolab.tpl"
125 | # svc = var.users
126 | # zt_identity = zerotier_identity.instances["azu"]
127 | # zt_network = zerotier_network.demolab.id
128 | # }
129 |
130 | # #
131 | # # Oracle Cloud Infrastructure
132 | # #
133 |
134 | # variable "compartment_id" { default = "set_me_as_a_TF_VAR_" }
135 |
136 | # module "oci" {
137 | # source = "./modules/oci"
138 | # for_each = { for k, v in var.instances : k => v if k == "oci" && v.enabled }
139 | # name = "oci"
140 | # vpc_cidr = "192.168.0.0/16"
141 | # subnet_cidr = "192.168.1.0/24"
142 | # compartment_id = var.compartment_id
143 | # dnsdomain = zerotier_network.demolab.name
144 | # pod_cidr = "10.42.5.1/24"
145 | # script = "init-demolab.tpl"
146 | # svc = var.users
147 | # zt_identity = zerotier_identity.instances["oci"]
148 | # zt_network = zerotier_network.demolab.id
149 | # }
150 |
151 | # #
152 | # # IBM Cloud
153 | # #
154 |
155 | # module "ibm" {
156 | # source = "./modules/ibm"
157 | # for_each = { for k, v in var.instances : k => v if k == "ibm" && v.enabled }
158 | # name = "ibm"
159 | # vpc_cidr = "192.168.0.0/16"
160 | # subnet_cidr = "192.168.1.0/24"
161 | # dnsdomain = zerotier_network.demolab.name
162 | # pod_cidr = "10.42.6.1/24"
163 | # script = "init-demolab.tpl"
164 | # svc = var.users
165 | # zt_identity = zerotier_identity.instances["ibm"]
166 | # zt_network = zerotier_network.demolab.id
167 | # }
168 |
169 | # #
170 | # # Vultr
171 | # #
172 |
173 | # module "vul" {
174 | # source = "./modules/vul"
175 | # for_each = { for k, v in var.instances : k => v if k == "vul" && v.enabled }
176 | # name = "vul"
177 | # dnsdomain = zerotier_network.demolab.name
178 | # pod_cidr = "10.42.7.1/24"
179 | # script = "init-demolab.tpl"
180 | # svc = var.users
181 | # zt_identity = zerotier_identity.instances["vul"]
182 | # zt_network = zerotier_network.demolab.id
183 | # }
184 |
185 | # #
186 | # # Alibaba cloud
187 | # #
188 |
189 | # module "ali" {
190 | # source = "./modules/ali"
191 | # for_each = { for k, v in var.instances : k => v if k == "ali" && v.enabled }
192 | # name = "ali"
193 | # dnsdomain = zerotier_network.demolab.name
194 | # pod_cidr = "10.42.8.1/24"
195 | # script = "init-demolab.tpl"
196 | # svc = var.users
197 | # zt_identity = zerotier_identity.instances["ali"]
198 | # zt_network = zerotier_network.demolab.id
199 | # }
200 |
201 | # #
202 | # # Equinix Metal
203 | # #
204 |
205 | # module "eqx" {
206 | # source = "./modules/eqx"
207 | # for_each = { for k, v in var.instances : k => v if k == "eqx" && v.enabled }
208 | # name = "eqx"
209 | # dnsdomain = zerotier_network.demolab.name
210 | # pod_cidr = "10.42.9.1/24"
211 | # script = "init-demolab.tpl"
212 | # svc = var.users
213 | # zt_identity = zerotier_identity.instances["eqx"]
214 | # zt_network = zerotier_network.demolab.id
215 | # }
216 |
--------------------------------------------------------------------------------
/modules/ali/main.tf:
--------------------------------------------------------------------------------
1 | data "alicloud_instance_type_families" "default" {
2 | instance_charge_type = "PostPaid"
3 | }
4 |
5 | data "alicloud_instance_types" "this" {
6 | instance_type_family = "ecs.g6"
7 | cpu_core_count = 2
8 | }
9 |
10 | data "alicloud_zones" "this" {
11 | available_disk_category = "cloud_ssd"
12 | available_instance_type = data.alicloud_instance_types.this.instance_types[0].id
13 | }
14 |
15 | resource "alicloud_vpc" "this" {
16 | vpc_name = var.name
17 | cidr_block = "192.168.0.0/16"
18 | }
19 |
20 | resource "alicloud_vswitch" "this" {
21 | zone_id = data.alicloud_zones.this.zones[0].id
22 | vswitch_name = "ali"
23 | cidr_block = "192.168.1.0/24"
24 | vpc_id = alicloud_vpc.this.id
25 | }
26 |
27 | resource "alicloud_security_group" "this" {
28 | name = "ali"
29 | vpc_id = alicloud_vpc.this.id
30 | }
31 |
32 | resource "alicloud_security_group_rule" "allow_all" {
33 | type = "ingress"
34 | ip_protocol = "udp"
35 | nic_type = "intranet"
36 | policy = "accept"
37 | port_range = "9993/9993"
38 | priority = 1
39 | security_group_id = alicloud_security_group.this.id
40 | cidr_ip = "0.0.0.0/0"
41 | }
42 |
43 | data "cloudinit_config" "this" {
44 | gzip = false
45 | base64_encode = false
46 |
47 | part {
48 | filename = "init.sh"
49 | content_type = "text/x-shellscript"
50 | content = templatefile("${path.root}/${var.script}", {
51 | "dnsdomain" = var.dnsdomain
52 | "hostname" = var.name
53 | "pod_cidr" = var.pod_cidr
54 | "svc" = var.svc
55 | "zeronsd" = var.zeronsd
56 | "zt_identity" = var.zt_identity
57 | "zt_network" = var.zt_network
58 | "zt_token" = var.zt_token
59 | })
60 | }
61 | }
62 |
63 | resource "alicloud_instance" "this" {
64 | instance_name = "ali"
65 | host_name = "ali.demo.lab"
66 | image_id = "ubuntu_20_04_x64_20G_alibase_20210623.vhd"
67 | instance_type = data.alicloud_instance_types.this.instance_types[0].id
68 | security_groups = [alicloud_security_group.this.id]
69 | vswitch_id = alicloud_vswitch.this.id
70 | internet_charge_type = "PayByTraffic"
71 | password = "N3wZ3r0T13r!"
72 | instance_charge_type = "PostPaid"
73 | tags = { "Name" : "ali" }
74 | user_data = data.cloudinit_config.this.rendered
75 | }
76 |
77 | resource "alicloud_eip" "this" {
78 | bandwidth = "200"
79 | internet_charge_type = "PayByTraffic"
80 | }
81 |
82 | resource "alicloud_eip_association" "this" {
83 | allocation_id = alicloud_eip.this.id
84 | instance_id = alicloud_instance.this.id
85 | }
86 |
--------------------------------------------------------------------------------
/modules/ali/variables.tf:
--------------------------------------------------------------------------------
1 | variable "name" {
2 | type = string
3 | }
4 |
5 | variable "dnsdomain" {
6 | type = string
7 | }
8 |
9 | variable "pod_cidr" {
10 | type = string
11 | }
12 |
13 | variable "script" {
14 | type = string
15 | }
16 |
17 | variable "svc" {
18 | type = map(
19 | object({
20 | username = string
21 | ssh_pubkey = string
22 | }))
23 |
24 | validation {
25 | condition = alltrue([for u in var.svc : can(regex("^ssh-", u.ssh_pubkey))])
26 | error_message = "The ssh_pubkey value must be a valid ssh pubkey, starting with \"ssh-\"."
27 | }
28 | }
29 |
30 | variable "zeronsd" {
31 | default = false
32 | }
33 |
34 | variable "zt_identity" {
35 | type = object({
36 | id = string
37 | private_key = string
38 | public_key = string
39 | })
40 |
41 | validation {
42 | condition = length(var.zt_identity.id) == 10
43 | error_message = "The zt_identity id must be be 10 characters long."
44 | }
45 |
46 | sensitive = true
47 | }
48 |
49 | variable "zt_network" {
50 | type = string
51 |
52 | validation {
53 | condition = length(var.zt_network) == 16
54 | error_message = "The zt_network id must be be 16 characters long."
55 | }
56 | }
57 |
58 | variable "zt_token" {
59 | type = string
60 | default = "01234567890123456789012345678912"
61 |
62 | validation {
63 | condition = length(var.zt_token) == 32
64 | error_message = "The zt_token must be be 32 characters long."
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/modules/ali/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | alicloud = {
4 | source = "aliyun/alicloud"
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/modules/aws/main.tf:
--------------------------------------------------------------------------------
1 |
2 | resource "aws_vpc" "this" {
3 | cidr_block = var.cidr_block
4 | enable_dns_support = true
5 | enable_dns_hostnames = false
6 | assign_generated_ipv6_cidr_block = true
7 | tags = { "Name" = var.name }
8 | }
9 |
10 | resource "aws_subnet" "this" {
11 | availability_zone = var.availability_zone
12 | cidr_block = cidrsubnet(aws_vpc.this.cidr_block, 8, 0)
13 | ipv6_cidr_block = cidrsubnet(aws_vpc.this.ipv6_cidr_block, 8, 0)
14 | assign_ipv6_address_on_creation = true
15 | vpc_id = aws_vpc.this.id
16 | tags = { "Name" = var.name }
17 | }
18 |
19 | resource "aws_internet_gateway" "this" {
20 | vpc_id = aws_vpc.this.id
21 | tags = { "Name" = var.name }
22 | }
23 |
24 | resource "aws_route_table" "this" {
25 | vpc_id = aws_vpc.this.id
26 | tags = { "Name" = var.name }
27 | }
28 |
29 | resource "aws_route_table_association" "this" {
30 | subnet_id = aws_subnet.this.id
31 | route_table_id = aws_route_table.this.id
32 | }
33 |
34 | resource "aws_route" "the_internet_v4" {
35 | route_table_id = aws_route_table.this.id
36 | gateway_id = aws_internet_gateway.this.id
37 | destination_cidr_block = "0.0.0.0/0"
38 | }
39 |
40 | resource "aws_route" "the_internet_v6" {
41 | route_table_id = aws_route_table.this.id
42 | gateway_id = aws_internet_gateway.this.id
43 | destination_ipv6_cidr_block = "::/0"
44 | }
45 |
46 | resource "aws_network_acl" "this" {
47 | vpc_id = aws_vpc.this.id
48 | tags = { "Name" = var.name }
49 | }
50 |
51 | resource "aws_network_acl_rule" "allow_all" {
52 | network_acl_id = aws_network_acl.this.id
53 | cidr_block = "0.0.0.0/0"
54 | rule_number = 100
55 | protocol = "-1"
56 | from_port = 0
57 | to_port = 0
58 | rule_action = "allow"
59 | }
60 |
61 | resource "aws_network_acl_rule" "allow_all_v6" {
62 | network_acl_id = aws_network_acl.this.id
63 | ipv6_cidr_block = "::/0"
64 | rule_number = 101
65 | protocol = "-1"
66 | from_port = 0
67 | to_port = 0
68 | rule_action = "allow"
69 | }
70 |
71 | resource "aws_security_group" "this" {
72 | vpc_id = aws_vpc.this.id
73 | name = var.name
74 | description = var.name
75 |
76 | ingress {
77 | from_port = 9993
78 | to_port = 9993
79 | protocol = "udp"
80 | cidr_blocks = ["0.0.0.0/0"]
81 | ipv6_cidr_blocks = ["::/0"]
82 | }
83 |
84 | egress {
85 | from_port = 0
86 | to_port = 0
87 | protocol = "-1"
88 | cidr_blocks = ["0.0.0.0/0"]
89 | ipv6_cidr_blocks = ["::/0"]
90 | }
91 |
92 | tags = { "Name" = var.name }
93 | }
94 |
95 |
96 | data "aws_ami" "this" {
97 | owners = ["099720109477"]
98 | most_recent = true
99 |
100 | filter {
101 | name = "name"
102 | values = [var.aws_ami]
103 | }
104 |
105 | filter {
106 | name = "virtualization-type"
107 | values = ["hvm"]
108 | }
109 | }
110 |
111 | resource "aws_instance" "this" {
112 | ami = data.aws_ami.this.id
113 | instance_type = var.instance_type
114 | source_dest_check = false
115 | subnet_id = aws_subnet.this.id
116 | tags = { "Name" = var.name }
117 | user_data = data.cloudinit_config.this.rendered
118 | vpc_security_group_ids = [aws_security_group.this.id]
119 | }
120 |
121 | resource "aws_eip" "this" {
122 | vpc = true
123 | }
124 |
125 | resource "aws_eip_association" "eip_assoc" {
126 | network_interface_id = aws_instance.this.primary_network_interface_id
127 | allocation_id = aws_eip.this.id
128 | }
129 |
130 | data "cloudinit_config" "this" {
131 | gzip = true
132 | base64_encode = true
133 |
134 | part {
135 | filename = "init.sh"
136 | content_type = "text/x-shellscript"
137 | content = templatefile("${path.root}/${var.script}", {
138 | "dnsdomain" = var.dnsdomain
139 | "hostname" = var.name
140 | "pod_cidr" = var.pod_cidr
141 | "svc" = var.svc
142 | "zeronsd" = var.zeronsd
143 | "zt_identity" = var.zt_identity
144 | "zt_network" = var.zt_network
145 | "zt_token" = var.zt_token
146 | })
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/modules/aws/variables.tf:
--------------------------------------------------------------------------------
1 | variable "name" {
2 | type = string
3 | }
4 |
5 | variable "availability_zone" {
6 | default = "us-east-2a"
7 | }
8 |
9 | variable "aws_ami" {
10 | default = "ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*"
11 | }
12 |
13 | variable "cidr_block" {
14 | default = "192.168.0.0/16"
15 | }
16 |
17 | variable "instance_type" {
18 | default = "t3.micro"
19 | }
20 |
21 | variable "dnsdomain" {
22 | type = string
23 | }
24 |
25 | variable "pod_cidr" {
26 | type = string
27 | }
28 |
29 | variable "script" {
30 | type = string
31 | }
32 |
33 | variable "svc" {
34 | type = map(
35 | object({
36 | username = string
37 | ssh_pubkey = string
38 | }))
39 |
40 | validation {
41 | condition = alltrue([for u in var.svc : can(regex("^ssh-", u.ssh_pubkey))])
42 | error_message = "The ssh_pubkey value must be a valid ssh pubkey, starting with \"ssh-\"."
43 | }
44 | }
45 |
46 | variable "zeronsd" {
47 | default = false
48 | }
49 |
50 | variable "zt_identity" {
51 | type = object({
52 | id = string
53 | private_key = string
54 | public_key = string
55 | })
56 |
57 | validation {
58 | condition = length(var.zt_identity.id) == 10
59 | error_message = "The zt_identity id must be be 10 characters long."
60 | }
61 |
62 | sensitive = true
63 | }
64 |
65 | variable "zt_network" {
66 | type = string
67 |
68 | validation {
69 | condition = length(var.zt_network) == 16
70 | error_message = "The zt_network id must be be 16 characters long."
71 | }
72 | }
73 |
74 | variable "zt_token" {
75 | type = string
76 | default = "01234567890123456789012345678912"
77 |
78 | validation {
79 | condition = length(var.zt_token) == 32
80 | error_message = "The zt_token must be be 32 characters long."
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/modules/aws/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | aws = {
4 | source = "hashicorp/aws"
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/modules/azu/main.tf:
--------------------------------------------------------------------------------
1 |
2 | resource "azurerm_resource_group" "this" {
3 | location = var.location
4 | name = var.name
5 | }
6 |
7 | resource "azurerm_virtual_network" "this" {
8 | name = var.name
9 | resource_group_name = azurerm_resource_group.this.name
10 | location = azurerm_resource_group.this.location
11 | address_space = var.address_space
12 | }
13 |
14 | resource "azurerm_public_ip" "this_v4" {
15 | name = "${var.name}-v4"
16 | resource_group_name = azurerm_resource_group.this.name
17 | location = azurerm_resource_group.this.location
18 | sku = "Standard"
19 | allocation_method = "Static"
20 | ip_version = "IPv4"
21 | }
22 |
23 | resource "azurerm_subnet" "this_v4" {
24 | name = "${var.name}-v4"
25 | resource_group_name = azurerm_resource_group.this.name
26 | virtual_network_name = azurerm_virtual_network.this.name
27 | address_prefixes = var.v4_address_prefixes
28 | }
29 |
30 | resource "azurerm_public_ip" "this_v6" {
31 | name = "${var.name}-v6"
32 | resource_group_name = azurerm_resource_group.this.name
33 | location = azurerm_resource_group.this.location
34 | sku = "Standard"
35 | allocation_method = "Static"
36 | ip_version = "IPv6"
37 | }
38 |
39 | resource "azurerm_subnet" "this_v6" {
40 | name = "${var.name}-v6"
41 | resource_group_name = azurerm_resource_group.this.name
42 | virtual_network_name = azurerm_virtual_network.this.name
43 | address_prefixes = var.v6_address_prefixes
44 | }
45 |
46 | resource "azurerm_network_interface" "this" {
47 | name = var.name
48 | resource_group_name = azurerm_resource_group.this.name
49 | location = azurerm_resource_group.this.location
50 | enable_ip_forwarding = true
51 | enable_accelerated_networking = false
52 |
53 | ip_configuration {
54 | name = "${var.name}-v4"
55 | subnet_id = azurerm_subnet.this_v4.id
56 | public_ip_address_id = azurerm_public_ip.this_v4.id
57 | private_ip_address_allocation = "Dynamic"
58 | primary = "true"
59 | }
60 |
61 | ip_configuration {
62 | name = "${var.name}-v6"
63 | subnet_id = azurerm_subnet.this_v6.id
64 | public_ip_address_id = azurerm_public_ip.this_v6.id
65 | private_ip_address_version = "IPv6"
66 | private_ip_address_allocation = "Dynamic"
67 | }
68 | }
69 |
70 | resource "azurerm_network_security_group" "this" {
71 | name = var.name
72 | resource_group_name = azurerm_resource_group.this.name
73 | location = azurerm_resource_group.this.location
74 | }
75 |
76 | resource "azurerm_network_security_rule" "zerotier" {
77 | name = "${var.name}-zerotier"
78 | resource_group_name = azurerm_resource_group.this.name
79 | network_security_group_name = azurerm_network_security_group.this.name
80 | priority = 100
81 | direction = "Inbound"
82 | access = "Allow"
83 | protocol = "Udp"
84 | source_port_range = "*"
85 | destination_port_range = "9993"
86 | source_address_prefix = "*"
87 | destination_address_prefix = "*"
88 | }
89 |
90 | resource "azurerm_network_security_rule" "egress" {
91 | name = "${var.name}-egress"
92 | resource_group_name = azurerm_resource_group.this.name
93 | network_security_group_name = azurerm_network_security_group.this.name
94 | priority = 100
95 | direction = "Outbound"
96 | access = "Allow"
97 | protocol = "*"
98 | source_port_range = "*"
99 | destination_port_range = "*"
100 | source_address_prefix = "*"
101 | destination_address_prefix = "*"
102 | }
103 |
104 | resource "azurerm_network_interface_security_group_association" "ubuntu" {
105 | network_interface_id = azurerm_network_interface.this.id
106 | network_security_group_id = azurerm_network_security_group.this.id
107 | }
108 |
109 | resource "tls_private_key" "azu-rsa" {
110 | algorithm = "RSA"
111 | rsa_bits = "2048"
112 | }
113 |
114 | resource "azurerm_linux_virtual_machine" "this" {
115 | name = var.name
116 | resource_group_name = azurerm_resource_group.this.name
117 | location = azurerm_resource_group.this.location
118 | size = "Standard_D2_v4"
119 | admin_username = "notroot"
120 | network_interface_ids = [
121 | azurerm_network_interface.this.id
122 | ]
123 |
124 | admin_ssh_key {
125 | public_key = tls_private_key.azu-rsa.public_key_openssh
126 | username = "notroot"
127 | }
128 |
129 | os_disk {
130 | caching = "ReadOnly"
131 | storage_account_type = "Standard_LRS"
132 | }
133 |
134 | source_image_reference {
135 | publisher = "canonical"
136 | offer = "0001-com-ubuntu-server-focal"
137 | sku = "20_04-lts"
138 | version = "latest"
139 | }
140 |
141 | custom_data = data.cloudinit_config.this.rendered
142 | }
143 |
144 | data "cloudinit_config" "this" {
145 | gzip = false
146 | base64_encode = true
147 |
148 | part {
149 | filename = "init.sh"
150 | content_type = "text/x-shellscript"
151 | content = templatefile("${path.root}/${var.script}", {
152 | "dnsdomain" = var.dnsdomain
153 | "hostname" = var.name
154 | "pod_cidr" = var.pod_cidr
155 | "svc" = var.svc
156 | "zeronsd" = var.zeronsd
157 | "zt_identity" = var.zt_identity
158 | "zt_network" = var.zt_network
159 | "zt_token" = var.zt_token
160 | })
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/modules/azu/variables.tf:
--------------------------------------------------------------------------------
1 |
2 | variable "name" {
3 | type = string
4 | }
5 |
6 | variable "location" {
7 | default = "eastus"
8 | }
9 |
10 | variable "address_space" {
11 | default = ["192.168.0.0/16", "ace:cab:deca::/48"]
12 | }
13 |
14 | variable "v4_address_prefixes" {
15 | default = ["192.168.1.0/24"]
16 | }
17 |
18 | variable "v6_address_prefixes" {
19 | default = ["ace:cab:deca:deed::/64"]
20 | }
21 |
22 | variable "dnsdomain" {
23 | type = string
24 | }
25 |
26 | variable "script" {
27 | type = string
28 | }
29 |
30 | variable "svc" {
31 | type = map(
32 | object({
33 | username = string
34 | ssh_pubkey = string
35 | }))
36 |
37 | validation {
38 | condition = alltrue([for u in var.svc : can(regex("^ssh-", u.ssh_pubkey))])
39 | error_message = "The ssh_pubkey value must be a valid ssh pubkey, starting with \"ssh-\"."
40 | }
41 | }
42 |
43 | variable "pod_cidr" {
44 | type = string
45 | }
46 |
47 | variable "zeronsd" {
48 | default = false
49 | }
50 |
51 | variable "zt_identity" {
52 | type = object({
53 | id = string
54 | private_key = string
55 | public_key = string
56 | })
57 |
58 | validation {
59 | condition = length(var.zt_identity.id) == 10
60 | error_message = "The zt_identity id must be be 10 characters long."
61 | }
62 |
63 | sensitive = true
64 | }
65 |
66 | variable "zt_network" {
67 | type = string
68 |
69 | validation {
70 | condition = length(var.zt_network) == 16
71 | error_message = "The zt_network id must be be 16 characters long."
72 | }
73 | }
74 |
75 | variable "zt_token" {
76 | type = string
77 | default = "01234567890123456789012345678912"
78 |
79 | validation {
80 | condition = length(var.zt_token) == 32
81 | error_message = "The zt_token must be be 32 characters long."
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/modules/azu/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | azurerm = {
4 | source = "hashicorp/azurerm"
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/modules/do/main.tf:
--------------------------------------------------------------------------------
1 |
2 | data "cloudinit_config" "this" {
3 | gzip = false
4 | base64_encode = false
5 |
6 | part {
7 | filename = "init.sh"
8 | content_type = "text/x-shellscript"
9 | content = templatefile("${path.root}/${var.script}", {
10 | "dnsdomain" = var.dnsdomain
11 | "hostname" = var.name
12 | "pod_cidr" = var.pod_cidr
13 | "svc" = var.svc
14 | "zeronsd" = var.zeronsd
15 | "zt_identity" = var.zt_identity
16 | "zt_network" = var.zt_network
17 | "zt_token" = var.zt_token
18 | })
19 | }
20 | }
21 |
22 | resource "tls_private_key" "this" {
23 | algorithm = "RSA"
24 | rsa_bits = "2048"
25 | }
26 |
27 | resource "digitalocean_ssh_key" "this" {
28 | name = var.name
29 | public_key = tls_private_key.this.public_key_openssh
30 | }
31 |
32 | resource "digitalocean_droplet" "this" {
33 | image = var.image
34 | size = var.size
35 | name = var.name
36 | region = var.region
37 | ipv6 = true
38 | tags = []
39 | ssh_keys = [digitalocean_ssh_key.this.id]
40 | user_data = data.cloudinit_config.this.rendered
41 | }
42 |
43 | resource "digitalocean_firewall" "this" {
44 | name = var.name
45 | droplet_ids = [digitalocean_droplet.this.id]
46 | inbound_rule {
47 | protocol = "udp"
48 | port_range = "9993"
49 | source_addresses = ["0.0.0.0/0", "::/0"]
50 | }
51 |
52 | outbound_rule {
53 | protocol = "tcp"
54 | port_range = "1-65535"
55 | destination_addresses = ["0.0.0.0/0", "::/0"]
56 | }
57 |
58 | outbound_rule {
59 | protocol = "udp"
60 | port_range = "1-65535"
61 | destination_addresses = ["0.0.0.0/0", "::/0"]
62 | }
63 |
64 | outbound_rule {
65 | protocol = "icmp"
66 | destination_addresses = ["0.0.0.0/0", "::/0"]
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/modules/do/variables.tf:
--------------------------------------------------------------------------------
1 |
2 | variable "name" {
3 | type = string
4 | }
5 |
6 | variable "image" {
7 | default = "ubuntu-20-04-x64"
8 | type = string
9 | }
10 |
11 | variable "region" {
12 | default = "nyc1"
13 | type = string
14 | }
15 |
16 | variable "size" {
17 | default = "s-1vcpu-1gb-amd"
18 | type = string
19 | }
20 |
21 | variable "dnsdomain" {
22 | type = string
23 | }
24 |
25 | variable "pod_cidr" {
26 | type = string
27 | }
28 |
29 |
30 | variable "script" {
31 | type = string
32 | }
33 |
34 | variable "svc" {
35 | type = map(
36 | object({
37 | username = string
38 | ssh_pubkey = string
39 | }))
40 |
41 | validation {
42 | condition = alltrue([for u in var.svc : can(regex("^ssh-", u.ssh_pubkey))])
43 | error_message = "The ssh_pubkey value must be a valid ssh pubkey, starting with \"ssh-\"."
44 | }
45 | }
46 |
47 | variable "zeronsd" {
48 | default = false
49 | }
50 |
51 | variable "zt_identity" {
52 | type = object({
53 | id = string
54 | private_key = string
55 | public_key = string
56 | })
57 |
58 | validation {
59 | condition = length(var.zt_identity.id) == 10
60 | error_message = "The zt_identity id must be be 10 characters long."
61 | }
62 |
63 | sensitive = true
64 | }
65 |
66 | variable "zt_network" {
67 | type = string
68 |
69 | validation {
70 | condition = length(var.zt_network) == 16
71 | error_message = "The zt_network id must be be 16 characters long."
72 | }
73 | }
74 |
75 | variable "zt_token" {
76 | type = string
77 | default = "01234567890123456789012345678912"
78 |
79 | validation {
80 | condition = length(var.zt_token) == 32
81 | error_message = "The zt_token must be be 32 characters long."
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/modules/do/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | digitalocean = {
4 | source = "digitalocean/digitalocean"
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/modules/eqx/main.tf:
--------------------------------------------------------------------------------
1 |
2 | data "metal_operating_system" "this" {
3 | name = var.operating_system
4 | provisionable_on = var.plan
5 | }
6 |
7 | data "metal_metro" "sv" {
8 | code = "dc"
9 | }
10 |
11 | resource "metal_project" "this" {
12 | name = var.project
13 | }
14 |
15 | resource "tls_private_key" "this" {
16 | algorithm = "RSA"
17 | rsa_bits = "2048"
18 | }
19 |
20 | resource "metal_project_ssh_key" "this" {
21 | name = var.name
22 | public_key = tls_private_key.this.public_key_openssh
23 | project_id = metal_project.this.id
24 | }
25 |
26 | resource "metal_device" "this" {
27 | hostname = var.name
28 | plan = var.plan
29 | metro = var.metro
30 | operating_system = data.metal_operating_system.this.slug
31 | billing_cycle = var.billing_cycle
32 | project_id = metal_project.this.id
33 | project_ssh_key_ids = [metal_project_ssh_key.this.id]
34 | user_data = data.cloudinit_config.this.rendered
35 | }
36 |
37 | data "cloudinit_config" "this" {
38 | gzip = false
39 | base64_encode = false
40 |
41 | part {
42 | filename = "init.sh"
43 | content_type = "text/x-shellscript"
44 | content = templatefile("${path.root}/${var.script}", {
45 | "dnsdomain" = var.dnsdomain
46 | "hostname" = var.name
47 | "pod_cidr" = var.pod_cidr
48 | "svc" = var.svc
49 | "zeronsd" = var.zeronsd
50 | "zt_identity" = var.zt_identity
51 | "zt_network" = var.zt_network
52 | "zt_token" = var.zt_token
53 | })
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/modules/eqx/variables.tf:
--------------------------------------------------------------------------------
1 | variable "name" {
2 | type = string
3 | }
4 |
5 | variable "project" {
6 | default = "quickstart"
7 | }
8 |
9 | variable "plan" {
10 | default = "c3.small.x86"
11 | }
12 |
13 | variable "metro" {
14 | default = "dc"
15 | }
16 |
17 | variable "operating_system" {
18 | default = "Ubuntu 20.04 LTS"
19 | }
20 |
21 | variable "billing_cycle" {
22 | default = "hourly"
23 | }
24 |
25 | variable "dnsdomain" {
26 | type = string
27 | }
28 |
29 | variable "pod_cidr" {
30 | type = string
31 | }
32 |
33 | variable "script" {
34 | type = string
35 | }
36 |
37 | variable "svc" {
38 | type = map(
39 | object({
40 | username = string
41 | ssh_pubkey = string
42 | }))
43 |
44 | validation {
45 | condition = alltrue([for u in var.svc : can(regex("^ssh-", u.ssh_pubkey))])
46 | error_message = "The ssh_pubkey value must be a valid ssh pubkey, starting with \"ssh-\"."
47 | }
48 | }
49 |
50 | variable "zeronsd" {
51 | default = false
52 | }
53 |
54 | variable "zt_identity" {
55 | type = object({
56 | id = string
57 | private_key = string
58 | public_key = string
59 | })
60 |
61 | validation {
62 | condition = length(var.zt_identity.id) == 10
63 | error_message = "The zt_identity id must be be 10 characters long."
64 | }
65 |
66 | sensitive = true
67 | }
68 |
69 | variable "zt_network" {
70 | type = string
71 |
72 | validation {
73 | condition = length(var.zt_network) == 16
74 | error_message = "The zt_network id must be be 16 characters long."
75 | }
76 | }
77 |
78 | variable "zt_token" {
79 | type = string
80 | default = "01234567890123456789012345678912"
81 |
82 | validation {
83 | condition = length(var.zt_token) == 32
84 | error_message = "The zt_token must be be 32 characters long."
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/modules/eqx/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | metal = {
4 | source = "equinix/metal"
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/modules/gcp/main.tf:
--------------------------------------------------------------------------------
1 |
2 | data "google_project" "this" {}
3 |
4 | resource "google_compute_network" "this" {
5 | name = var.name
6 | project = data.google_project.this.project_id
7 | auto_create_subnetworks = false
8 | }
9 |
10 | resource "google_compute_subnetwork" "this" {
11 | name = var.name
12 | network = google_compute_network.this.self_link
13 | region = var.region
14 | ip_cidr_range = var.ip_cidr_range
15 | }
16 |
17 | resource "google_compute_address" "this" {
18 | name = var.name
19 | description = var.name
20 | region = var.region
21 | project = data.google_project.this.project_id
22 | address_type = "EXTERNAL"
23 | }
24 |
25 | resource "google_compute_firewall" "ingress" {
26 | name = "${var.name}-ingress"
27 | network = google_compute_network.this.self_link
28 |
29 | source_ranges = ["0.0.0.0/0"]
30 |
31 | direction = "INGRESS"
32 | allow {
33 | protocol = "udp"
34 | ports = ["9993"]
35 | }
36 | }
37 |
38 | resource "google_compute_firewall" "egress" {
39 | name = "${var.name}-egress"
40 | network = google_compute_network.this.self_link
41 |
42 | direction = "EGRESS"
43 | allow { protocol = "tcp" }
44 | allow { protocol = "udp" }
45 | allow { protocol = "icmp" }
46 | }
47 |
48 | resource "google_compute_instance" "this" {
49 | can_ip_forward = true
50 | machine_type = var.machine_type
51 | description = var.name
52 | hostname = "${var.name}.${var.dnsdomain}"
53 | name = var.name
54 | project = data.google_project.this.project_id
55 | zone = var.zone
56 |
57 | boot_disk {
58 | initialize_params {
59 | image = var.image
60 | }
61 | }
62 |
63 | network_interface {
64 | subnetwork = google_compute_subnetwork.this.id
65 | access_config {
66 | nat_ip = google_compute_address.this.address
67 | }
68 | }
69 |
70 | metadata = {
71 | user-data = data.cloudinit_config.this.rendered
72 | }
73 | }
74 |
75 | data "cloudinit_config" "this" {
76 | gzip = false
77 | base64_encode = false
78 |
79 | part {
80 | filename = "init.sh"
81 | content_type = "text/x-shellscript"
82 | content = templatefile("${path.root}/${var.script}", {
83 | "dnsdomain" = var.dnsdomain
84 | "hostname" = var.name
85 | "pod_cidr" = var.pod_cidr
86 | "svc" = var.svc
87 | "zeronsd" = var.zeronsd
88 | "zt_identity" = var.zt_identity
89 | "zt_network" = var.zt_network
90 | "zt_token" = var.zt_token
91 | })
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/modules/gcp/variables.tf:
--------------------------------------------------------------------------------
1 | variable "name" {
2 | type = string
3 | }
4 |
5 | variable "image" {
6 | default = "ubuntu-os-cloud/ubuntu-2004-lts"
7 | }
8 |
9 | variable "ip_cidr_range" {
10 | default = "192.168.1.0/24"
11 | }
12 |
13 | variable "machine_type" {
14 | default = "e2-medium"
15 | }
16 |
17 | variable "region" {
18 | default = "europe-west4"
19 | }
20 |
21 | variable "zone" {
22 | default = "europe-west4-a"
23 | }
24 |
25 | variable "dnsdomain" {
26 | type = string
27 | }
28 |
29 | variable "pod_cidr" {
30 | type = string
31 | }
32 |
33 | variable "script" {
34 | type = string
35 | }
36 |
37 | variable "svc" {
38 | type = map(
39 | object({
40 | username = string
41 | ssh_pubkey = string
42 | }))
43 |
44 | validation {
45 | condition = alltrue([for u in var.svc : can(regex("^ssh-", u.ssh_pubkey))])
46 | error_message = "The ssh_pubkey value must be a valid ssh pubkey, starting with \"ssh-\"."
47 | }
48 | }
49 |
50 | variable "zeronsd" {
51 | default = false
52 | }
53 |
54 | variable "zt_identity" {
55 | type = object({
56 | id = string
57 | private_key = string
58 | public_key = string
59 | })
60 |
61 | validation {
62 | condition = length(var.zt_identity.id) == 10
63 | error_message = "The zt_identity id must be be 10 characters long."
64 | }
65 |
66 | sensitive = true
67 | }
68 |
69 | variable "zt_network" {
70 | type = string
71 |
72 | validation {
73 | condition = length(var.zt_network) == 16
74 | error_message = "The zt_network id must be be 16 characters long."
75 | }
76 | }
77 |
78 | variable "zt_token" {
79 | type = string
80 | default = "01234567890123456789012345678912"
81 |
82 | validation {
83 | condition = length(var.zt_token) == 32
84 | error_message = "The zt_token must be be 32 characters long."
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/modules/gcp/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | google = {
4 | source = "hashicorp/google"
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/modules/ibm/main.tf:
--------------------------------------------------------------------------------
1 |
2 | data "ibm_resource_group" "this" {
3 | name = "Default"
4 | }
5 |
6 | data "ibm_is_image" "this" {
7 | name = var.image
8 | }
9 |
10 | data "ibm_is_instance_profiles" "this" {}
11 |
12 | resource "ibm_is_vpc" "this" {
13 | name = var.name
14 | resource_group = data.ibm_resource_group.this.id
15 | address_prefix_management = "manual"
16 | }
17 |
18 | resource "ibm_is_vpc_address_prefix" "this" {
19 | name = var.name
20 | zone = var.zone
21 | vpc = ibm_is_vpc.this.id
22 | cidr = var.vpc_cidr
23 | }
24 |
25 | resource "ibm_is_public_gateway" "this" {
26 | name = var.name
27 | vpc = ibm_is_vpc.this.id
28 | zone = var.zone
29 | }
30 |
31 | resource "ibm_is_subnet" "this" {
32 | name = var.name
33 | vpc = ibm_is_vpc.this.id
34 | zone = var.zone
35 | ipv4_cidr_block = var.subnet_cidr
36 | public_gateway = ibm_is_public_gateway.this.id
37 | depends_on = [ibm_is_vpc_address_prefix.this]
38 | }
39 |
40 | resource "ibm_is_security_group" "this" {
41 | name = var.name
42 | vpc = ibm_is_vpc.this.id
43 | resource_group = data.ibm_resource_group.this.id
44 | }
45 |
46 | resource "ibm_is_security_group_rule" "inbound_udp" {
47 | group = ibm_is_security_group.this.id
48 | remote = "0.0.0.0/0"
49 | direction = "inbound"
50 | udp {
51 | port_min = 9993
52 | port_max = 9993
53 | }
54 | }
55 |
56 | resource "ibm_is_security_group_rule" "outbound_udp" {
57 | group = ibm_is_security_group.this.id
58 | remote = "0.0.0.0/0"
59 | direction = "outbound"
60 | udp {
61 | port_min = 1
62 | port_max = 65535
63 | }
64 | }
65 |
66 | resource "ibm_is_security_group_rule" "outbound_tcp" {
67 | group = ibm_is_security_group.this.id
68 | remote = "0.0.0.0/0"
69 | direction = "outbound"
70 | tcp {
71 | port_min = 1
72 | port_max = 65535
73 | }
74 | }
75 |
76 | resource "ibm_is_instance" "this" {
77 | name = var.name
78 | image = data.ibm_is_image.this.id
79 | profile = var.instance_profile
80 |
81 | primary_network_interface {
82 | subnet = ibm_is_subnet.this.id
83 | security_groups = [ibm_is_security_group.this.id]
84 | allow_ip_spoofing = false
85 | }
86 |
87 | vpc = ibm_is_vpc.this.id
88 | zone = var.zone
89 | keys = []
90 | resource_group = data.ibm_resource_group.this.id
91 | user_data = data.cloudinit_config.this.rendered
92 | }
93 |
94 | data "cloudinit_config" "this" {
95 | gzip = false
96 | base64_encode = false
97 |
98 | part {
99 | filename = "init.sh"
100 | content_type = "text/x-shellscript"
101 | content = templatefile("${path.root}/${var.script}", {
102 | "dnsdomain" = var.dnsdomain
103 | "hostname" = var.name
104 | "pod_cidr" = var.pod_cidr
105 | "svc" = var.svc
106 | "zeronsd" = var.zeronsd
107 | "zt_identity" = var.zt_identity
108 | "zt_network" = var.zt_network
109 | "zt_token" = var.zt_token
110 | })
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/modules/ibm/variables.tf:
--------------------------------------------------------------------------------
1 |
2 | variable "name" {
3 | type = string
4 | }
5 |
6 | variable "vpc_cidr" {
7 | type = string
8 | }
9 |
10 | variable "subnet_cidr" {
11 | type = string
12 | }
13 |
14 | variable "zone" {
15 | default = "us-east-1"
16 | }
17 |
18 | variable "instance_profile" {
19 | default = "cx2-2x4"
20 | }
21 |
22 | variable "image" {
23 | default = "ibm-ubuntu-20-04-2-minimal-amd64-1"
24 | }
25 |
26 | variable "dnsdomain" {
27 | type = string
28 | }
29 |
30 | variable "pod_cidr" {
31 | type = string
32 | }
33 |
34 | variable "script" {
35 | type = string
36 | }
37 |
38 | variable "svc" {
39 | type = map(
40 | object({
41 | username = string
42 | ssh_pubkey = string
43 | }))
44 |
45 | validation {
46 | condition = alltrue([for u in var.svc : can(regex("^ssh-", u.ssh_pubkey))])
47 | error_message = "The ssh_pubkey value must be a valid ssh pubkey, starting with \"ssh-\"."
48 | }
49 | }
50 |
51 | variable "zeronsd" {
52 | default = false
53 | }
54 |
55 | variable "zt_identity" {
56 | type = object({
57 | id = string
58 | private_key = string
59 | public_key = string
60 | })
61 |
62 | validation {
63 | condition = length(var.zt_identity.id) == 10
64 | error_message = "The zt_identity id must be be 10 characters long."
65 | }
66 |
67 | sensitive = true
68 | }
69 |
70 | variable "zt_network" {
71 | type = string
72 |
73 | validation {
74 | condition = length(var.zt_network) == 16
75 | error_message = "The zt_network id must be be 16 characters long."
76 | }
77 | }
78 |
79 | variable "zt_token" {
80 | type = string
81 | default = "01234567890123456789012345678912"
82 |
83 | validation {
84 | condition = length(var.zt_token) == 32
85 | error_message = "The zt_token must be be 32 characters long."
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/modules/ibm/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | ibm = {
4 | source = "IBM-Cloud/ibm"
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/modules/oci/main.tf:
--------------------------------------------------------------------------------
1 |
2 | resource "oci_core_vcn" "this" {
3 | cidr_block = var.vpc_cidr
4 | compartment_id = var.compartment_id
5 | display_name = var.name
6 | dns_label = var.name
7 | }
8 |
9 | resource "oci_core_internet_gateway" "this" {
10 | compartment_id = var.compartment_id
11 | display_name = var.name
12 | vcn_id = oci_core_vcn.this.id
13 | }
14 |
15 | resource "oci_core_route_table" "this" {
16 | compartment_id = var.compartment_id
17 | vcn_id = oci_core_vcn.this.id
18 | display_name = var.name
19 |
20 | route_rules {
21 | destination = "0.0.0.0/0"
22 | destination_type = "CIDR_BLOCK"
23 | network_entity_id = oci_core_internet_gateway.this.id
24 | }
25 | }
26 |
27 | resource "oci_core_security_list" "this" {
28 | compartment_id = var.compartment_id
29 | vcn_id = oci_core_vcn.this.id
30 | display_name = var.name
31 |
32 | egress_security_rules {
33 | protocol = "all"
34 | destination = "0.0.0.0/0"
35 | }
36 |
37 | ingress_security_rules {
38 | protocol = "17"
39 | source = "0.0.0.0/0"
40 | udp_options {
41 | min = "9993"
42 | max = "9993"
43 | }
44 | }
45 | }
46 |
47 | resource "oci_core_subnet" "this" {
48 | cidr_block = var.subnet_cidr
49 | display_name = var.name
50 | dns_label = var.name
51 | security_list_ids = [oci_core_security_list.this.id]
52 | compartment_id = var.compartment_id
53 | vcn_id = oci_core_vcn.this.id
54 | route_table_id = oci_core_route_table.this.id
55 | dhcp_options_id = oci_core_vcn.this.default_dhcp_options_id
56 | }
57 |
58 | data "oci_identity_availability_domain" "this" {
59 | compartment_id = var.compartment_id
60 | ad_number = 2
61 | }
62 |
63 | data "cloudinit_config" "this" {
64 | gzip = false
65 | base64_encode = true
66 |
67 | part {
68 | filename = "init.sh"
69 | content_type = "text/x-shellscript"
70 | content = templatefile("${path.root}/${var.script}", {
71 | "dnsdomain" = var.dnsdomain
72 | "hostname" = var.name
73 | "pod_cidr" = var.pod_cidr
74 | "svc" = var.svc
75 | "zeronsd" = var.zeronsd
76 | "zt_identity" = var.zt_identity
77 | "zt_network" = var.zt_network
78 | "zt_token" = var.zt_token
79 | })
80 | }
81 | }
82 |
83 | resource "oci_core_instance" "this" {
84 | availability_domain = data.oci_identity_availability_domain.this.name
85 | compartment_id = var.compartment_id
86 | display_name = var.name
87 | shape = var.shape
88 |
89 | create_vnic_details {
90 | subnet_id = oci_core_subnet.this.id
91 | display_name = "primaryvnic"
92 | assign_public_ip = true
93 | hostname_label = var.name
94 | }
95 |
96 | source_details {
97 | source_type = "image"
98 | source_id = "ocid1.image.oc1.iad.aaaaaaaayfc7vgsvgtmrlka74mdhyawbjmpcllntrowcuimb6nfxyqur734q"
99 | }
100 |
101 | metadata = {
102 | user_data = data.cloudinit_config.this.rendered
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/modules/oci/variables.tf:
--------------------------------------------------------------------------------
1 | variable "name" {}
2 | variable "compartment_id" {}
3 | variable "vpc_cidr" {}
4 | variable "subnet_cidr" {}
5 | variable "shape" { default = "VM.Standard.E2.1.Micro" }
6 |
7 | variable "dnsdomain" {
8 | type = string
9 | }
10 |
11 | variable "pod_cidr" {
12 | type = string
13 | }
14 |
15 | variable "script" {
16 | type = string
17 | }
18 |
19 | variable "svc" {
20 | type = map(
21 | object({
22 | username = string
23 | ssh_pubkey = string
24 | }))
25 |
26 | validation {
27 | condition = alltrue([for u in var.svc : can(regex("^ssh-", u.ssh_pubkey))])
28 | error_message = "The ssh_pubkey value must be a valid ssh pubkey, starting with \"ssh-\"."
29 | }
30 | }
31 |
32 | variable "zeronsd" {
33 | default = false
34 | }
35 |
36 | variable "zt_identity" {
37 | type = object({
38 | id = string
39 | private_key = string
40 | public_key = string
41 | })
42 |
43 | validation {
44 | condition = length(var.zt_identity.id) == 10
45 | error_message = "The zt_identity id must be be 10 characters long."
46 | }
47 |
48 | sensitive = true
49 | }
50 |
51 | variable "zt_network" {
52 | type = string
53 |
54 | validation {
55 | condition = length(var.zt_network) == 16
56 | error_message = "The zt_network id must be be 16 characters long."
57 | }
58 | }
59 | variable "zt_token" {
60 | type = string
61 | default = "01234567890123456789012345678912"
62 |
63 | validation {
64 | condition = length(var.zt_token) == 32
65 | error_message = "The zt_token must be be 32 characters long."
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/modules/vul/main.tf:
--------------------------------------------------------------------------------
1 | data "vultr_region" "this" {
2 | filter {
3 | name = "id"
4 | values = ["ewr"]
5 | }
6 | }
7 |
8 | data "vultr_os" "this" {
9 | filter {
10 | name = "name"
11 | values = ["Ubuntu 20.04 x64"]
12 | }
13 | }
14 |
15 | data "vultr_plan" "this" {
16 | filter {
17 | name = "id"
18 | values = ["vc2-1c-1gb"]
19 | }
20 | }
21 |
22 | resource "vultr_startup_script" "this" {
23 | name = var.name
24 | script = base64encode(templatefile("${path.root}/${var.script}", {
25 | "dnsdomain" = var.dnsdomain
26 | "hostname" = var.name
27 | "pod_cidr" = var.pod_cidr
28 | "svc" = var.svc
29 | "zeronsd" = var.zeronsd
30 | "zt_identity" = var.zt_identity
31 | "zt_network" = var.zt_network
32 | "zt_token" = var.zt_token
33 | }))
34 | }
35 |
36 |
37 | resource "vultr_firewall_group" "this" {
38 | description = var.name
39 | }
40 |
41 | resource "vultr_firewall_rule" "zt_v4" {
42 | firewall_group_id = vultr_firewall_group.this.id
43 | protocol = "udp"
44 | ip_type = "v4"
45 | subnet = "0.0.0.0"
46 | subnet_size = 0
47 | port = "9993"
48 | notes = "zerotier"
49 | }
50 |
51 | resource "vultr_firewall_rule" "zt_v6" {
52 | firewall_group_id = vultr_firewall_group.this.id
53 | protocol = "udp"
54 | ip_type = "v6"
55 | subnet = "::"
56 | subnet_size = 0
57 | port = "9993"
58 | notes = "zerotier"
59 | }
60 |
61 | resource "vultr_instance" "this" {
62 | plan = data.vultr_plan.this.id
63 | region = data.vultr_region.this.id
64 | os_id = data.vultr_os.this.id
65 | label = var.name
66 | tag = var.name
67 | hostname = var.name
68 | enable_ipv6 = true
69 | backups = "disabled"
70 | ddos_protection = false
71 | activation_email = false
72 | firewall_group_id = vultr_firewall_group.this.id
73 | script_id = vultr_startup_script.this.id
74 | }
75 |
--------------------------------------------------------------------------------
/modules/vul/variables.tf:
--------------------------------------------------------------------------------
1 |
2 | variable "name" {
3 | type = string
4 | }
5 |
6 | variable "dnsdomain" {
7 | type = string
8 | }
9 |
10 | variable "pod_cidr" {
11 | type = string
12 | }
13 |
14 | variable "script" {
15 | type = string
16 | }
17 |
18 | variable "svc" {
19 | type = map(
20 | object({
21 | username = string
22 | ssh_pubkey = string
23 | }))
24 |
25 | validation {
26 | condition = alltrue([for u in var.svc : can(regex("^ssh-", u.ssh_pubkey))])
27 | error_message = "The ssh_pubkey value must be a valid ssh pubkey, starting with \"ssh-\"."
28 | }
29 | }
30 |
31 | variable "zeronsd" {
32 | default = false
33 | }
34 |
35 | variable "zt_identity" {
36 | type = object({
37 | id = string
38 | private_key = string
39 | public_key = string
40 | })
41 |
42 | validation {
43 | condition = length(var.zt_identity.id) == 10
44 | error_message = "The zt_identity id must be be 10 characters long."
45 | }
46 |
47 | sensitive = true
48 | }
49 |
50 | variable "zt_network" {
51 | type = string
52 |
53 | validation {
54 | condition = length(var.zt_network) == 16
55 | error_message = "The zt_network id must be be 16 characters long."
56 | }
57 | }
58 |
59 | variable "zt_token" {
60 | type = string
61 | default = "01234567890123456789012345678912"
62 |
63 | validation {
64 | condition = length(var.zt_token) == 32
65 | error_message = "The zt_token must be be 32 characters long."
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/modules/vul/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | vultr = {
4 | source = "vultr/vultr"
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/outputs.tf:
--------------------------------------------------------------------------------
1 |
2 | output "networks" {
3 | value = {
4 | demolab = zerotier_network.demolab.id
5 | }
6 | }
7 |
8 | output "identities" {
9 | value = {
10 | for k, v in zerotier_identity.instances :
11 | k => v.id
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/provider.tf:
--------------------------------------------------------------------------------
1 | provider "azurerm" {
2 | features {}
3 | }
4 |
--------------------------------------------------------------------------------
/variables.tf:
--------------------------------------------------------------------------------
1 |
2 | variable "users" {
3 | default = {
4 | alice = {
5 | username = "alice"
6 | ssh_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGxBkqXD41K8LfyJrjf8PSrxsNqhNUlWfqIzM52iWy+B alice@sean.io"
7 | }
8 | bob = {
9 | username = "bob"
10 | ssh_pubkey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKPxC8oiMHeqcTH507iWJbHs/4/yx3vOOBDf/n6Eowj7 bob@sean.io"
11 | }
12 | }
13 | }
14 |
15 | variable "devices" {
16 | default = {
17 | laptop = {
18 | member_id = "a11c3411ce"
19 | description = "alice laptop"
20 | }
21 | workstation = {
22 | member_id = "b0bd0bb0bb"
23 | description = "bob's desktop"
24 | }
25 | }
26 | }
27 |
28 | variable "instances" {
29 | default = {
30 | do = {
31 | description = "Digital Ocean"
32 | ip_assignment = "10.0.1.1"
33 | enabled = true
34 | }
35 | aws = {
36 | description = "Amazon Web Services"
37 | ip_assignment = "10.0.2.1"
38 | enabled = true
39 | }
40 | gcp = {
41 | description = "Google Compute Platform"
42 | ip_assignment = "10.0.3.1"
43 | enabled = true
44 | }
45 | azu = {
46 | description = "Microsoft Azure"
47 | ip_assignment = "10.0.4.1"
48 | enabled = true
49 | }
50 | oci = {
51 | description = "Oracle Cloud Infrastructure"
52 | ip_assignment = "10.0.5.1"
53 | enabled = true
54 | }
55 | ibm = {
56 | description = "IBM Cloud"
57 | ip_assignment = "10.0.6.1"
58 | enabled = true
59 | }
60 | vul = {
61 | description = "Vultr"
62 | ip_assignment = "10.0.7.1"
63 | enabled = true
64 | }
65 | ali = {
66 | description = "Alibaba Cloud"
67 | ip_assignment = "10.0.8.1"
68 | enabled = true
69 | }
70 | eqx = {
71 | description = "Equinix Metal"
72 | ip_assignment = "10.0.9.1"
73 | enabled = true
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/versions.tf:
--------------------------------------------------------------------------------
1 | terraform {
2 | required_providers {
3 | zerotier = {
4 | source = "zerotier/zerotier"
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------