rclone: Add WebDAV volume share service
This allows for serving any Podman volume over WebDAV, with the ability to serve a dedicated subdirectory per user. Authentication is provided by LDAP, and users are expected to be in the `rclone_user` group.
This commit is contained in:
parent
bc592eca86
commit
96a1e177e3
|
@ -157,6 +157,14 @@ systemd:
|
|||
[Service]
|
||||
Environment=UPSTREAM_HOST=shiori UPSTREAM_PORT=8080
|
||||
|
||||
- name: nginx-proxy-http@webdav.localhost.service
|
||||
enabled: true
|
||||
dropins:
|
||||
- name: rclone-upstream.conf
|
||||
contents: |
|
||||
[Service]
|
||||
Environment=UPSTREAM_HOST=rclone-webdav UPSTREAM_PORT=8080
|
||||
|
||||
- name: letsencrypt-dns-register@localhost.service
|
||||
enabled: true
|
||||
dropins:
|
||||
|
@ -165,7 +173,7 @@ systemd:
|
|||
[Service]
|
||||
ExecStartPre=/bin/podman volume create --ignore letsencrypt-certificates
|
||||
ExecStart=
|
||||
ExecStart=/bin/sh -c "V=$(podman volume mount letsencrypt-certificates) && cp -Rv /etc/ssl/private $V && chown -R 10000:10000 $V"
|
||||
ExecStart=/bin/sh -c "V=$(podman volume mount letsencrypt-certificates) && cp -Rv /etc/ssl/private/. $V && chown -R 10000:10000 $V"
|
||||
ExecStartPost=/bin/podman volume unmount letsencrypt-certificates
|
||||
|
||||
storage:
|
||||
|
|
|
@ -12,9 +12,14 @@ FROM docker.io/debian:bookworm-slim@sha256:7802002798b0e351323ed2357ae6dc5a8c4d0
|
|||
RUN apt-get update -y && apt-get upgrade -y && apt-get install -y --no-install-recommends \
|
||||
ca-certificates gettext gosu
|
||||
|
||||
RUN adduser --system --group --uid 10000 --no-create-home rclone
|
||||
RUN apt-get update -y && apt-get install -y --no-install-recommends \
|
||||
curl python3 python3-ldap3
|
||||
|
||||
RUN adduser --system --group --uid 10000 --no-create-home rclone && \
|
||||
install -D --owner rclone --group rclone -d /var/lib/rclone-storage
|
||||
|
||||
COPY --from=builder /build /
|
||||
COPY container/ldap-auth-proxy /usr/bin
|
||||
|
||||
USER rclone
|
||||
ENTRYPOINT ["/usr/bin/rclone"]
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
An auth proxy for Rclone, backed by a generic LDAP server.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
|
||||
from ldap3 import Server, Connection, AUTO_BIND_NO_TLS, SUBTREE
|
||||
from ldap3.utils.conv import escape_filter_chars
|
||||
|
||||
ldap_host = os.getenv("AUTH_LDAP_HOST", "localhost")
|
||||
ldap_port = os.getenv("AUTH_LDAP_PORT", 389)
|
||||
|
||||
ldap_bind_dn = os.getenv("AUTH_LDAP_BIND_DN", "")
|
||||
ldap_password = os.getenv("AUTH_LDAP_PASSWORD", "")
|
||||
|
||||
ldap_base_dn = os.getenv("AUTH_LDAP_BASE_DN", "ou=people,dc=ldap,dc=local")
|
||||
ldap_search_filter = os.getenv("AUTH_LDAP_SEARCH_FILTER", "(uid={username})")
|
||||
|
||||
serve_base_dir = os.getenv("SERVE_BASE_DIR", "/var/lib/rclone-storage")
|
||||
serve_append_user_dir = os.getenv("SERVE_APPEND_USER_DIR", "false")
|
||||
|
||||
|
||||
def main():
|
||||
i = json.load(sys.stdin)
|
||||
username = i["user"]
|
||||
password = i["pass"]
|
||||
|
||||
if not username or not password:
|
||||
print("Error: No username or password given", file=sys.stderr)
|
||||
return
|
||||
|
||||
server = Server(ldap_host, port=int(ldap_port))
|
||||
connection = Connection(
|
||||
server, user=ldap_bind_dn, password=ldap_password, auto_bind=AUTO_BIND_NO_TLS
|
||||
)
|
||||
connection.search(
|
||||
search_base=ldap_base_dn,
|
||||
search_filter=ldap_search_filter.format(username=escape_filter_chars(username)),
|
||||
search_scope=SUBTREE,
|
||||
attributes=["uid"],
|
||||
time_limit=15,
|
||||
)
|
||||
|
||||
total_entries = len(connection.response)
|
||||
if total_entries == 0:
|
||||
print(f"Error: No results found for '{username}'", file=sys.stderr)
|
||||
return
|
||||
if total_entries > 1:
|
||||
print(f"Error: More than one result found for '{username}'", file=sys.stderr)
|
||||
return
|
||||
|
||||
response = connection.response[0]
|
||||
if "uid" not in response["attributes"] or len(response["attributes"]["uid"]) == 0:
|
||||
print(f"Error: Empty UID attribute returned for '{username}'", file=sys.stderr)
|
||||
return
|
||||
|
||||
uid = response["attributes"]["uid"][0]
|
||||
serve_root = (
|
||||
serve_base_dir + "/" + uid
|
||||
if serve_append_user_dir == "true"
|
||||
else serve_base_dir
|
||||
)
|
||||
output = {
|
||||
"type": "local",
|
||||
"_root": serve_root,
|
||||
"_obscure": "pass",
|
||||
"user": uid,
|
||||
"pass": password,
|
||||
"host": "",
|
||||
}
|
||||
|
||||
json.dump(output, sys.stdout)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,20 @@
|
|||
[Unit]
|
||||
Description=Rclone WebDAV Server
|
||||
Wants=container-build@rclone.service lldap.service
|
||||
After=container-build@rclone.service lldap.service
|
||||
|
||||
[Container]
|
||||
AutoUpdate=local
|
||||
ContainerName=%N
|
||||
Environment=SERVE_APPEND_USER_DIR=true
|
||||
EnvironmentFile=%E/coreos-home-server/rclone/rclone.env
|
||||
Exec=serve webdav --addr :8080 --auth-proxy /usr/bin/ldap-auth-proxy
|
||||
Image=localhost/rclone:latest
|
||||
Network=internal
|
||||
Volume=%N:/var/lib/rclone-storage:z
|
||||
|
||||
[Service]
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -2,6 +2,14 @@
|
|||
RCLONE_LOG_LEVEL=INFO
|
||||
RCLONE_LINKS=true
|
||||
|
||||
# Configuration for LDAP authentication.
|
||||
AUTH_LDAP_HOST=lldap
|
||||
AUTH_LDAP_PORT=3890
|
||||
AUTH_LDAP_BASE_DN=ou=people,dc=ldap,dc=local
|
||||
AUTH_LDAP_BIND_DN=uid=${LLDAP_ADMIN_USERNAME},ou=people,dc=ldap,dc=local
|
||||
AUTH_LDAP_PASSWORD=${LLDAP_ADMIN_PASSWORD}
|
||||
AUTH_LDAP_SEARCH_FILTER=(&(memberof=cn=rclone_user,ou=groups,dc=ldap,dc=local)(|(uid={username})(mail={username})))
|
||||
|
||||
# Configuration for default encrypted remote, configured to wrap the default unencrypted remote.
|
||||
# Password and salt values must be processed via `rclone obscure` before setting.
|
||||
RCLONE_CONFIG_CRYPT_TYPE=crypt
|
||||
|
|
|
@ -6,3 +6,5 @@ storage:
|
|||
local: service/rclone/
|
||||
- path: /etc/systemd/system
|
||||
local: service/rclone/systemd/
|
||||
- path: /etc/containers/systemd
|
||||
local: service/rclone/quadlet/
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
[Unit]
|
||||
Description=Rclone WebDAV Server for Volume %I
|
||||
Wants=container-build@rclone.service
|
||||
After=container-build@rclone.service
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
NotifyAccess=all
|
||||
SyslogIdentifier=%N
|
||||
Restart=on-failure
|
||||
Environment=PODMAN_SYSTEMD_UNIT=%n
|
||||
ExecStart=/bin/podman run --replace --name %p-%i --net internal --sdnotify=conmon \
|
||||
--env-file %E/coreos-home-server/rclone/rclone.env \
|
||||
--volume %i:/var/lib/rclone-storage:z \
|
||||
localhost/rclone:latest serve webdav \
|
||||
--addr :8080 \
|
||||
--auth-proxy /usr/bin/ldap-auth-proxy
|
||||
ExecStop=/bin/podman stop --ignore --time 10 %p-%i
|
||||
ExecStopPost=/bin/podman rm --ignore --force %p-%i
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
Loading…
Reference in New Issue