Closed TJM closed 1 year ago
Username Templating: https://developer.hashicorp.com/vault/docs/concepts/username-templating
@TJM Can you provide an example of how you envision this is used? Reason I ask is that username isn't an arbitrary thing. It must correspond to a valid user in Artifactory. May be I am missing something (since I'm still relatively green on Vault)?
Actually I have that above.. (rolename-UNIXTIMESTAMP). The username does not need to correspond to a valid user in artifactory, they are created dynamically. The group name might need to exist? I need to determine what the "specs" for username are (max length of a dynamic user account, allowed character set, etc).
Perhaps I'm conflating Vault username with Artifactory token's username
. Sounds like they are not the same thing.
We would be using applied-permissions/group:GROUPNAME
, but #27 would be using the applied-permissions/USERNAME
(cool)
This is fine. I think we could get by with documentation for this, rather than "verifying" the admin token's scope.
Also:
The username is then used to set the subject of the token:
<service-id>/users/<username>
Limited to 255 characters.
I'm very familiar with that API and its intricacies 😄
If the scope includes applied-permissions/user then the token's username must be a valid existing user that is enabled (and not locked or disabled).
This is what I'm referring to as the username
must correspond to an existing user in Artifactory.
Can you please provide an end-to-end example (vault cli, vault info, artifactory user data?) to illustrate what you are proposing? These contradictory requirements are not clear to me at all.
For example, I attempted to create a token for an user foobar
who doesn't exist in my local Artifactory and this is the error message I received:
{
"error": "invalid_request",
"error_description": "requested username does not exists or not enabled. username [foobar] user status [DOES NOT EXISTS]"
}
Using access tokens with generated users: https://www.jfrog.com/confluence/display/JFROG/Access+Tokens#AccessTokens-SupportAuthenticationforNon-existingUsers
More docs: https://discuss.hashicorp.com/t/username-templates/46020/4
Source code for mysql database plugin (Template stuff): https://github.com/hashicorp/vault/blob/main/plugins/database/mysql/mysql.go#L29-L99
Source code for template mod: https://github.com/hashicorp/vault/blob/v1.12.2/sdk/helper/template/template.go
I think we can make this work.
Sorry, my last comment was just some docs that were apparently saved in my browser instead of being submitted?
to respond to your last comment...
I am suggesting that we create a group scoped token, such as:
scope=applied-permissions/group:readers
... then you can set username=whatever-you-want-up-to-256-chars
, and it should create a token with a dynamic (?) (not created in the user database) for it.
Example:
[tmcneely@local artifactory-secrets-plugin]$ curl -H "Authorization: Bearer $ACCESS_TOKEN" http://localhost:8082/access/api/v1/tokens -d "scope=applied-permissions/groups:readers" -d "username=whatever-you-want-up-to-256-chars"
{
"token_id" : "0fe0cff5-8e12-43b4-b3c6-4a88d3584707",
"access_token" : "eyJ2ZXIiOiIyIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYiLCJraWQiOiJZZjFINmx6SEdhWlcya2NPZGZxdUpCYTY1N2JfamVZcDRraE10NHFEQVBjIn0.eyJleHQiOiJ7XCJyZXZvY2FibGVcIjpcInRydWVcIn0iLCJzdWIiOiJqZmFjQDAxZ3QwOTl4OWptZGRyMGFhYWgwYjkwMGhtXC91c2Vyc1wvd2hhdGV2ZXIteW91LXdhbnQtdXAtdG8tMjU2LWNoYXJzIiwic2NwIjoiYXBwbGllZC1wZXJtaXNzaW9uc1wvZ3JvdXBzOnJlYWRlcnMiLCJhdWQiOiIqQCoiLCJpc3MiOiJqZmFjQDAxZ3QwOTl4OWptZGRyMGFhYWgwYjkwMGhtIiwiaWF0IjoxNjc3NTMwNDIyLCJqdGkiOiIwZmUwY2ZmNS04ZTEyLTQzYjQtYjNjNi00YTg4ZDM1ODQ3MDcifQ.MhSgut0w6emAgPILIh7R6q5FEUnQj4hcnpJw5oOXbjdprHW7W07OXtawTZWA5d0KwmtIOu8v1zt3ByCStmrFSNuZDJY-oy2V0-6cyGXCadn0N6q65FXliRw7Cz79rq-V0S0f5h5CmVtMiyiyv_4t1RlmPNvjVOJlKfnHzBJIa2kqts-8C0lblF5yb4UHSxaKm6GRSKv2kdcugnLxfGamBUGblZM2RYlGTRo2c8-HVMWhBaqyVmruEq2tIdUa6wJl9r0GaQYtnVyqCm6hBcPmhWKcjCnFd5D7Oq4Uj5NIJXI3t_RcFoN1iaooglYZjsZC2OcPrKphT-3GSy-1lp7-vw",
"scope" : "applied-permissions/groups:readers",
"token_type" : "Bearer"
}
[tmcneely@local artifactory-secrets-plugin]$ jwt decode eyJ2ZXIiOiIyIiwidHlwIjoiSldUIiwiYWxnIjoiUlMyNTYiLCJraWQiOiJZZjFINmx6SEdhWlcya2NPZGZxdUpCYTY1N2JfamVZcDRraE10NHFEQVBjIn0.eyJleHQiOiJ7XCJyZXZvY2FibGVcIjpcInRydWVcIn0iLCJzdWIiOiJqZmFjQDAxZ3QwOTl4OWptZGRyMGFhYWgwYjkwMGhtXC91c2Vyc1wvd2hhdGV2ZXIteW91LXdhbnQtdXAtdG8tMjU2LWNoYXJzIiwic2NwIjoiYXBwbGllZC1wZXJtaXNzaW9uc1wvZ3JvdXBzOnJlYWRlcnMiLCJhdWQiOiIqQCoiLCJpc3MiOiJqZmFjQDAxZ3QwOTl4OWptZGRyMGFhYWgwYjkwMGhtIiwiaWF0IjoxNjc3NTMwNDIyLCJqdGkiOiIwZmUwY2ZmNS04ZTEyLTQzYjQtYjNjNi00YTg4ZDM1ODQ3MDcifQ.MhSgut0w6emAgPILIh7R6q5FEUnQj4hcnpJw5oOXbjdprHW7W07OXtawTZWA5d0KwmtIOu8v1zt3ByCStmrFSNuZDJY-oy2V0-6cyGXCadn0N6q65FXliRw7Cz79rq-V0S0f5h5CmVtMiyiyv_4t1RlmPNvjVOJlKfnHzBJIa2kqts-8C0lblF5yb4UHSxaKm6GRSKv2kdcugnLxfGamBUGblZM2RYlGTRo2c8-HVMWhBaqyVmruEq2tIdUa6wJl9r0GaQYtnVyqCm6hBcPmhWKcjCnFd5D7Oq4Uj5NIJXI3t_RcFoN1iaooglYZjsZC2OcPrKphT-3GSy-1lp7-vw
Token header
------------
{
"typ": "JWT",
"alg": "RS256",
"kid": "Yf1H6lzHGaZW2kcOdfquJBa657b_jeYp4khMt4qDAPc"
}
Token claims
------------
{
"aud": "*@*",
"ext": "{\"revocable\":\"true\"}",
"iat": 1677530422,
"iss": "jfac@01gt099x9jmddr0aaah0b900hm",
"jti": "0fe0cff5-8e12-43b4-b3c6-4a88d3584707",
"scp": "applied-permissions/groups:readers",
"sub": "jfac@01gt099x9jmddr0aaah0b900hm/users/whatever-you-want-up-to-256-chars"
Trying to understand the root problem here as I think we're going to be doing something similar. Essentially having CI/CD processes assume "roles" in JFrog (ie group) after logging into Vault with the CI/CD pipeline's OIDC tokens (yay secret-free authentication!). In our proof-of-concept I'm constructing the roles through terraform with something like this:
locals {
jfrog_group_roles = [
"rnd-pipelines-pre",
"rnd-pipelines-prod",
"it-pipelines-pre",
"it-pipelines-prod",
]
}
resource "vault_generic_endpoint" "artifactory_group_roles" {
depends_on = [
module.plugins.secret_mounts
]
for_each = { for group in local.jfrog_group_roles : group => group }
path = "artifactory/roles/${each.key}"
ignore_absent_fields = true
data_json = jsonencode({
username = each.key
scope = "applied-permissions/groups:${each.key}"
# 43200 seconds = 12 hours. Vault will accept '12h' but show a diff
# because it uses seconds when returning the current state
default_ttl = 43200
max_ttl = 43200
})
}
In this case the "username" of the token is just the role name, would this end up hitting rate limits as described in the ticket if too many pipelines assumed the same role simultaneously?
Closed by #47
Enhancement to allow creating a templated/dynamic username instead of a "static" account. Maybe default to something like a prefix-unixtimestamp or whatever (
automation-1677017997
)?Backstory
We have had a problem where "someone" (something) was using an expired token, and because all the different namespaces were using the same role, and thus the same "username" that username was being "blocked" by artifactory. So, even if they had the correct token, they were getting a backoff. If each retrieved token had a dynamically created user account, they couldn't affect eachother.
This may also be relevant to #27