0

I'm trying to run the Piwigo gallery/digital asset manager on a Raspberry Pi using Apache 2.4 on Arch Linux ARM. Piwigo's running happily. For Windows file naming and CIFS reasons, I need to make thousands of symlinks to an outside directory into Piwigo's FTP upload directory. My script to do this works perfectly.

This is not another person trying to symlink their home directory without understanding permissions.

In troubleshooting down, I've found that symlinks work fine in DocumentRoot but fail with 403 in all subdirectories of DocumentRoot that I've tested. Nothing outside DocumentRoot is involved. Symlinks in new test directories which cannot possibly contain .ht* files also fail.

I've read a hundred or so of the most relevant-seeming of the 465 results on Server Fault for "apache symlink", as well as various other posts around the Web. Those answers as well as Apache documentation all say that directives should "apply only to the named file-system directory, sub-directories, and their contents." - the Option directive should propagate down unless overridden, and my config should prevent overriding in any case. If I can figure out why symlinks are failing inside DocumentRoot, I'm confident they'll link out.


Apache is freshly restarted.

[alarm@alarmpi ~]$ sudo systemctl restart httpd
[alarm@alarmpi ~]$ sudo systemctl status httpd
* httpd.service - Apache Web Server
   Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled)
   Active: active (running) since Sat 2017-12-09 14:18:43 EST; 1min 2s ago
  Process: 8578 ExecStop=/usr/bin/httpd -k graceful-stop (code=exited, status=0/SUCCESS)
  Process: 8099 ExecReload=/usr/bin/httpd -k graceful (code=exited, status=0/SUCCESS)
 Main PID: 8583 (httpd)
    Tasks: 6 (limit: 4915)
   CGroup: /system.slice/httpd.service
           |-8583 /usr/bin/httpd -k start -DFOREGROUND
           |-8584 /usr/bin/httpd -k start -DFOREGROUND
           |-8585 /usr/bin/httpd -k start -DFOREGROUND
           |-8586 /usr/bin/httpd -k start -DFOREGROUND
           |-8587 /usr/bin/httpd -k start -DFOREGROUND
           `-8588 /usr/bin/httpd -k start -DFOREGROUND

Dec 09 14:18:43 alarmpi systemd[1]: Started Apache Web Server.
Dec 09 14:18:44 alarmpi httpd[8583]: AH00558: httpd: Could not reliably determine the server's fully qualified domain

And here's the httpd.conf, fed through awk to strip the comments and blank lines. Obviously, whatever's wrong is probably in here. If memory serves, the two additions to default are loading PHP7 and making .PHPs executable.

[alarm@alarmpi ~]$ sudo awk '!/^ *#/ && NF' /etc/httpd/conf/httpd.conf
ServerRoot "/etc/httpd"
Listen 80
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule reqtimeout_module modules/mod_reqtimeout.so
LoadModule include_module modules/mod_include.so
LoadModule filter_module modules/mod_filter.so
LoadModule mime_module modules/mod_mime.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule env_module modules/mod_env.so
LoadModule headers_module modules/mod_headers.so
LoadModule unique_id_module modules/mod_unique_id.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule version_module modules/mod_version.so
LoadModule slotmem_shm_module modules/mod_slotmem_shm.so
LoadModule mpm_prefork_module modules/mod_mpm_prefork.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule status_module modules/mod_status.so
LoadModule autoindex_module modules/mod_autoindex.so
<IfModule !mpm_prefork_module>
        #LoadModule cgid_module modules/mod_cgid.so
</IfModule>
<IfModule mpm_prefork_module>
        #LoadModule cgi_module modules/mod_cgi.so
</IfModule>
LoadModule negotiation_module modules/mod_negotiation.so
LoadModule dir_module modules/mod_dir.so
LoadModule userdir_module modules/mod_userdir.so
LoadModule alias_module modules/mod_alias.so
LoadModule php7_module modules/libphp7.so
<FilesMatch \.php$>
        SetHandler application/x-httpd-php
</FilesMatch>
<IfModule unixd_module>
User http
Group http
</IfModule>
ServerAdmin [email protected]
<Directory />
    AllowOverride none
    Require all denied
</Directory>
DocumentRoot "/srv/http"
<Directory "/srv/http">
    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>
<IfModule dir_module>
    DirectoryIndex index.html
</IfModule>
<Files ".ht*">
    Require all denied
</Files>
ErrorLog "/var/log/httpd/error_log"
LogLevel warn
<IfModule log_config_module>
    LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
    LogFormat "%h %l %u %t \"%r\" %>s %b" common
    <IfModule logio_module>
      LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
    </IfModule>
    CustomLog "/var/log/httpd/access_log" common
</IfModule>
<IfModule alias_module>
    ScriptAlias /cgi-bin/ "/srv/http/cgi-bin/"
</IfModule>
<IfModule cgid_module>
</IfModule>
<Directory "/srv/http/cgi-bin">
    AllowOverride None
    Options None
    Require all granted
</Directory>
<IfModule headers_module>
    RequestHeader unset Proxy early
</IfModule>
<IfModule mime_module>
    TypesConfig conf/mime.types
    AddType application/x-compress .Z
    AddType application/x-gzip .gz .tgz
</IfModule>
Include conf/extra/httpd-mpm.conf
Include conf/extra/httpd-multilang-errordoc.conf
Include conf/extra/httpd-autoindex.conf
Include conf/extra/httpd-languages.conf
Include conf/extra/httpd-userdir.conf
Include conf/extra/httpd-default.conf
Include conf/extra/phpmyadmin.conf
<IfModule proxy_html_module>
Include conf/extra/proxy-html.conf
</IfModule>
Include conf/extra/php7_module.conf
<IfModule ssl_module>
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
</IfModule>

EDIT: Arch just puts one httpd.conf file in /etc/httpd/conf/ , then includes stuff from /etc/httpd/conf/extra . Here are the contents of all Included .conf files, in order of inclusion:

[alarm@alarmpi conf]$ sudo awk '!/^ *#/ && NF' /etc/httpd/conf/extra/httpd-mpm.conf
<IfModule !mpm_netware_module>
    PidFile "/run/httpd/httpd.pid"
</IfModule>
<IfModule mpm_prefork_module>
    StartServers             5
    MinSpareServers          5
    MaxSpareServers         10
    MaxRequestWorkers      250
    MaxConnectionsPerChild   0
</IfModule>
<IfModule mpm_worker_module>
    StartServers             3
    MinSpareThreads         75
    MaxSpareThreads        250
    ThreadsPerChild         25
    MaxRequestWorkers      400
    MaxConnectionsPerChild   0
</IfModule>
<IfModule mpm_event_module>
    StartServers             3
    MinSpareThreads         75
    MaxSpareThreads        250
    ThreadsPerChild         25
    MaxRequestWorkers      400
    MaxConnectionsPerChild   0
</IfModule>
<IfModule mpm_netware_module>
    ThreadStackSize      65536
    StartThreads           250
    MinSpareThreads         25
    MaxSpareThreads        250
    MaxThreads            1000
    MaxConnectionsPerChild   0
</IfModule>
<IfModule mpm_mpmt_os2_module>
    StartServers             2
    MinSpareThreads          5
    MaxSpareThreads         10
    MaxConnectionsPerChild   0
</IfModule>
<IfModule mpm_winnt_module>
    ThreadsPerChild        150
    MaxConnectionsPerChild   0
</IfModule>
<IfModule !mpm_netware_module>
    MaxMemFree            2048
</IfModule>
<IfModule mpm_netware_module>
    MaxMemFree             100
</IfModule>

==================

[alarm@alarmpi conf]$ sudo awk '!/^ *#/ && NF' /etc/httpd/conf/extra/httpd-multilang-errordoc.conf
Alias /error/ "/usr/share/httpd/error/"
<Directory "/usr/share/httpd/error">
    AllowOverride None
    Options IncludesNoExec
    AddOutputFilter Includes html
    AddHandler type-map var
    Require all granted
    LanguagePriority en cs de es fr it ja ko nl pl pt-br ro sv tr
    ForceLanguagePriority Prefer Fallback
</Directory>
ErrorDocument 400 /error/HTTP_BAD_REQUEST.html.var
ErrorDocument 401 /error/HTTP_UNAUTHORIZED.html.var
ErrorDocument 403 /error/HTTP_FORBIDDEN.html.var
ErrorDocument 404 /error/HTTP_NOT_FOUND.html.var
ErrorDocument 405 /error/HTTP_METHOD_NOT_ALLOWED.html.var
ErrorDocument 408 /error/HTTP_REQUEST_TIME_OUT.html.var
ErrorDocument 410 /error/HTTP_GONE.html.var
ErrorDocument 411 /error/HTTP_LENGTH_REQUIRED.html.var
ErrorDocument 412 /error/HTTP_PRECONDITION_FAILED.html.var
ErrorDocument 413 /error/HTTP_REQUEST_ENTITY_TOO_LARGE.html.var
ErrorDocument 414 /error/HTTP_REQUEST_URI_TOO_LARGE.html.var
ErrorDocument 415 /error/HTTP_UNSUPPORTED_MEDIA_TYPE.html.var
ErrorDocument 500 /error/HTTP_INTERNAL_SERVER_ERROR.html.var
ErrorDocument 501 /error/HTTP_NOT_IMPLEMENTED.html.var
ErrorDocument 502 /error/HTTP_BAD_GATEWAY.html.var
ErrorDocument 503 /error/HTTP_SERVICE_UNAVAILABLE.html.var
ErrorDocument 506 /error/HTTP_VARIANT_ALSO_VARIES.html.var

==================

[alarm@alarmpi conf]$ sudo awk '!/^ *#/ && NF' /etc/httpd/conf/extra/httpd-autoindex.conf
IndexOptions FancyIndexing HTMLTable VersionSort
Alias /icons/ "/usr/share/httpd/icons/"
<Directory "/usr/share/httpd/icons">
    Options Indexes MultiViews
    AllowOverride None
    Require all granted
</Directory>
AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip
AddIconByType (TXT,/icons/text.gif) text/*
AddIconByType (IMG,/icons/image2.gif) image/*
AddIconByType (SND,/icons/sound2.gif) audio/*
AddIconByType (VID,/icons/movie.gif) video/*
AddIcon /icons/binary.gif .bin .exe
AddIcon /icons/binhex.gif .hqx
AddIcon /icons/tar.gif .tar
AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv
AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip
AddIcon /icons/a.gif .ps .ai .eps
AddIcon /icons/layout.gif .html .shtml .htm .pdf
AddIcon /icons/text.gif .txt
AddIcon /icons/c.gif .c
AddIcon /icons/p.gif .pl .py
AddIcon /icons/f.gif .for
AddIcon /icons/dvi.gif .dvi
AddIcon /icons/uuencoded.gif .uu
AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl
AddIcon /icons/tex.gif .tex
AddIcon /icons/bomb.gif core
AddIcon /icons/back.gif ..
AddIcon /icons/hand.right.gif README
AddIcon /icons/folder.gif ^^DIRECTORY^^
AddIcon /icons/blank.gif ^^BLANKICON^^
DefaultIcon /icons/unknown.gif
ReadmeName README.html
HeaderName HEADER.html
IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t

==================

[alarm@alarmpi conf]$ sudo awk '!/^ *#/ && NF' /etc/httpd/conf/extra/httpd-languages.conf
AddLanguage ca .ca
AddLanguage cs .cz .cs
AddLanguage da .dk
AddLanguage de .de
AddLanguage el .el
AddLanguage en .en
AddLanguage eo .eo
AddLanguage es .es
AddLanguage et .et
AddLanguage fr .fr
AddLanguage he .he
AddLanguage hr .hr
AddLanguage it .it
AddLanguage ja .ja
AddLanguage ko .ko
AddLanguage ltz .ltz
AddLanguage nl .nl
AddLanguage nn .nn
AddLanguage no .no
AddLanguage pl .po
AddLanguage pt .pt
AddLanguage pt-BR .pt-br
AddLanguage ru .ru
AddLanguage sv .sv
AddLanguage tr .tr
AddLanguage zh-CN .zh-cn
AddLanguage zh-TW .zh-tw
LanguagePriority en ca cs da de el eo es et fr he hr it ja ko ltz nl nn no pl pt pt-BR ru sv tr zh-CN zh-TW
ForceLanguagePriority Prefer Fallback
AddCharset us-ascii.ascii .us-ascii
AddCharset ISO-8859-1  .iso8859-1  .latin1
AddCharset ISO-8859-2  .iso8859-2  .latin2 .cen
AddCharset ISO-8859-3  .iso8859-3  .latin3
AddCharset ISO-8859-4  .iso8859-4  .latin4
AddCharset ISO-8859-5  .iso8859-5  .cyr .iso-ru
AddCharset ISO-8859-6  .iso8859-6  .arb .arabic
AddCharset ISO-8859-7  .iso8859-7  .grk .greek
AddCharset ISO-8859-8  .iso8859-8  .heb .hebrew
AddCharset ISO-8859-9  .iso8859-9  .latin5 .trk
AddCharset ISO-8859-10  .iso8859-10  .latin6
AddCharset ISO-8859-13  .iso8859-13
AddCharset ISO-8859-14  .iso8859-14  .latin8
AddCharset ISO-8859-15  .iso8859-15  .latin9
AddCharset ISO-8859-16  .iso8859-16  .latin10
AddCharset ISO-2022-JP .iso2022-jp .jis
AddCharset ISO-2022-KR .iso2022-kr .kis
AddCharset ISO-2022-CN .iso2022-cn .cis
AddCharset Big5.Big5   .big5 .b5
AddCharset cn-Big5 .cn-big5
AddCharset WINDOWS-1251 .cp-1251   .win-1251
AddCharset CP866   .cp866
AddCharset KOI8  .koi8
AddCharset KOI8-E  .koi8-e
AddCharset KOI8-r  .koi8-r .koi8-ru
AddCharset KOI8-U  .koi8-u
AddCharset KOI8-ru .koi8-uk .ua
AddCharset ISO-10646-UCS-2 .ucs2
AddCharset ISO-10646-UCS-4 .ucs4
AddCharset UTF-7   .utf7
AddCharset UTF-8   .utf8
AddCharset UTF-16  .utf16
AddCharset UTF-16BE .utf16be
AddCharset UTF-16LE .utf16le
AddCharset UTF-32  .utf32
AddCharset UTF-32BE .utf32be
AddCharset UTF-32LE .utf32le
AddCharset euc-cn  .euc-cn
AddCharset euc-gb  .euc-gb
AddCharset euc-jp  .euc-jp
AddCharset euc-kr  .euc-kr
AddCharset EUC-TW  .euc-tw
AddCharset gb2312  .gb2312 .gb
AddCharset iso-10646-ucs-2 .ucs-2 .iso-10646-ucs-2
AddCharset iso-10646-ucs-4 .ucs-4 .iso-10646-ucs-4
AddCharset shift_jis   .shift_jis .sjis

==================

[alarm@alarmpi conf]$ sudo awk '!/^ *#/ && NF' /etc/httpd/conf/extra/httpd-userdir.conf
UserDir public_html
<Directory "/home/*/public_html">
    AllowOverride FileInfo AuthConfig Limit Indexes
    Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
    Require method GET POST OPTIONS
</Directory>

==================

[alarm@alarmpi conf]$ sudo awk '!/^ *#/ && NF' /etc/httpd/conf/extra/httpd-default.conf
Timeout 60
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5
UseCanonicalName Off
AccessFileName .htaccess
ServerTokens Full
ServerSignature Off
HostnameLookups Off
<IfModule reqtimeout_module>
  RequestReadTimeout header=20-40,MinRate=500 body=20,MinRate=500
</IfModule>

==================

[alarm@alarmpi conf]$ sudo awk '!/^ *#/ && NF' /etc/httpd/conf/extra/phpmyadmin.conf
Alias /phpmyadmin "/usr/share/webapps/phpMyAdmin"
<Directory "/usr/share/webapps/phpMyAdmin">
    DirectoryIndex index.php
    AllowOverride All
    Options FollowSymlinks
    Require all granted
</Directory>

==================

[alarm@alarmpi conf]$ sudo awk '!/^ *#/ && NF' /etc/httpd/conf/extra/proxy-html.conf
ProxyHTMLLinks  a               href
ProxyHTMLLinks  area            href
ProxyHTMLLinks  link            href
ProxyHTMLLinks  img             src longdesc usemap
ProxyHTMLLinks  object          classid codebase data usemap
ProxyHTMLLinks  q               cite
ProxyHTMLLinks  blockquote      cite
ProxyHTMLLinks  ins             cite
ProxyHTMLLinks  del             cite
ProxyHTMLLinks  form            action
ProxyHTMLLinks  input           src usemap
ProxyHTMLLinks  head            profile
ProxyHTMLLinks  base            href
ProxyHTMLLinks  script          src for
ProxyHTMLEvents onclick ondblclick onmousedown onmouseup \
                onmouseover onmousemove onmouseout onkeypress \
                onkeydown onkeyup onfocus onblur onload \
                onunload onsubmit onreset onselect onchange

==================

[alarm@alarmpi conf]$ sudo awk '!/^ *#/ && NF' /etc/httpd/conf/extra/php7_module.conf
<IfModule dir_module>
        <IfModule php7_module>
                DirectoryIndex index.php index.html
                <FilesMatch "\.php$">
                        SetHandler application/x-httpd-php
                </FilesMatch>
                <FilesMatch "\.phps$">
                        SetHandler application/x-httpd-php-source
                </FilesMatch>
        </IfModule>
</IfModule>

And the remainder of .conf files in /etc/httpd/conf/extra/ , in case one of those included files in turn includes them, in alphabetic order.

[alarm@alarmpi extra]$ sudo awk '!/^ *#/ && NF' /etc/httpd/conf/extra/httpd-dav.conf
DavLockDB "/etc/httpd/var/DavLock"
Alias /uploads "/etc/httpd/uploads"
<Directory "/etc/httpd/uploads">
    Dav On
    AuthType Digest
    AuthName DAV-upload
    AuthUserFile "/etc/httpd/user.passwd"
    AuthDigestProvider file
    <RequireAny>
        Require method GET POST OPTIONS
        Require user admin
    </RequireAny>
</Directory>
BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully
BrowserMatch "MS FrontPage" redirect-carefully
BrowserMatch "^WebDrive" redirect-carefully
BrowserMatch "^WebDAVFS/1.[01234]" redirect-carefully
BrowserMatch "^gnome-vfs/1.0" redirect-carefully
BrowserMatch "^XML Spy" redirect-carefully
BrowserMatch "^Dreamweaver-WebDAV-SCM1" redirect-carefully
BrowserMatch " Konqueror/4" redirect-carefully

==================

[alarm@alarmpi extra]$ sudo awk '!/^ *#/ && NF' /etc/httpd/conf/extra/httpd-info.conf
<Location /server-status>
    SetHandler server-status
    Require host .example.com
    Require ip 127
</Location>
<Location /server-info>
    SetHandler server-info
    Require host .example.com
    Require ip 127
</Location>

==================

[alarm@alarmpi extra]$ sudo awk '!/^ *#/ && NF' /etc/httpd/conf/extra/httpd-manual.conf
AliasMatch ^/manual(?:/(?:da|de|en|es|fr|ja|ko|pt-br|ru|tr|zh-cn))?(/.*)?$ "/usr/share/httpd/manual$1"
<Directory "/usr/share/httpd/manual">
    Options Indexes
    AllowOverride None
    Require all granted
    <Files *.html>
        SetHandler type-map
    </Files>
    <Files *.html.tr.utf8>
        ForceType "text/html; charset=utf-8"
    </Files>
    AddLanguage da .da
    SetEnvIf Request_URI ^/manual/(da|de|en|es|fr|ja|ko|pt-br|ru|tr|zh-cn)/ prefer-language=$1
    RedirectMatch 301 ^/manual(?:/(da|de|en|es|fr|ja|ko|pt-br|ru|tr|zh-cn)){2,}(/.*)?$ /manual/$1$2
    LanguagePriority en da de es fr ja ko pt-br ru tr
    ForceLanguagePriority Prefer Fallback
</Directory>

==================

[alarm@alarmpi extra]$ sudo awk '!/^ *#/ && NF' /etc/httpd/conf/extra/httpd-ssl.conf
Listen 443
SSLCipherSuite HIGH:MEDIUM:!MD5:!RC4
SSLProxyCipherSuite HIGH:MEDIUM:!MD5:!RC4
SSLHonorCipherOrder on
SSLProtocol all -SSLv3
SSLProxyProtocol all -SSLv3
SSLPassPhraseDialog  builtin
SSLSessionCache        "shmcb:/run/httpd/ssl_scache(512000)"
SSLSessionCacheTimeout  300
<VirtualHost _default_:443>
DocumentRoot "/srv/http"
ServerName www.example.com:443
ServerAdmin [email protected]
ErrorLog "/var/log/httpd/error_log"
TransferLog "/var/log/httpd/access_log"
SSLEngine on
SSLCertificateFile "/etc/httpd/conf/server.crt"
SSLCertificateKeyFile "/etc/httpd/conf/server.key"
<FilesMatch "\.(cgi|shtml|phtml|php)$">
    SSLOptions +StdEnvVars
</FilesMatch>
<Directory "/srv/http/cgi-bin">
    SSLOptions +StdEnvVars
</Directory>
BrowserMatch "MSIE [2-5]" \
         nokeepalive ssl-unclean-shutdown \
         downgrade-1.0 force-response-1.0
CustomLog "/var/log/httpd/ssl_request_log" \
          "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
</VirtualHost>

==================

[alarm@alarmpi extra]$ sudo awk '!/^ *#/ && NF' /etc/httpd/conf/extra/httpd-vhosts.conf
<VirtualHost *:80>
    ServerAdmin [email protected]
    DocumentRoot "/etc/httpd/docs/dummy-host.example.com"
    ServerName dummy-host.example.com
    ServerAlias www.dummy-host.example.com
    ErrorLog "/var/log/httpd/dummy-host.example.com-error_log"
    CustomLog "/var/log/httpd/dummy-host.example.com-access_log" common
</VirtualHost>
<VirtualHost *:80>
    ServerAdmin [email protected]
    DocumentRoot "/etc/httpd/docs/dummy-host2.example.com"
    ServerName dummy-host2.example.com
    ErrorLog "/var/log/httpd/dummy-host2.example.com-error_log"
    CustomLog "/var/log/httpd/dummy-host2.example.com-access_log" common
</VirtualHost>

Just to express the problem more clearly than English grammar allows:

From now on, we're Apache's user. We own pretty much everything. No fancy ACL tools are installed. No plus signs in the permissions.

[alarm@alarmpi ~]$ sudo -u http -s

[http@alarmpi alarm]$ cd /srv/http/

[http@alarmpi ~]$ ls -al
total 348
drwxr-xr-x  7 http http   4096 Dec  9 19:40 .
drwxr-xr-x  4 root root   4096 Dec 10  2016 ..
-rw-------  1 http http    551 Dec  9 19:09 .bash_history
drwx------  3 http http   4096 Nov 27 23:51 .config
drwx------  3 http http   4096 Nov 26 19:39 .cache
drwx------  3 http http   4096 Nov 26 19:39 .local
-rwxr-xr-x  1 http http 234673 Dec  9 14:39 PicSub.jpg
-rwxr-xr-x  1 http http 318682 Feb 22  2017 PicTop.jpg
-rw-r--r--  1 http root     20 May 29  2017 info.php
drwxr-xr-x 15 http http   4096 Nov 26 18:23 piwigo

Let's make a directory. I've never made one named demodir before recording this example.

[http@alarmpi ~]$ mkdir -v demodir
mkdir: created directory 'demodir'

Move one of the pictures into the subdirectory, then create four symlinks - one within each directory, and one each going up and down directory levels.

[http@alarmpi ~]$ mv PicSub.jpg demodir/
[http@alarmpi ~]$ ln -s demodir/PicSub.jpg linkintoptosub.jpg
[http@alarmpi ~]$ ln -s PicTop.jpg linkintoptotop.jpg
[http@alarmpi ~]$ ln -s PicTop.jpg demodir/linkinsubtotop.jpg
[http@alarmpi ~]$ ln -s demodir/PicSub.jpg demodir/linkinsubtosub.jpg

EDIT: User http can read and execute demodir.

[alarm@alarmpi conf]$ sudo -u http -s
[http@alarmpi conf]$ cd /srv/http/
[http@alarmpi ~]$ ls -al
total 348
drwxr-xr-x  7 http http   4096 Dec  9 19:46 .
drwxr-xr-x  4 root root   4096 Dec 10  2016 ..
-rw-------  1 http http   1346 Dec 10 01:32 .bash_history
drwx------  3 http http   4096 Nov 26 19:39 .cache
drwx------  3 http http   4096 Nov 27 23:51 .config
drwx------  3 http http   4096 Nov 26 19:39 .local
-rwxr-xr-x  1 http http 318682 Feb 22  2017 PicTop.jpg
drwxr-xr-x  2 http http   4096 Dec  9 19:47 demodir
-rw-r--r--  1 http root     20 May 29  2017 info.php
lrwxrwxrwx  1 http http     18 Dec  9 19:45 linkintoptosub.jpg -> demodir/PicSub.jpg
lrwxrwxrwx  1 http http     10 Dec  9 19:46 linkintoptotop.jpg -> PicTop.jpg
drwxr-xr-x 15 http http   4096 Nov 26 18:23 piwigo

Files work no matter where they are. Symlinks work in DocumentRoot, no matter where they link to. Symlinks 403 in the subdirectory, no matter where they link to.

[http@alarmpi ~]$ curl -I 192.168.0.100/PicTop.jpg
HTTP/1.1 200 OK
Date: Sun, 10 Dec 2017 01:00:43 GMT
Server: Apache/2.4.25 (Unix) PHP/7.1.5
Last-Modified: Wed, 22 Feb 2017 19:23:20 GMT
ETag: "4dcda-549236de59a00"
Accept-Ranges: bytes
Content-Length: 318682
Content-Type: image/jpeg

[http@alarmpi ~]$ curl -I 192.168.0.100/demodir/PicSub.jpg
HTTP/1.1 200 OK
Date: Sun, 10 Dec 2017 01:00:48 GMT
Server: Apache/2.4.25 (Unix) PHP/7.1.5
Last-Modified: Sat, 09 Dec 2017 19:39:25 GMT
ETag: "394b1-55fed756f159d"
Accept-Ranges: bytes
Content-Length: 234673
Content-Type: image/jpeg

[http@alarmpi ~]$ curl -I 192.168.0.100/linkintoptotop.jpg
HTTP/1.1 200 OK
Date: Sun, 10 Dec 2017 01:01:52 GMT
Server: Apache/2.4.25 (Unix) PHP/7.1.5
Last-Modified: Wed, 22 Feb 2017 19:23:20 GMT
ETag: "4dcda-549236de59a00"
Accept-Ranges: bytes
Content-Length: 318682
Content-Type: image/jpeg

[http@alarmpi ~]$ curl -I 192.168.0.100/linkintoptosub.jpg
HTTP/1.1 200 OK
Date: Sun, 10 Dec 2017 01:02:27 GMT
Server: Apache/2.4.25 (Unix) PHP/7.1.5
Last-Modified: Sat, 09 Dec 2017 19:39:25 GMT
ETag: "394b1-55fed756f159d"
Accept-Ranges: bytes
Content-Length: 234673
Content-Type: image/jpeg

[http@alarmpi ~]$ curl -I 192.168.0.100/demodir/linkinsubtosub.jpg
HTTP/1.1 403 Forbidden
Date: Sun, 10 Dec 2017 01:03:09 GMT
Server: Apache/2.4.25 (Unix) PHP/7.1.5
Vary: accept-language,accept-charset
Accept-Ranges: bytes
Content-Type: text/html; charset=utf-8
Content-Language: en

[http@alarmpi ~]$ curl -I 192.168.0.100/demodir/linkinsubtotop.jpg
HTTP/1.1 403 Forbidden
Date: Sun, 10 Dec 2017 01:03:37 GMT
Server: Apache/2.4.25 (Unix) PHP/7.1.5
Vary: accept-language,accept-charset
Accept-Ranges: bytes
Content-Type: text/html; charset=utf-8
Content-Language: en

EDITed to split these error.log messages off from being hidden under the curl results.

[http@alarmpi ~]$ tail /var/log/httpd/error_log
PHP Warning:  PHP Startup: Unable to load dynamic library '/usr/lib/php/modules/imagick.so' - /usr/lib/php/modules/imagick.so: cannot open shared object file: No such file or directory in Unknown on line 0
[Sat Dec 09 14:17:35.240202 2017] [mpm_prefork:notice] [pid 8565] AH00163: Apache/2.4.25 (Unix) PHP/7.1.5 configured -- resuming normal operations
[Sat Dec 09 14:17:35.300240 2017] [core:notice] [pid 8565] AH00094: Command line: '/usr/bin/httpd -D FOREGROUND'
[Sat Dec 09 14:18:42.441258 2017] [mpm_prefork:notice] [pid 8565] AH00170: caught SIGWINCH, shutting down gracefully
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using fe80::ba27:ebff:fee9:83f0. Set the 'ServerName' directive globally to suppress this message
PHP Warning:  PHP Startup: Unable to load dynamic library '/usr/lib/php/modules/imagick.so' - /usr/lib/php/modules/imagick.so: cannot open shared object file: No such file or directory in Unknown on line 0
[Sat Dec 09 14:18:46.230867 2017] [mpm_prefork:notice] [pid 8583] AH00163: Apache/2.4.25 (Unix) PHP/7.1.5 configured -- resuming normal operations
[Sat Dec 09 14:18:46.231507 2017] [core:notice] [pid 8583] AH00094: Command line: '/usr/bin/httpd -D FOREGROUND'
[Sat Dec 09 20:03:09.745189 2017] [core:error] [pid 8587] [client 192.168.0.100:35756] AH00037: Symbolic link not allowed or link target not accessible: /srv/http/demodir/linkinsubtosub.jpg
[Sat Dec 09 20:03:37.784041 2017] [core:error] [pid 8588] [client 192.168.0.100:35758] AH00037: Symbolic link not allowed or link target not accessible: /srv/http/demodir/linkinsubtotop.jpg

In the examples I've read, 403s on symlinks, if they're not permissions/encrypton/ACL problems, are SELinux. SELinux isn't officially supported on Arch, let alone ALARM. It isn't listed by pacman. Unofficially installing it takes three pages of heinous hacking which I absolutely have not done. Anyway, its main diagnostic command is unrecognized.

[http@alarmpi ~]$ sestatus
-bash: sestatus: command not found

What am I missing, please?

Ideally this would be tagged with symlinks, but I'm new and can't make tags.

9
  • What would be more valuable is the content of your Apache .conf files. It may be under /etc/httpd/sites.enabled/*.conf or as on a Debian system, under /etc/apache2/sites.enabled/*.conf. Dec 10, 2017 at 6:53
  • Also make sure that you webserver has access to the sub-directory (i.e. demodir needs to be readable by apache2, on Debian it's www-data, on RPM based systems it's httpd) Dec 10, 2017 at 7:17
  • What gets written in the error_log when you hit one of these? Dec 10, 2017 at 11:45
  • @alexis-wilke Added the contents of all Apache .conf files, and an ls saying that Apache's user can read and execute demodir.
    – Hypocee ee
    Dec 10, 2017 at 18:04
  • 1
    You could also include ls -al /srv/http/demodir just to make sure the user and permissions in the subdirectory are identical to those in the top directory. Dec 10, 2017 at 21:54

1 Answer 1

0

The ln commands here look incorrect.

[http@alarmpi ~]$ mv PicSub.jpg demodir/
[http@alarmpi ~]$ ln -s demodir/PicSub.jpg linkintoptosub.jpg
[http@alarmpi ~]$ ln -s PicTop.jpg linkintoptotop.jpg
[http@alarmpi ~]$ ln -s PicTop.jpg demodir/linkinsubtotop.jpg
[http@alarmpi ~]$ ln -s demodir/PicSub.jpg demodir/linkinsubtosub.jpg

The linking to PicTop.jpg requires a ../ when done inside the demodir.

The linking to PicSub.jpg from within demodir must not includedemodir/.

(ok)  [http@alarmpi ~]$ mv PicSub.jpg demodir/
(ok)  [http@alarmpi ~]$ ln -s demodir/PicSub.jpg linkintoptosub.jpg
(ok)  [http@alarmpi ~]$ ln -s PicTop.jpg linkintoptotop.jpg
(fix) [http@alarmpi ~]$ ln -s ../PicTop.jpg demodir/linkinsubtotop.jpg
(fix) [http@alarmpi ~]$ ln -s PicSub.jpg demodir/linkinsubtosub.jpg

That being said, ls -l shows me broken links in a different color on Ubuntu. Maybe Arch does not do that? I have not used that Linux Distro in ages. One way to test whether a softlink is valid is to use a command such as test -f <filename> or stat <filename>. It will return an error if the softlink does not point to the right place.

To recreate a softlink, make sure to remove the existing one first.

[http@alarmpi ~]$ rm demodir/linkinsubtotop.jpg
[http@alarmpi ~]$ ln -s ../PicTop.jpg demodir/linkinsubtotop.jpg
1
  • 1
    Yup, there you go - the problem is not with Apache, but with my usage of ln while troubleshooting. After adjusting httpd.conf to no effect, then learning and running and groveling through strace logs, I went to demonstrate that the problem wasn't with symlink target permissions and wound up discovering that ln doesn't work the way I thought it did when making relative links in subdirectories. I thought it knew how to insert dots. For extra fun, no Arch's ls doesn't sort good from bad links but user http didn't have terminal coloring turned on anyway.
    – Hypocee ee
    Dec 10, 2017 at 23:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.