hashicorp / vault

A tool for secrets management, encryption as a service, and privileged access management
https://www.vaultproject.io/
Other
31.12k stars 4.21k forks source link

Vault 1.8.1 acl templating breaks with JWT #12336

Closed evilmog closed 3 years ago

evilmog commented 3 years ago

Describe the bug A clear and concise description of what the bug is.

To Reproduce Steps to reproduce the behavior: Configure vault with JWT and then template under PKI or SSH Certificates the following allowed domain template:

{{identity.entity.aliases.auth_jwt_31588259.metadata.name}}

This matches the documentation https://learn.hashicorp.com/tutorials/vault/policy-templating

the vault ssh config is something similar to one configured by this python script:

import hvac
import os
import json
import requests
import time
from getpass import getpass

vault_base = "redacted"

shares = 5
threshold = 3

# Access Levels to Create
access_levels = [ "level1", "level2", "level3"]
# this is the prefix for groups to be created
group_prefix = "redacted_"
ssh_role_prefix = "ssh-jwt-"
access_policies = {}

#JWT Config Info
bound_issuer="redacted"
jwks_url="redacted"
oidc_response_mode = "form_post"
user_claim = "emailAddress"
groups_claim = "blueGroups"
preferred_username = "uid"
emailAddress = "name"
allowed_redirect_uris = []
claim_mappings = { }
claim_mappings["emailAddress"] ="name"
claim_mappings["preferred_username"] ="uid"

# OIDC Client ID from onboarding
bound_audiences = ["redacted"]

# SSH Config Into - TTL
ssh_ttl = 3600
ssh_token_period = 10

# PKI Config
# set the config options
# Root Tuning
root_pki_tune_options_dict = {}
root_pki_tune_options_dict['max_lease_ttl'] = "87601h"
root_pki_tune_options_dict['default_lease_ttl'] = "87600h"
# Intermediate Tuning
intermediate_pki_tune_options_dict = {}
intermediate_pki_tune_options_dict['max_lease_ttl'] = "43849h"
intermediate_pki_tune_options_dict['default_lease_ttl'] = "43848h"

# Root CA Config
pki_root_name = "pki_root"
root_ca_params = {}
root_ca_params['ttl'] = "87600h"
root_ca_params['max_lease_ttl'] = "87601h"
root_ca_params['cn'] = "Redacted"
root_ca_params['key_type'] = "rsa"
root_ca_params['key_bits'] = "4096"
root_ca_params['max_path_length'] = 3
root_ca_params['type'] = "internal"
root_ca_params['OU'] = "Redacted" 
root_ca_params['Organization'] = "Redacted"
root_ca_params['Country'] = "US" 
root_ca_params['Locality'] = "Redacted"
root_ca_params['province'] = "Redacted"

# Intermediate CA Config

int_ca_params = {}
pki_int_name = "tester_pki"
int_ca_params['ttl'] = "43848h"
int_ca_params['max_lease_ttl'] = "87601h"
int_ca_params['cn'] = "redacted"
int_ca_params['key_type'] = "rsa"
int_ca_params['key_bits'] = "4096"
int_ca_params['max_path_length'] = 3
int_ca_params['type'] = "internal"
int_ca_params['OU'] = "redacted" 
int_ca_params['Organization'] = "redacted"
int_ca_params['Country'] = "US" 
int_ca_params['Locality'] = "redacted"
int_ca_params['province'] = "GA"

client = hvac.Client(vault_base)

if client.sys.is_initialized() is False:
    # initialize
    result = client.sys.initialize(shares, threshold)

    # get token from initialized vault
    root_token = result['root_token']
    # get recovery keys
    keys = result['keys']

    # unseal with individual keys
    client.sys.submit_unseal_key(key=keys[0])
    client.sys.submit_unseal_key(key=keys[1])
    client.sys.submit_unseal_key(key=keys[2])

    if not os.path.exists(os.path.expanduser('~/.vault')):
        os.makedirs(os.path.expanduser('~/.vault'))

    with open(os.path.expanduser("~/.vault/test_vault.unseal"),'w') as out:
        for key in keys:
            out.write(key + '\n')
    out.close()

    with open(os.path.expanduser("~/.vault/test_vault.root"), 'w') as out:
        out.write(root_token + '\n')
    out.close()

    # sleep and wait for initialization
    time.sleep(10)
else:
    print("Sorry this vault is already initialized")
    exit()

# create vault header token
vault_headers = {'X-Vault-Token': root_token}

# login with new root token
client = hvac.Client(url=vault_base, token=root_token)

# Enable JWT Auth
client.sys.enable_auth_method(method_type='jwt')

# Configure JWT Auth
client.auth.jwt.configure(
    bound_issuer=bound_issuer,
    oidc_response_mode=oidc_response_mode,
    oidc_response_types="id_token",
    jwks_url=jwks_url
)

# list the authentication methods
auth_methods = client.sys.list_auth_methods()

# Get the JWT Token Accessor for later use
jwt_token_accessor = auth_methods['data']['jwt/']['accessor']

# create ssh-client-signer
client.sys.enable_secrets_engine(
    backend_type='ssh',
    path='ssh-client-signer',
    default_lease_ttl=2764800,
    max_lease_ttl=2764800
)

ssh_ca_pub_payload_dict = {}

if os.path.exists(os.path.expanduser('~/.vault/ssh_ca.json')):
    ssh_ca_pub_payload_dict['generate_signing_key'] = "false"
    with open(os.path.expanduser('~/.vault/ssh_ca.json')) as json_file:
        ssh_ca_text = json_file.read()
        ssh_ca_json = json.loads(ssh_ca_text)
    ssh_ca_pub_payload_dict['private_key'] = ssh_ca_json['private_key']
    ssh_ca_pub_payload_dict['public_key'] = ssh_ca_json['public_key']
    json_file.close()
else:
    ssh_ca_pub_payload_dict['generate_signing_key'] = "true"

requests.post(vault_base + "/v1/ssh-client-signer/config/ca", headers=vault_headers, data = json.dumps(ssh_ca_pub_payload_dict))
ssh_ca_pub = requests.get(vault_base + "/v1/ssh-client-signer/public_key")
with open(os.path.expanduser("~/.vault/ssh_ca.pub"), 'w') as out:
    out.write(ssh_ca_pub.text)
out.close()

# PKI Root

# create PKI Mount Point - Root
pki_data = {}
pki_data['type'] = "pki"
root_create = requests.post(vault_base + "/v1/sys/mounts/" + pki_root_name, headers=vault_headers, data=json.dumps(pki_data))
root_tune = requests.post(vault_base + "/v1/sys/mounts/" + pki_root_name + "/tune", headers=vault_headers, data=json.dumps(root_pki_tune_options_dict))
# create PKI Mount Point - Tester PKI
tester_create = requests.post(vault_base + "/v1/sys/mounts/" + pki_int_name, headers=vault_headers, data=json.dumps(pki_data))
tester_tune = requests.post(vault_base + "/v1/sys/mounts/" + pki_int_name +"/tune", headers=vault_headers, data=json.dumps(intermediate_pki_tune_options_dict))

# create PKI directory
if not os.path.exists(os.path.expanduser('~/.vault/pki')):
    os.makedirs(os.path.expanduser('~/.vault/pki'))
if not os.path.exists(os.path.expanduser('~/.vault/pki/root')):
    os.makedirs(os.path.expanduser('~/.vault/pki/root'))
if not os.path.exists(os.path.expanduser('~/.vault/pki/tester')):
    os.makedirs(os.path.expanduser('~/.vault/pki/tester'))

# Generate the Root CA
root_generate = requests.post(vault_base + "/v1/" + pki_root_name + "/root/generate/internal", headers=vault_headers, data=json.dumps(root_ca_params))
root_ca_pub = json.loads(root_generate.text)['data']['certificate']
# write ca to file
with open(os.path.expanduser("~/.vault/pki/root/ca.pub"), 'w') as out:
    out.write(root_ca_pub)
out.close()

# Generate the Intermediate CA
int_generate = requests.post(vault_base + "/v1/" + pki_int_name + "/intermediate/generate/internal", headers=vault_headers, data=json.dumps(int_ca_params))
int_ca_csr = json.loads(int_generate.text)['data']['csr']
# write csr to file
with open(os.path.expanduser("~/.vault/pki/tester/ca.csr"), "w") as out:
    out.write(int_ca_csr)
out.close()

# generate csr dict
csr_dict = {}
csr_dict['csr'] = int_ca_csr
csr_dict['ttl'] = int_ca_params['ttl']
# sign intermediate
root_sign = requests.post(vault_base + "/v1/" + pki_root_name + "/root/sign-intermediate", headers=vault_headers,data=json.dumps(csr_dict))
signed_intermediate=json.loads(root_sign.text)['data']['certificate'] + "\n" + json.loads(root_sign.text)['data']['issuing_ca']
# write signed to file
with open(os.path.expanduser("~/.vault/pki/tester/ca.chain"), "w") as out:
    out.write(int_ca_csr)
out.close()
with open(os.path.expanduser("~/.vault/pki/tester/ca.pub"), "w") as out:
    out.write(json.loads(root_sign.text)['data']['certificate'])
out.close()

# set signed cert
signed_dict = {}
signed_dict['format']= 'pem_bundle'
signed_dict['certificate'] = signed_intermediate

post_intermediate = requests.post(vault_base + "/v1/" + pki_int_name + "/intermediate/set-signed", headers=vault_headers,data=json.dumps(signed_dict))

# Create Default Policy
# start policy block
jwt_default_policy_block = '''
path "sys/mounts" {
  capabilities = ["list", "read"]
}

path "secrets/data/vpn/'''+ pki_int_name +'''" {
  capabilities = ["read", "list"]
}

path "''' + pki_int_name +'''/issue/*" {
    capabilities = ["create", "update"]
}

path "''' + pki_int_name +'''/certs" {
    capabilities = ["list"]
}

path "''' + pki_int_name +'''/revoke" {
    capabilities = ["create", "update"]
}

path "''' + pki_int_name +'''/tidy" {
    capabilities = ["create", "update"]
}

path "''' + pki_int_name +'''/cert/ca" {
    capabilities = ["read"]
}

path "''' + pki_root_name + '''/cert/ca" {
    capabilities = ["read"]
}

path "ssh-client-signer/*" {
   capabilities = ["list", "read"]
}

path "ssh-client-signer/config/ca" {
  capabilities = ["read", "list"]
}

# DEFAULT Policy Entries
# Allow tokens to look up their own properties
path "auth/token/lookup-self" {
    capabilities = ["read"]
}

# Allow tokens to revoke themselves
path "auth/token/revoke-self" {
    capabilities = ["update"]
}

# Allow a token to look up its own capabilities on a path
path "sys/capabilities-self" {
    capabilities = ["update"]
}

# Allow a token to look up its own entity by id or name
path "identity/entity/id/{{identity.entity.id}}" {
  capabilities = ["read"]
}
path "identity/entity/name/{{identity.entity.name}}" {
  capabilities = ["read"]
}

# Allow a token to look up its resultant ACL from all policies. This is useful
# for UIs. It is an internal path because the format may change at any time
# based on how the internal ACL features and capabilities change.
path "sys/internal/ui/resultant-acl" {
    capabilities = ["read"]
}

# Allow a token to renew a lease via lease_id in the request body; old path for
# old clients, new path for newer
path "sys/renew" {
    capabilities = ["update"]
}
path "sys/leases/renew" {
    capabilities = ["update"]
}

# Allow looking up lease properties. This requires knowing the lease ID ahead
# of time and does not divulge any sensitive information.
path "sys/leases/lookup" {
    capabilities = ["update"]
}

# Allow a token to wrap arbitrary values in a response-wrapping token
path "sys/wrapping/wrap" {
    capabilities = ["update"]
}

# Allow a token to look up the creation time and TTL of a given
# response-wrapping token
path "sys/wrapping/lookup" {
    capabilities = ["update"]
}

# Allow a token to unwrap a response-wrapping token. This is a convenience to
# avoid client token swapping since this is also part of the response wrapping
# policy.
path "sys/wrapping/unwrap" {
    capabilities = ["update"]
}

# Allow general purpose tools
path "sys/tools/hash" {
    capabilities = ["update"]
}
path "sys/tools/hash/*" {
    capabilities = ["update"]
}

# Allow checking the status of a Control Group request if the user has the
# accessor
path "sys/control-group/request" {
    capabilities = ["update"]
}
'''
# end policy block

# create default policy
client.sys.create_or_update_policy(
    name='jwt-default-policy',
    policy=jwt_default_policy_block,
)

# Create Customized Policy
#path "ssh-client-signer/sign/ssh-jwt-advsim" {
#  capabilities = ["create", "update", "list", "read"]
#}

# loop over access policy levels to create names policies
for level in access_levels:
    # construct policy name
    access_policy_name = 'ssh-ca-' + level + '-policy'
    assigned_policies = ["jwt-default-policy"]
    external_group_name = group_prefix + level
    # programatic policy development
    access_policies[level] = "path \"ssh-client-signer/sign/ssh-jwt-" + level +"\" {\n"
    access_policies[level] += "  capabilities = [\"create\", \"update\", \"list\", \"read\"]\n}"

    # create the policy itself
    client.sys.create_or_update_policy(
        name=access_policy_name,
        policy=access_policies[level],
    )
    # JWT
    client.auth.jwt.create_role(
        name=level,
        role_type='jwt',
        allowed_redirect_uris = allowed_redirect_uris,
        user_claim = user_claim,
        bound_audiences = bound_audiences,
        groups_claim = groups_claim,
        token_period = ssh_token_period,
        token_ttl = ssh_ttl,
        claim_mappings = claim_mappings,
        token_policies = assigned_policies
    )

    # create external groups
    client.secrets.identity.create_or_update_group_by_name(
        name=level,
        group_type = "external",
        policies = access_policy_name,
    )
    # get group id of external group
    read_response = client.secrets.identity.read_group_by_name(name=level)
    group_id = read_response['data']['id']

    # create internal group
    client.secrets.identity.create_or_update_group_by_name(
        name=external_group_name,
        group_type = "internal",
        policies = access_policy_name,
        member_group_ids = group_id,
    )

    ssh_role_name = ssh_role_prefix + level
    default_username = "zone-" + level
    ssh_role_dict = {}
    ssh_role_dict['name'] = ssh_role_name
    ssh_role_dict['allowed_extensions'] = "permit-pty,permit-port-forwarding,permit-agent-forwarding,permit-X11-forwarding"
    ssh_role_dict['default_extensions'] = {"permit-X11-forwarding": "", "permit-agent-forwarding": "", "permit-port-forwarding": "", "permit-pty": ""}
    ssh_role_dict['allow_bare_domains'] = "false"
    ssh_role_dict['allow_host_certificates'] = "false"
    ssh_role_dict['allow_subdomains'] = "false"
    ssh_role_dict['allow_user_certificates'] = "true"
    ssh_role_dict['allow_user_key_ids'] = "false"
    ssh_role_dict['allowed_critical_options'] = ""
    ssh_role_dict['allowed_domains'] = "*"
    ssh_role_dict['allowed_users_template'] = "true"
    ssh_role_dict['key_id_format'] = "{{token_display_name}}"
    ssh_role_dict['key_type'] = "ca"
    ssh_role_dict['max_ttl'] = "1209600"
    ssh_role_dict['ttl'] = "86400"
    ssh_role_dict['default_username'] = default_username
    allowed_users = ('zone-' + level + ',' + level + ',{{identity.entity.aliases.auth_' + jwt_token_accessor + '.metadata.name}}')
    ssh_role_dict['allowed_users'] = allowed_users
    requests.post(vault_base + "/v1/ssh-client-signer/roles/" + ssh_role_name , headers=vault_headers, data = json.dumps(ssh_role_dict))

ta_text=""
# check to see if ta.key exists and if so provision it to secretstore
if os.path.exists(os.path.expanduser('~/.vault/ta.key')):
    with open(os.path.expanduser('~/.vault/ta.key')) as ta_file:
        ta_text = ta_file.read()
    ta_file.close()
    options_dict = {}
    options_dict['version'] = 2
    client.sys.enable_secrets_engine(backend_type='kv',options=options_dict,path='secrets',)
    ta_dict = {}
    ta_dict['options'] = {}
    ta_dict['data'] = {}
    ta_dict['options']['cas'] = 0
    ta_dict['data']['ta_key'] = ta_text
    ta_json=json.dumps(ta_dict)
    requests.post(vault_base + "/v1/secrets/data/vpn/" + pki_int_name, headers=vault_headers, data=ta_json)

role_tester_vpn ={}
role_tester_vpn['allow_any_name'] = "false"
role_tester_vpn['allow_bare_domains'] = "false"
role_tester_vpn['allow_glob_domains'] = "false"
role_tester_vpn['allow_ip_sans'] = "true"
role_tester_vpn['allow_localhost'] = "false"
role_tester_vpn['allow_subdomains'] = "false"
role_tester_vpn['allowed_domains'] = ["{{" + jwt_token_accessor + "}}"]
role_tester_vpn['allowed_domains_template'] = "true"
role_tester_vpn['allow_token_displayname'] = "false"
role_tester_vpn['allow_other_sans'] = []
role_tester_vpn['allow_serial_numbers'] = []
role_tester_vpn['basic_constraints_valid_for_non_ca'] = "false"
role_tester_vpn['client_flag'] = "true"
role_tester_vpn['code_signing_flag'] = "false"
role_tester_vpn['email_protection_flag'] = "false"
role_tester_vpn['enforce_hostnames'] = "true"
role_tester_vpn['key_bits'] = "4096"
role_tester_vpn['key_type'] = "rsa"
role_tester_vpn['key_usage'] = ["DigitalSignature","KeyAgreement","KeyEncipherment"]
role_tester_vpn['max_ttl'] = "1209600"
role_tester_vpn['ttl'] = "1209600"
role_tester_vpn['no_store'] = "false"
role_tester_vpn['require_cn'] = "true"
role_tester_vpn['use_csr_common_name'] = "true"
role_tester_vpn['use_csr_sans'] = "true"
requests.post(vault_base + "/v1/" + pki_int_name + "/roles/" + "testervpn", headers=vault_headers, data=json.dumps(role_tester_vpn))

Expected behavior A certificate issued from user@domain.com instead an error is given

{'errors': ['common name redacted@redacted.com not allowed by this role']}

the only fix was to downgrade to 1.7.3

Environment:

Vault server configuration file(s):

ui = true

listener "tcp" {
  address       = "0.0.0.0:8200"
  cluster_address = "0.0.0.0:8201"
  tls_cert_file = "/etc/certs/vault.crt"
  tls_key_file  = "/etc/certs/vault.key"
  tls_disable_client_certs = true
}

storage "raft" {
  path = "/etc/raft"
  node_id = "xft_atl_vault_1"
}

seal "transit" {
  address            = "[redacted]"
  token              = "[redacted]"
  disable_renewal    = "false"
  key_name           = "atl-vault-autounseal"
  mount_path         = "transit/"
}

cluster_addr = "[redacted]"
api_addr = "[redacted]"

Additional context Add any other context about the problem here.

Worked perfect in 1.7.3, doesn't work in 1.8.1

pmmukh commented 3 years ago

Hi @evilmog! Thanks for submitting this issue! Could you possibly give a smaller set of steps required to reproduce this issue ? The python script looks fairly large and complex, wondering if a few cli/api commands chained together could be used to recreate this issue. If not and everything done in the python script is necessary to recreate the bug, could you write down some details as to what is going on in the script and how it is triggering the bug exactly ?

evilmog commented 3 years ago

All the script does is configure the test vault to be exactly the same.

Upgrading our binary from 1.7.3 to 1.8.1 breaks user alias templating in the ssh certificate allowed principals.

So to setup:

1: configure vault JWT authentication against your sso provider, make sure it configures groups.

2: configure vault for ssh certificate issuance

3: configure a TLS cert secret

4: create an internal group and policy for the ssh cert

5: create an external group and policy for ssh cert

6: configure an ssh certificate role with specific allowed principals

7: configure TLS issuance for openvpn, plus policy

8: authenticate with the JWT via your oidc sso, give the JWT to vault for a token

Try to request a certificate in the ssh and tls certificate endpoints

Authentication fails with groups assigned and created via the JWT in 1.8.1 while it works in 1.8.3

That script will provision you a vault with JWT auth, you just need to replace some of the redacted elements like bound issuer.

On Tue., Sep. 7, 2021, 5:05 p.m. Pratyoy Mukhopadhyay, < @.***> wrote:

Hi @evilmog https://github.com/evilmog! Thanks for submitting this issue! Could you possibly give a smaller set of steps required to reproduce this issue ? The python script looks fairly large and complex, wondering if a few cli/api commands chained together could be used to recreate this issue. If not and everything done in the python script is necessary to recreate the bug, could you write down some details as to what is going on in the script and how it is triggering the bug exactly ?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/hashicorp/vault/issues/12336#issuecomment-914681764, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAZBQUUWLSW5ZSFCD27UQADUA2LCRANCNFSM5CKBZDVA . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

pmmukh commented 3 years ago

ah perfect, having that broken down like that is super helpful for me, thank you!! Follow up questions on the script:

evilmog commented 3 years ago

You may need to make your own JWT or setup keycloak as an OIDC provider to issue you jwt's with groups

This should work on python 3.8 ish, it's super hacky and I should have down terraform.

You will want to use a token that's not a root token issued from the JWT sign on, you will want a group, we call our groups blueGroups but whatever in the OIDC will populate the JWT SSO groups should work.

That's where I suspect the problem is, either in the templating of the allowed usernames of the key principal or something to do with the JWT but whenever we request the cert with the new token on 1.8.3 we get rejected vs in 1.7.3 we get accepted.

I can line up a WebEx to show you sometime if that would help.

I'll see if I can get an exact method to replicate tomorrow.

On Tue., Sep. 7, 2021, 5:40 p.m. Pratyoy Mukhopadhyay, < @.***> wrote:

ah perfect, having that broken down like that is super helpful for me, thank you!! Follow up questions on the script:

  • What is the python version needed to run it?
  • Beyond updating the redacted variables and starting up a dev mode vault server on 127.0.0.1:8200, should I need to do anything else to be able to reproduce this ?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/hashicorp/vault/issues/12336#issuecomment-914696948, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAZBQUSDBFGBJ67CKK5JBATUA2PFLANCNFSM5CKBZDVA . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

pmmukh commented 3 years ago

thanks for the additional details! I will try to repro this shortly keeping the advice in mind, let you know here if I run into any issues!

evilmog commented 3 years ago

I am wondering if the required options changed in the JWT plugin, I noticed this when I tried to change my number of token uses on the JWT endpoint, it used to default to JWT in the JWT/OIDC options, I wonder if its not set it if breaks.

I'm going to see if I can reload a vault from scratch and see if its fixed that way rather than doing a straight upgrade.

On Thu, Sep 9, 2021 at 2:53 PM Pratyoy Mukhopadhyay < @.***> wrote:

thanks for the additional details! I will try to repro this shortly keeping the advice in mind, let you know here if I run into any issues!

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/hashicorp/vault/issues/12336#issuecomment-916428428, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAZBQUQRMDMB5Y5FADY55ZLUBENF3ANCNFSM5CKBZDVA . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

pmmukh commented 3 years ago

Sorry, I haven't gotten around to doing a repro of this yet, but just wanted to check, did the things you were planning to try in your last message end up resolving this or shedding any more light on it ?

evilmog commented 3 years ago

I haven't managed to get things going, I'll have to try a full from scratch rebuild so see if I can get this to reproduce

On Wed, Sep 15, 2021 at 5:13 PM Pratyoy Mukhopadhyay < @.***> wrote:

Sorry, I haven't gotten around to doing a repro of this yet, but just wanted to check, did the things you were planning to try in your last message end up resolving this or shedding any more light on it ?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/hashicorp/vault/issues/12336#issuecomment-920453447, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAZBQUXEJU6UNDCPF4RF7TLUCESCXANCNFSM5CKBZDVA . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

DaspawnW commented 3 years ago

Hi guys,

in my opinion its not even required to go via the JWT or OIDC option. We discovered the same bug and it seems an allowed_common_name with the mail address as value is already enough to break it.

Working scenario on HC Vault 1.7.3 / 1.7.4:

docker run --rm -p 8200:8200 --cap-add=IPC_LOCK -e 'VAULT_DEV_ROOT_TOKEN_ID=myroot' -d vault:1.7.3

export VAULT_ADDR='http://0.0.0.0:8200'
export VAULT_TOKEN="myroot"

vault secrets enable pki
vault secrets tune -max-lease-ttl=8760h pki
vault write pki/root/generate/internal common_name=test.com ttl=8760h

vault write pki/roles/example allowed_domains=test@test.com allow_bare_domains=true
vault write pki/issue/example common_name=test@test.com

Not working scenario on HC Vault 1.8.2:

docker run --rm -p 8200:8200 --cap-add=IPC_LOCK -e 'VAULT_DEV_ROOT_TOKEN_ID=myroot' vault:1.8.2

export VAULT_ADDR='http://0.0.0.0:8200'
export VAULT_TOKEN="myroot"

vault secrets enable pki
vault secrets tune -max-lease-ttl=8760h pki
vault write pki/root/generate/internal common_name=test.com ttl=8760h

vault write pki/roles/example allowed_domains=test@test.com allow_bare_domains=true
vault write pki/issue/example common_name=test@test.com

Error writing data to pki/issue/example: Error making API request.

URL: PUT http://0.0.0.0:8200/v1/pki/issue/example
Code: 400. Errors:

* common name test@test.com not allowed by this role
pmmukh commented 3 years ago

Thanks so much for the repro steps @DaspawnW , can confirm that I just tried those, and was able to repro the problem on 1.8x and see that it doesn't exist in 1.7x.

stevendpclark commented 3 years ago

Closing issue as it was fixed within #12716

The fix will be available within the next major release and will be backported to Vault 1.8