nextcloud: Use php-fpm container instead of apache container

- Configuring just php-fpm is easier compared to configuring Apache + mod_php.
There is no need to configure trusted proxies as the requests are made using the
FastCGI protocol.

- There is no need for a full web server as we already run Apache.

- Place nextcloud data in /var/lib/container so that non-PHP files can be served
directly without php-fpm involved. This location is more suitable for switching
to nextcloud based on a .deb file (if ever). This is done by configuring the
volume to serve a bind mounted directory of our choice.

- Update Apache configuration to proxy to php-fpm instead of another web server.
Include the changes needed for Apache configuration to serve non-php files
directly.

- Managed the volume using quadlet podman systemd generator.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
Sunil Mohan Adapa 2024-04-11 13:33:42 -07:00 committed by James Valleroy
parent e7e1a6b41d
commit 85cc9f08fa
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
5 changed files with 80 additions and 25 deletions

View File

@ -489,22 +489,44 @@ def is_package_manager_busy():
return False
def podman_create(container_name: str, image_name: str,
volumes: dict[str, str] | None = None,
def podman_create(container_name: str, image_name: str, volume_name: str,
volume_path: str, volumes: dict[str, str] | None = None,
env: dict[str, str] | None = None):
"""Remove and recreate a podman container."""
service_stop(f'{volume_name}-volume.service')
service_stop(container_name)
directory = pathlib.Path('/etc/containers/systemd')
directory.mkdir(parents=True, exist_ok=True)
subprocess.run(['podman', 'volume', 'rm', '--force', volume_name],
check=False)
directory = pathlib.Path('/etc/containers/systemd')
directory.mkdir(parents=True, exist_ok=True)
pathlib.Path(volume_path).mkdir(parents=True, exist_ok=True)
# Create storage volume
volume_file = directory / f'{volume_name}.volume'
contents = f'''[Volume]
Device={volume_path}
Driver=local
VolumeName={volume_name}
Options=bind
'''
volume_file.write_text(contents)
service_file = directory / f'{container_name}.container'
volume_lines = '\n'.join([
f'Volume={source}:{dest}' for source, dest in (volumes or {}).items()
])
env_lines = '\n'.join(
[f'Environment={key}={value}' for key, value in (env or {}).items()])
contents = f'''[Container]
contents = f'''[Unit]
Requires=nextcloud-freedombox-volume.service
After=nextcloud-freedombox-volume.service
[Container]
AutoUpdate=registry
ContainerName=%N
{env_lines}
@ -522,11 +544,18 @@ WantedBy=default.target
service_daemon_reload()
def podman_uninstall(container_name: str, volume_name: str, image_name: str):
def podman_uninstall(container_name: str, volume_name: str, image_name: str,
volume_path: str):
"""Remove a podman container's components and systemd unit."""
subprocess.run(['podman', 'volume', 'rm', volume_name], check=True)
subprocess.run(['podman', 'image', 'rm', image_name], check=True)
subprocess.run(['podman', 'volume', 'rm', '--force', volume_name],
check=True)
subprocess.run(['podman', 'image', 'rm', '--ignore', image_name],
check=True)
volume_file = pathlib.Path(
'/etc/containers/systemd/') / f'{volume_name}.volume'
volume_file.unlink(missing_ok=True)
service_file = pathlib.Path(
'/etc/containers/systemd/') / f'{container_name}.container'
service_file.unlink(missing_ok=True)
shutil.rmtree(volume_path, ignore_errors=True)
service_daemon_reload()

View File

@ -87,7 +87,7 @@ class NextcloudApp(app_module.App):
self.add(firewall)
firewall_local_protection = FirewallLocalProtection(
'firewall-local-protection-nextcloud', ['8181'])
'firewall-local-protection-nextcloud', ['9000'])
self.add(firewall_local_protection)
webserver = Webserver('webserver-nextcloud', 'nextcloud-freedombox',

View File

@ -16,10 +16,36 @@ Redirect 301 /.well-known/caldav /nextcloud/remote.php/dav
Redirect 301 /.well-known/webfinger /nextcloud/index.php/.well-known/webfinger
Redirect 301 /.well-known/nodeinfo /nextcloud/index.php/.well-known/nodeinfo
<Location /nextcloud>
ProxyPass http://127.0.0.1:8181
Alias /nextcloud/ /var/lib/nextcloud/
## Send the scheme from user's request to enable Nextcloud to redirect URLs,
## set cookies, set absolute URLs (if any) properly.
RequestHeader set X-Forwarded-Proto 'https' env=HTTPS
</Location>
<IfModule proxy_fcgi_module>
ProxyPassMatch "^/nextcloud/(.*\.php(/.*)?)$" "fcgi://localhost:9000/var/www/html/$1"
</IfModule>
<Directory /var/lib/nextcloud/>
<IfModule !mod_php7.c>
<IfModule proxy_fcgi_module>
# Enable http authorization headers
<IfModule setenvif_module>
SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1
</IfModule>
<FilesMatch ".+\.phps$">
# Deny access to raw php sources by default
# To re-enable it's recommended to enable access to the files
# only in specific virtual host or directory
Require all denied
</FilesMatch>
# Deny access to files without filename (e.g. '.php')
<FilesMatch "^\.ph(ar|p|ps|tml)$">
Require all denied
</FilesMatch>
</IfModule>
</IfModule>
Require all granted
# Allow a limited set of directives in .htaccess files found in /, /config,
# and /data directories of nextcloud.
AllowOverride AuthConfig FileInfo Indexes Limit Options
</Directory>

View File

@ -48,9 +48,7 @@ clients = [{
backup = {
'data': {
'directories': [
'/var/lib/containers/storage/volumes/nextcloud-volume-freedombox/'
],
'directories': ['/var/lib/nextcloud/'],
'files': ['/var/lib/plinth/backups-data/nextcloud-database.sql']
}
}

View File

@ -17,8 +17,8 @@ from plinth.actions import privileged
CONTAINER_NAME = 'nextcloud-freedombox'
SERVICE_NAME = 'nextcloud-freedombox'
VOLUME_NAME = 'nextcloud-volume-freedombox'
IMAGE_NAME = 'docker.io/library/nextcloud:stable-apache'
VOLUME_NAME = 'nextcloud-freedombox'
IMAGE_NAME = 'docker.io/library/nextcloud:stable-fpm'
DB_HOST = 'localhost'
DB_NAME = 'nextcloud_fbx'
@ -26,8 +26,7 @@ DB_USER = 'nextcloud_fbx'
GUI_ADMIN = 'nextcloud-admin'
REDIS_DB = 8 # Don't clash with other redis apps
_volume_path = pathlib.Path(
'/var/lib/containers/storage/volumes/') / VOLUME_NAME
_data_path = pathlib.Path('/var/lib/nextcloud/')
_systemd_location = pathlib.Path('/etc/systemd/system/')
_cron_service_file = _systemd_location / 'nextcloud-cron-freedombox.service'
_cron_timer_file = _systemd_location / 'nextcloud-cron-freedombox.timer'
@ -55,16 +54,18 @@ def setup():
'/run/slapd/ldapi': '/run/slapd/ldapi',
VOLUME_NAME: '/var/www/html'
}
env = {'TRUSTED_PROXIES': '127.0.0.1', 'OVERWRITEWEBROOT': '/nextcloud'}
env = {'OVERWRITEWEBROOT': '/nextcloud'}
action_utils.podman_create(container_name=CONTAINER_NAME,
image_name=IMAGE_NAME, volumes=volumes, env=env)
image_name=IMAGE_NAME, volume_name=VOLUME_NAME,
volume_path=str(_data_path), volumes=volumes,
env=env)
action_utils.service_start(CONTAINER_NAME)
# OCC isn't immediately available after the container is spun up.
# Wait until CAN_INSTALL file is available.
timeout = 300
while timeout > 0:
if (_volume_path / '_data/config/CAN_INSTALL').exists():
if (_data_path / 'config/CAN_INSTALL').exists():
break
timeout = timeout - 1
@ -278,7 +279,8 @@ def uninstall():
_drop_database()
action_utils.podman_uninstall(container_name=CONTAINER_NAME,
volume_name=VOLUME_NAME,
image_name=IMAGE_NAME)
image_name=IMAGE_NAME,
volume_path=str(_data_path))
for path in [_cron_service_file, _cron_timer_file]:
path.unlink(missing_ok=True)
@ -366,7 +368,7 @@ def _get_dbpassword():
def _create_redis_config():
"""Create a php file for Redis configuration."""
config_file = _volume_path / '_data/config/freedombox.config.php'
config_file = _data_path / 'config/freedombox.config.php'
file_content = fr'''<?php
$CONFIG = [
'memcache.distributed' => '\OC\Memcache\Redis',