├── .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 | ZeroNSD
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 | old man yells at cloud
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 |

import repo

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 |

click new workspace

82 |

version control workflow

83 |

connect to a version control provider

84 |

choose a repository

85 |

name workspace

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 |

configure variables

94 |

zerotier_central_token

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 |

click on main.tf

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 |

list workspaces

158 |

queue plan

159 | 160 |

Confirm the plan by clicking "Confirm & Apply"

161 |

confirm plan

162 | 163 |

observe ran plan

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 |

observe in central

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 |

open variables.tf

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 |

edit variables.tf

189 | 190 |

Save your work by clicking "Commit changes" at the bottom of the page.

191 |

commit changes

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 |

observe planned workspace

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 |

navigate to runs.. main.tf

204 |

confirm and apply variables.tf

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 |

add digitalocean_token

216 | 217 |

218 | Next, edit main.tf and uncomment the Digital Ocean module. 219 | click on main.tf 220 |

221 | 222 |

uncomment digital ocean

223 |

commit changes

224 |

observe planned workspace

225 |

navigate to runs

226 |

confirm and apply digital ocean

227 |

plan finished

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 |

examine outputs

234 | 235 |

236 | You can also find it in the ZeroTier Central webUI. 237 | examine outputs 238 |

239 | 240 |

Join your laptop to the network. Make sure to check "Allow DNS"

241 |

examine outputs

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 |

ssh to digital ocean

246 | 247 | ## Spin up Multiple Clouds 248 | 249 |

250 | Baton Bunny, Copyright 1959  Warner Bros.
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 | hit a webserver
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 | https://www.flickr.com/photos/valkyrieh116/311526846
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 | https://www.flickr.com/photos/agizienski/3605131450
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 |

import repo

675 |

import repo

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 | --------------------------------------------------------------------------------