Skip to content

Latest commit

 

History

History
1398 lines (975 loc) · 51.5 KB

install.linode.ubuntu.20.04.md

File metadata and controls

1398 lines (975 loc) · 51.5 KB

Installing Ubuntu 20.04 on Linode

This is based off a 12.04 reference image which I upgraded to 14.04, 16.04, 18.04, and then to 20.04.

I picked Newark for the location.

  • I picked 2048MB swap and used the rest for the single server

  • Don't forget to configure DNS

  • DNS servers:

    • ns1.linode.com
    • ns2.linode.com
    • ns3.linode.com
    • ns4.linode.com
    • ns5.linode.com
  • And make sure to add an SPF record - it's a text record. By default, it's just:

    v=spf1 mx ~all
    
    • Which says "accept mail from any servers which have an a record, and if it's not, soft fail it."
  1. Updates!

     sudo apt update
     sudo apt dist-upgrade
    
  2. Make accounts

     adduser matt
     usermod -a -G sudo,adm matt
     adduser liz
     usermod -a -G sudo,adm liz
    

    and for the boys' emails:

     adduser miles18
     usermod -L miles18
     adduser max18
     usermod -L max18
    
  3. Install users' authorized_keys files in to ~/.ssh

  4. Set up ssh

    1. For an old machine, use the old keys - you did save /etc, didn't you?

    2. For a new machine, use the new keys generated by the distro.

    3. make sure to add to the firewall

      ufw allow ssh
      
    4. In /etc/ssh/ssh_config, set:

      PermitRootLogin forced-commands-only
      

      (the forced commands only is so we can run backups) and set

      PasswordAuthentication no
      
    5. restart it

      service ssh restart
      
  5. Enable firewall

    sudo ufw enable
    
  6. Set the hostname:

    1. Edit /etc/hostname and set it to linode

    2. Edit /etc/hosts and set the 127.0.1.1 line to look like:

      127.0.1.1       linode
      

    This will prevent bounce messages from some mailservers, since they rely on the host name that the server claims to be (which is gotten from /etc/hostname) and then try to reverse that and make sure they're the same.

  7. Install useful things

    sudo apt install tree emacs-nox git software-properties-common snapd iftop htop
    
  8. Set up letsencrypt certbot

    1. Install certbot snap

      sudo snap install --classic certbot
      
    2. Set it up semi-manually:

      sudo certbot --standalone certonly -d mattcaron.net,www.mattcaron.net
      sudo certbot --standalone certonly -d owncloud.mattcaron.net
      sudo certbot --standalone certonly -d sympa.mattcaron.net
      sudo certbot --standalone certonly -d mail.mattcaron.net
      sudo certbot --standalone certonly -d lists.pfmbonsai.com
      sudo certbot --standalone certonly -d pfmbonsai.com
      sudo certbot --standalone certonly -d school.mattcaron.net
      sudo certbot --standalone certonly -d chat.mattcaron.net
      sudo certbot --standalone certonly -d video.mattcaron.net
      
    3. Make the directories in /etc group accessible by ssl-cert and make the gid sticky

      sudo chgrp -R ssl-cert /etc/letsencrypt
      sudo chmod -R g+rsX /etc/letsencrypt
      
    4. Move everything in /etc/ssl/private to old, and then make new symlinks to the things in /etc/letsencrypt.

    5. Add create /etc/cron.d/letsencrypt thusly:

      # /etc/cron.d/letsencrypt: crontab entries to check for new certs
      
      PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/snap/bin
      MAILTO=root
      
      37 01 * * *     root     service apache2 stop; certbot renew; chmod -R g+r /etc/letsencrypt; service apache2 start; service dovecot reload; service exim4 reload
      

      Note that:

      • we restart services
      • we fix up the permissions to be group readable, because new files will be created as 0600.
  9. Install apache

    1. Install packages:

      sudo apt install apache2 libapache2-mod-php php php-cli php-pear php-db php-mysql mysql-server php-apcu
      
    2. Enable the userdir and rewrite modules

      sudo a2enmod userdir
      sudo a2enmod rewrite
      sudo a2enmod ssl
      sudo a2enmod php7.4
      
    3. Fix up the php configuration by editing /etc/apache2/mods-available/php7.4.conf and commenting out this bit:

      #    <IfModule mod_userdir.c>
      #        <Directory /home/*/public_html>
      #            php_admin_value engine Off
      #        </Directory>
      #    </IfModule>
      
    4. Restart

      sudo service apache2 restart
      
    5. Go through this and apply all settings:

    6. Allow it through the firewall

       sudo ufw allow http
       sudo ufw allow https
      
  10. Install dovecot (imap)

    1. Install it:

      sudo apt install dovecot-imapd

    2. Configure it:

      1. Edit /etc/dovecot/conf.d/10-master.conf and:

        1. find the inet_listener imaps line, and uncomment the body.

        2. find the service auth section and add to the bottom:

          #SASL                                                                         
          unix_listener auth-client {
            mode = 0600
            user = Debian-exim
          }
          
      2. Edit /etc/dovecot/conf.d/10-mail.conf and find the mail_location line, uncomment it and set it to:

        mail_location = maildir:/home/%u/Maildir
        
      3. Edit /etc/dovecot/conf.d/10-ssl.conf and:

        1. Change it to use the mattcaron.net cert:

          ssl_cert = </etc/ssl/private/mail.mattcaron.net/fullchain.pem
          ssl_key = </etc/ssl/private/mail.mattcaron.net/privkey.pem
          
        2. Change ssl to "required"

          ssl = required
          
      4. Edit /etc/dovecot/conf.d/20-imap.conf and set:

           mail_max_userip_connections = 100
        

        (because I have a ton of machines that poll for email behind a NAT)

      5. Edit /etc/dovecot/conf.d/15-lda.conf and set:

        postmaster_address = postmaster
        

        Yes, this is kind of stupid, especially in light of the comment in the config preceding this line, but I've been getting errors about how it's not set, to just explicitly set it to the sane default.

    3. Pre-create the maildir for new users

      sudo maildirmake.dovecot /etc/skel/Maildir
      sudo maildirmake.dovecot /etc/skel/Maildir/.Drafts
      sudo maildirmake.dovecot /etc/skel/Maildir/.Sent
      sudo maildirmake.dovecot /etc/skel/Maildir/.Trash
      sudo maildirmake.dovecot /etc/skel/Maildir/.Templates
      sudo maildirmake.dovecot /etc/skel/Maildir/.Junk
      
    4. Set it up for any existing users:

      sudo cp -r /etc/skel/Maildir /home/myuser/
      sudo chown -R myuser:usergroup /home/myuser/Maildir
      sudo chmod -R 700 /home/myuser/Maildir
      
    5. Allow it through the firewall

      sudo ufw allow imaps
      
  11. Exim

    1. Install it

      sudo apt install exim4-daemon-heavy exim4
      
    2. Find the default exim configuration file (called configure.default, and found in the src/ directory of the source code and modify it as follows:

      primary_hostname = mattcaron.net
      domainlist local_domains = mattcaron.net : pfmbonsai.com
      domainlist relay_to_domains = hostlist
      relay_from_hosts = localhost
      tls_advertise_hosts = *
      tls_privatekey = /etc/ssl/private/mail.mattcaron.net/privkey.pem
      daemon_smtp_ports = 25 : 465
      tls_on_connect_ports = 465
      qualify_domain = mattcaron.net
      auth_advertise_hosts = ${if eq {$tls_cipher}{}{}{*}}
      
      1. Find the system_aliases router and change the line:

        data = ${lookup{$local_part}lsearch{SYSTEM_ALIASES_FILE}}
        

        to:

        data = ${lookup{$local_part}lsearch{/etc/aliases}}
        

        Ref: https://github.com/Exim/exim/wiki/AuthenticatedSmtpUsingPam

      2. At the bottom of the main section (before ACL CONFIGURATION), add:

        # Only allow auth over TLS, otherwise folks would be sending plaintext passwords
        auth_advertise_hosts = ${if eq {$tls_cipher}{}{}{*}}
        

        Ref: http://wiki2.dovecot.org/HowTo/EximAndDovecotSASL

      3. Down at the bottom, at the end of the AUTHENTICATION CONFIGURATION, add:

        dovecot_plain:
          driver = dovecot
          public_name = PLAIN
          server_socket = /var/run/dovecot/auth-client
          server_set_id = $auth1
        

        Note that we don't use the default debian config, as it is annoying.

    3. Then copy the modified file to /etc/exim4/exim4.conf on the remote server. Make sure it's group readable and growned by Debian-exim.

    4. Make the Debian-exim user a member of the shadow group so it can read /etc/shadow and therefore do authentication. Also, the ssl-cert group, so it can read certs.

      sudo usermod -a -G ssl-cert Debian-exim
      sudo usermod -a -G shadow Debian-exim
      
    5. Make a pam config for it - we'll just piggyback on the dovecot one, as it's reasonable and similar

      cd /etc/pam.d
      sudo ln -s dovecot exim4
      
    6. Allow through firewall

      sudo ufw allow smtp
      sudo ufw allow ssmtp
      
  12. Integrate exim with dovecot

    1. Edit /etc/exim4/exim4.conf

      1. At the bottom of the routers section, add the following (or change existing to look the same):

        localuser:
          driver = accept
          check_local_user
          # local_part_suffix = +* : -*
          # local_part_suffix_optional
          transport = dovecot_delivery
          cannot_route_message = Unknown user
        
      2. Create a new transport for dovecot-lda:

        dovecot_delivery:
          driver = pipe
            # You may or may not want to add -d $local_part@$domain depending on if
            # you need a userdb lookup done.
            command = /usr/lib/dovecot/dovecot-lda -f $sender_address
            message_prefix =
            message_suffix =
            log_output
            delivery_date_add
            envelope_to_add
            return_path_add
            #group = mail
            #mode = 0660
            temp_errors = 64 : 69 : 70: 71 : 72 : 73 : 74 : 75 : 78
        
      3. Create a new transport for dovecot-lda - spammy version

        dovecot_spam_junk_delivery:
          driver = pipe
          # You may or may not want to add -d $local_part@$domain depending on if
          # you need a userdb lookup done.
          command = /usr/lib/dovecot/dovecot-lda -f $sender_address -m Junk
          message_prefix =
          message_suffix =
          log_output
          delivery_date_add
          envelope_to_add
          return_path_add
          #group = mail
          #mode = 0660
          temp_errors = 64 : 69 : 70: 71 : 72 : 73 : 74 : 75 : 78
        
  13. Set up DKIM signing (because, you know, spammers won't sign messages or spoofing is a problem or, something..)

    1. Make some directories to hold things:

      sudo mkdir /etc/exim4/dkim
      cd /etc/exim4/dkim
      
    2. Generate keys for each domain:

      sudo openssl genrsa -out mattcaron.net.private.pem 2048 -outform PEM
      sudo openssl rsa -in mattcaron.net.private.pem -out mattcaron.net.pem -pubout -outform PEM
      
      sudo openssl genrsa -out pfmbonsai.com.private.pem 2048 -outform PEM
      sudo openssl rsa -in pfmbonsai.com.private.pem -out pfmbonsai.com.pem -pubout -outform PEM
      
    3. Publish the public keys in DNS using the date as the selector.

      1. IMPORTANT - my current exim config exim uses the same selector (date) for ALL domains, so they all need to match.
    4. Fix perms on generated files:

      sudo chown -R Debian-exim:Debian-exim /etc/exim4/dkim
      sudo chmod -R go-rwx /etc/exim4/dkim
      
    5. In /etc/exim4/exim4.conf:

      1. Cchange the remote_smtp section to be like this:

        remote_smtp:
          driver = smtp
          dkim_canon = relaxed
          dkim_selector = 20151029
          dkim_domain = ${sg{${lc:${domain:$h_from:}}}{^www\.}{}}
          dkim_private_key = ${if exists{/etc/exim4/dkim/${dkim_domain}.private.pem}{/etc/exim4/dkim/${dkim_domain}.private.pem}{0}}
        
      2. Allow remote MUA's to set the sender for the envelope. This stops exim from stripping it and forcing it to be the canonical domain (so we can support multiple virtual domains). Anyway, go to the end of the Main Configuration section, right above ACL CONFIGURATION and add:

        local_sender_retain = true
        local_from_check = false
        
  14. Configure the time zone:

    sudo dpkg-reconfigure tzdata
    

    and set it to America/New York

  15. Add spamassassin and other optional dependencies:

    1. install

      sudo apt install spamassassin libdigest-sha-perl libgeo-ip-perl libio-socket-ip-perl libencode-detect-perl libnet-patricia-perl libmodule-install-perl
      

      Note: this is helpful for debugging:

      spamassassin -D --lint 2>&1 | grep -i failed
      
    2. In /etc/exim4/exim4.conf:

      1. add the following router right before the "localuser" router:

        # router to send incoming email to spamchek transport for checking 
        spamcheck_router:
          no_verify
          check_local_user
          # When to scan a message :
          #   -   it isn't already flagged as spam
          #   -   it isn't already scanned
          #   -   it isn't sent from my private home server
          #   -   it isn't sent from the server (linode or localhost)
          condition = "${if and { {!def:h_X-Spam-Flag:} {!eq {$received_protocol}{spam-scanned}} {!match {$sender_host_address} {${lookup dnsdb{a=mattandliz.dyndns.org}}}} {!match {$sender_host_address} {${lookup dnsdb{a=mattcaron.net}}}}} {1}{0}}"
          driver = accept
          transport = spamcheck
        
        # router to deliver spam to the junk folder
        spam_deliver_to_junk
          driver = accept
          check_local_user
          local_parts = !www:!root:!nobody:!postmaster:!abuse:!admin
          transport = dovecot_spam_junk_delivery
          condition = ${if def:h_X-Spam-Flag: {true}}
        
      2. add the following transport (it can go anywhere, order doesn't matter)

        # Scan for spam via spamassassin. Note that this works by calling exim
        # *again* and essentially redlivering the message, except that it has
        # already been scanned (see the "spam-scanned" add here, and the conditional
        # up in the router), so it only gets called the first time
        spamcheck:
          debug_print = "T: spamassassin_pipe for $local_part@$domain"
          driver = pipe
          command = /usr/sbin/exim4 -oMr spam-scanned -bS
          use_bsmtp
          # run the filter as debian-spamd because it has access to all of the
          # spamassassin files
          transport_filter = /usr/bin/spamc -u debian-spamd
          home_directory = "/tmp"
          current_directory = "/tmp"
          # must use a privileged user to set $received_protocol on the way back in!
          user = Debian-exim
          group = Debian-spamd
          return_fail_output
          message_prefix =
          message_suffix =
        
        # This delivers mail via dovecot to the Junk folder. 
        dovecot_spam_junk_delivery:
          driver = pipe
          # You may or may not want to add -d $local_part@$domain depending on if 
          # you need a userdb lookup done.
          command = /usr/lib/dovecot/dovecot-lda -f $sender_address -m Junk
          message_prefix =
          message_suffix =
          log_output
          delivery_date_add
          envelope_to_add
          return_path_add
          #group = mail
          #mode = 0660
          temp_errors = 64 : 69 : 70: 71 : 72 : 73 : 74 : 75 : 78
        
    3. Edit /etc/spamassassin/local.cf and change as follows:

      rewrite_header Subject *****SPAM*****
      
    4. Once all of the above is set up, edit /etc/default/spamassassin and set:

      ENABLED=1
      

      and:

      CRON=1
      
    5. Set up the global bayes learning directory. It will be group rw for the adm group, as it's assumed that only those users would ssh in and teach it things. Also, spamd changes uid to Debian-exim, so make sure that user owns the DB and can read things. (You may have to run the chown again after creating the databases with sa-learn). It also likes to change group IDs on the files, so you need to make sure that all those are correct, and that any users who are going to train SA are in the Debian-exim group (which implies that you trust them).

       sudo mkdir -p /var/spamassassin/bayes_db
       sudo chown -R Debian-exim:debian-spamd /var/spamassassin
       sudo chmod -R g+rwX /var/spamassassin
       sudo chmod g+s /var/spamassassin
       sudo usermod -a -G debian-spamd <user list>
      

      The g+s looks a little odd here, but let me explain. The spamcheck transport runs as Debian-exim, which will create /var/spamassassin/bayes_journal if it does not exist. However, the spamd process likes to run as debian-spamd (so it can access all its files), which won't be able to read said journal and will complain bitterly. By setting the directory to be setgid, all created files will have the correct gid set, ensuring that both Debian-exim and debian-spamd can read and write everything in it. This also means that the sa_learn wrapper that I run periodically will work, because I am in the debian-spamd group.

    6. Add the following to /etc/spamassassing/local.cf, so that it uses the above (note that the last part of bayes_path is a prefix, not a directory).

      use_bayes 1
      bayes_path /var/spamassassin/bayes_db/bayes
      bayes_file_mode 0660
      
    7. Also, because we only have a couple of users, limit children - edit /etc/default/spamassassin and set:

      OPTIONS="--create-prefs --max-children 5 --helper-home-dir"
      

      to

      OPTIONS="--create-prefs --max-children 2 --helper-home-dir"
      
    8. restart

      sudo service spamassassin restart

  16. Edit /etc/aliases and:

    1. change root to go to matt:

      root: matt
      
  17. Add sympa

    1. Establish base checkout:

      cd ~/workspace/code
      git clone https://github.com/sympa-community/sympa.git
      cd sympa
      git checkout -b production 6.2.66
      

      to upgrade later:

      cd ~/workspace/code/sympa
      git fetch
      git checkout production
      git merge 6.2.66
      

      (where 6.2.66 is the current version)

    2. Copy this whole mess over to the linode server:

       push_sympa
      

      == All the rest of this is on the linode server ==

    3. Install prerequisites

      sudo apt install libapache2-mod-fcgid libdbd-mysql-perl apache2-suexec-pristine apache2-suexec-pristine intltool libclass-singleton-perl libdatetime-format-mail-perl libemail-simple-perl libnet-cidr-perl libproc-processtable-perl libcrypt-openssl-x509-perl libcrypt-smime-perl libdata-password-perl libauthcas-perl libdbd-odbc-perl libclone-perl libcrypt-eksblowfish-perl libdbd-csv-perl
      
    4. Create a user w/ no shell and a given uid/gid which are the next available

      sudo adduser sympa --uid 110 --gid 117 --disabled-login
      sudo usermod -s /bin/false sympa
      
    5. Enable apache modules

      sudo a2enmod suexec
      sudo a2enmod cgi
      
    6. Set the versions in the environment (subsequent shells use this to save typing):

      export VER=6.2.66
      export OLDVER=6.2.64
      
    7. Make the destination directory:

      sudo mkdir /opt/sympa-${VER}
      sudo chown matt:matt /opt/sympa-${VER}
      
    8. Build and install it:

      cd ~/workspace/code/sympa
      autoreconf -i
      ./configure --prefix=/opt/sympa-${VER} --sysconfdir=/opt/sympa-${VER}/etc/ --with-initdir=/opt/sympa-${VER}/etc/init.d --with-cgidir=/opt/sympa-${VER}/cgi-bin --without-smrshdir
      make
      make install
      
    9. Fix permissions

      sudo chown -R sympa:sympa /opt/sympa-${VER}
      sudo chmod a+rX -R /opt/sympa-${VER}
      
      sudo chown -R sympa:sympa /var/spool/sympa
      sudo chown -R sympa:sympa /var/lib/sympa
      
    10. Make some compatibilty symlinks:

      sudo -E -u sympa -s
      cd /opt/sympa-${VER}
      ln -s /var/lib/sympa/expl .
      ln -s /var/lib/sympa/wwsarchive .
      ln -s /var/lib/sympa/x509-user-certs .
      cd /opt/sympa-${VER}/etc
      ln -s /etc/sympa .
      cd /opt/sympa-${VER}/static_content
      rmdir css
      ln -s /var/lib/sympa/static_content/css .
      ln -s /etc/mail/sympa/aliases /etc/mail/sympa_aliases
      cp -a /opt/sympa-${OLDVER}/spool /opt/sympa-${VER}
      exit
      sudo service sympa stop
      cd /opt
      sudo rm sympa
      sudo ln -s sympa-${VER}/ sympa
      cd /etc/init.d
      sudo ln -s /opt/sympa/etc/init.d/sympa .
      cd /etc/rc6.d
      ln -s ../init.d/sympa K99sympa
      cd /etc/rc5.d
      ln -s ../init.d/sympa S99sympa
      cd /etc/rc4.d
      ln -s ../init.d/sympa S99sympa           
      cd /etc/rc3.d
      ln -s ../init.d/sympa S99sympa
      cd /etc/rc2.d
      ln -s ../init.d/sympa S99sympa
      cd /etc/rc1.d
      ln -s ../init.d/sympa K99sympa
      cd /etc/rc0.d
      ln -s ../init.d/sympa K99sympa
      cd /etc
      sudo ln -s /etc/sympa/sympa.conf .
      cd /var/lib/sympa/
      mkdir pictures
      
    11. Run the upgrade script (on upgrade)

      sudo -u sympa /opt/sympa-${VER}/bin/sympa.pl --upgrade
      
    12. Auto-install a pile of perl modules:

      sudo /opt/sympa-${VER}/bin/sympa_wizard.pl --check
      

      (hit enter a bunch of times)

    13. Fix up supporting bits and bobs

      sudo touch /etc/sympa/facility
      sudo chown sympa:sympa /etc/sympa/facility
      sudo mkdir -p /var/lock/subsys/sympa
      sudo chown sympa:sympa /var/lock/subsys/sympa
      sudo mkdir /var/spool/sympa/wwsbounce
      sudo chown sympa:sympa /var/spool/sympa/wwsbounce
      sudo chmod u+s /opt/sympa/bin/queue /opt/sympa/bin/bouncequeue
      

      The queue needs to be suid root so that the mail server works when it calls queue and bouncequeue. Some of the routers call the filter and that needs to be as sympa, but we still want them to run other actions as exim.

    14. Set up the configs:

      1. Edit /etc/sympa/wwsympa.conf and comment out:

        #ldap_force_canonical_email 1

      2. Edit /etc/sympa/sympa.conf and set the following

        listmaster              [email protected]
        create_list listmaster
        wwsympa_url https://sympa.mattcaron.net/wws
        
      3. Edit /etc/exim4/exim4.conf and add the following below system_aliases:

        sympa_aliases_domain:
          driver = redirect
          domains = +local_domains
          allow_fail
          allow_defer
          data = ${lookup{$local_part@$domain}lsearch{/etc/mail/sympa/aliases}}
          user = sympa
          group = sympa
          file_transport = address_file
          pipe_transport = address_pipe
        
        # Aliases for sympa                                                             
        sympa_aliases:
          driver = redirect
          domains = +local_domains
          allow_fail
          allow_defer
          data = ${lookup{$local_part}lsearch{/etc/mail/sympa/aliases}}
          user = sympa
          group = sympa
          file_transport = address_file
          pipe_transport = address_pipe
        
    15. Remove /etc/apache2/conf.d/sympa (if it exists; it's just a symlink) and instead set up /etc/apache2/sites-available/sympa as follows:

       <VirtualHost *:80>
           ServerName sympa.mattcaron.net
           ServerAdmin [email protected]
           Redirect permanent / https://sympa.mattcaron.net/wws
       </VirtualHost>
      
       <VirtualHost *:443>
           ServerName sympa.mattcaron.net
           ServerAdmin [email protected]
      
           SSLEngine on
           SSLCertificateFile    /etc/ssl/private/sympa.mattcaron.net/fullchain.pem
           SSLCertificateKeyFile /etc/ssl/private/sympa.mattcaron.net/privkey.pem
      
           # Standard SSL protocol adustments for IE
           BrowserMatch "MSIE [2-6]" \
                      nokeepalive ssl-unclean-shutdown \
                      downgrade-1.0 force-response-1.0
           BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
      
           Alias /static-sympa /opt/sympa/static_content
           ScriptAlias /wws /var/www/sympa/wwsympa.cgi
           SuexecUserGroup sympa sympa
      
           # Use simple cgi here. It's not heavily used and base cgi is the most 
           # compatible
           AddHandler cgi-script .fcgi .cgi .pl .sh
      
           RewriteEngine On
           RewriteRule  ^/$                 /wws  [R,L]
      
           <Directory "/var/www/sympa/">
               AllowOverride None
               Options ExecCGI
               Order allow,deny
               Allow from all
           </Directory>
        </VirtualHost>
      
     1. Fake out `suexec`, because it is hardcoded to want things in `/var/www`:
    
        1. Make dir:
    
               sudo mkdir /var/www/sympa/
               sudo chmod a+rx /var/www/sympa/
               sudo chown sympa:sympa /var/www/sympa/
    
        1. Create `/var/www/sympa/wwsympa.cgi`:
    
               #!/bin/sh
               # The script we want Sympa to execute is accessed via a symlink but
               # suexec doesn't like that so this script is a wrapper which gets
               # executed directly to avoid that problem.
               exec  /opt/sympa/cgi-bin/wwsympa.fcgi
    
        1. Fix the perms on it:
    
               sudo chown sympa:sympa /var/www/sympa/wwsympa.cgi
               sudo chmod u+x /var/www/sympa/wwsympa.cgi
    
    1. /etc/sympa/sympa.conf changes:

      1. Limit number of spawned bulk processes, set:

        bulk_max_count  1
        
      2. Make sure dmarc_protection_mode is set in the config - add this to the bottom:

        # Munge email which would otherwise be dropped.                                 
        dmarc_protection_mode dmarc_any
        
    2. Fix up the mailserver aliases /etc/aliases and /etc/mail/sympa/aliases by replacing /usr/lib/sympa/lib/sympa with /opt/sympa/bin (if necessary)

    3. Restart things as necesary.

      sudo service apache2 restart
      sudo systemctl daemon-reload
      sudo service sympa restart
      
    4. Don't forget to add sympa.mattcaron.net to DNS

    5. Edit /etc/sympa/topics.conf, delete everything, then add:

       gaming
       title Gaming
      
       gaming/roleplaying_games
       title Role Playing Games
      
       gaming/wargaming
       title Wargaming
      
    6. Add a second domain for sympa

      1. In /etc/exim4/exim4.conf, add the following under ROUTERS CONFIGURATION before the big comment block preceding system_aliases(because order matters for exim):

         # This router does the same as system_aliases, except that it checks
         # the domain as well.
         #
         # IMPORTANT: Needs to go before things that match on ! +local_domains
         # and, most importantly, before the bl_server bit because that
         # forwards all non-local domains off to msex1.
         #
         # Remember - routers are run in order
         #
         system_aliases_domain:
           driver = redirect
           allow_fail
           allow_defer
           data = ${lookup{$local_part@$domain}lsearch{/etc/aliases}}
         # user = exim
           file_transport = address_file
           pipe_transport = address_pipe 
        

        Ref: http://www.sympa.org/manual/virtual-hosts

      2. Add any new domains to the top of /etc/mail/sympa/aliases, as necessary. As in:

         [email protected]:      "| /opt/sympa/bin/queue [email protected]"
         [email protected]: "| /opt/sympa/bin/queue [email protected]"
         bounce+*@domain.com:   "| /opt/sympa/bin/bouncequeue [email protected]"
        
      3. And add the following to /etc/exim4/exim4.conf, in the ROUTERS CONFIGURATION section (doesn't matter where):

         # Aliases for sympa (robot virtual subdomains)
         sympa_aliases_robot:
           driver = redirect
           domains = +local_domains
           allow_fail
           allow_defer
           data = ${lookup{$domain-$local_part}lsearch{/etc/mail/sympa/aliases}}
           user = sympa
           group = sympa
           file_transport = address_file
           pipe_transport = address_pipe
        
      4. Create bits / copy in defaults:

         sudo mkdir /etc/sympa/domain.com
         sudo cp /home/matt/workspace/code/sympa/doc/samples/robot.conf /etc/sympa/domain.com/.
         sudo chown -R sympa:sympa /etc/sympa/domain.com
         sudo chmod 750 /etc/sympa/domain.com
         sudo chmod 640 /etc/sympa/domain.com/robot.conf
         sudo mkdir /var/lib/sympa/domain.com
         sudo chown sympa:sympa /var/lib/sympa/domain.com
         sudo chmod 750 /var/lib/sympa/domain.com
        
      5. Edit /etc/sympa/domain.com/robot.conf

        1. Set as follows:

          http_host  sympa.domain.com
          listmaster [email protected]
          title New Domain MailingLists Service
          

          and, below http_host, you'll want to add:

          wwsympa_url  https://sympa.domain.com/wws
          
      6. Create /etc/sympa/domain.com/topics.conf

         topic1
         title Some Topic
        
         topic2
         title Some other topic
        
         general
         title General Membership
        
      7. Restart

         sudo service sympa restart
        
  18. Set up mysql snapshot

    1. Clone backup utils:

      mkdir -p ~/workspace/code/scripts
      cd ~/workspace/code/scripts
      git clone https://github.com/mattcaron/backup_scripts.git
      mkdir ~/bin
      cd ~/bin
      ln -s ~/workspace/code/scripts/backup_scripts/mysql_backup .
      mkdir -p ~/attic/backup/`hostname`
      
    2. Create ~/attic/backup/``hostname``/mysql.pw and put the root password into it.

    3. fix perms:

      chmod 600 ~/attic/backup/`hostname`/mysql.pw
      
    4. Add to crontab:

      @daily               /home/matt/bin/mysql_backup > /dev/null
      
  19. Lock root account

    sudo usermod -L root
    
  20. Add monitoring:

    1. Make sure landscape is installed (to get landscape-sysinfo):

      sudo apt install landscape-common
      
    2. Then add the following to my crontab:

      @daily               /usr/bin/ntpq -p; echo; df -lh; echo; landscape-sysinfo
      
  21. OwnCloud / NextCloud

    1. Make sure to add owncloud.mattcaron.net to linode DNS

    2. Optimization ref (probably obsolete)

      http://forum.owncloud.org/viewtopic.php?f=8&t=10692

    3. Install deps:

      sudo apt install apache2 php php-mbstring php-gd php-xml php-intl php-sqlite3 php-mysql curl libcurl4 php-curl libapache2-mod-xsendfile php-apcu php-bz2 php-zip php-pclzip php-imagick php-bcmath php-gmp
      
    4. Download tarball (add to source control, etc.)

    5. Make the xsendfile cache:

      sudo mkdir /tmp/oc-noclean
      sudo chown www-data:www-data /tmp/oc-noclean
      
    6. Increase PHP's memory limit:

      1. Edit /etc/php/7.4/apache2/php.ini

      2. Find:

        memory_limit = 128M
        

        and set it to:

        memory_limit = 512M
        
    7. Log in to the DB server and create a user and password

       CREATE DATABASE owncloud;
      
       GRANT ALL PRIVILEGES ON owncloud.* TO "owncloud"@"localhost" IDENTIFIED BY "password";
      
    8. Make an /etc/apache2/sites-available/owncloud.mattcaron.net as follows:

       <VirtualHost *:80>
           ServerName owncloud.mattcaron.net
           ServerAdmin [email protected]
      
           DocumentRoot /home/matt/public_html/owncloud.mattcaron.net
           <Directory /home/matt/public_html/owncloud.mattcaron.net>
               Options Indexes FollowSymLinks MultiViews
               AllowOverride All
               Order allow,deny
               allow from all
      
         SetEnv MOD_X_SENDFILE_ENABLED 1
         XSendFile On
             XSendFilePath /tmp/oc-noclean
           </Directory>
       </VirtualHost>
      
       <VirtualHost *:443>
           ServerName owncloud.mattcaron.net
           ServerAdmin [email protected]
      
           SSLEngine on
           SSLCertificateFile    /etc/ssl/private/owncloud.mattcaron.net/fullchain.pem
           SSLCertificateKeyFile /etc/ssl/private/owncloud.mattcaron.net/privkey.pem
      
           # Standard SSL protocol adustments for IE
           BrowserMatch "MSIE [2-6]" \
                      nokeepalive ssl-unclean-shutdown \
                      downgrade-1.0 force-response-1.0
           BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
      
           DocumentRoot /home/matt/public_html/owncloud.mattcaron.net
           <Directory /home/matt/public_html/owncloud.mattcaron.net>
               Options Indexes FollowSymLinks MultiViews
               AllowOverride All
               Order allow,deny
               allow from all
      
         SetEnv MOD_X_SENDFILE_ENABLED 1
         XSendFile On
             XSendFilePath /tmp/oc-noclean
           </Directory>
       </VirtualHost>
      
    9. Enable headers and rewrite modules:

       sudo a2enmod headers
       sudo a2enmod rewrite
      
    10. Enable it:

       sudo a2ensite owncloud.mattcaron.net
       sudo service apache2 reload
      
    11. Set mysql binlogs to 1 day.

      There is a bug in Nextcloud (and possibly Ownclound) 21.x and later where it updates auth tokens and sessions in the DB every time someone logs in which, given the nature of the application is a lot. As such, it generates a LOT of data per day and fills up disks. We really only need this if the server crashes to restore intermediate state, and we have backups from the previous night. So, only keep one day.

      1. Edit /etc/my.cnf.migrated and set skip-log-bin.
    12. Go to:

      https://owncloud.mattcaron.net/

      and do the initial setup, entering random admin credentials and choosing MySQL for the DB. Enter the DB credentials for the owncloud user on the owncloud DB you established above.

      The data folder is /usr/share/owncloud/data (the default) which links to /var/lib/owncloud/data/ (which should be backed up).

      Anyway, once that's all done it will let you in.

    13. Once logged in:

      1. Under the "Apps" menu, office & text section, enable:

        • Calendar
        • Contacts
        • Notes
        • Collabora Online

        (these are all official apps)

      2. Under the "Users" menu

        • Give appropriate people admin access
        • Delete the admin account
      3. Set up cron:

         sudo -u www-data crontab -e
        

        and add:

         */5  *  *  *  * php -f       /home/matt/public_html/owncloud.mattcaron.net/cron.php
        
      4. Then, in the Admin panel, tell it to use cron.

      5. A note on backups:

        These are already handled by the mysql_backup script and backing up homedirs. So, nothing additional need be done here, so long as the previous stuff is set up.

    14. Notes on configuring apps:

      • Thunderbird
      • Android
        • Address book
          1. Install "CardDAV sync free" or "CardDAV sync" from Google play.
          2. Launch CardDAV.
          3. Add account.
          4. Use the same URL as Thunderbird: https://owncloud.mattcaron.net/remote.php/carddav/addressbooks/matt/contacts
          5. Tick "Use SSL".
          6. Enter credentials.
          7. Next.
          8. Enter an appropriate name.
          9. Untick "Sync from server to phone only".
          10. Next.
        • Calendar
          1. Install "CalDAV sync free" or "CalDAV sync" from google play.
          2. Open Calendar.
          3. Top right corner, click "Add Account".
          4. Choose "CalDAV sync adapter".
          5. Enter creds, and use the same URL as Thunderbird: https://owncloud.mattcaron.net/remote.php/caldav/calendars/matt/familycalendar
          6. Of note - under account name, use your email, because that's used as the address of the organizer.
          7. Click "sign in or register".
          8. Once the account is there, click on it, and click "Accounts & sync".
          9. Click the "CalDav sync adapter" account when that comes up.
          10. Tick the box next to the sync state to turn it on
          11. Calendar should now work.
          12. Theoretically, a long press on the calendar name during set up will change the ugly poop color, but I couldn't get that to work, so I left it.
  22. MySQL and Apache tuning

    The default MySQL and Apache configs that ship with Ubuntu do not seem particularly suited to "low RAM boxes" (though, the old curmudgeon in me is amazed that 1.5GB if "low ram" these days, but I don't build a box with less than 1GB, as it's so cheap....)

    1. Apache

      1. Disable unused modules

         sudo a2dismod auth_basic
         sudo a2dismod authn_file
         sudo a2dismod authz_groupfile
         sudo a2dismod authz_user
         sudo a2dismod fcgid
         sudo a2dismod status
        
      2. Edit /etc/apache2/mods-enabled/mpm_prefork.conf and tweak values thusly.

        Original:

         <IfModule mpm_prefork_module>
            StartServers          5
            MinSpareServers       5
            MaxSpareServers      10
            MaxClients          150
            MaxRequestsPerChild   0
         </IfModule>
        

        Revised:

         <IfModule mpm_prefork_module>
             StartServers          2
             MinSpareServers       1
             MaxSpareServers       5
             MaxClients           50
             MaxRequestsPerChild   0
         </IfModule>
        
      3. Restart:

           sudo service apache2 restart
        
    2. MySQL

      1. Edit /etc/mysql/my.conf and change parameters in the [mysql] section as follows:

        Original:

         key_buffer_size         = 16M
         #max_connections        = 100
        

        Modified:

         key_buffer_size         = 8M
         max_connections         = 50
        
      2. Restart:

          sudo service mysql restart
        
  23. MegaMek

    1. Create a user, then disable it.

       sudo adduser megamek
       sudo usermod -s /usr/sbin/nologin -L megamek
      
    2. Install a JRE:

       sudo apt install default-jre
      
    3. Open up a firewall port:

       sudo ufw allow from any to any port 2346 proto tcp comment 'megamek'
      
    4. Create /lib/systemd/system/megamek.service with the following (and the correct password):

       [Unit]
       Description=MegaMek service
       After=network.target auditd.service
      
       [Service]
       ExecStart=/usr/bin/java -Xmx1024m -jar /home/megamek/megamek/MegaMek.jar -dedicated -port 2346 -password XXX
       ExecReload=/bin/kill -HUP $MAINPID
       KillMode=process
       Restart=always
       RestartPreventExitStatus=255
       Type=simple
       WorkingDirectory=/home/megamek/megamek
       RuntimeDirectoryMode=0755
       User=megamek
      
       [Install]
       Alias=megamek.service
      
    5. Enable and start the service:

       sudo systemctl enable megamek
       sudo systemctl start megamek
      
    6. Notes:

      1. It lives in /home/megamek/
      2. /home/megamek/megamek is a symlink to the current version.
      3. Download from https://megamek.org/downloads.html (just MegaMek stable)
  24. MediaWiki (school.mattcaron.net)

    1. Make the dir.

    2. Grab the mediawiki tarball and untar it in the root.

    3. Make /etc/apache2/sites-available and enable it:

      sudo a2ensite rpg.mattcaron.net && sudo systemctl reload apache2
      
    4. Create the database and user:

      CREATE DATABASE mw_school;
      GRANT ALL PRIVILEGES ON mw_school.* TO "mw_school"@"localhost" IDENTIFIED BY "password";
      
    5. Push everything up and then go to https://school.mattcaron.net/ to configure it, grab the LocalSettings.php file and stuff it where it needs to be. Yay.

  25. Coturn (STUN/TURN server, used by Synapse for VoIP stuff)

    1. Install it from the repos:

      sudo apt install coturn
      
    2. Make the server user a member of the ssl-cert group so it can read the certs, and a member of the syslog file so it can write /var/log

      sudo usermod -a -G ssl-cert turnserver
      sudo usermod -a -G syslog turnserver
      
    3. Configure it by editing /etc/turnserver.conf:

      1. Uncomment the following options to enable them:
        1. tls-listening-port
        2. fingerprint
        3. use-auth-secret
        4. secure-stun
        5. no-cli
        6. no-tcp-relay
      2. Set the log-file to /var/log/turn.log.
      3. Set static-auth-secret to something strongish.
      4. Set the realm to chat.mattcaron.net.
      5. Set the cert and key files to appropriate values for chat.mattcaron.net.
      6. Set the user-quota and total-quota.
      7. Set the proc-user and proc-group to turnserver.
    4. Set it to start by editing /etc/default/coturn and uncommenting TUNSERVER_ENABLED=1.

    5. Allow through firewall:

      sudo ufw allow 5349 comment "turn tls"

  26. Synapse (matix server)

    1. Install it from the latest stable repo:

      sudo wget -qO /usr/share/keyrings/matrix-org-archive-keyring.gpg https://packages.matrix.org/debian/matrix-org-archive-keyring.gpg
      
      echo "deb [signed-by=/usr/share/keyrings/matrix-org-archive-keyring.gpg] https://packages.matrix.org/debian/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/matrix-org.list
      
      sudo apt update
      sudo apt install matrix-synapse-py3
      

      follow the instruction prompts, setting the domain and sumbitting anonymous usage statistics.

    2. Configure it by editing /etc/matrix-synapse/homeserver.yml

      1. Set the admin_contact.
      2. Disable federation by setting federation_domain_whitelist to this server and only this server (so it federates with itself).
      3. Enable URL previews and uncomment the blacklist, as recommended.
        1. We do not explicitly blacklist the external IP because we want previews for those to work, and it's all externally accessible already anyway.
      4. Set enable_registration to false.
        1. I don't want people to register themselves; only me.
      5. Set the registration shared secret.
      6. Set allow_guest_access to false.
      7. Set it to auto-join admin-help, as a minimal room set.
      8. Comment out the trusted keyservers - we don't trust anyone.
      9. Set the TURN server config
        1. Set the turn_uris to have chat.mattcaron.net as the list.
        2. Set the turn_shared_secret to be the same as the one in /etc/turnserver.conf.
        3. Set turn_allowed_guests to false.
      10. Note that we don't:
        1. Set the certs because we run this unencrypted on localhost and then proxy it through apache, which does our TLS termination.
        2. Set it up to send emails - the app notifies one of new messages, so I'm not enabling this unless people ask for it.
          1. Of course, not setting email (and not setting a 3PID server) means we can't set email and phone numbers.
        3. Push notifications - again, the app notifies one of new messages, so why annoy people more?
    3. Set up the VHost proxy.

      1. Create /etc/apache2/sites-available/chat.mattcaron.net thusly:

        <VirtualHost *:80>
            ServerName chat.mattcaron.net
            ServerAdmin [email protected]
        
            RewriteEngine on    
            RewriteRule ^/(.*)$  https://chat.mattcaron.net/$1  [R,L]
        </VirtualHost>
        
        <VirtualHost *:443>
            ServerName chat.mattcaron.net
            ServerAdmin [email protected]
        
            SSLEngine on
            SSLCertificateFile    /etc/ssl/private/chat.mattcaron.net/fullchain.pem
            SSLCertificateKeyFile /etc/ssl/private/chat.mattcaron.net/privkey.pem
        
            Include ssl_common.fragment
        
            AllowEncodedSlashes NoDecode
            ProxyPass /_matrix http://127.0.0.1:8008/_matrix nocanon
            ProxyPassReverse /_matrix http://127.0.0.1:8008/_matrix
            ProxyPass /_synapse/client http://127.0.0.1:8008/_synapse/client nocanon
            ProxyPassReverse /_synapse/client http://127.0.0.1:8008/_synapse/client
        </VirtualHost>
        
        # Server to server comms (disabled for now as we do not want federation)
        #<VirtualHost *:8448>
        #    SSLEngine on
        #    ServerName example.com;
        #
        #    AllowEncodedSlashes NoDecode
        #    ProxyPass /_matrix http://127.0.0.1:8008/_matrix nocanon
        #    ProxyPassReverse /_matrix http://127.0.0.1:8008/_matrix
        #</VirtualHost>
        
      2. Enable it:

        sudo a2enmod proxy
        sudo a2enmod proxy_http
        sudo a2ensite chat.mattcaron.net
        sudo service apache2 reload
        
      3. Create users:

        register_new_matrix_user -c /etc/matrix-synapse/homeserver.yaml http://localhost:8008
        

        and then follow the prompts.

      4. Set up DB snapshot - add to /etc/cron.d/matrix:

        # /etc/cron.d/matrix: crontab entries to backup matrix sqlite database
        
        PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/snap/bin
        MAILTO=root
        
        @daily     root     service matrix-synapse stop && /home/matt/bin/matrix-synapse_sqlite_backup > /dev/null ; service matrix-synapse start ; chown matt:matt /home/matt/attic/backup/linode/matrix-synapse_sqlite_backup.*
        
  27. Element Web (frontend for Matrix server)

    1. Copy over ~/public_html/chat.mattcaron.net.
    2. In /etc/apache2/sites-available/chat.mattcaron.net, set the DocumentRoot to /home/matt/public_html/chat.mattcaron.net.
  28. Jitsi

    1. Add video.mattcaron.net to DNS and get a cert.

    2. Add the repo and key.

    3. Install from repo:

      sudo apt update
      sudo apt install jitsi-meet
      
    4. When prompted:

      1. Set the hostname to video.mattcaron.net
      2. Tell it to use my own certificate
      3. Key is at /etc/ssl/private/video.mattcaron.net/privkey.pem
      4. Certificated is at /etc/ssl/private/video.mattcaron.net/fullchain.pem
    5. It automatically detects apache being there, installs a config file for it, and enables it.

    6. Configure the Element clients to use the correct Jisti domain. Create /.well-known/matrix/client and add the following to it:

      {
        "im.vector.riot.jitsi": {
          "preferredDomain": "video.mattcaron.net"
        }
      }
      
    7. It uses prosody for authentication, and need to be set up to lock it down:

      1. Edit /etc/prosody/conf.avail/video.mattcaron.net.cfg.lua

      2. Change:

        authentication = "anonymous"
        

        to

        authentication = "internal_plain"
        
    8. But we want invited guests to be able to create temporary accounts:

      1. Add the following VirtualHost entry in the file:

        VirtualHost "guest.video.mattcaron.net"
            authentication = "anonymous"
            c2s_require_encryption = false
        
      2. Edit /etc/jitsi/meet/video.mattcaron.net-config.js and change:

        // anonymousdomain: 'guest.example.com',
        

        to

        anonymousdomain: 'guest.video.mattcaron.net',
        

        and find and set the following as follows (often just uncommenting them):

        maxFullResolutionParticipants: 5,
        
        requireDisplayName: true,
        
        prejoinPageEnabled: true,
        
        disableThirdPartyRequests: true,
        
        doNotStoreRoom: true,
        
        disableTileView: true,
        
      3. Edit /etc/jitsi/jicofo/sip-communicator.properties and add the following line to the end:

        org.jitsi.jicofo.auth.URL=XMPP:video.mattcaron.net
        
      4. Restart everything:

        sudo service prosody restart
        sudo service jicofo restart
        sudo service jitsi-videobridge2 restart
        
      5. Once configured, add users as follows:

        sudo prosodyctl register user your_domain password
        
    9. Allow media port through the firewall:

      sudo ufw allow 10000 comment "jitsi media"