📱 Erkannter Endgerättyp ⛱️ Tag und Nacht. Verbraucht keinen oder einen 🍪.
guest
Login 🧬 0 Ihre DNS in den Krei.se-DNS-Servern, führt zum Bio-Labor 🍪 0 Anzahl Ihrer gespeicherten Kekse, führt zur Keksdose

NFS Userproxy for Webapps

Feb 28 13:44:08 userproxy gssproxy[700]: [CID 16][2026/02/28 12:44:08]: gp_rpc_execute: executing 6 (GSSX_ACQUIRE_CRED) for service "nfs-client", euid: 0,socket: (null)
Feb 28 13:44:08 userproxy gssproxy[700]: [CID 16][2026/02/28 12:44:08]: gp_rpc_execute: executing 8 (GSSX_INIT_SEC_CONTEXT) for service "nfs-client", euid: 0,socket: (null)
Feb 28 13:44:08 userproxy gssproxy[700]: [CID 16][2026/02/28 12:44:08]: gp_rpc_execute: executing 6 (GSSX_ACQUIRE_CRED) for service "nfs-client", euid: 0,socket: (null)
Feb 28 13:44:08 userproxy gssproxy[700]: [CID 16][2026/02/28 12:44:08]: gp_rpc_execute: executing 8 (GSSX_INIT_SEC_CONTEXT) for service "nfs-client", euid: 0,socket: (null)
Feb 28 13:44:08 userproxy gssproxy[700]: [CID 15][2026/02/28 12:44:08]: gp_rpc_execute: executing 6 (GSSX_ACQUIRE_CRED) for service "nfs-client", euid: 1018,socket: (null)
Feb 28 13:44:08 userproxy gssproxy[700]: [CID 15][2026/02/28 12:44:08]: gp_rpc_execute: executing 8 (GSSX_INIT_SEC_CONTEXT) for service "nfs-client", euid: 1018,socket: (null)

current solution

cat webdav.conf 
<IfModule mod_ssl.c>
<VirtualHost *:443>
    ServerName userproxy.it-verband-chemnitz.de
    ServerAlias webdav.it-verband-chemnitz.de

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/userproxy.it-verband-chemnitz.de/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/userproxy.it-verband-chemnitz.de/privkey.pem

    # One attempt to make sure we never have the frontend touch the filesystem
    # this means Location / goes to this Directory, will issue a stat()
    DocumentRoot /var/www/empty

    #userproxy front, if you connect here 
    <Directory />
        # disallows .htaccess
        AllowOverride None
        # hmm well
        Options None
        # allow from all new syntax
        Require all granted
    </Directory>

    # this makes Apache not look into the Directory /homes which would issue a lookup of .htaccess and stat() with user www-data
    <Location /homes>
        AuthType Basic
        AuthName "WebDAV"
        AuthBasicProvider PAM
        AuthPAMService webdav
        Require valid-user

        # header passing authed user
        RequestHeader set X-Remote-User expr=%{REMOTE_USER}
        #SetHandler "proxy:http://127.0.0.1:8080/homes"
        Options -FollowSymLinks

    </Location>

    # PROXYPRESERVEHOST

    # without this the backend would not know who is connecting and just see 127.0.0.1:8080
    # with it the backend gets the request parameter:    Host: userproxy.it-verband-chemnitz.de.

    # Gemini explain: Why you need it: WebDAV responses (like PROPFIND) contain XML tags called <D:href>.
    ## These tags tell the client where the files are. If the Backend thinks its name is 127.0.0.1,
    ## it will send links back to the client like http://127.0.0.1:8080/homes/richard/file.txt.
    ## The client (Nautilus) will try to click that and fail because it can't reach 127.0.0.1.
    ## ProxyPreserveHost ensures the Backend knows its "Public Name."

    ProxyPreserveHost On

    #RewriteEngine On
    #RewriteCond %{REQUEST_URI} ^/home/[^/]+$
    #RewriteRule ^(.*)$ $1/ [R=301,L]

    # forwarder
    ## Apache's connection pooling.
    ## The Frontend ProxyPass keeps idle connections open to the Backend (127.0.0.1:8080) to save time. However, mpm_itk drops root privileges to richard on the first request. If the Frontend reuses that same connection milliseconds later for a background check without the auth header, or for another user, the backend child process is already richard. It cannot setuid() again because it is no longer root, causing the request to fail unexpectedly.

    ## Force the Frontend to close the connection after every single request so the Backend spawns a fresh mpm_itk worker from root every time.

    ProxyPass /homes http://127.0.0.1:8080/safe-homes nocanon disablereuse=On

    # this is NOT a backfeed, the data comes back via socket automagically. This only does:

    ## If the Backend sends a "Redirect" (like the trailing slash issue we had), it sends a header:
    ## Location: http://127.0.0.1:8080/homes/richard/

    ## If you didn't have ProxyPassReverse, the Frontend would send that exact string to Nautilus. Nautilus would try to connect to http://127.0.0.1:8080 and die.
    ## ProxyPassReverse sees that "Location" header, recognizes the 127.0.0.1:8080 part, and rewrites it to the Frontend's public URL:
    ## Location: https://userproxy.it-verband-chemnitz.de/homes/richard/

    ProxyPassReverse /homes http://127.0.0.1:8080/homes

    # im currently working on this RequestHeader edit

    # =========================================================
    # WEBDAV INTEROPERABILITY: THE DESTINATION HEADER
    # =========================================================

    # PROBLEM: WebDAV 'MOVE' and 'COPY' methods use a 'Destination' header 
    # to tell the server where to put the new file.
    # The client (Nautilus/WinExplorer) sends an ABSOLUTE URL:
    #   Destination: https://userproxy.it-verband-chemnitz.de/homes/richard/new.txt

    # CONFLICT: Our Backend (8080) is plain HTTP. When it sees an HTTPS 
    # destination, it thinks we are asking it to move a file to a 
    # DIFFERENT server (External Cross-Server Move), which it will deny (403/502).

    # SOLUTION: We must "downgrade" the header to HTTP so the Backend 
    # recognizes the destination as its own local filesystem.
    # The 'early' flag ensures this happens before the proxy logic executes.
    RequestHeader edit Destination ^https:// http:// early

    # NOTE: Since we are now using Namespace Matching (/homes -> /homes), 
    # we NO LONGER need to edit the path itself (e.g., /home -> /shadow_homes).
    # This reduces complexity and improves reliability with GVfs.

</VirtualHost>
</IfModule>

<VirtualHost 127.0.0.1:8080>

    DavLockDB /var/lib/apache2/webdav/DavLock

    LogLevel alert rewrite:trace6 mpm_itk:trace3

    ## UseCanonicalName On: Prevents Apache from generating self-referential 
    ## redirects using its internal IP (127.0.0.1). Forces it to use the ServerName.
    ServerName https://userproxy.it-verband-chemnitz.de
    UseCanonicalName On

    #DocumentRoot /var/www/empty

    Alias /safe-homes /var/www/empty

    # dont go insane this is the http request /homes to the actual on disk folder /homes lol
    # Alias /homes /homes

    ## IDENTITY BRIDGE: Capture the header from the Frontend.
    ## mpm_itk switches the process UID to Richard BEFORE the Directory walk.
    # was in location which is wrong, drop to user priv asap
    AssignUserIDExpr %{HTTP:X-Remote-User}

    <Directory />
        AllowOverride None
        Options -FollowSymLinks -Indexes
        Require all granted
    </Directory>

    # 3. The Pivot
    <Location /safe-homes>
        Require all granted
        RewriteEngine On

        # Because Alias ran first, the URI string here is "/var/www/empty/richard/"
        # We capture the "/richard/" part and bounce it to "/homes/richard/"
        RewriteRule ^/var/www/empty(.*)$ /homes$1 [L]
    </Location>

Alias /homes /homes

    #<LocationMatch "^/homes/([^/]+)">
    #<Location /homes>
    <Directory /homes>
        DAV On

        # DirectoryCheckHandler On: Critical for mpm_itk + Kerberos.
        # It tells Apache to proceed even if the parent process (www-data) 
        # can't fully validate the path components.
        # DirectoryCheckHandler On

        # Disable .htaccess searches to prevent "EUID 33" probes on NFS.
        AllowOverride None

        # Options -FollowSymLinks: Prevents lstat() calls by the parent 
        # process. Using +SymLinksIfOwnerMatch is the "safe" compromise.
        Options +Indexes +SymLinksIfOwnerMatch -MultiViews

        Require all granted
    </Directory>
    #</Location>
    #</LocationMatch>

    # better mpm itk info
    # LogLevel mpm_itk:info
    # LogLevel mpm_itk:trace2

    ErrorLog ${APACHE_LOG_DIR}/backend_error.log
    CustomLog ${APACHE_LOG_DIR}/backend_access.log combined
    # thanks gemini
    ## %P: Process ID
    ## %{tid}P: Thread ID (useful for event/worker MPMs)
    ## %u: Authenticated User (from header)

    LuaHookLog /etc/apache2/get_uid.lua log_uid

    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{X-Remote-User}i\" PID:%P OS_UID:%{system_uid}n" itk_debug
    CustomLog ${APACHE_LOG_DIR}/backend_access.log itk_debug

</VirtualHost>

current pam

root@userproxy:/etc/apache2/sites-enabled# cat /etc/pam.d/webdav 
#auth required pam_unix.so
#account required pam_unix.so
#session required pam_unix.so

# gets a user principal
# auth    required    pam_krb5.so minimum_uid=1000 
# ccache_dir=/var/lib/gssproxy/webclients ccname_template=FILE:%d/krb5cc_%U

# This hook knows the password and stores the ticket to ccache_dir=/var/lib/gssproxy/webclients
auth required pam_exec.so expose_authtok /usr/local/bin/webdav-kinit.sh

# 2. Always succeed the "account" check 
# This bypasses the need for the user to be in /etc/passwd
account required    pam_permit.so

current mount hook

root@userproxy:/etc/apache2/sites-enabled# cat /usr/local/bin/webdav-kinit.sh 
#!/bin/bash
# /usr/local/bin/webdav-kinit.sh

LOG_FILE="/var/log/webdav-kinit.log"

# 1. Capture the timestamp and metadata
# PAM_RHOST: The remote IP of the client (Nautilus/curl/etc)
# PAM_USER:  The user logging in
# PAM_SERVICE: Usually 'webdav' (from your config)
NOW=$(date "+%Y-%m-%d %H:%M:%S")
CLIENT_IP="${PAM_RHOST:-unknown_ip}"
SERVICE="${PAM_SERVICE:-unknown_service}"
PID=$$

MOUNTPOINT="/homes/$PAM_USER"

# plaintext password from pam_exec
read -r PASSWORD

# Log the initial connection attempt
echo "[$NOW] [PID: $PID] AUTH_START: User='$PAM_USER' Client='$CLIENT_IP' Service='$SERVICE'" >> "$LOG_FILE"

USER_UID=$(id -u "$PAM_USER")

#CCACHE="/var/lib/gssproxy/webclients/krb5cc_${USER_UID}"

# better syntax similar to krb5 lines in pam
CCACHE_DIR="/var/lib/gssproxy/webclients"
CCACHE="$CCACHE_DIR/krb5cc_${USER_UID}"

# Request the ticket and force it into the gssproxy path
echo "$PASSWORD" | kinit -c "FILE:$CCACHE" "$PAM_USER"
KINIT_RET=$?

# exit 0

# kinit failed (bad password), exit immediately
if [ $KINIT_RET -ne 0 ]; then

    echo "[$NOW] [PID: $PID] KINIT_FAIL: Code=$KINIT_RET Error='$KINIT_ERR'" >> "$LOG_FILE"
    exit $KINIT_RET
fi

# 2. Trigger the AutoFS mount
# The Apache frontend runs as www-data, so doing this might result in "Permission denied" 
# to read the folder contents. That does not matter! The simple act of calling stat() 
# forces the kernel to ask AutoFS to resolve the path, which triggers the mount.

echo "[$NOW] [PID: $PID] KINIT_SUCCESS: Ticket saved to $CCACHE" >> "$LOG_FILE"

MOUNT_CHECK=$(grep -qs "/homes/$PAM_USER " /proc/mounts && echo "MOUNTED" || echo "NOT_MOUNTED_YET")

echo "[$NOW] [PID: $PID] MOUNT_STATUS_BEFORE: $MOUNT_CHECK" >> "$LOG_FILE"
echo "------------------------------------------------------------------" >> "$LOG_FILE"

if ! grep -qs "$MOUNTPOINT " /proc/mounts; then
    # Not mounted? Poke it to wake up AutoFS.
    # We suppress errors because www-data might get "Permission Denied" 
    # even if the mount succeeds (which is fine).
    # stat "$MOUNTPOINT" >/dev/null 2>&1
    # stat "$MOUNTPOINT" >> "$LOG_FILE"
    mount $MOUNTPOINT

    # POKE_OUT=$(/usr/bin/timeout 3s /usr/bin/ls -ld "$MOUNTPOINT" 2>&1)
    # POKE_RET=$?

    # if [ $POKE_RET -eq 0 ]; then
    #     echo "[$NOW] [PID: $PID] POKE_SUCCESS: $POKE_OUT" >> "$LOG_FILE"
    # elif [ $POKE_RET -eq 124 ]; then
    #     echo "[$NOW] [PID: $PID] POKE_TIMEOUT: NFS Server did not respond in 3s!" >> "$LOG_FILE"
    # else
    #     echo "[$NOW] [PID: $PID] POKE_MSG: $POKE_OUT (Code: $POKE_RET)" >> "$LOG_FILE"
    # fi

fi

MOUNT_CHECK=$(grep -qs "/homes/$PAM_USER " /proc/mounts && echo "MOUNTED" || echo "NOT_MOUNTED_YET")

echo "[$NOW] [PID: $PID] MOUNT_STATUS_AFTER: $MOUNT_CHECK" >> "$LOG_FILE"
echo "------------------------------------------------------------------" >> "$LOG_FILE"

# timeout 0.5s stat "$MOUNTPOINT" >/dev/null 2>&1 || true

#if ! mountpoint -q "$MOUNTPOINT"; then
    # Instead of stat, just try to 'ls' the PARENT directory 
    # to wake up AutoFS without touching the restricted files.
    # ls /homes >/dev/null 2>&1
#fi

exit 0

Timeouts NFS Mounts

1. If using Systemd Automounts

If your mounts are managed via systemd (which the logs suggest), you can find the global timeout in /etc/systemd/system.conf or by looking at the specific automount unit.

However, since these are likely dynamic, you want to set the Global AutoFS timeout which systemd usually honors.

  1. The AutoFS/SSSD Timeout

Check your /etc/autofs.conf (or /etc/default/autofs on older Debian). Look for: code Ini

timeout = 300

Change it to: code Ini

timeout = 3600 # 1 hour

Then restart: systemctl restart autofs

  1. SSSD Side

Since sssd_autofs is starting and stopping, ensure the responder stays alive a bit longer to avoid the "startup delay" when a user clicks. In /etc/sssd/sssd.conf: code Ini

[autofs]

Keep the responder alive for 1 hour after the last request

idle_timeout = 3600

TBI

close 777 on webclients gssproxy read by www-data is enough, better switch to root alltogether

Current PTF

Hintergrund ändern. Verbraucht keinen oder einen 🍪.

Verknüpften Viewport öffnen

🎮 Steuerung
Dokumentation 🕹️
Sie sind leider kein Entwickler :(

Content Nodes Amount

Diligence / PTF Amount

FPS

Vertex-Count