You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

244 lines
8.6 KiB

7 years ago
7 years ago
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
3 years ago
4 years ago
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
3 years ago
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
3 years ago
5 years ago
2 years ago
  1. package weed_server
  2. import (
  3. "context"
  4. "fmt"
  5. "net/http"
  6. "os"
  7. "strings"
  8. "sync"
  9. "sync/atomic"
  10. "time"
  11. "github.com/seaweedfs/seaweedfs/weed/stats"
  12. "google.golang.org/grpc"
  13. "github.com/seaweedfs/seaweedfs/weed/util/grace"
  14. "github.com/seaweedfs/seaweedfs/weed/operation"
  15. "github.com/seaweedfs/seaweedfs/weed/pb"
  16. "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
  17. "github.com/seaweedfs/seaweedfs/weed/pb/master_pb"
  18. "github.com/seaweedfs/seaweedfs/weed/util"
  19. "github.com/seaweedfs/seaweedfs/weed/filer"
  20. _ "github.com/seaweedfs/seaweedfs/weed/filer/arangodb"
  21. _ "github.com/seaweedfs/seaweedfs/weed/filer/cassandra"
  22. _ "github.com/seaweedfs/seaweedfs/weed/filer/dameng"
  23. _ "github.com/seaweedfs/seaweedfs/weed/filer/elastic/v7"
  24. _ "github.com/seaweedfs/seaweedfs/weed/filer/etcd"
  25. _ "github.com/seaweedfs/seaweedfs/weed/filer/hbase"
  26. _ "github.com/seaweedfs/seaweedfs/weed/filer/leveldb"
  27. _ "github.com/seaweedfs/seaweedfs/weed/filer/leveldb2"
  28. _ "github.com/seaweedfs/seaweedfs/weed/filer/leveldb3"
  29. _ "github.com/seaweedfs/seaweedfs/weed/filer/mongodb"
  30. _ "github.com/seaweedfs/seaweedfs/weed/filer/mysql"
  31. _ "github.com/seaweedfs/seaweedfs/weed/filer/mysql2"
  32. _ "github.com/seaweedfs/seaweedfs/weed/filer/postgres"
  33. _ "github.com/seaweedfs/seaweedfs/weed/filer/postgres2"
  34. _ "github.com/seaweedfs/seaweedfs/weed/filer/redis"
  35. _ "github.com/seaweedfs/seaweedfs/weed/filer/redis2"
  36. _ "github.com/seaweedfs/seaweedfs/weed/filer/redis3"
  37. _ "github.com/seaweedfs/seaweedfs/weed/filer/sqlite"
  38. _ "github.com/seaweedfs/seaweedfs/weed/filer/ydb"
  39. "github.com/seaweedfs/seaweedfs/weed/glog"
  40. "github.com/seaweedfs/seaweedfs/weed/notification"
  41. _ "github.com/seaweedfs/seaweedfs/weed/notification/aws_sqs"
  42. _ "github.com/seaweedfs/seaweedfs/weed/notification/gocdk_pub_sub"
  43. _ "github.com/seaweedfs/seaweedfs/weed/notification/google_pub_sub"
  44. _ "github.com/seaweedfs/seaweedfs/weed/notification/kafka"
  45. _ "github.com/seaweedfs/seaweedfs/weed/notification/log"
  46. "github.com/seaweedfs/seaweedfs/weed/security"
  47. )
  48. type FilerOption struct {
  49. Masters *pb.ServerDiscovery
  50. FilerGroup string
  51. Collection string
  52. DefaultReplication string
  53. DisableDirListing bool
  54. MaxMB int
  55. DirListingLimit int
  56. DataCenter string
  57. Rack string
  58. DataNode string
  59. DefaultLevelDbDir string
  60. DisableHttp bool
  61. Host pb.ServerAddress
  62. recursiveDelete bool
  63. Cipher bool
  64. SaveToFilerLimit int64
  65. ConcurrentUploadLimit int64
  66. ShowUIDirectoryDelete bool
  67. DownloadMaxBytesPs int64
  68. DiskType string
  69. AllowedOrigins []string
  70. ExposeDirectoryData bool
  71. }
  72. type FilerServer struct {
  73. inFlightDataSize int64
  74. listenersWaits int64
  75. // notifying clients
  76. listenersLock sync.Mutex
  77. listenersCond *sync.Cond
  78. inFlightDataLimitCond *sync.Cond
  79. filer_pb.UnimplementedSeaweedFilerServer
  80. option *FilerOption
  81. secret security.SigningKey
  82. filer *filer.Filer
  83. filerGuard *security.Guard
  84. volumeGuard *security.Guard
  85. grpcDialOption grpc.DialOption
  86. // metrics read from the master
  87. metricsAddress string
  88. metricsIntervalSec int
  89. // track known metadata listeners
  90. knownListenersLock sync.Mutex
  91. knownListeners map[int32]int32
  92. }
  93. func NewFilerServer(defaultMux, readonlyMux *http.ServeMux, option *FilerOption) (fs *FilerServer, err error) {
  94. v := util.GetViper()
  95. signingKey := v.GetString("jwt.filer_signing.key")
  96. v.SetDefault("jwt.filer_signing.expires_after_seconds", 10)
  97. expiresAfterSec := v.GetInt("jwt.filer_signing.expires_after_seconds")
  98. readSigningKey := v.GetString("jwt.filer_signing.read.key")
  99. v.SetDefault("jwt.filer_signing.read.expires_after_seconds", 60)
  100. readExpiresAfterSec := v.GetInt("jwt.filer_signing.read.expires_after_seconds")
  101. volumeSigningKey := v.GetString("jwt.signing.key")
  102. v.SetDefault("jwt.signing.expires_after_seconds", 10)
  103. volumeExpiresAfterSec := v.GetInt("jwt.signing.expires_after_seconds")
  104. volumeReadSigningKey := v.GetString("jwt.signing.read.key")
  105. v.SetDefault("jwt.signing.read.expires_after_seconds", 60)
  106. volumeReadExpiresAfterSec := v.GetInt("jwt.signing.read.expires_after_seconds")
  107. v.SetDefault("cors.allowed_origins.values", "*")
  108. allowedOrigins := v.GetString("cors.allowed_origins.values")
  109. domains := strings.Split(allowedOrigins, ",")
  110. option.AllowedOrigins = domains
  111. v.SetDefault("filer.expose_directory_metadata.enabled", true)
  112. returnDirMetadata := v.GetBool("filer.expose_directory_metadata.enabled")
  113. option.ExposeDirectoryData = returnDirMetadata
  114. fs = &FilerServer{
  115. option: option,
  116. grpcDialOption: security.LoadClientTLS(util.GetViper(), "grpc.filer"),
  117. knownListeners: make(map[int32]int32),
  118. inFlightDataLimitCond: sync.NewCond(new(sync.Mutex)),
  119. }
  120. fs.listenersCond = sync.NewCond(&fs.listenersLock)
  121. option.Masters.RefreshBySrvIfAvailable()
  122. if len(option.Masters.GetInstances()) == 0 {
  123. glog.Fatal("master list is required!")
  124. }
  125. v.SetDefault("filer.options.max_file_name_length", 255)
  126. maxFilenameLength := v.GetUint32("filer.options.max_file_name_length")
  127. fs.filer = filer.NewFiler(*option.Masters, fs.grpcDialOption, option.Host, option.FilerGroup, option.Collection, option.DefaultReplication, option.DataCenter, maxFilenameLength, func() {
  128. if atomic.LoadInt64(&fs.listenersWaits) > 0 {
  129. fs.listenersCond.Broadcast()
  130. }
  131. })
  132. fs.filer.Cipher = option.Cipher
  133. // we do not support IP whitelist right now
  134. fs.filerGuard = security.NewGuard([]string{}, signingKey, expiresAfterSec, readSigningKey, readExpiresAfterSec)
  135. fs.volumeGuard = security.NewGuard([]string{}, volumeSigningKey, volumeExpiresAfterSec, volumeReadSigningKey, volumeReadExpiresAfterSec)
  136. fs.checkWithMaster()
  137. go stats.LoopPushingMetric("filer", string(fs.option.Host), fs.metricsAddress, fs.metricsIntervalSec)
  138. go fs.filer.KeepMasterClientConnected(context.Background())
  139. if !util.LoadConfiguration("filer", false) {
  140. v.SetDefault("leveldb2.enabled", true)
  141. v.SetDefault("leveldb2.dir", option.DefaultLevelDbDir)
  142. _, err := os.Stat(option.DefaultLevelDbDir)
  143. if os.IsNotExist(err) {
  144. os.MkdirAll(option.DefaultLevelDbDir, 0755)
  145. }
  146. glog.V(0).Infof("default to create filer store dir in %s", option.DefaultLevelDbDir)
  147. } else {
  148. glog.Warningf("skipping default store dir in %s", option.DefaultLevelDbDir)
  149. }
  150. util.LoadConfiguration("notification", false)
  151. fs.option.recursiveDelete = v.GetBool("filer.options.recursive_delete")
  152. v.SetDefault("filer.options.buckets_folder", "/buckets")
  153. fs.filer.DirBucketsPath = v.GetString("filer.options.buckets_folder")
  154. // TODO deprecated, will be removed after 2020-12-31
  155. // replaced by https://github.com/seaweedfs/seaweedfs/wiki/Path-Specific-Configuration
  156. // fs.filer.FsyncBuckets = v.GetStringSlice("filer.options.buckets_fsync")
  157. isFresh := fs.filer.LoadConfiguration(v)
  158. notification.LoadConfiguration(v, "notification.")
  159. handleStaticResources(defaultMux)
  160. if !option.DisableHttp {
  161. defaultMux.HandleFunc("/healthz", fs.filerHealthzHandler)
  162. defaultMux.HandleFunc("/", fs.filerHandler)
  163. }
  164. if defaultMux != readonlyMux {
  165. handleStaticResources(readonlyMux)
  166. readonlyMux.HandleFunc("/healthz", fs.filerHealthzHandler)
  167. readonlyMux.HandleFunc("/", fs.readonlyFilerHandler)
  168. }
  169. existingNodes := fs.filer.ListExistingPeerUpdates(context.Background())
  170. startFromTime := time.Now().Add(-filer.LogFlushInterval)
  171. if isFresh {
  172. glog.V(0).Infof("%s bootstrap from peers %+v", option.Host, existingNodes)
  173. if err := fs.filer.MaybeBootstrapFromOnePeer(option.Host, existingNodes, startFromTime); err != nil {
  174. glog.Fatalf("%s bootstrap from %+v: %v", option.Host, existingNodes, err)
  175. }
  176. }
  177. fs.filer.AggregateFromPeers(option.Host, existingNodes, startFromTime)
  178. fs.filer.LoadFilerConf()
  179. fs.filer.LoadRemoteStorageConfAndMapping()
  180. grace.OnInterrupt(func() {
  181. fs.filer.Shutdown()
  182. })
  183. fs.filer.Dlm.LockRing.SetTakeSnapshotCallback(fs.OnDlmChangeSnapshot)
  184. return fs, nil
  185. }
  186. func (fs *FilerServer) checkWithMaster() {
  187. isConnected := false
  188. for !isConnected {
  189. fs.option.Masters.RefreshBySrvIfAvailable()
  190. for _, master := range fs.option.Masters.GetInstances() {
  191. readErr := operation.WithMasterServerClient(false, master, fs.grpcDialOption, func(masterClient master_pb.SeaweedClient) error {
  192. resp, err := masterClient.GetMasterConfiguration(context.Background(), &master_pb.GetMasterConfigurationRequest{})
  193. if err != nil {
  194. return fmt.Errorf("get master %s configuration: %v", master, err)
  195. }
  196. fs.metricsAddress, fs.metricsIntervalSec = resp.MetricsAddress, int(resp.MetricsIntervalSeconds)
  197. return nil
  198. })
  199. if readErr == nil {
  200. isConnected = true
  201. } else {
  202. time.Sleep(7 * time.Second)
  203. }
  204. }
  205. }
  206. }