Skip to content

Instantly share code, notes, and snippets.

@ozero
Last active November 22, 2021 02:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ozero/beb89fc6a462320dc60de3ff64006c9d to your computer and use it in GitHub Desktop.
Save ozero/beb89fc6a462320dc60de3ff64006c9d to your computer and use it in GitHub Desktop.
Humhub: Mounting S3 Using goofys & catfs, allows to more capacity than your VPS.

Humhub: Mounting S3 Using goofys & catfs.

Install from these repository.

Here is example of usage for Humhub,

  • Catfs caches any files you accessed through goofys, up to (Your_storage_size - 1) GB. (-free:1G option below.)
  • Assumes your php runs as uid=33 & gid=33 (-uid 33 --gid 33 option below).

Try at your shell

$ sudo goofys -o allow_other --file-mode 0666 --dir-mode 0777 --uid 33 --gid 33 --cache=-o:allow_other:--free:1G:/PATH/TO/MY/CATFS_CACHE MY_S3_BUCKET:MY_S3_DIRECTORY_FOR_UPLOAD /PATH/TO/MY/S3_BUCKET_MOUNTPOINT

Auto mount in /etc/fstab

goofys#MY_S3_BUCKET:MY_S3_DIRECTORY_FOR_UPLOAD /PATH/TO/MY/S3_BUCKET_MOUNTPOINT fuse allow_other,--file-mode=0666,--dir-mode=0777,--uid=33,--gid=33,--cache=-o:allow_other:--free:1G:/PATH/TO/MY/CATFS_CACHE 0 0

Then check /etc/fstab with sudo mount -a and mount | grep fuse.

Move upload into S3 & swap upload directory

  • Copy all /PATH/TO/MY/HUMHUB/uploads files into /PATH/TO/MY/S3_BUCKET_MOUNTPOINT
  • Rename /PATH/TO/MY/HUMHUB/uploads to /PATH/TO/MY/HUMHUB/uploads_bak
  • Make symbolic link sudo ln -s /PATH/TO/MY/S3_BUCKET_MOUNTPOINT /PATH/TO/MY/HUMHUB/uploads

Issues needs to solve

  • All media traffic still delivered from your Server, not CDN (CloudFront)
    • Need to output proper CF distribution URL.
    • Override /protected/humhub/modules/file/models/File.php 's getUrl().
  • CloudFront has "Restrict viewer access" feature, can identify access from Guest or Humhub user, with Signed cookie.
    • Limit lifetime of the signed cookie with DateLessThan policy
    • You can also limit with IpAddress policy, but site will broken with iOS 15's "Private relay" feature.
  • But CloudFront itself can't check whether the access (from the Humhub user) has proper privilege for private post or community.
    • Concept to solve: We may be able to use CloudFront functions or Lambda@Edge to validate access.
    • Add JSON Web Token on all /file/file/download URLs and CF func / Lambda@Edge validate them.
    • Users are given short-time JWT string for every valid (privileged) user access. JWT is issued from your code on Humhub, So only their access are allowed on CloudFront distribution.
    • Minor cons: If even as the post was move into private, JWT & the URL still Valid until JWT expires.

More cool concepts are welcome.

@ozero
Copy link
Author

ozero commented Nov 10, 2021

Problems with using Signed cookies for access control

Even if you limit the permissions after posting the files, the download URL of the file is still accessible from all members.

Solution

When user move a post, rename the GUID of the attached file, issue another download URL.

  • What do I need to protect?: File access rights.
  • How is this accomplished?: As long as the URL is not a known one.
  • How to be achieved practically?: Changing URL (file GUID) to another, not known one.

(Then what will occur when matured space (with plenty of files) move between public & private...?

@ozero
Copy link
Author

ozero commented Nov 10, 2021

Memo: exclude goofys mount point from updatedb https://qiita.com/ataru_mix/items/0fe56e407a4d0f6f01e4

@ozero
Copy link
Author

ozero commented Nov 12, 2021

Distribute images from CF, without modifying Humhub, Using NGINX Rewrite

You don't need to modify getUrl() on Humhub, change config of your webserver.

Examples: Using NGINX Rewrite Rules

https://www.nginx.com/blog/creating-nginx-rewrite-rules/

  • Put some NGINX Rewrite Rules below in your config file, /etc/nginx/site_enabled .
  • Assume your /uploads are synced in S3 & distributed from https://YOUR_CLOUDFRONT_ALT_DOMAIN/, using CloudFront's alternative domain name.

Examples:

  • Redirect Profile photo to CDN:

rewrite ^.*?uploads\/profile_image\/([0-9a-f\-]+)(\.[a-z]+).*?$ https://YOUR_CLOUDFRONT_ALT_DOMAIN/profile_image/$1$2 last;

  • Redirect Profile banner to CDN:

rewrite ^.*?uploads\/profile_image\/banner\/([0-9a-f\-]+)(\.[a-z]+).*?$ https://YOUR_CLOUDFRONT_ALT_DOMAIN/profile_image/banner/$1$2 last;

  • Thumbnail of attached files

rewrite ^.*?file\/file\/download.*?preview\-image.*?guid=([0-9a-f])([0-9a-f])([0-9a-f\-]+).*?$ https://YOUR_CLOUDFRONT_ALT_DOMAIN/file/$1/$2/$1$2$3/preview-image last;

  • Attached files

rewrite ^.*?file\/file\/download\?guid=([0-9a-f])([0-9a-f])([0-9a-f\-]+).*?$ https://YOUR_CLOUDFRONT_ALT_DOMAIN/file/$1/$2/$1$2$3/file last;

@ozero
Copy link
Author

ozero commented Nov 16, 2021

OK, I've made them somehow useful, I'll write what I did someday. Draft are below:


Part A: Get unlimited storage

Strategy: disk usage matters

  • Create S3 bucket
  • Install s3fs
  • mount manually & check sync
  • mount by fstab & check sync
  • copy content via s3fs mountpoint
  • rename /humhub/uploads & create Symlink to mountpoint

current pros & cons

Part B: Get unlimited bandwidth

Strategy: security matters

  • https don't secures domain name, but entire query string
  • Humhub secures the URL
  • CDN & Signed-URL make the URL ephemeral, secures content
  • Case: A space gone private, how its contents secured?
  • Don't expect Humhub project to support S3, use it as-is.

B-1. Create CloudFront distribution

  • Create RSA key
  • Limit viewers
  • Set up Alternate domain name (CNAME) with your DNS zone setting.

B-2. Redirect the request to upload content

  • Strategy: Nginx rewrite → Redirect & signing → CloudFront
  • Write your original redirector to CloudFront
  • Redirect requests for your redirector using Nginx rewrite rules
  • you'll found need to add a header for direct download

B-3. Pass original filename using Cloud function

  • append filenames as query string by JS in theme.
  • Redirector pass the query string to CloudFront
  • Cloud function reads the query string and add header
  • Set the function on the behavior, view-response.

@ozero
Copy link
Author

ozero commented Nov 22, 2021

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment