A Webikwa blog about tech

Featured Articles

I plan to add to add to this post as I learn more. Signal a messaging app similar to Whatsapp and Facebook Messenger. I like Signal. Signal has a strong emphasis on security and no ads. Unlike other chat providers, Signal doesn't sell, store, or even see your data. Signal is run by a non-profit organization, and they make money through donations, not by exploiting their users.

Because of its emphasis on security and because of the lack of commercial pressure to make it addictive, its not as exciting as its competitors. But it's still more fun to use than regular text and Signal makes it easier to keep your conversations organized. And your inbox wont be flooded with messages from people you don't know.

Regarding Signal's Security

Signal has excellent encryption so I don't worry about having my messages intercepted in transit by some electronic monitoring tool, and Signal does not store messages on any central system so I don't worry about Signal's servers getting hacked.

But as with any secure tool, the weakest link is the users. It's easy for someone to invite people into chats who shouldn't be in those chats, and unless messages are set to disappear, a lost or unattended phone can put a bounty of secret messages into the wrong hands.

Nobody can sneak into a group uninvited, but the danger is a member of the group can invite someone they shouldn't. Signal protects the privacy of its users and unless the user adds information to their profile, they might just be represented by a first name or nickname, or even just a set of initials. So I might have two people in my contacts named Sally, but I might not have a phone number or other information to tell me which Sally is my coworker and which Sally is my sister-in-law. So if I'm not careful, I might invite my sister-in-law to a conversation about my company's plan to acquire a competitor, or I might invite my coworker to conversation about my brother's drinking problem

Diligence is the best defense against wrongfully invited members. Be careful when adding members to a group and if you see a member who you're not sure about, double check who they are with someone you know

Disappearing messages are the best defense against stolen information. Use disappearing messages when security is more important than the convenience of being able to refer to a message later. In most cases, I want to be able to look back over messages, so I don't usually set that option. But that means anything I send could end up on a phone that gets stolen.

My biggest annoyance with Signal is it doesn't log you out after a period of inactivity. In fact there isn't even a simple way to log out manually (* more details below). I have a password manager, Bitwarden, that I have to keep logging into. If someone steals my phone or if it's confiscated by an abusive federal police force operating with little regard for the Constitution, they probably won't get my passwords. But if they get past my phone's lock screen, they'll have my Signal messages. That's because technically you're not really logged in. You just registered your phone. But I still think they can do better

Because of those concerns, I don't use Signal for discussing operations in which my disregard for written and sensible rules might get my coworkers killed or compromise national security. But for most things I'm likely to discuss, Signal is good enough.

Signal vs Text

Signal works over an Internet connection, like most messaging systems that your probably familiar with but not like standard text messaging which works over your mobile phone network. So while generally you can send a regular text anywhere you can make a cell phone call, you can send a Signal message anywhere you can send an email. With mobile data (Internet through you cell connection) becoming more and more available, that difference is becoming less and less important

Text messaging doesn't support groups. See below where I discuss muting.

Some Tips

These tips address some issues I've come accross or are in reponse to things my friends or coworkers have said, so this is not like a universally applicable FAQ but may have some useful information for anyone who stumbles across this post.

I can't do better than Signal's own website for explaining how to get started. Start there and come back here for the possibility that I've written something useful. Maybe there's something here that can provide help if you need.

Ignore Spammy Message Requests

People you don't know can't bombard you with messages but they can do the next best thing which is to bombard you with message requests. Within a half hour of installing Signal on my phone for the first time, I got a message request from someone who used a girl's name (Candy or something like that). Don't feel compelled to respond to message requests.

Create Groups as Needed

Create new groups as needed for specific conversations. So if you're a member of an event planning committee and you are currently planning a specific event, create a smaller group for that event. Doing that:

  • Reduces the possibility of unwanted members listening in to your conversation;
  • Provides the ability to add a member to the specific group who shouldn't be a member of the committee;
  • Keeps conversations organized the way emails threads are organized by subject

Use the Mute Option

The head of an organization that I'm a member of said she was getting too many notifications. I think muting is the answer.

Muting is one of the reasons I think groups are important: Say you have a conversation with a dozen friends about something important - like an upcoming visit from a VIP. Now say you also have a conversation with the same people in which your sharing cat pics.

As much as you like cats, you might not want to be buzzed every time your friend sends a cat photo. So you mute the cat group and check on the cats periodically. But you leave the VIP group unmuted so you can get immediate updates. You can't do that with text messaging - at least not with general text messaging apps. You either mute conversations that include those same contacts or not

Signal's PIN

Someone asked about Signal's PIN. Since Signal doesn't log you out, the PIN isn't needed to log in each time. But it's necessary to prevent data loss in case you reinstall Signal, and I think it might be necessary if you don't use signal for so long - could be months - that it eventually logs you out.

If you forgot your PIN, you can set a new one through your account settings

Support Signal

Providing a service costs money. Not exploiting you user's data or manipulating their intentions requires some other means of revenue. The Signal Foundation relies on donations, so please consider supporting them

Elaboration about inability to log out

The reason you can't log out of Signal is you're not logged in to Signal. When you registered you phone with Signal you authorized Signal to send messages to your phone. But there is no Signal username or password. You can't pick up another phone and log into Signal to see your messages. Messages aren't stored at Signal.org. You can't download them. Since you're not logged in, you can't log out. But I still think the Signal foundation can do better to improve projections against a stolen phone because relying on the phone's screen lock seems pretty weak

Acknowledgement and Starting Point

This post is about steps I took to deploy a Django project to a VPS running Debian 12. I used Putty on a Windows 11 laptop to connect to the VPS. My Django project is on the laptop for development.

This post is mostly my notes on Deploying Django on a single Debian or Ubuntu Server by Antonis Christofides. That book has been a guide to me and I keep refering to it. But here I've added notes about steps that you may do differently, in some cases must do differently because the book is a few years old. I recommend following Mr. Christofides's book and looking to this post for additional information that might be helpful. That's especially true if you're setting up your VPS from scratch, because Chapters 1 and 2 have a lot of information about finding a VPS provider that I won't be touching here

The book uses placeholders $DJANGO_PROJECT for the project name and $DJANGO_USER for the user name, noting that they're usually the same but can be different. In this post, I have more placeholders and instead of the $SOMETHING format, my placeholders will look more like benvillesomething.

Some of my placeholders will be:

  • benvillelibrarysite for the project name, the project root directory, and other directories named after the project
  • benvillelibrary for the project package, sometimes called the settings directory, which usually has the same name as the project name but can be different
  • benvilleus for the user account under which the project runs
  • benvilleadmin for the user who you are logged in as when you create and edit this project if you're not logging in as root

Plus a couple more. The first three names can all be the same in your setup

Users

Mr. Christofides strongly recommends using ssh keys, but there are some cases where you must or prefer to stick with user names and passwords. I'm not recommending one over the other. If you stick with passwords, you should create a user with sudo privileges and disable ssh for root. For the project I'm working on as I write this post, I'm logging in as a sudo-enabled user. Most of my examples will reflect that. I also added two-factor authentication.

There are several tutorials online for creating a sudo-enabled user and disabling ssh for root on Debian systems, for example How-To Geek's. Here's how I do it:

  • Using an ssh client like Putty, log in as root
    • Create a new user for yourself
      adduser benvilleadmin
    • Add that new user to sudoers
      usermod -aG sudo benvilleadmin
  • Stay logged in as root. Open a separate ssh instance and log in as benvilleadmin
    • Test benvilleadmin's sudo privileges
      sudo ls
    • edit /etc/ssh/sshd_config to change PermitRootLogin from 'yes' to 'no'
      sudo nano /etc/ssh/sshd_config
      In the Nano editor,
      • Search for PermitRootLogin by pressing Ctrl-w.
      • Change PermitRootLogin from 'yes' to 'no'
      • Press ctrl-x to exit, then press 'y' to confirm that you want to save
      • Hit 'enter' to confirm saving to /etc/ssh/sshd_config
    • Restart the ssh daemon.
      sudo systemctl restart ssh
  • Log out as root, and try logging back in as root to confirm that root login is disabled

Two Factor Authentication

For the project I'm working on, I'm using passwords and 2FA

2FA works with an app that has to be set up on a device such as your phone. I use Bitwarden which also has a Windows desktop app.

To enable 2fa, I installed libpam-google-authenticator

sudo apt install -y libpam-google-authenticator

For those suspicious of Google, Linux Babe tells us that the authentication process doesn't send anything to Google - they just wrote the open-source software

The next step will display a QR code in your terminal.

google-authenticator

I was suprized the first time I did this to see the QR code pop up in a console. Your authenticator app should have an option somewhere that allows you to scan a code to set up TOTP (Time-based One Time Password). If the code is too big for you to scan and you're using Putty, you can make your font smaller by following these steps:

  • Ctrl-right-click in the terminal window
  • Choose "Change Settings"
  • Choose "Appearance" in the menu under "Window"
  • Click "Change" next to the font display
  • Select a smaller font size and click "Ok"
  • Click "Apply"

If you can't scan the code, your app should also have an option to enter the secret key manually

I suggest continuing with Linux Babe's instructions, although there are others you can find.

I also suggest staying logged in with one ssh window to make changes and log in with a separate window to test.

I also recommend learning on a VPS that you can painlessly rebuild in case you lock yourself out. If you can't or don't want to rebuild, your VPS provider probably has a way for you to log in with an admin console from their website

Project System User

Logged in as benvilleadmin, I created benvilleus with the following command:

sudo adduser --system --home=/var/opt/benvillelibrarysite --disabled-password --group --shell=/bin/bash benvilleus

If I were logged in as root with ssh keys, the command would be the same except without sudo.

As the book says, it doesn't matter much what the home directory is so you're free to give it a different name than benvillelibrarysite, or even place it somewhere other than /var/opt/. You can also skip the --home option and leave the home directory as non-existant. In most cases sticking with --home=/var/opt/benvillelibrarysite is the best choice

Mr. Christofides's example includes "no-create-home", because he covered creating the directory separately, but I didn't bother with that so I let the system create the directory for me

Directories

Chapter 3 of Deploying Django on a single Debian or Ubuntu Server provides good information about the directory structure of a Linux machine and how that structure relates to the software people develop and install.

On my laptop, my project structure is something like what's below. I made some changes for clarity. I also included additional apps (touglates, wibekwa, and wibekwa_base) that I added to the project I'm working on. I won't discuss those apps in this post but I included them in the structure so you can see where the apps that you might be working on would be

My development structure is:

benvillelibrarysite/
    benvillelibrary/  
        settings/
            base.py
            dev.py
        static/
        templates/
        urls.py
        wsgi.py
    media/
    touglates/
    venv/
    wibekwa/
    wibekwa_base/
    db.sqlite3
    manage.py
    requirements.txt

On the production server, the files and directories look something like this

/etc/opt/benvillelibrarysite/             /etc/opt is for settings specific to the app on this server
    static/
    settings/
        local.py
/opt/benvillelibrarysite/                 /opt is for applications not part of the operating system
    benvillelibrary/  
        settings/
            base.py
            production.py
        static/
        templates/
        urls.py
        wsgi.py
    touglates/
    venv/
    wibekwa/
    wibekwa_base/
    manage.py
    requirements.txt
/var/opt/benvillelibrarysite/            /var is for data that changes as the app is used
    benvillelibrarysite.db
    media/

Project Root Directory

The project root directory holds the program files. I created the project root directory /opt/benvillelibrarysite

sudo mkdir /opt/benvillelibrarysite

Then, as the book says, I "clone or otherwise copy" my Django project into the new directory.
For me, "clone or otherwise copy" meant a combination of using sftp to upload from my laptop, cloning apps from my repositories, and creating or recreating some of files.

I used Filezilla (but other sftp clients, like Win-SCP are fine) to upload requirements.txt, manage.py, and the django package directory

In my case, with root disabled, I can't sftp directly to /opt/benvillelibrarysite/, so I created the directory /home/benvilleadmin/transfer/, and uploaded to that directory.

Then with Putty I copied the files and directories from the transfer directory to the project root directory.

sudo cp /home/benvilleadmin/transfer/* /opt/benvillelibrarysite/

I added some of the apps that I'm using for this project by cloning them from Github

From inside the project root directory:

sudo git clone https://github.com/tougshire/wibekwa
sudo git clone https://github.com/tougshire/wibekwa_base
sudo git clone https://github.com/tougshire/touglates

Virtualenv

The next step is setting up the virtual environment. The following code block from the book combines two commands:

virtualenv --system-site-packages --python=/usr/bin/python3 \\
    /opt/$DJANGO_PROJECT/venv
/opt/$DJANGO_PROJECT/venv/bin/pip install \\
    -r /opt/$DJANGO_PROJECT/requirements.txt

The first command is

virtualenv --system-site-packages --python=/usr/bin/python3 \\
    /opt/$DJANGO_PROJECT/venv

If your system doesn't have virtualenv installed, install it with sudo apt install virtualenv

For my set up, the first command translates to

sudo virtualenv --system-site-packages --python=/usr/bin/python3 /opt/benvillelibrarysite/venv

The above created the virtual environment /opt/benvillelibrarysite/venv

On my laptop, I had previously created requirements.txt using pip freeze. To do this, I called the following command with the virtual environment activated from the development project root directory:

pip freeze > requirements.txt

During develpment, you almost certainly issued commands from within an activated virtual environment, but you don't have to activate the environment before running these commands on the server

With requirements.txt uploaded to the server, the next command installs the same packages on the server that you had in development

In the book, the command is

/opt/$DJANGO_PROJECT/venv/bin/pip install \\
    -r /opt/$DJANGO_PROJECT/requirements.txt

For my project, that translates to

sudo /opt/benvillelibrarysite/venv/bin/pip install \\-r /opt/benvillelibrarysite/requirements.txt

' Then I compiled the project

sudo /opt/benvillelibrarysite/venv/bin/python -m compileall -x /opt/benvillelibrarysite/venv/ /opt/benvillelibrarysite

The Data Directory and the Log Directory

Following the book, you would create the data directory at this point but I already created /var/opt/benvillelibrarysite when I created benvilleus.

I created the log directory as per section 3.4

sudo mkdir -p /var/log/benvillelibrarysite
sudo chown benvilleus /var/log/benvillelibrarysite

The Production Settings Directory

As noted, on Debian based systems, configuration settings for programs that are installed in /opt/ go in /etc/opt/

sudo mkdir /etc/opt/benvillelibrarysite

I created /etc/opt/benvillelibrarysite/settings.py and edited it as follows:

from benvillelibrary.settings import *

DEBUG = True
ALLOWED_HOSTS = ['benvillelibrary.gov', 'www.benvillelibrary.gov']
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': '/var/opt/benvillelibrarysite/benvillelibrarysite.db',
    }
}

Then I set the permissions as below.

sudo chgrp benvilleus /etc/opt/benvillelibrarysite
sudo chmod u=rwx,g=rx,o= /etc/opt/benvillelibrarysite

Then I compiled the files

sudo /opt/benvillelibrarysite/venv/bin/python -m compileall /etc/opt/benvillelibrarysite

With all of this set up, I tested the server, as per Section 3.7 near the end

sudo -u benvilleus PYTHONPATH=/etc/opt/benvillelibrarysite:/opt/benvillelibrarysite \\
DJANGO_SETTINGS_MODULE=settings \\
    /opt/benvillelibrarysite/venv/bin/python \\
    /opt/benvillelibrarysite/manage.py \\
    runserver 0.0.0.0:8000

The book notes that you can use --settings instead of DJANGO_SETTINGS_MODULE but I prefer DJANGO_SETTINGS_MODULE because it's clearer to me.

I'm using sudo -u benvilleus to become benvilleus for this command. Sudo without -u defaults to root.

Since I am using sudo, I'm not using su. So instead of

su - c "/opt/benvillelibrarysite ..."

I used

/opt/benvillelibrarysite ...

I changed directory to /opt/benvillelibrarysite before running the commands because I can't run the them from benvilleadmin's home dictory

The web server

I followed Chapter 4, using Apache because I'm more familiar with Apache than with Nginx.

But one big thing I did, which isn't in the book, is I added ssl with Certbot.

Ssl changes your protocol from http to https (https://benvillelibrary.gov). Without ssl, your visiters may get security warnings.

You can follow the first sections and stop at 4.3 for Nginx or 4.6 for Apache, install the certificate as per my instructions below, and then continue where you stopped

To enable ssl, you need a certificate, and one way to obtain a certificate is with Certbot.

For those who have used Certbot before, you may have installed it with

sudo apt install certbot  #<-- no longer recommended

but Certbot now "strongly recommends" installing with Snap. Since Snap wasn't preinstalled on Debian 12 minimal, I installed it following the instructions at snapcraft.io/docs/installing-snap-on-debian, which are

sudo apt update
sudo apt install snapd

with Snap installed, I installed Certbot in accordance with their instructions at certbot.eff.org/instructions?ws=apache&os=snap with

sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

And ran it with

sudo certbot --apache

(certbot also has a --nginx option)

From there I followed Certbot's instructions, choosing to add a redirect so when visitors request http://benvillelibrary.gov they automatically get https://benvillelibrary.gov

As per the book, I already created a config file named /etc/apache2/sites-available/benvillelibrary.gov.conf. Certbot created a new file named /etc/apache2/sites-available/benvillelibrary.gov-le-ssl.conf, and added a rewrite in the first file to redirect to the second

At this point, the only directives in the first conf file that are important are the server name, server aliases, and the rewrite directives. So any edits the book says to make in bevillelibrary.gov.conf should be made in benvillelibrary.gov-le-ssl.conf instead.

Static and Media Files

I followed Chapter 5 pretty much as described. The collectstatic command looks like this for me

sudo PYTHONPATH=/etc/opt/benvillelibrarysite:/opt/benvillelibrarysite \\
    DJANGO_SETTINGS_MODULE=settings \\
    /opt/benvillelibrarysite/venv/bin/python \\
    /opt/benvillelibrarysite/manage.py collectstatic

Where the book says to go to http://$DOMAIN/static/admin/img/icon_searchbox.png use https://$DOMAIN/static/admin/img/search.svg (i.e. https://benvillelibrary.gov/static/admin/img/search.svg) instead because
the latest Django uses a different set of images. Also http is changed to https on the assumption that you've enabled ssl

Gunicorn

Chapter 6 says to install Gunicorn with Pip instead of Apt because the Debian packaged Gunicorn only supports Python 2.
The part about Python 2 is no longer true, but it's still best to install Gunicorn into the virtual environment with Pip.

sudo /opt/benvillelibrarysite/venv/bin/pip install gunicorn

If you already installed Gunicorn on your system (outside the virtual environment), you'll probably get a message saying Gunicorn is already installed - and it won't let you go forward. You can override that check with -I

sudo /opt/benvillelibrarysite/venv/bin/pip install -I gunicorn

See the Gunicorn docs page Deploying Gunicorn

My version of the command to run Gunicorn from the command line is

sudo -u benvilleus PYTHONPATH=/etc/opt/benvillelibrarysite:/opt/benvillelibrarysite \\
    DJANGO_SETTINGS_MODULE=settings \\
    /opt/benvillelibrarysite/venv/bin/gunicorn \\
    benvillelibrary.wsgi:application

Systemd

Section 6.3 of the book is configuring systemd. This is how to get the project running when the server starts without you having to start it from the command line. The remaining sections of Chapter 6 cover optimizing. I don't have much to add to Chapter 6 except for the 2 core VPS that I'm using as I write these instructions, I increased my workers to 8. I can probably go higher, but I don't expect the project I'm working on to have a lot of visiters

Mail

Mr. Christofides recommended a mail transport agent called Dragonfly Mail Agent, or dma. I couldn't get dma to work. I was able to draft the test message but dma never sent the email, didn't create a log, and didn't report any errors. I'll try on a differnet vps sometime and maybe update this post, but for project I'm working on as I write this post, I used Postfix

sudo apt install postfix

Where the Postfix installation wizard asked for type of configuration, I chose "Internet Site". Where it asked for the system mail name, I entered "mailer.benvillelibrary.gov".

Then I edited /etc/postfix/main.cf as follows:

  • Find or create the myhostname entry and set it to something like
    myhostname = mailer.benvillelibrary.gov
    You can use something other than "mailer."
  • Find or edit relayhost as follows:
    relayhost = [$EMAIL_HOST]:587
    Where $EMAIL_HOST is the host name provided by your smarthost. In the book this would be mail.runbox.com. I'm using Brevo for my project so the one I used is smtp-relay.brevo.com. I'll be using Brevo for the remaining examples.
  • Add the following:
    smtp_sasl_auth_enable = yes
    smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
  • Create the password file hash:/etc/postfix/sasl_passwd and enter a line like this:
    [smtp-relay.brevo.com]:587 admin@benvillelibrary.gov:S3cretPa$$w0rd

After making the above edits, I mapped the password file to a database with:

sudo postmap /etc/postfix/sasl_passwd

Then I restarted Postfix with

sudo systemctl stop postfix
sudo systemctl start postfix

I added the following lines to /etc/opt/benvillelibrarysite/settings.py:

SERVER_EMAIL = 'info@benvillelibrary.gov'  
DEFAULT_FROM_EMAIL = 'info@benvillelibrary.gov'  
ADMINS = [
    ('administrator', 'admin@benvillelibrary.gov'),
]
MANAGERS = ADMINS

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'localhost'
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''
EMAIL_PORT = 25
EMAIL_USE_TLS = False

Then restarted the project with

sudo service benvillelibrarysite stop
sudo service benvillelibrarysite start

I checked Django's ability to send an email through Postfix by running

sudo -u benvilleus PYTHONPATH=/etc/opt/benvillelibrarysite:/opt/benvillelibrarysite \\
DJANGO_SETTINGS_MODULE=settings \\
/opt/benvillelibrarysite/venv/bin/python \\
/opt/benvillelibrarysite/manage.py shell

and with the shell active:

from django.conf import settings
from django.core.mail import send_mail

admin_emails = [x[1] for x in settings.ADMINS]
send_mail("Test", "This is a  test", settings.SERVER_EMAIL,
          admin_emails)

More to come

I'll add more to this post but Mr. Christofides's book is pretty complete and you can probably just continue with that from here.