.. highlight:: rst .. _tokens: Using EOS Tokens for Authorization ================================== 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: .. code-block:: bash { "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. Enabling Token Issuing ---------------------- To enable issuing of tokens, the space configuration value ``token.enegeration`` has to be set unequal to 0. .. code-block:: bash 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**: .. code-block:: bash EOS_MGM_TOKEN_KEYFILE=/etc/eos/token.key The token key file must be owned by the daemon user and have 400 permission! Token creation -------------- The CLI interface to create a token is shown here: .. code-block:: bash # 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 Token inspection ---------------- The CLI interface to show the contents of a token is shown here: .. code-block:: bash 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": [] }, } Token usage ----------- A file token can be used in two ways: * as a filename * via CGI '?authz=$TOKEN' .. code-block:: bash # 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. Token issuing permission ------------------------ 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. Token lifetime --------------- The token lifetime is given as a unix timestamp during the token creation. Token Revocation ---------------- 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 .. code-block:: bash # 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 Token Origin Restrictions ------------------------- The client location from where a token can be used can be restricted by using the ``origins`` entries. .. code-block:: bash # 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. Token via GRPC -------------- 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. .. code-block:: bash [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. .. code-block:: bash [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 Using tokens with SSS security ------------------------------ 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. .. code-block:: bash ############################ # 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": [] }, } Using tokens for scoped eosxd access ------------------------------------ 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: .. code-block:: bash 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: .. code-block:: bash eos token --token zteos64:... Finally to use the token on a mount client you define only the following variable: .. code-block:: bash # 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/