Rewrite admin documentation

This commit is contained in:
Ciarán Ainsworth 2022-07-01 09:02:29 +00:00
commit 17f1941b0b
248 changed files with 10920 additions and 10168 deletions

View file

@ -0,0 +1,88 @@
# Change your instance URL
```{danger}
We recommend you don't change your instance URL. Changing it __will__ cause instability and problems with federation. If you change your URL, the Funkwhale project can't offer support for problems that arise.
```
Your instance URL is your pod's unique identifier in the {term}`fediverse`. If you want to change it, you need to update a lot of information
- The instance URL in your {file}`.env` file.
- The instance URL in your vhost.
- Any references to the old URL in your database.
To clean the database, the {file}`manage.py` script contains a `fix_federation_ids` command.
```{warning}
Running `fix_federation_ids` with the `--no-dry-run` flag is irreversible. Make sure you [back up your data](../upgrade_docs/backup.md).
```
## Update your instance URL
1. Change the `FUNKWHALE_HOSTNAME` and `DJANGO_ALLOWED_HOSTS` value in your {file}`.env` file.
2. Change the `server_name` values in your {file}`/etc/nginx/sites-enabled/funkwhale.conf` file.
3. Run the `fix_federation_ids` command to clean up your database.
````{tabbed} Debian
```{code} bash
poetry run python manage.py fix_federation_ids https://old-url https://new-url --no-dry-run --no-input
```
````
````{tabbed} Docker
```{code} bash
docker-compose run --rm api python manage.py fix_federation_ids https://old-url https://new-url --no-dry-run --no-input
```
````
Example output:
```
Will replace 108 found occurrences of 'https://old-url' by 'https://new-url':
- 20 music.Artist
- 13 music.Album
- 39 music.Track
- 31 music.Upload
- 1 music.Library
- 4 federation.Actor
- 0 federation.Activity
- 0 federation.Follow
- 0 federation.LibraryFollow
Replacing on 20 music.Artist…
Replacing on 13 music.Album…
Replacing on 39 music.Track…
Replacing on 31 music.Upload…
Replacing on 1 music.Library…
Replacing on 4 federation.Actor…
Replacing on 0 federation.Activity…
Replacing on 0 federation.Follow…
Replacing on 0 federation.LibraryFollow…
```
4. Restart your webserver to pick up the changes.
````{tabbed} Nginx
```{code} bash
sudo systemctl restart nginx
```
````
````{tabbed} Apache
```{code} bash
sudo systemctl restart apache2
```
````

View file

@ -0,0 +1,277 @@
# Customize your environment file
Your `.env` (environment) file contains variables you can change to customize your pod. You can change these variables at any time to alter how your pod runs.
You need to restart your Funkwhale services after changing your `.env` file.
````{tabbed} Debian
```{code} bash
sudo systemctl restart funkwhale.target
```
````
````{tabbed} Docker
```{code} bash
docker-compose restart
```
````
## Variables
````{important}
Some environment variables accept a URL as a value. To encode URLs and avoid problems with special characters, use `urllib.parse` on your URL value.
```py
python3 -c 'import urllib.parse; print(urllib.parse.quote_plus("p@ssword"))
```
```{seealso}
The [django-environ documentation](https://github.com/joke2k/django-environ/blob/main/docs/tips.rst#using-unsafe-characters-in-urls).
```
````
### Pod configuration
```{eval-rst}
.. autodata:: config.settings.common.FUNKWHALE_HOSTNAME
:annotation: = mypod.audio
.. autodata:: config.settings.common.FUNKWHALE_PROTOCOL
:annotation: = https
```
### Database and redis configuration
```{eval-rst}
.. autodata:: config.settings.common.DATABASE_URL
:annotation: = postgresql://<user>:<password>@<host>:<port>/<database>
.. autodata:: config.settings.common.DB_CONN_MAX_AGE
.. autodata:: config.settings.common.CACHE_URL
:annotation: = redis://<host>:<port>/<database>
.. autodata:: config.settings.common.CELERY_BROKER_URL
:annotation: = redis://127.0.0.1:6379/0
```
### Accounts and registration
```{eval-rst}
.. autodata:: config.settings.common.ACCOUNT_EMAIL_VERIFICATION_ENFORCE
:annotation: = true
.. autodata:: config.settings.common.USERS_INVITATION_EXPIRATION_DAYS
:annotation: = 7
.. autodata:: config.settings.common.DISABLE_PASSWORD_VALIDATORS
:annotation: = true
.. autodata:: config.settings.common.ACCOUNT_USERNAME_BLACKLIST
:annotation: = test,funkwhale
```
```{py:data} LDAP_ENABLED
---
value: false
---
Whether to enable LDAP authentication.
See {doc}`/administrator_documentation/configuration_docs/ldap` for more information.
```
### Media storage and serving configuration
```{eval-rst}
.. autodata:: config.settings.common.MEDIA_URL
:annotation: = https://mypod.audio/media/
.. autodata:: config.settings.common.MEDIA_ROOT
:annotation: = /srv/funkwhale/data/media
.. autodata:: config.settings.common.PROXY_MEDIA
:annotation: = true
.. autodata:: config.settings.common.EXTERNAL_MEDIA_PROXY_ENABLED
:annotation: = false
.. autodata:: config.settings.common.ATTACHMENTS_UNATTACHED_PRUNE_DELAY
:annotation: = true
.. autodata:: config.settings.common.REVERSE_PROXY_TYPE
:annotation: = nginx
.. autodata:: config.settings.common.PROTECT_FILES_PATH
:annotation: = /_protected
```
### S3 storage configuration
```{eval-rst}
.. autodata:: config.settings.common.AWS_QUERYSTRING_AUTH
.. autodata:: config.settings.common.AWS_QUERYSTRING_EXPIRE
.. autodata:: config.settings.common.AWS_ACCESS_KEY_ID
.. autodata:: config.settings.common.AWS_SECRET_ACCESS_KEY
.. autodata:: config.settings.common.AWS_STORAGE_BUCKET_NAME
.. autodata:: config.settings.common.AWS_S3_CUSTOM_DOMAIN
.. autodata:: config.settings.common.AWS_S3_ENDPOINT_URL
:annotation: = https://minio.mydomain.com
.. autodata:: config.settings.common.AWS_S3_REGION_NAME
:annotation: = eu-west-2
.. autodata:: config.settings.common.AWS_LOCATION
:annotation: = funkwhale_music
```
### In-place import configuration
```{eval-rst}
.. autodata:: config.settings.common.MUSIC_DIRECTORY_PATH
:annotation: = /srv/funkwhale/data/music
.. autodata:: config.settings.common.MUSIC_DIRECTORY_SERVE_PATH
:annotation: = /srv/funkwhale/data/music
```
### API configuration
```{eval-rst}
.. autodata:: config.settings.common.THROTTLING_ENABLED
.. autodata:: config.settings.common.THROTTLING_RATES
:annotation: = signup=5/d,password-reset=2/d,anonymous-reports=5/d
```
```{dropdown} Standard endpoints
| Endpoint name | Description | Default rate (per user) |
|---------------------------|----------------------------------------------------------|-------------------------|
| `anonymous-wildcard` | Anonymous requests not covered by other limits | 1000 per hour |
| `authenticated-wildcard` | Authenticated requests not covered by other limits | 2000 per hour |
| `authenticated-create` | Authenticated POST requests | 1000 per hour |
| `anonymous-create` | Anonymous POST requests | 1000 per day |
| `authenticated-list` | Authenticated GET requests | 10000 per hour |
| `anonymous-list` | Anonymous GET requests | 10000 per day |
| `authenticated-retrieve` | Authenticated GET requests on resource details | 10000 per hour |
| `anonymous-retrieve` | Anonymous GET requests on resource details | 10000 per day |
| `authenticated-destroy` | Authenticated DELETE requests on resource details | 500 per hour |
| `anonymous-destroy` | Anonymous DELETE requests on resource details | 1000 per day |
| `authenticated-update` | Authenticated PATCH and PUT requests on resource details | 1000 per hour |
| `anonymous-update` | Anonymous PATCH and PUT requests on resource details | 1000 per day |
| `subsonic` | All Subsonic API requests | 2000 per hour |
```
```{dropdown} User action endpoints
| Endpoint name | Description | Default rate (per user) |
|---------------------------|----------------------------------------------------------|-------------------------|
| `login` | User login | 30 per hour |
| `signup` | User signup | 10 per day |
| `verify-email` | Email address confirmation | 20 per hour |
| `password-change` | Password change (when authenticated) | 20 per hour |
| `password-reset` | Password reset request | 20 per hour |
| `password-reset-confirm` | Password reset confirmation | 20 per hour |
| `fetch` | Fetch remote objects | 200 per day |
```
```{dropdown} Dangerous endpoints
| Endpoint name | Description | Default rate (per user) |
|---------------------------|----------------------------------------------------------|-------------------------|
| `authenticated-reports` | Authenticated report submissions | 100 per day |
| `anonymous-reports` | Anonymous report submissions | 10 per day |
| `authenticated-oauth-app` | Authenticated OAuth app creation | 10 per hour |
| `anonymous-oauth-app` | Anonymous OAuth app creation | 10 per day |
| `oauth-authorize` | OAuth app authorization | 100 per hour |
| `oauth-token` | OAuth token creation | 100 per hour |
| `oauth-revoke-token` | OAuth token deletion | 100 per hour |
```
```{eval-rst}
.. autodata:: config.settings.common.ADMIN_URL
.. autodata:: config.settings.common.EXTERNAL_REQUESTS_VERIFY_SSL
.. autodata:: config.settings.common.EXTERNAL_REQUESTS_TIMEOUT
```
### Federation configuration
```{eval-rst}
.. autodata:: config.settings.common.FEDERATION_OBJECT_FETCH_DELAY
.. autodata:: config.settings.common.FEDERATION_DUPLICATE_FETCH_DELAY
```
### Metadata configuration
```{eval-rst}
.. autodata:: config.settings.common.TAGS_MAX_BY_OBJ
.. autodata:: config.settings.common.MUSICBRAINZ_HOSTNAME
.. autodata:: config.settings.common.MUSICBRAINZ_CACHE_DURATION
```
### Channels and podcast configuration
```{eval-rst}
.. autodata:: config.settings.common.PODCASTS_RSS_FEED_REFRESH_DELAY
.. autodata:: config.settings.common.PODCASTS_RSS_FEED_MAX_ITEMS
.. autodata:: config.settings.common.PODCASTS_THIRD_PARTY_VISIBILITY
```
### Subsonic configuration
```{eval-rst}
.. autodata:: config.settings.common.SUBSONIC_DEFAULT_TRANSCODING_FORMAT
```
### Email configuration
```{eval-rst}
.. autodata:: config.settings.common.EMAIL_CONFIG
:annotation: = consolemail://
.. autodata:: config.settings.common.DEFAULT_FROM_EMAIL
:annotation: = Funkwhale <noreply@yourdomain>
.. autodata:: config.settings.common.EMAIL_SUBJECT_PREFIX
```
### Plugin configuration
```{eval-rst}
.. autodata:: config.settings.common.FUNKWHALE_PLUGINS_PATH
```
```{py:data} FUNKWHALE_PLUGINS
---
value: "['funkwhale_api.contrib.scrobbler', 'funkwhale_api.contrib.listenbrainz', 'funkwhale_api.contrib.maloja']"
---
List of Funkwhale plugins to load.
```
### Other settings
```{eval-rst}
.. autodata:: config.settings.common.INSTANCE_SUPPORT_MESSAGE_DELAY
.. autodata:: config.settings.common.FUNKWHALE_SUPPORT_MESSAGE_DELAY
.. autodata:: config.settings.common.MIN_DELAY_BETWEEN_DOWNLOADS_COUNT
.. autodata:: config.settings.common.MARKDOWN_EXTENSIONS
.. autodata:: config.settings.common.LINKIFIER_SUPPORTED_TLDS
```

View file

@ -0,0 +1,137 @@
# Customize the Funkwhale frontend
You can customize the look and behavior of the Funkwhale UI using a JSON configuration file. This file enables you to make very basic changes to the Funkwhale web app.
## Set up your custom configuration
### Create your configuration file
To customize your Funkwhale pod, you need to serve a {file}`settings.json` file at `https://yourinstanceurl/settings.json`. Follow these steps to set up your configuration file:
1. SSH into your Funkwhale server.
2. Navigate to your `/srv/funkwhale` folder
```{code} bash
cd /srv/funkwhale
```
3. Create a new `custom` directory for your file.
```{code} bash
mkdir custom
```
4. Create a new config file and populate it with placeholder settings.
```{code} bash
cat <<EOF > custom/settings.json
{
"additionalStylesheets": [],
"defaultServerUrl": null
}
EOF
```
```{dropdown} Supported parameters
| Parameter | Data type | Description | Example |
|-------------------------|------------|-----------------------------------------------------------------------------------------------|--------------------------------|
| `additionalStylesheets` | Array<URL> | A list of URLs (relative or absolute) pointing to stylesheets. | `["https://test/theme.css"]` |
| `defaultServerUrl` | URL | The URL of the API server you want to connect the frontend to. Defaults to the current domain | `"https://api.yourdomain.com"` |
```
### Configure your reverse proxy
Once you've created your {file}`settings.json` file you need to configure your reverse proxy to serve it.
````{tabbed} Nginx
Add the following snippet to your {file}`/etc/nginx/sites-available/funkwhale.conf` config file:
```
location /settings.json {
alias /srv/funkwhale/custom;
}
```
````
````{tabbed} Apache
Add the following snippet to your vhost configuration:
```
Alias /settings.json /srv/funkwhale/custom/settings.json
```
````
Reload your webserver. You should be able to see the contents of your configuration file at `https://yourinstanceurl/settings.json`.
## Add a custom theme
You can use a custom stylesheet to theme your Funkwhale pod. To do this:
1. Navigate to your {file}`/srv/funkwhale/custom` directory.
```{code} bash
cd /srv/funkwhale/custom
```
2. Copy your CSS file to this directory, or create a new one.
```{code} bash
# A basic CSS file. Turns the pod's background red.
cat <<EOF > custom.css
body {
background-color: red;
}
EOF
```
3. Add the location of your CSS file to the `additionalStylesheets` parameter in your {file}`settings.json` file.
```{code} bash
nano settings.json
# Add ["/front/custom/custom.css"] to the additionalStylesheets parameter
# The resulting file looks like this:
# {
# "additionalStylesheets": ["/front/custom/custom.css"],
# "defaultServerUrl": null
# }
```
4. Add the whole {file}`custom` dir to your vhost configuration.
````{tabbed} Nginx
Add the following to your {file}`/etc/nginx/sites-available/funkwhale.conf` file:
```
location /custom {
alias /srv/funkwhale/custom;
}
```
````
````{tabbed} Apache
Add the following to your vhost file.
```
Alias /custom /srv/funkwhale/custom
<Directory "/srv/funkwhale/custom">
Options FollowSymLinks
AllowOverride None
Require all granted
</Directory>
```
````
5. Restart your webserver.
Refresh your Funkwhale app. The background should now be red.

View file

@ -0,0 +1,20 @@
# Configure your Funkwhale pod
You can customize a lot of settings relating to your Funkwhale pod. These range from server-level settings to user-specific settings. Check out the guides in this section to get started.
```{toctree}
---
caption: Configuration options
maxdepth: 1
---
Environment file <env_file>
Instance settings <instance_settings>
object_storage
Frontend customization <frontend>
optimize
ldap
change_url
mrf
```

View file

@ -0,0 +1,211 @@
# Instance settings
You can find instance settings on your pod's web interface. These settings control high level pod configuration. You don't need to restart the pod after changing these settings.
To find your instance settings:
```{tabbed} Desktop
1. Log in to your {term}`pod`.
2. Select the wrench icon ({fa}`wrench`) at the top of the sidebar to open the {guilabel}`Administration` menu.
3. Select {guilabel}`Settings`. The {guilabel}`Instance settings` page opens.
```
```{tabbed} Mobile
1. Log in to your {term}`pod`.
2. Select the wrench icon ({fa}`wrench`) at the top of the page to open the {guilabel}`Administration` menu.
3. Select {guilabel}`Settings`. The {guilabel}`Instance settings` page opens.
```
## Available settings
### Instance information
```{glossary}
Pod name
The public name of your Funkwhale pod. This is displayed on the "Home" and "About" pages.
Short description
A short description of your pod. Users see this on the pod's "Home" page.
Long description
A longer description of your pod. Users see this on the pod's "About" page. Supports markdown formatting.
Contact email
A contact email address that users and visitors can use to contact the pod administrator.
Rules
A free text field for you to add your pod's rules and code of conduct. This is seen on the pod's "About" page. Supports markdown formatting
Terms of service
A free text field for you to add your pod's terms of service and privacy policy. This is seen on the pod's "About" page. Supports markdown formatting.
Banner image
A large image seen on the pod's "Home" and "About" pages. The image should be at least 600x100px.
Support message
A short message you can display to your pod's users to ask for support or just send a periodic message. Supports markdown.
```
---
### Sign-ups
```{glossary}
Open registration to new users
Enable this setting to allow new users to create an account on your pod.
Enable manual sign-up validation
Enable this setting to require all new registrations to be validated by a moderator.
Sign-up form customization
Use this tool to create a custom sign-up form. New users see this form when creating a new account. Supports markdown
```
---
### Security
````{glossary}
API Requires authentication
Controls whether {term}`unauthenticated users <Anonymous>` can access content on your pod. If __enabled__, users need to have an account on your pod to access content. If __disabled__, users without an account can listen to content stored in public libraries.
```{seealso}
{doc}`../../moderator_documentation/content/library_visibility`.
```
Default permissions
A list of {term}`permissions` that are added to users by default. If your pod is publicly accessible, you should leave this empty.
Upload quota
The default upload quota for users in MB. You can override this on a per-user basis.
```{seealso}
{doc}`../../moderator_documentation/reports/handle_users`
```
````
---
### Music
```{glossary}
Transcoding enabled
Enable this setting to let your server transcode files into a different format if the client requests it. This is useful if a device doesn't support formats like Ogg or FLAC.
Transcoding cache duration
The number of minutes you want to store transcoded files on your server. Funkwhale removes transcoded tracks that haven't been downloaded within this duration to save space.
```
### Channels
```{glossary}
Enable channels
Whether user channels can be created and followed on your pod.
Max channels allowed per user
The maximum number of channels each user can create.
```
---
### Playlists
```{glossary}
Max tracks per playlist
The maximum number of tracks a user can add to a playlist.
```
---
### Moderation
```{glossary}
Enable allow-listing
Enable this setting to ensure your pod only communicates with pods you have added to your allow list. When this setting is disabled, your pod will communicate with all other servers not included in your deny list.
Publish your allowed-domains list
Whether to make your list of allowed domains public. Enable this if you want users to check who you are federating with.
Accountless report categories
A list of {term}`categories <Report categories>` that {term}`anonymous` users can submit.
```
---
### Federation
```{glossary}
Federation enabled
Whether to enable federation features on your pod.
Enable public index
Whether to allow other pods and bots to index public content on your pod.
Federation collection page size
The number of items to display in ActivityPub collections.
Music cache duration
The number of minutes you want to store local copies of federated tracks on your server. Funkwhale removes federated tracks that haven't been downloaded within this duration to save space.
Federation actor fetch delay
The number of minutes the server waits before refetching actors on request authentication.
```
---
### Subsonic
```{glossary}
Enabled Subsonic API
Whether to enable the Subsonic API. This controls whether users are able to connect to your pod using Subsonic apps.
```
---
### User Interface
```{glossary}
Custom CSS code
Add CSS rules to control the look and feel of your pod. These rules are added to a `<style>` tag on each page.
Funkwhale Support message
Whether to show a notification to your pod's users to support the Funkwhale project.
```
---
### Statistics
```{glossary}
Enable usage and library stats in nodeinfo endpoint
Whether to share anonymized usage and library statistics in your pod's nodeinfo endpoint.
Private mode in nodeinfo
Enable this setting to indicate you don't want your instance to be tracked by third-party services.
```

View file

@ -0,0 +1,156 @@
# Configure LDAP
{abbr}`LDAP (Lightweight Directory Access Protocol)` is a protocol for providing directory services. It acts as a central authority for user login information. Funkwhale supports LDAP through the [Django LDAP authentication module](https://django-auth-ldap.readthedocs.io/).
```{important}
LDAP users can't change their password in the app.
```
## Dependencies
LDAP support requires extra dependencies. We include these in our requirements files to make it easier to set up. If you aren't using LDAP, you can safely remove these.
```{dropdown} OS dependencies
- `libldap2-dev`
- `libsasl2-dev`
```
```{dropdown} Python dependencies
- `python-ldap`
- `python-django-auth-ldap`
```
## Environment variables
You can configure LDAP authentication using environment variables in your `.env` file.
### Basic features
```{py:data} LDAP_ENABLED
---
value: True
type: Boolean
---
Set this to `True` to enable LDAP support
```
```{py:data} LDAP_SERVER_URI
---
type: URI
value: ldap://my.host:389
---
The LDAP {abbr}`URI (Uniform Resource Identifier)` of your authentication server.
```
```{py:data} LDAP_BIND_DN
---
type: String
value: cn=admin,dc=domain,dc=com
---
LDAP user {abbr}`DN (Distinguised Name)` to bind on so you can perform searches.
```
```{py:data} LDAP_BIND_PASSWORD
---
type: String
value: bindpassword
---
LDAP user password for bind {abbr}`DN (Distinguised Name)`.
```
```{py:data} LDAP_SEARCH_FILTER
---
type: String
value: (|(cn={0})(mail={0}))
---
The LDAP user filter, using `{0}` as the username placeholder. Uses standard [LDAP search syntax](https://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx).
```
```{py:data} LDAP_START_TLS
---
type: Boolean
value: False
---
Set to `True` to enable LDAP StartTLS support.
```
```{py:data} LDAP_ROOT_DN
---
type: String
value: dc=domain,dc=com
---
The LDAP search root {abbr}`DN (Distinguised Name)`. Supports several entries in a comma-delimited list.
```
```{py:data} LDAP_USER_ATTR_MAP
---
type: String
value: first_name:givenName, last_name:sn, username:cn, email:mail
---
A mapping of Django user attributes to LDAP values.
```
```{py:data} AUTH_LDAP_BIND_AS_AUTHENTICATING_USER
---
type: Boolean
value: False
---
Controls whether to use direct binding.
```
### Group features
LDAP provides extra features for working with groups. Group configuration is an advanced feature. Most users don't need to configure these settings.
```{seealso}
[Django's LDAP documentation](https://django-auth-ldap.readthedocs.io/en/latest/groups.html) for groups.
```
```{py:data} LDAP_GROUP_DN
---
type: String
value: ou=groups,dc=domain,dc=com
---
The LDAP group search root {abbr}`DN (Distinguised Name)`. This needs to be set to `True` to enable group features.
```
```{py:data} LDAP_GROUP_FILTER
---
type: String
value: objectClass=groupOfNames
---
The LDAP group filter.
```
```{py:data} LDAP_REQUIRE_GROUP
---
type: String
value: cn=enabled,ou=groups,dc=domain,dc=com
---
The group that users need to be a member of to authenticate.
```
```{py:data} LDAP_DENY_GROUP
---
type: String
value: cn=disabled,ou=groups,dc=domain,dc=com
---
A group whose members can't authenticate.
```

View file

@ -0,0 +1,182 @@
# Message Rewrite Facility (MRF)
Funkwhale includes a feature that mimics [Pleromas Message Rewrite Facility (MRF)](https://docs-develop.pleroma.social/backend/configuration/mrf/). The MRF enables instance admins to create custom moderation rules. You can use these rules to complement Funkwhale's [built-in moderation tools](../../moderator_documentation/index.md).
## Architecture
The MRF is a pluggable system that processes messages and forwards them to a list of registered policies. Each policy can mutate the message, leave it as is, or discard it.
We implement some of Funkwhale's built-in moderation tools as a MRF policy. For example:
- Allow-list, when checking incoming messages ([code](https://dev.funkwhale.audio/funkwhale/funkwhale/blob/stable/api/funkwhale_api/moderation/mrf_policies.py)).
- Domain and user blocking, when checking incoming messages ([code](https://dev.funkwhale.audio/funkwhale/funkwhale/blob/stable/api/funkwhale_api/federation/mrf_policies.py))
```{note}
Pleroma MRF policies can also affect outgoing messages. This is not currently supported in Funkwhale.
```
## Disclaimer
Writing custom MRF rules can impact the performance and stability of your pod. It can also affect message delivery. Every time your pod receives a message it calls your policy.
The Funkwhale project consider all custom MRF policies to fall under the purview of the AGPL. This means you're required to release the source of your custom MRF policy modules publicly.
## Write your first MRF policy
MRF policies are written as Python 3 functions that take at least one `payload` parameter. This payload is the raw ActivityPub message, received via HTTP, following the HTTP signature check.
In the example below we write a policy that discards all Follow requests from listed domains:
```{code} py
import urllib.parse
from funkwhale_api.moderation import mrf
BLOCKED_FOLLOW_DOMAINS = ['domain1.com', 'botdomain.org']
# You need to register the policy to apply it.
# The name can be anything you want. It will appear in the mrf logs
@mrf.inbox.register(name='blocked_follow_domains')
def blocked_follow_domains_policy(payload, **kwargs):
actor_id = payload.get('actor')
domain = urllib.parse.urlparse(actor_id).hostname
if domain not in BLOCKED_FOLLOW_DOMAINS:
# Raising mrf.Skip isn't necessary but it provides
# info in the debug logs. Otherwise, you can return:
raise mrf.Skip("This domain isn't blocked")
activity_type = payload.get('type')
object_type = payload.get('object', {}).get('type')
if object_type == 'Follow' and activity_type == 'Create':
raise mrf.Discard('Follow from blocked domain')
```
You need to store this code in a Funkwhale plugin. To create one, execute the following:
```{code} bash
# Plugin names can only contain ASCII letters, numbers and underscores.
export PLUGIN_NAME="myplugin"
# This is the default path where Funkwhale will look for plugins.
# If you want to use another path, update this path and ensure
# your PLUGINS_PATH is also included in your .env file.
export PLUGINS_PATH="/srv/funkwhale/plugins/"
mkdir -p $PLUGINS_PATH/$PLUGIN_NAME
cd $PLUGINS_PATH/$PLUGIN_NAME
touch __init__.py # required to make the plugin a valid Python package
# Create the required apps.py file to register our plugin in Funkwhale.
cat > apps.py <<EOF
from django.apps import AppConfig
class Plugin(AppConfig):
name = "$PLUGIN_NAME"
EOF
```
Once you've created the plugin, put your code in an `mrf_policies.py` file. Place this file inside the plugin directory. Next, enable the plugin in your {file}`.env` file by adding its name to the {attr}`FUNKWHALE_PLUGINS` list. Add this variable if it's not there.
## Test your MRF policy
To make the job of writing and debugging MRF policies easier, we provide a management command.
- List registered MRF policies.
````{tabbed} Debian
```{code} bash
poetry run python manage.py mrf_check --list
```
````
````{tabbed} Docker
```{code} bash
docker-compose run --rm api python manage.py mrf_check --list
```
````
- Check how your MRF policy handles a follow.
````{tabbed} Debian
```{code} bash
export MRF_MESSAGE='{"actor": "https://normal.domain/@alice", "type": "Create", "object": {"type": "Follow"}}'
echo $MRF_MESSAGE | poetry run python manage.py mrf_check inbox - -p blocked_follow_domains
```
````
````{tabbed} Docker
```{code} bash
export MRF_MESSAGE='{"actor": "https://normal.domain/@alice", "type": "Create", "object": {"type": "Follow"}}'
echo $MRF_MESSAGE | docker-compose run --rm api python manage.py mrf_check inbox - -p blocked_follow_domains
```
````
- Check how your MRF handles a problematic follow.
````{tabbed} Debian
```{code} bash
export MRF_MESSAGE='{"actor": "https://botdomain.org/@bob", "type": "Create", "object": {"type": "Follow"}}'
echo $MRF_MESSAGE | poetry run python manage.py mrf_check inbox - -p blocked_follow_domains
```
````
````{tabbed} Docker
```{code} bash
export MRF_MESSAGE='{"actor": "https://botdomain.org/@bob", "type": "Create", "object": {"type": "Follow"}}'
echo $MRF_MESSAGE | docker-compose run --rm api python manage.py mrf_check inbox - -p blocked_follow_domains
```
````
- Check a payload against activity already present in the database. You can find the UUID of an activity by visiting `/api/admin/federation/activity`.
````{tabbed} Debian
```{code} bash
export ACTIVITY_UUID="06208aea-c687-4e8b-aefd-22f1c3f76039"
echo $MRF_MESSAGE | poetry run python manage.py mrf_check inbox $ACTIVITY_UUID -p blocked_follow_domains
```
````
````{tabbed} Docker
```{code} bash
export ACTIVITY_UUID="06208aea-c687-4e8b-aefd-22f1c3f76039"
echo $MRF_MESSAGE | docker-compose run --rm api python manage.py mrf_check inbox $ACTIVITY_UUID -p blocked_follow_domains
```
````
There are extra options for testing MRF policies. Check the command help for more options.
````{tabbed} Debian
```{code} bash
poetry run python manage.py mrf_check --help
```
````
````{tabbed} Docker
```{code}
docker-compose run --rm api python manage.py mrf_check --help
```
````

View file

@ -0,0 +1,171 @@
# Store media in an object store
By default, Funkwhale stores all media data in the `/srv/funkwhale/data/media` directory. If you prefer to use an S3-compatible object store, follow the instructions in this guide.
```{contents}
:local:
:depth: 1
```
## Secure your object store
Before you begin, you need to secure your object store. Many S3-compatible stores list contents in the root by default. This exposes the URLs of your audio files and means that users can bypass authentication.
To prevent listing content, add the following policy to your S3-compatible object store.
```{code} json
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"s3:GetObject"
],
"Effect": "Allow",
"Principal": {
"AWS": [
"*"
]
},
"Resource": [
"arn:aws:s3:::<yourbucketname>/*"
],
"Sid": "Public"
}
]
}
```
If you're using `awscli`, you can store this policy in a `/tmp/policy` file and apply it using the following command:
```{code} bash
aws s3api put-bucket-policy --bucket <yourbucketname> --policy file:///tmp/policy
```
## Update your environment file
To set up S3-compatible storage, fill out the relevant details in the `.env` file. If you want to serve audio files from the bucket, set `PROXY_MEDIA` to `false`.
````{dropdown} Environment variables
```{eval-rst}
.. autodata:: config.settings.common.AWS_QUERYSTRING_AUTH
.. autodata:: config.settings.common.AWS_QUERYSTRING_EXPIRE
.. autodata:: config.settings.common.AWS_ACCESS_KEY_ID
.. autodata:: config.settings.common.AWS_SECRET_ACCESS_KEY
.. autodata:: config.settings.common.AWS_STORAGE_BUCKET_NAME
.. autodata:: config.settings.common.AWS_S3_CUSTOM_DOMAIN
.. autodata:: config.settings.common.AWS_S3_ENDPOINT_URL
:annotation: = https://minio.mydomain.com
.. autodata:: config.settings.common.AWS_S3_REGION_NAME
:annotation: = eu-west-2
.. autodata:: config.settings.common.AWS_LOCATION
:annotation: = funkwhale_music
.. autodata:: config.settings.common.PROXY_MEDIA
```
````
## Set up your reverse proxy
```{note}
Serving files from object storage is not currently supported on Apache deployments.
```
Serving files from an object store requires some changes to the reverse proxy.
1. Open your Nginx configuration file in an editor.
````{tabbed} Debian
```{code} bash
sudo nano /etc/nginx/sites-available/funkwhale.template
```
````
````{tabbed} Docker
```{code} bash
nano /srv/funkwhale/nginx/funkwhale.template
```
````
2. Comment out the `location /_protected/media/` block by adding a `#` to the start of each line.
```{code}
# location /_protected/media/ {
# internal;
# alias ${MEDIA_ROOT};
# }
```
3. Uncomment the `location ~ /_protected/media/(.+)` block by removing the `#` from the start of each line.
```{code}
location ~ /_protected/media/(.+) {
internal;
proxy_set_header Authorization "";
proxy_pass $1;
}
```
4. Add your S3 store URL to the `img-src` and `media-src` headers.
```{code}
add_header Content-Security-Policy "...img-src 'self' https://<your-s3-URL> data:;...media-src https://<your-s3-URL> 'self' data:";
```
5. Test your Nginx configuration.
```{code} bash
sudo nginx -t
```
6. Restart Funkwhale and Nginx to pick up the changes.
````{tabbed} Debian
```{code} bash
sudo systemctl restart funkwhale.target
sudo systemctl restart nginx
```
````
````{tabbed} Docker
```{code} bash
docker-compose restart
sudo systemctl restart nginx
```
````
That's it! Files are now uploaded to and stored from your S3 bucket.
## Troubleshooting
````{dropdown} No Resolver Found
You may see the following error when streaming music from your S3-compatible store:
```{code}
[error] 2832#2832: *1 no resolver defined to resolve [address] client: [IP], server: [servername], request: "GET API request", host: "[your_domain]", referrer: "[your_domain/library]"
```
This happens when the Nginx config is unable to use your servers DNS resolver. We're still looking into this issue. You can work around this by adding a resolver to the `location ~/_protected/media/(.+)` block.
```{code}
location ~ /_protected/media/(.+) {
resolver 1.1.1.1;
internal;
proxy_set_header Authorization "";
proxy_pass $1;
}
```
````

View file

@ -0,0 +1,57 @@
# Optimize memory usage
Funkwhale has a reasonable memory footprint. If you're running Funkwhale on a limited device, you can use these tweaks to reduce the footprint.
## Reduce workers concurrency
Funkwhale uses Celery to handle asynchronous tasks. By default, Celery spawns a worker per CPU core. This can lead to higher memory usage.
You can set the number of workers using the `CELERYD_CONCURRENCY` variable in your `.env` file. For example, a value of `CELERYD_CONCURRENCY=1` spawns a single worker.
```{note}
Reducing the number of celery workers slows down the handling of asynchronous tasks. On larger instances, this can cause performance problems.
```
## Switch to solo pool execution
Celery uses a `prefork` pool by default. This enables the server to process many tasks at the same time. You can switch to a `solo` pool which handles tasks one at a time. This reduces memory overhead but removes the ability to process tasks concurrently.
````{tabbed} Debian
1. Open your `funkwhale-worker` unit file in an editor.
```{code} bash
sudo nano /etc/systemd/system/funkwhale-worker.service
```
2. Add the `--pool=solo` flag to the `ExecStart` line of your unit file.
```{code} text
ExecStart=/srv/funkwhale/.local/bin/poetry run celery -A --pool=solo funkwhale_api.taskapp worker -l INFO --concurrency=${CELERYD_CONCURRENCY}
```
3. Restart the Celery service.
```{code} bash
sudo systemctl restart funkwhale-worker.service
```
````
````{tabbed} Docker
1. Add the `--pool=solo` flag to the `celerybeat` command in `docker-compose.yml`.
```{code} yaml
celerybeat:
command: celery -A --pool=solo funkwhale_api.taskapp beat --pidfile= -l INFO
```
2. Restart Celery.
```{code} bash
docker-compose restart celerybeat
```
````