-
Notifications
You must be signed in to change notification settings - Fork 82
doc: REST API authentication #1021
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
# Glusterd2 REST API authentication | ||
|
||
Glusterd2 REST API authentication is based on [JWT](https://jwt.io). If REST | ||
authentication is enabled then each client request should include | ||
`Authorization` header as example below. | ||
|
||
Authorization: bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnbHVzdGVyY2xpIiwiaWF0IjoxNTMxNzI5NzQyLCJleHAiOjE1MzE3Mjk3NTJ9._WIwO7PrHUHIT62SdzfkyNjqD1GEgX2cYqN8ACZCtaw | ||
|
||
**Note**: REST Authentication can be disabled by adding `restauth=false` | ||
in Glusterd2 config file(Default path is | ||
`/var/lib/glusterd2/glusterd2.toml` in case of rpm installation) | ||
|
||
## Default user | ||
|
||
When `glusterd2` starts for the first time, it creates the | ||
`$GLUSTERD2_STATEDIR/auth` file which will contain the secret. If | ||
"gluster" user group is available in the system then this `auth` file | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we mention how to create "gluster" user group? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we need to do this. This is simple unix knowledge. In any case, if installed from packages, the package should take care of creating the users/groups. I don't think any gluster packages do this yet. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It may be worthwhile to point out this is a unix group interacting with file permissions and not some special gluster thing in that case. The word group and phrase "in that machine" alone may not hint at that strongly enough. |
||
can be read by any user in that machine who are part of "gluster" | ||
group. | ||
|
||
**Note**: If "gluster" user group is not available during first start | ||
of `glusterd2` it limits the read permission to `root:root` | ||
|
||
## glustercli | ||
|
||
With default installation, `glustercli` will know the location of | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps, "The glustercli command knows the default location of the auth file." would read more clearly. |
||
`auth` file. `glustercli` will generate JWT token using the secret | ||
available in `auth` file and attach it with every REST API calls. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "every REST API call." or "all REST API calls." |
||
|
||
Default installation will not require any change to use `glustercli`. | ||
|
||
If `auth` file is in different path(When `glusterd2` is running with | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the auth file is in a different path (for example, when |
||
custom `workdir`), then run `glustercli` by specifying | ||
`--secret-file`. For example, | ||
|
||
glustercli --secret-file=/root/setup1/glusterd2/auth peer status | ||
|
||
**Note**: bash alias can be added like `alias glustercli='glustercli | ||
--secret-file=/root/setup1/glusterd2/auth` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you think it might be a little out of place to teach people about bash aliases in the doc? |
||
|
||
Secret is taken by `glustercli` in following order of precedence | ||
(highest to lowest): | ||
|
||
--secret | ||
--secret-file | ||
GLUSTERD2_AUTH_SECRET (environment variable) | ||
--secret-file (default path) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Putting --secret-file here again seems confusing from a user perspective IMO. Just don't mention it and say "default path". It may play into the implementation but I don't think that's relevant to the person reading the doc. |
||
|
||
## Curl example | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should be under ReST API section? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A link should be added from the ReST API document to this doc. |
||
|
||
Download the utility script `glustercli-auth-header.py` from | ||
[here](https://github.com/gluster/glusterd2/tree/master/pkg/tools/) | ||
and save it in server nodes. | ||
|
||
Add alias in `~/.bashrc` as below, | ||
|
||
alias gcurl='curl -H "$(python3 ~/glustercli-auth-header.py --secret-file=/var/lib/glusterd2/auth)"' | ||
|
||
**Note**: Change the path of script and auth file to match your setup. | ||
|
||
Thats all, use `gcurl` wherever `curl` is necessory. For example, to start a Volume | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. necessary not necessory |
||
|
||
gcurl -XPOST http://localhost:24007/v1/volumes/gv1/start | ||
|
||
## Python example | ||
|
||
Install `jwt` library using `pip install jwt` or using `dnf install | ||
python-jwt`. Generate JWT token using, | ||
|
||
import jwt | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might make sense to enclose this block with ````python` esp. if its expected this will be viewed using github/github-style-markdown-renderer. |
||
import requests | ||
|
||
user = "glustercli" | ||
secret_file = "/var/lib/glusterd2/auth" | ||
secret = open(secret_file).read() | ||
|
||
claims = { | ||
"iss": "glustercli", | ||
"iat": datetime.utcnow(), | ||
"exp": datetime.utcnow() + timedelta(seconds=10) | ||
} | ||
|
||
token = jwt.encode(claims, secret, algorithm='HS256') | ||
resp = requests.get("http://localhost:24007/v1/peers", | ||
headers={"Authorization": "bearer " + token} | ||
|
||
peers = [] | ||
if resp.status_code == 200: | ||
peers = json.loads(resp.content) | ||
|
||
|
||
## Using REST APIs from outside the Cluster nodes | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd prefer you remove this section completely for now. Or at least modify such that it doesn't involve copying out the secret file. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. makes sense. I updated the same in other comment #1021 (comment)
We are following shared secret approach. No change required for curl example unless we decide to do shared token approach again. |
||
APIs for creating external users is not yet implemented. Temporarily | ||
copy the `auth` file from one of the server nodes and place it in a | ||
secured location.(Say `~/.glustercli/auth`) | ||
|
||
Run `glustercli` by specifying `--secret-file`. For example, | ||
|
||
glustercli --secret-file=~/.glustercli/auth peer status | ||
|
||
Or use `curl` as below | ||
|
||
curl -H "$(python3 ~/glustercli-auth-header.py --secret-file=~/.glustercli/auth)" \ | ||
http://gluster1.example.com:24007/v1/peers | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import sys | ||
from argparse import ArgumentParser | ||
from datetime import datetime, timedelta | ||
|
||
import jwt | ||
|
||
|
||
def jwt_token(user, secret): | ||
claims = dict() | ||
claims['iss'] = user | ||
claims['iat'] = datetime.utcnow() | ||
claims['exp'] = datetime.utcnow() + timedelta(seconds=10) | ||
|
||
token = jwt.encode(claims, secret, algorithm='HS256') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks fine, To provide more security, we need to add URL tampering protection also in the server side, we need to enable (HTTP method) validation to avoid miss use of the token. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. makes sense. Please open new issue for the same. |
||
return (b'Authorization: bearer ' + token).decode() | ||
|
||
|
||
if __name__ == "__main__": | ||
parser = ArgumentParser() | ||
parser.add_argument("--user", default="glustercli") | ||
parser.add_argument("--secret-file", required=True) | ||
args = parser.parse_args() | ||
|
||
secret = "" | ||
try: | ||
with open(args.secret_file) as f: | ||
secret = f.read() | ||
except IOError as err: | ||
sys.stderr.write("Unable to open secret file\n") | ||
sys.stderr.write("Error: %s\n" % err) | ||
sys.exit(1) | ||
|
||
print(jwt_token(args.user, secret)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we document anywhere what GLUSTERD2_STATEDIR path mean? Might be good to point out to a link.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we have a document about the paths yet. We should do it.
Apart from that, I have a concern about using the GD2 secret by external tools.
The secret is meant to be accessible only by GD2. GD2 should be using this secret to create and handout tokens. Having the clients access the secret is a big security risk. Later in the document, we ask that the secret be copied out externally and have external client generate their own token. This is a serious lapse of security. Because the secret can be passed along to unintended users and those users would have full access to the GD2 cluster.
Ideally, as I mentioned earlier GD2 should be the one generating and handing out tokens. Even for
glustercli
it would be preferrable that you use token. GD2 should generate a token for CLI and save it to a known place. And we should add a way to generate tokens to GD2 (ReST or CLI to be decided).Sorry about bringing this up now. I should have been paying attention when the actual PR was being reviewed. I'll open up a new issue about
glustercli
not using the secret file directly. We can merge this PR without waiting for that, but only if it is modified to address my comments later on.