Hosting a GlobalChat Draw Server

GlobalChat Draw is a new open-source end-to-end encrypted chat system with drawing capabilities built using Apple’s latest graphics APIs. A mobile client is also in development. Before you complain about the price of the Mac app, remind yourself that Apple charges developers $100/year just to have the privilege of publication. Furthermore be aware that the clients (both the mobile and Mac apps) and server are all provided open-source from the author’s github page.

The technology used to create this is very interesting, especially if you’re learning, but today we’ll focus on hosting a server. Perhaps in another post we can delve into the source code for each component and understand how it works.

Server Deployment

Now let’s deploy a GlobalChat server. We’ll use the instructions from the official gdraw.chat website:

Anyone can host a GlobalChat server. The current recommended way is to use Ubuntu snaps with Linux.

snap install --beta crystal-gchat-server

crystal-gchat-server.change-passwords -i

crystal-gchat-server

Here is a systemd unit we can use to automatically launch it.

1
2
3
4
5
6
7
8
9
10
11
12
13
[Unit]
Description=GlobalChat Draw Server
After=network.target
StartLimitIntervalSec=0
[Service]
Type=simple
Restart=always
RestartSec=1
User=root
ExecStart=/snap/bin/crystal-gchat-server
[Install]
WantedBy=multi-user.target

Install that in /etc/systemd/system/gchat.service

Enable it so that it runs automatically on startup

systemctl enable gchat

Start it now

systemctl start gchat

And make sure it’s running

systemctl status gchat

To access the logs

journalctl -fxu gchat

Now there is a new server publicly visible within the application! This works because there is a master server list (also open source) to which the server software registers itself to. Thanks to Apple’s strict policies, there are moderation tools built-in as well – notice that you can set your admin password during server setup.

Winbox on Linux through Docker

Discovered an excellent wine in docker solution today and so I thought it would be fun to use it to run winbox on my linux laptop. This was very easy to do, with great results, so I am writing it here for future use.

winbox.gif

You will need:

  • A working docker installation in Linux
  • A NIC for docker to bridge for use by winbox
  1. Create directories for wine data and templates
    mkdir -p ~/docker-data/{wine-data,wine-templates}
  2. Write the winbox wine template script:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    cat <<EOF > ~/docker-data/wine-templates/winbox.template
    #!/bin/bash
    export WINEPREFIX=/home/docker/wine/winbox
    export WINEARCH='win32'
    if [ ! -f /home/docker/wine/winbox/drive_c/winbox/winbox.exe ]
    then
    winetricks liberation corefonts
    mkdir -p /home/docker/wine/winbox/drive_c/winbox
    cd /home/docker/wine/winbox/drive_c/winbox
    curl -SLo winbox.exe https://mt.lv/winbox
    fi
    wine /home/docker/wine/winbox/drive_c/winbox/winbox.exe
    exit
    EOF
  3. Write the docker run script. Be sure to update the parent NIC specified with yours.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    cat <<EOF > ~/docker-winbox.sh
    xhost +si:localuser:$(whoami) >/dev/null
    docker network create -d macvlan -o parent=enp7s0 winbox_net
    docker run \
    --rm \
    -ti \
    --network=winbox_net \
    -e DISPLAY \
    -v /tmp/.X11-unix:/tmp/.X11-unix:ro \
    -v ~/docker-data/wine-templates:/mnt \
    -v ~/docker-data/wine-data:/home/docker/wine/ \
    -u docker \
    yantis/wine sh /mnt/winbox.template
    docker network remove winbox_net
    EOF
    chmod +x ~/docker-winbox.sh
  4. Execute it as needed, but first read the note below
    ~/docker-winbox.sh

The first execution will try to download assets from the web such as fonts for wine and the executable to winbox itself. These get cached and internet access is not needed for subsequent invocations. Running a custom network is liable to break the internet within the container, so execute the following in order to get everything cached, and then flip back to the script for future invocations.

1
2
3
4
5
6
7
8
9
10
xhost +si:localuser:$(whoami) >/dev/null
docker run \
--rm \
-ti \
-e DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix:ro \
-v ~/docker-data/wine-templates:/mnt \
-v ~/docker-data/wine-data:/home/docker/wine/ \
-u docker \
yantis/wine sh /mnt/winbox.template

Reference

https://docs.docker.com/network/macvlan/
https://hicu.be/macvlan-vs-ipvlan

Discovering Unraid

Another blog post while I wait for Array to be created.

How does Unraid being non-free square against the circle of retaining data sovereignty?

You should retain access to your own stuff without having to ask permission by some third party who may or may not even be reachable let alone necessarily agreeable in the future.

How does this work? How can I be OK with this?

My guess is that at the end of the day Unraid can be thought of as merely a very advanced WebGUI management layer between you and the free XFS file system, on top of the free Linux kernel.

Therefore by purchasing Unraid I am not giving up too much freedom, rather I am only deferring learning and building what would be an ideal WebGUI (Unraid) for managing the underlying complexity. I am paying for the hard work by the Unraid developers and to support the community.

This system helps users to enjoy a more convenient usage of the underlying system which is too complex for mere mortal like me. There is no ethical issue here about data sovereignty because XFS is free. If Unraid team stops supporting Unraid, I can access the regular linux shell and recover my data.

By supporting Unraid, the negative outcome I’m already prepared for becomes less likely.

This is OK, but what happens in the future as I grow reliant on that UI in a disaster scenario where I can’t receive an update – does it stay alive?

A couple of relevant communication in the Unraid Wiki FAQ sort of answer.

How hard is unRAID to use if I don’t know Linux?
Not hard at all. Although the unRAID server software is based on a slimmed-down Linux, it is managed almost entirely from your web browser, typically on a Windows or Mac computer. Some users are happy with that, but many want to take advantage of the many tweaks and addons for unRAID, and these usually require a little hands-on work. But they are completely optional, and are generally accompanied with lots of help and instructions, and there is a very helpful user community. Many users find this makes for a good introduction to Linux, at their own pace. See also this thread, especially this post, for more comments.

Does unRAID need Internet access?
The unRAID server software generally does not require Internet access. Of course, you will need Internet access from another desktop to download the software and software updates, and to read the unRAID forums and this Wiki!
However, there are many benefits to providing Internet access to your unRAID server. The unRAID software and your plugins and Docker containers can all be updated from within the software. Usability and manageability are improved with email and other notifications. unRAID supports NTP, the Internet time service, so if you enable the NTP service, your server will keep accurate time. In addition, expert unRAID users have created many addons for unRAID, such as plugins, Dockers, and VM’s, that can provide numerous application services such as torrent support, etc. All of these benefits are completely optional. See also this.

Can Unraid replicate?

I didn’t see any official support for the concept of pairing two servers together, no. Not yet.

Unraid really helps with learning about Disk Health

Having a UI for easily inspecting disk health, performing tests, and downloading SMART reports and a community from which to learn about these topics is wonderful.

Paranoid about Flash Drive going bad?

Better take a backup of it soon as described in the wiki https://wiki.unraid.net/UnRAID_6/Changing_The_Flash_Device

More reading here on that subject: https://unraid.net/blog/unraid-new-users-blog-series

Paranoid about disks themselves?

Read about that here https://www.reddit.com/r/unRAID/comments/dg9x7r/unraid_drive_reliability_calculator/

Path to Unraid

Let me start with a bit of meta-blogging since it has been awhile since I’ve edited my website…

This is really just a test post to get my feet wet with editing my site again after all this time.

It seems that I don’t write here anymore, like I used to.

When creating this blog site my goal was to keep it purely technical and apolitical.

This has been mostly a success, but as our political climate grows more tumultuous, this site grows silent. Why?

This is due in part to the realization that even the technical work is political, and growing fear that kept me silent (apolitical) in my writing goals from the outset.

I’ll focus only on the former briefly, and segue to the main thrust of this post.

For example, when it comes to choosing where to store my (~ 10 TB) data – do I put it on Google? DropBox?

What causes do they support? Do I support them? Do I trust them? Even if I did, do I trust their provider to keep them online? To secure my data? What about my own ISP?

Of course if you really follow the plot you realize you’ve got your work cut out for you.

Instead, I’ll self-host and then that’s fodder for a blog post to help others.

I’m currently investigating this problem and am excited to buy into Unraid.

The biggest selling point of Unraid (and there are many) is the community application system.

This system is what I dreamed for around 2015 when I learned Docker.

But let’s be clear that the Docker Hub is for developers; using it or pushing it onto home users is a corruption of its intended purpose.

This is why you have such a rich ecosystem around Docker (and by rich I mean complicated and hard to understand, case in point: k8s).

I think the community application system within Unraid understands and addresses this gap, and so I’ll begin documenting this process here as it unfolds.

Blocking Ads with OPNsense's internal dnsmasq

I first tried to use UnboundDNS, but it seemed unreliable once modified for adblocking. I later discovered that dnsmasq does everything I expected from Unbound, but with the familiar configuration interface. It’s been battle-tested for adblocking, and so as a pre-requisite, enable and configure that.

Once you’re done, enable SSH and connect to your OPNsense box.

I used my phosphor user’s home directory to store my adblock files. Replace my username with yours where applicable

Steven Black maintains a nice hosts file that blocks a lot of things. We will download that and strip out the comments (dnsmasq requires this when loading extra hosts files).

1
2
3
mkdir adblock
cd adblock
curl -sSL "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" | grep 0.0.0.0 | grep -v '#' > hosts

Next go to your OPNsense Web GUi and navigate to Services -> Dnsmasq DNS -> Settings

In the Advanced section add the following, replacing my username with yours, or wherever you put your hosts file:

1
addn-hosts=/home/phosphor/adblock/hosts

You can add multiple hosts files this way if you wish. Finally click Save and then Apply Configuration.

opnsense dnsmasq settings

Now you can test the adblock. You may need to reset your DNS cache on the clients you are testing. I like to use this site to test:

https://blockads.fivefilters.org


test results

Hunting DNS queries to block

So dnsmasq can also log queries if you add log-queries to the advanced configuration section. Then, the opnsense dnsmasq logs will show queries.

Resources

How to setup Apple push notifications for Riot using your own Sygnal instance

You will need:

  • An active apple developer license
  • A server in which to deploy sygnal

Push notifications in Matrix are configured by the client.

This is necessary due to the fact that that push is controlled by the device vendor. In other words, the iOS app tells the homeserver where the Sygnal server with the certificate corresponding to the App’s “App ID” is.

So you need to deploy a Sygnal server if you haven’t already, and load it with the certificate matching your the App ID you’ve compiled Riot as.

  1. Modify the plist file for Riot, configuring the AppID you’ll use and the Push Gateway (Sygnal) URL. This URL is relative to Synapse, as synapse will be the one receiving the messages through other matrix users. It will use this URL to push the notification to your device.

    riot-plist.png
  2. Login to the apple developer portal at https://developer.apple.com

  3. Go to the Certificates area, then go to “All”, and click the plus button

    apple-id-certs-area.png sidebar-all-certs.png new-cert-btn.png
  4. Choose the correct certificate for push, highlighted in green: correct-selection.png

  5. Go through the process of acquiring the certificate

    request-csr.png request-csr-2.png download-cert.png
  6. Locate the certificate and keypair, and export it as p12.

    locate-cert.png export-cert.png export-name-cert.png
  7. Convert the p12 file to a pem file:

    1
    2
    3
    [keyvan@airframe ~]$ openssl pkcs12 -in ~/Desktop/Certificates.p12 -out apns.pem -nodes -clcerts
    Enter Import Password:
    MAC verified OK
  8. Configure Sygnal to use this pem file:

    1
    2
    3
    4
    [apps]
    pw.keyvan.riot.type = apns
    pw.keyvan.riot.platform = sandbox
    pw.keyvan.riot.certfile = /mnt/storage/apns.pem

Notes

You may want to do some maintenance on existing pushers in your Synapse database.

To list existing pusher entries:

1
select * from pushers;

To delete any existing pusher entries:

1
delete from pushers;

A simple React pagination component

I had to do pagination in a react app today. Often times for things like this, it’s easier to write your own thing than to use a library. Here’s the Pager component I ended up with:

Pager.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import React from 'react';
export const Pager = React.createClass({
render: function() {
const {
perPage,
totalRows,
pageNumber,
lastPageNumber,
query
} = this.props;
const checkDisabled = (change) => {
let newPage = pageNumber+change;
if ( newPage < 1 ) return true;
if ( newPage > lastPageNumber ) return true;
return false
}
const jumpToPage = (pageNo) => query({ perPage, showPageNumber: pageNo });
const changePage = (change) => (event) => jumpToPage(pageNumber+change);
return <span>
<span className="total">
total {totalRows} #{((pageNumber-1)*perPage) + 1}
</span>
<span className="pager">
<button disabled={checkDisabled(-1)} onClick={changePage(-1)}>prev</button>
<select value={pageNumber} onChange={({target:{value}})=>jumpToPage(value)}>
{ Array(lastPageNumber).fill().map((_,i)=><option key={i} value={i+1}>
page: {i+1}/{lastPageNumber}
</option>)}
</select>
<button disabled={checkDisabled(1)} onClick={changePage(1)}>next</button>
</span>
</span>;
}
})

This pager component calls the query prop function (a redux action, say) in response to previous and next buttons, and direct page selection, with an object like so { perPage: 10, showPageNumber: 1 }

Naturally, this being react, how this is used and what query does is out of scope, unknown and irrelevant, but here’s a little screenshot of the UI in which this is being used, for kicks:

pager screenshot

Chicken breast and red potatoes single oven recipe

yield:

chicken breast and potatoes

ingredients:

  • 2 half chicken breasts
  • 1/4 cup butter
  • 6 cloves crushed garlic
  • 2 cups seasoned dry bread crumbs
  • 8 red potatoes

directions:

  • Preheat oven to 400 degrees F
  • prep chicken
    • In a small saucepan melt butter/margarine with garlic. Dip chicken pieces in butter/garlic sauce, letting extra drip off, then coat completely with bread crumbs.
    • Place coated chicken in a lightly greased baking dish. Combine any leftover butter/garlic sauce with bread crumbs and spoon mixture over chicken pieces.
  • prep potatoes
    • Toss potatoes with oil, salt and pepper. Arrange, cut side down, on a large lipped cookie sheet or jellyroll pan. tossed potatoes
  • Put the potatoes on the lowest rack, and chicken around half-way. Bake for 45 minutes. chicken mid and potatoes low

notes:

came out great! the oven configuration and timing seemed perfect.

the chicken was boring in the middle because of no marination, also i think the breading was pretty unnecessary / hid the taste of the garlic butter.

the potatoes were pretty amazing, especially how they became crispy on the bottoms

sources:

Homemade Mirin Recipe

yield: 1/2 cup

ingredients:

  • 5 tablespoons (65 g) sugar, such as organic cane sugar
  • 1/2 cup (120 ml) sake
  • 1 1/2 teaspoons pure cane syrup, such as Steen’s (optional)

directions:

  1. Combine the ingredients in a very small saucepan, such as butter warmer/melter. Bring to boil over medium heat, give things a stir to ensure the sugar has dissolved.
  2. Remove from the heat and set aside to cool. Taste and add the cane syrup for depth, if you like.

source: http://www.vietworldkitchen.com/blog/2013/09/homemade-mirin-recipe.html

Yakitori Recipe

yield: 1 cup

ingredients:

  • 1/2 cups soy sauce
  • 1/4 cups sugar
  • 1/2 cups mirin
  • 1/4 cups sake
  • 1 garlic cloves crushed
  • 1 slices fresh ginger peeled 1/8 inch thick
  • 1 tablespoons water
  • 1 tablespoons cornstarch

directions:

  1. In a small saucepan, combine soy sauce, sugar, mirin, sake, garlic and gingerroot.
  2. Cook over medium high heat 3 to 4 minutes.
  3. In a small bowl, blend water and cornstarch.
  4. Stir constarch mixture into soy sauce mixture.
  5. Cook until thickened, stirring constantly.
  6. Strain sauce.
  7. Keep at room temperature for up to 24 hours.
  8. Refrigerate.

source: http://www.food.com/recipe/yakitori-sauce-344705