We provide a generic EOS mechanism to delegate permissions to a token bearer with s.c. EOS tokens.
The JSON representation of an EOS token looks like shown here:
{
"token": {
"permission": "rwx",
"expires": "1571319146",
"owner": "",
"group": "",
"generation": "1",
"path": "/eos/dev/token",
"allowtree": false,
"vtoken": "",
"voucher": "baecb618-f0e4-11e9-85d9-fa163eb6b6cf",
"requester": "[Thu Oct 17 15:47:59 2019] uid:0[root] gid:0[root] tident:root.13809:107@localhost name:daemon dn: prot:sss host:localhost domain:localdomain geo:cern sudo:1",
"origins": []
},
"signature": "daUeOZafRUt6VfQZ+g3FMbR/ZA5WvARELqFwdQxbyFU=",
"serialized": "CgJyeBDq2qHtBTIJL2Vvcy9kZXYvSiRiYWVjYjYxOC1mMGU0LTExZTktODVkOS1mYTE2M2ViNmI2Y2ZSnAFbVGh1IE9jdCAxNyAxNTo0Nzo1OSAyMDE5XSB1aWQ6MFtyb290XSBnaWQ6MFtyb290XSB0aWRlbnQ6cm9vdC4xMzgwOToxMDdAbG9jYWxob3N0IG5hbWU6ZGFlbW9uIGRuOiBwcm90OnNzcyBob3N0OmxvY2FsaG9zdCBkb21haW46bG9jYWxkb21haW4gZ2VvOmFqcCBzdWRvOjE=",
"seed": 1399098912
}
Essentially this token gives the bearer the permission to rwx for the file /eos/dev/token. The token does not bear an owner and group information, which means, that the creations will be accounted on the mapped authenticated user using this token or an enforced sys.owner.auth entry. If the token should map the authenticated user, one can add owner and group fields. In practical terms the token removes existing user and system ACL entries and places the token user/group/permission entries as a system ACL.
Tokens are signed, zlib compressed, base64url encoded with a replacement of the ‘+’ and ‘/’ characters with ‘-‘ and ‘_’ and a URL encodign of the ‘=’ character to avoid interferences/confusion with directory and file names.
The voucher field is tagged on the file when a file has been created and is also used as the logging id for this file upload. The requester field reports when, by whom and how a token has been generated.
To enable issuing of tokens, the space configuration value token.enegeration has to be set unequal to 0.
eos space config default space.token.generation=1
By default the signing key is derived from the instance sss keytab. If you want to define your own signature key, you can point to a file containing the key in /etc/sysconfig/eos_env:
EOS_MGM_TOKEN_KEYFILE=/etc/eos/token.key
The token key file must be owned by the daemon user and have 400 permission!
The CLI interface to create a token is shown here:
# create a generic read-only token for a file valid 5 minutes
EXPIRE=`date +%s; let LATER=$EXPIRE+300
eos token --path /eos/myfile --expires $LATER
zteos64:MDAwMDAwNzR4nONS4WIuKq8Q-Dlz-ltWI3H91Pxi~cSsAv2S~OzUPP2SeAgtpMAY7f1e31Ts-od-rgcLZ~a2~bhwcZO9cracyhm1b3c6jpRIEWWOws71Ox6xAABeTC8I
# create a generic read-only token for a directory - mydir has to end with a '/' - valid 5 minutes
eos token --path /eos/mydir/ --expires $LATER
# create a generic read-only token for a directory tree - mytree has to end with a '/' - valid 5 minutes
eos token --path /eos/mydir/ --tree --expires $LATER
# create a generic write token for a file - valid 5 minutes
eos token --path /eos/myfile --permission rwx --expires $LATER
The CLI interface to show the contents of a token is shown here:
eos token --token zteos64:MDAwMDAwNzR4nONS4WIuKq8Q-Dlz-ltWI3H91Pxi_cSsAv2S_OzUPP2SeAgtpMAY7f1e31Ts-od-rgcLZ_a2_bhwcZO9cracyhm1b3c6jpRIEWWOws7
TOKEN="zteos64:MDAwMDAwNzR4nONS4WIuKq8Q-Dlz-ltWI3H91Pxi_cSsAv2S_OzUPP2SeAgtpMAY7f1e31Ts-od-rgcLZ_a2_bhwcZO9cracy"
env EOSAUTHZ=$TOKEN eos whoami
Virtual Identity: uid=0 (99,3,0) gid=0 (99,4,0) [authz:unix] sudo* host=localhost domain=localdomain geo-location=ajp
{
"token": {
"permission": "rx",
"expires": "1600000000",
"owner": "",
"group": "",
"generation": "1",
"path": "/eos/myfile",
"allowtree": false,
"origins": []
},
}
A file token can be used in two ways:
# as a filename
xrdcp root://myeos//zteos64:MDAwMDAwNzR4nONS4WIuKq8Q-Dlz-ltWI3H91Pxi_cSsAv2S_OzUPP2SeAgtpMAY7f1e31Ts-od-rgcLZ_a2_bhwcZO9cracy /tmp/
# via CGI
xrdcp "root://myeos//eos/myfile?authz=zteos64:MDAwMDAwNzR4nONS4WIuKq8Q-Dlz-ltWI3H91Pxi_cSsAv2S_OzUPP2SeAgtpMAY7f1e31Ts-od+rgcLZ_a2_bhwcZO9cracy" /tmp/
If a token contains a subtree permission, the only way to use it for a file access is to use the CGI form. The filename form is practical to hide the filename for up-/downloads.
The root user can issue any token. Everybody else can only issue tokens for files in existing parent directories or directory trees, where the calling user is the current owner.
The token lifetime is given as a unix timestamp during the token creation.
Tokens are issued with a generation entry. The generation value is a globally configured 64-bit unsigned number. In case of emergency all tokens can be revoked by increasing the generation value. The generation value is configured via the key token.generation in the default space
# change the generation value
eos config default space.token.generation=256
# show the generation value
eos space status default | grep token.generation
token.generation := 256
The client location from where a token can be used can be restricted by using the origins entries.
# all machines at CERN authenticating via kerberos as user nobody
eos token --path /eos/myfile --origin \.*.cern.ch:nobody:krb5"
# all machines at CERN authenticating via unix as user kubernetes from machine k8s.cern.ch
eos token --path /eos/myfile --origin "k8s.cern.ch:kubernetes:unix"
# general syntax is a regexp for origin like <regexp hostname>:<regexp username>:<regexp auth protocol>
The default origin regexp is .*:.*:.* accepting all origins. If the regex is invalid, the command will return with an error message.
Tokens can be requested and verified using GRPC TokenRequest as shown here with the GRPC CLI. To request a token at least path, expires and permission should be defined.
[root@ajp mgm]# eos-grpc-ns --acl rwx -p /eos/ajp/xrootd token
request:
{
"authkey": "",
"token": {
"token": {
"token": {
"permission": "rwx",
"expires": "1571226882",
"owner": "",
"group": "",
"generation": "0",
"path": "/eos/ajp/xrootd",
"allowtree": false,
"vtoken": "",
"origins": []
},
"signature": "",
"serialized": "",
"seed": 0
}
}
}
reply:
{
"error": {
"code": "0",
"msg": "zteos64:MDAwMDAwODR4nOPS4WIuKq8QaOqa85ZVii0vPyk_pVIJShvx66fmF-snZhXoVxTl55ekCCk8KMu4qK4Z7_jNTmF5u0_z5hP1J97v3K3G29cid0O4gv-5FEnmKUyavGstGwCiYjHe"
}
}
request took 6226 micro seconds
To verify a token, the vtoken field should hold the token to decode.
[root@ajp mgm]# eos-grpc-ns --ztoken zteos64:MDAwMDAwODR4nOPS4WIuKq8QaOqa85ZVii0vPyk_pVIJShvx66fmF-snZhXoVxTl55ekCCk8KMu4qK4Z7_jNTmF5u0_z5hP1J97v3K3G29cid0O4gv-5FEnmKUyavGstGwCiYjHe token
request:
{
"authkey": "",
"token": {
"token": {
"token": {
"permission": "rx",
"expires": "1571226893",
"owner": "",
"group": "",
"generation": "0",
"path": "",
"allowtree": false,
"vtoken": "zteos64:MDAwMDAwODR4nOPS4WIuKq8QaOqa85ZVii0vPyk_pVIJShvx66fmF-snZhXoVxTl55ekCCk8KMu4qK4Z7_jNTmF5u0_z5hP1J97v3K3G29cid0O4gv-5FEnmKUyavGstGwCiYjHe",
"origins": []
},
"signature": "",
"serialized": "",
"seed": 0
}
}
}
reply:
{
"error": {
"code": "0",
"msg": "{\n \"token\": {\n \"permission\": \"rwx\",\n \"expires\": \"1571321093\",\n \"owner\": \"nobody\",\n \"group\": \"nobody\",\n \"generation\": \"0\",\n \"path\": \"/eos/ajp/xrootd\",\n \"allowtree\": false,\n \"vtoken\": \"\",\n \"voucher\": \"6496c338-f0e6-11e9-b81d-fa163eb6b6cf\",\n \"requester\": \"[Thu Oct 17 15:59:53 2019] uid:99[nobody] gid:99[nobody] tident:.1:46602@[:1] name: dn: prot:grpc host:[:1] domain:localdomain geo:cern sudo:0\",\n \"origins\": []\n },\n \"signature\": \"2B8qIUfJ6rTusI2NFXKH70AoXZ55wKUUDijFCK3e2bY=\",\n \"serialized\": \"CgNyd3gQheqh7QUaBm5vYm9keSIGbm9ib2R5Mg8vZW9zL2FqcC94cm9vdGRKJDY0OTZjMzM4LWYwZTYtMTFlOS1iODFkLWZhMTYzZWI2YjZjZlKNAVtUaHUgT2N0IDE3IDE1OjU5OjUzIDIwMTldIHVpZDo5OVtub2JvZHldIGdpZDo5OVtub2JvZHldIHRpZGVudDouMTo0NjYwMkBbOjFdIG5hbWU6IGRuOiBwcm90OmdycGMgaG9zdDpbOjFdIGRvbWFpbjpsb2NhbGRvbWFpbiBnZW86YWpwIHN1ZG86MA==\",\n \"seed\": 844966647\n}\n"
}
}
The possible return codes are:
-EINVAL | : the token cannot be decompressed |
-EINVAL | : the token cannot be parsed |
-EACCES | : the generation number inside the token is not valid anymore |
-EKEYEXPIRED : the token validity has expired
-EPERM | : the token signature is not correct |
It is very useful to issue scoped tokens to applications. To avoid the complication of appending tokens to each and every URL one can use sss security to forward a generic token for each request via the endorsement environment variable.
Client and server should share an sss key for a user, which is actually not authorized to use the instance e.g.
############################
# client
############################
echo 0 u:nfsnobody g:nfsnobody n:eos-test N:5506672669367468033 c:1282122142 e:0 k:0123456789012345678901234567890123456789012345678901234567890123 > $HOME/.eos.keytab
# point to keytab file
export XrdSecSSSKT=$HOME/.eos.keytab
# enforce sss
export XrdSecPROTOCOL=sss
############################
#server
############################
# server shares the same keytab entry
echo 0 u:nfsnobody g:nfsnobody n:eos-test N:5506672669367468033 c:1282122142 e:0 k:0123456789012345678901234567890123456789\012345678901234567890123 >> /etc/eos.keytab
# server bans user nfsnobody or maybe uses already user allow, which bans this user by default
eos access ban user nfsnobody
# server issues a scoped token binding to a user/group
TOKEN=`eos token --path /eos/cms/www/ --permission rwx --expires 1600000000 --owner cmsprod --group zh`
############################
# client
############################
# exports the token in the environment
export XrdSecsssENDORSEMENT=zteos64:....
# test the ID
eos whoami
Virtual Identity: uid=5410 (65534,99,5410) gid=1339 (65534,99,1338) [authz:sss] host=localhost domain=localdomain geo-location=ajp key=zteos64:....
{
"token": {
"permission": "rwx",
"expires": "1000000000",
"owner": "cmsprod",
"group": "zh",
"generation": "0",
"path": "/eos/cms/www/",
"allowtree": false,
"vtoken": "",
"origins": []
},
}
As a user you can create a token e.g. for applications like CIs, webservices etc. if the EOS instances it configured to issue tokens.
To create a token as a user you do:
eos token --path /eos/user/f/foo/ci/ --expires 1654328760 --perm rwx --tree
If you create a token as a user, the token puts the calling role as the identity into the token.
You can inspect your token to verify that it contains what you want using:
eos token --token zteos64:...
Finally to use the token on a mount client you define only the following variable:
# put the token into your client environment
export XrdSecsssENDORSEMENT=zteos64:...
# you should now have rwx permission on this tree
ls /eos/user/f/foo/ci/