<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.2">Jekyll</generator><link href="http://localhost:4000/feed.xml" rel="self" type="application/atom+xml" /><link href="http://localhost:4000/" rel="alternate" type="text/html" /><updated>2026-05-15T06:26:43+02:00</updated><id>http://localhost:4000/feed.xml</id><title type="html">axaneco’s tech blog</title><subtitle>documentation of my private IT projects</subtitle><entry><title type="html">Setup of a Guacamole HTML5 remote XFCE desktop via VNC for Ubuntu 24.04</title><link href="http://localhost:4000/doc/2025/07/21/remote-vm-with-xfce.html" rel="alternate" type="text/html" title="Setup of a Guacamole HTML5 remote XFCE desktop via VNC for Ubuntu 24.04" /><published>2025-07-21T11:00:00+02:00</published><updated>2025-07-21T11:00:00+02:00</updated><id>http://localhost:4000/doc/2025/07/21/remote-vm-with-xfce</id><content type="html" xml:base="http://localhost:4000/doc/2025/07/21/remote-vm-with-xfce.html"><![CDATA[<p>Goal of this article is to document the setup of a Guacamole HTML5 remote XFCE desktop via VNC on Ubuntu 24.04.x.
The Guacamole setup is based on 
<a href="https://znil.net/index.php?title=Ubuntu_20.04.x_LTS_-_Guacamole_HTML5_Remotedesktop_Gateway_installieren_mit_Apache_Reverse_Proxy">Ubuntu 20.04.x LTS - Guacamole HTML5 Remotedesktop Gateway installieren mit Apache Reverse Proxy</a> (in german, written by Bernhard Linz)</p>

<h2 id="install-the-guacamole-server">Install the guacamole server</h2>

<p>So, let’s assume we have a fresh Ubuntu 24.04.x installation on a remote machine with DNS name <code class="language-plaintext highlighter-rouge">test.example.org</code> running a ssh server.
First, we become root:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo</span> <span class="nt">-i</span></code></pre></figure>

<p>Next, we install everything that is needed to run the Apache Tomcat server. We’ll need tomcat9, since tomcat10 (that is the default for Ubuntu 24.04) is not compatible with Guacamole. So we have to add the respective repostitory first.</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">add-apt-repository <span class="nt">-y</span> <span class="nt">-s</span> <span class="s2">"deb http://archive.ubuntu.com/ubuntu/ jammy main universe"</span>
apt <span class="nb">install </span>make libssh2-1-dev libtelnet-dev libpango1.0-dev libossp-uuid-dev libcairo2-dev libpng-dev libvncserver-dev libvorbis-dev  gcc libssh-dev libpulse-dev tomcat9 tomcat9-admin tomcat9-docs ghostscript libwebp-dev libavcodec-dev libavutil-dev libswscale-dev libjpeg-turbo8-dev libtool-bin libossp-uuid-dev libavformat-dev freerdp2-dev libwebsockets-dev libssl-dev</code></pre></figure>

<p>Now, on calling <code class="language-plaintext highlighter-rouge">http://test.example.org:8080</code> we should see the standard Apache Tomcat “It works!” page.</p>

<p>As of July 2025, the current Guacamole version is 1.6.0, so we download the following files:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">cd</span> /usr/src
wget https://downloads.apache.org/guacamole/1.6.0/source/guacamole-server-1.6.0.tar.gz
wget https://downloads.apache.org/guacamole/1.6.0/binary/guacamole-1.6.0.war</code></pre></figure>

<p>So we have server source code and client binary.</p>

<p>Now we unpack the server code</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">tar </span>xvzf guacamole-server-1.6.0.tar.gz</code></pre></figure>

<p>and compile it:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">cd</span> /usr/src/guacamole-server-1.6.0
./configure <span class="nt">--with-systemd-dir</span><span class="o">=</span>/etc/systemd/system</code></pre></figure>

<p>That should result in an output like this:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nt">------------------------------------------------</span>
guacamole-server version 1.6.0
<span class="nt">------------------------------------------------</span>

   Library status:

     freerdp ............. <span class="nb">yes</span> <span class="o">(</span>2.x<span class="o">)</span>
     pango ............... <span class="nb">yes
     </span>libavcodec .......... <span class="nb">yes
     </span>libavformat ......... <span class="nb">yes
     </span>libavutil ........... <span class="nb">yes
     </span>libssh2 ............. <span class="nb">yes
     </span>libssl .............. <span class="nb">yes
     </span>libswscale .......... <span class="nb">yes
     </span>libtelnet ........... <span class="nb">yes
     </span>libVNCServer ........ <span class="nb">yes
     </span>libvorbis ........... <span class="nb">yes
     </span>libpulse ............ <span class="nb">yes
     </span>libwebsockets ....... <span class="nb">yes
     </span>libwebp ............. <span class="nb">yes
     </span>wsock32 ............. no

   Protocol support:

      Kubernetes .... <span class="nb">yes
      </span>RDP ........... <span class="nb">yes
      </span>SSH ........... <span class="nb">yes
      </span>Telnet ........ <span class="nb">yes
      </span>VNC ........... <span class="nb">yes

   </span>Services / tools:

      guacd ...... <span class="nb">yes
      </span>guacenc .... <span class="nb">yes
      </span>guaclog .... <span class="nb">yes

   </span>FreeRDP plugins: /usr/lib/x86_64-linux-gnu/freerdp2
   Init scripts: no
   Systemd units: /etc/systemd/system

Type <span class="s2">"make"</span> to compile guacamole-server.</code></pre></figure>

<p>If that’s the case as all “yes”-entries are present, we can compile:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">make
make <span class="nb">install</span></code></pre></figure>

<p>Last we have to configure the dynamic linker run-time bindings:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">ldconfig</code></pre></figure>

<p>Now we can start the <code class="language-plaintext highlighter-rouge">systemd</code> service that the installation has provided:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">systemctl start guacd.service
systemctl status guacd.service</code></pre></figure>

<p>which should give us an output like</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">● guacd.service - Guacamole Server
     Loaded: loaded <span class="o">(</span>/etc/systemd/system/guacd.service<span class="p">;</span> disabled<span class="p">;</span> vendor preset: enabled<span class="o">)</span>
     Active: active <span class="o">(</span>running<span class="o">)</span> since Fri 2023-04-21 14:52:28 UTC<span class="p">;</span> 6s ago
       Docs: man:guacd<span class="o">(</span>8<span class="o">)</span>
   Main PID: 47807 <span class="o">(</span>guacd<span class="o">)</span>
      Tasks: 1 <span class="o">(</span>limit: 2233<span class="o">)</span>
     Memory: 10.2M
        CPU: 10ms
     CGroup: /system.slice/guacd.service
             └─47807 /usr/local/sbin/guacd <span class="nt">-f</span>

Apr 21 14:52:28 test181 systemd[1]: Started Guacamole Server.
Apr 21 14:52:28 test181 guacd[47807]: Guacamole proxy daemon <span class="o">(</span>guacd<span class="o">)</span> version 1.5.1 started
Apr 21 14:52:28 test181 guacd[47807]: guacd[47807]: INFO:        Guacamole proxy daemon <span class="o">(</span>guacd<span class="o">)</span> version 1.5.1 started
Apr 21 14:52:28 test181 guacd[47807]: guacd[47807]: INFO:        Listening on host 127.0.0.1, port 4822
Apr 21 14:52:28 test181 guacd[47807]: Listening on host 127.0.0.1, port 4822</code></pre></figure>

<p>We stop the server in order to amend configurations:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">systemctl stop guacd.service</code></pre></figure>

<p>First we create the directory</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">mkdir</span> /etc/guacamole</code></pre></figure>

<p>There we create a file <code class="language-plaintext highlighter-rouge">/etc/guacamole/guacamole.properties</code> with the content</p>

<p><code class="language-plaintext highlighter-rouge">basic-user-mapping: /etc/guacamole/user-mapping.xml</code></p>

<p>For our purposes, this file reads like</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">&lt;user-mapping&gt;
        &lt;<span class="o">!</span><span class="nt">--</span> Per-user authentication and config information <span class="nt">--</span><span class="o">&gt;</span>
        &lt;<span class="o">!</span><span class="nt">--</span> FIRST USER <span class="nt">--</span><span class="o">&gt;</span>
        &lt;authorize 
                <span class="nv">username</span><span class="o">=</span><span class="s2">"test01"</span>
                <span class="nv">password</span><span class="o">=</span><span class="s2">"52c88bca7c5b3ab3e7ed8901ea1bc83f"</span>
                <span class="nv">encoding</span><span class="o">=</span><span class="s2">"md5"</span><span class="o">&gt;</span>
                &lt;<span class="o">!</span><span class="nt">--</span> First authorized Remote connection <span class="nt">--</span><span class="o">&gt;</span>
                &lt;connection <span class="nv">name</span><span class="o">=</span><span class="s2">"SSH Admin"</span><span class="o">&gt;</span>
                        &lt;protocol&gt;ssh&lt;/protocol&gt;
                        &lt;param <span class="nv">name</span><span class="o">=</span><span class="s2">"hostname"</span><span class="o">&gt;</span>10.10.10.10&lt;/param&gt;      
                        &lt;param <span class="nv">name</span><span class="o">=</span><span class="s2">"port"</span><span class="o">&gt;</span>22&lt;/param&gt;                    
                        &lt;param <span class="nv">name</span><span class="o">=</span><span class="s2">"username"</span><span class="o">&gt;</span>test01&lt;/param&gt;     
                        &lt;param <span class="nv">name</span><span class="o">=</span><span class="s2">"enable-sftp"</span><span class="o">&gt;</span><span class="nb">true</span>&lt;/param&gt;
                &lt;/connection&gt; 
                &lt;<span class="o">!</span><span class="nt">--</span> second authorized Remote connection <span class="nt">--</span><span class="o">&gt;</span>
                &lt;connection <span class="nv">name</span><span class="o">=</span><span class="s2">"Terminal Server VNC"</span><span class="o">&gt;</span>
                        &lt;protocol&gt;vnc&lt;/protocol&gt;
                        &lt;param <span class="nv">name</span><span class="o">=</span><span class="s2">"hostname"</span><span class="o">&gt;</span>127.0.0.1&lt;/param&gt; 
                        &lt;param <span class="nv">name</span><span class="o">=</span><span class="s2">"port"</span><span class="o">&gt;</span>5901&lt;/param&gt;         
                        &lt;param <span class="nv">name</span><span class="o">=</span><span class="s2">"server-layout"</span><span class="o">&gt;</span>de-de-qwertz&lt;/param&gt;
                &lt;/connection&gt;
        &lt;/authorize&gt;
&lt;/user-mapping&gt;</code></pre></figure>

<p>Of course we have to amend hostname, username, and password for our system.</p>

<p>The password hash can be created by</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">echo</span> <span class="nt">-n</span> <span class="s1">'secretPassword'</span> | <span class="nb">md5sum</span></code></pre></figure>

<p>Finally, we have to append the Guacamole home to the environment:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">echo </span><span class="nv">GUACAMOLE_HOME</span><span class="o">=</span><span class="s2">"/etc/guacamole"</span> <span class="o">&gt;&gt;</span> /etc/environment</code></pre></figure>

<p>It makes sense to reboot at this point.</p>

<p>Now we enable the Guacamole service for automated start:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">systemctl <span class="nb">enable </span>guacd.service</code></pre></figure>

<p>Next, we install the client:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">cp</span> /usr/src/guacamole-1.6.0.war /var/lib/tomcat9/webapps/guacamole.war</code></pre></figure>

<p>Then we have to link the property directory to the place where it is expected:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">ln</span> <span class="nt">-s</span> /etc/guacamole /usr/share/tomcat9/.guacamole</code></pre></figure>

<p>To speed up the tomcat restart, we have to create the following file</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">touch</span> /usr/share/tomcat9/bin/setenv.sh
<span class="nb">chmod</span> +x /usr/share/tomcat9/bin/setenv.sh</code></pre></figure>

<p>with this content:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c">#!/bin/sh</span>
<span class="nb">export </span><span class="nv">CATALINA_OPTS</span><span class="o">=</span><span class="s2">"</span><span class="nv">$CATALINA_OPTS</span><span class="s2"> -Djava.security.egd=file:/dev/./urandom"</span></code></pre></figure>

<p>Now we can start the guacamole server:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">systemctl start guacd.service</code></pre></figure>

<p>If we now browse to</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">http://test.example.org:8080/guacamole/</code></pre></figure>

<p>we should be offered a login and then the connection options “SSH Admin” and “Terminal Server VNC”.</p>

<p>The SSH connection should work already (if not, you did probably not reboot), for the VNC we need to prepare some more things, as follows.</p>

<h2 id="install-the-vnc-server">Install the VNC server</h2>

<p>Let’s assume that we have a user <code class="language-plaintext highlighter-rouge">test01</code> as our (sudo-enabled) standard user for the machine we just prepared, and that we are now logged in as that user.</p>

<p>We install the needed stuff:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">-y</span> xfce4 xfce4-goodies tigervnc-standalone-server tigervnc-xorg-extension autocutsel xfonts-base xfonts-100dpi xfonts-75dpi</code></pre></figure>

<p>Then we create a vnc password for our (non-root) user:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">vncpasswd</code></pre></figure>

<p>Next, we have to create the vnc startup file in our <strong>user</strong> home directory and make it executable:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">cd
mkdir</span> .vnc
<span class="nb">touch</span> ~/.vnc/xstartup
<span class="nb">chmod </span>755 ~/.vnc/xstartup</code></pre></figure>

<p>The contents of this file is</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c">#!/bin/sh</span>
<span class="nb">unset </span>SESSION_MANAGER
<span class="nb">unset </span>DBUS_SESSION_BUS_ADDRESS
<span class="nv">DBUS_SESSION_BUS_ADDRESS</span><span class="o">=</span>unix:path<span class="o">=</span><span class="nv">$XDG_RUNTIME_DIR</span>/bus
<span class="nb">exec </span>startxfce4 </code></pre></figure>

<p>For starting the VNC server on boot, we change to root again with <code class="language-plaintext highlighter-rouge">sudo -i</code> and create a service file <code class="language-plaintext highlighter-rouge">/etc/systemd/system/vncserver@.service</code>, as suggested by <a href="https://serverok.in/install-xfce-vnc-remote-desktop-on-ubuntu">Install Xfce VNC remote desktop on Ubuntu</a>.</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="o">[</span>Unit]
<span class="nv">Description</span><span class="o">=</span>Remote desktop service <span class="o">(</span>VNC<span class="o">)</span>
<span class="nv">After</span><span class="o">=</span>syslog.target network.target

<span class="o">[</span>Service]
<span class="nv">Type</span><span class="o">=</span>simple
<span class="nv">User</span><span class="o">=</span>test01
<span class="nv">PAMName</span><span class="o">=</span>login
<span class="nv">PIDFile</span><span class="o">=</span>/home/%u/.vnc/%H%i.pid
<span class="nv">ExecStartPre</span><span class="o">=</span>/bin/sh <span class="nt">-c</span> <span class="s1">'/usr/bin/vncserver -kill :%i &gt; /dev/null 2&gt;&amp;1 || :'</span>
<span class="nv">ExecStart</span><span class="o">=</span>/usr/bin/vncserver :%i <span class="nt">-geometry</span> 1440x900 <span class="nt">-alwaysshared</span> <span class="nt">-fg</span>
<span class="nv">ExecStop</span><span class="o">=</span>/usr/bin/vncserver <span class="nt">-kill</span> :%i

<span class="o">[</span>Install]
<span class="nv">WantedBy</span><span class="o">=</span>multi-user.target</code></pre></figure>

<p>In the <code class="language-plaintext highlighter-rouge">Service</code> section, we replace <code class="language-plaintext highlighter-rouge">test01</code> by the username of our local user.</p>

<p>To enable the service by default, we make the service available at boot time by</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">cd</span> /etc/systemd/system/multi-user.target.wants/
<span class="nb">ln</span> <span class="nt">-s</span> /etc/systemd/system/vncserver@.service ./vncserver@1.service</code></pre></figure>

<p>Time for a last reboot!</p>

<p>Then, we should be able to login to <code class="language-plaintext highlighter-rouge">http://test.example.com:8080/guacamole</code> with the credentials we stored in <code class="language-plaintext highlighter-rouge">/etc/guacamole/user-mapping.xml</code>, click on “Terminal Server VNC”, type in the VNC password we have defined before, and see a virtual XFCE screen in our browser.</p>

<p>That’s it! Now we have set up a Guacamole HTML5 remote XFCE desktop via VNC for Ubuntu 24.04.</p>

<p>For security reasons, it makes absolutely sense to add a 2FA as described in the already mentioned (german) <a href="https://znil.net/index.php?title=Ubuntu_20.04.x_LTS_-_Guacamole_HTML5_Remotedesktop_Gateway_installieren_mit_Apache_Reverse_Proxy">article</a> by Bernhard Linz. For security reasons, it makes also sense to map the Tomcat (http port 8080) via Apache or nginx reverse proxy to https, as described in the linked article.</p>

<p>Addena:</p>

<p>1</p>

<p>If you have a virtual server at a provider like Netcup which provides a VNC console to the machine, after the last reboot you’ll end up in a graphical login screen that produces a deadlock. To avoid being locked out you can change the default boot target:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>systemctl set-default multi-user.target</code></pre></figure>

<p>After a reboot you’ll see the console login again.</p>

<p>2</p>

<p>In order to start applications like Firefox etc from the command line, append the following to the <code class="language-plaintext highlighter-rouge">.bashrc</code> file:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nv">XAUTHORITY</span><span class="o">=</span><span class="nv">$HOME</span>/.Xauthority
<span class="nb">export </span>XAUTHORITY</code></pre></figure>]]></content><author><name></name></author><category term="doc" /><summary type="html"><![CDATA[Goal of this article is to document the setup of a Guacamole HTML5 remote XFCE desktop via VNC on Ubuntu 24.04.x. The Guacamole setup is based on Ubuntu 20.04.x LTS - Guacamole HTML5 Remotedesktop Gateway installieren mit Apache Reverse Proxy (in german, written by Bernhard Linz)]]></summary></entry><entry><title type="html">Avoid DNS deadlock when updating Pihole</title><link href="http://localhost:4000/doc/2025/02/23/pihole-deadlock.html" rel="alternate" type="text/html" title="Avoid DNS deadlock when updating Pihole" /><published>2025-02-23T10:00:00+01:00</published><updated>2025-02-23T10:00:00+01:00</updated><id>http://localhost:4000/doc/2025/02/23/pihole-deadlock</id><content type="html" xml:base="http://localhost:4000/doc/2025/02/23/pihole-deadlock.html"><![CDATA[<p>During the last <a href="https://docs.pi-hole.net/">Pihole</a>-Update I went into a self-manufactured deadlock in DNS resolving. That was, because during the update of Pihole the DNS resolving by Pihole itself was interrupted, and the update script waited for DNS resolving to become available again, but that would not be the case until the update script was finished. So, this was a network design problem, because the Pihole gets its IP address and its DNS server from my router, als all other clients do too. But the router gives to all clients the Pihole as DNS resolver. For that reason, the Pihole was the DNS resolver for itself, what lead to the deadlock.</p>

<p>To solve this, I had to make some changes at the router, which is a barebone (Qotom Q330G4) running Ubuntu 24.04 server with iptabes and isc-dhcp-server.</p>

<p>First, I disabled the systemd-resolved mechanis as Kristof suggested in his <a href="https://mattei.io/network/ubuntu/ipv6/2024/05/19/ubuntu-24-04-disable-systemd-resolved-stub-listener.html">blog</a>:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c"># Create directory</span>
<span class="nb">sudo mkdir</span> <span class="nt">-p</span> /etc/systemd/resolved.conf.d/
<span class="c"># Set permissions</span>
<span class="nb">sudo chmod </span>755 /etc/systemd/resolved.conf.d/
<span class="c"># Disable</span>
<span class="nb">printf</span> <span class="s2">"[Resolve]</span><span class="se">\n</span><span class="s2">DNSStubListener=no</span><span class="se">\n</span><span class="s2">"</span> | <span class="nb">sudo tee</span> /etc/systemd/resolved.conf.d/noresolved.conf
<span class="c"># Restart systemd-resolved</span>
<span class="nb">sudo </span>systemctl restart systemd-resolved.service</code></pre></figure>

<p>Then, I set up dnsmaq as DNS resolver with the following configuration:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c"># /etc/dnsmasq.conf</span>
<span class="c"># Set up your local domain here    </span>
<span class="nv">domain</span><span class="o">=</span>example.com

<span class="c"># Example: The option local=/localnet/ ensures that any domain name query which ends in .localnet will be answered if possible from /etc/hosts or DHCP, but never sent to an upstream server</span>
<span class="c"># don't forward requests (andrewoberstar.com/blog/2012/12/30/raspberry-pi-as-server-dns-and-dhcp)</span>
<span class="nb">local</span><span class="o">=</span>/example.com/

listen-address<span class="o">=</span>192.0.2.254
resolv-file<span class="o">=</span>/etc/dnsresolv.conf
log-facility<span class="o">=</span>/var/log/dnsmasq.log
log-queries
no-negcache
<span class="c">#strict-order</span>

<span class="c"># Use the hosts file on this machine</span>
expand-hosts</code></pre></figure>

<p>Here, <code class="language-plaintext highlighter-rouge">192.0.2.254</code> is the IP adress of the router.<br />
The resolv file reads</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c">#/etc/dnsresolv.conf </span>
domain example.com
search example.com

<span class="c">#quad9</span>
nameserver 9.9.9.10
<span class="c"># cloudflare</span>
nameserver 1.1.1.1</code></pre></figure>

<p>With this configuration, the router can work as an auxiliary DNS resolver, i.e. for the Pihole itself - not to confuse with the upstream DNS resolver configured in Pihole, that are only used when Pihole is running! (It’s complicated, I know.)</p>

<p>But, to let the Pihole know that the router <code class="language-plaintext highlighter-rouge">192.0.2.254</code> is ready to serve it’s DNS requests, I had to configure the router’s DHCP setting accordingly:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c">#/etc/dhcp/dhcpd.conf</span>
<span class="c"># LAN (Internal)</span>
subnet 192.0.2.0 netmask 255.255.255.0 <span class="o">{</span>
  interface enp2s0<span class="p">;</span>
  option routers 192.0.2.254<span class="p">;</span>
  option domain-name-servers 192.0.2.53<span class="p">;</span>
  option broadcast-address 192.0.2.255<span class="p">;</span>
  default-lease-time 43200<span class="p">;</span>
  max-lease-time 43200<span class="p">;</span>
<span class="o">}</span>

<span class="o">[</span>...]

host dns <span class="o">{</span>
 hardware ethernet 00:53:af:ab:cd:ef<span class="p">;</span>
 fixed-address 192.0.2.53<span class="p">;</span>
 option domain-name-servers 192.0.2.254<span class="p">;</span>
<span class="o">}</span></code></pre></figure>

<p>With this setup, the DNS resolver for queries coming from the Pihole machine (again, <strong>not</strong> to be confused with the upstream servers configured in Piholte itself) is the router’s dnsmasq, that in turn has i.e. Clouflare and quad9 configured as external upstream servers.</p>

<p>Thus, when Pihole is under update and needs DNS resolution during the process (or even during normal operation), the router will provide this.<br />
Furthermore, the router can serve as a backup DNS resolver in case Pihole is not available for other reasons, only thing is to change <code class="language-plaintext highlighter-rouge">option domain-name-servers</code>from <code class="language-plaintext highlighter-rouge">192.0.2.53</code> to <code class="language-plaintext highlighter-rouge">192.0.2.254</code> in the router’s dhcpd configuration for the LAN subnet. There will be of course no DNS filtering then (except the upstram DNS server provides it), but the DNS resolving is always provided.</p>]]></content><author><name></name></author><category term="doc" /><summary type="html"><![CDATA[During the last Pihole-Update I went into a self-manufactured deadlock in DNS resolving. That was, because during the update of Pihole the DNS resolving by Pihole itself was interrupted, and the update script waited for DNS resolving to become available again, but that would not be the case until the update script was finished. So, this was a network design problem, because the Pihole gets its IP address and its DNS server from my router, als all other clients do too. But the router gives to all clients the Pihole as DNS resolver. For that reason, the Pihole was the DNS resolver for itself, what lead to the deadlock.]]></summary></entry><entry><title type="html">Connect a new exporting server to a running Prometheus instance</title><link href="http://localhost:4000/doc/2025/02/22/nodeexporter.html" rel="alternate" type="text/html" title="Connect a new exporting server to a running Prometheus instance" /><published>2025-02-22T10:00:00+01:00</published><updated>2025-02-22T10:00:00+01:00</updated><id>http://localhost:4000/doc/2025/02/22/nodeexporter</id><content type="html" xml:base="http://localhost:4000/doc/2025/02/22/nodeexporter.html"><![CDATA[<p>(Reminder only, especially no full explanation of how to set up a prometheus host.)</p>

<p>Exporting machine: Machine that is running the node exporter/Apache exporter.<br />
Prometheus machine: Machine that is running the Prometheus server to collect the exported telemetry data. Also may be the host for Grafana to display the data in a nice way.</p>

<p>On the exporting machine:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">adduser pexp <span class="nt">--system</span> <span class="nt">--no-create-home</span></code></pre></figure>

<p>Install <code class="language-plaintext highlighter-rouge">/opt/apache-exporter</code> and <code class="language-plaintext highlighter-rouge">/opt/node-exporter</code> binaries.</p>

<p>On the Prometheus machine, execute script</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c">#!/bin/bash</span>
<span class="k">if</span> <span class="o">[</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span> <span class="o">]</span>
  <span class="k">then
    </span><span class="nb">echo</span> <span class="s2">"No argument supplied"</span>
<span class="k">else
    </span><span class="nb">sudo </span>openssl req <span class="nt">-new</span> <span class="nt">-newkey</span> rsa:2048 <span class="nt">-days</span> 3650 <span class="nt">-nodes</span> <span class="nt">-x509</span> <span class="nt">-keyout</span> <span class="nv">$1_exporter</span>.key <span class="nt">-out</span> <span class="nv">$1_exporter</span>.crt <span class="nt">-subj</span> <span class="s2">"/C=DE/ST=Hamburg/L=Hamburg/O=org/OU=IT/CN=example.com"</span> <span class="nt">-addext</span> <span class="s2">"subjectAltName = IP:</span><span class="nv">$1</span><span class="s2">"</span>
<span class="k">fi</span></code></pre></figure>

<p>with exporting machine public IP address as argument in order to get some transport encryption for the data between exporting machine and Prometheus machine.</p>

<p>Copy the [public_IP]_exporter.crt of the new exporter machine to <code class="language-plaintext highlighter-rouge">/etc/prometheus</code> at the prometheus machine.
Copy (i.e. scp) <code class="language-plaintext highlighter-rouge">.key</code> and <code class="language-plaintext highlighter-rouge">.crt</code>files to exporting machine under <code class="language-plaintext highlighter-rouge">/etc/node_exporter</code>. Chown to <code class="language-plaintext highlighter-rouge">pexp:root</code> there.</p>

<p>On exporting machine, create <code class="language-plaintext highlighter-rouge">/opt/node_exporter/web.yml</code>:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">tls_server_config:
  cert_file: /etc/node_exporter/[public_IP]_exporter.crt
  key_file: /etc/node_exporter/[public_IP]_exporter.key</code></pre></figure>

<p>and chown to <code class="language-plaintext highlighter-rouge">pexp:root</code>.</p>

<p>Under <code class="language-plaintext highlighter-rouge">/lib/systemd/system</code> create an <code class="language-plaintext highlighter-rouge">apacheexporter.service</code></p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="o">[</span>Unit]
<span class="nv">Description</span><span class="o">=</span>Apache Exporter <span class="k">for </span>Prometheus
<span class="nv">Documentation</span><span class="o">=</span>https://github.com/Lusitaniae/apache_exporter
<span class="nv">After</span><span class="o">=</span>network-online.target

<span class="o">[</span>Service]
<span class="nv">User</span><span class="o">=</span>pexp
<span class="nv">Restart</span><span class="o">=</span>on-failure

<span class="nv">ExecStart</span><span class="o">=</span>/opt/apache-exporter/apache_exporter <span class="nt">--web</span>.config<span class="o">=</span>/etc/node_exporter/web.yml <span class="nt">--scrape_uri</span><span class="o">=</span>http://localhost/server-status/?auto   <span class="nt">--telemetry</span>.address<span class="o">=</span>0.0.0.0:9117   <span class="nt">--telemetry</span>.endpoint<span class="o">=</span>/metrics

<span class="o">[</span>Install]
<span class="nv">WantedBy</span><span class="o">=</span>multi-user.target</code></pre></figure>

<p>and a <code class="language-plaintext highlighter-rouge">nodeexporter.service</code>:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="o">[</span>Unit]
<span class="nv">Description</span><span class="o">=</span>Prometheus Node Exporter
<span class="nv">Documentation</span><span class="o">=</span>https://prometheus.io/docs/guides/node-exporter/
<span class="nv">After</span><span class="o">=</span>network-online.target

<span class="o">[</span>Service]
<span class="nv">User</span><span class="o">=</span>pexp
<span class="nv">Restart</span><span class="o">=</span>on-failure

<span class="nv">ExecStart</span><span class="o">=</span>/opt/node-exporter/node_exporter <span class="nt">--web</span>.config.file<span class="o">=</span>/etc/node_exporter/web.yml

<span class="o">[</span>Install]
<span class="nv">WantedBy</span><span class="o">=</span>multi-user.target</code></pre></figure>

<p>Enable both services:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">cd</span> /etc/systemd/system/multi-user.target.wants
<span class="nb">ln</span> <span class="nt">-sf</span> /lib/systemd/system/apacheexporter.service <span class="nb">.</span>
<span class="nb">ln</span> <span class="nt">-sf</span> /lib/systemd/system/nodeexporter.service .</code></pre></figure>

<p>Start the services</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">service apacheexporter start
service apacheexporter status
service nodeexporter start
service nodeexporter status</code></pre></figure>

<p><code class="language-plaintext highlighter-rouge">netstat -anp</code> should show the exporter processes listening at ports 9117 and 9100.</p>

<p>On the Prometheus machine, add the new config items to <code class="language-plaintext highlighter-rouge">/etc/prometheus/prometheus.yml</code>:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">  - job_name: <span class="s2">"new_exporter"</span>
    scheme: https
    tls_config:
      ca_file: /etc/prometheus/[public_IP]_exporter.crt
    static_configs:
    - targets: <span class="o">[</span><span class="s2">"[public_IP]:9100"</span><span class="o">]</span>

  - job_name: <span class="s2">"nextcloud-apache"</span>
    scheme: https
    tls_config:
      ca_file: /etc/prometheus/[public_IP]_exporter.crt
    static_configs:
    - targets: <span class="o">[</span><span class="s2">"[public_IP]:9117"</span><span class="o">]</span></code></pre></figure>

<p>Then, restart the Prometheus service with <code class="language-plaintext highlighter-rouge">service prometheus restart</code>.</p>

<p>Last, at the exporting machine, configure the firewall to enable incoming tcp access at ports 9100 and 9117 originating from the IP of the Prometheus host only.</p>

<p>In Grafana, add the new job to the node exporter charts.</p>]]></content><author><name></name></author><category term="doc" /><summary type="html"><![CDATA[(Reminder only, especially no full explanation of how to set up a prometheus host.)]]></summary></entry><entry><title type="html">Setting up a Nextcloud server with Collabora Online support to migrate away from Google docs</title><link href="http://localhost:4000/doc/2025/02/16/nextcloud-co.html" rel="alternate" type="text/html" title="Setting up a Nextcloud server with Collabora Online support to migrate away from Google docs" /><published>2025-02-16T10:00:00+01:00</published><updated>2025-02-16T10:00:00+01:00</updated><id>http://localhost:4000/doc/2025/02/16/nextcloud-co</id><content type="html" xml:base="http://localhost:4000/doc/2025/02/16/nextcloud-co.html"><![CDATA[<p>Since some of us feel the <a href="https://en.wikipedia.org/wiki/Coup_d%27%C3%A9tat" target="_blank">urge</a> to migrate our cloud data away from Google, Microsoft or other US based services, this is one way to set up a self-hosted open source service as a private cloud.</p>

<p>We will need a Nextcloud instance as a frontend for cloud storage and basic file handling for images, PDF, notes, markdown documents etc., and also a Collabora Online instance for providing office document capabilities.</p>

<p>It goes as follows:</p>

<p>We log in to our preferred VPS/root server provider and order a machine with 4 vCPUs and 4-8 GB RAM, which comes usually with 128 to 256 GB HDD space. (I’m with netcup - not affiliated - and took a VPS with 4 vCPUs, 8 GB RAM and 256 GB HDD at 6,84 EUR/month.) We deploy a fresh Ubuntu Server 24.04, we deploy necessary updates, we reboot.</p>

<p>At our DNS registrar, we register A records for nx.example.com and co.example.com for the IP of the new machine, given that we own the domain example.com.</p>

<p>We install an appropriate firewall and/or startup scripts. It makes sense to open ports 80 and 443 for web as well as to install an ssh server for remote access.
We open port 9980 tcp only for our client IP.</p>

<p>Now we execute phase 1 of this great Nextcloud install <a href="https://mailserverguru.com/install-nextcloud-on-ubuntu-24-04-lts" target="_blank">instruction</a>.</p>

<p>Then, we create an Apache configration nx.example.com.conf with the following contents:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">&lt;VirtualHost <span class="k">*</span>:80&gt;
        ServerAdmin webmaster@localhost
	ServerName nx.example.com
	ServerAlias nx.example.com
        DocumentRoot /var/www/nextcloud
        
        &lt;Directory /var/www/nextcloud&gt;
            Options Indexes FollowSymLinks
            AllowOverride All
            Require all granted
	&lt;/Directory&gt;
        
        ErrorLog <span class="k">${</span><span class="nv">APACHE_LOG_DIR</span><span class="k">}</span>/error.log
        CustomLog <span class="k">${</span><span class="nv">APACHE_LOG_DIR</span><span class="k">}</span>/access.log combined
&lt;/VirtualHost&gt;</code></pre></figure>

<p>Please be aware that we use the path <code class="language-plaintext highlighter-rouge">/var/www/nextcloud</code> instead of <code class="language-plaintext highlighter-rouge">/var/www/html/nextcloud</code> as per the tutorial.</p>

<p>After enabling the instance by <code class="language-plaintext highlighter-rouge">a2ensite nx.example.com.conf</code> and restarting Apache we switch the server to https:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">apt-get <span class="nb">install </span>certbot python3-certbot-apache
certbot <span class="nt">-d</span> nx.example.com</code></pre></figure>

<p>After that, we have a running nextcloud instance and can login at <code class="language-plaintext highlighter-rouge">https://nx.example.com</code>. We even can view pictures and PDF documents that are supplied with the server as examples. But we cannot open the also supplied docx-Document. In order to do that, we have to setup a Collabora Online instance, which we can do at the same server.</p>

<p>For installing this instance we follow the official <a href="https://www.collaboraonline.com/code/#learnmorecode" target="_blank">instructions</a> until the end of point 4.</p>

<p>In the configuration file <code class="language-plaintext highlighter-rouge">/etc/coolwsd/coolwsd.xml</code> we change the following settings:</p>

<p>Under <code class="language-plaintext highlighter-rouge">&lt;ssl desc="SSL settings"&gt;</code> we switch <code class="language-plaintext highlighter-rouge">enable</code> to <code class="language-plaintext highlighter-rouge">false</code> and <code class="language-plaintext highlighter-rouge">termination</code>to <code class="language-plaintext highlighter-rouge">true</code>.</p>

<p>Furthermore, under <code class="language-plaintext highlighter-rouge">&lt;net desc="Network settings"&gt;</code> we set the <code class="language-plaintext highlighter-rouge">proto</code> to <code class="language-plaintext highlighter-rouge">IPv4</code> (in case we have IPv4 als the internal IP protocol at our server).</p>

<p>Now, we can call the URL <code class="language-plaintext highlighter-rouge">http://nx.example.com:9980</code> from our browser, and we should get an “OK” as answer. That means, that our Collabora Online instance is working.</p>

<p>Next, we have to make this instance available to our Nextcloud server. In order to do that, we first enable some additional Apache modules for reverse proxying:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">a2enmod proxy
a2enmod proxy_http
a2enmod proxy_connect
a2enmod proxy_wstunnel</code></pre></figure>

<p>After that, we can create a new Apache config for our Collabora Online service:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">&lt;VirtualHost <span class="k">*</span>:80&gt;
    ServerAdmin webmaster@localhost
    ServerName co.example.com
    ServerAlias co.example.com

 <span class="c">########################################</span>
 <span class="c"># Reverse proxy for Collabora Online   #</span>
 <span class="c">########################################</span>

 AllowEncodedSlashes NoDecode
 ProxyPreserveHost On

 <span class="c"># static html, js, images, etc. served from coolwsd</span>
 <span class="c"># browser is the client part of Collabora Online</span>
 ProxyPass           /browser http://127.0.0.1:9980/browser <span class="nv">retry</span><span class="o">=</span>0
 ProxyPassReverse    /browser http://127.0.0.1:9980/browser

 <span class="c"># WOPI discovery URL</span>
 ProxyPass           /hosting/discovery http://127.0.0.1:9980/hosting/discovery <span class="nv">retry</span><span class="o">=</span>0
 ProxyPassReverse    /hosting/discovery http://127.0.0.1:9980/hosting/discovery

 <span class="c"># Capabilities</span>
 ProxyPass           /hosting/capabilities http://127.0.0.1:9980/hosting/capabilities <span class="nv">retry</span><span class="o">=</span>0
 ProxyPassReverse    /hosting/capabilities http://127.0.0.1:9980/hosting/capabilities

 <span class="c"># Main websocket</span>
 ProxyPassMatch      <span class="s2">"/cool/(.*)/ws$"</span>      ws://127.0.0.1:9980/cool/<span class="nv">$1</span>/ws nocanon

 <span class="c"># Admin Console websocket</span>
 ProxyPass           /cool/adminws ws://127.0.0.1:9980/cool/adminws

 <span class="c"># Download as, Fullscreen presentation and Image upload operations</span>
 ProxyPass           /cool http://127.0.0.1:9980/cool
 ProxyPassReverse    /cool http://127.0.0.1:9980/cool

 <span class="c"># Compatibility with integrations that use the /lool/convert-to endpoint</span>
 ProxyPass           /lool http://127.0.0.1:9980/cool
 ProxyPassReverse    /lool http://127.0.0.1:9980/cool
&lt;/VirtualHost&gt;</code></pre></figure>

<p>We activate this service by <code class="language-plaintext highlighter-rouge">a2ensite co.example.com</code>, restart Apache and make the service available as https with <code class="language-plaintext highlighter-rouge">certbot -d co.example.com</code>. Although we cannot access the service directly, this step is important for integration with the Nextcloud service, which is the last step.</p>

<p>We log into our Nextcloud server at <code class="language-plaintext highlighter-rouge">https://nx.example.com</code>, click at our avatar in the upper right corner and switch to <code class="language-plaintext highlighter-rouge">+ Apps</code>. At the left pane, we then find under “Featured Apps” the “Nextcloud Office”, which we download and enable. Then, we head over to Avatar -&gt; Administration Settings, click on the magnifying glass in the top pane and serach for “office”. We select “Office Administration”, select “Use your own server” and put in the URL <code class="language-plaintext highlighter-rouge">https://co.example com</code>.<br />
<strong>This will NOT work using the same URL as for the nextcloud server, e.g. <code class="language-plaintext highlighter-rouge">nx.example.com</code>, even if both services are located at the same machine!</strong></p>

<p>Last, we edit the Allow list for WOPI requests with the values <code class="language-plaintext highlighter-rouge">127.0.0.1, [our server's IP address]</code> and set in <code class="language-plaintext highlighter-rouge">/etc/php/8.3/php.ini</code> the values</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">memory_limit <span class="o">=</span> 512M
upload_max_filesize <span class="o">=</span> 64M</code></pre></figure>

<p>With these settings, we should at least get no red errors at the Nextcloud admin page. Now, we should be able to open the supplied docx document as well. Further tweaking according to the linked documentation.</p>

<p>That’s it, we have an integrated Cloud/Docs server where we can migrate all our files and documents to!</p>

<p>(We can and should now close port 9980 tcp for any external access.)</p>]]></content><author><name></name></author><category term="doc" /><summary type="html"><![CDATA[Since some of us feel the urge to migrate our cloud data away from Google, Microsoft or other US based services, this is one way to set up a self-hosted open source service as a private cloud.]]></summary></entry><entry><title type="html">Running multiple Jekyll instances on one server</title><link href="http://localhost:4000/doc/2023/11/19/multiple-jekyll-servers.html" rel="alternate" type="text/html" title="Running multiple Jekyll instances on one server" /><published>2023-11-19T10:00:00+01:00</published><updated>2023-11-19T10:00:00+01:00</updated><id>http://localhost:4000/doc/2023/11/19/multiple-jekyll-servers</id><content type="html" xml:base="http://localhost:4000/doc/2023/11/19/multiple-jekyll-servers.html"><![CDATA[<p>Since we have already <a href="https://blog.axaneco.net/doc/2023/03/19/jekyll-server.html" target="_blank">learned</a> how to set up a single Jekyll sever, the question may come up how to run two or more Jekyll instances in parallel on the same server. This is what we’ll explain in the following.</p>

<p>First, we create a new blog in our existing home directory:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo</span> <span class="nt">-i</span>
su - jekyll
<span class="nb">export </span><span class="nv">GEM_HOME</span><span class="o">=</span><span class="s2">"/home/jekyll/gems"</span>
<span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="s2">"home/jekyll/gems/bin:</span><span class="nv">$PATH</span><span class="s2">"</span>
jekyll new blog1</code></pre></figure>

<p>Now, we have to create the second jekyll service unit file as <code class="language-plaintext highlighter-rouge">/lib/systemd/system/jekyll1.service</code>:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="o">[</span>Unit]
<span class="nv">Description</span><span class="o">=</span>Jekyll service
<span class="nv">After</span><span class="o">=</span>syslog.target
<span class="nv">After</span><span class="o">=</span>network.target

<span class="o">[</span>Service]
<span class="c"># Added solution -- add WorkingDirectory to directory where you clone your markdown files for Jekyll to render</span>
<span class="nv">WorkingDirectory</span><span class="o">=</span>/home/jekyll/blog1
<span class="c"># Name of the user account that is running the Jekyll server</span>
<span class="nv">User</span><span class="o">=</span>jekyll
<span class="nv">Type</span><span class="o">=</span>simple
<span class="c"># Location (source) of the markdown files to be rendered</span>
<span class="nv">ExecStart</span><span class="o">=</span>/usr/bin/bash /home/jekyll/start1.sh
<span class="nv">Restart</span><span class="o">=</span>always
<span class="nv">StandardOutput</span><span class="o">=</span>journal
<span class="nv">StandardError</span><span class="o">=</span>journal
<span class="nv">SyslogIdentifier</span><span class="o">=</span>jekyll1

<span class="o">[</span>Install]
<span class="nv">WantedBy</span><span class="o">=</span>multi-user.target</code></pre></figure>

<p>where the <code class="language-plaintext highlighter-rouge">start1.sh</code> script reads as</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">export </span><span class="nv">GEM_HOME</span><span class="o">=</span><span class="s1">'/home/jekyll/gems'</span> 
<span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="s1">'/home/jekyll/gems/bin:$PATH'</span>

<span class="nb">cd</span> /home/jekyll/blog1 
<span class="nb">export </span><span class="nv">BUNDLE_GEMFILE</span><span class="o">=</span><span class="s1">'/home/jekyll/blog1/Gemfile'</span> 
bundle <span class="nb">exec </span>jekyll serve <span class="nt">--host</span> 127.0.0.1 <span class="nt">--port</span> 4001 </code></pre></figure>

<p>For automated start on boot, we again have to link this service under <code class="language-plaintext highlighter-rouge">/etc/systemd/system/multi-user.target.wants</code>:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">cd</span> /etc/systemd/system/multi-user.target.wants
<span class="nb">sudo ln</span> <span class="nt">-s</span> /lib/systemd/system/jekyll1.service <span class="nb">.</span>
<span class="nb">sudo </span>systemctl daemon-reload</code></pre></figure>

<p>For now, we can start the service manually by <code class="language-plaintext highlighter-rouge">sudo service jekyll1 start</code>.</p>

<p>We leave the reverse proxy configuration for the jekyll instance on port 4001 as an exercise to the reader.</p>

<p>That’s it! Now we have a second Jekyll blog running SSL encrypted behind an Apache reverse proxy.</p>]]></content><author><name></name></author><category term="doc" /><summary type="html"><![CDATA[Since we have already learned how to set up a single Jekyll sever, the question may come up how to run two or more Jekyll instances in parallel on the same server. This is what we’ll explain in the following.]]></summary></entry><entry><title type="html">Setup of a niltalk chatserver behind an Apache reverse https proxy</title><link href="http://localhost:4000/doc/2023/09/13/niltalk-reverse-proxy.html" rel="alternate" type="text/html" title="Setup of a niltalk chatserver behind an Apache reverse https proxy" /><published>2023-09-13T08:00:00+02:00</published><updated>2023-09-13T08:00:00+02:00</updated><id>http://localhost:4000/doc/2023/09/13/niltalk-reverse-proxy</id><content type="html" xml:base="http://localhost:4000/doc/2023/09/13/niltalk-reverse-proxy.html"><![CDATA[<p>Since our <a href="https://blog.axaneco.net/doc/2023/03/20/reverse-proxy.html">generic</a> proxy approach does not work out of the box for the <a href="https://github.com/knadh/niltalk">niltalk</a> chat server, we will see in this post how to put everything together for this case.</p>

<p>We assume thet we will run the talk server at the address <code class="language-plaintext highlighter-rouge">https://talk.example.com</code>.</p>

<h2 id="installing-niltalk">Installing niltalk</h2>

<p>Let’s login to out target machine and be sure, that we have <strong>no open port 9000</strong> to the outer world.</p>

<p>If we do not want to build the server by ourselves, we get the binary:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">user@talk.example.com:/home/user<span class="nv">$ </span>wget https://github.com/knadh/niltalk/releases/download/v0.1.1/niltalk_0.1.1_linux_amd64.tar.gz</code></pre></figure>

<p>After unpacking, we create a target directory</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">user@talk.example.com:/home/user<span class="nv">$ </span><span class="nb">sudo mkdir</span> /opt/niltalk</code></pre></figure>

<p>and copy the binary there</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">user@talk.example.com:/home/user<span class="nv">$ </span><span class="nb">sudo cp </span>niltalk /opt/niltalk</code></pre></figure>

<p>For the static web templates, we need to clone the niltalk git-respository</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">user@talk.example.com:/home/user<span class="nv">$ </span>git clone https://github.com/knadh/niltalk.git</code></pre></figure>

<p>and to copy the <code class="language-plaintext highlighter-rouge">static</code> directory also to the target folder:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">user@talk.example.com:/home/user<span class="nv">$ </span><span class="nb">sudo cp </span>niltalk/static /opt/niltalk</code></pre></figure>

<p>To create a configuration, we have to run the server with the command</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">user@talk.example.com:/opt/niltalk<span class="nv">$ </span><span class="nb">sudo </span>niltalk <span class="nt">--new-config</span></code></pre></figure>

<p>This creates a file <code class="language-plaintext highlighter-rouge">config.toml</code> that we can amend later on as needed. For now, it makes sense to set the storage to <code class="language-plaintext highlighter-rouge">memory</code>:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c"># Storage kind, one of redis|memory|fs.</span>
storage <span class="o">=</span> <span class="s2">"memory"</span>

<span class="c"># Redis cache server.</span>
<span class="c"># Rooms are cached until they expires. Messages are not cached.</span>
<span class="c">#[store]</span>
<span class="c">#address = "redis:6379" # Eg: 127.0.0.1:6379</span>
<span class="c">#password = ""</span>
<span class="c">#db = 0</span>
<span class="c">#active_conns = 100</span>
<span class="c">#idle_conns = 20</span>
<span class="c">#timeout = "3s"</span>

<span class="c">#prefix_room = "NIL:ROOM:%s"</span>
<span class="c">#prefix_session = "NIL:SESS:ROOM:%s"</span>

<span class="c"># InMemory store config.</span>
<span class="o">[</span>store]
<span class="c"># no options available.</span>

<span class="c"># FileSystem store config.</span>
<span class="c"># [store]</span>
<span class="c"># path = "db.json"</span></code></pre></figure>

<p>At this point we have the chat server installed. Running it by typing <code class="language-plaintext highlighter-rouge">sudo /opt/nilserver/nilserver</code> should result in an open port 9000.</p>

<h2 id="creating-the-service">Creating the service</h2>

<p>Since we want to start the talk server by default, we create a service file <code class="language-plaintext highlighter-rouge">/lib/systemd/system/niltalk.service</code> with the following contents:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="o">[</span>Unit]
<span class="nv">Description</span><span class="o">=</span>niltalk server
<span class="nv">After</span><span class="o">=</span>multi-user.target

<span class="o">[</span>Service]
<span class="nv">WorkingDirectory</span><span class="o">=</span>/opt/niltalk
<span class="nv">ExecStart</span><span class="o">=</span>/opt/niltalk/niltalk <span class="nt">--static-dir</span><span class="o">=</span>/opt/niltalk/static
<span class="nv">ExecReload</span><span class="o">=</span>/bin/kill <span class="nt">-HUP</span> <span class="nv">$MAINPID</span>

<span class="o">[</span>Install]
<span class="nv">WantedBy</span><span class="o">=</span>multi-user.target</code></pre></figure>

<p>We enable the service by linking it to the right place:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">user@talk.example.com:/etc/systemd/system/multi-user.target.wants<span class="nv">$ </span><span class="nb">sudo ln</span> <span class="nt">-s</span> /lib/systemd/system/niltalk.service .</code></pre></figure>

<p>Now we should be able to start and stop the service using the <code class="language-plaintext highlighter-rouge">sudo service niltalk start</code> and <code class="language-plaintext highlighter-rouge">sudo service niltalk stop</code> commands, respectively.</p>

<h2 id="apache-reverse-proxy">Apache reverse proxy</h2>

<p>The standard approach does not work here. The talk server is using web sockets, which are not proxied/rewritten by default. So we have to amend our configuration to do that.</p>

<p>But first, we can go with the “standard” approach to get the certificate. The Apache <code class="language-plaintext highlighter-rouge">talk.example.com.conf</code> configuration reads as</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">&lt;VirtualHost <span class="k">*</span>:80&gt;
        ServerAdmin webmaster@localhost
        ServerName talk.example.com
        ServerAlias talk.example.com
        ProxyPreserveHost On
        ProxyPass / http://127.0.0.1:9000/
        ProxyPassReverse / http://127.0.0.1:9000/
&lt;/VirtualHost&gt;</code></pre></figure>

<p>So we get our certificate via <code class="language-plaintext highlighter-rouge">sudo certbot --apache -d talk.example.com</code>.</p>

<p>But now we have to add some configuration on the newly created <code class="language-plaintext highlighter-rouge">talk.example.com-le-ssl.conf</code>:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">&lt;IfModule mod_ssl.c&gt;
&lt;VirtualHost <span class="k">*</span>:443&gt;
        ServerAdmin webmaster@localhost
        ServerName talk.example.com
        ServerAlias talk.example.com

        ProxyPreserveHost On
        ProxyRequests Off
        RewriteEngine On

        RewriteCond %<span class="o">{</span>HTTP:UPGRADE<span class="o">}</span> ^WebSocket<span class="nv">$ </span>          <span class="o">[</span>NC,OR]
        RewriteCond %<span class="o">{</span>HTTP:CONNECTION<span class="o">}</span> ^Upgrade<span class="nv">$ </span>         <span class="o">[</span>NC]
        RewriteRule .<span class="k">*</span> ws://127.0.0.1:9000%<span class="o">{</span>REQUEST_URI<span class="o">}</span>  <span class="o">[</span>P,QSA,L]
        RewriteCond %<span class="o">{</span>DOCUMENT_ROOT<span class="o">}</span>/%<span class="o">{</span>REQUEST_FILENAME<span class="o">}</span> <span class="o">!</span><span class="nt">-f</span>
        RewriteRule .<span class="k">*</span> http://127.0.0.1:9000%<span class="o">{</span>REQUEST_URI<span class="o">}</span> <span class="o">[</span>P,QSA,L]
        RequestHeader <span class="nb">set </span>X-Forwarded-Proto <span class="s2">"https"</span>

        &lt;Location /&gt;
                Require all granted
                ProxyPassReverse http://127.0.0.1:9000/
                ProxyPassReverseCookieDomain 127.0.0.1 talk.example.com
        &lt;/Location&gt;

SSLCertificateFile /etc/letsencrypt/live/talk.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/talk.example.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
&lt;/VirtualHost&gt;
&lt;/IfModule&gt;</code></pre></figure>

<p>So basically, we are enabling the websocket use for our proxy.</p>

<p>In order to avoid client reconnection every 5 minutes, we use a (maybe dirty, I’m open for better solutions) workaround and set the connection timeout in <code class="language-plaintext highlighter-rouge">/etc/apache2/apache2.conf</code> to 12 hours:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">Timeout 43200</code></pre></figure>

<p>In order to get this all working, we have to enable the Apache <code class="language-plaintext highlighter-rouge">headers</code> module and to restart the webserver:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nv">$ </span><span class="nb">sudo </span>a2enmod headers
<span class="nv">$ </span><span class="nb">sudo </span>service apache2 restart</code></pre></figure>

<p>Finally, we have to set the right IP and URL in our niltalk config file <code class="language-plaintext highlighter-rouge">/opt/niltalk/config.toml</code>:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="o">[</span>app]
<span class="c"># Address to listen, use "tor" to run an hidden service.</span>
<span class="c"># address = "0.0.0.0:9000"</span>
address <span class="o">=</span> <span class="s2">"127.0.0.1:9000"</span>

<span class="c"># No trailing slashes.</span>
root_url <span class="o">=</span> <span class="s2">"https://talk.example.com"</span></code></pre></figure>

<p>After restarting the chat server by</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nv">$ </span><span class="nb">sudo </span>service niltalk stop
<span class="nv">$ </span><span class="nb">sudo </span>service niltalk start</code></pre></figure>

<p>we are done.</p>

<p>That’s it! Now we have a niltalk chatserver behind an Apache reverse https proxy.</p>

<p>Main source for the Apache config part: <a href="https://mattermost.atlassian.net/browse/MM-518">mattermost</a>.</p>]]></content><author><name></name></author><category term="doc" /><summary type="html"><![CDATA[Since our generic proxy approach does not work out of the box for the niltalk chat server, we will see in this post how to put everything together for this case.]]></summary></entry><entry><title type="html">Setup of a CalDAV server (Radicale) and integration with Mozilla Thunderbird</title><link href="http://localhost:4000/doc/2023/08/13/caldav-server-and-thunderbird-integration.html" rel="alternate" type="text/html" title="Setup of a CalDAV server (Radicale) and integration with Mozilla Thunderbird" /><published>2023-08-13T11:00:00+02:00</published><updated>2023-08-13T11:00:00+02:00</updated><id>http://localhost:4000/doc/2023/08/13/caldav-server-and-thunderbird-integration</id><content type="html" xml:base="http://localhost:4000/doc/2023/08/13/caldav-server-and-thunderbird-integration.html"><![CDATA[<p>Goal of this article is to document the setup of the CalDAV server “Radicale” for synchronizing calendars between different Thunderbird clients on Ubuntu 22.04.x.</p>

<h2 id="install-the-caldav-server">Install the CalDAV server</h2>

<p>So, let’s assume we have an Ubuntu 22.04.x installation with running Apache on a remote machine with DNS name <code class="language-plaintext highlighter-rouge">caldav.example.org</code> running a ssh server.
First, we become root:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo</span> <span class="nt">-i</span></code></pre></figure>

<p>Next, we install everything that is needed to run the Radicale 2 server:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">apt-get <span class="nb">install </span>radicale python3-bcrypt python3-passlib apache2-utils </code></pre></figure>

<p>In the configuration file <code class="language-plaintext highlighter-rouge">/etc/radicale/config</code>, the following entries have to be modified:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="o">[</span>server]
hosts <span class="o">=</span> localhost:5232
ssl <span class="o">=</span> False

<span class="o">[</span>auth]
<span class="nb">type</span> <span class="o">=</span> htpasswd
htpasswd_filename <span class="o">=</span> /etc/radicale/users
htpasswd_encryption <span class="o">=</span> bcrypt</code></pre></figure>

<p>Now we add a user whose calendar should be synchronized:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>htpasswd <span class="nt">-c</span> <span class="nt">-B</span> /etc/radicale/users user01</code></pre></figure>

<p>To avoid acess rights problems, we have to change ownership of the Radicale files to the respective user:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">chown</span> <span class="nt">-R</span> radicale:radicale /var/lib/radicale</code></pre></figure>

<p>We can now start the server by executing the command</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">systemctl start radicale.service</code></pre></figure>

<p>Now we have a local running Radicale CalDAV server at port 5232:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c"># netstat -anp | grep 5232</span>
tcp        0      0 127.0.0.1:5232          0.0.0.0:<span class="k">*</span>               LISTEN      301430/python3</code></pre></figure>

<p>Eveen tough Radicale comes with an option to use SSL certificates, we set up a reverse proxy instead to include the <code class="language-plaintext highlighter-rouge">certbot</code> functionality, following the article  <a href="https://blog.axaneco.net/doc/2023/03/20/reverse-proxy.html">Generic setup of a https nginx/Apache reverse proxy</a>. Obviously, the settings here are</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">        ServerName caldav.example.org
        ServerAlias caldav.example.org</code></pre></figure>

<p>and</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">        ProxyPass / http://127.0.0.1:5232/
        ProxyPassReverse / http://127.0.0.1:5232/</code></pre></figure>

<p>After retrieving the SSL certificates via <code class="language-plaintext highlighter-rouge">certbot</code>, we should see a login page with user and password input fields when calling <code class="language-plaintext highlighter-rouge">https://caldav.example.org</code> in a browser, and we should be able to login with <code class="language-plaintext highlighter-rouge">user01</code> and the corresponding password that we have created before.</p>

<p>We have two links available, one for creating a new addressbook or calendar and one for uploading an addressbook or calendar. For now, we can leave everything as it is, as we go for the Thunderbird integration, after finally enabling the server for automatic start:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">systemctl <span class="nb">enable </span>radicale.service </code></pre></figure>

<h2 id="use-caldav-with-thunderbird">Use CalDAV with Thunderbird</h2>

<p>UPDATE 2025-11-01: The solution written below is outdated, there’s now a canonical way to use CalDAV with Thunderbird without addons, documented <a href="https://support.mozilla.org/en-US/kb/creating-new-calendars">here</a>.<br />
Thanks to Nick for pointing that out to me.</p>

<p><del>That’s quite easy: Via <code class="language-plaintext highlighter-rouge">Tools -&gt; Addons and Themes</code> we install the addons <code class="language-plaintext highlighter-rouge">TbSync</code> and <code class="language-plaintext highlighter-rouge">Provider for CalDAV &amp; CardDAV</code>.</del></p>

<p><del>Then, via <code class="language-plaintext highlighter-rouge">Edit -&gt; Synchronization Settings (TbSync)</code> we go to <code class="language-plaintext highlighter-rouge">Account actions -&gt; Add new account -&gt; CalDAV &amp; CardDAV</code>. There we choose <code class="language-plaintext highlighter-rouge">Manual configuration</code> (since the automatic one does not work with our Radicale server, not sure if this is a server or a Thunderbird bug).</del></p>

<p><del>Now we return to our Radicale web frontend where we can either create a new calendar from scratch or by uploading an existing ICS file. In both cases, a new resource entry will be created, that provides an URL of the form <code class="language-plaintext highlighter-rouge">https://caldav.example.org/user01/[some UUID]</code>. We copy this URL to the clipboard and paste it into the Thunderbird CalDAV account information mask. Finally, we create an account name and register the user credentials (<code class="language-plaintext highlighter-rouge">user01</code> plus password).</del></p>

<p><del>On the <code class="language-plaintext highlighter-rouge">General</code> Tab of the TbSync account settings, we can now enable the synchronization of the just created account and trigger a first sync via <code class="language-plaintext highlighter-rouge">Synchronize now</code>. After setting the <code class="language-plaintext highlighter-rouge">Periodic synchronization</code> to 10 minutes (or whatever you like), the Thunderbird configuration for CalDAV is done. These steps, of course, have to be repeated for every Thunderbird client that should use the calendar synchronization for user01.</del></p>

<p>That’s it! Now we have set up the CalDAV server “Radicale” for synchronizing calendars between different Thunderbird clients on Ubuntu 22.04.x.</p>]]></content><author><name></name></author><category term="doc" /><summary type="html"><![CDATA[Goal of this article is to document the setup of the CalDAV server “Radicale” for synchronizing calendars between different Thunderbird clients on Ubuntu 22.04.x.]]></summary></entry><entry><title type="html">Setup of a Guacamole HTML5 remote XFCE desktop via VNC for Ubuntu 22.04</title><link href="http://localhost:4000/doc/2023/04/03/remote-vm-with-xfce.html" rel="alternate" type="text/html" title="Setup of a Guacamole HTML5 remote XFCE desktop via VNC for Ubuntu 22.04" /><published>2023-04-03T11:00:00+02:00</published><updated>2023-04-03T11:00:00+02:00</updated><id>http://localhost:4000/doc/2023/04/03/remote-vm-with-xfce</id><content type="html" xml:base="http://localhost:4000/doc/2023/04/03/remote-vm-with-xfce.html"><![CDATA[<p><strong>THIS POST IS OUTDATED</strong>
See updated <a href="/doc/2025/07/21/remote-vm-with-xfce.html">post</a>.</p>

<p>Goal of this article is to document the setup of a Guacamole HTML5 remote XFCE desktop via VNC on Ubuntu 22.04.x.
The Guacamole setup is based on 
<a href="https://znil.net/index.php?title=Ubuntu_20.04.x_LTS_-_Guacamole_HTML5_Remotedesktop_Gateway_installieren_mit_Apache_Reverse_Proxy">Ubuntu 20.04.x LTS - Guacamole HTML5 Remotedesktop Gateway installieren mit Apache Reverse Proxy</a> (in german, written by Bernhard Linz)</p>

<h2 id="install-the-guacamole-server">Install the guacamole server</h2>

<p>So, let’s assume we have a fresh Ubuntu 22.04.x installation on a remote machine with DNS name <code class="language-plaintext highlighter-rouge">test.example.org</code> running a ssh server.
First, we become root:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo</span> <span class="nt">-i</span></code></pre></figure>

<p>Next, we install everything that is needed to run the Apache Tomcat server:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">apt <span class="nb">install </span>make libssh2-1-dev libtelnet-dev libpango1.0-dev libossp-uuid-dev libcairo2-dev libpng-dev libssh2-1 libvncserver-dev libvorbis-dev  gcc libssh-dev libpulse-dev tomcat9 tomcat9-admin tomcat9-docs ghostscript libwebp-dev libavcodec-dev libavutil-dev libswscale-dev libjpeg-turbo8-dev libtool-bin libossp-uuid-dev libavformat-dev freerdp2-dev libwebsockets-dev libssl-dev</code></pre></figure>

<p>Now, on calling <code class="language-plaintext highlighter-rouge">http://test.example.org:8080</code> we should see the standard Apache Tomcat “It works!” page.</p>

<p>As of April 2023, the current Guacamole version is 1.5.1, so we download the following files:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">cd</span> /usr/src
wget https://downloads.apache.org/guacamole/1.5.1/source/guacamole-server-1.5.1.tar.gz
wget https://downloads.apache.org/guacamole/1.5.1/binary/guacamole-1.5.1.war</code></pre></figure>

<p>So we have server source code and client binary.</p>

<p>Now we unpack the server code</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">tar </span>xvzf guacamole-server-1.5.1.tar.gz</code></pre></figure>

<p>and compile it:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">cd</span> /usr/src/guacamole-server-1.5.1
./configure <span class="nt">--with-systemd-dir</span><span class="o">=</span>/etc/systemd/system</code></pre></figure>

<p>That should result in an output like this:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nt">------------------------------------------------</span>
guacamole-server version 1.5.1
<span class="nt">------------------------------------------------</span>

   Library status:

     freerdp2 ............ <span class="nb">yes
     </span>pango ............... <span class="nb">yes
     </span>libavcodec .......... <span class="nb">yes
     </span>libavformat.......... <span class="nb">yes
     </span>libavutil ........... <span class="nb">yes
     </span>libssh2 ............. <span class="nb">yes
     </span>libssl .............. <span class="nb">yes
     </span>libswscale .......... <span class="nb">yes
     </span>libtelnet ........... <span class="nb">yes
     </span>libVNCServer ........ <span class="nb">yes
     </span>libvorbis ........... <span class="nb">yes
     </span>libpulse ............ <span class="nb">yes
     </span>libwebsockets ....... <span class="nb">yes
     </span>libwebp ............. <span class="nb">yes
     </span>wsock32 ............. no

   Protocol support:

      Kubernetes .... <span class="nb">yes
      </span>RDP ........... <span class="nb">yes
      </span>SSH ........... <span class="nb">yes
      </span>Telnet ........ <span class="nb">yes
      </span>VNC ........... <span class="nb">yes

   </span>Services / tools:

      guacd ...... <span class="nb">yes
      </span>guacenc .... <span class="nb">yes
      </span>guaclog .... <span class="nb">yes

   </span>FreeRDP plugins: /usr/lib/x86_64-linux-gnu/freerdp2
   Init scripts: no
   Systemd units: /etc/systemd/system

Type <span class="s2">"make"</span> to compile guacamole-server.</code></pre></figure>

<p>If that’s the case as all “yes”-entries are present, we can compile:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">make
make <span class="nb">install</span></code></pre></figure>

<p>Last we have to configure the dynamic linker run-time bindings:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">ldconfig</code></pre></figure>

<p>Now we can start the <code class="language-plaintext highlighter-rouge">systemd</code> service that the installation has provided:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">systemctl start guacd.service
systemctl status guacd.service</code></pre></figure>

<p>which should give us an output like</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">● guacd.service - Guacamole Server
     Loaded: loaded <span class="o">(</span>/etc/systemd/system/guacd.service<span class="p">;</span> disabled<span class="p">;</span> vendor preset: enabled<span class="o">)</span>
     Active: active <span class="o">(</span>running<span class="o">)</span> since Fri 2023-04-21 14:52:28 UTC<span class="p">;</span> 6s ago
       Docs: man:guacd<span class="o">(</span>8<span class="o">)</span>
   Main PID: 47807 <span class="o">(</span>guacd<span class="o">)</span>
      Tasks: 1 <span class="o">(</span>limit: 2233<span class="o">)</span>
     Memory: 10.2M
        CPU: 10ms
     CGroup: /system.slice/guacd.service
             └─47807 /usr/local/sbin/guacd <span class="nt">-f</span>

Apr 21 14:52:28 test181 systemd[1]: Started Guacamole Server.
Apr 21 14:52:28 test181 guacd[47807]: Guacamole proxy daemon <span class="o">(</span>guacd<span class="o">)</span> version 1.5.1 started
Apr 21 14:52:28 test181 guacd[47807]: guacd[47807]: INFO:        Guacamole proxy daemon <span class="o">(</span>guacd<span class="o">)</span> version 1.5.1 started
Apr 21 14:52:28 test181 guacd[47807]: guacd[47807]: INFO:        Listening on host 127.0.0.1, port 4822
Apr 21 14:52:28 test181 guacd[47807]: Listening on host 127.0.0.1, port 4822</code></pre></figure>

<p>We stop the server in order to amend configurations:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">systemctl stop guacd.service</code></pre></figure>

<p>First we create the directory</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">mkdir</span> /etc/guacamole</code></pre></figure>

<p>There we create a file <code class="language-plaintext highlighter-rouge">/etc/guacamole/guacamole.properties</code> with the content</p>

<p><code class="language-plaintext highlighter-rouge">basic-user-mapping: /etc/guacamole/user-mapping.xml</code></p>

<p>For our purposes, this file reads like</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">&lt;user-mapping&gt;
        &lt;<span class="o">!</span><span class="nt">--</span> Per-user authentication and config information <span class="nt">--</span><span class="o">&gt;</span>
        &lt;<span class="o">!</span><span class="nt">--</span> FIRST USER <span class="nt">--</span><span class="o">&gt;</span>
        &lt;authorize 
                <span class="nv">username</span><span class="o">=</span><span class="s2">"test01"</span>
                <span class="nv">password</span><span class="o">=</span><span class="s2">"52c88bca7c5b3ab3e7ed8901ea1bc83f"</span>
                <span class="nv">encoding</span><span class="o">=</span><span class="s2">"md5"</span><span class="o">&gt;</span>
                &lt;<span class="o">!</span><span class="nt">--</span> First authorized Remote connection <span class="nt">--</span><span class="o">&gt;</span>
                &lt;connection <span class="nv">name</span><span class="o">=</span><span class="s2">"SSH Admin"</span><span class="o">&gt;</span>
                        &lt;protocol&gt;ssh&lt;/protocol&gt;
                        &lt;param <span class="nv">name</span><span class="o">=</span><span class="s2">"hostname"</span><span class="o">&gt;</span>10.10.10.10&lt;/param&gt;      
                        &lt;param <span class="nv">name</span><span class="o">=</span><span class="s2">"port"</span><span class="o">&gt;</span>22&lt;/param&gt;                    
                        &lt;param <span class="nv">name</span><span class="o">=</span><span class="s2">"username"</span><span class="o">&gt;</span>test01&lt;/param&gt;     
                        &lt;param <span class="nv">name</span><span class="o">=</span><span class="s2">"enable-sftp"</span><span class="o">&gt;</span><span class="nb">true</span>&lt;/param&gt;
                &lt;/connection&gt; 
                &lt;<span class="o">!</span><span class="nt">--</span> second authorized Remote connection <span class="nt">--</span><span class="o">&gt;</span>
                &lt;connection <span class="nv">name</span><span class="o">=</span><span class="s2">"Terminal Server VNC"</span><span class="o">&gt;</span>
                        &lt;protocol&gt;vnc&lt;/protocol&gt;
                        &lt;param <span class="nv">name</span><span class="o">=</span><span class="s2">"hostname"</span><span class="o">&gt;</span>127.0.0.1&lt;/param&gt; 
                        &lt;param <span class="nv">name</span><span class="o">=</span><span class="s2">"port"</span><span class="o">&gt;</span>5901&lt;/param&gt;         
                        &lt;param <span class="nv">name</span><span class="o">=</span><span class="s2">"server-layout"</span><span class="o">&gt;</span>de-de-qwertz&lt;/param&gt;
                &lt;/connection&gt;
        &lt;/authorize&gt;
&lt;/user-mapping&gt;</code></pre></figure>

<p>Of course we have to amend hostname, username, and password for our system.</p>

<p>The password hash can be created by</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">echo</span> <span class="nt">-n</span> <span class="s1">'secretPassword'</span> | <span class="nb">md5sum</span></code></pre></figure>

<p>Finally, we have to append the Guacamole home to the environment:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">echo </span><span class="nv">GUACAMOLE_HOME</span><span class="o">=</span><span class="s2">"/etc/guacamole"</span> <span class="o">&gt;&gt;</span> /etc/environment</code></pre></figure>

<p>It makes sense to reboot at this point.</p>

<p>Now we enable the Guacamole service for automated start:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">systemctl <span class="nb">enable </span>guacd.service</code></pre></figure>

<p>Next, we install the client:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">cp</span> /usr/src/guacamole-1.5.1.war /var/lib/tomcat9/webapps/guacamole.war</code></pre></figure>

<p>Then we have to link the property directory to the place where it is expected:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">ln</span> <span class="nt">-s</span> /etc/guacamole /usr/share/tomcat9/.guacamole</code></pre></figure>

<p>To speed up the tomcat restart, we have to create the following file</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">touch</span> /usr/share/tomcat9/bin/setenv.sh
<span class="nb">chmod</span> +x /usr/share/tomcat9/bin/setenv.sh</code></pre></figure>

<p>with this content:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c">#!/bin/sh</span>
<span class="nb">export </span><span class="nv">CATALINA_OPTS</span><span class="o">=</span><span class="s2">"</span><span class="nv">$CATALINA_OPTS</span><span class="s2"> -Djava.security.egd=file:/dev/./urandom"</span></code></pre></figure>

<p>Now we can start the guacamole server:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">systemctl start guacd.service</code></pre></figure>

<p>If we now browse to</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">http://test.example.org:8080/guacamole/</code></pre></figure>

<p>we should be offered a login and then the connection options “SSH Admin” and “Terminal Server VNC”.</p>

<p>The SSH connection should work already, for the VNC we need to prepare some more things, as follows.</p>

<h2 id="install-the-vnc-server">Install the VNC server</h2>

<p>Let’s assume that we have a user <code class="language-plaintext highlighter-rouge">test01</code> as our (sudo-enabled) standard user for the machine we just prepared, and that we are now logged in as that user.</p>

<p>We install the needed stuff:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>apt <span class="nb">install</span> <span class="nt">-y</span> xfce4 xfce4-goodies tightvncserver autocutsel xfonts-base xfonts-100dpi xfonts-75dpi</code></pre></figure>

<p>Then we create a vnc password for our user:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">vncpasswd</code></pre></figure>

<p>Next, we have to create the vnc startup file in our home directory and make it executable:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">touch</span> ~/.vnc/xstartup
<span class="nb">chmod </span>755 ~/.vnc/xstartup</code></pre></figure>

<p>The contents of this file is</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c">#!/bin/sh</span>
<span class="nb">unset </span>SESSION_MANAGER
<span class="nb">unset </span>DBUS_SESSION_BUS_ADDRESS
<span class="nv">DBUS_SESSION_BUS_ADDRESS</span><span class="o">=</span>unix:path<span class="o">=</span><span class="nv">$XDG_RUNTIME_DIR</span>/bus
<span class="nb">exec </span>startxfce4 </code></pre></figure>

<p>For starting the VNC server on boot, we normally would need a service file <code class="language-plaintext highlighter-rouge">/etc/systemd/system/vncserver@.service</code>, as suggested by <a href="https://serverok.in/install-xfce-vnc-remote-desktop-on-ubuntu">Install Xfce VNC remote desktop on Ubuntu</a>. Unfortunately, for Ubuntu 22.04.x this method does not longer work (it will give some mysterious “/usr/bin/startxfce4: X server already running on display :1” log entry), and we have to find a workaround. This goes as follows:</p>

<p>We want to login automatically on boot, so we create a service unit <code class="language-plaintext highlighter-rouge">/lib/systemd/system/getty@.service</code> as follows:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c">#  SPDX-License-Identifier: LGPL-2.1-or-later</span>
<span class="c">#</span>
<span class="c">#  This file is part of systemd.</span>
<span class="c">#</span>
<span class="c">#  systemd is free software; you can redistribute it and/or modify it</span>
<span class="c">#  under the terms of the GNU Lesser General Public License as published by</span>
<span class="c">#  the Free Software Foundation; either version 2.1 of the License, or</span>
<span class="c">#  (at your option) any later version.</span>

<span class="o">[</span>Unit]
<span class="nv">Description</span><span class="o">=</span>Getty on %I
<span class="nv">Documentation</span><span class="o">=</span>man:agetty<span class="o">(</span>8<span class="o">)</span> man:systemd-getty-generator<span class="o">(</span>8<span class="o">)</span>
<span class="nv">Documentation</span><span class="o">=</span>http://0pointer.de/blog/projects/serial-console.html
<span class="nv">After</span><span class="o">=</span>systemd-user-sessions.service plymouth-quit-wait.service getty-pre.target
<span class="nv">After</span><span class="o">=</span>rc-local.service

<span class="c"># If additional gettys are spawned during boot then we should make</span>
<span class="c"># sure that this is synchronized before getty.target, even though</span>
<span class="c"># getty.target didn't actually pull it in.</span>
<span class="nv">Before</span><span class="o">=</span>getty.target
<span class="nv">IgnoreOnIsolate</span><span class="o">=</span><span class="nb">yes</span>

<span class="c"># IgnoreOnIsolate causes issues with sulogin, if someone isolates</span>
<span class="c"># rescue.target or starts rescue.service from multi-user.target or</span>
<span class="c"># graphical.target.</span>
<span class="nv">Conflicts</span><span class="o">=</span>rescue.service
<span class="nv">Before</span><span class="o">=</span>rescue.service

<span class="c"># On systems without virtual consoles, don't start any getty. Note</span>
<span class="c"># that serial gettys are covered by serial-getty@.service, not this</span>
<span class="c"># unit.</span>
<span class="nv">ConditionPathExists</span><span class="o">=</span>/dev/tty0

<span class="o">[</span>Service]
<span class="c"># the VT is cleared by TTYVTDisallocate</span>
<span class="c"># The '-o' option value tells agetty to replace 'login' arguments with an</span>
<span class="c"># option to preserve environment (-p), followed by '--' for safety, and then</span>
<span class="c"># the entered username.</span>
<span class="nv">ExecStart</span><span class="o">=</span>-/sbin/agetty <span class="nt">--autologin</span> test01 <span class="nt">--noclear</span> %I <span class="nv">$TERM</span>
<span class="nv">Type</span><span class="o">=</span>idle
<span class="nv">Restart</span><span class="o">=</span>always
<span class="nv">RestartSec</span><span class="o">=</span>0
<span class="nv">UtmpIdentifier</span><span class="o">=</span>%I
<span class="nv">TTYPath</span><span class="o">=</span>/dev/%I
<span class="nv">TTYReset</span><span class="o">=</span><span class="nb">yes
</span><span class="nv">TTYVHangup</span><span class="o">=</span><span class="nb">yes
</span><span class="nv">TTYVTDisallocate</span><span class="o">=</span><span class="nb">yes
</span><span class="nv">IgnoreSIGPIPE</span><span class="o">=</span>no
<span class="nv">SendSIGHUP</span><span class="o">=</span><span class="nb">yes</span>

<span class="c"># Unset locale for the console getty since the console has problems</span>
<span class="c"># displaying some internationalized messages.</span>
<span class="nv">UnsetEnvironment</span><span class="o">=</span>LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION

<span class="o">[</span>Install]
<span class="nv">WantedBy</span><span class="o">=</span>getty.target
<span class="nv">DefaultInstance</span><span class="o">=</span>tty1</code></pre></figure>

<p>In the <code class="language-plaintext highlighter-rouge">Service</code> section, we replace <code class="language-plaintext highlighter-rouge">test01</code> by the username of our local user.</p>

<p>If the service should not be enabled by default, we make the service available at boot time by</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">cd</span> /etc/systems/system/getty.target.wants/
<span class="nb">sudo ln</span> <span class="nt">-sf</span> /lib/systemd/system/getty@.service .</code></pre></figure>

<p>To start the VNC server after the automatic login, we append the following line to our <code class="language-plaintext highlighter-rouge">~/.bashrc</code>:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">echo</span> <span class="s2">"/usr/bin/vncserver :1 -depth 24 -geometry 1920x1080 -localhost"</span> <span class="o">&gt;&gt;</span> ~/.bashrc</code></pre></figure>

<p>Time for a last reboot!</p>

<p>Then, we should be able to login to <code class="language-plaintext highlighter-rouge">http://test.example.com:8080/guacamole</code> with the credentials we stored in <code class="language-plaintext highlighter-rouge">/etc/guacamole/user-mapping.xml</code>, click on “Terminal Server VNC”, type in the VNC password we have defined before, and see a virtual XFCE screen in our browser.</p>

<p>That’s it! Now we have set up a Guacamole HTML5 remote XFCE desktop via VNC for Ubuntu 22.04.</p>

<p>For security reasons, it makes absolutely sense to add a 2FA as described in the already mentioned (german) <a href="https://znil.net/index.php?title=Ubuntu_20.04.x_LTS_-_Guacamole_HTML5_Remotedesktop_Gateway_installieren_mit_Apache_Reverse_Proxy">article</a> by Bernhard Linz. For security reasons, it makes also sense to map the Tomcat (http port 8080) via Apache or nginx reverse proxy to https, as described <a href="https://blog.axaneco.net/doc/2023/03/20/reverse-proxy.html">here in my blog</a>.</p>]]></content><author><name></name></author><category term="doc" /><summary type="html"><![CDATA[THIS POST IS OUTDATED See updated post.]]></summary></entry><entry><title type="html">Generic setup of a https nginx/Apache reverse proxy</title><link href="http://localhost:4000/doc/2023/03/20/reverse-proxy.html" rel="alternate" type="text/html" title="Generic setup of a https nginx/Apache reverse proxy" /><published>2023-03-20T07:00:00+01:00</published><updated>2023-03-20T07:00:00+01:00</updated><id>http://localhost:4000/doc/2023/03/20/reverse-proxy</id><content type="html" xml:base="http://localhost:4000/doc/2023/03/20/reverse-proxy.html"><![CDATA[<p>Assume we have a web site engine (web server, blog, shellinabox, you name it) running in your private network at ip 10.10.10.10 port 3500, serving http.</p>

<p>We want to expose this local/intern web service to the internet, without giving the internet access to our local machine. We want to use the https-protocol and proxy via web server nginx or Apache, using a (sub)domain that we own, let’s assume for simplifying reasons that this is <code class="language-plaintext highlighter-rouge">https://sub.example.com</code>.</p>

<p>Here we look at two very basic proxy setups to realize this task.</p>

<h2 id="nginx">nginx</h2>

<p>So, given that nginx is already installed, for the proxy functionality, at the server that serves <code class="language-plaintext highlighter-rouge">https://sub.example.com</code>, we configure a nginx site as <code class="language-plaintext highlighter-rouge">/etc/nginx/sites-available/sub.example.com</code>:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c">#</span>
<span class="c"># Virtual Host configuration nginx</span>
<span class="c">#</span>

server <span class="o">{</span>
        listen 80<span class="p">;</span>
        server_name sub.example.com<span class="p">;</span>
        
        location / <span class="o">{</span>
                proxy_pass http://10.10.10.10:3500/<span class="p">;</span>
                proxy_set_header Host <span class="nv">$host</span><span class="p">;</span>
        <span class="o">}</span>
<span class="o">}</span></code></pre></figure>

<p>Next, we install <code class="language-plaintext highlighter-rouge">certbot</code> for obtaining SSL certificates for our server:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>apt-get <span class="nb">install </span>certbot python3-certbot-nginx </code></pre></figure>

<p>Now we have to make our website public, in order that <code class="language-plaintext highlighter-rouge">certbot</code> can obtain a certificate from <a href="https://letsencrypt.org/" target="_blank">Let’s Encrypt</a> via the <a href="https://en.wikipedia.org/wiki/Automatic_Certificate_Management_Environment" target="_blank">ACME</a> protocol. We do that by making the site available:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo ln</span> <span class="nt">-s</span> /etc/nginx/sites-available/sub.example.com /etc/nginx/sites-enabled</code></pre></figure>

<p>After that, we have to reload the nginx configuration:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>service nginx reload</code></pre></figure>

<p>Now we should be able to access our server via <code class="language-plaintext highlighter-rouge">http://sub.example.com</code>which is internally served by <code class="language-plaintext highlighter-rouge">http://10.10.10.10:3500</code>.</p>

<p>To obtain the certificate, we now run</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>certbot <span class="nt">--nginx</span> <span class="nt">-d</span> sub.example.com</code></pre></figure>

<p>This will provide our server with a Let’s Encrypt certificate, create and enable a new nginx ssl configuration <code class="language-plaintext highlighter-rouge">/etc/nginx/sites-available/sub.example.com-ssl</code>, and modify the original config enable SSL. The automatically changed config file should now read like</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c">#</span>
<span class="c"># Virtual Host configuration nginx</span>
<span class="c">#</span>

server <span class="o">{</span>
        server_name sub.example.com<span class="p">;</span>
        
        location / <span class="o">{</span>
                proxy_pass http://10.10.10.10:3500/<span class="p">;</span>
                proxy_set_header Host <span class="nv">$host</span><span class="p">;</span>
        <span class="o">}</span>

    listen 443 ssl<span class="p">;</span> <span class="c"># managed by Certbot</span>
    ssl_certificate /etc/letsencrypt/live/sub.example.com/fullchain.pem<span class="p">;</span> <span class="c"># managed by Certbot</span>
    ssl_certificate_key /etc/letsencrypt/live/sub.example.com/privkey.pem<span class="p">;</span> <span class="c"># managed by Certbot</span>
    include /etc/letsencrypt/options-ssl-nginx.conf<span class="p">;</span> <span class="c"># managed by Certbot</span>
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem<span class="p">;</span> <span class="c"># managed by Certbot</span>
<span class="o">}</span>

server <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="nv">$host</span> <span class="o">=</span> sub.example.com<span class="o">)</span> <span class="o">{</span>
        <span class="k">return </span>301 https://<span class="nv">$host$request_uri</span><span class="p">;</span>
    <span class="o">}</span> <span class="c"># managed by Certbot</span>

        listen 80<span class="p">;</span>
        server_name sub.example.com<span class="p">;</span>
    <span class="k">return </span>404<span class="p">;</span> <span class="c"># managed by Certbot</span>
<span class="o">}</span></code></pre></figure>

<p>That’s it! Now we have a local/intern web service exposed to the internet using the https-protocol via nginx.</p>

<h2 id="apache">Apache</h2>

<p>Again we assume that the web server itself is already installed. So we configure at the server that serves <code class="language-plaintext highlighter-rouge">https://sub.example.com</code> an Apache site as <code class="language-plaintext highlighter-rouge">/etc/apache2/sites-available/sub.example.com.conf</code>:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c">#</span>
<span class="c"># Virtual Host configuration Apache</span>
<span class="c">#</span>
&lt;VirtualHost <span class="k">*</span>:80&gt;
        ServerAdmin webmaster@localhost
        ServerName sub.example.com
        ServerAlias sub.example.com
        ProxyPreserveHost On
        
        ProxyPass / http://10.10.10.10:3500/
        ProxyPassReverse / http://10.10.10.10:3500/
&lt;/VirtualHost&gt;</code></pre></figure>

<p>For the proxy configuration to work, we want to be sure to have proxy functionality enabled:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>a2enmod proxy
<span class="nb">sudo </span>a2enmod proxy_http
<span class="nb">sudo </span>systemctl restart apache2</code></pre></figure>

<p>Next, we install <code class="language-plaintext highlighter-rouge">certbot</code> for obtaining SSL certificates for our server:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>apt-get <span class="nb">install </span>certbot python3-certbot-apache </code></pre></figure>

<p>Now we have to make our website public, in order that <code class="language-plaintext highlighter-rouge">certbot</code> can obtain a certificate from <a href="https://letsencrypt.org/" target="_blank">Let’s Encrypt</a> via the <a href="https://en.wikipedia.org/wiki/Automatic_Certificate_Management_Environment" target="_blank">ACME</a> protocol. We do that by making the site available:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>a2ensite sub.example.com.conf</code></pre></figure>

<p>After that, we have to restart Apache:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>service apache2 restart</code></pre></figure>

<p>Now we should be able to access our server via <code class="language-plaintext highlighter-rouge">http://sub.example.com</code>which is internally served by <code class="language-plaintext highlighter-rouge">http://10.10.10.10:3500</code>.</p>

<p>To obtain the certificate, we now run</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>certbot <span class="nt">--apache</span> <span class="nt">-d</span> sub.example.com</code></pre></figure>

<p>This will provide our server with a Let’s Encrypt certificate, create and enable a new Apache ssl configuration <code class="language-plaintext highlighter-rouge">/etc/apache/sites-available/sub.example.com-le-ssl.conf</code>, and modify the original config to redirect from http to https. The automatically created config file should read like</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c">#</span>
<span class="c"># Virtual Host configuration Apache</span>
<span class="c">#</span>
&lt;IfModule mod_ssl.c&gt;
&lt;VirtualHost <span class="k">*</span>:443&gt;
        ServerAdmin webmaster@localhost
        ServerName sub.example.com
        ServerAlias sub.example.com
        ProxyPreserveHost On
        
        ProxyPass / http://10.10.10.10:3500/
        ProxyPassReverse / http://10.10.10.10:3500/

SSLCertificateFile /etc/letsencrypt/live/sub.example.com/fullchain.pem<span class="p">;</span>
SSLCertificateKeyFile /etc/letsencrypt/live/sub.example.com/privkey.pem<span class="p">;</span>
Include /etc/letsencrypt/options-ssl-apache.conf
&lt;/VirtualHost&gt;
&lt;/IfModule&gt;</code></pre></figure>

<p>That’s it! Now we have a local/intern web service exposed to the internet using the https-protocol via Apache.</p>]]></content><author><name></name></author><category term="doc" /><summary type="html"><![CDATA[Assume we have a web site engine (web server, blog, shellinabox, you name it) running in your private network at ip 10.10.10.10 port 3500, serving http.]]></summary></entry><entry><title type="html">Setting up a Jekyll server with Apache reverse proxy</title><link href="http://localhost:4000/doc/2023/03/19/jekyll-server.html" rel="alternate" type="text/html" title="Setting up a Jekyll server with Apache reverse proxy" /><published>2023-03-19T10:00:00+01:00</published><updated>2023-03-19T10:00:00+01:00</updated><id>http://localhost:4000/doc/2023/03/19/jekyll-server</id><content type="html" xml:base="http://localhost:4000/doc/2023/03/19/jekyll-server.html"><![CDATA[<p>OK, this site runs as a <a href="https://jekyllrb.com" target="_blank">Jekyll</a> blog, so how do we do that?</p>

<p>First, we install Jekyll at the intended server, for Ubuntu:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>apt-get <span class="nb">install </span>ruby-full build-essential zlib1g-dev</code></pre></figure>

<p>Then, we add a designated user to run the blog:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>useradd jekyll</code></pre></figure>

<p>After that, we export some necessary variables for the next step:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo</span> <span class="nt">-i</span>
su - jekyll
<span class="nb">export </span><span class="nv">GEM_HOME</span><span class="o">=</span><span class="s2">"/home/jekyll/gems"</span>
<span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="s2">"home/jekyll/gems/bin:</span><span class="nv">$PATH</span><span class="s2">"</span></code></pre></figure>

<p>To complete the Jekyll installation, we install the necessary Ruby components. Be sure that you are user <code class="language-plaintext highlighter-rouge">jekyll</code> and inside the <code class="language-plaintext highlighter-rouge">/home/jekyll</code> directory:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">gem <span class="nb">install </span>jekyll bundler</code></pre></figure>

<p>Then, we create a new blog:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">jekyll new blog</code></pre></figure>

<p>Finally, we have to create the service unit file as <code class="language-plaintext highlighter-rouge">/lib/systemd/system/jekyll.service</code>:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="o">[</span>Unit]
<span class="nv">Description</span><span class="o">=</span>Jekyll service
<span class="nv">After</span><span class="o">=</span>syslog.target
<span class="nv">After</span><span class="o">=</span>network.target

<span class="o">[</span>Service]
<span class="c"># Added solution -- add WorkingDirectory to directory where you clone your markdown files for Jekyll to render</span>
<span class="nv">WorkingDirectory</span><span class="o">=</span>/home/jekyll/blog
<span class="c"># Name of the user account that is running the Jekyll server</span>
<span class="nv">User</span><span class="o">=</span>jekyll
<span class="nv">Type</span><span class="o">=</span>simple
<span class="c"># Location (source) of the markdown files to be rendered</span>
<span class="nv">ExecStart</span><span class="o">=</span>/usr/bin/bash /home/jekyll/start.sh
<span class="nv">Restart</span><span class="o">=</span>always
<span class="nv">StandardOutput</span><span class="o">=</span>journal
<span class="nv">StandardError</span><span class="o">=</span>journal
<span class="nv">SyslogIdentifier</span><span class="o">=</span>jekyll

<span class="o">[</span>Install]
<span class="nv">WantedBy</span><span class="o">=</span>multi-user.target</code></pre></figure>

<p>where the <code class="language-plaintext highlighter-rouge">start.sh</code> script reads as</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">export </span><span class="nv">GEM_HOME</span><span class="o">=</span><span class="s1">'/home/jekyll/gems'</span> 
<span class="nb">export </span><span class="nv">PATH</span><span class="o">=</span><span class="s1">'/home/jekyll/gems/bin:$PATH'</span>

<span class="nb">cd</span> /home/jekyll/blog 
<span class="nb">export </span><span class="nv">BUNDLE_GEMFILE</span><span class="o">=</span><span class="s1">'/home/jekyll/blog/Gemfile'</span> 
bundle <span class="nb">exec </span>jekyll serve <span class="nt">--host</span> 127.0.0.1 <span class="nt">--port</span> 4000 </code></pre></figure>

<p>For automated start on boot, we have to link this service under <code class="language-plaintext highlighter-rouge">/etc/systemd/system/multi-user.target.wants</code>:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">cd</span> /etc/systemd/system/multi-user.target.wants
<span class="nb">sudo ln</span> <span class="nt">-s</span> /lib/systemd/system/jekyll.service <span class="nb">.</span>
<span class="nb">sudo </span>systemctl daemon-reload</code></pre></figure>

<p>For now, we can start the service manually by <code class="language-plaintext highlighter-rouge">sudo service jekyll start</code>.</p>

<p>For the proxy configuration to work, we want to be sure to have proxy functionality enabled:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>a2enmod proxy
<span class="nb">sudo </span>a2enmod proxy_http
<span class="nb">sudo </span>systemctl restart apache2</code></pre></figure>

<p>So, our Jekyll blog server is at present running at port 4000 localhost.<br />
To publish it, we have to amend the Apache configuration as follows.</p>

<p><code class="language-plaintext highlighter-rouge">/etc/apache2/sites-available/blog.example.com.conf</code>:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">&lt;VirtualHost <span class="k">*</span>:80&gt;
    ServerAdmin webmaster@localhost
    ServerName blog.example.com
    ServerAlias blog.example.com

    ProxyPass / http://127.0.0.1:4000/
    ProxyPassReverse / http://127.0.0.1:4000/

    ErrorLog <span class="k">${</span><span class="nv">APACHE_LOG_DIR</span><span class="k">}</span>/blog.example.com.error.log
    CustomLog <span class="k">${</span><span class="nv">APACHE_LOG_DIR</span><span class="k">}</span>/access.log vhost_combined
&lt;/VirtualHost&gt;</code></pre></figure>

<p>Now it’s time to restart the web server:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>service apache2 restart</code></pre></figure>

<p>Then, given that <code class="language-plaintext highlighter-rouge">certbot</code> is installed, run</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell">certbot <span class="nt">--apache</span> <span class="nt">-d</span> blog.example.com</code></pre></figure>

<p>in order to create a SSL-encrypted site.</p>

<p>That’s it! Now we have a Jekyll blog running SSL encrypted behind an Apache reverse proxy.</p>

<p>This post was updated 2023-11-19 to fix/clarify some service related details.</p>]]></content><author><name></name></author><category term="doc" /><summary type="html"><![CDATA[OK, this site runs as a Jekyll blog, so how do we do that?]]></summary></entry></feed>