hashicorp / terraform-provider-vault

Terraform Vault provider
https://www.terraform.io/docs/providers/vault/
Mozilla Public License 2.0
451 stars 535 forks source link

[Bug]: creating OIDC auth method & default role fails with 404 #2185

Closed onefourfive closed 4 months ago

onefourfive commented 4 months ago

Terraform Core Version

1.0.11

Terraform Vault Provider Version

3.25.0

Vault Server Version

1.12.2

Affected Resource(s)

Expected Behavior

  1. Vault oidc auth backend enabled.
  2. Vault oidc/role/default-role role provisioned.
  3. Vault oidc auth backend configured with oidc/role/default-role set as default role.

Actual Behavior

  1. Vault oidc/role/default-role role provisioning attempted first. This attempts a write to a path that does not yet exist and results in a 404.
  2. Provisioning fails.

Relevant Error/Panic Output Snippet

Error: error writing JWT auth backend role "auth/oidc/role/oidc-default": Error making API request.

URL: PUT http://localhost:8200/v1/auth/oidc/role/oidc-default
Code: 404. Errors:

* no handler for route "auth/oidc/role/oidc-default". route entry not found.

  with vault_jwt_auth_backend_role.oidc-default,
  on vault-config.tf line 32, in resource "vault_jwt_auth_backend_role" "oidc-default":
  32: resource "vault_jwt_auth_backend_role" "oidc-default" {

Terraform Configuration Files

resource "vault_jwt_auth_backend" "oidc" {
  path = "oidc"
  type = "oidc"
  oidc_client_id = data.vault_kv_secret_v2.vault_oidc.data.client-id
  oidc_client_secret = data.vault_kv_secret_v2.vault_oidc.data.client-secret
  oidc_discovery_url = "https://mydomain.myauth.com"
 # default_role = vault_jwt_auth_backend_role.oidc-default.role_name
}

resource "vault_jwt_auth_backend_role" "oidc-default" {
  backend = "oidc"
  role_name = "oidc-default"
  user_claim = "sub"
  groups_claim = "groups"
  oidc_scopes = ["groups"]
  allowed_redirect_uris = [
    "https://${local.vault_fqdn}/ui/vault/auth/oidc/oidc/callback",
    "http://${local.vault_fqdn}:8250/oidc/callback",
    "http://localhost:8250/oidc/callback"
    ]
  bound_audiences = [ "${data.vault_kv_secret_v2.vault_oidc.data.client-id}" ]
  token_ttl = 14400
  token_max_ttl = 28800
}

Steps to Reproduce

  1. Vault config: no oidc auth enabled
  2. Run terraform.

Debug Output

No response

Panic Output

No response

Important Factoids

No response

References

No response

Would you like to implement a fix?

None

fairclothjm commented 4 months ago

@onefourfive Hello, I am sorry you are having trouble!

I believe the issue is that TF does not understand the dependency relationship between your two resources. In particular, vault_jwt_auth_backend_role needs make a reference to the vault_jwt_auth_backend's named value because the backend must be mounted before we can create the role.

Could you try updating your config. Example:

resource "vault_jwt_auth_backend_role" "oidc-default" {
-  backend = "oidc"
+  backend = vault_jwt_auth_backend.oidc.path
onefourfive commented 4 months ago

@fairclothjm thank you for your help!

Unfortunately that causes a cycle error when implemented as such:

resource "vault_jwt_auth_backend" "oidc" {
  path = "oidc"
  type = "oidc"
  oidc_client_id = data.vault_kv_secret_v2.vault_oidc.data.client-id
  oidc_client_secret = data.vault_kv_secret_v2.vault_oidc.data.client-secret
  oidc_discovery_url = "https://mydomain.myauth.com"
+  default_role = vault_jwt_auth_backend_role.oidc-default.role_name
}

resource "vault_jwt_auth_backend_role" "oidc-default" {
+  backend = vault_jwt_auth_backend.oidc.path
  role_name = "oidc-default"
  user_claim = "sub"
  groups_claim = "groups"
  oidc_scopes = ["groups"]
  allowed_redirect_uris = [
    "https://${local.vault_fqdn}/ui/vault/auth/oidc/oidc/callback",
    "http://${local.vault_fqdn}:8250/oidc/callback",
    "http://localhost:8250/oidc/callback"
    ]
  bound_audiences = [ "${data.vault_kv_secret_v2.vault_oidc.data.client-id}" ]
  token_ttl = 14400
  token_max_ttl = 28800
}
Error: Cycle: vault_jwt_auth_backend_role.oidc-default, vault_jwt_auth_backend.oidc
fairclothjm commented 4 months ago

@onefourfive What if we don't reverence the named value in the vault_jwt_auth_backend? But instead just use the hardcoded string? Or better yet make it a variable that both resource use?

resource "vault_jwt_auth_backend" "oidc" {
   // ...
+  default_role = "oidc-default"
-  default_role = vault_jwt_auth_backend_role.oidc-default.role_name
}

Or use a var:

resource "vault_jwt_auth_backend" "oidc" {
   // ...
+  default_role = var.oidc_default
-  default_role = vault_jwt_auth_backend_role.oidc-default.role_name
}

resource "vault_jwt_auth_backend_role" "oidc-default" {
    backend = vault_jwt_auth_backend.oidc.path
+  role_name = var.oidc_default
-  role_name = "oidc-default"
  // ...
}
onefourfive commented 4 months ago

That gets me past the check, but when I apply I get the same error as earlier:

locals {
...
+  oidc_default_role_name = "oidc-default"
}

resource "vault_jwt_auth_backend" "oidc" {
...
+  default_role = local.oidc_default_role_name
}

resource "vault_jwt_auth_backend_role" "oidc-default" {
...
+  role_name = local.oidc_default_role_name
}
Error: error writing JWT auth backend role "auth/oidc/role/oidc-default": Error making API request.

URL: PUT http://localhost:8200/v1/auth/oidc/role/oidc-default
Code: 404. Errors:

* no handler for route "auth/oidc/role/oidc-default". route entry not found.

  with vault_jwt_auth_backend_role.oidc-default,
  on vault-config.tf line 37, in resource "vault_jwt_auth_backend_role" "oidc-default":
  37: resource "vault_jwt_auth_backend_role" "oidc-default" {
fairclothjm commented 4 months ago

@onefourfive With the above setup, is vault_jwt_auth_backend_role still referencing the backend resource's named value?

resource "vault_jwt_auth_backend_role" "oidc-default" {
   backend = vault_jwt_auth_backend.oidc.path
onefourfive commented 4 months ago

No actually it's set explicitly:

resource "vault_jwt_auth_backend_role" "oidc-default" {
  backend = "oidc"
  role_name = local.oidc_default_role_name
fairclothjm commented 4 months ago

ok, I think it should not be set explicitly. I think it should reference the named value otherwise TF can't build the appropriate dependency graph.

fairclothjm commented 4 months ago

Putting it all together, I think you need something like:

locals {
   // ...
   oidc_default_role_name = "oidc-default"
}

resource "vault_jwt_auth_backend" "oidc" {
   default_role = local.oidc_default_role_name
   // ...
}

resource "vault_jwt_auth_backend_role" "oidc-default" {
   backend = vault_jwt_auth_backend.oidc.path
   role_name = local.oidc_default_role_name
   // ...
}
onefourfive commented 4 months ago

I disabled the OIDC auth method and deployed that, and it seemed to work! Thank you.

To summarize, I deployed those resources--

Is this something that should be explicitly documented? Maybe a note for backend in vault_jwt_auth_backend_role docs along the lines of

The unique name of the auth backend to configure. Defaults to `jwt`.
+If this role is provisioned as a `default_role`, refer to that backend by reference.
fairclothjm commented 4 months ago

Glad it got resolved!

Is this something that should be explicitly documented?

It isn't only this scenario where you could run into this issue. It is always good practice to refer to a dependent resource by reference. Terraform cannot create a vault_jwt_auth_backend_role resource without the vault_jwt_auth_backend resource first being provisioned.

TLDR: always set backend or path as a reference to the backend mount resource