Running a Glype Proxy in Docker
1. Install glype into /var/www/htdocs on your server
2. Create a DNS entry for server (I used the NOIP service)
3. Update /etc/hosts adding your DNS, e.g.
127.0.0.1 localhost MicroServer myproxy.hopto.org justsomestuff-dev.co.uk puppet
NOTE 1: For Ubuntu 14.04 based disties there's a bug in aufs. Part of this procedure involves installing apache into the docker container. If you use aufs as the underlying filesystem you'll get
Installing : httpd-2.4.18-1.fc23.x86_64 15/18 Error unpacking rpm package httpd-2.4.18-1.fc23.x86_64 Error unpacking rpm package httpd-2.4.18-1.fc23.x86_64 error: unpacking of archive failed on file /usr/sbin/suexec;573c37c5: cpio: cap_set_file
The bug is with the underlying server not the container. To get around this I switched to devicemapper. Update /etc/default/docker adding following to docker file and restart docker (devicemapper part):
DOCKER_OPTS="--dns 184.108.40.206 --dns 220.127.116.11 -s devicemapper"
Note 2: Centos/RedHat is already based on devicemapper for containers but you need to create an lv for docker if this is your server platform. You therefore need to reserve some space in root VG and carry out the following:
# vgs VG #PV #LV #SN Attr VSize VFree centos 2 3 0 wz--n- 30.51g 10.83g # docker-storage-setup INFO: Metadata volume docker-poolmeta already exists. Not creating a new one. Logical volume "docker-pool" created. WARNING: Converting logical volume centos/docker-pool and centos/docker-poolmeta to pool's data and metadata volumes. THIS WILL DESTROY CONTENT OF LOGICAL VOLUME (filesystem etc.) Converted centos/docker-pool to thin pool. Logical volume "docker-pool" changed.
# lvs LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert docker-pool centos twi-a-t--- 4.33g 0.00 0.15 root centos -wi-ao---- 17.66g swap centos -wi-ao---- 2.00g
You'll also need to do the following:
# groupadd -g 48 apache disable selinux # systemctl start docker
Of course if you actually use selinux (and I'm told some people do) you'll have to do something much more clever than just disabling it!
Setting up the docker container
For this test I decided to use a container based on Fedora. First pull down the latest Fedora image from docker hub
:~$ docker pull fedora:23 23: Pulling from library/fedora a3ed95caeb02: Already exists 236608c7b546: Pull complete Digest: sha256:008c29c39619425de93eee20100661bef85a3f4fe0eaaf5b33532f615ccc2cd7 Status: Downloaded newer image for fedora:23 :~$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE fedora 23 ddd5c9c1d0f2 5 minutes ago 204.7 MB centos-with-git latest 37533ba9a45a 5 weeks ago 318.8 MB centos-with-git test 37533ba9a45a 5 weeks ago 318.8 MB centos latest 0f0be3675ebb 7 weeks ago 196.6 MB phpwithmysql v2 b5a8a29f293e 6 months ago 483.7 MB phpwithmysql latest d206203dd8ea 6 months ago 483.7 MB wordpress latest ba297914d5df 6 months ago 512.3 MB php 5.6-apache e0cd35a1d2d5 7 months ago 480.7 MB mysql 5.7 93220007617a 7 months ago 321.2 MB hello-world latest 0ebda6c3e276 12 months ago 910 B :~$
Now we need to customise our docker image. To do that start it with a bash shell.
:~$ docker run --name web-test -v /var/www/htdocs:/var/www/htdocs -it fedora:23 /bin/bash (From another window) :~$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 5f4313be03a3 fedora:23 "/bin/bash" About a minute ago Up About a minute web-test
The “-v /var/www/htdocs:/var/www/htdocs” part of the command means that server directory /var/www/htdocs will be mounted in the docker container as /var/www/htdocs. I did this to make customisations to glype easier (they can be made from the server rather than having to update the docker image)
You should now get the container bash prompt in which you can run commands
[root@5f4313be03a3 /]# ls /var/www/htdocs glype
Now install the additional packages:
dnf -y install httpd php php-common php-xml openssl mod_ssl && dnf -y clean all sed -i.orig 's/#ServerName/ServerName/' /etc/httpd/conf/httpd.conf
Set up the virtual host (we're going to use https):
cd /etc/httpd/conf.d vi test-proxy.conf
<IfModule mod_ssl.c> <VirtualHost _default_:443> ServerAdmin firstname.lastname@example.org ServerName myproxy.hopto.org ServerAlias tony-proxy DocumentRoot /var/www/htdocs <Directory /var/www/htdocs/> Options Indexes FollowSymLinks MultiViews AllowOverride all Require all granted </Directory> # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, # error, crit, alert, emerg. # It is also possible to configure the loglevel for particular # modules, e.g. #LogLevel info ssl:warn ErrorLog /var/log/httpd/error.log CustomLog /var/log/httpd/access.log combined # For most configuration files from conf-available/, which are # enabled or disabled at a global level, it is possible to # include a line for only one particular virtual host. For example the # following line enables the CGI configuration for this host only # after it has been globally disabled with "a2disconf". #Include conf-available/serve-cgi-bin.conf # SSL Engine Switch: # Enable/Disable SSL for this virtual host. SSLEngine on # A self-signed (snakeoil) certificate can be created by installing # the ssl-cert package. See # /usr/share/doc/apache2/README.Debian.gz for more info. # If both key and certificate are stored in the same file, only the # SSLCertificateFile directive is needed. SSLCertificateFile /etc/httpd/ssl/apache.crt SSLCertificateKeyFile /etc/httpd/ssl/apache.key # Server Certificate Chain: # Point SSLCertificateChainFile at a file containing the # concatenation of PEM encoded CA certificates which form the # certificate chain for the server certificate. Alternatively # the referenced file can be the same as SSLCertificateFile # when the CA certificates are directly appended to the server # certificate for convinience. #SSLCertificateChainFile /etc/httpd/ssl.crt/server-ca.crt # Certificate Authority (CA): # Set the CA certificate verification path where to find CA # certificates for client authentication or alternatively one # huge file containing all of them (file must be PEM encoded) # Note: Inside SSLCACertificatePath you need hash symlinks # to point to the certificate files. Use the provided # Makefile to update the hash symlinks after changes. #SSLCACertificatePath /etc/ssl/certs/ #SSLCACertificateFile /etc/httpd/ssl.crt/ca-bundle.crt # Certificate Revocation Lists (CRL): # Set the CA revocation path where to find CA CRLs for client # authentication or alternatively one huge file containing all # of them (file must be PEM encoded) # Note: Inside SSLCARevocationPath you need hash symlinks # to point to the certificate files. Use the provided # Makefile to update the hash symlinks after changes. #SSLCARevocationPath /etc/httpd/ssl.crl/ #SSLCARevocationFile /etc/httpd/ssl.crl/ca-bundle.crl # Client Authentication (Type): # Client certificate verification type and depth. Types are # none, optional, require and optional_no_ca. Depth is a # number which specifies how deeply to verify the certificate # issuer chain before deciding the certificate is not valid. #SSLVerifyClient require #SSLVerifyDepth 10 # SSL Engine Options: # Set various options for the SSL engine. # o FakeBasicAuth: # Translate the client X.509 into a Basic Authorisation. This means that # the standard Auth/DBMAuth methods can be used for access control. The # user name is the `one line' version of the client's X.509 certificate. # Note that no password is obtained from the user. Every entry in the user # file needs this password: `xxj31ZMTZzkVA'. # o ExportCertData: # This exports two additional environment variables: SSL_CLIENT_CERT and # SSL_SERVER_CERT. These contain the PEM-encoded certificates of the # server (always existing) and the client (only existing when client # authentication is used). This can be used to import the certificates # into CGI scripts. # o StdEnvVars: # This exports the standard SSL/TLS related `SSL_*' environment variables. # Per default this exportation is switched off for performance reasons, # because the extraction step is an expensive operation and is usually # useless for serving static content. So one usually enables the # exportation for CGI and SSI requests only. # o OptRenegotiate: # This enables optimized SSL connection renegotiation handling when SSL # directives are used in per-directory context. #SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire <FilesMatch "\.(cgi|shtml|phtml|php)$"> SSLOptions +StdEnvVars </FilesMatch> <Directory /usr/lib/cgi-bin> SSLOptions +StdEnvVars </Directory> # SSL Protocol Adjustments: # The safe and default but still SSL/TLS standard compliant shutdown # approach is that mod_ssl sends the close notify alert but doesn't wait for # the close notify alert from client. When you need a different shutdown # approach you can use one of the following variables: # o ssl-unclean-shutdown: # This forces an unclean shutdown when the connection is closed, i.e. no # SSL close notify alert is send or allowed to received. This violates # the SSL/TLS standard but is needed for some brain-dead browsers. Use # this when you receive I/O errors because of the standard approach where # mod_ssl sends the close notify alert. # o ssl-accurate-shutdown: # This forces an accurate shutdown when the connection is closed, i.e. a # SSL close notify alert is send and mod_ssl waits for the close notify # alert of the client. This is 100% SSL/TLS standard compliant, but in # practice often causes hanging connections with brain-dead browsers. Use # this only for browsers where you know that their SSL implementation # works correctly. # Notice: Most problems of broken clients are also related to the HTTP # keep-alive facility, so you usually additionally want to disable # keep-alive for those clients, too. Use variable "nokeepalive" for this. # Similarly, one has to force some clients to use HTTP/1.0 to workaround # their broken HTTP/1.1 implementation. Use variables "downgrade-1.0" and # "force-response-1.0" for this. BrowserMatch "MSIE [2-6]" \ nokeepalive ssl-unclean-shutdown \ downgrade-1.0 force-response-1.0 # MSIE 7 and newer should be able to use keepalive BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown </VirtualHost> </IfModule>
Create a script to run when the docker container is launched:
vi /etc/httpd/run_apache_foreground #!/bin/bash #set variables APACHE_LOG_DIR="/var/log/httpd" APACHE_LOCK_DIR="/var/lock/httpd" APACHE_RUN_USER="apache" APACHE_RUN_GROUP="apache" APACHE_PID_FILE="/var/run/httpd/httpd.pid" APACHE_RUN_DIR="/var/run/httpd" #create directories if necessary if ! [ -d /var/run/httpd ]; then mkdir /var/run/httpd;fi if ! [ -d /var/log/httpd ]; then mkdir /var/log/httpd;fi if ! [ -d /var/lock/httpd ]; then mkdir /var/lock/httpd;fi #run Apache httpd -D FOREGROUND
Set the permissions of the script
chmod 755 /etc/httpd/run_apache_foreground
Set up SSL certificate
[root@4d72e44a810d httpd]# mkdir /etc/httpd/ssl [root@4d72e44a810d httpd]# openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/httpd/ssl/apache.key -out /etc/httpd/ssl/apache.crt Generating a 2048 bit RSA private key ..............+++ .......................+++ writing new private key to '/etc/httpd/ssl/apache.key' ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [XX]:GB State or Province Name (full name) :Cornwall Locality Name (eg, city) [Default City]:St Tudy Organization Name (eg, company) [Default Company Ltd]:JSS Organizational Unit Name (eg, section) : Common Name (eg, your name or your server's hostname) :glype-proxy Email Address :email@example.com
I had to correct invalid symbolic link (not sure if that always happens?)
[root@9607ef40c1b9 var]# cd /var; rm lock rm: remove symbolic link 'lock'? y [root@9607ef40c1b9 var]# ln -s ../run lock
Now add user so can access externaly mounted directory (/var/www/htdocs)
groupadd -g 1000 tony useradd -u 1000 -g 1000 -d /home/tony -m -s /bin/bash tony add apache to tape group
(last step was due to a clash with group IDs between the container and server due to different Linux distributions)
Now we can save the container state. To leave the container environment, you must press Ctrl+p followed by Ctrl+q, if you just call exit in the shell, you will also stop the container and lost what you have done so far.
:~$ docker commit web-test glype-proxy sha256:4cba2b74369a1670826ffa98f4d31f0764ecf8f4bb4592a9a1e8e7c14a87d4e4 :~$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE glype-proxy latest 26bc3d21a2f8 7 seconds ago 257 MB fedora 23 ddd5c9c1d0f2 5 weeks ago 204.7 MB
Start container using our customised image with listening on external port 8443 which maps to internal 443 (-p 8443:443) and runs the script to run apache in foreground.
:~$ docker run -v /var/www/htdocs:/var/www/htdocs -p 8443:443 -d -t glype-proxy /etc/httpd/run_apache_foreground 7eb47608b5a00722a107ad12ee6d947ea14db99c429ced6e89fbc1329e517b0b :~$ :~$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 7eb47608b5a0 glype-proxy "/etc/httpd/run_apach" 30 seconds ago Up 21 seconds 0.0.0.0:8443->443/tcp happy_turing afa9adf95f2d fedora:23 "/bin/bash" 47 minutes ago Up 47 minutes web-test
Problem, when I test connecting to port 8443 I get an error saying ioncube not installed. So I need to install that.
docker stop 7eb47608b5a0
As before start container with a bash shell
docker run --name web-test3 -v /development/glype:/development/glype -it glype-proxy /bin/bash
Copy the ioncube package:
docker cp ioncube_loaders_lin_x86-64_5.1.2.tar.gz 3f4e9fbc1b36:/var/tmp [root@3f4e9fbc1b36 /]# cd /var/tmp [root@3f4e9fbc1b36 tmp]# ls ioncube_loaders_lin_x86-64_5.1.2.tar.gz [root@3f4e9fbc1b36 tmp]# tar xvzf ioncube_loaders_lin_x86-64_5.1.2.tar.gz bash: tar: command not found
doh, need to install tar
dnf -y install tar && dnf -y clean all [root@3f4e9fbc1b36 local]# cd /usr/local [root@3f4e9fbc1b36 local]# tar xzf /var/tmp/ioncube_loaders_lin_x86-64_5.1.2.tar.gz [root@3f4e9fbc1b36 local]# cd ioncube [root@3f4e9fbc1b36 ioncube]# cp loader-wizard.php /var/www/htdocs/glype [root@3f4e9fbc1b36 ioncube]# cd /usr/lib64/php/modules [root@3f4e9fbc1b36 modules]# cp /usr/local/ioncube/ioncube_loader_lin_5.6* . [root@3f4e9fbc1b36 modules]# chmod 755 ioncube* [root@3f4e9fbc1b36 modules]# cd /etc/php.d [root@3f4e9fbc1b36 php.d]# vi 00-ioncube.ini add line: zend_extension = /usr/lib64/php/modules/ioncube_loader_lin_5.6.so ctrl+p ctrl+q docker commit web-test3 glype-proxy
Now try again
docker run -v /var/www/htdocs:/var/www/htdocs -p 8443:443 -d -t glype-proxy /etc/httpd/run_apache_foreground
Broswe to https://myproxy.hopto.org:8443/loader-wizard.php - should confirm it's installed OK
Browse to https://myproxy.hopto.org:8443 - Glype proxy page should now open
Running the container
Socket Based Activation
Intially my aim was to start this container on demand. systemd has socket based activation (like inetd performs) so that if you connect on a socket a process will start. However, docker containers are not socket aware. I then found the following atlassian article which describes how you can use systemd-socket-proxyd to start non socket aware processes using socket activation. Could I get this to work, could I fk! I could get a socket running fine and if I manually started the proxy service, the docker container started but the link between the socket starting the proxy just didn't want to know. Unfortunately systemd-socket-proxyd isn't extensively documented (as far as I could see) so after some frustrating hours, I switched to plan B. ===Plan B=== Plan B just involves using a SSH client on my smart phone ( I use JuicsSSH) to start the docker container (using SSH keys and a script to start the docker container on the server makes it pretty slick). I then have the glype proxy available for use.