2015-02-07 23:35:28 +00:00
|
|
|
package security
|
|
|
|
|
|
|
|
import (
|
2019-02-14 08:08:20 +00:00
|
|
|
"fmt"
|
2015-02-07 23:35:28 +00:00
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2023-08-22 15:26:31 +00:00
|
|
|
jwt "github.com/golang-jwt/jwt/v5"
|
2022-07-29 07:17:28 +00:00
|
|
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
2015-02-07 23:35:28 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type EncodedJwt string
|
2019-02-14 08:08:20 +00:00
|
|
|
type SigningKey []byte
|
|
|
|
|
2021-12-29 11:39:41 +00:00
|
|
|
// SeaweedFileIdClaims is created by Master server(s) and consumed by Volume server(s),
|
|
|
|
// restricting the access this JWT allows to only a single file.
|
2019-02-14 08:08:20 +00:00
|
|
|
type SeaweedFileIdClaims struct {
|
|
|
|
Fid string `json:"fid"`
|
2023-08-22 06:42:39 +00:00
|
|
|
jwt.RegisteredClaims
|
2019-02-14 08:08:20 +00:00
|
|
|
}
|
2015-02-07 23:35:28 +00:00
|
|
|
|
FEATURE: add JWT to HTTP endpoints of Filer and use them in S3 Client
- one JWT for reading and one for writing, analogous to how the JWT
between Master and Volume Server works
- I did not implement IP `whiteList` parameter on the filer
Additionally, because http_util.DownloadFile now sets the JWT,
the `download` command should now work when `jwt.signing.read` is
configured. By looking at the code, I think this case did not work
before.
## Docs to be adjusted after a release
Page `Amazon-S3-API`:
```
# Authentication with Filer
You can use mTLS for the gRPC connection between S3-API-Proxy and the filer, as
explained in [Security-Configuration](Security-Configuration) -
controlled by the `grpc.*` configuration in `security.toml`.
Starting with version XX, it is also possible to authenticate the HTTP
operations between the S3-API-Proxy and the Filer (especially
uploading new files). This is configured by setting
`filer_jwt.signing.key` and `filer_jwt.signing.read.key` in
`security.toml`.
With both configurations (gRPC and JWT), it is possible to have Filer
and S3 communicate in fully authenticated fashion; so Filer will reject
any unauthenticated communication.
```
Page `Security Overview`:
```
The following items are not covered, yet:
- master server http REST services
Starting with version XX, the Filer HTTP REST services can be secured
with a JWT, by setting `filer_jwt.signing.key` and
`filer_jwt.signing.read.key` in `security.toml`.
...
Before version XX: "weed filer -disableHttp", disable http operations, only gRPC operations are allowed. This works with "weed mount" by FUSE. It does **not work** with the [S3 Gateway](Amazon S3 API), as this does HTTP calls to the Filer.
Starting with version XX: secured by JWT, by setting `filer_jwt.signing.key` and `filer_jwt.signing.read.key` in `security.toml`. **This now works with the [S3 Gateway](Amazon S3 API).**
...
# Securing Filer HTTP with JWT
To enable JWT-based access control for the Filer,
1. generate `security.toml` file by `weed scaffold -config=security`
2. set `filer_jwt.signing.key` to a secret string - and optionally filer_jwt.signing.read.key` as well to a secret string
3. copy the same `security.toml` file to the filers and all S3 proxies.
If `filer_jwt.signing.key` is configured: When sending upload/update/delete HTTP operations to a filer server, the request header `Authorization` should be the JWT string (`Authorization: Bearer [JwtToken]`). The operation is authorized after the filer validates the JWT with `filer_jwt.signing.key`.
If `filer_jwt.signing.read.key` is configured: When sending GET or HEAD requests to a filer server, the request header `Authorization` should be the JWT string (`Authorization: Bearer [JwtToken]`). The operation is authorized after the filer validates the JWT with `filer_jwt.signing.read.key`.
The S3 API Gateway reads the above JWT keys and sends authenticated
HTTP requests to the filer.
```
Page `Security Configuration`:
```
(update scaffold file)
...
[filer_jwt.signing]
key = "blahblahblahblah"
[filer_jwt.signing.read]
key = "blahblahblahblah"
```
Resolves: #158
2021-12-29 18:47:53 +00:00
|
|
|
// SeaweedFilerClaims is created e.g. by S3 proxy server and consumed by Filer server.
|
|
|
|
// Right now, it only contains the standard claims; but this might be extended later
|
|
|
|
// for more fine-grained permissions.
|
|
|
|
type SeaweedFilerClaims struct {
|
2023-08-22 06:42:39 +00:00
|
|
|
jwt.RegisteredClaims
|
FEATURE: add JWT to HTTP endpoints of Filer and use them in S3 Client
- one JWT for reading and one for writing, analogous to how the JWT
between Master and Volume Server works
- I did not implement IP `whiteList` parameter on the filer
Additionally, because http_util.DownloadFile now sets the JWT,
the `download` command should now work when `jwt.signing.read` is
configured. By looking at the code, I think this case did not work
before.
## Docs to be adjusted after a release
Page `Amazon-S3-API`:
```
# Authentication with Filer
You can use mTLS for the gRPC connection between S3-API-Proxy and the filer, as
explained in [Security-Configuration](Security-Configuration) -
controlled by the `grpc.*` configuration in `security.toml`.
Starting with version XX, it is also possible to authenticate the HTTP
operations between the S3-API-Proxy and the Filer (especially
uploading new files). This is configured by setting
`filer_jwt.signing.key` and `filer_jwt.signing.read.key` in
`security.toml`.
With both configurations (gRPC and JWT), it is possible to have Filer
and S3 communicate in fully authenticated fashion; so Filer will reject
any unauthenticated communication.
```
Page `Security Overview`:
```
The following items are not covered, yet:
- master server http REST services
Starting with version XX, the Filer HTTP REST services can be secured
with a JWT, by setting `filer_jwt.signing.key` and
`filer_jwt.signing.read.key` in `security.toml`.
...
Before version XX: "weed filer -disableHttp", disable http operations, only gRPC operations are allowed. This works with "weed mount" by FUSE. It does **not work** with the [S3 Gateway](Amazon S3 API), as this does HTTP calls to the Filer.
Starting with version XX: secured by JWT, by setting `filer_jwt.signing.key` and `filer_jwt.signing.read.key` in `security.toml`. **This now works with the [S3 Gateway](Amazon S3 API).**
...
# Securing Filer HTTP with JWT
To enable JWT-based access control for the Filer,
1. generate `security.toml` file by `weed scaffold -config=security`
2. set `filer_jwt.signing.key` to a secret string - and optionally filer_jwt.signing.read.key` as well to a secret string
3. copy the same `security.toml` file to the filers and all S3 proxies.
If `filer_jwt.signing.key` is configured: When sending upload/update/delete HTTP operations to a filer server, the request header `Authorization` should be the JWT string (`Authorization: Bearer [JwtToken]`). The operation is authorized after the filer validates the JWT with `filer_jwt.signing.key`.
If `filer_jwt.signing.read.key` is configured: When sending GET or HEAD requests to a filer server, the request header `Authorization` should be the JWT string (`Authorization: Bearer [JwtToken]`). The operation is authorized after the filer validates the JWT with `filer_jwt.signing.read.key`.
The S3 API Gateway reads the above JWT keys and sends authenticated
HTTP requests to the filer.
```
Page `Security Configuration`:
```
(update scaffold file)
...
[filer_jwt.signing]
key = "blahblahblahblah"
[filer_jwt.signing.read]
key = "blahblahblahblah"
```
Resolves: #158
2021-12-29 18:47:53 +00:00
|
|
|
}
|
|
|
|
|
2021-12-29 11:39:41 +00:00
|
|
|
func GenJwtForVolumeServer(signingKey SigningKey, expiresAfterSec int, fileId string) EncodedJwt {
|
2019-02-14 08:08:20 +00:00
|
|
|
if len(signingKey) == 0 {
|
2015-02-07 23:35:28 +00:00
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2019-02-14 08:08:20 +00:00
|
|
|
claims := SeaweedFileIdClaims{
|
|
|
|
fileId,
|
2023-08-22 06:42:39 +00:00
|
|
|
jwt.RegisteredClaims{},
|
2019-05-04 15:42:25 +00:00
|
|
|
}
|
|
|
|
if expiresAfterSec > 0 {
|
2023-08-22 06:42:39 +00:00
|
|
|
claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Second * time.Duration(expiresAfterSec)))
|
FEATURE: add JWT to HTTP endpoints of Filer and use them in S3 Client
- one JWT for reading and one for writing, analogous to how the JWT
between Master and Volume Server works
- I did not implement IP `whiteList` parameter on the filer
Additionally, because http_util.DownloadFile now sets the JWT,
the `download` command should now work when `jwt.signing.read` is
configured. By looking at the code, I think this case did not work
before.
## Docs to be adjusted after a release
Page `Amazon-S3-API`:
```
# Authentication with Filer
You can use mTLS for the gRPC connection between S3-API-Proxy and the filer, as
explained in [Security-Configuration](Security-Configuration) -
controlled by the `grpc.*` configuration in `security.toml`.
Starting with version XX, it is also possible to authenticate the HTTP
operations between the S3-API-Proxy and the Filer (especially
uploading new files). This is configured by setting
`filer_jwt.signing.key` and `filer_jwt.signing.read.key` in
`security.toml`.
With both configurations (gRPC and JWT), it is possible to have Filer
and S3 communicate in fully authenticated fashion; so Filer will reject
any unauthenticated communication.
```
Page `Security Overview`:
```
The following items are not covered, yet:
- master server http REST services
Starting with version XX, the Filer HTTP REST services can be secured
with a JWT, by setting `filer_jwt.signing.key` and
`filer_jwt.signing.read.key` in `security.toml`.
...
Before version XX: "weed filer -disableHttp", disable http operations, only gRPC operations are allowed. This works with "weed mount" by FUSE. It does **not work** with the [S3 Gateway](Amazon S3 API), as this does HTTP calls to the Filer.
Starting with version XX: secured by JWT, by setting `filer_jwt.signing.key` and `filer_jwt.signing.read.key` in `security.toml`. **This now works with the [S3 Gateway](Amazon S3 API).**
...
# Securing Filer HTTP with JWT
To enable JWT-based access control for the Filer,
1. generate `security.toml` file by `weed scaffold -config=security`
2. set `filer_jwt.signing.key` to a secret string - and optionally filer_jwt.signing.read.key` as well to a secret string
3. copy the same `security.toml` file to the filers and all S3 proxies.
If `filer_jwt.signing.key` is configured: When sending upload/update/delete HTTP operations to a filer server, the request header `Authorization` should be the JWT string (`Authorization: Bearer [JwtToken]`). The operation is authorized after the filer validates the JWT with `filer_jwt.signing.key`.
If `filer_jwt.signing.read.key` is configured: When sending GET or HEAD requests to a filer server, the request header `Authorization` should be the JWT string (`Authorization: Bearer [JwtToken]`). The operation is authorized after the filer validates the JWT with `filer_jwt.signing.read.key`.
The S3 API Gateway reads the above JWT keys and sends authenticated
HTTP requests to the filer.
```
Page `Security Configuration`:
```
(update scaffold file)
...
[filer_jwt.signing]
key = "blahblahblahblah"
[filer_jwt.signing.read]
key = "blahblahblahblah"
```
Resolves: #158
2021-12-29 18:47:53 +00:00
|
|
|
}
|
|
|
|
t := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
|
|
encoded, e := t.SignedString([]byte(signingKey))
|
|
|
|
if e != nil {
|
|
|
|
glog.V(0).Infof("Failed to sign claims %+v: %v", t.Claims, e)
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return EncodedJwt(encoded)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GenJwtForFilerServer creates a JSON-web-token for using the authenticated Filer API. Used f.e. inside
|
|
|
|
// the S3 API
|
|
|
|
func GenJwtForFilerServer(signingKey SigningKey, expiresAfterSec int) EncodedJwt {
|
|
|
|
if len(signingKey) == 0 {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
claims := SeaweedFilerClaims{
|
2023-08-22 06:42:39 +00:00
|
|
|
jwt.RegisteredClaims{},
|
FEATURE: add JWT to HTTP endpoints of Filer and use them in S3 Client
- one JWT for reading and one for writing, analogous to how the JWT
between Master and Volume Server works
- I did not implement IP `whiteList` parameter on the filer
Additionally, because http_util.DownloadFile now sets the JWT,
the `download` command should now work when `jwt.signing.read` is
configured. By looking at the code, I think this case did not work
before.
## Docs to be adjusted after a release
Page `Amazon-S3-API`:
```
# Authentication with Filer
You can use mTLS for the gRPC connection between S3-API-Proxy and the filer, as
explained in [Security-Configuration](Security-Configuration) -
controlled by the `grpc.*` configuration in `security.toml`.
Starting with version XX, it is also possible to authenticate the HTTP
operations between the S3-API-Proxy and the Filer (especially
uploading new files). This is configured by setting
`filer_jwt.signing.key` and `filer_jwt.signing.read.key` in
`security.toml`.
With both configurations (gRPC and JWT), it is possible to have Filer
and S3 communicate in fully authenticated fashion; so Filer will reject
any unauthenticated communication.
```
Page `Security Overview`:
```
The following items are not covered, yet:
- master server http REST services
Starting with version XX, the Filer HTTP REST services can be secured
with a JWT, by setting `filer_jwt.signing.key` and
`filer_jwt.signing.read.key` in `security.toml`.
...
Before version XX: "weed filer -disableHttp", disable http operations, only gRPC operations are allowed. This works with "weed mount" by FUSE. It does **not work** with the [S3 Gateway](Amazon S3 API), as this does HTTP calls to the Filer.
Starting with version XX: secured by JWT, by setting `filer_jwt.signing.key` and `filer_jwt.signing.read.key` in `security.toml`. **This now works with the [S3 Gateway](Amazon S3 API).**
...
# Securing Filer HTTP with JWT
To enable JWT-based access control for the Filer,
1. generate `security.toml` file by `weed scaffold -config=security`
2. set `filer_jwt.signing.key` to a secret string - and optionally filer_jwt.signing.read.key` as well to a secret string
3. copy the same `security.toml` file to the filers and all S3 proxies.
If `filer_jwt.signing.key` is configured: When sending upload/update/delete HTTP operations to a filer server, the request header `Authorization` should be the JWT string (`Authorization: Bearer [JwtToken]`). The operation is authorized after the filer validates the JWT with `filer_jwt.signing.key`.
If `filer_jwt.signing.read.key` is configured: When sending GET or HEAD requests to a filer server, the request header `Authorization` should be the JWT string (`Authorization: Bearer [JwtToken]`). The operation is authorized after the filer validates the JWT with `filer_jwt.signing.read.key`.
The S3 API Gateway reads the above JWT keys and sends authenticated
HTTP requests to the filer.
```
Page `Security Configuration`:
```
(update scaffold file)
...
[filer_jwt.signing]
key = "blahblahblahblah"
[filer_jwt.signing.read]
key = "blahblahblahblah"
```
Resolves: #158
2021-12-29 18:47:53 +00:00
|
|
|
}
|
|
|
|
if expiresAfterSec > 0 {
|
2023-08-22 06:42:39 +00:00
|
|
|
claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Second * time.Duration(expiresAfterSec)))
|
2016-06-19 01:57:33 +00:00
|
|
|
}
|
2019-02-14 08:08:20 +00:00
|
|
|
t := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
|
|
encoded, e := t.SignedString([]byte(signingKey))
|
2015-02-07 23:35:28 +00:00
|
|
|
if e != nil {
|
2019-02-14 08:08:20 +00:00
|
|
|
glog.V(0).Infof("Failed to sign claims %+v: %v", t.Claims, e)
|
2015-02-07 23:35:28 +00:00
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return EncodedJwt(encoded)
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetJwt(r *http.Request) EncodedJwt {
|
|
|
|
|
|
|
|
// Get token from query params
|
|
|
|
tokenStr := r.URL.Query().Get("jwt")
|
|
|
|
|
|
|
|
// Get token from authorization header
|
|
|
|
if tokenStr == "" {
|
|
|
|
bearer := r.Header.Get("Authorization")
|
|
|
|
if len(bearer) > 7 && strings.ToUpper(bearer[0:6]) == "BEARER" {
|
|
|
|
tokenStr = bearer[7:]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-04 20:02:45 +00:00
|
|
|
// Get token from http only cookie
|
|
|
|
if tokenStr == "" {
|
|
|
|
token, err := r.Cookie("AT")
|
|
|
|
if err == nil {
|
|
|
|
tokenStr = token.Value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-07 23:35:28 +00:00
|
|
|
return EncodedJwt(tokenStr)
|
|
|
|
}
|
|
|
|
|
2021-12-29 11:40:41 +00:00
|
|
|
func DecodeJwt(signingKey SigningKey, tokenString EncodedJwt, claims jwt.Claims) (token *jwt.Token, err error) {
|
2015-02-07 23:35:28 +00:00
|
|
|
// check exp, nbf
|
2021-12-29 11:40:41 +00:00
|
|
|
return jwt.ParseWithClaims(string(tokenString), claims, func(token *jwt.Token) (interface{}, error) {
|
2019-02-14 08:08:20 +00:00
|
|
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
|
|
|
return nil, fmt.Errorf("unknown token method")
|
|
|
|
}
|
2019-02-15 08:09:19 +00:00
|
|
|
return []byte(signingKey), nil
|
2015-02-07 23:35:28 +00:00
|
|
|
})
|
|
|
|
}
|