filesystem.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. // Copyright (C) 2019 Nicola Murino
  2. //
  3. // This program is free software: you can redistribute it and/or modify
  4. // it under the terms of the GNU Affero General Public License as published
  5. // by the Free Software Foundation, version 3.
  6. //
  7. // This program is distributed in the hope that it will be useful,
  8. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. // GNU Affero General Public License for more details.
  11. //
  12. // You should have received a copy of the GNU Affero General Public License
  13. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  14. package vfs
  15. import (
  16. "os"
  17. "slices"
  18. "github.com/sftpgo/sdk"
  19. "github.com/drakkan/sftpgo/v2/internal/kms"
  20. "github.com/drakkan/sftpgo/v2/internal/util"
  21. )
  22. var (
  23. disabledFilesystemProviders []sdk.FilesystemProvider
  24. loadDisabledProvidersFn func()
  25. )
  26. // Filesystem defines filesystem details
  27. type Filesystem struct {
  28. RedactedSecret string `json:"-"`
  29. Provider sdk.FilesystemProvider `json:"provider"`
  30. OSConfig sdk.OSFsConfig `json:"osconfig,omitempty"`
  31. S3Config S3FsConfig `json:"s3config,omitempty"`
  32. GCSConfig GCSFsConfig `json:"gcsconfig,omitempty"`
  33. AzBlobConfig AzBlobFsConfig `json:"azblobconfig,omitempty"`
  34. CryptConfig CryptFsConfig `json:"cryptconfig,omitempty"`
  35. SFTPConfig SFTPFsConfig `json:"sftpconfig,omitempty"`
  36. HTTPConfig HTTPFsConfig `json:"httpconfig,omitempty"`
  37. }
  38. // SetEmptySecrets sets the secrets to empty
  39. func (f *Filesystem) SetEmptySecrets() {
  40. f.S3Config.AccessSecret = kms.NewEmptySecret()
  41. f.S3Config.SSECustomerKey = kms.NewEmptySecret()
  42. f.GCSConfig.Credentials = kms.NewEmptySecret()
  43. f.AzBlobConfig.AccountKey = kms.NewEmptySecret()
  44. f.AzBlobConfig.SASURL = kms.NewEmptySecret()
  45. f.CryptConfig.Passphrase = kms.NewEmptySecret()
  46. f.SFTPConfig.Password = kms.NewEmptySecret()
  47. f.SFTPConfig.PrivateKey = kms.NewEmptySecret()
  48. f.SFTPConfig.KeyPassphrase = kms.NewEmptySecret()
  49. f.HTTPConfig.Password = kms.NewEmptySecret()
  50. f.HTTPConfig.APIKey = kms.NewEmptySecret()
  51. }
  52. // SetEmptySecretsIfNil sets the secrets to empty if nil
  53. func (f *Filesystem) SetEmptySecretsIfNil() {
  54. if f.S3Config.AccessSecret == nil {
  55. f.S3Config.AccessSecret = kms.NewEmptySecret()
  56. }
  57. if f.S3Config.SSECustomerKey == nil {
  58. f.S3Config.SSECustomerKey = kms.NewEmptySecret()
  59. }
  60. if f.GCSConfig.Credentials == nil {
  61. f.GCSConfig.Credentials = kms.NewEmptySecret()
  62. }
  63. if f.AzBlobConfig.AccountKey == nil {
  64. f.AzBlobConfig.AccountKey = kms.NewEmptySecret()
  65. }
  66. if f.AzBlobConfig.SASURL == nil {
  67. f.AzBlobConfig.SASURL = kms.NewEmptySecret()
  68. }
  69. if f.CryptConfig.Passphrase == nil {
  70. f.CryptConfig.Passphrase = kms.NewEmptySecret()
  71. }
  72. if f.SFTPConfig.Password == nil {
  73. f.SFTPConfig.Password = kms.NewEmptySecret()
  74. }
  75. if f.SFTPConfig.PrivateKey == nil {
  76. f.SFTPConfig.PrivateKey = kms.NewEmptySecret()
  77. }
  78. if f.SFTPConfig.KeyPassphrase == nil {
  79. f.SFTPConfig.KeyPassphrase = kms.NewEmptySecret()
  80. }
  81. if f.HTTPConfig.Password == nil {
  82. f.HTTPConfig.Password = kms.NewEmptySecret()
  83. }
  84. if f.HTTPConfig.APIKey == nil {
  85. f.HTTPConfig.APIKey = kms.NewEmptySecret()
  86. }
  87. }
  88. // SetNilSecretsIfEmpty set the secrets to nil if empty.
  89. // This is useful before rendering as JSON so the empty fields
  90. // will not be serialized.
  91. func (f *Filesystem) SetNilSecretsIfEmpty() {
  92. if f.S3Config.AccessSecret != nil && f.S3Config.AccessSecret.IsEmpty() {
  93. f.S3Config.AccessSecret = nil
  94. }
  95. if f.S3Config.SSECustomerKey != nil && f.S3Config.SSECustomerKey.IsEmpty() {
  96. f.S3Config.SSECustomerKey = nil
  97. }
  98. if f.GCSConfig.Credentials != nil && f.GCSConfig.Credentials.IsEmpty() {
  99. f.GCSConfig.Credentials = nil
  100. }
  101. if f.AzBlobConfig.AccountKey != nil && f.AzBlobConfig.AccountKey.IsEmpty() {
  102. f.AzBlobConfig.AccountKey = nil
  103. }
  104. if f.AzBlobConfig.SASURL != nil && f.AzBlobConfig.SASURL.IsEmpty() {
  105. f.AzBlobConfig.SASURL = nil
  106. }
  107. if f.CryptConfig.Passphrase != nil && f.CryptConfig.Passphrase.IsEmpty() {
  108. f.CryptConfig.Passphrase = nil
  109. }
  110. f.SFTPConfig.setNilSecretsIfEmpty()
  111. f.HTTPConfig.setNilSecretsIfEmpty()
  112. }
  113. // IsEqual returns true if the fs is equal to other
  114. func (f *Filesystem) IsEqual(other Filesystem) bool {
  115. if f.Provider != other.Provider {
  116. return false
  117. }
  118. switch f.Provider {
  119. case sdk.S3FilesystemProvider:
  120. return f.S3Config.isEqual(other.S3Config)
  121. case sdk.GCSFilesystemProvider:
  122. return f.GCSConfig.isEqual(other.GCSConfig)
  123. case sdk.AzureBlobFilesystemProvider:
  124. return f.AzBlobConfig.isEqual(other.AzBlobConfig)
  125. case sdk.CryptedFilesystemProvider:
  126. return f.CryptConfig.isEqual(other.CryptConfig)
  127. case sdk.SFTPFilesystemProvider:
  128. return f.SFTPConfig.isEqual(other.SFTPConfig)
  129. case sdk.HTTPFilesystemProvider:
  130. return f.HTTPConfig.isEqual(other.HTTPConfig)
  131. default:
  132. return true
  133. }
  134. }
  135. // IsSameResource returns true if fs point to the same resource as other
  136. func (f *Filesystem) IsSameResource(other Filesystem) bool {
  137. if f.Provider != other.Provider {
  138. return false
  139. }
  140. switch f.Provider {
  141. case sdk.S3FilesystemProvider:
  142. return f.S3Config.isSameResource(other.S3Config)
  143. case sdk.GCSFilesystemProvider:
  144. return f.GCSConfig.isSameResource(other.GCSConfig)
  145. case sdk.AzureBlobFilesystemProvider:
  146. return f.AzBlobConfig.isSameResource(other.AzBlobConfig)
  147. case sdk.CryptedFilesystemProvider:
  148. return f.CryptConfig.isSameResource(other.CryptConfig)
  149. case sdk.SFTPFilesystemProvider:
  150. return f.SFTPConfig.isSameResource(other.SFTPConfig)
  151. case sdk.HTTPFilesystemProvider:
  152. return f.HTTPConfig.isSameResource(other.HTTPConfig)
  153. default:
  154. return true
  155. }
  156. }
  157. // GetPathSeparator returns the path separator
  158. func (f *Filesystem) GetPathSeparator() string {
  159. switch f.Provider {
  160. case sdk.LocalFilesystemProvider, sdk.CryptedFilesystemProvider:
  161. return string(os.PathSeparator)
  162. default:
  163. return "/"
  164. }
  165. }
  166. // Validate verifies the FsConfig matching the configured provider and sets all other
  167. // Filesystem.*Config to their zero value if successful
  168. func (f *Filesystem) Validate(additionalData string) error {
  169. switch f.Provider {
  170. case sdk.S3FilesystemProvider:
  171. if err := f.S3Config.ValidateAndEncryptCredentials(additionalData); err != nil {
  172. return err
  173. }
  174. f.OSConfig = sdk.OSFsConfig{}
  175. f.GCSConfig = GCSFsConfig{}
  176. f.AzBlobConfig = AzBlobFsConfig{}
  177. f.CryptConfig = CryptFsConfig{}
  178. f.SFTPConfig = SFTPFsConfig{}
  179. f.HTTPConfig = HTTPFsConfig{}
  180. return nil
  181. case sdk.GCSFilesystemProvider:
  182. if err := f.GCSConfig.ValidateAndEncryptCredentials(additionalData); err != nil {
  183. return err
  184. }
  185. f.OSConfig = sdk.OSFsConfig{}
  186. f.S3Config = S3FsConfig{}
  187. f.AzBlobConfig = AzBlobFsConfig{}
  188. f.CryptConfig = CryptFsConfig{}
  189. f.SFTPConfig = SFTPFsConfig{}
  190. f.HTTPConfig = HTTPFsConfig{}
  191. return nil
  192. case sdk.AzureBlobFilesystemProvider:
  193. if err := f.AzBlobConfig.ValidateAndEncryptCredentials(additionalData); err != nil {
  194. return err
  195. }
  196. f.OSConfig = sdk.OSFsConfig{}
  197. f.S3Config = S3FsConfig{}
  198. f.GCSConfig = GCSFsConfig{}
  199. f.CryptConfig = CryptFsConfig{}
  200. f.SFTPConfig = SFTPFsConfig{}
  201. f.HTTPConfig = HTTPFsConfig{}
  202. return nil
  203. case sdk.CryptedFilesystemProvider:
  204. if err := f.CryptConfig.ValidateAndEncryptCredentials(additionalData); err != nil {
  205. return err
  206. }
  207. f.OSConfig = sdk.OSFsConfig{}
  208. f.S3Config = S3FsConfig{}
  209. f.GCSConfig = GCSFsConfig{}
  210. f.AzBlobConfig = AzBlobFsConfig{}
  211. f.SFTPConfig = SFTPFsConfig{}
  212. f.HTTPConfig = HTTPFsConfig{}
  213. return validateOSFsConfig(&f.CryptConfig.OSFsConfig)
  214. case sdk.SFTPFilesystemProvider:
  215. if err := f.SFTPConfig.ValidateAndEncryptCredentials(additionalData); err != nil {
  216. return err
  217. }
  218. f.OSConfig = sdk.OSFsConfig{}
  219. f.S3Config = S3FsConfig{}
  220. f.GCSConfig = GCSFsConfig{}
  221. f.AzBlobConfig = AzBlobFsConfig{}
  222. f.CryptConfig = CryptFsConfig{}
  223. f.HTTPConfig = HTTPFsConfig{}
  224. return nil
  225. case sdk.HTTPFilesystemProvider:
  226. if err := f.HTTPConfig.ValidateAndEncryptCredentials(additionalData); err != nil {
  227. return err
  228. }
  229. f.OSConfig = sdk.OSFsConfig{}
  230. f.S3Config = S3FsConfig{}
  231. f.GCSConfig = GCSFsConfig{}
  232. f.AzBlobConfig = AzBlobFsConfig{}
  233. f.CryptConfig = CryptFsConfig{}
  234. f.SFTPConfig = SFTPFsConfig{}
  235. return nil
  236. case sdk.LocalFilesystemProvider:
  237. f.S3Config = S3FsConfig{}
  238. f.GCSConfig = GCSFsConfig{}
  239. f.AzBlobConfig = AzBlobFsConfig{}
  240. f.CryptConfig = CryptFsConfig{}
  241. f.SFTPConfig = SFTPFsConfig{}
  242. f.HTTPConfig = HTTPFsConfig{}
  243. return validateOSFsConfig(&f.OSConfig)
  244. default:
  245. return util.NewI18nError(
  246. util.NewValidationError("invalid filesystem provider"),
  247. util.I18nErrorFsValidation,
  248. )
  249. }
  250. }
  251. // HasRedactedSecret returns true if configured the filesystem configuration has a redacted secret
  252. func (f *Filesystem) HasRedactedSecret() bool {
  253. // TODO move vfs specific code into each *FsConfig struct
  254. switch f.Provider {
  255. case sdk.S3FilesystemProvider:
  256. if f.S3Config.SSECustomerKey.IsRedacted() {
  257. return true
  258. }
  259. return f.S3Config.AccessSecret.IsRedacted()
  260. case sdk.GCSFilesystemProvider:
  261. return f.GCSConfig.Credentials.IsRedacted()
  262. case sdk.AzureBlobFilesystemProvider:
  263. if f.AzBlobConfig.AccountKey.IsRedacted() {
  264. return true
  265. }
  266. return f.AzBlobConfig.SASURL.IsRedacted()
  267. case sdk.CryptedFilesystemProvider:
  268. return f.CryptConfig.Passphrase.IsRedacted()
  269. case sdk.SFTPFilesystemProvider:
  270. if f.SFTPConfig.Password.IsRedacted() {
  271. return true
  272. }
  273. if f.SFTPConfig.PrivateKey.IsRedacted() {
  274. return true
  275. }
  276. return f.SFTPConfig.KeyPassphrase.IsRedacted()
  277. case sdk.HTTPFilesystemProvider:
  278. if f.HTTPConfig.Password.IsRedacted() {
  279. return true
  280. }
  281. return f.HTTPConfig.APIKey.IsRedacted()
  282. }
  283. return false
  284. }
  285. // HideConfidentialData hides filesystem confidential data
  286. func (f *Filesystem) HideConfidentialData() {
  287. switch f.Provider {
  288. case sdk.S3FilesystemProvider:
  289. f.S3Config.HideConfidentialData()
  290. case sdk.GCSFilesystemProvider:
  291. f.GCSConfig.HideConfidentialData()
  292. case sdk.AzureBlobFilesystemProvider:
  293. f.AzBlobConfig.HideConfidentialData()
  294. case sdk.CryptedFilesystemProvider:
  295. f.CryptConfig.HideConfidentialData()
  296. case sdk.SFTPFilesystemProvider:
  297. f.SFTPConfig.HideConfidentialData()
  298. case sdk.HTTPFilesystemProvider:
  299. f.HTTPConfig.HideConfidentialData()
  300. }
  301. }
  302. // GetACopy returns a filesystem copy
  303. func (f *Filesystem) GetACopy() Filesystem {
  304. f.SetEmptySecretsIfNil()
  305. fs := Filesystem{
  306. Provider: f.Provider,
  307. OSConfig: sdk.OSFsConfig{
  308. ReadBufferSize: f.OSConfig.ReadBufferSize,
  309. WriteBufferSize: f.OSConfig.WriteBufferSize,
  310. },
  311. S3Config: S3FsConfig{
  312. BaseS3FsConfig: sdk.BaseS3FsConfig{
  313. Bucket: f.S3Config.Bucket,
  314. Region: f.S3Config.Region,
  315. AccessKey: f.S3Config.AccessKey,
  316. RoleARN: f.S3Config.RoleARN,
  317. Endpoint: f.S3Config.Endpoint,
  318. StorageClass: f.S3Config.StorageClass,
  319. ACL: f.S3Config.ACL,
  320. KeyPrefix: f.S3Config.KeyPrefix,
  321. UploadPartSize: f.S3Config.UploadPartSize,
  322. UploadConcurrency: f.S3Config.UploadConcurrency,
  323. DownloadPartSize: f.S3Config.DownloadPartSize,
  324. DownloadConcurrency: f.S3Config.DownloadConcurrency,
  325. DownloadPartMaxTime: f.S3Config.DownloadPartMaxTime,
  326. UploadPartMaxTime: f.S3Config.UploadPartMaxTime,
  327. ForcePathStyle: f.S3Config.ForcePathStyle,
  328. SkipTLSVerify: f.S3Config.SkipTLSVerify,
  329. },
  330. AccessSecret: f.S3Config.AccessSecret.Clone(),
  331. SSECustomerKey: f.S3Config.SSECustomerKey.Clone(),
  332. },
  333. GCSConfig: GCSFsConfig{
  334. BaseGCSFsConfig: sdk.BaseGCSFsConfig{
  335. Bucket: f.GCSConfig.Bucket,
  336. AutomaticCredentials: f.GCSConfig.AutomaticCredentials,
  337. StorageClass: f.GCSConfig.StorageClass,
  338. ACL: f.GCSConfig.ACL,
  339. KeyPrefix: f.GCSConfig.KeyPrefix,
  340. UploadPartSize: f.GCSConfig.UploadPartSize,
  341. UploadPartMaxTime: f.GCSConfig.UploadPartMaxTime,
  342. },
  343. Credentials: f.GCSConfig.Credentials.Clone(),
  344. },
  345. AzBlobConfig: AzBlobFsConfig{
  346. BaseAzBlobFsConfig: sdk.BaseAzBlobFsConfig{
  347. Container: f.AzBlobConfig.Container,
  348. AccountName: f.AzBlobConfig.AccountName,
  349. Endpoint: f.AzBlobConfig.Endpoint,
  350. KeyPrefix: f.AzBlobConfig.KeyPrefix,
  351. UploadPartSize: f.AzBlobConfig.UploadPartSize,
  352. UploadConcurrency: f.AzBlobConfig.UploadConcurrency,
  353. DownloadPartSize: f.AzBlobConfig.DownloadPartSize,
  354. DownloadConcurrency: f.AzBlobConfig.DownloadConcurrency,
  355. UseEmulator: f.AzBlobConfig.UseEmulator,
  356. AccessTier: f.AzBlobConfig.AccessTier,
  357. },
  358. AccountKey: f.AzBlobConfig.AccountKey.Clone(),
  359. SASURL: f.AzBlobConfig.SASURL.Clone(),
  360. },
  361. CryptConfig: CryptFsConfig{
  362. OSFsConfig: sdk.OSFsConfig{
  363. ReadBufferSize: f.CryptConfig.ReadBufferSize,
  364. WriteBufferSize: f.CryptConfig.WriteBufferSize,
  365. },
  366. Passphrase: f.CryptConfig.Passphrase.Clone(),
  367. },
  368. SFTPConfig: SFTPFsConfig{
  369. BaseSFTPFsConfig: sdk.BaseSFTPFsConfig{
  370. Endpoint: f.SFTPConfig.Endpoint,
  371. Username: f.SFTPConfig.Username,
  372. Prefix: f.SFTPConfig.Prefix,
  373. DisableCouncurrentReads: f.SFTPConfig.DisableCouncurrentReads,
  374. BufferSize: f.SFTPConfig.BufferSize,
  375. EqualityCheckMode: f.SFTPConfig.EqualityCheckMode,
  376. },
  377. Password: f.SFTPConfig.Password.Clone(),
  378. PrivateKey: f.SFTPConfig.PrivateKey.Clone(),
  379. KeyPassphrase: f.SFTPConfig.KeyPassphrase.Clone(),
  380. },
  381. HTTPConfig: HTTPFsConfig{
  382. BaseHTTPFsConfig: sdk.BaseHTTPFsConfig{
  383. Endpoint: f.HTTPConfig.Endpoint,
  384. Username: f.HTTPConfig.Username,
  385. SkipTLSVerify: f.HTTPConfig.SkipTLSVerify,
  386. EqualityCheckMode: f.HTTPConfig.EqualityCheckMode,
  387. },
  388. Password: f.HTTPConfig.Password.Clone(),
  389. APIKey: f.HTTPConfig.APIKey.Clone(),
  390. },
  391. }
  392. if len(f.SFTPConfig.Fingerprints) > 0 {
  393. fs.SFTPConfig.Fingerprints = make([]string, len(f.SFTPConfig.Fingerprints))
  394. copy(fs.SFTPConfig.Fingerprints, f.SFTPConfig.Fingerprints)
  395. }
  396. return fs
  397. }
  398. // IsFsDisabled returns if a filesystem provider is disabled
  399. func IsFsDisabled(provider sdk.FilesystemProvider) bool {
  400. if loadDisabledProvidersFn != nil {
  401. loadDisabledProvidersFn()
  402. }
  403. return slices.Contains(disabledFilesystemProviders, provider)
  404. }