<h1 id="nextcloud">NextCloud</h1>
<p>I want to install NextCloud for my family, but <strong>only</strong> for my family. This means making things hard for myself by installing it behind my firewall with a private nat ipaddress. That presented problems with getting a valid <a href="https://letsencrypt.org/">Let's encrypt</a> cert.</p>
<p>It all now works, and thanks to timttmy I was able to get the WireGuard VPN installed and working.</p>
<h2 id="pi-4">Pi 4</h2>
<p>Get a Pi, and a SSD, enable it. You should review <a href="https://jamesachambers.com/raspberry-pi-4-usb-boot-config-guide-for-ssd-flash-drives/">Raspberry Pi 4 USB Boot Config Guide for SSD / Flash Drives</a>, for issues with SSD drives and the Raspberry Pi.</p>
<p>You can install Raspbian as normal. I already covered this in <a href="https://hackerpublicradio.org/eps.php?id=2356">hpr2356 :: Safely enabling ssh in the default Raspbian Image</a>, and <a href="https://github.com/kenfallon/fix-ssh-on-pi">Safely enabling ssh in the default Raspberry Pi OS (previously called Raspbian) Image</a>.</p>
<p>And then follow the instructions in <a href="https://www.tomshardware.com/how-to/boot-raspberry-pi-4-usb">How to Boot Raspberry Pi 4 From a USB SSD or Flash Drive</a>.</p>
<h2 id="next-cloud">Next Cloud</h2>
<p>Install Apache, MariaDB, and PHP</p>
<ul>
<li><a href="https://www.techrepublic.com/article/how-to-install-nextcloud-20-on-ubuntu-server-20-04/">How to install Nextcloud 20 on Ubuntu Server 20.04</a></li>
<li><a href="https://docs.nextcloud.com/server/20/admin_manual/installation/source_installation.html">NextCloud - Installation and server configuration - Installation on Linux</a></li>
<li><a href="https://nextcloud.com/install/#instructions-server">Download NextCloud</a></li>
</ul>
<pre><code># diff /etc/apache2/apache2.conf /etc/apache2/apache2.conf.orig
171,172c171,172
&lt;       Options FollowSymLinks
&lt;       AllowOverride All
---
&gt;       Options Indexes FollowSymLinks
&gt;       AllowOverride None</code></pre>
<h3 id="install-phpmyadmin">Install PHPMyAdmin</h3>
<ul>
<li><a href="https://pimylifeup.com/raspberry-pi-phpmyadmin/">How to Install PHPMyAdmin on the Raspberry Pi</a></li>
</ul>
<h3 id="required-changes-to-nextcloud-config.">Required Changes to nextcloud config.</h3>
<pre><code>root@nextcloud:~# diff /root/nextcloud-config.php.orig /var/www/html/nextcloud/config/config.php 
&gt;     1 =&gt; &#39;nextcloud&#39;,
&gt;     2 =&gt; &#39;192.168.123.123&#39;,
&gt;     3 =&gt; &#39;nextcloud.example.com&#39;,
&gt;   &#39;memcache.local&#39; =&gt; &#39;\OC\Memcache\APCu&#39;,</code></pre>
<pre><code># diff /etc/apache2/sites-available/000-default.conf.orig /etc/apache2/sites-enabled/000-default.conf
28a29,32
&gt;         RewriteEngine On
&gt;         RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]
&gt;       Redirect 301 /.well-known/carddav /var/www/html/nextcloud/remote.php/dav
&gt;       Redirect 301 /.well-known/caldav /var/www/html/nextcloud/remote.php/dav</code></pre>
<h3 id="required-changes-to-php.ini-config.">Required Changes to <code>php.ini</code> config.</h3>
<pre><code>root@nextcloud:~# diff /etc/php/7.3/apache2/php.ini.orig /etc/php/7.3/apache2/php.ini
401c401
&lt; memory_limit = 128M
---
&gt; memory_limit = 2000M
689c689
&lt; post_max_size = 8M
---
&gt; post_max_size = 2048M
841c841
&lt; upload_max_filesize = 2M
---
&gt; upload_max_filesize = 2048M</code></pre>
<h3 id="upgrade">Upgrade</h3>
<p>You can upgrade using the procedure described by <a href="https://hackerpublicradio.org/correspondents/0078.html">klaatu</a> in <a href="https://hackerpublicradio.org/eps.php?id=3232">hpr3232 :: Nextcloud</a>, or as admin via the UI <code>https://nextcloud.example.com/nextcloud/index.php/settings/user</code>, Administration, Overview.</p>
<p>You will see a lot of <a href="https://docs.nextcloud.com/server/12/admin_manual/configuration_server/security_setup_warnings.html">Warnings on Admin Page</a>, but don't panic. The server is not accessible on the Internet after all.</p>
<p>The errors have links to how you can fix them and some are very easy to do.</p>
<p>I got an error "Error occurred while checking server setup". I used this <a href="https://help.nextcloud.com/t/solved-12-0-2-to-12-0-3-update-failes-at-create-backup/21490">tip</a> to move root owned files out of next cloud dir.</p>
<p>For me it was mostly about enabling caching via <a href="https://docs.nextcloud.com/server/12/admin_manual/configuration_server/caching_configuration.html#id1">APCU</a>, and enabling <a href="https://docs.nextcloud.com/server/12/admin_manual/configuration_server/security_setup_warnings.html#you-are-accessing-this-site-via-http">You are accessing this site via HTTP</a>.</p>
<p>The first is fixed in the <code>nextcloud/config/config.php</code> page, the next is fixed by installing a valid SSL cert from Let's Encrypt.</p>
<h2 id="ssl-lets-encrypt">SSL Let's Encrypt</h2>
<p>Based on the following article I installed it manually.</p>
<p><a href="https://code.luasoftware.com/tutorials/lets-encrypt/obtain-lets-encrypt-ssl-using-manual-dns-verification/">Obtain Let's Encrypt SSL Certificate Using Manual DNS Verification</a></p>
<h3 id="install-certbot">Install certbot</h3>
<pre><code># apt install certbot</code></pre>
<p>Then run the script manually specifying that the challenge should be over dns.</p>
<pre><code># certbot certonly --manual --preferred-challenges dns 
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Enter email address (used for urgent renewal and security notices) (Enter &#39;c&#39; to
cancel): letsencrypt@example.com

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(A)gree/(C)ancel: A

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let&#39;s Encrypt project and the non-profit
organization that develops Certbot? We&#39;d like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: n
Please enter in your domain name(s) (comma and/or space separated)  (Enter &#39;c&#39;
to cancel): nextcloud.example.com
Obtaining a new certificate
Performing the following challenges:
dns-01 challenge for nextcloud.example.com

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NOTE: The IP of this machine will be publicly logged as having requested this
certificate. If you&#39;re running certbot in manual mode on a machine that is not
your server, please ensure you&#39;re okay with that.

Are you OK with your IP being logged?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: y

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Please deploy a DNS TXT record under the name
_acme-challenge.nextcloud.example.com with the following value:

0c5dbJpS5t0VKzglhdfFhZ6CGmZlLHNaNnAQe2VeJyKi

Before continuing, verify the record is deployed.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Press Enter to Continue</code></pre>
<p>It was at this point I went to my hosting companys page and created a subdomain called <code>nextcloud</code>. Then I added a <code>TXT</code> record called <code>_acme-challenge</code> with the text <code>0c5dbJpS5t0VKzglhdfFhZ6CGmZlLHNaNnAQe2VeJyKi</code>.</p>
<p>In order to verify that we use the command:</p>
<pre><code># apt-get install -y dnsutils

$ dig -t TXT _acme-challenge.nextcloud.example.com

; &lt;&lt;&gt;&gt; DiG 9.11.5-P4-5.1+deb10u2-Debian &lt;&lt;&gt;&gt; -t TXT _acme-challenge.nextcloud.example.com
;; global options: +cmd
;; Got answer:
;; -&gt;&gt;HEADER&lt;&lt;- opcode: QUERY, status: NOERROR, id: 39298
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;_acme-challenge.nextcloud.example.com. IN TXT

;; ANSWER SECTION:
_acme-challenge.nextcloud.example.com. 3600 IN TXT &quot;0c5dbJpS5t0VKzglhdfFhZ6CGmZlLHNaNnAQe2VeJyKi&quot;

;; Query time: 7 msec
;; SERVER: 178.21.112.12#53(178.21.112.12)
;; WHEN: Thu Dec 10 16:27:53 CET 2020
;; MSG SIZE  rcvd: 121
</code></pre>
<p>Now that the answer section is correct we can continue with the <code>certbot</code> script.</p>
<pre><code>Waiting for verification...
Cleaning up challenges

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/nextcloud.example.com/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/nextcloud.example.com/privkey.pem
   Your cert will expire on 2021-03-10. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot
   again. To non-interactively renew *all* of your certificates, run
   &quot;certbot renew&quot;
 - If you like Certbot, please consider supporting our work by:

   Donating to ISRG / Let&#39;s Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le</code></pre>
<h3 id="renew">Renew</h3>
<p>Unfortunately the renew is not automatic. <a href="https://webmasters.stackexchange.com/questions/116316/could-not-renew-letsencrypt-certificate-error-the-manual-plugin-is-not-working">"You don't have to renew Certificate with"renew" option. You have to run the same command you ran for Certificate creation."</a></p>
<p>So I just set up a 3 monthly recurring reminder in NextCloud to do this.</p>
<h3 id="delete">Delete</h3>
<p>If you need to delete the cert you can do it as follows.</p>
<pre><code>root@nextcloud:~# certbot certificates
Saving debug log to /var/log/letsencrypt/letsencrypt.log

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Found the following certs:
  Certificate Name: nextcloud.example.com
    Domains: nextcloud.example.com
    Expiry Date: 2021-03-10 14:28:07+00:00 (VALID: 89 days)
    Certificate Path: /etc/letsencrypt/live/nextcloud.example.com/fullchain.pem
    Private Key Path: /etc/letsencrypt/live/nextcloud.example.com/privkey.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
root@nextcloud:~# certbot delete
Saving debug log to /var/log/letsencrypt/letsencrypt.log

Which certificate(s) would you like to delete?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: nextcloud.example.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate numbers separated by commas and/or spaces, or leave input
blank to select all options shown (Enter &#39;c&#39; to cancel): 1

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Deleted all files relating to certificate nextcloud.example.com.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -</code></pre>
<h2 id="apache-setup">Apache setup</h2>
<p>Setting up Apache is not well explained anywhere I could find.</p>
<p>The good news is that <a href="https://ssl-config.mozilla.org/#server=apache&amp;version=2.4.41&amp;config=intermediate&amp;openssl=1.1.1d&amp;guideline=5.6">moz://a SSL Configuration Generator</a> page takes the misery out of making tea. I mean, it will help you with your configuration. If you do like misery you can of course read the <a href="https://wiki.mozilla.org/Talk:Security/Server_Side_TLS">Talk:Security/Server Side TLS</a> page.</p>
<p>The most helpful articles were:</p>
<ul>
<li><a href="https://linuxize.com/post/secure-apache-with-let-s-encrypt-on-debian-10/">Secure Apache with Let's Encrypt on Debian 10</a></li>
<li><a href="https://develike.com/en/articles/adding-a-trusted-self-signed-ssl-certificate-to-apache-on-debian-ubuntu">Adding a trusted self-signed SSL certificate to Apache on Debian/Ubuntu</a></li>
<li><a href="https://www.digitalocean.com/community/tutorials/how-to-create-a-self-signed-ssl-certificate-for-apache-in-debian-10">How To Create a Self-Signed SSL Certificate for Apache in Debian 10</a></li>
</ul>
<p>I made the following changes:</p>
<pre><code>root@nextcloud:/etc/apache2/sites-available# diff 000-default.conf.orig 000-default.conf
28a29,30
&gt;         RewriteEngine On
&gt;         RewriteRule ^(.*)$ https://%{HTTP_HOST}$1 [R=301,L]

root@nextcloud:/etc/apache2/sites-available# diff default-ssl.conf.orig default-ssl.conf
32,33c32,33
&lt;               SSLCertificateFile      /etc/ssl/certs/ssl-cert-snakeoil.pem
&lt;               SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
---
&gt;               SSLCertificateFile      /etc/letsencrypt/live/nextcloud.example.com/fullchain.pem
&gt;               SSLCertificateKeyFile   /etc/letsencrypt/live/nextcloud.example.com/privkey.pem
129a130,131
&gt;               # enable HTTP/2, if available
&gt;               Protocols h2 http/1.1
130a133,134
&gt;               # HTTP Strict Transport Security (mod_headers is required) (63072000 seconds)
&gt;               Header always set Strict-Transport-Security &quot;max-age=63072000&quot;</code></pre>
<h2 id="testing">Testing</h2>
<p>To test the cert you can connect to the localhost on the server.</p>
<pre><code>root@nextcloud:/etc/apache2/sites-available# openssl s_client -crlf -debug -connect localhost:443 -status -servername nextcloud.example.com
CONNECTED(00000003)
write to 0x643cf8 [0x652568] (321 bytes =&gt; 321 (0x141))
[snip...]
read from 0x643cf8 [0x6492b3] (5 bytes =&gt; 5 (0x5))
0000 - 48 54 54 50 2f                                    HTTP/
3069898768:error:1408F10B:SSL routines:ssl3_get_record:wrong version number:../ssl/record/ssl3_record.c:332:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 5 bytes and written 321 bytes
Verification: OK
---
New, (NONE), Cipher is (NONE)
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
[snip...]</code></pre>
<p>I had been using <code>systemctl restart apache2.service</code> to restart apache, but the recommended way is to use <code>apache2ctl</code>.</p>
<pre><code>root@nextcloud:/etc/apache2/sites-available# apache2ctl 
Usage: /usr/sbin/apache2ctl start|stop|restart|graceful|graceful-stop|configtest|status|fullstatus|help
       /usr/sbin/apache2ctl &lt;apache2 args&gt;
       /usr/sbin/apache2ctl -h            (for help on &lt;apache2 args&gt;)

root@nextcloud:/etc/apache2/sites-available# apache2ctl restart
AH00558: apache2: Could not reliably determine the server&#39;s fully qualified domain name, using 127.0.1.1. Set the &#39;ServerName&#39; directive globally to suppress this message

root@nextcloud:/etc/apache2/sites-available# apache2 -t
[Thu Dec 10 18:18:49.187628 2020] [core:warn] [pid 4108] AH00111: Config variable ${APACHE_RUN_DIR} is not defined
apache2: Syntax error on line 80 of /etc/apache2/apache2.conf: DefaultRuntimeDir must be a valid directory, absolute or relative to ServerRoot</code></pre>
<p>For some reason that fixed it.</p>
<pre><code># openssl s_client -crlf -debug -connect localhost:443 -status -servername nextcloud.example.com
CONNECTED(00000003)
write to 0xe4918 [0xf3188] (324 bytes =&gt; 324 (0x144))
[snip...]
OCSP response: 
======================================
OCSP Response Data:
    OCSP Response Status: successful (0x0)
    Response Type: Basic OCSP Response
    Version: 1 (0x0)
    Responder Id: C = US, O = Let&#39;s Encrypt, CN = R3
    Produced At: Dec 22 16:04:00 2020 GMT
[snip...]
---
Certificate chain
 0 s:CN = nextcloud.example.com
   i:C = US, O = Let&#39;s Encrypt, CN = R3
 1 s:C = US, O = Let&#39;s Encrypt, CN = R3
   i:O = Digital Signature Trust Co., CN = DST Root CA X3
---
Server certificate
-----BEGIN CERTIFICATE-----
[snip...]</code></pre>
<h2 id="dns-rebind-protection">DNS Rebind Protection</h2>
<p>Now that everything is up and running we just need to create a new <code>A</code> record pointing to our internal IP Address. Unfortunately while <code>nextcloud.example.com</code> resolves to <code>192.168.123.123</code> externally, it fails to return an answer internally.</p>
<p>A little investigation lead to the fact that my <a href="https://en.avm.de/service/fritzbox/fritzbox-7390/knowledge-base/publication/show/663_No-DNS-resolution-of-private-IP-addresses/">firewall</a>, was seeing this as a <a href="https://en.wikipedia.org/wiki/DNS_rebinding">DNS Rebinding</a> attack. It correctly blocks these DNS entires. I was able to add an exception under <code>Network &gt; DHCP &gt; Rebind protection &gt; Discard upstream RFC1918 responses</code>.</p>
<p>On your router you should check under DHCP/DNS entries for <code>RFC1918</code> or <code>DNS Rebinding</code>.</p>
<p>You can verify your install as follows:</p>
<pre><code># apt-get install -y dnsutils

$ dig nextcloud.example.com

; &lt;&lt;&gt;&gt; DiG 9.16.8-Debian &lt;&lt;&gt;&gt; nextcloud.example.com
;; global options: +cmd
;; Got answer:
;; -&gt;&gt;HEADER&lt;&lt;- opcode: QUERY, status: NOERROR, id: 29350
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;nextcloud.example.com.          IN      A

;; ANSWER SECTION:
nextcloud.example.com.   3600    IN      A       192.168.123.123

;; Query time: 40 msec
;; SERVER: 192.168.0.71#53(192.168.0.71)
;; WHEN: Thu Dec 10 16:53:39 GMT 2020
;; MSG SIZE  rcvd: 65</code></pre>
<h2 id="completing">Completing</h2>
<p>Back in the admin console you should keep upgrading, and fixing errors until it says <code>Your version is up to date.</code> and <code>All checks passed.</code></p>
<p>At this point you are ready to open the server up to your users while they are outside the home, in work or school.</p>
<h1 id="firewall-setup">Firewall setup</h1>
<p>timttmy has already done an episode on WireGuard where he goes into the details of how to install it manually.</p>
<p>I cheated and used the <a href="https://www.pivpn.io/">PIVPN</a> which now supports wireguard.</p>
<p>This is a good walkthrough with screenshots in the article <a href="https://pimylifeup.com/raspberry-pi-wireguard/">Setting up a WireGuard VPN on the Raspberry Pi</a>.</p>
<p>Once that's done you should have the following commands available.</p>
<pre><code># pivpn
::: Control all PiVPN specific functions!
:::
::: Usage: pivpn &lt;command&gt; [option]
:::
::: Commands:
:::  -a,  add              Create a client conf profile
:::  -c,  clients          List any connected clients to the server
:::  -d,  debug            Start a debugging session if having trouble
:::  -l,  list             List all clients
:::  -qr, qrcode           Show the qrcode of a client for use with the mobile app
:::  -r,  remove           Remove a client
:::  -h,  help             Show this help dialog
:::  -u,  uninstall        Uninstall pivpn from your system!
:::  -up, update           Updates PiVPN Scripts
:::  -bk, backup           Backup VPN configs and user profiles
</code></pre>
<p>During the install process you will select a port to use. This port needs to be allowed in from the Internet to your internal server. Where this will be done is different for every router, but have a look around for <code>port forwarding</code> or <code>permit access</code> to do this.</p>
<h1 id="setting-up-client-on-lineageos">Setting up Client on LineageOS</h1>
<p>It is at this point that you will need to have accounts created in NextCloud.</p>
<ul>
<li><a href="https://nextcloud.example.com/nextcloud/">https://nextcloud.example.com/nextcloud/</a></li>
</ul>
<p>You can do this under your profile &gt; users in an admin account.</p>
<p>I created an account for each of the family members, a generic one for the house, and a readonly one for the <a href="https://hackerpublicradio.org/eps.php?id=3039">MagicMirror</a>.</p>
<p>The house account houses (pun intended) the shared calendar, files, and contacts. All the family accounts have read and write access to these, except for the MagicMirror one which only needs to read the calendar and contacts.</p>
<h2 id="fdroid">Fdroid</h2>
<p>Now you can install the software you will need on your phones.</p>
<ul>
<li><a href="https://f-droid.org/en/packages/com.nextcloud.client/">NextCloud</a> Synchronization client</li>
<li><a href="https://f-droid.org/en/packages/at.bitfire.davdroid/">DAVx</a> DAVx⁵ CalDAV/CardDAV Synchronization and Client</li>
<li><a href="https://f-droid.org/en/packages/org.dmfs.tasks/">OpenTasks</a> Keep track of your list of goals</li>
<li><a href="https://f-droid.org/en/packages/com.wireguard.android/">WireGuard</a> Next generation secure VPN network tunnel</li>
</ul>
<p>You will need to setup the NextCloud client using the url <code>https://nextcloud.example.com/nextcloud/</code>, username and password.</p>
<p>Then you set up DAVx using another url <code>https://nextcloud.example.com/nextcloud/remote.php/dav</code>, but the same , username and password.</p>
<p>By the way if you want to access files you can do so via <code>davs://nextcloud.example.com/nextcloud/remote.php/dav/files/house/</code></p>
<p>I set up the NextCloud client to automatically upload photos, and videos to the server.</p>
<p>To set up WireGuard you need to create a connection for each device connecting</p>
<pre><code>root@nextcloud:~# pivpn add
Enter a Name for the Client: Mobile_Worker
::: Client Keys generated
::: Client config generated
::: Updated server config
::: WireGuard reloaded
======================================================================
::: Done! Mobile_Worker.conf successfully created!
::: Mobile_Worker.conf was copied to /home/ken/configs for easy transfer.
::: Please use this profile only on one device and create additional
::: profiles for other devices. You can also use pivpn -qr
::: to generate a QR Code you can scan with the mobile app.
======================================================================</code></pre>
<p>Then open display the qrcode as follows:</p>
<pre><code>root@nextcloud:~# pivpn qrcode
::  Client list  ::
1) Mobile_Worker
Please enter the Index/Name of the Client to show: </code></pre>
<p>Pressing <code>1</code> in my case will display the QRCode.</p>
<p>Open the WireGuard app on the phone and press <code>+</code> to add an account, and select <em>scan from qr code</em>.</p>
<p>Point it to QRCode and that's it.</p>
<p>If you want to remove a client, you can just use <code>pivpn remove</code></p>
<pre><code>root@nextcloud:~# pivpn remove
::  Client list  ::
1) Mobile_Worker
Please enter the Index/Name of the Client to be removed from the list above: 6
Do you really want to delete Mobile_Worker? [Y/n] y
::: Updated server config
::: Client config for Mobile_Worker removed
::: Client Keys for Mobile_Worker removed
::: Successfully deleted Mobile_Worker
::: WireGuard reloaded</code></pre>
<h2 id="magicmirror">MagicMirror</h2>
<p>The final step is to have the <a href="https://hackerpublicradio.org/eps.php?id=3039">MagicMirror</a> in the living room display the shared calendar.</p>
<p>To display your calendar there, you need to have an <a href="https://en.wikipedia.org/wiki/ICalendar">ics iCalendar</a> file.</p>
<p>You can get that by login into NextCloud as the MagicMirror user via the web, going to the calendar you desire to export. Click the <code>...</code> menu and select "Copy Private Link".</p>
<p>You can then add the <code>?export</code> at the end of the url to get an ical export.</p>
<p><a href="https://hackerpublicradio.org/correspondents/0225.html">Dave</a> gave me a tip on how to have MagicMirror serve this file, by using its own local webserver. You point it to a local directory eg: <code>https://localhost:8080/modules/.calendars/</code>. Don't forget to create it.</p>
<pre><code>mkdir -p ~/MagicMirror/modules/.calendars/</code></pre>
<p>I wrote a script that would first get a new version of the ical file, and if it is downloaded correctly would immediately overwrite the previous one.</p>
<pre><code>[magicmirror@magicmirror ~]$ cat /home/pi/bin/cal.bash
#!/bin/bash
wget --quiet --output-document /home/pi/MagicMirror/modules/.calendars/home_calendar.ics.tmp --auth-no-challenge --http-user=magicmirror --http-password=&quot;PASSWORD&quot; &quot;https://nextcloud.example.com/nextcloud/remote.php/dav/calendars/magicmirror/personal_shared_by_House/?export&quot; &gt; /dev/null 2&gt;&amp;1
if [ -s /home/pi/MagicMirror/modules/.calendars/home_calendar.ics.tmp ]
then
  mv /home/pi/MagicMirror/modules/.calendars/home_calendar.ics.tmp /home/pi/MagicMirror/modules/.calendars/home_calendar.ics
fi
[snip...]</code></pre>
<p>I then scheduled this to run every 15 minutes.</p>
<pre><code>[magicmirror@magicmirror ~]$ crontab -l
*/15 * * * * /home/pi/bin/cal.bash &gt;/dev/null 2&gt;&amp;1</code></pre>
<p>The final step was to update my Calendar entry in the <code>~/MagicMirror/config/config.js</code> config file.</p>
<pre><code>        // Calendar
        {
            module: &quot;calendar&quot;,
            header: &quot;Calendar&quot;,
            position: &quot;top_center&quot;,
            config: {
                colored: true,
                maxTitleLength: 30,
                fade: false,
                calendars: [
                    {
                        name: &quot;Family Calendar&quot;,
                        url: &quot;https://localhost:8080/modules/.calendars/home_calendar.ics&quot;,
                        symbol: &quot;calendar-check&quot;,
                        color: &quot;#825BFF&quot; // violet-ish
                    },
                    {
                        name: &quot;Birthday Calendar&quot;,
                        url: &quot;https://localhost:8080/modules/.calendars/birthday_calendar.ics&quot;,
                        symbol: &quot;calendar-check&quot;,
                        color: &quot;#FFCC00&quot; // violet-ish
                    },
                    {
                        // Calendar uses repeated &#39;RDATE&#39; entries, which this iCal parser
                        // doesn&#39;t seem to recognise. Only the next event is visible, and
                        // the calendar has to be refreshed *after* the event has passed.
                        name: &quot;HPR Community News recordings&quot;,
                        url: &quot;https://hackerpublicradio.org/HPR_Community_News_schedule.ics&quot;,
                        symbol: &quot;calendar-check&quot;,
                        color: &quot;#C465A7&quot; // purple
                    },
                    {
                        // https://inzamelkalender.gad.nl/ical-info
                        name: &quot;GAD Calendar&quot;,
                        url: &quot;https://inzamelkalender.gad.nl/ical/0381200000107654&quot;,
                        symbol: &quot;calendar-check&quot;,
                        color: &quot;#00CC00&quot; // Green
                    },
                ]
            }
        },</code></pre>
<p>The contacts birthday wasn't available to the MagicMirror user immediately after I created it, so I was able to force an update as follows:</p>
<pre><code>root@nextcloud:/var/www/html/nextcloud# sudo -u www-data php occ dav:sync-birthday-calendar
Start birthday calendar sync for all users ...
    7 [============================]</code></pre>
<h1 id="conclusion">Conclusion</h1>
<p>With that we have a family sharing solution just like other normal house holds. Yet with the security of knowing that the data doesn't leave the house, and is not being used without your approval.</p>
<p>You can tell it's a hit, because now people are scheduling tech support tasks via the app.</p>
<p>Ah well.</p>

