Arch Post-Installation Checklist: Difference between revisions

From The Brainwrecked Wiki
Jump to navigation Jump to search
m Rspamd: Debugging table causing missing edit link
m Yay: Fixed some code blocks
 
(102 intermediate revisions by the same user not shown)
Line 5: Line 5:
Make sure you have a primary user set up.
Make sure you have a primary user set up.


# useradd -m -u <id -ge 1000> -g users -G wheel,games,video,audio,optical,storage,scanner,power <user>
{{bc|
# passwd <user>
sudo useradd -m -u <id -ge 1000> -g users -G wheel,games,video,audio,optical,storage,scanner,power <user>
sudo passwd <user>
}}


= /etc/fstab =
= /etc/fstab =
Line 12: Line 14:
= Time Zone =
= Time Zone =


# ln -sf /usr/share/zoneinfo/<region>/<city> /etc/localtime
{{bc|
# hwclock --systohc
sudo ln -sf /usr/share/zoneinfo/<region>/<city> /etc/localtime
sudo hwclock --systohc
}}


= Localization =
= Localization =
Line 19: Line 23:
Un-comment your desired locales in {{ic|/etc/locale.conf}}, then run
Un-comment your desired locales in {{ic|/etc/locale.conf}}, then run


locale-gen
{{bc|sudo locale-gen}}


If you don't have the default {{ic|/etc/locale.gen}} file:
If you don't have the default {{ic|/etc/locale.gen}} file:


# rm /etc/locale.gen
{{bc|
# pacman -Syu glibc
sudo rm /etc/locale.gen
sudo pacman -Syu glibc
}}


Edit {{ic|/etc/locale.conf}} as you see fit
Edit {{ic|/etc/locale.conf}} as you see fit
Line 122: Line 128:
}}
}}


== IPTables Firewall ==
= Packages =


{{hc|/etc/iptables/iptables.rules|2=
== Mirror List ==
# SIMPLE STATEFUL FIREWALL
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [100308:88697975]
:TCP - [0:0]
:UDP - [0:0]
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p icmp -m icmp --icmp-type 8 -m conntrack --ctstate NEW -j ACCEPT
-A INPUT -m conntrack --ctstate INVALID -j DROP
-A INPUT -p udp -m conntrack --ctstate NEW -j UDP
-A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j TCP
-A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
-A INPUT -p tcp -j REJECT --reject-with tcp-reset
-A INPUT -j REJECT --reject-with icmp-proto-unreachable


# OPEN NECESSARY PORTS HERE
Install and use {{ic|reflector}} to automate the use and selection of mirrors.
-A TCP -p tcp -m tcp --dport 22 -j ACCEPT -m comment --comment "ssh"
 
-A TCP -p tcp -m tcp --dport 80 -j ACCEPT -m comment --comment "http"
{{bc|
-A TCP -p tcp -m tcp --dport 443 -j ACCEPT -m comment --comment "https"
sudo pacman -Syu reflector
-A TCP -p tcp -m tcp --dport 465 -j ACCEPT -m comment --comment "smtps"
sudo reflector -c <country> -p https -l 5 --sort rate --save /etc/pacman.d/mirrorlist
-A TCP -p tcp -m tcp --dport 587 -j ACCEPT -m comment --comment "starttls"
-A TCP -p tcp -m tcp --dport 873 -j ACCEPT  -m comment --comment "rsync"
-A TCP -p tcp -m tcp --dport 993 -j ACCEPT -m comment --comment "imaps"
-A TCP -p tcp -m tcp --dport 64738 -j ACCEPT -m comment --comment "mumble tcp"
-A UDP -p udp -m udp --dport 64738 -j ACCEPT -m comment --comment "mumble udp"
COMMIT
}}
}}


= Packages =
== Yay ==
 
Make sure you have the {{ic|base-devel}} group installed.
 
{{bc|sudo pacman -Syu --needed base-devel git}}


== Mirror List ==
Create a directory where AUR build files will go


Install and use {{ic|reflector}} to automate the use and selection of mirrors.
{{bc|1=
sudo mkdir /var/lib/pacman/aur
sudo chmod 1777 /var/lib/pacman/aur
mkdir /var/lib/pacman/aur/$(whoami)
}}


# pacman -Syu reflector
Build {{ic|yay}}.
# reflector -c <country> -p https -l 5 --sort rate --save /etc/pacman.d/mirrorlist


== Yay ==
{{bc|1=<nowiki>
cd /var/lib/pacman/aur/$(whoami)
git clone https://aur.archlinux.org/yay.git
cd yay
makepkg -Ccisr
</nowiki>}}


# pacman -Syu --needed base-devel
The create the configuration file.
# mkdir /var/lib/pacman/aur
# chmod 1777 /var/lib/pacman/aur
$ mkdir /var/lib/pacman/aur/$(whoami)
$ cd /var/lib/pacman/aur/$(whoami)
$ git clone https://aur.archlinux.org/yay.git
$ cd yay
$ makepkg -Ccisr


{{hc|~/.config/yay/config.json|2=
{{hc|~/.config/yay/config.json|2=
Line 220: Line 209:
== General Utilities ==
== General Utilities ==


# yay -Syu --needed apache-tools dnsutils fail2ban git rsync openssh pkgfile polkit vim
These packages supplement the base system.
 
== E-mail ==
 
# yay -Syu --needed dovecot postfix redis rspamd swaks
 
== Nginx Web Server and Reverse Proxy ==
 
# yay -Syu --needed ca-certificates certbot-nginx mariadb nginx php-fpm
 
== Roundcube Web Mail ==
 
# yay -Syu --needed pigeonhole roundcube
 
= Fail2Ban =
 
{{hc|/etc/fail2ban/jail.local|2=
[DEFAULT]
bantime        = 1d
destemail      = root@bwt.com.de
sender          = fail2ban@bwt.com.de
action          = %(action_mwl)s
}}
 
{{hc|/etc/fail2ban/jail.d/sshd.local|2=
[sshd]
enabled        = true
filter          = sshd
banaction      = iptables
backend        = systemd
maxretry        = 5
findtime        = 1d
bantime        = 2w
ignoreip        = 127.0.0.1/8
}}
 
{{hc|/etc/fail2ban/jail.d/postfix-ispmail.local|2=
[postfix-ispmail]
enabled        = true
filter          = postfix
port            = smtp,submission
banaction      = iptables
backend        = systemd
maxretry        = 5
findtime        = 1d
bantime        = 2w
ignoreip        = 192.99.246.231/32
}}
 
{{hc|/etc/fail2ban/filter.d/postfix-ispmail.conf|2=
<nowiki>[INCLUDES]
before = common.conf
 
[Definition]
_daemon = postfix(-\w+)?/(?:submission/|smtps/)?smtp[ds]
failregex = ^%(__prefix_line)slost connection after AUTH from \S+\[<HOST>\]$
ignoreregex =
 
[Init]
journalmatch = _SYSTEMD_UNIT=postfix.service</nowiki>
}}
 
= SSH Setup =
 
Look for and edit the following lines in {{ic|/etc/ssh/sshd_config}}:


{{hc|/etc/ssh/ssd_config|2=
{{bc|yay -Syu --needed adduser archey3 arj bind-tools bzip2 dmidecode dnsutils fail2ban git haveged htop hwdetect inxi iotop lshw lzop nano ncdu openssh p7zip pacman-contrib perl-rename pkgfile polkit ranger rsync strace sudo tmux unrar unzip vim whois zip}}
<nowiki>Port <port>
AddressFamily <any|inet|inet6>
ListenAddress <ip4-address>
ListenAddress <ip6-address>


LogLevel VERBOSE
If you are not running inside a VM, you may also wish to add these packages:


PermitRootLogin prohibit-password
{{bc|yay -Syu hddtemp lm_sensors memtest86+ smartmontools}}


PubkeyAuthentication yes
The {{ic|havaged}} service can be enabled and started right away as no configuration is needed.


PasswordAuthentication no
{{bc|sudo systemctl enable --now haveged}}
PermitEmptyPasswords no


ChallengeResponseAuthentication no
Your sensors need to be configured before starting the {{ic|lm_sensors}} service.


UsePAM yes
{{note|This package is useless inside a VM.}}
AllowUsers <space-separated-list-of-users>


AllowAgentForwarding no
{{bc|
AllowTcpForwarding no</nowiki>
sudo sensors-detect
sudo systemctl enable --now lm_sensors
}}
}}


Then make sure the {{ic|sshd}} service is enabled and running:
Don't start {{ic|fail2ban}} or {{ic|sshd}} quite yet as they have configuration that needs to be done.
 
# systemctl enable --now sshd
 
= MariaDB =
 
== Data Directory ==
 
If you want to put your databases somewhere other than the default {{ic|/var/lib/mysql}}:
 
# mkdir <dir>
# chown -R mysql:mysql <dir>
 
== tmpfs for tmpdir ==
 
The directory used by MariaDB for storing temporary files is named tmpdir. For example, it is used to perform disk based large sorts, as well as for internal and explicit temporary tables.
 
Create the directory with appropriate permissions:
 
# mkdir -pv <dir>
# chown mysql:mysql <dir>
 
Find the id and gid of the mysql user and group:
 
{{hc|$ id mysql|2=
uid=27(mysql) gid=27(mysql) groups=27(mysql)}}
 
Add to your /etc/fstab file.
 
{{hc|/etc/fstab|2=
# Static information about the filesystems.
# See fstab(5) for details.
 
# <file system> <dir>          <type>  <options>                                      <dump> <pass>
...
tmpfs          /srv/sqltmp    tmpfs  rw,noatime,gid=933,uid=993,size=100M,mode=0750  0 0
...
}}
 
== Configuration File ==
 
{{hc|/etc/my.cnf.d/server.cnf|2=
...
[mysqld]
...
datadir=<dir>
skip-networking
tmpdir=<dir>
...
}}
 
== Initial Setup ==
 
# mariadb-install-db --user=mysql --basedir=/usr --datadir=<dir></nowiki>
 
== Start the Server ==
 
# systemctl enable --now mariadb
 
== Security Settings ==
 
# mysql_secure_installation
 
Create a user:
 
{{hc|$ mysql -u root -p|2=
MariaDB> CREATE USER '<user>'@'localhost' IDENTIFIED BY '<password>';
MariaDB> GRANT ALL PRIVILEGES ON mydb.* TO '<password>'@'localhost';
MariaDB> FLUSH PRIVILEGES;
MariaDB> quit
}}
 
= Redis =
 
Redis is used by several programs, including rspamd (e-mail), NextCloud, OnlyOffice, and Wordpress (optional).
 
By default, {{ic|redis}} is already configured to bind to 127.0.0.1:6379, but the Unix socket is not enabled.
 
{{hc|/etc/redis.conf|2=
...
# Unix socket.
#
# Specify the path for the Unix socket that will be used to listen for
# incoming connections. There is no default, so Redis will not listen
# on a unix socket when not specified.
#
unixsocket /run/redis/redis.sock
unixsocketperm 700
...
}}
 
You will also need to add users to the {{ic|redis}} group so processes can access {{ic|redis}}
 
# for username in http rspamd; do usermod -G redis ${username}; done
 
= E-mail =
 
== Postfix ==
 
=== Create MariaDB Databases for Postfix ===
 
MariaDB [(none)]> CREATE DATABASE MAIL;
MariaDB [(none)]> CREATE USER 'mailuser'@'127.0.0.1' IDENTIFIED BY '<password>';
MariaDB [(none)]> CREATE USER 'mailadmin'@'localhost' IDENTIFIED BY '<password>';
MariaDB [(none)]> GRANT ALL ON MAIL.* TO 'mailadmin'@'localhost';
MariaDB [(none)]> GRANT SELECT ON MAIL.* TO 'mailuser'@'127.0.0.1';
 
MariaDB [(none)]> USE MAIL;
MariaDB [(MAIL)]> CREATE TABLE IF NOT EXISTS `virtual_domains` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
MariaDB [(none)]> USE MAIL;
MariaDB [(MAIL)]> CREATE TABLE IF NOT EXISTS `virtual_users` (
`id` int(11) NOT NULL auto_increment,
`domain_id` int(11) NOT NULL,
`email` varchar(100) NOT NULL,
`password` varchar(150) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`),
FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
USE MAIL;
MariaDB [(MAIL)]> CREATE TABLE IF NOT EXISTS `virtual_aliases` (
`id` int(11) NOT NULL auto_increment,
`domain_id` int(11) NOT NULL,
`source` varchar(100) NOT NULL,
`destination` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
=== Configure Postfix to Use MariaDB ===
 
{{hc|/etc/postfix/mysql-virtual-mailbox-domains.cf|2=
user = mailuser
password = <password>
hosts = localhost
dbname = MAIL
query = SELECT 1 FROM virtual_domains WHERE name='%s'
}}
 
{{hc|/etc/postfix/mysql-virtual-mailbox-maps.cf|2=
user = mailuser
password = <password>
hosts = 127.0.0.1
dbname = MAIL
query = SELECT 1 FROM virtual_users WHERE email='%s'
}}
 
{{hc|/etc/postfix/mysql-virtual-alias-maps.cf|2=
user = mailuser
password = <password>
hosts = 127.0.0.1
dbname = MAIL
query = SELECT destination FROM virtual_aliases WHERE source='%s'
}}
 
{{hc|/etc/postfix/mysql-email2email.cf|2=
user = mailuser
password = <password>
hosts = 127.0.0.1
dbname = MAIL
query = SELECT email FROM virtual_users WHERE email='%s'
}}
 
# postconf virtual_mailbox_domains=mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
# postconf virtual_mailbox_maps=mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
# postconf virtual_alias_maps=mysql:/etc/postfix/mysql-virtual-alias-maps.cf,mysql:/etc/postfix/mysql-email2email.cf
# chgrp postfix /etc/postfix/mysql-*.cf
# chmod 640 /etc/postfix/mysql-*.cf
 
== Dovecot ==
 
# groupadd -g 5000 vmail
# useradd -g vmail -u 5000 vmail -d /var/vmail -m
# chown -R vmail:vmail /srv/mail
 
# cp /usr/share/doc/dovecot/example-config/dovecot.conf /etc/dovecot
# cp -r /usr/share/doc/dovecot/example-config/conf.d /etc/dovecot
 
{{hc|/etc/dovecot/conf.d/10-auth.conf|2=
...
#!include auth-system.conf.ext
!include auth-sql.conf.ext
...
}}
 
{{hc|/etc/dovecot/conf.d/auth-sql.conf.ext|2=
...
#userdb {
#  driver = sql
#  args = /etc/dovecot/dovecot-sql.conf.ext
#}
...
userdb {
  driver = static
  args = uid=vmail gid=vmail home=/srv/mail/%d/%n
}
}}
 
{{hc|/etc/dovecot/conf.d/10-mail.conf|2=
...
mail_location = maildir:/srv/mail/%d/%n/Maildir
...
namespace inbox {
...
  separator = .
...
}
...
}}
 
{{hc|/etc/dovecot/conf.d/10-master.conf|2=
...
service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    group = postfix
    mode = 0600
    user = postfix
  }
...
}
...
service auth {
...
  # Postfix smtp-auth
  unix_listener /var/spool/postfix/private/auth {
    mode = 0660
    user = postfix
    group = postfix
  }
...
}
...
}}
 
{{hc|/etc/dovecot/conf.d/10-ssl.conf|2=
...
ssl = yes
...
ssl_cert = </etc/letsencrypt/live/<domain>/fullchain.pem
ssl_key = </etc/letsencrypt/live/<domain>/privkey.pem
...
ssl_ca = </etc/letsencrypt/live/<domain>/chain.pem
...
}}
 
{{hc|/etc/dovecot/conf.d/15-mailboxes.conf|2=
...
mailbox INBOX.Drafts {
  special_use = \Drafts
  auto = subscribe
}
mailbox INBOX.Junk {
  special_use = \Junk
  auto = subscribe
  autoexpunge = 30d
}
mailbox INBOX.Trash {
  special_use = \Trash
  auto = subscribe
  autoexpunge = 30d
}
mailbox INBOX.Sent {
  special_use = \Sent
  auto = subscribe
}
...
}}
 
{{hc|/etc/dovecot/conf.d/20-imap.conf|2=
...
protocol imap {
  # Space separated list of plugins to load (default is global mail_plugins).
  mail_plugins = $mail_plugins imap_sieve
...
}
}}
 
{{hc|/etc/dovecot/conf.d/20-lmtp.conf|2=
...
protocol lmtp {
  # Space separated list of plugins to load (default is global mail_plugins).
  mail_plugins = $mail_plugins sieve
...
}
}}
 
{{hc|/etc/dovecot/conf.d/90-sieve.conf|2=
...
sieve_after = /etc/dovecot/sieve-after
...
sieve_plugins = sieve_imapsieve sieve_extprograms
...
# From elsewhere to Junk folder
imapsieve_mailbox1_name = Junk
imapsieve_mailbox1_causes = COPY
imapsieve_mailbox1_before = file:/etc/dovecot/sieve/learn-spam.sieve
 
# From Junk folder to elsewhere
imapsieve_mailbox2_name = *
imapsieve_mailbox2_from = Junk
imapsieve_mailbox2_causes = COPY
imapsieve_mailbox2_before = file:/etc/dovecot/sieve/learn-ham.sieve
 
sieve_pipe_bin_dir = /etc/dovecot/sieve
sieve_global_extensions = +vnd.dovecot.pipe
}
}}
 
# mkdir /etc/dovecot/sieve{-after,}
 
{{hc|/etc/dovecot/sieve-after/spam-to-folder.sieve|2=
require ["fileinto","mailbox"];
 
if header :contains "X-Spam" "Yes" {
fileinto :create "INBOX.Junk";
stop;
}
}}
 
{{hc|/etc/dovecot/sieve/learn-spam.sieve|2=
require ["vnd.dovecot.pipe", "copy", "imapsieve"];
pipe :copy "rspamd-learn-spam.sh";
}}
 
{{hc|/etc/dovecot/sieve/learn-ham.sieve|2=
require ["vnd.dovecot.pipe", "copy", "imapsieve"];
pipe :copy "rspamd-learn-ham.sh";
}}
 
{{hc|/etc/dovecot/dovecot-sql.conf.ext|2=
driver = mysql
connect = host=127.0.0.1 dbname=MAIL user=mailuser password=<password>
default_pass_scheme = SHA256-CRYPT
password_query = SELECT email as user, password FROM virtual_users WHERE email='%u';
}}
 
{{hc|/etc/dovecot/sieve/rspamd-learn-spam.sh|2=
#!/usr/bin/env sh
exec /usr/bin/rspamc learn_spam
}}
 
{{hc|/etc/dovecot/sieve/rspamd-learn-ham.sh|2=
#!/usr/bin/env sh
exec /usr/bin/rspamc learn_ham
}}
 
# sievec /etc/dovecot/sieve-after/spam-to-folder.sieve
# sievec /etc/dovecot/sieve/learn-spam.sieve
# sievec /etc/dovecot/sieve/learn-ham.sieve
# chown root:root /etc/dovecot/dovecot-sql.conf.ext
# chmod 600 /etc/dovecot/dovecot-sql.conf.ext
# chmod 600 /etc/dovecot/sieve/learn-{spam,ham}.sieve
# chmod 700 /etc/dovecot/sieve/rspamd-learn-{spam,ham}.sh
# chown vmail:vmail /etc/dovecot/sieve/learn-{spam,ham}.sieve
# chown vmail:vmail /etc/dovecot/sieve/rspamd-learn-{spam,ham}.sh
 
== Final Postfix Configuration ==
 
# postconf virtual_transport=lmtp:unix:private/dovecot-lmtp
# postconf smtpd_sasl_type=dovecot
# postconf smtpd_sasl_path=private/auth
# postconf smtpd_sasl_auth_enable=yes
# postconf smtpd_tls_security_level=may
# postconf smtpd_tls_auth_only=yes
# postconf smtpd_tls_cert_file=/etc/letsencrypt/live/<domain>/fullchain.pem
# postconf smtpd_tls_key_file=/etc/letsencrypt/live/<domain>/privkey.pem
# postconf smtp_tls_security_level=may
# postconf smtpd_milters=inet:127.0.0.1:11332
# postconf non_smtpd_milters=inet:127.0.0.1:11332
# postconf milter_protocol=6
# postconf milter_mail_macros="i {mail_addr} {client_addr} {client_name} {auth_authen}"
 
{{hc|/etc/postfix/master.cf|2=
...
submission inet n - - - - 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_sender_login_maps=mysql:/etc/postfix/mysql-email2email.cf
-o smtpd_sender_restrictions=reject_sender_login_mismatch
-o smtpd_sasl_local_domain=$myhostname
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_recipient_restrictions=reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject
...
}}
 
== Rspamd ==
 
# mkdir /etc/rspamd/conf.d
 
{{hc|/etc/rspamd/override.d/milter_headers.conf|2=
extended_spam_headers = true;
}}
 
{{hc|/etc/rspamd/override.d/classifier-bayes.conf|2=
autolearn = true;
}}
 
# mkdir /var/lib/rspamd/dkim
# chown _rspamd:_rspamd /var/lib/rspamd/dkim
# rspamadm dkim_keygen -d <domain> -s $(date +%Y%m%d)
 
{{hc|/var/lib/rspamd/dkim/<domain>.YYYYMMDD.key|2=
<nowiki>-----BEGIN PRIVATE KEY-----
[base64 key]
-----END PRIVATE KEY-----</nowiki>
}}
 
The second part of the output of {{ic|rspamadm dkim_keygen}} can be directly pasted into a DNS zone if you are running your own server.  If you only have control of your domain name through a 3rd party:
 
{| class="wikitable"
! Type
! Host
! Value
|-
| TXT || @ || <nowiki>v=spf1 a mx ip4:<ip-address> -all</nowiki>
|-
| TXT || _dmarc || <nowiki>v=DMARC1; p=none; sp=none; rf=afrf; pct=100; ri=86400</nowiki>
|-
| TXT || YYYYMMDD._domainkey || <nowiki>v=DKIM1; k=rsa; p=AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789...</nowiki>
|}
 
{{hc|/etc/rspamd/local.d/dkim_signing.conf|2=
path = "/var/lib/rspamd/dkim/$domain.$selector.key";
selector_map = "/etc/rspamd/dkim_selectors.map";
}}
 
{{hc|/etc/rspamd/dkim_selectors.map|2=
<domain> YYYYMMDD
}}
 
= PHP-FPM =
 
Look for and edit the following lines in {{ic|/etc/php/php.ini}}:
 
{{hc|/etc/php/php.ini|2=
[PHP]
open_basedir = /srv/http/:/home/:/tmp/:/usr/share/pear/:/usr/share/webapps/:/dev/urandom:/proc/meminfo
include_path = ".:/php/includes:/usr/share/pear"
upload_max_filesize = 8M
 
extension=apcu
extension=dba
extension=exif
extension=gd
extension=gettext
extension=iconv
extension=intl
extension=sodium
extension=ldap
extension=mysqli
zend_extension=opcache
extension=pdo_mysql
extension=soap
 
[Pdo_mysql]
pdo_mysql.cache_size = 2000
 
[opcache]
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.revalidate_freq=1
opcache.save_comments=1
}}
 
== tmpfs for PHP Sessions ==
 
{{hc|/etc/php/php.ini|2=
[Session]
session.save_path = "/srv/phptmp"
}}
 
Find the id and gid of the mysql user and group:
 
{{hc|$ id mysql|2=
uid=27(mysql) gid=27(mysql) groups=27(mysql)
}}
 
{{hc|/etc/fstab|2=
# Static information about the filesystems.
# See fstab(5) for details.
 
# <file system> <dir>          <type>  <options>                                      <dump> <pass>
...
tmpfs /srv/phptmp tmpfs rw,noatime,gid=27,uid=27,size=100M,mode=0750 0 0
...
}}
 
Then make sure the {{ic|php-fpm}} service is enabled and running:
 
# systemctl enable --now php-fpm
 
= Nginx =
 
== Main Configuration ==
 
First, edit {{ic|/etc/nginx/nginx.conf}}:
 
{{hc|/etc/nginx/nginx.conf|2=
<nowiki>user http;
worker_processes 1; # one(1) worker or equal the number of _real_ cpu cores. 4=4 core cpu
worker_priority 15; # renice workers to reduce priority compared to system processes for machine health. Worst case nginx will get ~25% system resources at nice=15
worker_rlimit_nofile 1024; # maximum number of open files
worker_cpu_affinity auto;
 
events {
multi_accept on;
accept_mutex on; # serially accept() connections and pass to workers, efficient if workers gt 1
accept_mutex_delay 500ms; # worker process will accept mutex after this delay if not assigned. (default 500ms)
worker_connections 1024; # number of parallel or concurrent connections per worker_processes
}
 
http {
charset utf-8;
aio on; # asynchronous file I/O, fast with ZFS, make sure sendfile=off
sendfile off; # on for decent direct disk IO, off for VMs
tcp_nopush off; # turning on requires sendfile=on
tcp_nodelay on; # Nagle buffering algorithm, used for keepalive only
server_tokens off; # version number in error pages
log_not_found off;
types_hash_max_size 4096;
 
# MIME
include mime.types;
default_type application/octet-stream;
 
# logging
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log warn;
 
# SSL
ssl_session_timeout            1440m;
ssl_session_cache shared:le_nginx_SSL:10m;
ssl_session_tickets off;
ssl_prefer_server_ciphers off;
 
# Diffie-Hellman parameter for DHE ciphersuites
ssl_dhparam                    /etc/letsencrypt/ssl-dhparams.pem;
 
# Mozilla Intermediate configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
 
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 valid=60s;
resolver_timeout 2s;
 
# Size Limits
#client_body_buffer_size 8k;
#client_header_buffer_size 1k;
client_max_body_size 16M;
#large_client_header_buffers 4 4k/8k;
 
## From StackOverflow: for "upstream sent too big header while reading response header from upstream"
fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
 
# Timeouts, do not keep connections open longer then necessary to reduce
# resource usage and deny Slowloris type attacks.
client_body_timeout 5s; # maximum time between packets the client can pause when sending nginx any data
client_header_timeout 5s; # maximum time the client has to send the entire header to nginx
keepalive_timeout 75s; # timeout which a single keep-alive client connection will stay open
send_timeout 15s; # maximum time between packets nginx is allowed to pause when sending the client data
 
## General Options
 
gzip off; # disable on the fly gzip compression due to higher latency, only use gzip_static
#gzip_http_version 1.0; # serve gzipped content to all clients including HTTP/1.0
gzip_static on; # precompress content (gzip -9) with an external script
#gzip_vary on; # send response header "Vary: Accept-Encoding"
gzip_proxied any; # allows compressed responses for any request even from proxies
ignore_invalid_headers on;
keepalive_requests 50; # number of requests per connection, does not affect SPDY
keepalive_disable none; # allow all browsers to use keepalive connections
max_ranges 1; # allow a single range header for resumed downloads and to stop large range header DoS attacks
msie_padding off;
open_file_cache max=1000 inactive=2h;
open_file_cache_errors on;
open_file_cache_min_uses 1;
open_file_cache_valid 1h;
output_buffers 1 512;
postpone_output 1440; # postpone sends to match our machine's MSS
read_ahead 512K; # kernel read head set to the output_buffers
recursive_error_pages on;
reset_timedout_connection on; # reset timed out connections freeing ram
server_name_in_redirect off; # if off, nginx will use the requested Host header
source_charset utf-8; # same value as "charset"
 
## Request limits
limit_req_zone $binary_remote_addr zone=gulag:1m rate=60r/m;
 
## Log Format
log_format main '$remote_addr $host $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" $ssl_cipher $request_time';
 
# load configs
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*.conf;
 
}</nowiki>
}}
 
Make the {{ic|sites-available}} and {{ic|sites-enabled}} directories.
 
# mkdir /etc/nginx/sites-<nowiki>{available,enabled}</nowiki>
 
== Non-Proxied Web Site ==
 
Make a test site available by creating {{ic|/etc/nginx/sites-available/<domain>.conf}}:<br/>
(Remove the {{ic|#}} after setting up Let's Encrypt.)
 
{{hc|/etc/nginx/sites-available/<domain>.conf|2=
<nowiki>server {
listen 80;
listen [::]:80;
server_name <domain>;
root /srv/http/$host;
index index.html index.php;
 
# if ($host = <domain>) {
# return 301 https://$host$request_uri;
# }
#
# return 404;
#
#}
#
#server {
#
# listen 443 ssl http2;
# listen [::]:443 ssl http2;
# server_name <domain>;
# root /srv/http/$host;
# index index.html index.php;
#
# ssl_certificate /etc/letsencrypt/live/<domain>/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/<domain>/privkey.pem;
# ssl_trusted_certificate /etc/letsencrypt/live/<domain>/chain.pem;
#
# fastcgi_param HTTPS on;
#
# add_header Strict-Transport-Security max-age=15768000;
add_header Cache-Control "public";
add_header X-Frame-Options "DENY";
 
access_log /var/log/nginx/access.log main buffer=32k;
error_log /var/log/nginx/error.log error;
limit_req zone=gulag burst=200 nodelay;
 
 
# ACME challenge
location ^~ /.well-known {
allow all;
alias /var/lib/letsencrypt/$host/.well-known;
default_type "text/plain";
try_files $uri =404;
}
 
location ~ /(data|conf|bin|inc)/ {
deny all;
}
 
# static file 404's are strict access by hostname aren't logged and expires header is set to maximum age
location ~* \.(jpg|jpeg|gif|css|png|js|ico|html)$ {
access_log off;
expires max;
}
 
location ~ \.php$ {
try_files $uri = 404;
fastcgi_pass unix:/run/php-fpm/php-fpm.sock;
fastcgi_index index.php;
include fastcgi.conf;
}
 
location ~ \.php5$ {
try_files $uri = 404;
fastcgi_pass unix:/run/php-fpm/php-fpm.sock;
fastcgi_index index.php5;
include fastcgi.conf;
}
 
location ~ /\.ht {
deny all;
}
 
}</nowiki>
}}
 
Enable the site
 
# ln -s ../sites-available/<domain>.conf /etc/nginx/sites-enabled/<domain>.conf
 
Then create {{ic|index.php}} for your new site.
 
{{hc|/srv/http/<domain>/index.php|2=
<nowiki><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>Server Configuration Confirmation</title>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
 
<body style="font-family:sans-serif;">
 
<h1>Web server is properly configured!</h1>
 
<?php echo "<h2>PHP is properly configured as well!</h2>" ?>
 
<p>Time to add some content!</p>
 
</body>
</html></nowiki>
}}
 
Finally, make sure the {{ic|nginx}} service is enabled and running:
 
# systemctl enable --now nginx
 
= Let's Encrypt =
 
# certbot --nginx
 
The {{ic|nginx}} plugin will guide you and help automate the process.
 
Take note that the {{ic|nginx}} configuration in this guide takes into account the settings suggested by {{ic|certbot}}.
 
== Automatic Renewal ==
 
These will automatically renew your certificates every 2 months on the first Sunday of the month at 4am.
 
=== Systemd ===
 
{{hc|/etc/systemd/system/certbot.service|2=
[Unit]
Description=Let's Encrypt renewal
 
[Service]
Type=oneshot
ExecStart=/usr/bin/certbot renew --agree-tos --no-redirect
}}
 
{{hc|/etc/systemd/system/certbot.timer|2=
[Unit]
Description=Bi-monthly renewal of Let's Encrypt's certificates
 
[Timer]
OnCalendar=Sun *-1,3,5,7,9,11-1..7 04:00:00
RandomizedDelaySec=1h
Persistent=true
 
[Install]
WantedBy=timers.target
}}
 
=== Crontab ===
 
0 4 1-7 2-12/2 0 /usr/bin/certbot renew --agree-tos --no-redirect
 
= ISPmail Admin =
 
Create a config like [[Arch_Post-Installation_Checklist#Non-Proxied_Web_Site]]
 
Then, in the site's root:
 
# wget 'https://www.ima.jungclaussen.com/dwn/dwn.php?v=0.9.6&f=.tar.gz'
# tar -xvf 'dwn.php?v=0.9.6&f=.tar.gz'
# mv ispmail*/htdocs/* ./
# rm -r 'dwn.php?v=0.9.6&f=.tar.gz' ispmail*
 
Then edit the configuration file:
 
{{hc|<ispmail_root>/cfg/config.inc.php|2=
<?php
 
// @package    ISPmail_Admin
// @author      Ole Jungclaussen
// @version    0.9.0
 
// SHOW PHP ERRORS (DEBUGGING)
 
// ini_set('display_startup_errors', 1);
// ini_set('display_errors', 1);
// error_reporting(-1);
 
// DATABASE ACCESS
 
define('IMA_CFG_DB_HOST',      '127.0.0.1');
define('IMA_CFG_DB_PORT',      '3306');
define('IMA_CFG_DB_USER',      'mailadmin');
define('IMA_CFG_DB_PASSWORD',  '<password>');
define('IMA_CFG_DB_DATABASE',  'MAIL');
 
// PASSWORD HASHES (enable only *one*)
 
define('IMA_CFG_USE_SHA256_HASHES', true);
// define('IMA_CFG_USE_MD5_HASHES', true);
 
// ACCESS CONTROL: uncomment the type you want to use.
 
define('IMA_CFG_LOGIN', IMA_LOGINTYPE_ACCOUNT);
// define('IMA_CFG_LOGIN', IMA_LOGINTYPE_ADM);
// define('IMA_CFG_LOGIN', IMA_LOGINTYPE_ADMAUTO);
 
// ADMINISTRATOR'S NAME AND PASSWORD
 
define('IMA_CFG_ADM_USER',  'admin');      // admin username
define('IMA_CFG_ADM_PASS',  '<password>'); // admin password
 
// LISTS
// Spread long lists on multiple pages.  Set number of maximum entries
// per page.  Changes take effect after login/logout.  If not defined,
// defaults to 65535.
 
// define('IMA_LIST_MAX_ENTRIES', 200);
?>
}}
 
You should now be able to log in and set up your virtual mailboxes.
 
= Roundcube =

Latest revision as of 03:39, 28 December 2019

Passwords & User Creation

Make sure root has a password.

Make sure you have a primary user set up.

sudo useradd -m -u <id -ge 1000> -g users -G wheel,games,video,audio,optical,storage,scanner,power <user>
sudo passwd <user>

/etc/fstab

Time Zone

sudo ln -sf /usr/share/zoneinfo/<region>/<city> /etc/localtime
sudo hwclock --systohc

Localization

Un-comment your desired locales in /etc/locale.conf, then run

sudo locale-gen

If you don't have the default /etc/locale.gen file:

sudo rm /etc/locale.gen
sudo pacman -Syu glibc

Edit /etc/locale.conf as you see fit

/etc/locale.conf
LANG=en_US.UTF-8
LANGUAGE=en_US
LC_COLLATE=POSIX
LC_MESSAGES=C
LC_CTYPE=en_US.UTF-8
LC_NUMERIC=en_US.UTF-8
LC_TIME=en_US.UTF-8
LC_MONETARY=en_US.UTF-8
LC_PAPER=en_US.UTF-8
LC_NAME=en_US.UTF-8
LC_ADDRESS=en_US.UTF-8
LC_TELEPHONE=en_US.UTF-8
LC_MEASUREMENT=en_US.UTF-8
LC_IDENTIFICATION=en_US.UTF-8

Set up /etc/vconsole.conf with a keymap and (optionally) a font:

/etc/vconsole.conf
KEYMAP=us
FONT=Lat2-Terminus16

Initramfs

Look for and edit the following lines:

/etc/mkinitcpio.conf
MODULES=([amdgpu|bochs_drm|cirrus|i915|nouveau|(nvidia nvidia_modeset nvidia_uvm nvidia_drm)] [ehci_pci usb_storage]>)
HOOKS=(base udev autodetect modconf block [zfs] filesystems keyboard fsck [encrypt] keymap consolefont)
COMPRESSION=lz4

Networking

Host Name

Make sure a host name is set in /etc/hostname

Then edit /etc/hosts

/etc/hosts
127.0.0.1	localhost.localdomain	locahost
::1		localhost.localdomain	localhost
127.0.1.1	<hostname>.localdomain	<hostname>

Static Addressing

Use systemd-networkd when a machine will use a static address without consulting a DHCP server.

IPv4 Only

/etc/systemd/network/network.network
[Match]
MACAddress=<mac-address>
[Network]
Address=<ipv4-address>/<mask>
DNS=<ipv4-address>
Gateway=<ipv4-address>
LinkLocalAddressing=no
IPv6AcceptRA=no

IPv4 & IPv6

/etc/systemd/network/network.network
[Match]
MACAddress=<mac-address>
[Network]
Address=<ipv6-address>/<mask>
DNS=<ipv6-address>
Gateway=<ipv6-address>
Address=<ipv4-address>/<mask>
DNS=<ipv4-address>
Gateway=<ipv4-address>

Dynamic Addressing

It's preferable to use connman or Network Manager for dynamic addresses as systemd-networkd doesn't play well with interfaces coming and going.

If you'd rather use systemd-networkd for DHCP:

/etc/systemd/network/network.network
[Match]
MACAddress=<mac-address>
[Network]
DHCP=yes
[DHCP]
UseMTU=true

Packages

Mirror List

Install and use reflector to automate the use and selection of mirrors.

sudo pacman -Syu reflector
sudo reflector -c <country> -p https -l 5 --sort rate --save /etc/pacman.d/mirrorlist

Yay

Make sure you have the base-devel group installed.

sudo pacman -Syu --needed base-devel git

Create a directory where AUR build files will go

sudo mkdir /var/lib/pacman/aur
sudo chmod 1777 /var/lib/pacman/aur
mkdir /var/lib/pacman/aur/$(whoami)

Build yay.

cd /var/lib/pacman/aur/$(whoami)
git clone https://aur.archlinux.org/yay.git
cd yay
makepkg -Ccisr

The create the configuration file.

~/.config/yay/config.json
{
	"aururl": "https://aur.archlinux.org",
	"buildDir": "/var/lib/pacman/aur/$USER",
	"editor": "nano",
	"editorflags": "",
	"makepkgbin": "makepkg",
	"makepkgconf": "",
	"pacmanbin": "pacman",
	"pacmanconf": "/etc/pacman.conf",
	"tarbin": "bsdtar",
	"redownload": "no",
	"rebuild": "no",
	"answerclean": "none",
	"answerdiff": "all",
	"answeredit": "",
	"answerupgrade": "0",
	"gitbin": "git",
	"gpgbin": "gpg",
	"gpgflags": "",
	"mflags": "",
	"sortby": "votes",
	"gitflags": "",
	"removemake": "yes",
	"requestsplitn": 150,
	"sortmode": 0,
	"completionrefreshtime": 7,
	"sudoloop": false,
	"timeupdate": false,
	"devel": true,
	"cleanAfter": false,
	"gitclone": true,
	"provides": true,
	"pgpfetch": true,
	"upgrademenu": true,
	"cleanmenu": true,
	"diffmenu": true,
	"editmenu": true,
	"combinedupgrade": false,
	"useask": false
}

General Utilities

These packages supplement the base system.

yay -Syu --needed adduser archey3 arj bind-tools bzip2 dmidecode dnsutils fail2ban git haveged htop hwdetect inxi iotop lshw lzop nano ncdu openssh p7zip pacman-contrib perl-rename pkgfile polkit ranger rsync strace sudo tmux unrar unzip vim whois zip

If you are not running inside a VM, you may also wish to add these packages:

yay -Syu hddtemp lm_sensors memtest86+ smartmontools

The havaged service can be enabled and started right away as no configuration is needed.

sudo systemctl enable --now haveged

Your sensors need to be configured before starting the lm_sensors service.

Note: This package is useless inside a VM.
sudo sensors-detect
sudo systemctl enable --now lm_sensors

Don't start fail2ban or sshd quite yet as they have configuration that needs to be done.