diff --git a/README.md b/README.md index e1af0eb..adf793c 100644 --- a/README.md +++ b/README.md @@ -217,41 +217,8 @@ Also, in case of downloading, you need to have a file inside a container. ### NNS In all download/upload routes you can use container name instead of its id (`$CID`). +Read more about it in [docs/nns.md](./docs/nns.md). -Steps to start using name resolving: - -1. Enable NNS resolving in config (`rpc_endpoint` must be a valid neo rpc node, see [configs](./config) for other examples): - -```yaml -rpc_endpoint: http://morph-chain.frostfs.devenv:30333 -resolve_order: - - nns -``` - -2. Make sure your container is registered in NNS contract. If you use [frostfs-dev-env](https://git.frostfs.info/TrueCloudLab/frostfs-dev-env) -you can check if your container (e.g. with `container-name` name) is registered in NNS: - -```shell -$ curl -s --data '{"id":1,"jsonrpc":"2.0","method":"getcontractstate","params":[1]}' \ - http://morph-chain.frostfs.devenv:30333 | jq -r '.result.hash' - -0x8e6c3cd4b976b28e84a3788f6ea9e2676c15d667 - -$ docker exec -it morph_chain neo-go \ - contract testinvokefunction \ - -r http://morph-chain.frostfs.devenv:30333 0x8e6c3cd4b976b28e84a3788f6ea9e2676c15d667 \ - resolve string:container-name.container int:16 \ - | jq -r '.stack[0].value | if type=="array" then .[0].value else . end' \ - | base64 -d && echo - -7f3vvkw4iTiS5ZZbu5BQXEmJtETWbi3uUjLNaSs29xrL -``` - -3. Use container name instead of its `$CID`. For example: - -```shell -$ curl http://localhost:8082/get_by_attribute/container-name/FileName/object-name -``` #### Create a container @@ -462,109 +429,7 @@ object ID, like this: #### Authentication -You can always upload files to public containers (open for anyone to put -objects into), but for restricted containers you need to explicitly allow PUT -operations for a request signed with your HTTP Gateway keys. - -If you don't want to manage gateway's secret keys and adjust policies when -gateway configuration changes (new gate, key rotation, etc) or you plan to use -public services, there is an option to let your application backend (or you) to -issue Bearer Tokens and pass them from the client via gate down to FrostFS level -to grant access. - -FrostFS Bearer Token basically is a container owner-signed policy (refer to FrostFS -documentation for more details). There are two options to pass them to gateway: - * "Authorization" header with "Bearer" type and base64-encoded token in - credentials field - * "Bearer" cookie with base64-encoded token contents - -For example, you have a mobile application frontend with a backend part storing -data in FrostFS. When a user authorizes in the mobile app, the backend issues a FrostFS -Bearer token and provides it to the frontend. Then, the mobile app may generate -some data and upload it via any available FrostFS HTTP Gateway by adding -the corresponding header to the upload request. Accessing policy protected data -works the same way. - -##### Example -In order to generate a bearer token, you need to have wallet (which will be used to sign the token) - -1. Suppose you have a container with private policy for wallet key - -``` -$ frostfs-cli container create -r --wallet -policy --basic-acl 0 --await -CID: 9dfzyvq82JnFqp5svxcREf2iy6XNuifYcJPusEDnGK9Z - -$ frostfs-cli ape-manager add -r --wallet \ - --target-type container --target-name 9dfzyvq82JnFqp5svxcREf2iy6XNuifYcJPusEDnGK9Z \ - --rule "allow Object.* RequestCondition:"\$Actor:publicKey"=03b09baabff3f6107c7e9acb8721a6fc5618d45b50247a314d82e548702cce8cd5 *" \ - --chain-id -``` - - -2. Form a Bearer token (10000 is lifetime expiration in epoch) to impersonate - HTTP Gateway request as wallet signed request and save it to **bearer.json**: -``` -{ - "body": { - "allowImpersonate": true, - "lifetime": { - "exp": "10000", - "nbf": "0", - "iat": "0" - } - }, - "signature": null -} -``` - -3. Sign it with the wallet: -``` -$ frostfs-cli util sign bearer-token --from bearer.json --to signed.json -w -``` - -4. Encode to base64 to use in header: -``` -$ base64 -w 0 signed.json -# output: Ck4KKgoECAIQBhIiCiCZGdlbN7DPGPMg9rsWqV+p2XdMzUqknRiexewSFp8kmBIbChk17MUri6OJ0X5ftsHzy7NERDNFB4C92PcaGgMIkE4SZgohAxpsb7vfAso1F0X6hrm6WpRS14WsT3/Ct1SMoqRsT89KEkEEGxKi8GjKSf52YqhppgaOTQHbUsL3jn7SHLqS3ndAQ7NtAATnmRHleZw2V2xRRSRBQdjDC05KK83LhdSax72Fsw== -``` - -After that, the Bearer token can be used: - -``` -$ curl -F 'file=@cat.jpeg;filename=cat.jpeg' -H "Authorization: Bearer Ck4KKgoECAIQBhIiCiCZGdlbN7DPGPMg9rsWqV+p2XdMzUqknRiexewSFp8kmBIbChk17MUri6OJ0X5ftsHzy7NERDNFB4C92PcaGgMIkE4SZgohAxpsb7vfAso1F0X6hrm6WpRS14WsT3/Ct1SMoqRsT89KEkEEGxKi8GjKSf52YqhppgaOTQHbUsL3jn7SHLqS3ndAQ7NtAATnmRHleZw2V2xRRSRBQdjDC05KK83LhdSax72Fsw==" \ - http://localhost:8082/upload/BJeErH9MWmf52VsR1mLWKkgF3pRm3FkubYxM7TZkBP4K -# output: -# { -# "object_id": "DhfES9nVrFksxGDD2jQLunGADfrXExxNwqXbDafyBn9X", -# "container_id": "BJeErH9MWmf52VsR1mLWKkgF3pRm3FkubYxM7TZkBP4K" -# } -``` - -##### Note: Bearer Token owner - -You can specify exact key who can use Bearer Token (gateway wallet address). -To do this, encode wallet address in base64 format - -``` -$ echo 'NhVtreTTCoqsMQV5Wp55fqnriiUCpEaKm3' | base58 --decode | base64 -# output: NezFK4ujidF+X7bB88uzREQzRQeAvdj3Gg== -``` - -Then specify this value in Bearer Token Json -``` -{ - "body": { - "ownerID": { - "value": "NezFK4ujidF+X7bB88uzREQzRQeAvdj3Gg==" - }, - ... -``` - -##### Note: Policy override - -Instead of impersonation, you can define the set of policies that will be applied -to the request sender. This allows to restrict access to specific operation and -specific objects without giving full impersonation control to the token user. +Read more about request authentication in [docs/authentication.md](./docs/authemtnication.md) ### Metrics and Pprof diff --git a/docs/api.md b/docs/api.md index f7eb3a4..e59956a 100644 --- a/docs/api.md +++ b/docs/api.md @@ -8,7 +8,7 @@ | `/zip/{cid}/{prefix}` | [Download objects in archive](#download-zip) | **Note:** `cid` parameter can be base58 encoded container ID or container name -(the name must be registered in NNS, see appropriate section in [README](../README.md#nns)). +(the name must be registered in NNS, see appropriate section in [nns.md](./nns.md)). Route parameters can be: @@ -18,7 +18,7 @@ Route parameters can be: ### Bearer token -All routes can accept [bearer token](../README.md#authentication) from: +All routes can accept [bearer token](./authentication.md) from: * `Authorization` header with `Bearer` type and base64-encoded token in credentials field diff --git a/docs/authentication.md b/docs/authentication.md new file mode 100644 index 0000000..d8bb235 --- /dev/null +++ b/docs/authentication.md @@ -0,0 +1,108 @@ +# Request authentication + +HTTP Gateway does not authorize requests. Gateway converts HTTP request to a +FrostFS request and signs it with its own private key. + +You can always upload files to public containers (open for anyone to put +objects into), but for restricted containers you need to explicitly allow PUT +operations for a request signed with your HTTP Gateway keys. + +If you don't want to manage gateway's secret keys and adjust policies when +gateway configuration changes (new gate, key rotation, etc) or you plan to use +public services, there is an option to let your application backend (or you) to +issue Bearer Tokens and pass them from the client via gate down to FrostFS level +to grant access. + +FrostFS Bearer Token basically is a container owner-signed policy (refer to FrostFS +documentation for more details). There are two options to pass them to gateway: +* "Authorization" header with "Bearer" type and base64-encoded token in + credentials field +* "Bearer" cookie with base64-encoded token contents + +For example, you have a mobile application frontend with a backend part storing +data in FrostFS. When a user authorizes in the mobile app, the backend issues a FrostFS +Bearer token and provides it to the frontend. Then, the mobile app may generate +some data and upload it via any available FrostFS HTTP Gateway by adding +the corresponding header to the upload request. Accessing policy protected data +works the same way. + +##### Example +In order to generate a bearer token, you need to have wallet (which will be used to sign the token) + +1. Suppose you have a container with private policy for wallet key + +``` +$ frostfs-cli container create -r --wallet -policy --basic-acl 0 --await +CID: 9dfzyvq82JnFqp5svxcREf2iy6XNuifYcJPusEDnGK9Z + +$ frostfs-cli ape-manager add -r --wallet \ + --target-type container --target-name 9dfzyvq82JnFqp5svxcREf2iy6XNuifYcJPusEDnGK9Z \ + --rule "allow Object.* RequestCondition:"\$Actor:publicKey"=03b09baabff3f6107c7e9acb8721a6fc5618d45b50247a314d82e548702cce8cd5 *" \ + --chain-id +``` + + +2. Form a Bearer token (10000 is lifetime expiration in epoch) to impersonate + HTTP Gateway request as wallet signed request and save it to **bearer.json**: +``` +{ + "body": { + "allowImpersonate": true, + "lifetime": { + "exp": "10000", + "nbf": "0", + "iat": "0" + } + }, + "signature": null +} +``` + +3. Sign it with the wallet: +``` +$ frostfs-cli util sign bearer-token --from bearer.json --to signed.json -w +``` + +4. Encode to base64 to use in header: +``` +$ base64 -w 0 signed.json +# output: Ck4KKgoECAIQBhIiCiCZGdlbN7DPGPMg9rsWqV+p2XdMzUqknRiexewSFp8kmBIbChk17MUri6OJ0X5ftsHzy7NERDNFB4C92PcaGgMIkE4SZgohAxpsb7vfAso1F0X6hrm6WpRS14WsT3/Ct1SMoqRsT89KEkEEGxKi8GjKSf52YqhppgaOTQHbUsL3jn7SHLqS3ndAQ7NtAATnmRHleZw2V2xRRSRBQdjDC05KK83LhdSax72Fsw== +``` + +After that, the Bearer token can be used: + +``` +$ curl -F 'file=@cat.jpeg;filename=cat.jpeg' -H "Authorization: Bearer Ck4KKgoECAIQBhIiCiCZGdlbN7DPGPMg9rsWqV+p2XdMzUqknRiexewSFp8kmBIbChk17MUri6OJ0X5ftsHzy7NERDNFB4C92PcaGgMIkE4SZgohAxpsb7vfAso1F0X6hrm6WpRS14WsT3/Ct1SMoqRsT89KEkEEGxKi8GjKSf52YqhppgaOTQHbUsL3jn7SHLqS3ndAQ7NtAATnmRHleZw2V2xRRSRBQdjDC05KK83LhdSax72Fsw==" \ + http://localhost:8082/upload/BJeErH9MWmf52VsR1mLWKkgF3pRm3FkubYxM7TZkBP4K +# output: +# { +# "object_id": "DhfES9nVrFksxGDD2jQLunGADfrXExxNwqXbDafyBn9X", +# "container_id": "BJeErH9MWmf52VsR1mLWKkgF3pRm3FkubYxM7TZkBP4K" +# } +``` + +##### Note: Bearer Token owner + +You can specify exact key who can use Bearer Token (gateway wallet address). +To do this, encode wallet address in base64 format + +``` +$ echo 'NhVtreTTCoqsMQV5Wp55fqnriiUCpEaKm3' | base58 --decode | base64 +# output: NezFK4ujidF+X7bB88uzREQzRQeAvdj3Gg== +``` + +Then specify this value in Bearer Token Json +``` +{ + "body": { + "ownerID": { + "value": "NezFK4ujidF+X7bB88uzREQzRQeAvdj3Gg==" + }, + ... +``` + +##### Note: Policy override + +Instead of impersonation, you can define the set of policies that will be applied +to the request sender. This allows to restrict access to specific operation and +specific objects without giving full impersonation control to the token user. diff --git a/docs/nns.md b/docs/nns.md new file mode 100644 index 0000000..acb9f21 --- /dev/null +++ b/docs/nns.md @@ -0,0 +1,36 @@ +# Nicename Resolving with NNS + +Steps to start using name resolving: + +1. Enable NNS resolving in config (`rpc_endpoint` must be a valid neo rpc node, see [configs](./config) for other examples): + +```yaml +rpc_endpoint: http://morph-chain.frostfs.devenv:30333 +resolve_order: + - nns +``` + +2. Make sure your container is registered in NNS contract. If you use [frostfs-dev-env](https://git.frostfs.info/TrueCloudLab/frostfs-dev-env) + you can check if your container (e.g. with `container-name` name) is registered in NNS: + +```shell +$ curl -s --data '{"id":1,"jsonrpc":"2.0","method":"getcontractstate","params":[1]}' \ + http://morph-chain.frostfs.devenv:30333 | jq -r '.result.hash' + +0x8e6c3cd4b976b28e84a3788f6ea9e2676c15d667 + +$ docker exec -it morph_chain neo-go \ + contract testinvokefunction \ + -r http://morph-chain.frostfs.devenv:30333 0x8e6c3cd4b976b28e84a3788f6ea9e2676c15d667 \ + resolve string:container-name.container int:16 \ + | jq -r '.stack[0].value | if type=="array" then .[0].value else . end' \ + | base64 -d && echo + +7f3vvkw4iTiS5ZZbu5BQXEmJtETWbi3uUjLNaSs29xrL +``` + +3. Use container name instead of its `$CID`. For example: + +```shell +$ curl http://localhost:8082/get_by_attribute/container-name/FileName/object-name +```