-
Notifications
You must be signed in to change notification settings - Fork 344
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: restricted APIs #2400
feat: restricted APIs #2400
Conversation
In restricted mode I would not leave any endpoints unrestricted. |
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 would just point out that tests are important for this functionality, even though I know that the status is still in progress.
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.
Thanks @notanatol for implementing bcrypt. There are a few more comments regarding configuration handling and plain text http responses.
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.
Reviewed 2 of 13 files at r1.
Reviewable status: 2 of 14 files reviewed, 14 unresolved discussions (waiting on @acud, @agazso, @istae, @janos, and @notanatol)
pkg/api/api.go, line 280 at r3 (raw file):
Previously, janos (Janoš Guljaš) wrote…
For extensibility, a json object with field "key" would be better.
I agree, we should use jsonhttp.Created
for response with a JSON object.
pkg/api/router.go, line 31 at r3 (raw file):
// handle is a helper closure which simplifies the router setup. handle := func(path string, handler http.Handler) {
Since almost every path should be protected I'd recommend implementing the permission check directly into the handle
closure and at the same time create new closure (which will be used by the handle
closure) for unprotected endpoints.
pkg/auth/auth.go, line 25 at r3 (raw file):
type Authenticator struct { user string
If the user
isn't an object it's usually referred to as username
and password
instead of pass.
pkg/auth/auth.go, line 75 at r3 (raw file):
} func isValidHash(password string, hash []byte) bool {
Since it is used just in the Authorize
I'd inline this there.
pkg/auth/auth.go, line 84 at r3 (raw file):
} func (a *Authenticator) AddKey(user, role string) string {
The user
isn't used.
pkg/auth/auth.go, line 93 at r3 (raw file):
b := make([]byte, 16) _, _ = rand.Read(b)
The error shouldn't be silenced.
pkg/auth/auth.go, line 114 at r3 (raw file):
sub := ar.role // the user that wants to access a resource. if allow, _ := a.enforcer.Enforce(sub, obj, act); !allow {
The !allow
variable can be returned directly.
pkg/debugapi/router.go, line 32 at r3 (raw file):
router := mux.NewRouter() router.NotFoundHandler = http.HandlerFunc(jsonhttp.NotFoundHandler)
I'd recommend creating the same closure as in the regular API router and implementing the permission check directly into the handle
closure similar as was recommended in the regular router API.
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.
Reviewable status: 0 of 16 files reviewed, 14 unresolved discussions (waiting on @acud, @agazso, @istae, @janos, @mrekucci, and @notanatol)
pkg/api/api.go, line 254 at r3 (raw file):
Previously, janos (Janoš Guljaš) wrote…
This handler is returning responses in plain text, while other handlers are returning json-encoded responses. Is this what it intended?
No, by mistake, now fixed.
pkg/api/api.go, line 280 at r3 (raw file):
Previously, mrekucci wrote…
I agree, we should use
jsonhttp.Created
for response with a JSON object.
Done.
pkg/api/api.go, line 306 at r3 (raw file):
Previously, janos (Janoš Guljaš) wrote…
This middleware should not return plain text responses as it is part of the json-encoded endpoints. I think that there should be consistency, as clients may assume json encoded response even without checking content-type.
fixed
pkg/api/api.go, line 319 at r3 (raw file):
Previously, janos (Janoš Guljaš) wrote…
Use json encoded response.
fixed
pkg/api/api.go, line 326 at r3 (raw file):
Previously, janos (Janoš Guljaš) wrote…
JSON encoded response should be more aligned with other responses.
fixed
pkg/api/api.go, line 334 at r3 (raw file):
Previously, janos (Janoš Guljaš) wrote…
I think that this response deserves a body, json encoded, explaining that the auth key is not allowed.
fixed
pkg/auth/auth.go, line 54 at r1 (raw file):
Previously, janos (Janoš Guljaš) wrote…
The New constructor should receive variables for user and pass as arguments, instead to put a hard requirement on env variable configuration. The configuration should be handled in the cmd package and passed to other components as variables. With this approach, configuration is leaking into components. Another thing is that variables are names pretty generically. This can cause a miss-configuration, I would suppose to prefix them with BEE_, or just to make them as struct fields in the existing cmd configuration and handle them just as any other variables.
Done.
pkg/auth/auth.go, line 55 at r1 (raw file):
Previously, janos (Janoš Guljaš) wrote…
I would suggest to try to avoid or discourage users to configure plain text passwords. Instead to require to pass the passwords in the form of a hash, which they can generate either by some tool (online or offline) or with a dedicated bee command. This is the same as apache or nginx are requiring to have auth passwords configured as hashed with htpasswd or any other tool that is able to hash with supported hashers. I am suggesting golang.org/x/crypto/bcrypt.
fixed
pkg/auth/auth.go, line 64 at r1 (raw file):
Previously, janos (Janoš Guljaš) wrote…
I am suggesting to have the password hashed and checked with, for example, bcrypt.GenerateFromPassword.
fixed
pkg/auth/auth.go, line 25 at r3 (raw file):
Previously, mrekucci wrote…
If the
user
isn't an object it's usually referred to asusername
andpassword
instead of pass.
fixed
pkg/auth/auth.go, line 75 at r3 (raw file):
Previously, mrekucci wrote…
Since it is used just in the
Authorize
I'd inline this there.
fixed
pkg/auth/auth.go, line 84 at r3 (raw file):
Previously, mrekucci wrote…
The
user
isn't used.
fixed
pkg/auth/auth.go, line 93 at r3 (raw file):
Previously, mrekucci wrote…
The error shouldn't be silenced.
fixed
pkg/auth/auth.go, line 114 at r3 (raw file):
Previously, mrekucci wrote…
The
!allow
variable can be returned directly.
fixed
pkg/debugapi/router.go, line 306 at r3 (raw file):
Previously, janos (Janoš Guljaš) wrote…
The same comments on the api package applies here.
Done.
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.
Thanks @notanatol for addressing my comments. I have a few more, which are mostly opinions that are also part of the existing codebase.
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.
Reviewed 1 of 8 files at r5.
Reviewable status: 0 of 19 files reviewed, 18 unresolved discussions (waiting on @acud, @istae, @janos, @mrekucci, and @notanatol)
pkg/api/api.go, line 261 at r5 (raw file):
Previously, janos (Janoš Guljaš) wrote…
These types does not need to be exported, and can be provided through export_test.go for tests.
fixed
pkg/api/api.go, line 288 at r5 (raw file):
Previously, janos (Janoš Guljaš) wrote…
Usually errors are logged as well.
fixed
pkg/api/api.go, line 345 at r5 (raw file):
Previously, janos (Janoš Guljaš) wrote…
This error can be logged also.
Done.
pkg/api/router.go, line 31 at r3 (raw file):
Previously, mrekucci wrote…
Since almost every path should be protected I'd recommend implementing the permission check directly into the
handle
closure and at the same time create new closure (which will be used by thehandle
closure) for unprotected endpoints.
added
pkg/debugapi/router.go, line 334 at r5 (raw file):
Previously, janos (Janoš Guljaš) wrote…
This error can also be logged.
fixed
pkg/auth/mock.go, line 1 at r5 (raw file):
Previously, janos (Janoš Guljaš) wrote…
Usually, mocks are in a separate package beside the main implementation. Mainly to keep the package api clean from testing code.
Done.
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.
Reviewed 3 of 13 files at r1, 1 of 4 files at r2, 3 of 7 files at r4, 2 of 8 files at r5, 9 of 10 files at r6, 1 of 7 files at r7.
Reviewable status: 13 of 20 files reviewed, 18 unresolved discussions (waiting on @acud, @istae, @janos, and @notanatol)
pkg/node/devnode.go, line 119 at r3 (raw file):
Previously, janos (Janoš Guljaš) wrote…
The node package does not handle env variables, it explicitly expect input arguments to functions. Currently all configuration is done in the cmd/bee package and I would suggest to make the same with these parameters also. Just adding string flags as with any other configuration parameters will also provide the possibility to configure them with env variables, the same way as with any other parameter.
Done.
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.
should be addressed now, thanks.
Reviewable status: 13 of 20 files reviewed, 18 unresolved discussions (waiting on @acud, @istae, @janos, and @notanatol)
pkg/auth/auth.go
Outdated
return nil, err | ||
} | ||
|
||
passwordHash, err := bcrypt.GenerateFromPassword([]byte(password), 14) |
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 had in mind to avoid keeping in configuration password in plain text by using bcrypt, but this is not doing much for that as it is still required to set password in plain text in config in order to hash it here. The hash should be set in the configuration, instead the plain password. The hash may be generated by a separate command or a tool like htpasswd or any online bcrypt site. I think that the cli command would be nice. It does add a ux complexity, but I think for a justified security reason.
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.
should we maybe add it as a separate command?
/cmd/bee
<- current entry point
/cmd/passwordgen
<- new command that takes a plain text and returns its hash
or maybe just document so the user could use existing tools?
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.
My opinion is that a separate command would provide a direct way to obtain the hash, and to have in documentation how a password can be generated. But for this PR, the command is not necessary, only documenting where the hash can be generated. For example, nginx relied on apache's htpasswd tool for many years and clearly documented how to use it in relation to nginx. So I would not rush with the command, but I think that it provides a nice convenience.
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.
Reviewed 1 of 10 files at r6, 6 of 7 files at r7, all commit messages.
Reviewable status: all files reviewed, 22 unresolved discussions (waiting on @istae, @janos, and @notanatol)
pkg/api/api.go, line 267 at r7 (raw file):
Previously, janos (Janoš Guljaš) wrote…
These log messages would be better with more information. Like with a prefix indicating that it is from the api and which handler.
Done.
pkg/auth/mock/auth.go, line 1 at r7 (raw file):
Previously, janos (Janoš Guljaš) wrote…
Add copyright header.
Done.
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.
Reviewed 1 of 7 files at r4, 1 of 8 files at r5, 5 of 10 files at r6, 2 of 7 files at r7, 4 of 8 files at r8, 2 of 2 files at r9, 6 of 6 files at r10, all commit messages.
Reviewable status: 21 of 22 files reviewed, 22 unresolved discussions (waiting on @acud, @istae, @janos, and @notanatol)
.github/workflows/beekeeper.yml, line 50 at r10 (raw file):
run: | printf ${{ secrets.CR_PAT }} | docker login ghcr.io -u bee-worker --password-stdin make beekeeper BEEKEEPER_INSTALL_DIR=/usr/local/bin BEEKEEPER_USE_SUDO=true BEEKEEPER_BRANCH=auth-health-check
Remove the two trailing spaces at the end.
pkg/api/router.go, line 47 at r1 (raw file):
Previously, acud (acud) wrote…
this is a bit tricky, since there is no check about the identity of the caller here, any malicious dapp can try to query the API, then figure it is restricted, and then just add itself using this API endpoint as a legitimate API user
Agree, this should be protected by username/password.
pkg/auth/auth.go, line 36 at r10 (raw file):
[request_definition] r = sub, obj, act
Remove trailing spaces, also below.
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.
Reviewable status: 19 of 22 files reviewed, 22 unresolved discussions (waiting on @acud, @istae, @janos, @mrekucci, and @notanatol)
pkg/api/router.go, line 47 at r1 (raw file):
Previously, mrekucci wrote…
Agree, this should be protected by username/password.
Done.
pkg/auth/auth.go, line 59 at r7 (raw file):
Previously, janos (Janoš Guljaš) wrote…
My opinion is that a separate command would provide a direct way to obtain the hash, and to have in documentation how a password can be generated. But for this PR, the command is not necessary, only documenting where the hash can be generated. For example, nginx relied on apache's htpasswd tool for many years and clearly documented how to use it in relation to nginx. So I would not rush with the command, but I think that it provides a nice convenience.
I'll make sure to document how to hash the password.
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.
Reviewed 3 of 3 files at r11, all commit messages.
Reviewable status: all files reviewed, 22 unresolved discussions (waiting on @acud, @istae, and @janos)
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.
All looks good. I just have a question about the removed checks in ci beekeeper configuration.
.github/workflows/beekeeper.yml
Outdated
- name: Test content availability | ||
id: content-availability | ||
run: beekeeper check --cluster-name local-dns --checks=ci-content-availability | ||
- name: Test authenticated health |
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.
Should we wait for new beekeeper version to be released with the ci-auth check and to get back ?
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.
yes, I will mark it as draft and merge it only after BK changes are in.
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.
bravisimo... looks very nice 👏 I have a few very small suggestions
Reviewed 1 of 7 files at r4, 5 of 10 files at r6, 1 of 7 files at r7, 3 of 8 files at r8, 2 of 2 files at r9, 3 of 6 files at r10, 1 of 3 files at r11, 5 of 6 files at r12, 1 of 1 files at r13, all commit messages.
Reviewable status: all files reviewed, 22 unresolved discussions (waiting on @istae and @janos)
pkg/api/api.go, line 267 at r7 (raw file):
Previously, notanatol (Anatol) wrote…
Done.
same here and in the rest of the PR :)
pkg/api/api.go, line 267 at r13 (raw file):
if !ok { s.logger.Debug("api: auth handler: missing basic auth")
if the Debug
does not provide any additional information over Error
, it can be safely omitted
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.
Reviewed 3 of 8 files at r8, 1 of 2 files at r9, 3 of 6 files at r10, 1 of 3 files at r11, 5 of 6 files at r12, 1 of 1 files at r13.
Reviewable status: 19 of 22 files reviewed, 22 unresolved discussions (waiting on @acud, @istae, @janos, @mrekucci, and @notanatol)
pkg/api/api.go, line 267 at r7 (raw file):
Previously, acud (acud) wrote…
same here and in the rest of the PR :)
Done.
295e7a5
to
1d82702
Compare
322ed1c
to
691e561
Compare
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.
Reviewed 1 of 1 files at r16, 5 of 9 files at r18, 16 of 16 files at r19, all commit messages.
Reviewable status: all files reviewed, 22 unresolved discussions (waiting on @istae and @janos)
530190a
to
415ebc9
Compare
Codecov Report
@@ Coverage Diff @@
## master #2400 +/- ##
==========================================
- Coverage 63.38% 63.21% -0.18%
==========================================
Files 232 234 +2
Lines 25901 26264 +363
==========================================
+ Hits 16418 16602 +184
- Misses 7984 8143 +159
- Partials 1499 1519 +20
Continue to review full report at Codecov.
|
.github/workflows/beekeeper.yml
Outdated
@@ -14,7 +14,7 @@ jobs: | |||
REPLICA: 3 | |||
RUN_TYPE: "PR RUN" | |||
SETUP_CONTRACT_IMAGE_TAG: "0.2.0" | |||
BEEKEEPER_BRANCH: "master" | |||
BEEKEEPER_BRANCH: "restricted-2" |
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.
just a reminder to revert this before merging
.github/workflows/beekeeper.yml
Outdated
@@ -62,7 +62,7 @@ jobs: | |||
- name: Set local cluster | |||
run: | | |||
make beelocal ACTION=add-hosts | |||
timeout 10m make deploylocal BEEKEEPER_CLUSTER=local-dns | |||
timeout 10m make deploylocal BEEKEEPER_CLUSTER=local-dns BEEKEEPER_BRANCH=restricted-2 |
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.
and these too...
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.
Reviewed 3 of 8 files at r20, 1 of 2 files at r21, 2 of 2 files at r22, 2 of 2 files at r25, 2 of 3 files at r26, 2 of 2 files at r29, 1 of 1 files at r30, all commit messages.
Reviewable status: all files reviewed, 24 unresolved discussions (waiting on @istae, @janos, and @notanatol)
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.
Nice 👍. I have only one minor comment.
func (a *Authenticator) Enforce(apiKey, obj, act string) (bool, error) { | ||
decoded, err := base64.StdEncoding.DecodeString(apiKey) | ||
if err != nil { | ||
a.log.Error("decode token", err) |
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 would avoid both logging and returning errors in this function.
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'm going to reduce logging in a future PR but right now we need detailed information when certain cases occur, like token expiration.
586dec1
to
976e572
Compare
Kudos, SonarCloud Quality Gate passed!
|
RBAC for bee API and debug API.
It introduces a new command line argument
--restricted
that, if provided, would require obtaining a security token (via/auth
endpoint) and providing anAuthorization
header on every HTTP request to either of the APIs.A
/refresh
endpoint is being added to allow generation of new token based on the old one, without the need for credentials.The admin username and password should be provided as configuration parameters:
--admin-password
--token-encryption-key
The value of the
admin-password
has to be hashed using apache's bcrypt as described here: https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/In order to make it work please copy
policy.csv
andsecurity.conf
files (from the project root folder) to your home directory in the location:~/.config/bee
To get the token:
The received token is stateless and can be refreshed:
expiry
is in seconds from current time.This change is