diff --git a/README.md b/README.md new file mode 100644 index 0000000..7fc4ab3 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# 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. + +See variables.tf for variables including the list of sources we can check. + +See outputs.tf for outputs. diff --git a/main.tf b/main.tf new file mode 100644 index 0000000..0db2533 --- /dev/null +++ b/main.tf @@ -0,0 +1,69 @@ +terraform { + required_providers { + curl = { + version = "1.0.2" + source = "anschoewe/curl" + } + } +} + +# curl is the default method +data "curl" "myip" { + for_each = var.data_provider == "curl" ? toset(var.myip_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) : [] + url = each.key + method = "GET" + request_timeout_ms = 500 +} + +locals { + + # build a list of responses + service_response_bodies = var.data_provider == "curl" ? values(data.curl.myip)[*].response : values(data.http.myip)[*].response_body + + # remunge it without whitespace as a list of strings + split_output = split(",", replace(trimspace(join(",", local.service_response_bodies)), "/\\s/", "")) + + # we test responses for valid v4 or v6 + ipv4_matches = [ + for cidr in local.split_output : cidr + if can(cidrnetmask("${cidr}/32")) + ] + ipv6_matches = [ + for cidr in local.split_output : cidr + if can(cidrhost("${cidr}/128", 0)) + ] + + # 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 + ) + ) : [] + + # 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 + ) + ) : [] + +} + +# See outputs.tf for where we spit out the winners \ No newline at end of file diff --git a/outputs.tf b/outputs.tf new file mode 100644 index 0000000..01e2930 --- /dev/null +++ b/outputs.tf @@ -0,0 +1,16 @@ + +output "ipv4_matches" { + value = local.ipv4_matches +} + +output "ipv6_matches" { + value = local.ipv6_matches +} + +output "ipv4_most_common_response" { + value = join("", local.ipv4_most_common_response) +} + +output "ipv6_most_common_response" { + value = join("", local.ipv6_most_common_response) +} \ No newline at end of file diff --git a/variables.tf b/variables.tf new file mode 100644 index 0000000..6226a8f --- /dev/null +++ b/variables.tf @@ -0,0 +1,20 @@ +variable "myip_service_urls" { + default = [ + "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", + ] +} + +variable "data_provider" { + default = "curl" + description = "(curl) or (http) provider are both supported." +} \ No newline at end of file