Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Numerous ngrok_reserved_domain glitches #34

Open
jwadolowski opened this issue Sep 27, 2024 · 1 comment
Open

Numerous ngrok_reserved_domain glitches #34

jwadolowski opened this issue Sep 27, 2024 · 1 comment

Comments

@jwadolowski
Copy link

jwadolowski commented Sep 27, 2024

Overview

After many years of using ngrok and creating ad-hoc configs via web UI I've recently decided it's about time to move all of that into Terraform code. In order to put it all together I started with ngrok_reserved_domain resource.

Unfortunately the migration process revealed quite a few hiccups, therefore I decided to sum it all up in a ticket (please let me know if there's a better way to report such issues). Long story short - it does work, although developer experience wasn't particularly great and there was more rough edges than anticipated.

For reference:

$ terraform -v
Terraform v1.9.6
on darwin_arm64
+ provider registry.terraform.io/hashicorp/aws v5.69.0
+ provider registry.terraform.io/ngrok/ngrok v0.3.0

It's going to be quite lengthy (sorry in advance!).

Issues

1. Required vs optional arguments

Documentation says that all the arguments are optional. That simply doesn't make any sense since user has to provide either a string (so it becomes a subdomain of <user-provided-string>.ngrok.app) or an FQDN.

resource "ngrok_reserved_domain" "this" {
}

Luckily the API returns ERR_NGROK_422 with "The reserved domain name update failed because no values were provided. Specify at least one value." message in case of above.

Suggested actions:

  1. Documentation update (I guess it may require provider codebase changes as well)
  2. Input validation - if user didn't provide required arguments it doesn't make sense to even initiate HTTP communication with the API

2. domain vs name dilemma

According to the docs either name or domain can be specified. Here's what it says at the time of writing:

  • domain - (String) hostname of the reserved domain
  • name - (String) the domain name to reserve. It may be a full domain name like app.example.com. If the name does not contain a '.' it will reserve that subdomain on ngrok.io.

I find above extremely confusing. Let's say I'd like to use dev.example.com as my ngrok domain. Which of the following should I use?

resource "ngrok_reserved_domain" "foo" {
  name = "dev.example.com"
}

# OR...

resource "ngrok_reserved_domain" "bar" {
  domain = "dev.example.com"
}

# OR... perhaps both arguments should be provided?

What's interesting is that according to API reference /reserved_domains endpoint does not support name parameter at all, but... ReservedDomainCreate struct (which becomes POST /reserved_domains payload) does include it.

Let's break it down how it behaves in real life.

Argument API response Notes
name = "01b747d1" gist 01b747d1.ngrok.app created successfully; mind the name string must be unique
name = "a2158993.ngrok.pro" gist Here's how to create a built-in subdomain different than default <whatever>.ngrok.app
name = "0f31e481.example.com" gist Domain successfully created
domain = "45c97ae6" gist That's clearly an invalid domain. The API returns 400 with ERR_NGROK_448
domain = "334b8d11.example.com" gist Domain successfully created
name = "foo" + domain = "bar.example.com" gist ERR_NGROK_449 response with "You may only specify one of 'name' or 'domain'." message

Expected state/things to consider:

  • It should be a no brainer which argument to use in order to register a custom domain - at the time of writing both name = "foo.example.com" and domain = "foo.example.com" result in the exact same state. To avoid unnecessary confusion ngrok_reserved_domain should expose only one argument that controls this aspect (probably name as it supports both scenarios - FQDN + <placeholder>.ngrok.(app|dev|pro|pizza|<etc>))
  • API reference should list all the params supported by POST /reserved_domain endpoint
  • Resource docs do not mention how to deal with <foo>.ngrok.(dev|pro|pizza|io) and <bar>.ngrok-free.(app|dev) cases
  • Yet another doc glitch - by default ngrok.app gets used, not ngrok.io (I'm on the OG Pro plan, free account may behave differently)

Aside from above I stumbled upon a few other minor issues while experimenting with ngrok_reserved_domain resource:

2a. certificate_management_status discrepancies in HTTP response (click to expand)

2a certificate_management_status discrepancies in HTTP response

That's probably irrelevant and depends on some async background process, but it caught my attention anyway. Response body of successful POST /reserved_domains request sometimes includes "certificate_management_status": null element.

{
  "id": "<redacted>",
  "uri": "https://api.ngrok.com/reserved_domains/<redacted>",
  "created_at": "2024-09-27T20:14:54Z",
  "domain": "<redacted>",
  "region": "",
  "cname_target": "<redacted>",
  "http_endpoint_configuration": null,
  "https_endpoint_configuration": null,
  "certificate": null,
  "certificate_management_policy": {
    "authority": "letsencrypt",
    "private_key_type": "ecdsa"
  },
  "certificate_management_status": {
    "renews_at": null,
    "provisioning_job": {
      "error_code": null,
      "msg": "Managed certificate provisioning in progress.",
      "started_at": "2024-09-27T20:14:54Z",
      "retries_at": null
    }
  },
  "acme_challenge_cname_target": null,
  "error_redirect_url": null
}

vs

{
  "id": "<redacted>",
  "uri": "https://api.ngrok.com/reserved_domains/<redacted>",
  "created_at": "2024-09-27T20:16:45Z",
  "domain": "<redacted>",
  "region": "",
  "cname_target": "<redacted>",
  "http_endpoint_configuration": null,
  "https_endpoint_configuration": null,
  "certificate": null,
  "certificate_management_policy": {
    "authority": "letsencrypt",
    "private_key_type": "ecdsa"
  },
  "certificate_management_status": null,
  "acme_challenge_cname_target": null,
  "error_redirect_url": null
}
2b. Undocumented ERR_NGROK_448 and ERR_NGROK_449 codes (click to expand)

2b. Undocumented ERR_NGROK_448 and ERR_NGROK_449 codes

Neither ERR_NGROK_448 nor ERR_NGROK_449 is documented here.

2c. Broken 404s for https://ngrok.com/docs/* URLs (click to expand)

2c. Broken 404s for https://ngrok.com/docs/* URLs

I thought that perhaps ERR_NGROK_448 wasn't listed for some reason and visited https://ngrok.com/docs/errors/err_ngrok_448/. Here's how it looks for me (some .js and .css requests end with 404):

ngrok com_docs_errors_err_ngrok_448_

The problem affects any https://ngrok.com/docs/* URL that does not exist, i.e. https://ngrok.com/docs/foo or https://ngrok.com/docs/bar

3. certificate_management_policy defaults

There's a glitch in the docs - it says that the certificate_management_policy block supports the following values:

certificate_management_policy {
  authority = "letsencrypt" # default
  private_key_type = "rsa" # default, can be changed to "ecdsa"
}

The thing is all my attempts (without explicitly defined certificate_management_policy block) ended with the following response body:

{
  "id": "...",
  "...": "...",
  "certificate_management_policy": {
    "authority": "letsencrypt",
    "private_key_type": "ecdsa"
  }
}

Could it be that rsa is no longer the default?

@euank
Copy link

euank commented Oct 8, 2024

Apologies for the slow response, I just wanted to let you know that we really appreciate the detailed writeup and thoughts here, thank you!

No promise on timelines or anything, but these all seem like things to fix, and we'll comment back here when we get em sorted.
Just wanted to make sure you got some sort of response since we really do appreciate the report!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants