Wednesday, May 23, 2018

GDPR or AVG - regain control Part 2: Your Own Mail


Create your own mail server

Drop Yahoo, Google or Microsoft mail - they are reading your mail.

Debian, Postfix, Dovecot, MariaDb, rspamd

This is the second (and last) part of setting up your own internet tools in order to gain back control. Goal is to set up an email server (receive and send), secure it, and filter spam.

Hardware considerations

I used an abandoned ASRock ION330, where I replaced the 5400rpm harddisk for a SSD. I planned using an ODroid C2, but could not get rspamd running within a reasonable amount of time (days, that is...). The SSD is small, by today's standards, but 120GB is plenty of mail. YMMV, adapt storage to your needs.
A colleague had some RAM laying around, so I have an ION330 with maxed out RAM: 4GB...

Basic setup

This time no Etcher, but a bootable CD. Download Debian (debian-9.4.0-amd64-netinst.iso), burn it to CD, and boot from it.
Install, uncheck all options when selecting what to install. Set a root password.

Let's get cracking: security!

Basically, follow the instructions of the previous entry regarding setting up daily users, not allowing password based logins and prohibiting root logins.
You may have to install sudo:
su - apt update && apt upgrade -y && apt dist-upgrade apt install openssh-server sudo -y visodo
change:
# User privilege specification
root ALL=(ALL:ALL) ALL

to
# User privilege specification
root ALL=(ALL:ALL) ALL
frank ALL=(ALL:ALL) ALL

Save (Ctrl-X, return).
Logout of the elevated Super User (su) session (Ctrl-D), and type:
sudo apt update
You should be asked for your own(not root) password, and apt should be executed. You are now sudo'ed.

Security: enable logins over SSH

Installing the latest ssh server version is easier than with Ubuntu:
sudo -s cd /usr/local/src wget http://ftp.nl.debian.org/debian/pool/main/o/openssl/libssl1.1_1.1.0h-2_amd64.deb dpkg -i ./libssl1.1_1.1.0h-2_amd64.deb wget http://ftp.nl.debian.org/debian/pool/main/o/openssl/openssl_1.1.0h-2_amd64.deb dpkg -i openssl_1.1.0h-2_amd64.deb
You should do this before generating long keys, etc.

Your internet exposure

As a result of the previous entry, you have a contract with Domain Registrar. Your IPv4 (A record) and IPv6 (AAAA record) addresses point to [YOUR.DOMAIN], www.[YOUR.DOMAIN] and cloud.[YOUR.DOMAIN].
Now it is time to add your mail exchange (MX) record, and the IPv4 (A) and IPv6 (AAAA) records for your mail server. Your referrals should look like this example zone file:
. # General referral for your TLD to a mail server: [YOUR.DOMAIN]. 86400 IN MX 0 mail.[YOUR.DOMAIN]. # mail server itself should have IPv4 and IPv6 addresses: mail.[YOUR.DOMAIN]. 3600 IN A 11.22.33.999 mail.[YOUR.DOMAIN]. 3600 IN AAAA 2001:aa:bbcc:1:2:ff:43:1234 # Often used aliases, based on protocol: smtp.[YOUR.DOMAIN]. 86400 IN CNAME mail.[YOUR.DOMAIN]. imap.[YOUR.DOMAIN]. 86400 IN CNAME mail.[YOUR.DOMAIN]. # DMARC protection. Generate your own at https://elasticemail.com/dmarc based on your liking _dmarc.[YOUR.DOMAIN]. 3600 IN TXT "v=DMARC1\; p=reject\; rua=mailto:postmaster@[YOUR.DOMAIN]\; aspf=s\; adkim=s\;" # SPF - nice try to avaoid spam [YOUR.DOMAIN]. 3600 IN TXT "v=spf1 a:mail.[YOUR.DOMAIN] ?all"
The smtp and imap entries are not necessary, just common abbreviations for incoming and outgoing mail servers. In fact, these are the names of protocols being used. The CNAME type indicates smtp.[YOUR.DOMAIN] is an alias for your mail.[YOUR.DOMAIN]. Same applies to imap.[YOUR.DOMAIN].

I'll come to spf and dmarc later (and add DKIM as well)

Install: Letsenrypt certificates

Once more, I will use Letsencrypt certificates. Getting them to work is a little different.
sudo -s apt install certbot wget https://dl.eff.org/certbot-auto chmod a+x ./certbot-auto cd /usr/local/src wget http://ftp.nl.debian.org/debian/pool/main/o/openssl/libssl-dev_1.1.0h-2_amd64.deb dpkg -i ./libssl-dev_1.1.0h-2_amd64.deb exit sudo ./certbot-auto certonly --standalone --rsa-key-size 4096 -d mail.[YOUR.DOMAIN] -d imap.[YOUR.DOMAIN] -d smtp.[YOUR.DOMAIN] sudo crontab -e @weekly /root/renewal.sh > /home/frank/renewal.txt 2>&1 cat <<'EOF'>>/root/renewal.sh #!/bin/bash echo "-------------------------------------" echo "Renewals:" /root/certbot-auto renew echo "-------------------------------------" result=$(find /etc/letsencrypt/live/ -type l -mtime -1 ) if [ -n "$result" ]; then echo "Restarting services..." /bin/systemctl postfix reload /bin/systemctl dovecot reload /usr/sbin/service redis-server restart echo "-------------------------------------" fi mail -s "certbot Mail renewal" frank@[YOUR.DOMAIN] < /home/frank/renewal.txt exit 0 EOF

Installation: mail

Mail consists of a Mail transfer Agent (MTA) and a Mail User Agent (MUA). The configuration is kept in a database (MariaDb), and I want filtering against spammers as well as my server to be trusted.
All in all, I will install Postfix, Dovecot, MariaDb, rspamd DKIM, spf, DMARC.

Install postfix (Mail Transfer Agent)

sudo apt install postfix postfix-mysql dovecot-core dovecot-imapd dovecot-lmtpd dovecot-mysql dovecot-sieve dovecot-managesieved mariadb-server
In the Postfix configuration, choose internet site (default), and your [YOUR.DOMAIN] (without the mail prefix)
sudo service dovecot stop sudo service postfix stop sudo -s cd /etc/postfix/ vi main.cf # check/alter: myhostname = mail.[YOUR.DOMAIN] inet_protocols = all #or: inet_protocols = ipv4 mydestination = localhost

MariaDb install & setup

mysql_secure_installation set a new root password Remove anonymous users (Y - default) Disallow remote root (Y - default) Remove demo and Test databases (Y - default) Reload tables (Y - default) # Done. Now, for the db and tables: mysqladmin -p create maildb; mysql -p maildb grant select on maildb.* to mailowner@'127.0.0.1' identified by 'SomeMailPassword'; flush privileges; CREATE or replace TABLE domains ( id int(11) NOT NULL auto_increment, domain varchar(50) NOT NULL, PRIMARY KEY (id), UNIQUE KEY (domain) ) ENGINE=InnoDB; create or replace table users ( id int(11) NOT NULL auto_increment, domain_id int(11) NOT NULL, password varchar(255) NOT NULL, email varchar(127) NOT NULL, quota int unsigned DEFAULT 1024, enabled boolean DEFAULT 0, sendonly boolean DEFAULT 0, PRIMARY KEY (id), UNIQUE KEY ( email), FOREIGN KEY (domain_id) REFERENCES domains(id) ON DELETE CASCADE ) ENGINE=InnoDB; CREATE or replace TABLE aliases ( id int(11) NOT NULL AUTO_INCREMENT, domain_id int(11) NOT NULL, source varchar(100) NOT NULL, destination varchar(100) NOT NULL, enabled boolean DEFAULT 0, PRIMARY KEY (id), FOREIGN KEY (domain_id) REFERENCES domains (id) ON DELETE CASCADE ) ENGINE=InnoDB; CREATE or REPLACE TABLE tlspolicies ( id int(11) NOT NULL AUTO_INCREMENT, domain varchar(127) NOT NULL, policy enum('none', 'may', 'encrypt', 'dane', 'dane-only', 'fingerprint', 'verify', 'secure') NOT NULL, params varchar(255), PRIMARY KEY (id), UNIQUE KEY (domain) )ENGINE=InnoDB; insert into domains (domain) values ('[YOUR.DOMAIN]'); insert into domains (domain) values ('mail.[YOUR.DOMAIN]'); insert into domains (domain) values ('localhost'); insert into domains (domain) values ('home.local'); INSERT INTO maildb.aliases (domain_id, source, destination, enabled) VALUES ('1', 'webmaster@[YOUR.DOMAIN]', 'frank@[YOUR.DOMAIN]',0); INSERT INTO maildb.aliases (domain_id, source, destination, enabled) VALUES ('1', 'postmaster@[YOUR.DOMAIN]', 'frank@[YOUR.DOMAIN]',1); INSERT INTO maildb.aliases (domain_id, source, destination, enabled) VALUES ('1', 'cloudadmin@[YOUR.DOMAIN]', 'frank@[YOUR.DOMAIN]',1); INSERT INTO maildb.aliases (domain_id, source, destination, enabled) VALUES ('1', 'info@[YOUR.DOMAIN]', 'frank@[YOUR.DOMAIN]',1); INSERT INTO maildb.aliases (domain_id, source, destination, enabled) VALUES ('1', 'frank@home.local', 'frank@[YOUR.DOMAIN]',1); INSERT INTO maildb.aliases (domain_id, source, destination, enabled) VALUES ('1', 'root@home.local', 'frank@[YOUR.DOMAIN]',1); insert into tlspolicies (domain, policy, params) values ('gmx.de', 'secure', 'match=.gmx.net'); commit; quit

postfix (Mail Transfer Agent) - connect to MariaDb

mv /etc/postfix/main.cf /etc/postfix/main.cf.sav cat <<'EOF'>>/etc/postfix/main.cf mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 mydestination = localhost relayhost = inet_interfaces = all inet_protocols = all myhostname = mail.[YOUR.DOMAIN] ############################# ### mail queue config ############################# maximal_queue_lifetime = 1h bounce_queue_lifetime = 1h maximal_backoff_time = 15m minimal_backoff_time = 5m queue_run_delay = 5m ############################# ### TLS config ############################# tls_preempt_cipherlist = yes tls_ssl_options = NO_COMPRESSION tls_high_cipherlist = EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:E6:EECDH:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!IDEA:!ECDSA:kEDH:CAMELLIA128-SHA:AES128-SHA ############################# ### outgoing SMTP (Postfix sending) ############################# smtp_tls_security_level = dane smtp_dns_support_level = dnssec smtp_tls_policy_maps = mysql:/etc/postfix/sql/tls-policy.cf smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache smtp_tls_protocols = !SSLv2, !SSLv3 smtp_tls_ciphers = high smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt ############################# ### incoming SMTP ############################# smtpd_tls_security_level = may smtpd_tls_protocols = !SSLv2, !SSLv3 smtpd_tls_ciphers = high smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache smtpd_tls_cert_file=/etc/letsencrypt/live/mail.[YOUR.DOMAIN]/fullchain.pem smtpd_tls_key_file=/etc/letsencrypt/live/mail.[YOUR.DOMAIN]/privkey.pem ############################# ### local assignment to Dovecot ############################# virtual_transport = lmtp:unix:private/dovecot-lmtp ############################# ### Spamfilter & DKIM through rspamd ############################# smtpd_milters = inet:localhost:11332 non_smtpd_milters = inet:localhost:11332 milter_protocol = 6 milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen} milter_default_action = accept ############################# ### Server Restrictions (Clients, Relay, Receiptients) ### Server to server only. Client stuff is in master.cf (Submission part) ############################# ### Allow clients to forward mail, using postfix: ############################# smtpd_relay_restrictions = reject_non_fqdn_recipient reject_unknown_recipient_domain permit_mynetworks reject_unauth_destination ############################# ### Make Postfix accept incoming email, check_recipient_access checks if client is sendonly ############################# smtpd_recipient_restrictions = check_recipient_access mysql:/etc/postfix/sql/recipient-access.cf ############################# ### SMTP-Clients requirements (sending Server) ############################# smtpd_client_restrictions = permit_mynetworks check_client_access hash:/etc/postfix/without_ptr reject_unknown_client_hostname ############################# ### External servers should have valid Hostname in HELO. ############################# smtpd_helo_required = yes smtpd_helo_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname, reject_unknown_helo_hostname, check_helo_access hash:/etc/postfix/helo_access ############################# ### Clients sending too soon need to be blocked smtpd_data_restrictions = reject_unauth_pipelining ############################# ### MUA Restrictions (Mail User Agent) ############################# mua_relay_restrictions = reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_mynetworks,permit_sasl_authenticated,reject mua_sender_restrictions = permit_mynetworks,reject_non_fqdn_sender,reject_sender_login_mismatch,permit_sasl_authenticated,reject mua_client_restrictions = permit_mynetworks,permit_sasl_authenticated,reject ############################# ### Postscreen Filter ############################# ### Postscreen Whitelist / Blocklist postscreen_access_list = permit_mynetworks cidr:/etc/postfix/postscreen_access postscreen_blacklist_action = drop ############################# ### Incoming speed to high? drop connection (spam...) ############################# postscreen_greet_action = drop ############################# ### DNS blocklists ############################# postscreen_dnsbl_threshold = 2 postscreen_dnsbl_sites = ix.dnsbl.manitu.net*2 zen.spamhaus.org*2 postscreen_dnsbl_action = drop ############################# ### MySQL config ############################# virtual_alias_maps = mysql:/etc/postfix/sql/aliases.cf virtual_mailbox_maps = mysql:/etc/postfix/sql/accounts.cf virtual_mailbox_domains = mysql:/etc/postfix/sql/domains.cf local_recipient_maps = $virtual_mailbox_maps ############################# ### miscellaneous ############################# mailbox_command= mailbox_transport = lmtp:unix:private/dovecot-lmtp ############################# ### Maximal Size all Mailboxes (register with Dovecot, 0 = unlimited) ############################# mailbox_size_limit = 0 ############################# ### Maximal size incoming email in byte (50 MB) ############################# message_size_limit = 52428800 ############################# ### Notify user with new email ############################# biff = no ############################# ### appending .domain should be done by MUA ############################# append_dot_mydomain = no ############################# ### "Address Tagging" symbol ############################# recipient_delimiter = + compatibility_level = 2 EOF cat <<'EOF'>>/etc/postfix/mysql-virtual-mailbox-domains.cf user = mailowner password = SomeMailPassword hosts = 127.0.0.1 dbname = maildb query = SELECT 1 FROM domains WHERE domain ='%s' EOF cat <<'EOF'>>/etc/postfix/mysql-virtual-mailbox-maps.cf user = mailowner password = SomeMailPassword hosts = 127.0.0.1 dbname = maildb query = SELECT 1 FROM users WHERE email='%s' EOF cat <<'EOF'>>/etc/postfix/mysql-virtual-alias-maps.cf user = mailowner password = SomeMailPassword hosts = 127.0.0.1 dbname = maildb query = SELECT destination FROM aliases WHERE source='%s' and enabled=1 EOF cat <<'EOF'>>/etc/postfix/mysql-virtual-email2email.cf user = mailowner password = SomeMailPassword hosts = 127.0.0.1 dbname = maildb query = SELECT email FROM users WHERE email='%s' EOF cat <<'EOF'>>/etc/postfix/helo_access [YOUR.DOMAIN] REJECT Get lost - you are not who you say you are! mail[YOUR.DOMAIN] REJECT Get lost - you are not who you say you are! imap.[YOUR.DOMAIN] REJECT Get lost - you are not who you say you are! smtp.[YOUR.DOMAIN] REJECT Get lost - you are not who you say you are! www.[YOUR.DOMAIN] REJECT Get lost - you are not who you say you are! EOF service postfix restart postmap /etc/postfix/helo_access service postfix reload
Now, TEST the entries:
postmap -q [YOUR.DOMAIN] mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf (result: 1) postmap -q admin@[YOUR.DOMAIN] mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf (result: 1) postmap -q webmaster@[YOUR.DOMAIN] mysql:/etc/postfix/mysql-virtual-alias-maps.cf (no result) postmap -q info@[YOUR.DOMAIN] mysql:/etc/postfix/mysql-virtual-alias-maps.cf (result: frank@[YOUR.DOMAIN]) cp /etc/postfix/master.cf /etc/postfix/master.cf.bak vi /etc/postfix/master.cf: # # Postfix master process configuration file. For details on the format # of the file, see the master(5) manual page (command: "man 5 master" or # on-line: http://www.postfix.org/master.5.html). # # Do not forget to execute "postfix reload" after editing this file. # # ========================================================================== # service type private unpriv chroot wakeup maxproc command + args # (yes) (yes) (no) (never) (100) # ========================================================================== #smtp inet n - y - - smtpd smtp inet n - y - 1 postscreen -o smtpd_sasl_auth_enable=no smtpd pass - - y - - smtpd dnsblog unix - - y - 0 dnsblog tlsproxy unix - - y - 0 tlsproxy submission inet n - y - - smtpd -o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes -o smtpd_sasl_type=dovecot -o smtpd_sasl_path=private/auth -o smtpd_sasl_security_options=noanonymous # -o smtpd_reject_unlisted_recipient=no -o smtpd_client_restrictions=$mua_client_restrictions # -o smtpd_helo_restrictions=$mua_helo_restrictions -o smtpd_sender_restrictions=$mua_sender_restrictions -o smtpd_recipient_restrictions=$mua_client_restrictions -o smtpd_relay_restrictions=$mua_relay_restrictions -o smtpd_sender_login_maps=mysql:/etc/postfix/sql/sender-login-maps.cf -o smtpd_helo_required=no -o smtpd_helo_restrictions= -o cleanup_service_name=submission-header-cleanup -o milter_macro_daemon_name=ORIGINATING #smtps inet n - y - - smtpd # -o syslog_name=postfix/smtps # -o smtpd_tls_wrappermode=yes # -o smtpd_sasl_auth_enable=yes # -o smtpd_reject_unlisted_recipient=no # -o smtpd_client_restrictions=$mua_client_restrictions # -o smtpd_helo_restrictions=$mua_helo_restrictions # -o smtpd_sender_restrictions=$mua_sender_restrictions # -o smtpd_recipient_restrictions= # -o smtpd_relay_restrictions=permit_sasl_authenticated,reject # -o milter_macro_daemon_name=ORIGINATING #628 inet n - y - - qmqpd pickup unix n - y 60 1 pickup cleanup unix n - y - 0 cleanup qmgr unix n - n 300 1 qmgr tlsmgr unix - - y 1000? 1 tlsmgr rewrite unix - - y - - trivial-rewrite bounce unix - - y - 0 bounce defer unix - - y - 0 bounce trace unix - - y - 0 bounce verify unix - - y - 1 verify flush unix n - y 1000? 0 flush proxymap unix - - n - - proxymap proxywrite unix - - n - 1 proxymap smtp unix - - y - - smtp relay unix - - y - - smtp # -o smtp_helo_timeout=5 -o smtp_connect_timeout=5 showq unix n - y - - showq error unix - - y - - error retry unix - - y - - error discard unix - - y - - discard local unix - n n - - local virtual unix - n n - - virtual lmtp unix - - y - - lmtp anvil unix - - y - 1 anvil scache unix - - y - 1 scache # # Clean up MUA header: submission-header-cleanup unix n - n - 0 cleanup -o header_checks=regexp:/etc/postfix/submission_header_cleanup # ==================================================================== # Interfaces to non-Postfix software. Be sure to examine the manual # pages of the non-Postfix software to find out what options it wants. # # Many of the following services use the Postfix pipe(8) delivery # agent. See the pipe(8) man page for information about ${recipient} # and other message envelope options. # ==================================================================== # # maildrop. See the Postfix MAILDROP_README file for details. # Also specify in main.cf: maildrop_destination_recipient_limit=1 # maildrop unix - n n - - pipe flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient} # # ==================================================================== # # Recent Cyrus versions can use the existing "lmtp" master.cf entry. # # Specify in cyrus.conf: # lmtp cmd="lmtpd -a" listen="localhost:lmtp" proto=tcp4 # # Specify in main.cf one or more of the following: # mailbox_transport = lmtp:inet:localhost # virtual_transport = lmtp:inet:localhost # # ==================================================================== # # Cyrus 2.1.5 (Amos Gouaux) # Also specify in main.cf: cyrus_destination_recipient_limit=1 # #cyrus unix - n n - - pipe # user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user} # # ==================================================================== # Old example of delivery via Cyrus. # #old-cyrus unix - n n - - pipe # flags=R user=cyrus argv=/cyrus/bin/deliver -e -m ${extension} ${user} # # ==================================================================== # # See the Postfix UUCP_README file for configuration details. # uucp unix - n n - - pipe flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient) # # Other external delivery methods. # ifmail unix - n n - - pipe flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient) bsmtp unix - n n - - pipe flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient scalemail-backend unix - n n - 2 pipe flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension} mailman unix - n n - - pipe flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py ${nexthop} ${user} chmod -R o-rwx /etc/postfix service postfix restart cat<<"EOF'>>/etc/postfix/submission_header_cleanup ########## ### MUA header cleanup - this file is referenced in master.cf ######### /^Received:/ IGNORE /^X-Originating-IP:/ IGNORE /^X-Mailer:/ IGNORE /^User-Agent:/ IGNORE

Virtual Mail setup

mkdir -p /var/vmail/mailboxes mkdir -p /var/vmail/sieve/global chown -R vmail /var/vmail chgrp -R vmail /var/vmail chmod -R 770 /var/vmail ## Create the mail user - there is a standard "mail" one, so we'll create a virtual one: adduser --disabled-login --disabled-password --home /var/vmail vmail

Dovecot - Mail User Agent

service stop dovecot rm -rf /etc/dovecot/* cat <<'EOF'>>/etc/dovecot/dovecot.conf ############################# ### Activated Protocols ############################# protocols = imap lmtp sieve ############################# ### SSL Config ############################# ssl = required ssl_cert = </etc/letsencrypt/live/mail.[YOUR.DOMAIN]/fullchain.pem ssl_key = </etc/letsencrypt/live/mail.[YOUR.DOMAIN]/privkey.pem ssl_dh_parameters_length = 4096 ssl_protocols = !SSLv3 ssl_cipher_list = EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA256:EECDH:+CAMELLIA128:+AES128:!SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!IDEA:!ECDSA:kEDH:CAMELLIA128-SHA:AES128-SHA ssl_prefer_server_ciphers = yes ############################# ### Dovecot services ############################# service imap-login { inet_listener imap { port = 0 } inet_listener imaps { port = 993 ssl = yes } } service managesieve-login { inet_listener sieve { port = 4190 } } service lmtp { unix_listener /var/spool/postfix/private/dovecot-lmtp { # mode = 0660 mode = 0600 group = postfix user = postfix } # user = vmail } service auth { ### Postfix Auth socket unix_listener /var/spool/postfix/private/auth { mode = 0660 user = postfix group = postfix } ### LMTP Auth socket unix_listener auth-userdb { mode = 0660 user = vmail group = vmail } } ############################# ### Protocol settings ############################# protocol imap { mail_plugins = $mail_plugins quota imap_quota imap_sieve mail_max_userip_connections = 20 imap_idle_notify_interval = 29 mins } protocol lmtp { postmaster_address = postmaster@[YOUR.DOMAIN] mail_plugins = $mail_plugins sieve } ############################# ### Client authentication ############################# disable_plaintext_auth = yes auth_mechanisms = plain login passdb { driver = sql args = /etc/dovecot/dovecot-sql.conf } userdb { driver = sql args = /etc/dovecot/dovecot-sql.conf } ############################# ### Mail location ############################# mail_uid = vmail mail_gid = vmail mail_privileged_group = vmail mail_home = /var/vmail/mailboxes/%d/%n mail_location = maildir:~/mail:LAYOUT=fs ############################# ### Mailbox configuration ############################# namespace inbox { inbox = yes mailbox Spam { auto = subscribe special_use = \Junk } mailbox Trash { auto = subscribe special_use = \Trash } mailbox Drafts { auto = subscribe special_use = \Drafts } mailbox Sent { auto = subscribe special_use = \Sent } } ############################# ### Mail plugins ############################# plugin { sieve_plugins = sieve_imapsieve sieve_extprograms sieve_before = /var/vmail/sieve/global/spam-global.sieve sieve = file:/var/vmail/sieve/%d/%n/scripts;active=/var/vmail/sieve/%d/%n/active-script.sieve ### ### Spam learning ### # From elsewhere to Spam folder imapsieve_mailbox1_name = Spam imapsieve_mailbox1_causes = COPY imapsieve_mailbox1_before = file:/var/vmail/sieve/global/learn-spam.sieve # From Spam folder to elsewhere imapsieve_mailbox2_name = * imapsieve_mailbox2_from = Spam imapsieve_mailbox2_causes = COPY imapsieve_mailbox2_before = file:/var/vmail/sieve/global/learn-ham.sieve sieve_pipe_bin_dir = /usr/bin sieve_global_extensions = +vnd.dovecot.pipe quota = maildir:User quota quota_exceeded_message = Gebruiker %u gebruikt meer ruimte dan toegestaan. / User %u has exhausted allowed storage space. } EOF

Dovecot (MUA) - connect to MariaDb

cat<<'EOF'>>/etc/dovecot/dovecot-sql.conf # There are a few special variables you can use, eg.: # # %u - username # %n - user part in user@domain, same as %u if there's no domain # %d - domain part in user@domain, empty if there's no domain # %h - home directory # driver=mysql connect = "host=127.0.0.1 dbname=maildb user=mailowner password=SomeMailPassword" default_pass_scheme = SHA512-CRYPT # select u.email as user, d.domain as domain from users u, domains d where u.domain_id = d.id; password_query = SELECT email AS user, password FROM users where email = '%u' AND enabled = true; user_query = SELECT concat('*:storage=', quota, 'M') AS quota_rule FROM users WHERE email = '%u' AND sendonly = false; iterate_query = SELECT u.email as username, d.domain FROM users u, domains d where u.domain_id = d.id AND u.sendonly = false; EOF chmod 440 /etc/dovecot/dovecot-sql.conf

Files, mentioned in main.cf

mkdir /etc/postfix/sql cd /etc/postfix/sql cat<<'EOF'>>accounts.cf user = mailowner password = SomeMailPassword hosts = 127.0.0.1 dbname = maildb query = select 1 as found FROM users WHERE email = '%s' AND enabled = true LIMIT 1; EOF cat<<'EOF'>>aliases.cf: user = mailowner password = SomeMailPassword hosts = 127.0.0.1 dbname = maildb query = select destination FROM aliases WHERE source = '%s' AND enabled=TRUE EOF cat<<'EOF'>>domains.cf: user = mailowner password = SomeMailPassword hosts = 127.0.0.1 dbname = maildb query = select domain FROM domains WHERE domain='%s' EOF cat<<'EOF'>>recipient-access.cf user = mailowner password = SomeMailPassword hosts = 127.0.0.1 dbname = maildb query = select if(sendonly = true, 'REJECT', 'OK') AS access from users where email = '%s' and enabled = true LIMIT 1; EOF cat<<'EOF'>>sender-login-maps.cf user = mailowner password = SomeMailPassword hosts = 127.0.0.1 dbname = maildb query = select email as 'owns' from users where email = '%s' AND enabled=TRUE union select destination as 'owns' from aliases where source = '%s' AND enabled=TRUE EOF cat<<'EOF'>>tls-policy.cf # option not being used user = mailowner password = SomeMailPassword hosts = 127.0.0.1 dbname = maildb query = SELECT policy, params FROM tlspolicies WHERE domain = '%s' EOF cat<<'EOF'>>/etc/postfix/without_ptr # use postmap /etc/postfix/without_ptr 127.0.0.1 OK EOF # make postfix aware... Need to do after every alteration... postmap /etc/postfix/without_ptr touch /etc/postfix/postscreen_access chmod -R 640 /etc/postfix/sql service postfix restart

Anti Spam - filter & rspamd

cat<<'EOF'>>/var/vmail/sieve/global/spam-global.sieve require "fileinto"; if header :contains "X-Spam-Flag" "YES" { fileinto "Spam"; } if header :is "X-Spam" "Yes" { fileinto "Spam"; } EOF cat<<'EOF'>>/var/vmail/sieve/global/learn-spam.sieve require ["vnd.dovecot.pipe", "copy", "imapsieve"]; pipe :copy "rspamc" ["learn_spam"]; EOF cat<<'EOF'>>/var/vmail/sieve/global/learn-ham.sieve require ["vnd.dovecot.pipe", "copy", "imapsieve"]; pipe :copy "rspamc" ["learn_ham"]; EOF apt install -y lsb-release wget -O- https://rspamd.com/apt-stable/gpg.key | apt-key add - echo "deb http://rspamd.com/apt-stable/ $(lsb_release -c -s) main" > /etc/apt/sources.list.d/rspamd.list echo "deb-src http://rspamd.com/apt-stable/ $(lsb_release -c -s) main" >> /etc/apt/sources.list.d/rspamd.list apt update apt install -y rspamd systemctl stop rspamd cd /etc/rspamd/local.d/ cat<<'EOF'>>/etc/rspamd/local.d/options.inc local_addrs = "127.0.0.0/8, ::1"; dns { nameserver = ["192.168.4.200:53:10"]; } EOF cat<<'EOF'>>worker-normal.inc bind_socket = "localhost:11333"; ### Number of Workers. Defaults to # of cores (4). # count = 1 EOF root@mail:/etc/rspamd/local.d# rspamadm help Rspamadm 1.7.4 Usage: rspamadm [global_options] command [command_options] Available commands: pw Manage rspamd passwords keypair Create encryption key pairs configtest Perform configuration file test fuzzy_merge Merge fuzzy databases configdump Perform configuration file dump control Manage rspamd main control interface confighelp Shows help for configuration options statconvert Convert statistics from sqlite3 to redis fuzzyconvert Convert fuzzy hashes from sqlite3 to redis grep Search for patterns in rspamd logs signtool Sign and verify files tool lua Run LUA interpreter dkim_keygen Create dkim key pairs configwizard Perform guided configuration for Rspamd daemon corpus_test Create logs files from email corpus rescore Estimate optimal symbol weights from log files root@mail:/etc/rspamd/local.d# rspamadm pw [YOUR.RSPAMD.PASSWORD] $2$7..(snipped)..db cat<<'EOF'>>worker-controller.inc password=$2$7..(snipped)..db EOF cat<<'EOF'>>worker-proxy.inc bind_socket = "localhost:11332"; milter = yes; timeout = 120s; upstream "local" { default = yes; self_scan = yes; } EOF cat<<'EOF'>>logging.inc type = "file"; filename = "/var/log/rspamd/rspamd.log"; level = "error"; debug_modules = []; EOF cat<<'EOF'>>milter_headers.conf use = ["x-spamd-bar", "x-spam-level", "authentication-results"]; authenticated_headers = ["authentication-results"]; EOF cat<<'EOF'>>classifier-bayes.conf backend = "redis"; EOF

DKIM

mkdir /var/lib/rspamd/dkim/ rspamadm dkim_keygen -b 2048 -s 2018 -k /var/lib/rspamd/dkim/2018.key > /var/lib/rspamd/dkim/2018.txt chown -R _rspamd:_rspamd /var/lib/rspamd/dkim chmod 440 /var/lib/rspamd/dkim/* cat /var/lib/rspamd/dkim/2018.txt (it's the DKIM DNS record!) 2018._domainkey IN TXT ( "v=DKIM1; k=rsa; " "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzi6rHX6lDrUSsteJFB1r4tv6y+rJ8Yb9Ma6Bf+B0hdLAqh2JBoV1TiqNu8ChuR795wsm6NzwG6l+zU6OxtzsahjwqothX8AFEdpdR0ToagUpVUB6h8VkWds4GtUcw/GLJDt7L++hUUcnMK2yZ0gbeD2hC1dLeDSiA20CHyesIAAld+8QpHQx9uPEKSmUo0JBGLEys5F3NbSv/mBIa" "aha2GPJU6sIb6KVArXp9wFCUaXT26qiEIXck0upcLE7ml9zzUgmyHkqp723o9RW9YJ87GyNk6rzRN5aIUNBeKpcQyTaDHL/UlR8tl31WChw0Tq0vA1WXaHEwV1xf5SBhhElzQIDAQAB" ) ;
Add a TXT record, named 2018._domainkey. It should contain all between the quotes, without the quotes, extra whitespace, etc:
v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzi6rHX6lDrUSsteJFB1r4tv6y+rJ8Yb9Ma6Bf+B0hdLAqh2JBoV1TiqNu8ChuR795wsm6NzwG6l+zU6OxtzsahjwqothX8AFEdpdR0ToagUpVUB6h8VkWds4GtUcw/GLJDt7L++hUUcnMK2yZ0gbeD2hC1dLeDSiA20CHyesIAAld+8QpHQx9uPEKSmUo0JBGLEys5F3NbSv/mBIaaha2GPJU6sIb6KVArXp9wFCUaXT26qiEIXck0upcLE7ml9zzUgmyHkqp723o9RW9YJ87GyNk6rzRN5aIUNBeKpcQyTaDHL/UlR8tl31WChw0Tq0vA1WXaHEwV1xf5SBhhElzQIDAQAB cat<<'EOF'>>/etc/rspamd/local.d/dkim_signing.conf path = "/var/lib/rspamd/dkim/$selector.key"; selector = "2018"; ### Enable DKIM signing for alias sender addresses allow_username_mismatch = true; EOF
Copy to ARC (see: https://rspamd.com/doc/modules/arc.html) config:
cp -R /etc/rspamd/local.d/dkim_signing.conf /etc/rspamd/local.d/arc.conf

Redis cache

apt install -y redis-server cat<<'EOF'>>/etc/rspamd/local.d/redis.conf servers = "127.0.0.1"; EOF systemctl start rspamd

FINALIZE

Add users

Add email users to the database. The passwords are encrypted, and need some work to generate:
doveadm pw -s SHA512-CRYPT Enter new password: [PASSWORD FOR MAIL USER GOES HERE] retype new password: [ Same Password ] {SHA512-CRYPT}$6$...(snipped)... mysql -p use maildb insert into users (domain_id, password , email, enabled, sendonly) values (1, '{SHA512-CRYPT}$6$...(snipped)...', 'frank@[YOUR.DOMAIN]',true, false);
Repeat for admin@[YOUR.DOMAIN] (send-only account):
insert into users (domain_id, password , email, enabled, sendonly) values (1, '{SHA512-CRYPT}$6$...(snipped)...', 'admin@[YOUR.DOMAIN]',true, true);

Connect

Connecting your email program is not straightforward (that is: androids wizards will fail); the account to be used is frank@[YOUR.DOMAIN], not just frank.

The server uses SSL/TLS on port 993 for IMAP, and STARTTLS on port 587 for SMTP.
You may use imap.[YOUR.DOMAIN] for imap (incoming) mail, smtp.[YOUR.DOMAIN] as sending server, of mail.[YOUR.DOMAIN] for either, or both. Your choice.

You will have to go into the details to alter the username; it will probably default to frank where it should be frank@[YOUR.DOMAIN].

GDPR or AVG - regain control Part 1: Your Own Cloud

Create your own Cloud

Replace Google or Dropbox, and gain control over you own data. Encrypt it, protect it, share your data only with who you want.

ARM, Ubuntu and secured Nextcloud

This episode will be followed by an entry on email. For now, I settle for a relatively cheap ARM device (an ODroid XU4, to be precise), run it with Ubuntu, and install NextCloud.
The choice for Ubuntu has everything to do with the availability of software for Ubuntu on ARM devices, therwise it would have been Debian.

Of course, you will need some sort of storage, which I do not include in the equasion here. Plenty of options, even by the same manufacturer.

Hardware considerations

The basic setup involved buying the device. You might want to opt for the newer 64-bit Single Board Computers (SBC), like the RaspBerry Pi 3, PINE ROCK64, or the ODROID C2, but I have chosen the XU4, with a 32-bit processor, but 8 cores, in stead of 4. This benefits the performance over products like the C2, because I can run more threads. The penalty comes in the form of memory addressing space, which is not a real problem, unless you want to upload files greater than 2GB...

Anyway, costs involved here are about USD 80 at the time of writing. Not only do you need the device it self, it needs a power supply, an eMMC memory module (just do it - performance is sooo much better than a micro SD card), an eMMC adapter and a case would be nice, too. I currently own a PINE Rock64, an ODroid C2 and XU4, and a Nanopi 2 Fire and got my XU4 with all components mentioned from ameriDroid.com, because they had the XU4 on stock. Very speedy delivery, and a personal "Thank you Frank" on the receipt by Renate & Amanda - how nice!

Basic setup

So, you have your device delivered, unpack it, hook it up to you screen, using the HDMI connector, and attach a USB keyboard. Fire it up, and see if the image of your choice (I ordered an Ubuntu eMMC image) boots correctly. If so, power down and remove the eMMC card. Connect it to the eMMC adapter, and insert the adapter in an SD slot on your (presumably, MS Windows) PC.

Download Etcher, the excellent image burner. Download a Minimal Ubuntu Xenial (16.04) version. Locations where to download from can be found on the Wiki page. I have chosen the ubuntu-16.04.3-4.14-minimal-odroid-xu4-20171213.img.xz.

Use Echter to burn the image (and Ether will unpack it, too!) to the eMMC (or SD) card. Move the card back to the XU4, and power up. It will boot, and spin down. This is normal! Just wait a minute, and power up again - it performed a resize of the root filesystem to max out the usage of your eMMC/SD card. Log in as root, with odroid as password. Change the password:
root@odroid:~# passwd

Let's get cracking: security!

You have now chosen a good, strong password for the root account. Good. BTW, strong does not mean you have use numbers and signs, it means you have a password with high entropy. Please note: a password, consisting of 12 upper and lower case characters has an entropy of 8 byte. Using 8 random characters (All ASCII printable) is just over 5 byte. Using an 8 character password with upper and lower case, numbers and signs (which lots of systems use...) is actually LESS SECURE than using 12 letter passwords. Please take a look at this...

Time to loose passwords! (but that's another blog entry)

Still as root, perform the following:
root@odroid:~# apt update root@odroid:~# apt upgrade root@odroid:~# apt dist-upgrade root@odroid:~# apt install linux-image-xu3
... (xu3! yes, that is not a typo. Answer No to the Abort Now? question) add to /etc/fstab:
tmpfs /run/shm tmpfs defaults,noexec,nosuid 0 0
Now, it is time to reboot with the latest kernel and programs:
root@odroid:~# shutdown -r now

Security: enable logins over SSH

I want to be able to remotely administer my devices, so I want SSH running. First of all, check on the versions. Most of the SBC images are created by enthousiasts, but maintaining images for all different SBC's is a hell of a job, so not all software will be at the last stand.
So, check the current version of SSL software, and get the latest:
root@odroid:~# openssl version --> 1.0.2g mar 2016 (!) root@odroid:~# cd /usr/local/src && apt install gcc make -y root@odroid:~# wget https://www.openssl.org/source/openssl-1.1.0h.tar.gz && tar xzvf openssl-1.1.0h.tar.gz && cd openssl-1.1.0h root@odroid:~# ./config -Wl,--enable-new-dtags,-rpath,'$(LIBRPATH)' root@odroid:~# make root@odroid:~# make install root@odroid:~# shutdown -r now root@odroid:~# opeenssl version --> OpenSSL 1.1.0h 27 Mar 2018

Security: enable network time and date

As many security mechanisms work with data, which is only valid for a limited time frame, having the correct time is crucial (at least, for the client and the server). Those little SBCs I have, do not all have battery backed up Real Time Clocks (RTCs) like AMD/Intel computer motherboards. There is a successor of the Network Time Protocol, timedatectl, which also handles calls of programs to RTCs.
root@odroid:~# vi /etc/systemd/timesyncd.conf [Time] # choose ntp servers geographically close to you NTP=3.nl.pool.ntp.org 0.nl.pool.ntp.org 2.nl.pool.ntp.org 1.nl.pool.ntp.org FallbackNTP=0.debian.pool.ntp.org 0.pool.ntp.org 1.pool.ntp.org 0.nl.pool.ntp.org # check/correct timezone root@odroid:~# dpkg-reconfigure tzdata # Check with: root@odroid:~# timedatectl Local time: Tue 2018-04-24 18:11:43 CEST Universal time: Tue 2018-04-24 16:11:43 UTC RTC time: Tue 2018-04-24 16:11:44 Time zone: Europe/Amsterdam (CEST, +0200) Network time on: yes NTP synchronized: yes RTC in local TZ: no

Security: network and host

Now, it is time to set up network and hostname. As this is a server, I want fixed hostname and IP-addresses, as well as some security measures... First of all: rework /etc/sysctl.conf:
# Uncomment the next two lines to enable Spoof protection (reverse-path filter) # Turn on Source Address Verification in all interfaces to # prevent some spoofing attacks net.ipv4.conf.default.rp_filter=1 net.ipv4.conf.all.rp_filter=1 # Uncomment the next line to enable TCP/IP SYN cookies # See http://lwn.net/Articles/277146/ # Note: This may impact IPv6 TCP sessions too net.ipv4.tcp_syncookies=1 net.ipv4.tcp_max_syn_backlog = 2048 net.ipv4.tcp_synack_retries = 2 net.ipv4.tcp_syn_retries = 5 # Do not accept ICMP redirects (prevent MITM attacks) net.ipv4.conf.all.accept_redirects = 0 net.ipv6.conf.all.accept_redirects = 0 # Do not send ICMP redirects (we are not a router) net.ipv4.conf.all.send_redirects = 0 # Do not accept IP source route packets (we are not a router) net.ipv4.conf.all.accept_source_route = 0 net.ipv6.conf.all.accept_source_route = 0 # # Log Martian Packets net.ipv4.conf.all.log_martians = 1 net.ipv4.icmp_ignore_bogus_error_responses = 1 #
Enable IP Spoofing protection, edit /etc/host.conf:
order bind,hosts # multi on nospoof on
Set the hostname:
hostnamectl set-hostname --static cloud
Set a fixed IP-address, disable NetworkManager, fix resolv.conf:
vi /etc/network/interfaces.d/eth0 allow-hotplug eth0 iface eth0 inet static address 192.168.4.220/24 gateway 192.168.4.254 name-server 192.168.4.200 # IPv6 is DHCP, and handled by the router iface eth0 inet6 dhcp root@odroid:~# service network-manager stop root@odroid:~# systemctl disable network-manager vi /etc/resolvconf/resolv.conf.d/head # router insists on adding itself, in stead of my PiHole. # add IPv4 and IPv6 addresses of your own nameserver(s) nameserver 192.168.4.200 shutdown -r now ## # reboot and see if everything still works, then: # sudo apt purge network-manager

Security: add user(s)

For every day maintenance, I use a normal account, that has the possibility for elevated commands (Super User do, or Substitute User do: sudo). Add it using:
adduser frank usermod -G sudo,ssh,operator frank id frank uid=1000(frank) gid=1000(frank) groups=1000(frank),27(sudo),37(operator),111(ssh)
I can now login using ssh frank@192.168.4.220. Check if everything is OK: try if you can use sudo.

Security: clean up

The less programs you have, the less security issues you will encounter. I found some stuff, installed by default, that I do not want/use: - beforementioned network manager - x11 (the GUI; this is a server, no GUI required!) So:
frank@cloud:~$sudo apt purge network-manager frank@cloud:~$sudo apt purge x11-common frank@cloud:~$sudo apt autoremove
I also do not want to be greeted by Ubuntu spam. Change the file /etc/pam.d/sshd from:
# Print the message of the day upon successful login. # This includes a dynamically generated part from /run/motd.dynamic # and a static (admin-editable) part from /etc/motd. session optional pam_motd.so motd=/run/motd.dynamic session optional pam_motd.so noupdate # change to: # Print the message of the day upon successful login. # This includes a dynamically generated part from /run/motd.dynamic # and a static (admin-editable) part from /etc/motd. #session optional pam_motd.so motd=/run/motd.dynamic #session optional pam_motd.so noupdate
Install something different, and less inviting:
frank@cloud:~$ sudo apt install sysvbanner frank@cloud:~$ sudo vi /etc/issue.net Ongeautoriseerde toegang verboden! Unauthorized access forbidden!

Security: enhance encryption

In stead of 1024 bits, I use 4096 bits server keys and Diffie-Hellman parameters. Adapt /etc/sshd_config:
# HostKeys for protocol version 2 HostKey /etc/ssh/ssh_host_rsa_key #HostKey /etc/ssh/ssh_host_dsa_key #HostKey /etc/ssh/ssh_host_ecdsa_key #HostKey /etc/ssh/ssh_host_ed25519_key ... # Lifetime and size of ephemeral version 1 server key KeyRegenerationInterval 3600 ServerKeyBits 4096
Generate the Diffie-Hellman parameters (and take a break while you at it - this takes a long time!), and generate a new server key of 4096 bits:
sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096 sudo ssh-keygen -q -f /etc/ssh/ssh_host_rsa_key -N '' -b 4096 -t rsa

Security: no more password-based logins

Generate a key pair. I use MobaXterm, which can generate keys as well. Copy the public key to the server, in a special file. Make sure you are logged in as your daily maintenace user:
frank@cloud:~$ mkdir ~/.ssh frank@cloud:~$ vi ~/.ssh/authorized_keys # paste your public key, and save the file (ESC, :wq, ENTER) frank@cloud:~$ chmod 700 ~/.ssh frank@cloud:~$ chmod 600 ~/.ssh/authorized_keys
Now, log off, alter your terminal emulator to use your private key for logins, and log in again. As soon as you see 'Authenticating with public key "[any comment you gave when creating the key]"', success! You should now alter /etc/ssh/sshd_config, and alter, add, verify:
RSAAuthentication yes PubkeyAuthentication yes AuthorizedKeysFile %h/.ssh/authorized_keys PasswordAuthentication no
Now, reboot, and try logging in again. If you still see the message with Authenticating with key, it is time to disallow root logins alltogether!

Security: disable root logins

In order to disallow password-based and root logins completely, open /etc/ssh/sshd_config, and alter/add/verify:
PermitRootLogin no PermitEmptyPasswords no ChallengeResponseAuthentication no PasswordAuthentication no UsePAM no # Added AuthenticationMethods publickey Protocol 2 HostKey /etc/ssh/ssh_host_rsa_key ServerKeyBits 4096 SyslogFacility AUTH LogLevel INFO LoginGraceTime 2m StrictModes yes RSAAuthentication yes PubkeyAuthentication yes AuthorizedKeysFile %h/.ssh/authorized_keys
Reboot, and try root logins, and password based logins. You should encounter:
Ongeautoriseerde toegang verboden! Unauthorized access forbidden! Permission denied (publickey).

Let's get installing: nginx with cache!

Goal of it all is to get nginx, a high performance, low memory footprint http server running with NextCloud. I also want lots of caching, as these SBCs are not speedmonsters like the latest AMD or Intel chips. But they are very low power.
Unfortunately, nginx does not come with the cache purge module, so I will have to do that by hand. Log in, switch to a more permanent elevated session, and start installing and building(!) required software:
sudo -s cd /usr/local/src apt update && apt upgrade -y && apt install software-properties-common python-software-properties zip unzip screen curl ffmpeg libfile-fcntllock-perl -y wget http://nginx.org/keys/nginx_signing.key && apt-key add nginx_signing.key apt install language-pack-en-base -y && sudo LC_ALL=en_US.UTF-8 apt autoremove # In case anything might be installed: remove it completely apt remove nginx nginx-common nginx-full -y --allow-change-held-packages vi /etc/apt/sources.list.d/nginx.list # add: deb http://nginx.org/packages/ubuntu/ xenial nginx deb-src http://nginx.org/packages/ubuntu/ xenial nginx apt update
Now, in my case, this command ended with an error:
Err:3 http://nginx.org/packages/ubuntu xenial InRelease The following signatures couldn't be verified because the public key is not available: NO_PUBKEY ABF5BD827BD9BF62
The remedy is to install the nginx repository signing key:
wget http://nginx.org/keys/nginx_signing.key apt-key add ./nginx_signing.key apt update
Now it's ok. The following is to be expected, and can be ignored:
N: Skipping acquire of configured file 'nginx/binary-armhf/Packages' as repository 'http://nginx.org/packages/ubuntu xenial InRelease' doesn't support architecture 'armhf'


We will build it...
apt build-dep nginx -y # should not issue warnings apt source nginx
Errors to be ignored:
gpgv: Can't check signature: public key not found dpkg-source: warning: failed to verify signature on ./nginx_1.14.0-1~xenial.dsc W: Can't drop privileges for downloading as file 'nginx_1.14.0-1~xenial.dsc' couldn't be accessed by user '_apt'. - pkgAcquire::Run (13: Permission denied)
Just go ahead and proceed with:
mkdir -p /usr/local/src/nginx-1.14.0/debian/modules cd /usr/local/src/nginx-1.14.0/debian/modules ### ### THIS IS WHERE WE ADD STUFF TO NGINX ### wget https://github.com/FRiCKLE/ngx_cache_purge/archive/2.3.tar.gz tar -zxvf 2.3.tar.gz && rm 2.3.tar.gz cd .. vi rules # Find with-ld-opt="$(LDFLAGS): [ESC] /with-ld-opt="$(LDFLAGS) after the string, add: --add-module="$(CURDIR)/debian/modules/ngx_cache_purge-2.3" # Find: dh_shlibdeps -a append --dpkg-shlibdeps-params=--ignore-missing-info # save, change to the top directory and create the mew nginx: cd /usr/local/src/nginx-1.14.0 dpkg-buildpackage -uc -b -j4 cd /usr/local/src dpkg --install nginx_1.14.0-1~xenial_armhf.deb # Prevent nginx from being updated automagically: apt-mark hold nginx # Enable nginx service: systemctl enable nginx.service apt-mark hold nginx && systemctl enable nginx.service # alter the config in /etc/nginx/nginx.conf - only changes from the original are listed: 1,2c1,3 < user www-data; < worker_processes auto; --- > > user nginx; > worker_processes 1; 10,11d10 < multi_accept on; < use epoll; 16,19d14 < server_names_hash_bucket_size 64; < upstream php-handler { < server unix:/run/php/php7.2-fpm.sock; < } 21,24d15 < #include /etc/nginx/proxy.conf; < #include /etc/nginx/ssl.conf; < #include /etc/nginx/header.conf; < #include /etc/nginx/optimization.conf; 27,34c18,20 < log_format main '$remote_addr - $remote_user [$time_local] "$request" ' < '$status $body_bytes_sent "$http_referer" ' < '"$http_user_agent" "$http_x_forwarded_for"' < '"$host" sn="$server_name" ' < 'rt=$request_time ' < 'ua="$upstream_addr" us="$upstream_status" ' < 'ut="$upstream_response_time" ul="$upstream_response_length" ' < 'cs=$upstream_cache_status' ; --- > log_format main '$remote_addr - $remote_user [$time_local] "$request" ' > '$status $body_bytes_sent "$http_referer" ' > '"$http_user_agent" "$http_x_forwarded_for"'; 39,43c25,26 < send_timeout 3600; < tcp_nopush on; < tcp_nodelay on; < open_file_cache max=500 inactive=10m; < open_file_cache_errors on; --- > #tcp_nopush on; > 45,50c28,29 < reset_timedout_connection on; < server_tokens off; < resolver 192.168.4.200; < # resolver IP is your name server or router (e.g. your FritzBox) < resolver_timeout 10s;
Now, I have my storage server set up as NFS server, so I need the NFS client, and a permanent, boot resilient entry in the mount file, /etc/fstab:
# install nfs: apt install nfs-client # add to /etc/fstab: 192.168.4.140:/mnt/tank1/ds1/nc /var/nc nfs soft,intr,nosuid 0 0
You will have to work out your own options, like installing the samba client, and use a SMB share (MS Windows share), use local storage or yet another option. With large, 10 to 14TB disks or 2TB SSDs of date you still have a lot of local storage!.

NextCloud needs some directories, so create these:
mkdir -p /var/nc /var/www/letsencrypt /usr/local/tmp/cache /usr/local/tmp/sessions /usr/local/tmp/apc /upload_tmp chown -R www-data:www-data /upload_tmp /var/nc /var/www chown -R www-data:root /usr/local/tmp/sessions /usr/local/tmp/cache /usr/local/tmp/apc
Now, it is time to see what's cooking; test your nginx configuration, and correct any errors. If all is well, you should be able to surf to your localhost, and see the standard nginx welcome page.
# test the nginx configuration: nginx -t # When OK, start and see if purge is compiled within - one should get the ngx_cache_purge response: service nginx start nginx -V 2>&1 | grep ngx_cache_purge -o ngx_cache_purge # You could now do and see the nginx welcome message in html code: curl localhost

Installing and configuring: PHP 7.2

Nextcloud heavily depends on PHP, so I want the latest and the safest: version 7.2. Carsten Rieger has written the stream editor (sed) commands; I merely copied these off his site.
Let's get this show on the road:
apt install language-pack-en-base -y && sudo LC_ALL=en_US.UTF-8 add-apt-repository ppa:ondrej/php -y && apt update apt install php7.2-fpm php7.2-zip php7.2-mysql php7.2-curl php7.2-xml php7.2-intl php7.2-gd php7.2-mbstring php7.2-json php7.2-bz2 php7.2-ldap php-apcu imagemagick php-imagick -y # Thanks to Carsten Rieger: cp /etc/php/7.2/fpm/pool.d/www.conf /etc/php/7.2/fpm/pool.d/www.conf.bak cp /etc/php/7.2/cli/php.ini /etc/php/7.2/cli/php.ini.bak cp /etc/php/7.2/fpm/php.ini /etc/php/7.2/fpm/php.ini.bak cp /etc/php/7.2/fpm/php-fpm.conf /etc/php/7.2/fpm/php-fpm.conf.bak sed -i "s/;env\[HOSTNAME\] = /env[HOSTNAME] = /" /etc/php/7.2/fpm/pool.d/www.conf sed -i "s/;env\[TMP\] = /env[TMP] = /" /etc/php/7.2/fpm/pool.d/www.conf sed -i "s/;env\[TMPDIR\] = /env[TMPDIR] = /" /etc/php/7.2/fpm/pool.d/www.conf sed -i "s/;env\[TEMP\] = /env[TEMP] = /" /etc/php/7.2/fpm/pool.d/www.conf sed -i "s/;env\[PATH\] = /env[PATH] = /" /etc/php/7.2/fpm/pool.d/www.conf sed -i "s/pm.max_children = .*/pm.max_children = 240/" /etc/php/7.2/fpm/pool.d/www.conf sed -i "s/pm.start_servers = .*/pm.start_servers = 20/" /etc/php/7.2/fpm/pool.d/www.conf sed -i "s/pm.min_spare_servers = .*/pm.min_spare_servers = 10/" /etc/php/7.2/fpm/pool.d/www.conf sed -i "s/pm.max_spare_servers = .*/pm.max_spare_servers = 20/" /etc/php/7.2/fpm/pool.d/www.conf sed -i "s/;pm.max_requests = 500/pm.max_requests = 500/" /etc/php/7.2/fpm/pool.d/www.conf sed -i "s/output_buffering =.*/output_buffering = 'Off'/" /etc/php/7.2/cli/php.ini sed -i "s/max_execution_time =.*/max_execution_time = 1800/" /etc/php/7.2/cli/php.ini sed -i "s/max_input_time =.*/max_input_time = 3600/" /etc/php/7.2/cli/php.ini sed -i "s/post_max_size =.*/post_max_size = 10240M/" /etc/php/7.2/cli/php.ini sed -i "s/;upload_tmp_dir =.*/upload_tmp_dir = \/upload_tmp/" /etc/php/7.2/cli/php.ini sed -i "s/upload_max_filesize =.*/upload_max_filesize = 10240M/" /etc/php/7.2/cli/php.ini sed -i "s/max_file_uploads =.*/max_file_uploads = 100/" /etc/php/7.2/cli/php.ini sed -i "s/;date.timezone.*/date.timezone = Europe\/\Berlin/" /etc/php/7.2/cli/php.ini sed -i "s/;session.cookie_secure.*/session.cookie_secure = True/" /etc/php/7.2/cli/php.ini sed -i "s/;session.save_path =.*/session.save_path = \"N;700;\/usr\/local\/tmp\/sessions\"/" /etc/php/7.2/cli/php.ini sed -i '$aapc.enable_cli = 1' /etc/php/7.2/cli/php.ini sed -i "s/memory_limit = 128M/memory_limit = 512M/" /etc/php/7.2/fpm/php.ini sed -i "s/output_buffering =.*/output_buffering = 'Off'/" /etc/php/7.2/fpm/php.ini sed -i "s/max_execution_time =.*/max_execution_time = 1800/" /etc/php/7.2/fpm/php.ini sed -i "s/max_input_time =.*/max_input_time = 3600/" /etc/php/7.2/fpm/php.ini sed -i "s/post_max_size =.*/post_max_size = 10240M/" /etc/php/7.2/fpm/php.ini sed -i "s/;upload_tmp_dir =.*/upload_tmp_dir = \/upload_tmp/" /etc/php/7.2/fpm/php.ini sed -i "s/upload_max_filesize =.*/upload_max_filesize = 10240M/" /etc/php/7.2/fpm/php.ini sed -i "s/max_file_uploads =.*/max_file_uploads = 100/" /etc/php/7.2/fpm/php.ini sed -i "s/;date.timezone.*/date.timezone = Europe\/\Berlin/" /etc/php/7.2/fpm/php.ini sed -i "s/;session.cookie_secure.*/session.cookie_secure = True/" /etc/php/7.2/fpm/php.ini sed -i "s/;opcache.enable=.*/opcache.enable=1/" /etc/php/7.2/fpm/php.ini sed -i "s/;opcache.enable_cli=.*/opcache.enable_cli=1/" /etc/php/7.2/fpm/php.ini sed -i "s/;opcache.memory_consumption=.*/opcache.memory_consumption=128/" /etc/php/7.2/fpm/php.ini sed -i "s/;opcache.interned_strings_buffer=.*/opcache.interned_strings_buffer=8/" /etc/php/7.2/fpm/php.ini sed -i "s/;opcache.max_accelerated_files=.*/opcache.max_accelerated_files=10000/" /etc/php/7.2/fpm/php.ini sed -i "s/;opcache.revalidate_freq=.*/opcache.revalidate_freq=1/" /etc/php/7.2/fpm/php.ini sed -i "s/;opcache.save_comments=.*/opcache.save_comments=1/" /etc/php/7.2/fpm/php.ini sed -i "s/;session.save_path =.*/session.save_path = \"N;700;\/usr\/local\/tmp\/sessions\"/" /etc/php/7.2/fpm/php.ini sed -i "s/;emergency_restart_threshold =.*/emergency_restart_threshold = 10/" /etc/php/7.2/fpm/php-fpm.conf sed -i "s/;emergency_restart_interval =.*/emergency_restart_interval = 1m/" /etc/php/7.2/fpm/php-fpm.conf sed -i "s/;process_control_timeout =.*/process_control_timeout = 10s/" /etc/php/7.2/fpm/php-fpm.conf sed -i '$aapc.enabled=1' /etc/php/7.2/fpm/php.ini sed -i '$aapc.file_update_protection=2' /etc/php/7.2/fpm/php.ini sed -i '$aapc.optimization=0' /etc/php/7.2/fpm/php.ini sed -i '$aapc.shm_size=256M' /etc/php/7.2/fpm/php.ini sed -i '$aapc.include_once_override=0' /etc/php/7.2/fpm/php.ini sed -i '$aapc.shm_segments=1' /etc/php/7.2/fpm/php.ini sed -i '$aapc.ttl=7200' /etc/php/7.2/fpm/php.ini sed -i '$aapc.user_ttl=7200' /etc/php/7.2/fpm/php.ini sed -i '$aapc.gc_ttl=3600' /etc/php/7.2/fpm/php.ini sed -i '$aapc.num_files_hint=1024' /etc/php/7.2/fpm/php.ini sed -i '$aapc.enable_cli=0' /etc/php/7.2/fpm/php.ini sed -i '$aapc.max_file_size=5M' /etc/php/7.2/fpm/php.ini sed -i '$aapc.cache_by_default=1' /etc/php/7.2/fpm/php.ini sed -i '$aapc.use_request_time=1' /etc/php/7.2/fpm/php.ini sed -i '$aapc.slam_defense=0' /etc/php/7.2/fpm/php.ini sed -i '$aapc.mmap_file_mask=/usr/local/tmp/apc/apc.XXXXXX' /etc/php/7.2/fpm/php.ini sed -i '$aapc.stat_ctime=0' /etc/php/7.2/fpm/php.ini sed -i '$aapc.canonicalize=1' /etc/php/7.2/fpm/php.ini sed -i '$aapc.write_lock=1' /etc/php/7.2/fpm/php.ini sed -i '$aapc.report_autofilter=0' /etc/php/7.2/fpm/php.ini sed -i '$aapc.rfc1867=0' /etc/php/7.2/fpm/php.ini sed -i '$aapc.rfc1867_prefix =upload_' /etc/php/7.2/fpm/php.ini sed -i '$aapc.rfc1867_name=APC_UPLOAD_PROGRESS' /etc/php/7.2/fpm/php.ini sed -i '$aapc.rfc1867_freq=0' /etc/php/7.2/fpm/php.ini sed -i '$aapc.rfc1867_ttl=3600' /etc/php/7.2/fpm/php.ini sed -i '$aapc.lazy_classes=0' /etc/php/7.2/fpm/php.ini sed -i '$aapc.lazy_functions=0' /etc/php/7.2/fpm/php.ini sed -i "s/09,39.*/# &/" /etc/cron.d/php (crontab -l ; echo "09,39 * * * * /usr/lib/php/sessionclean 2>&1") | crontab -u root -

Configuring: ramdisk

In order to speed things up, some file locations can be located in RAM. There is a association to the owner, www-data. Find out what the UID od www-data is, and use that. In my system, www-data is a default users with id 33. If yours differs from 33, change it before using this code:
id www-data # results in 33 - if not, change following code accordingly: sed -i '$atmpfs /tmp tmpfs defaults,noatime,nosuid,nodev,noexec,mode=1777 0 0' /etc/fstab sed -i '$atmpfs /var/tmp tmpfs defaults,noatime,nosuid,nodev,noexec,mode=1777 0 0' /etc/fstab sed -i '$atmpfs /usr/local/tmp/apc tmpfs defaults,uid=33,size=300M,noatime,nosuid,nodev,noexec,mode=1777 0 0' /etc/fstab sed -i '$atmpfs /usr/local/tmp/cache tmpfs defaults,uid=33,size=300M,noatime,nosuid,nodev,noexec,mode=1777 0 0' /etc/fstab sed -i '$atmpfs /usr/local/tmp/sessions tmpfs defaults,uid=33,size=300M,noatime,nosuid,nodev,noexec,mode=1777 0 0' /etc/fstab mount -a service php7.2-fpm restart && service nginx restart

Installing and configuring: database

NextCloud uses a database. I choose to install MariaDb, the OSS MySQL version:
apt update && apt install mariadb-server -y mysql_secure_installation
When asked for the root password, press enter. Skip (answer N) the first question about changing the root password; answer Y to all other questions.
mv /etc/mysql/my.cnf /etc/mysql/my.cnf.bak vi /etc/mysql/my.cnf # add to my.cnf (it is now a new file): [server] skip-name-resolve innodb_buffer_pool_size = 128M innodb_buffer_pool_instances = 1 innodb_flush_log_at_trx_commit = 2 innodb_log_buffer_size = 32M innodb_max_dirty_pages_pct = 90 long_query_time = 1 max_heap_table_size= 64M query_cache_type = 1 query_cache_limit = 2M query_cache_min_res_unit = 2k query_cache_size = 64M tmp_table_size= 64M slow-query-log = 1 slow-query-log-file = /var/log/mysql/slow.log [client-server] !includedir /etc/mysql/conf.d/ !includedir /etc/mysql/mariadb.conf.d/ [client] default-character-set = utf8mb4 [mysqld] character-set-server = utf8mb4 collation-server = utf8mb4_general_ci binlog_format = MIXED innodb_large_prefix=on innodb_file_format=barracuda innodb_file_per_table=1
Save the file, and start db creation:
service mysql restart mysql -uroot -p CREATE DATABASE nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER nextcloud@localhost identified by '[your password]'; GRANT ALL PRIVILEGES on nextcloud.* to nextcloud@localhost; FLUSH privileges; quit;

Configuring: prepare nginx for NextCloud, and to use https

service nginx stop mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak vi /etc/nginx/conf.d/nextcloud.conf fastcgi_cache_path /usr/local/tmp/cache levels=1:2 keys_zone=NEXTCLOUD:100m inactive=60m; map $request_uri $skip_cache { default 1; ~*/thumbnail.php 0; ~*/apps/galleryplus/ 0; ~*/apps/gallery/ 0; } server { server_name YOUR.SERVERNAME.HERE; listen 192.168.4.220:80 default_server; listen [::]:80 default_server; location ^~ /.well-known/acme-challenge { proxy_pass http://127.0.0.1:81; proxy_set_header Host $host; } location / { return 301 https://$host$request_uri; } } server { server_name YOUR.SERVERNAME.HERE; listen 192.168.4.220:443 ssl http2 default_server; listen [::]:443 ssl http2 default_server; root /var/www/nextcloud/; access_log /var/log/nginx/nextcloud.access.log main; error_log /var/log/nginx/nextcloud.error.log warn; location = /robots.txt { allow all; log_not_found off; access_log off; } location = /.well-known/carddav { return 301 $scheme://$host/remote.php/dav; } location = /.well-known/caldav { return 301 $scheme://$host/remote.php/dav; } client_max_body_size 2048M; location / { rewrite ^ /index.php$uri; } location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/ { deny all; } location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { deny all; } location ~ \.(?:flv|mp4|mov|m4a)$ { mp4; mp4_buffer_size 100m; mp4_max_buffer_size 1024m; fastcgi_split_path_info ^(.+\.php)(/.*)$; include fastcgi_params; include php_optimization.conf; fastcgi_pass php-handler; fastcgi_param HTTPS on; } location ~ ^/(?:index|remote|public|cron|core/ajax/update|status|ocs/v[12]|updater/.+|ocs-provider/.+)\.php(?:$|/) { fastcgi_split_path_info ^(.+\.php)(/.*)$; include fastcgi_params; include php_optimization.conf; fastcgi_pass php-handler; fastcgi_param HTTPS on; fastcgi_cache_bypass $skip_cache; fastcgi_no_cache $skip_cache; fastcgi_cache NEXTCLOUD; } location ~ ^/(?:updater|ocs-provider)(?:$|/) { try_files $uri/ =404; index index.php; } location ~ \.(?:css|js|woff|svg|gif|png|html|ttf|ico|jpg|jpeg)$ { try_files $uri /index.php$uri$is_args$args; access_log off; expires 30d; } } vi /etc/nginx/conf.d/letsencrypt.conf i server { server_name 127.0.0.1; listen 127.0.0.1:81 default_server; charset utf-8; access_log /var/log/nginx/le.access.log main; error_log /var/log/nginx/le.error.log warn; location ^~ /.well-known/acme-challenge { default_type text/plain; root /var/www/letsencrypt; } } vi /etc/nginx/ssl.conf #ssl_certificate /etc/letsencrypt/live/[YOUR.DOMAINNAME]/fullchain.pem; #ssl_certificate_key /etc/letsencrypt/live/[YOUR.DOMAINNAME]/privkey.pem; #ssl_trusted_certificate /etc/letsencrypt/live/[YOUR.DOMAINNAME]/fullchain.pem; ssl_dhparam /etc/ssl/certs/dhparam.pem; ssl_session_timeout 1d; ssl_session_cache shared:SSL:50m; ssl_session_tickets off; ssl_protocols TLSv1.2; ssl_ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:!DSS; # ssl_ecdh_curve secp384r1; # Android still cannot cope with above, so: ssl_ecdh_curve prime256v1; ssl_stapling on; ssl_stapling_verify on; vi /etc/nginx/header.conf add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;"; add_header X-Robots-Tag none; add_header X-Download-Options noopen; add_header X-Permitted-Cross-Domain-Policies none; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "same-origin" always; vi /etc/nginx/proxy.conf proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Protocol $scheme; proxy_set_header X-Forwarded-For $remote_addr; proxy_set_header X-Forwarded-Port $server_port; proxy_set_header X-Forwarded-Server $host; proxy_connect_timeout 3600; proxy_send_timeout 3600; proxy_read_timeout 3600; proxy_redirect off; vi /etc/nginx/optimization.conf fastcgi_read_timeout 3600; fastcgi_buffers 64 64K; fastcgi_buffer_size 256k; fastcgi_busy_buffers_size 3840K; fastcgi_cache_key $http_cookie$request_method$host$request_uri; fastcgi_cache_use_stale error timeout invalid_header http_500; fastcgi_ignore_headers Cache-Control Expires Set-Cookie; gzip on; gzip_vary on; gzip_comp_level 4; gzip_min_length 256; gzip_proxied expired no-cache no-store private no_last_modified no_etag auth; gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; gzip_disable "MSIE [1-6]\."; vi /etc/nginx/php_optimization.conf fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param modHeadersAvailable true; fastcgi_param front_controller_active true; fastcgi_intercept_errors on; fastcgi_request_buffering off; fastcgi_cache_valid 404 1m; fastcgi_cache_valid any 1h; fastcgi_cache_methods GET HEAD;
Now you must remove the leading ‘#’ in the /etc/nginx/nginx.conf file in order to allow to load all files just created. And actually load them, of course.
service nginx restart

Install: NextCloud

Finally.... Get the tarball, unzip it, and surf to the location to do the configuration:
cd /usr/local/src wget https://download.nextcloud.com/server/releases/latest.tar.bz2 tar -xjf latest.tar.bz2 -C /var/www rm latest.tar.bz2 chown -R www-data:www-data /var/www/
Fire up the graphical installer at localhost, choose a name and password for the administrator account, fill in the database credentials and file locations, and you're done. There's an example here.

Install: Certificates

Get free certificates from Letsencrypt:
add-apt-repository ppa:certbot/certbot -y apt update && apt install letsencrypt -y service nginx stop letsencrypt certonly --standalone --agree-tos --rsa-key-size 4096 -d [YOUR.DOMAIN] -d www.[YOUR.DOMAIN] -d cloud.[YOUR.DOMAIN]
If you succeed in getting the certificates, uncomment the first three lines of /etc/nginx/ssl.conf - where the location of the certificates is mentioned, now that you have them.

Some changes in permissions (you might want to create a shell script for this...)
find /var/www/ -type f -print0 | xargs -0 chmod 0640 find /var/www/ -type d -print0 | xargs -0 chmod 0750 chown -R www-data:www-data /var/www/ chown -R www-data:www-data /upload_tmp/ chown -R www-data:www-data /var/nc/ chmod 0644 /var/www/nextcloud/.htaccess chmod 0644 /var/www/nextcloud/.user.ini chmod 600 /etc/letsencrypt/live/[YOUR.DOMAIN]/fullchain.pem chmod 600 /etc/letsencrypt/live/[YOUR.DOMAIN]/privkey.pem chmod 600 /etc/letsencrypt/live/[YOUR.DOMAIN]/chain.pem chmod 600 /etc/letsencrypt/live/[YOUR.DOMAIN]/cert.pem chmod 600 /etc/ssl/certs/dhparam.pem shutdown -r now
Now you should be using https (and getting redirected, if you do not type "https://" in the browser).

Install option: Redis cache server

Simple, but effective:
apt update && apt install redis-server php-redis -y cp /etc/redis/redis.conf /etc/redis/redis.conf.bak
Alter/check/add /etc/redis/redis.conf 1) from: port 6379 to: port 0 2) from: #unixsocket /var/run/redis/redis.sock #unixsocketperm 700 to: unixsocket /var/run/redis/redis.sock unixsocketperm 770 3) from: # maxclients 10000 to: maxclients 512 vi /etc/sysctl.conf add at bottom: vm.overcommit_memory = 1 vi /etc/rc.local add: sysctl -w net.core.somaxconn=65535 You can monitor the working of redis by the following command:
redis-cli -s /var/run/redis/redis.sock monitor