diff --git a/doc/book/src/connect/apps.md b/doc/book/src/connect/apps.md index 14d69ef8c..c9bba4098 100644 --- a/doc/book/src/connect/apps.md +++ b/doc/book/src/connect/apps.md @@ -1,6 +1,19 @@ # Apps (Nextcloud, Peertube...) -In this section, we cover the following software: [Nextcloud](#nextcloud), [Peertube](#peertube), [Mastodon](#mastodon), [Matrix](#matrix) +In this section, we cover the following web applications: + +| Name | Status | Note | +|------|--------|------| +| [Nextcloud](#nextcloud) | ✅ | Both Primary Storage and External Storage are supported | +| [Peertube](#peertube) | ✅ | `base_url` must be set to the website endpoint | +| [Mastodon](#mastodon) | ❓ | | +| [Matrix](#matrix) | ✅ | Tested with `synapse-s3-storage-provider` | +| [Pixelfed](#pixelfed) | ❓ | | +| [Pleroma](#pleroma) | ❓ | | +| [Lemmy](#lemmy) | ❓ | | +| [Funkwhale](#funkwhale) | ❓ | | +| [Misskey](#misskey) | ❓ | | +| [Prismo](#prismo) | ❓ | | ## Nextcloud @@ -108,109 +121,8 @@ Do not change the `use_path_style` and `legacy_auth` entries, other configuratio Peertube proposes a clever integration of S3 by directly exposing its endpoint instead of proxifying requests through the application. In other words, Peertube is only responsible of the "control plane" and offload the "data plane" to Garage. -In return, this system is a bit harder to configure, especially with Garage that supports less feature than other older S3 backends. -We show that it is still possible to configure Garage with Peertube, allowing you to spread the load and the bandwidth usage on the Garage cluster. - -### Enable path-style access by patching Peertube - -First, you will need to apply a small patch on Peertube ([#4510](https://github.com/Chocobozzz/PeerTube/pull/4510)): - -```diff -From e3b4c641bdf67e07d406a1d49d6aa6b1fbce2ab4 Mon Sep 17 00:00:00 2001 -From: Martin Honermeyer -Date: Sun, 31 Oct 2021 12:34:04 +0100 -Subject: [PATCH] Allow setting path-style access for object storage - ---- - config/default.yaml | 4 ++++ - config/production.yaml.example | 4 ++++ - server/initializers/config.ts | 1 + - server/lib/object-storage/shared/client.ts | 3 ++- - .../production/config/custom-environment-variables.yaml | 2 ++ - 5 files changed, 13 insertions(+), 1 deletion(-) - -diff --git a/config/default.yaml b/config/default.yaml -index cf9d69a6211..4efd56fb804 100644 ---- a/config/default.yaml -+++ b/config/default.yaml -@@ -123,6 +123,10 @@ object_storage: - # You can also use AWS_SECRET_ACCESS_KEY env variable - secret_access_key: '' - -+ # Reference buckets via path rather than subdomain -+ # (i.e. "my-endpoint.com/bucket" instead of "bucket.my-endpoint.com") -+ force_path_style: false -+ - # Maximum amount to upload in one request to object storage - max_upload_part: 2GB - -diff --git a/config/production.yaml.example b/config/production.yaml.example -index 70993bf57a3..9ca2de5f4c9 100644 ---- a/config/production.yaml.example -+++ b/config/production.yaml.example -@@ -121,6 +121,10 @@ object_storage: - # You can also use AWS_SECRET_ACCESS_KEY env variable - secret_access_key: '' - -+ # Reference buckets via path rather than subdomain -+ # (i.e. "my-endpoint.com/bucket" instead of "bucket.my-endpoint.com") -+ force_path_style: false -+ - # Maximum amount to upload in one request to object storage - max_upload_part: 2GB - -diff --git a/server/initializers/config.ts b/server/initializers/config.ts -index 8375bf4304c..d726c59a4b6 100644 ---- a/server/initializers/config.ts -+++ b/server/initializers/config.ts -@@ -91,6 +91,7 @@ const CONFIG = { - ACCESS_KEY_ID: config.get('object_storage.credentials.access_key_id'), - SECRET_ACCESS_KEY: config.get('object_storage.credentials.secret_access_key') - }, -+ FORCE_PATH_STYLE: config.get('object_storage.force_path_style'), - VIDEOS: { - BUCKET_NAME: config.get('object_storage.videos.bucket_name'), - PREFIX: config.get('object_storage.videos.prefix'), -diff --git a/server/lib/object-storage/shared/client.ts b/server/lib/object-storage/shared/client.ts -index c9a61459336..eadad02f93f 100644 ---- a/server/lib/object-storage/shared/client.ts -+++ b/server/lib/object-storage/shared/client.ts -@@ -26,7 +26,8 @@ function getClient () { - accessKeyId: OBJECT_STORAGE.CREDENTIALS.ACCESS_KEY_ID, - secretAccessKey: OBJECT_STORAGE.CREDENTIALS.SECRET_ACCESS_KEY - } -- : undefined -+ : undefined, -+ forcePathStyle: CONFIG.OBJECT_STORAGE.FORCE_PATH_STYLE - }) - - logger.info('Initialized S3 client %s with region %s.', getEndpoint(), OBJECT_STORAGE.REGION, lTags()) -diff --git a/support/docker/production/config/custom-environment-variables.yaml b/support/docker/production/config/custom-environment-variables.yaml -index c7cd28e6521..a960bab0bc9 100644 ---- a/support/docker/production/config/custom-environment-variables.yaml -+++ b/support/docker/production/config/custom-environment-variables.yaml -@@ -54,6 +54,8 @@ object_storage: - - region: "PEERTUBE_OBJECT_STORAGE_REGION" - -+ force_path_style: "PEERTUBE_OBJECT_STORAGE_FORCE_PATH_STYLE" -+ - max_upload_part: - __name: "PEERTUBE_OBJECT_STORAGE_MAX_UPLOAD_PART" - __format: "json" -``` - -You can then recompile it with: - -``` -npm run build -``` - -And it can be started with: - -``` -NODE_ENV=production NODE_CONFIG_DIR=/srv/peertube/config node dist/server.js -``` +In return, this system is a bit harder to configure. +We show how it is still possible to configure Garage with Peertube, allowing you to spread the load and the bandwidth usage on the Garage cluster. ### Create resources in Garage @@ -232,31 +144,32 @@ garage bucket create peertube-playlist Now we allow our key to read and write on these buckets: ``` -garage bucket allow peertube-playlist --read --write --key peertube-key -garage bucket allow peertube-video --read --write --key peertube-key +garage bucket allow peertube-playlists --read --write --owner --key peertube-key +garage bucket allow peertube-videos --read --write --owner --key peertube-key ``` -Finally, we need to expose these buckets publicly to serve their content to users: +We also need to expose these buckets publicly to serve their content to users: ```bash -garage bucket website --allow peertube-playlist -garage bucket website --allow peertube-video +garage bucket website --allow peertube-playlists +garage bucket website --allow peertube-videos +``` + +Finally, we must allow Cross-Origin Resource Sharing (CORS). +CORS are required by your browser to allow requests triggered from the peertube website (eg. peertube.tld) to your bucket's domain (eg. peertube-videos.web.garage.tld) + +```bash +export CORS='{"CORSRules":[{"AllowedHeaders":["*"],"AllowedMethods":["GET"],"AllowedOrigins":["*"]}]}' +aws --endpoint http://s3.garage.localhost s3api put-bucket-cors --bucket peertube-playlists --cors-configuration $CORS +aws --endpoint http://s3.garage.localhost s3api put-bucket-cors --bucket peertube-videos --cors-configuration $CORS ``` These buckets are now accessible on the web port (by default 3902) with the following URL: `http://:` where the root domain is defined in your configuration file (by default `.web.garage`). So we have currently the following URLs: - * http://peertube-playlist.web.garage:3902 - * http://peertube-video.web.garage:3902 + * http://peertube-playlists.web.garage:3902 + * http://peertube-videos.web.garage:3902 Make sure you (will) have a corresponding DNS entry for them. -### Configure a Reverse Proxy to serve CORS - -Now we will configure a reverse proxy in front of Garage. -This is required as we have no other way to serve CORS headers yet. -Check the [Configuring a reverse proxy](/cookbook/reverse_proxy.html) section to know how. - -Now make sure that your 2 dns entries are pointing to your reverse proxy. - ### Configure Peertube You must edit the file named `config/production.yaml`, we are only modifying the root key named `object_storage`: @@ -268,9 +181,6 @@ object_storage: # Put localhost only if you have a garage instance running on that node endpoint: 'http://localhost:3900' # or "garage.example.com" if you have TLS on port 443 - # This entry has been added by our patch, must be set to true - force_path_style: true - # Garage supports only one region for now, named garage region: 'garage' @@ -287,28 +197,23 @@ object_storage: prefix: '' # You must fill this field to make Peertube use our reverse proxy/website logic - base_url: 'http://peertube-playlist.web.garage' # Example: 'https://mirror.example.com' + base_url: 'http://peertube-playlists.web.garage.localhost' # Example: 'https://mirror.example.com' # Same settings but for webtorrent videos videos: bucket_name: 'peertube-video' prefix: '' # You must fill this field to make Peertube use our reverse proxy/website logic - base_url: 'http://peertube-video.web.garage' + base_url: 'http://peertube-videos.web.garage.localhost' ``` ### That's all Everything must be configured now, simply restart Peertube and try to upload a video. -You must see in your browser console that data are fetched directly from our bucket (through the reverse proxy). -### Miscellaneous - -*Known bug:* The playback does not start and some 400 Bad Request Errors appear in your browser console and on Garage. -If the description of the error contains HTTP Invalid Range: InvalidRange, the error is due to a buggy ffmpeg version. -You must avoid the 4.4.0 and use either a newer or older version. - -*Associated issues:* [#137](https://git.deuxfleurs.fr/Deuxfleurs/garage/issues/137), [#138](https://git.deuxfleurs.fr/Deuxfleurs/garage/issues/138), [#140](https://git.deuxfleurs.fr/Deuxfleurs/garage/issues/140). These issues are non blocking. +Peertube will start by serving the video from its own domain while it is encoding. +Once the encoding is done, the video is uploaded to Garage. +You can now reload the page and see in your browser console that data are fetched directly from your bucket. *External link:* [Peertube Documentation > Remote Storage](https://docs.joinpeertube.org/admin-remote-storage) diff --git a/doc/book/src/connect/publii.png b/doc/book/src/connect/publii.png new file mode 100644 index 000000000..4b175f537 Binary files /dev/null and b/doc/book/src/connect/publii.png differ diff --git a/doc/book/src/connect/websites.md b/doc/book/src/connect/websites.md index 6f66c8d05..6c3197df7 100644 --- a/doc/book/src/connect/websites.md +++ b/doc/book/src/connect/websites.md @@ -3,6 +3,12 @@ Garage is also suitable to host static websites. While they can be deployed with traditional CLI tools, some static website generators have integrated options to ease your workflow. +| Name | Status | Note | +|------|--------|------| +| [Hugo](#hugo) | ✅ | Publishing logic is integrated in the tool | +| [Publii](#publii) | ✅ | Require a correctly configured s3 vhost endpoint | +| [Generic Static Site Generator](#generic-static-site-generator) | ✅ | Works for Jekyll, Zola, Gatsby, Pelican, etc. | + ## Hugo Add to your `config.toml` the following section: @@ -39,39 +45,38 @@ hugo deploy ## Publii -It would require a patch either on Garage or on Publii to make both systems work. +[![A screenshot of Publii's GUI](./publii.png)](./publii.png) -Currently, the proposed workaround is to deploy your website manually: - - On the left menu, click on Server, choose Manual Deployment (the logo looks like a compressed file) - - Set your website URL, keep Output type as "Non-compressed catalog" - - Click on Save changes - - Click on Sync your website (bottom left of the app) - - On the new page, click again on Sync your website - - Click on Get website files - - You need to synchronize the output folder you see in your file explorer, we will use minio client. +Deploying a website to Garage from Publii is natively supported. +First, make sure that your Garage administrator allowed and configured Garage to support vhost access style. +We also suppose that your bucket ("my-bucket") and key is already created and configured. -Be sure that you [configured minio client](cli.html#minio-client-recommended). +Then, from the left menu, click on server. Choose "S3" as the protocol. +In the configuration window, enter: + - Your finale website URL (eg. "http://my-bucket.web.garage.localhost:3902") + - Tick "Use a custom S3 provider" + - Set the S3 endpoint, (eg. "http://s3.garage.localhost:3900") + - Then put your access key (eg. "GK..."), your secret key, and your bucket (eg. "my-bucket") + - And hit the button "Save settings" -Then copy this output folder +Now, each time you want to publish your website from Publii, just hit the bottom left button "Sync your website"! -```bash -mc mirror --overwrite output garage/my-site -``` -## Generic (eg. Jekyll) + +## Generic Static Site Generator Some tools do not support sending to a S3 backend but output a compiled folder on your system. We can then use any CLI tool to upload this content to our S3 target. First, start by [configuring minio client](cli.html#minio-client-recommended). -Then build your website: +Then build your website (example for jekyll): ```bash jekyll build ``` -And copy jekyll's output folder on S3: +And copy its output folder (`_site` for Jekyll) on S3: ```bash mc mirror --overwrite _site garage/my-site diff --git a/doc/book/src/cookbook/reverse_proxy.md b/doc/book/src/cookbook/reverse_proxy.md index 14633ae8d..50056dd2d 100644 --- a/doc/book/src/cookbook/reverse_proxy.md +++ b/doc/book/src/cookbook/reverse_proxy.md @@ -1,6 +1,6 @@ # Configuring a reverse proxy -The main reason to add a reverse proxy in front of Garage is to provide TLS to your users. +The main reason to add a reverse proxy in front of Garage is to provide TLS to your users and serve multiple web services on port 443. In production you will likely need your certificates signed by a certificate authority. The most automated way is to use a provider supporting the [ACME protocol](https://datatracker.ietf.org/doc/html/rfc8555) @@ -55,16 +55,15 @@ If you directly put the instructions in the root `nginx.conf`, keep in mind that And do not forget to reload nginx with `systemctl reload nginx` or `nginx -s reload`. -### Defining backends +### Exposing the S3 endpoints First, we need to tell to nginx how to access our Garage cluster. Because we have multiple nodes, we want to leverage all of them by spreading the load. +In nginx, we can do that with the `upstream` directive. -In nginx, we can do that with the upstream directive. -Because we have 2 endpoints: one for the S3 API and one to serve websites, -we create 2 backends named respectively `s3_backend` and `web_backend`. +Then in a `server` directive, we define the vhosts, the TLS certificates and the proxy rule. -A documented example for the `s3_backend` assuming you chose port 3900: +A possible configuration: ```nginx upstream s3_backend { @@ -78,9 +77,34 @@ upstream s3_backend { # that are more powerful than others server garage2.example.com:3900 weight=2; } + +server { + listen [::]:443 http2 ssl; + + ssl_certificate /tmp/garage.crt; + ssl_certificate_key /tmp/garage.key; + + # You need multiple server names here: + # - s3.garage.tld is used for path-based s3 requests + # - *.s3.garage.tld is used for vhost-based s3 requests + server_name s3.garage.tld *.s3.garage.tld; + + location / { + proxy_pass http://s3_backend; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + } +} ``` -A similar example for the `web_backend` assuming you chose port 3902: +## Exposing the web endpoint + +To better understand the logic involved, you can refer to the [Exposing buckets as websites](/cookbook/exposing_websites.html) section. +Otherwise, the configuration is very similar to the S3 endpoint. +You must only adapt `upstream` with the web port instead of the s3 port and change the `server_name` and `proxy_pass` entry + +A possible configuration: + ```nginx upstream web_backend { @@ -89,65 +113,19 @@ upstream web_backend { server garage1.example.com:3902; server garage2.example.com:3902 weight=2; } -``` -### Exposing the S3 API - -The configuration section for the S3 API is simple as we only support path-access style yet. -We simply configure the TLS parameters and forward all the requests to the backend: - -```nginx server { listen [::]:443 http2 ssl; + ssl_certificate /tmp/garage.crt; ssl_certificate_key /tmp/garage.key; - # should be the endpoint you want - # aws uses s3.amazonaws.com for example - server_name garage.example.com; + # You need multiple server names here: + # - *.web.garage.tld is used for your users wanting a website without reserving a domain name + # - example.com, my-site.tld, etc. are reserved domain name by your users that chose to host their website as a garage's bucket + server_name *.web.garage.tld example.com my-site.tld; location / { - proxy_pass http://s3_backend; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Host $host; - } -} - -``` - -### Exposing the web endpoint - -The web endpoint is a bit more complicated to configure as it listens on many different `Host` fields. -To better understand the logic involved, you can refer to the [Exposing buckets as websites](/cookbook/exposing_websites.html) section. -Also, for some applications, you may need to serve CORS headers: Garage can not serve them directly but we show how we can use nginx to serve them. -You can use the following example as your starting point: - -```nginx -server { - listen [::]:443 http2 ssl; - ssl_certificate /tmp/garage.crt; - ssl_certificate_key /tmp/garage.key; - - # We list all the Hosts fields that can access our buckets - server_name *.web.garage - example.com - my-site.tld - ; - - location / { - # Add these headers only if you want to allow CORS requests - # For production use, more specific rules would be better for your security - add_header Access-Control-Allow-Origin *; - add_header Access-Control-Max-Age 3600; - add_header Access-Control-Expose-Headers Content-Length; - add_header Access-Control-Allow-Headers Range; - - # We do not forward OPTIONS requests to Garage - # as it does not support them but they are needed for CORS. - if ($request_method = OPTIONS) { - return 200; - } - proxy_pass http://web_backend; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; @@ -155,7 +133,6 @@ server { } ``` - ## Apache httpd @TODO