From 31d909d81e450733b65456efc9a7987acf5c199f Mon Sep 17 00:00:00 2001 From: Alex Trull Date: Sat, 18 Nov 2023 19:48:58 +0100 Subject: [PATCH] Latest changes (#3) code: cleaner code and dropped lookup for modern method, dropped non-https responder, README update pipeline: github actions workflow, pre-commit, docs, tflint and checkov --- .github/workflows/build.yaml | 41 +++++++++++++++++++++++++++++ .pre-commit-config.yaml | 8 ++++++ README.md | 51 +++++++++++++++++++++++++++++++++--- main.tf | 43 ++++++++++++++---------------- variables.tf | 14 +++++++--- 5 files changed, 127 insertions(+), 30 deletions(-) create mode 100644 .github/workflows/build.yaml create mode 100644 .pre-commit-config.yaml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..7eb6b27 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,41 @@ +name: "Module Test" +on: [push, pull_request] + +permissions: read-all + +jobs: + terraform-plan: + name: "Terraform Lint and Plan" + runs-on: ubuntu-latest + defaults: + run: + shell: bash + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v2 + + - uses: terraform-docs/gh-actions@main + + - uses: terraform-linters/setup-tflint@v3 + + - name: Terraform Fmt + id: fmt + run: terraform fmt -no-color + + - name: Terraform Init + id: init + run: terraform init + + - name: Terraform Validate + id: validate + run: terraform validate -no-color + + - name: Terraform Plan + id: plan + run: terraform plan -no-color 2>&1 + + - uses: bridgecrewio/checkov-action@v12 \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..f788e8a --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,8 @@ +repos: +- repo: https://github.com/antonbabenko/pre-commit-terraform + rev: v1.83.6 + hooks: + - id: terraform_fmt + - id: terraform_docs + - id: terraform_checkov + - id: terraform_tflint diff --git a/README.md b/README.md index 7fc4ab3..3b09c37 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,52 @@ # Overbuilt myip module -Collect and determine what is probably our source ip, only, providing the most common response, which we hope will always be the most correct. +## What it does: -See variables.tf for variables including the list of sources we can check. +This module polls a series of fairly well known but occasionally unreliable 'what is my ip' services. It then produces the most common response that is a valid ipv4 or ipv6 address (both outputs are separately provided). -See outputs.tf for outputs. +We support two providers - `curl` and `http`. `curl` is the default provider because it has better failure handling whereas `http` provider will fail a plan/apply if the endpoint doesn't respond. + + +## Requirements + +| Name | Version | +|------|---------| +| [terraform](#requirement\_terraform) | ~> 1.0 | +| [curl](#requirement\_curl) | ~> 1.0.2 | +| [http](#requirement\_http) | ~> 3 | + +## Providers + +| Name | Version | +|------|---------| +| [curl](#provider\_curl) | 1.0.2 | +| [http](#provider\_http) | 3.4.0 | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [curl_curl.myip](https://registry.terraform.io/providers/anschoewe/curl/latest/docs/data-sources/curl) | data source | +| [http_http.myip](https://registry.terraform.io/providers/hashicorp/http/latest/docs/data-sources/http) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [data\_provider](#input\_data\_provider) | `curl` or `http` providers are both supported - we recommend `curl` | `string` | `"curl"` | no | +| [extra\_service\_urls](#input\_extra\_service\_urls) | Put your own in here if you want extra ones, this gets merged with the `service_urls` list | `list(string)` | `[]` | no | +| [service\_urls](#input\_service\_urls) | List of urls to use for getting our IP | `list(string)` |
[
"https://api.seeip.org",
"https://ipinfo.io/ip",
"https://ifconfig.co",
"https://icanhazip.com",
"https://api.ipify.org",
"https://ifconfig.me",
"https://ipecho.net/plain",
"https://ifconfig.io",
"http://eth0.me/",
"https://ident.me",
"https://ipv4.ident.me"
]
| no | + +## Outputs + +| Name | Description | +|------|-------------| +| [ipv4](#output\_ipv4) | n/a | +| [ipv4\_all\_matches](#output\_ipv4\_all\_matches) | n/a | +| [ipv6](#output\_ipv6) | n/a | +| [ipv6\_all\_matches](#output\_ipv6\_all\_matches) | n/a | + diff --git a/main.tf b/main.tf index 0db2533..53ac921 100644 --- a/main.tf +++ b/main.tf @@ -1,28 +1,35 @@ terraform { + required_version = "~> 1.0" required_providers { curl = { - version = "1.0.2" + version = "~> 1.0.2" source = "anschoewe/curl" } + http = { + version = "~> 3" + source = "hashicorp/http" + } } } # curl is the default method data "curl" "myip" { - for_each = var.data_provider == "curl" ? toset(var.myip_service_urls) : [] + for_each = var.data_provider == "curl" ? toset(local.service_urls) : [] http_method = "GET" uri = each.key } # but we can use http if you prefer data "http" "myip" { - for_each = var.data_provider == "http" ? toset(var.myip_service_urls) : [] + for_each = var.data_provider == "http" ? toset(local.service_urls) : [] url = each.key method = "GET" request_timeout_ms = 500 } locals { + # merge extra with primary list and make sure entries are unique + service_urls = distinct(concat(var.service_urls, var.extra_service_urls)) # build a list of responses service_response_bodies = var.data_provider == "curl" ? values(data.curl.myip)[*].response : values(data.http.myip)[*].response_body @@ -41,29 +48,17 @@ locals { ] # what follows is a really long winded version of uniq -c | sort -n - ipv4_index_list = [for index, item in local.ipv4_matches : length([for i in slice(local.ipv4_matches, 0, index + 1) : i if i == item])] - ipv4_joined_index = zipmap(local.ipv4_matches, local.ipv4_index_list) - ipv4_reverse_index = { for k, v in local.ipv4_joined_index : v => k... } - ipv4_most_common_response = local.ipv4_reverse_index != {} ? lookup( - local.ipv4_reverse_index, - element( - sort(keys(local.ipv4_reverse_index)), - length(local.ipv4_reverse_index) - 1 - ) - ) : [] + ipv4_index_list = [for index, item in local.ipv4_matches : length([for i in slice(local.ipv4_matches, 0, index + 1) : i if i == item])] + ipv4_joined_index = zipmap(local.ipv4_matches, local.ipv4_index_list) + ipv4_reverse_index = { for k, v in local.ipv4_joined_index : v => k... } + ipv4_most_common_response = local.ipv4_reverse_index != {} ? local.ipv4_reverse_index[element(sort(keys(local.ipv4_reverse_index)), length(local.ipv4_reverse_index) - 1)] : [] # uniq -c | sort -n again but for ipv6 - ipv6_index_list = [for index, item in local.ipv6_matches : length([for i in slice(local.ipv6_matches, 0, index + 1) : i if i == item])] - ipv6_joined_index = zipmap(local.ipv6_matches, local.ipv6_index_list) - ipv6_reverse_index = { for k, v in local.ipv6_joined_index : v => k... } - ipv6_most_common_response = local.ipv6_reverse_index != {} ? lookup( - local.ipv6_reverse_index, - element( - sort(keys(local.ipv6_reverse_index)), - length(local.ipv6_reverse_index) - 1 - ) - ) : [] + ipv6_index_list = [for index, item in local.ipv6_matches : length([for i in slice(local.ipv6_matches, 0, index + 1) : i if i == item])] + ipv6_joined_index = zipmap(local.ipv6_matches, local.ipv6_index_list) + ipv6_reverse_index = { for k, v in local.ipv6_joined_index : v => k... } + ipv6_most_common_response = local.ipv6_reverse_index != {} ? local.ipv6_reverse_index[element(sort(keys(local.ipv6_reverse_index)), length(local.ipv6_reverse_index) - 1)] : [] } -# See outputs.tf for where we spit out the winners \ No newline at end of file +# See outputs.tf where we spit out the winners \ No newline at end of file diff --git a/variables.tf b/variables.tf index 6226a8f..0891cd6 100644 --- a/variables.tf +++ b/variables.tf @@ -1,4 +1,4 @@ -variable "myip_service_urls" { +variable "service_urls" { default = [ "https://api.seeip.org", "https://ipinfo.io/ip", @@ -8,13 +8,21 @@ variable "myip_service_urls" { "https://ifconfig.me", "https://ipecho.net/plain", "https://ifconfig.io", - "http://eth0.me/", "https://ident.me", "https://ipv4.ident.me", ] + type = list(string) + description = "List of urls to use for getting our IP" +} + +variable "extra_service_urls" { + default = [] + type = list(string) + description = "Put your own in here if you want extra ones, this gets merged with the `service_urls` list" } variable "data_provider" { default = "curl" - description = "(curl) or (http) provider are both supported." + type = string + description = "`curl` or `http` providers are both supported - we recommend `curl`" } \ No newline at end of file