Skip to content

Latest commit

 

History

History
1454 lines (1007 loc) · 53.8 KB

install.linode.ubuntu.24.04.md

File metadata and controls

1454 lines (1007 loc) · 53.8 KB

Installing Ubuntu 24.04 on Linode

Note that this is based off a 12.04 reference image which I upgraded to 14.04, 16.04, 18.04, 20.04, 22.04, and then to 22.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."
  • Also, add a DMARC record:

    v=DMARC1; p=reject; rua=mailto:[email protected]
    
  • Which says "use DMARC 1 version, reject emails that don't meet criteria, and then email postmaster about 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/sshd_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. Configure the extra IPv6 address

    1. Create /etc/netplan/01-netcfg.yaml as follows:

      network:
        version: 2
        renderer: networkd
        ethernets:
          eth0:
          dhcp4: yes
          accept-ra: no
          ipv6-privacy: no
          addresses:
            - "2600:3c03:e000:8c4::1/64"
          routes:
            - to: default
              via: "fe80::1"
      

      Because the default SLAAC IP block is listed as a bunch of spammers, and this one is mine.

    2. Generate and apply

      sudo netplan generate
      sudo netplan apply
      
  8. Install useful things

    sudo apt install tree emacs-nox git software-properties-common snapd iftop htop
    
  9. 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 pfmbonsai.com
      sudo certbot --standalone certonly -d chat.mattcaron.net
      sudo certbot --standalone certonly -d video.mattcaron.net
      sudo certbot --standalone certonly -d rpg.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.
  10. Install apache

    1. Install packages:

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

      sudo a2enmod userdir
      sudo a2enmod rewrite
      sudo a2enmod ssl
      sudo a2enmod php8.3
      
    3. Fix up the php configuration so people can run php web content in their homedirs by editing /etc/apache2/mods-available/php8.3.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. Allow it through the firewall

       sudo ufw allow http
       sudo ufw allow https
      
  11. 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
          }
          
        3. At the bottom, add the following:

          service stats {
            unix_listener stats-reader {
              mode = 0666
            }
            unix_listener stats-writer {
              mode = 0666
            }
          }
          
      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
      
  12. 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
      
  13. 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
            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
        
  14. 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
        
  15. Configure the time zone:

    sudo dpkg-reconfigure tzdata
    

    and set it to America/New York

  16. 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 spamcheck 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:

      CRON=1
      
    5. And edit both /etc/default/spamassassin and /etc/default/spamd

      and change the line :

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

      to be like this:

      OPTIONS="--create-prefs --max-children 2 --helper-home-dir --username=debian-spamd"
      

      because, for some reason, the username isn't set by default, and we only have a couple of users so don't need to run a ton of daemons.

    6. And then enable it:

      sudo update-rc.d spamd enable
      sudo service spamd restart
      
    7. 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.

    8. Add the following to /etc/spamassassin/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
      
    9. And, because linode's customers regularly exceed the allowable DNS queries from the free services (see this post), add mthe following lines to the bottom of /etc/spamassassin/local.cf, so they won't be used in the scoring.

      dns_query_restriction deny multi.uribl.com
      dns_query_restriction deny bl.score.senderscore.com
      dns_query_restriction deny sa-trusted.bondedsender.org
      dns_query_restriction deny sa-accredit.habeas.com
      
    10. restart

      sudo service spamd restart

  17. Edit /etc/aliases and:

    1. change root to go to matt:

      root: matt
      
  18. 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.72
      

      to upgrade later:

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

      (where 6.2.72 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 ==

    1. 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 cpanminus
      
    2. 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
      
    3. Enable apache modules

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

      export VER=6.2.72
      export OLDVER=6.2.72
      
    5. Make the destination directory:

      sudo mkdir /opt/sympa-${VER}
      sudo chown matt:matt /opt/sympa-${VER}
      
    6. 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
      
    7. 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
      
    8. 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
      
    9. Run the upgrade script (on upgrade)

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

      cd ~/workspace/code/sympa
      sudo cpanm --installdeps --with-recommends .
      
    11. 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.

    12. 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
        
    13. Remove /etc/apache2/conf.d/sympa (if it exists; it's just a symlink) and instead set up /etc/apache2/sites-available/sympa.conf 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>
      
    14. 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
      
    15. /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
        
    16. 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)

    17. Restart things as necesary.

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

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

       gaming
       title Gaming
      
       gaming/roleplaying_games
       title Role Playing Games
      
       gaming/wargaming
       title Wargaming
      
    20. 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. Reload daemon and restart sudo systemctl daemon-reload sudo service sympa restart

  19. 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 .
      ln -s ~/workspace/code/scripts/backup_scripts/postgresql_backup .
      mkdir -p ~/attic/backup/`hostname`
      
    2. Create ~/attic/backup/``hostname``/mysql.pw and put the root password into it.

    3. Create /etc/sudoers.d/matt_pgdump with the following contents:

      matt linode = (postgres) NOPASSWD: /bin/pg_dumpall

    4. fix perms:

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

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

    sudo usermod -L root
    
  21. 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
      
  22. Install NextCloud

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

    2. Install deps:

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

    4. Make the xsendfile cache:

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

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

        1. Find:

          memory_limit = 128M
          

          and set it to:

          memory_limit = 512M
          
        2. Find:

          ;opcache.interned_strings_buffer=8
          

          uncomment it and set it to:

          opcache.interned_strings_buffer=16
          
      2. Also edit /etc/php/8.3/mods-available/apcu.ini and add:

        apc.enable_cli=1
        
    6. 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";
      
    7. Make an /etc/apache2/sites-available/owncloud.mattcaron.net.conf 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>
      
    8. Enable headers and rewrite modules:

       sudo a2enmod headers
       sudo a2enmod rewrite
      
    9. Enable it:

       sudo a2ensite owncloud.mattcaron.net
       sudo service apache2 reload
      
    10. 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, in the [mysqld] section, add skip-log-bin.
    11. Make sure 4 byte support is enabled

      1. Edit /etc/my.cnf.migrated and, in the [mysqld] section, set innodb_file_per_table = 1.
        1. If you change the above, restart the server sudo service mysql restart.
        2. And, if it wasn't enabled, you likely need to conver the Nextcloud DB. See the instructions.
    12. And make sure mysql is starting on boot:

       sudo systemctl enable mysql
      
    13. 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.

    14. Once logged in:

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

        • Calendar
        • Contacts
        • Notes

        (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.

    15. 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.
  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. 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-multicast-peers
        6. no-cli
        7. no-tcp-relay
        8. no-tcp
        9. no-tlsv1
        10. no-tlsv1_1
        11. no-tlsv1_2
      2. Set the log-file to /var/log/turn.log.
      3. Set static-auth-secret to something strongish.
      4. Set the realm to video.mattcaron.net.
      5. Set the cert and key files to appropriate values for video.mattcaron.net.
      6. 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"

  25. Synapse (matix server)

    1. Install it from the latest stable repo.

      NOTE: At the time of writing, there is not a noble release, but the jammy release seems to work fine, so we'll be using that. This should be revisited at some point - either when it breaks, or when either the Element or Matrix.org folks start building packages themselves. Look for noble in https://packages.matrix.org/debian/dists/.

      wget -O- https://packages.matrix.org/debian/matrix-org-archive-keyring.gpg | sudo gpg --no-default-keyring --keyring=/usr/share/keyrings/matrix-org-archive-keyring.gpg --import
      
      echo "deb [signed-by=/usr/share/keyrings/matrix-org-archive-keyring.gpg] https://packages.matrix.org/debian/ jammy main" | sudo tee /etc/apt/sources.list.d/matrix-org.list
      
      sudo apt update
      sudo apt install matrix-synapse-py3 libpq5 postgresql
      

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

      For reference, the original echo line is:

      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
      

      It gets the actual release name.

    2. Set up the database.

      1. Create account and database. Don't forget to note the password.

        sudo -u postgres -i
        createuser --pwprompt synapse_user
        createdb --encoding=UTF8 --locale=C --template=template0 --owner=synapse_user synapse
        
    3. 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. Set the database by commenting out any existing section and adding the following:

        database:
          name: psycopg2
          args:
            user: synapse_user
            password: <its a secret>
            dbname: synapse
            host: localhost
            cp_min: 5
            cp_max: 10
            keepalives_idle: 10
            keepalives_interval: 10
            keepalives_count: 3
        
      11. 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?
    4. Set it to start on boot:

      sudo systemctl enable matrix-synapse
      
    5. Set up the VHost proxy.

      1. Create /etc/apache2/sites-available/chat.mattcaron.net.conf 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. Backups are handled as follows:

        1. Database is PostgreSQL and is handled by postgresql_backup.

        2. Media is in /var/lib/matrix-synapse which is backed up by rsync in the linode-backup script which is run on Jarvis to pull it all down.

  26. 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.
  27. Jitsi

    Note: The key storage is deprecated, rework these

    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 ~/public_html/chat.mattcaron.net/.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"
      
  28. MediaWiki (rpg.mattcaron.net)

    1. Make the dir, index.html, etc. (this is all in git, so will just get pushed).

    2. Grab the mediawiki tarball and untar it in wwn (the current RPG game). We'll be using separate mediawiki per install, just like wordpress. It does support multihost, but this is clean and easy and there won't be many of these. Plus, it's actually clean segregation, not virtual, so any bugs in that code won't bite us.

    3. Make /etc/apache2/sites-available/rpg.mattcaron.net.conf and enable it

      sudo a2ensite rpg.mattcaron.net && sudo systemctl reload apache2
      
    4. Create the databases and users

      1. Log in to MySQL server and do:

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

    6. And then do the same for https://rpg.mattcaron.net/fallout>