Smokes your problems, coughs fresh air.

Tag: Apache (Page 1 of 2)

Setting up a Zimbra authenticated proxy

On March 18th, Synacor posted about a critical Zimbra security vulnerability (CVE 2019 9670), which was quick to be exploited in the wild, and subsequently evolved to be harder to erradicate.

I’ve always had a weariness of authentication implementations by hosted applications, so I decided to block the Zimbra web mail interface using iptables (firewall), and only allow access through a separately hosted HTTP proxy which requires authentication. This way, no stray requests to API endpoints accidentally left open will be allowed. That is, almost none: I had to add exceptions to allow webdav traffic for contact and calendar synchronization. If you don’t use that, the exceptions can be left out.

Below is an example Apache configuration. Apache requires several modules to be enabled, which is an exercise left to the reader. Also, a similar proxy is easily implemented in Nginx; I just happened to have a spare Apache server.

When you access the web mail page, first you have to authenticate using old style HTTP authentication:

zimbra-pre-login

Anyway, here’s the proxy config:

<VirtualHost *:80>
        RewriteEngine on
        RewriteRule ^/(.*) https://%{HTTP_HOST}/$1 [L,R]
        ServerName webmail.example.net
</VirtualHost>
 
<VirtualHost *:443>
        ServerName webmail.example.net
        ServerAdmin webmaster@localhost
 
        SSLEngine on
        SSLCertificateFile    /etc/letsencrypt/live/webmail.example.net/cert.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/webmail.example.net/privkey.pem
        SSLCertificateChainFile /etc/letsencrypt/live/webmail.example.net/chain.pem
 
        SSLProxyEngine On
        ProxyPass        / https://mail.example.net/
        ProxyPassReverse / https://mail.example.net/
 
        # For Webdav/carddav/caldav
        <Location /dav>
                Satisfy any
                Require all granted
        </Location>
 
        # For Let's Encrypt
        <Location /.well-known/>
                Satisfy any
                Require all granted
        </Location>
 
        # For Webdav/carddav/caldav
        <Location /principals/>
                Satisfy any
                Require all granted
        </Location>
 
        # For Webdav/carddav/caldav
        <Location /SOGo/>
                Satisfy any
                Require all granted
        </Location>
 
        # For Webdav/carddav/caldav
        <Location /groupdav.php>
                Satisfy any
                Require all granted
        </Location>
 
        <Location />
                AuthType Basic
                AuthName "Zimbra webmail pre-login"
                AuthUserFile /etc/apache2/htpasswd/webmail
                Require valid-user
 
                # Exception IPs: no auth needed (for monitoring for instance)
                Require ip 1.2.3.4
        </Location>
 
        ErrorLog ${APACHE_LOG_DIR}/webmail.example.net/error.log
        CustomLog ${APACHE_LOG_DIR}/webmail.example.net/access.log combined
</VirtualHost>

NFSN PHP file write permissions in safe_mode

I’ve been causing some (security) concerns for myself by thoughtlessly using the dreaded 777 permissions for upload directories to allow the various PHP-based websites that I host at nearlyfreespeech.net to write files there. What this drastic anti-security measure didn’t allow me to is to manage these uploaded files through SSH (and SCP/Rsync). In the chroot jail which I’m allowed to enter through SSH, I am ‘me’, while the files created from PHP end up being owned by user ‘web’. However, for some reason these files didn’t get owned by group ‘web’ of which the ‘me’ user is a member. Also, I got into trouble with new directories that were being created by the upload scripts.

Writing files in PHP more securely

When I make a very basic test script in PHP, which writes a new file to a 777 directory owned by ‘me’, the files simply end up being owned by web:web with 775 permissions. To make this work a little more securely, I have to change the authorization for two entities:

  1. The directory to which I want to write needs to be group writable (775) and owned by group ‘web’.
  2. The PHP file that does the writing needs to also be owned by group ‘web’.

This will also allow the PHP file to write files in subdirectories that it creates. In the 777 scenario above, it would be possible to create these dirs, but not to create files within them. The PHP safe_mode restrictions in effect won’t allow a script owned by user ‘me’ and another group than ‘web’ to write files in a directory owned by ‘web:web’ and 775 permissions set.

All in accordance with the advice on writing files in PHP by the NFSN team.

Application headaches

There’s a little more to it, though. The stuff that I uploaded through MediaWiki and WordPress with my super-liberal 777 permission set on the upload dir (owned by ‘me’) somehow never ended up with the same group write permissions as the files in PHP test described above.

WordPress on NearlyFreeSpeech.Net

Uploads created by WordPress did end up with the permissions of the beast (666) set. However, directories created by WordPress (the year/month no. subdirectories) ended up with ‘web:web’ ownership, which regardless of their 777 mode, didn’t allow PHP in safe_mode to create any files within these directories. This is easy enough to solve by changing the ownership of the PHP files doing the writing to group ‘web’. Of course, this is best coupled with making the same changes to the upload directories and also changing the mode of these to 775.

The WordPress installation notes on the NFSN member wiki [for members only] has some more details.

I reviewed the code in WordPress responsible for writing files and I noticed that, whether creating files or directory, it actually looks at the permissions of the parent directory to decide on the mode of the newly created entity (using something along the lines of “$new_file_perms = $parent_perms & 0000666” for files and “$new_dir_perms = $parent_perms & 0007777” for directories).

MediaWiki on NearlyFreeSpeech.Net

The NFSN member wiki offers some NFSN-specific instruction for setting up MediaWiki [in their walled garden].

As with WordPress, I changed the ownership of the top-level PHP files and the upload directories to group ‘web’, as well as changing the permissions of the upload directories to 775.

However, uploaded files are being created with the mode 644 instead of 664. This is hugely annoying, because, still, I’m not allowed to access these files through SSH. I have yet to find out how I can best remedy this. Probably, I’ll end up with writing a simple PHP script that I can call just to chmod everything within the upload directory when the urge to manipulate these files strikes me.

Another beef with MediaWiki is that it creates subdirectories in the uploads directory with mode 777 instead of looking at the mode of the parent dir as WordPress does so neatly.

Apache mod_proxy configuration for The Pirate Bay

I found several apache mod_proxy configs for setting up a proxy for The Pirate Bay, but none worked fully.

You need to enable/install:

  • mod_proxy
  • mod_rewrite
  • mod_headers
  • mod_proxy_http

<Virtualhost *:80>
        ServerName tpb.yourdomain.com
 
        # Plausible deniability, and respecting your fellow pirate's privacy.
        Loglevel emerg
        CustomLog /dev/null combined
        ErrorLog /dev/null
 
        <Proxy *>
          Order deny,allow
          Allow from all
        </Proxy>
 
        # Just to fix a few links...
        RewriteEngine On
        RewriteRule \/static\.thepiratebay\.se\/(.*)$ /static/$1 [R=302,L]
 
        ProxyRequests off
 
        # Cookies are imporant to be able to disable the annoying double-row mode.
        # The . before the domain is required, but I don't know why :)
        ProxyPassReverseCookieDomain .thepiratebay.se tpb.yourdomain.com
 
        ProxyPass / http://thepiratebay.se/
        ProxyPass /static/ http://static.thepiratebay.se/
        ProxyPass /torrents/ http://torrents.thepiratebay.se/
        ProxyHTMLURLMap http://thepiratebay.se /
        ProxyHTMLURLMap http://([a-z]*).thepiratebay.se /$1 R
 
        ProxyHTMLEnable On
 
        <Location /static/>
          ProxyPassReverse /
          SetOutputFilter proxy-html
          ProxyHTMLURLMap / /static/
          RequestHeader unset Accept-Encoding
        </Location>
 
        <Location /torrents/>
          ProxyPassReverse /
          SetOutputFilter proxy-html
          ProxyHTMLURLMap / /torrents/
          RequestHeader unset Accept-Encoding
        </Location>
 
</Virtualhost>

Safari: don’t give gzipped content a .gz extension

Yesterday, while helping Caloe with the website for her company De Buitenkok, I came across the mother of all stupid bugs in Safari. Me having recently announced payformystay.com, I loaded it up in Apple’s hipster browser only to notice that the CSS wasn’t loaded. Oops!

Reloading didn’t help, but … going over to the development version, everything loaded just fine. Conclusion? My recent optimizations—concatenating + gzipping all javascript and css—somehow fucked up payformystay for Safari users. The 14 Safari visitors (16.28% of our small group of alpha users) I received since the sixth must have gotten a pretty bleak image of the technical abilities of payformystay.com’s Chief Technician (me). 😥

The old cat | gzip

So, what happened?

To reduce the number of HTTP requests per page for all the JavaScript/CSS stuff (especially when none of it is in the browser cache yet), I made a few changes to my build file to scrape the <head> of my layout template (layout.php), which I made to look something like this:

<?php if (DEV_MODE): ?>
  <link rel="stylesheet" type="text/css" href="/layout/jquery.ui.selectmenu.css" />                                   <!--MERGE ME-->
  <link rel="stylesheet" type="text/css" href="/layout/fancybox/jquery.fancybox-1.3.4.css" />                         <!--MERGE ME-->
  <link rel="stylesheet" type="text/css" href="/layout/style.css" />                                                  <!--MERGE ME-->
 
  <script src="/layout/jquery-1.4.4.min.js" type="text/javascript"></script>                                          <!--MERGE ME-->
  <script src="/layout/jquery.base64.js" type="text/javascript"></script>                                             <!--MERGE ME-->
  <script src="/layout/jquery-ui-1.8.10.custom.min.js" type="text/javascript"></script>                               <!--MERGE ME-->
  <script src="/layout/jquery.ui.selectmenu.js" type="text/javascript"></script>                                      <!--MERGE ME-->
  <script src="/layout/jquery.cookie.js" type="text/javascript"></script>                                             <!--MERGE ME-->
  <script src="/layout/fancybox/jquery.fancybox-1.3.4.js" type="text/javascript"></script>                            <!--MERGE ME-->
  <script src="/layout/jquery.ba-hashchange.min.js" type="text/javascript"></script>                                  <!--MERGE ME-->
  <script src="/layout/jquery.writeCapture-1.0.5-min.js" type="text/javascript"></script>                             <!--MERGE ME-->
<?php else: # if (!DEV_MODE) ?>
  <link href="/layout/motherofall.css.gz?2" rel="stylesheet" type="text/css" />
  <script src="/layout/3rdparty.js.gz?2" type="text/javascript"></script>
<?php endif ?>

It’s very simple: All the files with a “<!--MERGE ME-->” comment on the same line got concatenated and gzipped into motherofall.css.gz and 3rdparty.js.gz respectively, like so:

MERGE_JS_FILES := $(shell grep '<script.*<!--MERGE ME-->' layout/layout.php|sed -e 's/^.*<script src="\/\([^"]*\)".*/\1/')
MERGE_CSS_FILES := $(shell grep '<link.*<!--MERGE ME-->' layout/layout.php|sed -e 's/^.*<link .*href="\/\([^"]*\)".*/\1/')
 
all: layout/3rdparty.js.gz layout/motherofall.css.gz
 
layout/3rdparty.js.gz: layout/layout.php $(MERGE_JS_FILES)
        cat $(MERGE_JS_FILES) | gzip > $@
 
layout/motherofall.css.gz: layout/layout.php $(MERGE_CSS_FILES)
        cat $(MERGE_CSS_FILES) | gzip > $@

Of course, I simplified away the rest of my Makefile. You may notice that I could have used yui-compressor or something alike to minify the concatenated files before gzipping them, but yui-compressor chokes on some of the third-party stuff. I am using it for optimizing my own css/js (again, only in production).

Safari ignores the Content-Type for anything ending in .gz

As far as the HTTP spec is concerned, “file” extensions mean absolutely nothing. They’re trivial drivel. Whether an URL ends in .gz, .css, .gif or .png, what it all comes down to is what the Content-Type header tells the browser about the response being sent.

You may have noticed me being lazy in the layout template above when I referenced the merged files:

<link href="/layout/motherofall.css.gz?2" rel="stylesheet" type="text/css" />
  <script src="/layout/3rdparty.js.gz?2" type="text/javascript"></script>

I chose to directly reference the gzipped version of the css/js, even though I had a .htaccess files in place (within /layout/) which was perfectly capable of using the right Content-Encoding for each Accept-Encoding.

$ cat /layout/.htaccess

AddEncoding gzip .gz
 
RewriteEngine On
 
RewriteCond %{HTTP:Accept-Encoding} gzip
RewriteCond %{REQUEST_FILENAME}.gz -f
RewriteRule ^(.*)$ $1.gz [QSA,L]
 
<Files *.css.gz>
ForceType text/css
</Files>
 
<Files *.js.gz>
ForceType application/javascript
</Files>

You may notice that the .htaccess file contains some configuration to make sure that the .gz files are not served as something like application/gzip-compressed.

Anyway, I went to see if there were any browsers left that do not yet Accept-Encoding: gzip and could find none. When, yesterday, I was faced with an unstyled version of my homepage, my first reaction was (after the one where I was like hitting reload 20 times, embarrassedly mumbling something about “those damn browser-caches!”): “O then, apparently, Safari must be some exception to the rule that browsers have all been supporting gzip encoding for like forever!”

No, it isn’t so. Apparently Safari ignores the Content-Type header for any resource with an URL ending in .gz. Yes, that’s right. Safari understands Content-Encoding: gzip just fine. No problem. Just don’t call it .gz.

The new cat ; gzip

So, let’s remove the .gz suffix from these files and be done with it. The .htaccess was already capable of instructing all necessary negotiations to be able to properly serve the gzipped version only when it’s accepted (which is always, but I digress).

A few adjustments to my Makefile:

MERGE_JS_FILES := $(shell grep '<script.*<!--MERGE ME-->' layout/layout.php|sed -e 's/^.*<script src="\/\([^"]*\)".*/\1/')
MERGE_CSS_FILES := $(shell grep '<link.*<!--MERGE ME-->' layout/layout.php|sed -e 's/^.*<link .*href="\/\([^"]*\)".*/\1/')
 
all: layout/3rdparty.js.gz layout/motherofall.css.gz layout/pfms.min.js.gz
 
layout/3rdparty.js: layout/layout.php $(MERGE_JS_FILES)
	cat $(MERGE_JS_FILES) > $@
 
layout/motherofall.css: layout/layout.php $(MERGE_CSS_FILES)
	cat $(MERGE_CSS_FILES) > $@
 
%.gz: %
	gzip -c $^ > $@

And here’s the simple change to my layout.php template:

<link href="/layout/motherofall.css?2" rel="stylesheet" type="text/css" />
  <script src="/layout/3rdparty.js?2" type="text/javascript"></script>

That’s it. I welcome back all 14 Safari users looking for paid work abroad! Be it that you’re looking for international work in Africa, in America, in Asia or in Europe, please come visit and have a look at what we have on offer. 😉

Allowing apache to set Nagios cmd file

On debian, to prevent:

Error: Could not stat() command file ‘/var/lib/nagios3/rw/nagios.cmd’!

Do:

/etc/init.d/nagios3 stop
dpkg-statoverride --update --add nagios www-data 2710 /var/lib/nagios3/rw
dpkg-statoverride --update --add nagios nagios 751 /var/lib/nagios3
/etc/init.d/nagios3 start

source.

Apache rewrite rule to rewrite to sub dir

One one particular site, I wanted to rewrite all requests to a sub dir. It took me over an hour, because of some obscure problem I haven’t been able to identify.

# The main site located in the /site dir. The .* after the ^ is weird, but without it, it wouldn't work.
RewriteCond %{REQUEST_URI} !^.*/site/.*
RewriteRule ^(.*)$ site$1 [L,R=permanent]

My guess is it has to do with:

AddHandler php-cgi-script .php
Action php-cgi-script /php5/php5-cgi

Rewrite rules to redirect to a temporary offline page

Sometimes you want to take a site offline for a while. You can put this in .htaccess or the vhost config:

ErrorDocument 503 "We are performing maintenance on the site. Check back in a few minutes."
RewriteCond %{REMOTE_ADDR} !=1.2.3.4
RewriteEngine On
RewriteRule .* - [R=503,L]

When using .htaccess, be sure to have AllowOverride All.

Or when using HTML files and images:

<VirtualHost *>
  ServerAdmin webmaster@ytec.nl
  ServerName www.example.nl
 
  DocumentRoot /var/www/down/
 
  ErrorDocument 503 /maintenance.html
 
  RewriteCond %{REQUEST_URI} =/maintenance.html [OR]
  RewriteCond %{REQUEST_URI} =/logo.jpg
  RewriteRule (.*) $1 [L]
 
  RewriteEngine On
  RewriteRule .* - [R=503,L]
</VirtualHost>

Changing an apache virtual host to ssl

To change a virtual host in apache to ssl:

#Redirect all normal traffic to the https site.
<VirtualHost *:80>
  RewriteEngine on
  RewriteRule ^/(.*) https://%{HTTP_HOST}/$1 [L,R]
</VirtualHost>
 
# This virtual host was *:80 first
<VirtualHost *:443>
   DocumentRoot /bla
   # If I don't specify this, nagios's check_ssl_cert doesn't work.
   ServerName www.joho.com
 
   # These lines were added to make it SSL
   SSLEngine on
   SSLCertificateFile    /etc/ssl/certs/ssl-cert-snakeoil.pem
   SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
   # if you have an official certificate, also add some of these:
   SSLCertificateChainFile /etc/ssl/certs/bla
 
   <Directory /bla>
      Order allow,deny
      Allow from all
      AllowOverride None
      Options -MultiViews FollowSymlinks Indexes
   </Directory>
 
</VirtualHost>
« Older posts

© 2022 BigSmoke

Theme by Anders NorenUp ↑