Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
W
webdav-auth
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container registry
Model registry
Operate
Environments
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
ai3
tools
webdav-auth
Compare revisions
lintian-fixes to master
Compare revisions
Changes are shown as if the
source
revision was being merged into the
target
revision.
Learn more about comparing revisions.
Source
ai3/tools/webdav-auth
Select target project
No results found
master
Select Git revision
Swap
Target
svp-bot/webdav-auth
Select target project
ai3/tools/webdav-auth
svp-bot/webdav-auth
2 results
lintian-fixes
Select Git revision
Show changes
Only incoming changes from source
Include changes to target since source was created
Compare
Commits on Source (4)
Improve request logging
· 4c5be8db
ale
authored
2 years ago
4c5be8db
Increase file limit on systemd unit
· 2155516e
ale
authored
2 years ago
2155516e
Improve README
· 8ab4e056
ale
authored
2 years ago
8ab4e056
Update .gitlab-ci.yml
· e2a24529
godog
authored
1 year ago
e2a24529
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
.gitlab-ci.yml
+5
-15
5 additions, 15 deletions
.gitlab-ci.yml
README.md
+43
-3
43 additions, 3 deletions
README.md
ai_webdav_auth/auth_server.py
+7
-4
7 additions, 4 deletions
ai_webdav_auth/auth_server.py
debian/ai-webdav-auth-server.service
+1
-0
1 addition, 0 deletions
debian/ai-webdav-auth-server.service
with
56 additions
and
22 deletions
.gitlab-ci.yml
View file @
e2a24529
include
:
"
https://git.autistici.org/ai3/build-deb/raw/master/ci-common.yml"
include
:
-
"
https://git.autistici.org/pipelines/debian/raw/master/common.yml"
-
"
https://git.autistici.org/pipelines/images/test/python/raw/master/ci.yml"
run_tests
:
stage
:
test
image
:
"
registry.git.autistici.org/ai3/docker/test/python:master"
script
:
-
apt-get update
-
env DEBIAN_FRONTEND=noninteractive apt-get install -qy --no-install-recommends python3-six libldap2-dev libsasl2-dev default-jre-headless
-
tox
artifacts
:
when
:
always
reports
:
coverage_report
:
coverage_format
:
cobertura
path
:
cover.xml
junit
:
junit.xml
variables
:
PY_TEST_PACKAGES
:
"
python3-six
libldap2-dev
libsasl2-dev
default-jre-headless"
This diff is collapsed.
Click to expand it.
README.md
View file @
e2a24529
webdav-auth
===
The authentication server bridges the
[
users-dav
](
https://git.autistici.org/ai3/docker/apache2-users-dav
)
container (which runs the
[
webdav-server
](
https://git.autistici.org/ai3/tools/webdav-server
)
component)
and the local
[
auth-server
](
https://git.autistici.org/id/auth
)
.
The authentication server bridges the
[
users-dav
](
https://git.autistici.org/ai3/docker/apache2-users-dav
)
container (which runs the
[
webdav-server
](
https://git.autistici.org/ai3/tools/webdav-server
)
component) and the local
[
auth-server
](
https://git.autistici.org/id/auth
)
.
It runs as a system-level daemon.
There are two issues with the way DAV authentication works in A/I:
*
*authentication*
: are username and password correct?
*
*authorization*
: can the user access this particular DAV path?
This is quite complicated by the fact that the DAV server process runs
in an isolated and untrusted container[^1], and we did not want to
just grant arbitrary LDAP access to anything in there. So we split the
authentication off to a process that runs
*outside*
the container, and
devised a simple restricted API just for the DAV server to talk to it
(safely, over a UNIX socket).
Here's how the process works in detail:
*
when a DAV request comes in, Apache spawns a DAV server running with
the appropriate user ID;
*
when the DAV server starts, it fetches the list of DAV accounts
associated with the user ID it is running as, and configures HTTP
handlers for those paths only;
*
on every DAV request, an authentication check is performed on the
provided username/password, verifying also that the username appears
in the list of DAV accounts associated with the current user ID.
Authorization is thusly provided by binding user IDs to specific DAV
accounts. The overall process is a bit convoluted, but it's convenient
to rely on Apache (specifically fcgid + suexec) to manage the setuid
processes.
The authentication server does not trust the DAV server, which is why
the user ID is not a request parameter but it is instead obtained by
looking at the
*peer*
of the UNIX socket connection.
[
^1
]:
This
used to be the case when we ran DAV and the users' PHP
processes in the same environment, now that they are completely
separate containers we could revisit this design decision.
This diff is collapsed.
Click to expand it.
ai_webdav_auth/auth_server.py
View file @
e2a24529
...
...
@@ -70,7 +70,6 @@ class DavAuthServer(socketserver.ThreadingUnixStreamServer):
@contextlib.contextmanager
def
ldapconn
(
self
,
bind_dn
=
None
,
bind_pw
=
None
):
logging
.
info
(
'
LDAP connection to %s
'
,
self
.
ldap_params
[
'
uri
'
])
c
=
ldap
.
initialize
(
self
.
ldap_params
[
'
uri
'
])
try
:
if
bind_dn
:
...
...
@@ -87,19 +86,24 @@ class DavAuthHandler(socketserver.StreamRequestHandler):
def
handle
(
self
):
pid
,
uid
,
gid
=
getpeercred
(
self
.
request
)
logging
.
info
(
'
request from uid=%d gid=%d pid=%d
'
,
uid
,
gid
,
pid
)
request_debug_str
=
''
try
:
request
=
json
.
loads
(
TextIOWrapper
(
self
.
rfile
,
'
utf-8
'
).
readline
())
if
request
[
'
type
'
]
==
'
auth
'
:
request_debug_str
=
'
auth(dn=%s)
'
%
request
[
'
dn
'
]
response
=
self
.
authenticate
(
uid
,
request
[
'
dn
'
],
request
[
'
password
'
])
elif
request
[
'
type
'
]
==
'
get_accounts
'
:
request_debug_str
=
'
get_accounts()
'
response
=
self
.
get_accounts
(
uid
)
else
:
raise
ValueError
(
'
Unknown request type
'
)
response_data
=
json
.
dumps
(
response
)
self
.
wfile
.
write
(
response_data
.
encode
(
'
utf-8
'
))
except
Exception
as
e
:
logging
.
exception
(
'
request error from uid %d: %s
'
,
uid
,
e
)
response_data
=
f
'
unhandled exception:
{
e
}
'
logging
.
info
(
'
request from uid=%d gid=%d pid=%d: %s -> %s
'
,
uid
,
gid
,
pid
,
request_debug_str
,
response_data
)
def
authenticate
(
self
,
uid
,
dn
,
password
):
# An empty password causes a "server is unwilling to perform"
...
...
@@ -146,7 +150,6 @@ class DavAuthHandler(socketserver.StreamRequestHandler):
accounts
[
realm
]
=
{
'
dn
'
:
r
[
0
],
'
ftpname
'
:
name
,
'
home
'
:
r
[
1
][
'
homeDirectory
'
][
0
].
decode
(
'
utf-8
'
)}
logging
.
info
(
'
account list for uid=%d: %s
'
,
uid
,
accounts
)
return
accounts
...
...
This diff is collapsed.
Click to expand it.
debian/ai-webdav-auth-server.service
View file @
e2a24529
...
...
@@ -9,6 +9,7 @@ EnvironmentFile=-/etc/default/ai-webdav-auth-server
ExecStart
=
/usr/bin/ai-webdav-auth-server $ARGS
Restart
=
always
RuntimeDirectory
=
authdav
LimitNOFILE
=
65535
# Hardening
NoNewPrivileges
=
yes
...
...
This diff is collapsed.
Click to expand it.