title: Linux Server Applications breadcrumbs:
Note: If not stated then it's for Debian. Some may be for CentOS (5?) and extremely outdated.
Outdated
apt install apache2
Update security.conf
:
ServerTokens Prod
ServerSignature Off
a2<en|dis><conf|mod|site> <...>
apache2ctl
Sends an emails when APT updates are available.
apt install apticron
/etc/apticron/apticron.conf
cp /usr/lib/apticron/apticron.conf /etc/apticron/apticron.conf
/etc/cron.d/apticron
(e.g. 30 23 * * *
).IPADDRESSNUM
and always print all IP adresses:
/usr/sbin/apticron
.IPADDRESSES=`(echo $( /bin/hostname --all-ip-addresses ) ;
IPADDRESSES=`(
apticron
TODO
apt install avahi-daemon
Possibly outdated
awscli
through pip3chmod +x /usr/local/bin/aws
aws configure [--profile <profile>]
eu-west-2
json
aws s3 cp <local_file> s3://<bucket>/
-g
CLI arg forces all output to stderr, which breaks logging. Use -f
instead.named-checkconf -p
bind9utils
package.named-compilezone -f raw -F text -o zone.tmp <zone> <zone-file>.signed
dnssec-dsfromkey <dnskey-file>
(use the one with digest type 2 (SHA-256))named-checkconf
dig cloudflare.com @<server>
should give status NOERROR
and contain the ad
flag (for "authentic data", i.e. it passed DNSSEC validation).dig www.dnssec-failed.org @<server>
should give status SERVFAIL
.dig www.dnssec-failed.org @<server> +cd
(for "checking disabled", useful for DNSSEC debugging) should give status NOERROR
but no ad
flag.dig chaos txt version.bind @<server>
dig chaos txt hostname.bind @<server>
A free community backend for Bitwarden.
TODO
See Storage: Ceph.
apt install certbot
/etc/letsencrypt/cli.ini
, add renew-hook = systemctl reload nginx
or equivalent.certbot -d <domain> --preferred-challenges=http --webroot --webroot-path=<webroot> certonly
certbot -d <domain> --preferred-challenges=dns --manual certonly
certbot renew --dry-run [--staging]
certbot revoke --cert-path <cert>
An NTP client and server. By design more accurate than e.g. ntpd and systemd-timesyncd.
apt install chrony
/etc/chrony/chrony.conf
):
server <address> iburst
pool <address> iburst
allow {all|<network>}
systemctl restart chrony
chronyc tracking
chronyc sources
fail2ban
.fail2ban-client status [sshd]
.Possibly outdated
This setup requires pubkey plus MFA (if configured) plus password.
apt install libpam-google-authenticator
/etc/pam.d/sshd
, add auth required pam_google_authenticator.so nullok
after @include common-auth
.In /etc/ssh/sshd_config
, set:
ChallengeResponseAuthentication yes
UsePAM yes
AuthenticationMethods publickey,keyboard-interactive
Restart sshd
and check that you can login with pubkey and MFA now.
(Optional) Add my google-auth-config-prompter.sh profile script to /etc/profile.d/
to ask user to configure Google Auth on login.
To allow a group to use only pubkey (no password or OTP):
/etc/ssh/sshd_config
, add Match Group no-mfa
containing AuthenticationMethods publickey
(indented) at the bottom.no-mfa
and add special users to it.To manually configure MFA for a user:
google-authenticator -tduW
See Storage: isdct.
Typically used with a data source like Prometheus.
./grafana.ini:/etc/grafana/grafana.ini:ro
./data:/var/lib/grafana/:rw
(requires UID 472)./logs:/var/log/grafana/:rw
(requires UID 472)grafana.ini
.See Home Assistant.
isc-dhcp-server
./etc/dhcp/dhcpd.conf
/etc/dhcp/dhcpd6.conf
authorative
statement in subnet declarations so that the server will reply with DHCPNAK for misconfigured clients.
This may significantly reduce reconfiguration delay when a client moves between subnets.range6
, prefer using CIDR notation.
If using range notation, try to align the start and end on a CIDR block to avoid excessive memory usage./116
gives 8191 addresses.pool[6]
are not applied to e.g. host
s with addresses in the same defined subnet. Don't use pool[6]
s if you don't need to.Get sensor values like temperature, voltage, fan speeds, etc.
apt install lm-sensors
sensors
sensors-detect
. When it asks, add the modules to /etc/modules
.systemctl restart kmod
sensors
journalctl
:
chip "<chip>"\n ignore <sensor>
in /etc/sensors3.conf
. Re-run sensors
. (See Kernel ACPI Error SMBus/IPMI/GenericSerialBus (ServerAdminBlog) for an example on certain HP servers.)sensors
. If it worked, then remove it from /etc/modules
to make it permanent.A MySQL fork that is generally MySQL compatible.
apt install mariadb-server
mysql_secure_installation
mariadb [-u <user> [-p]]
root
.-p
.GRANT ALL ON *.* TO '<user>'@'127.0.0.1' IDENTIFIED BY '<password>' WITH GRANT OPTION;
The instructions below use NFSv4 without Kerberos. This should only be used on trusted networks and requires manual user and group ID management.
apt install nfs-kernel-server
portmap
if you need support for NFSv2 and v3 (not NFSv4).(Recommended) Enable only v4:
In /etc/default/nfs-common
, set:
NEED_STATD="no"
NEED_IDMAPD="yes"
In /etc/default/nfs-kernel-server
, set:
RPCNFSDOPTS="-N 2 -N 3"
RPCMOUNTDOPTS="--manage-gids -N 2 -N 3"
Mask "rpcbind":
systemctl disable --now rpcbind.service
systemctl mask rpcbind.service
systemctl mask rpcbind.socket
Restart it: systemctl restart nfs-server.service
See which versions are running: cat /proc/fs/nfsd/versions
(-
means disabled)
mkdir /export
/zfspool/alpha /export/alpha none bind,defaults,nofail,x-systemd.requires=zfs-mount.service 0 0
/etc/exports
.
exports(5)
.exportfs -ra
systemctl restart nfs-server.service
exportfs -v
Example /etc/exports
:
# "fsid=root" is a special root export in NFSv4 where other exports are accessible relative to it.
# "sync" should generally always be used. While "async" gives better performance, it violates the spec and may cause data loss in case of power loss.
# "root_squash" maps client root users to an anon user to prevent remote root access. If that's desired, set "no_root_squash" instead.
# "no_subtree_check" disables subtree checking. Subtree checking may be appropriate for certain file systems, but in general it may cause more problems than it solves.
# "insecure" allows clients connecting from non-well-known ports.
/export/ *(fsid=root,ro,sync,root_squash,no_subtree_check,insecure)
/export/projects/ *(rw,sync,root_squash,no_subtree_check,insecure)
apt install nfs-common
mount -t nfs4 <server-hostname>:<export> <mountpoint>
/etc/fstab
entry: <nfs-server>:<export> <local-dir> nfs4 defaults 0 0
ntopng
.ntopng
is enabled and running.chown nobody:nogroup /var/log/ntopng
/etc/ntopng.conf
.-W=<new_port>
to enable HTTPS.-w=0
to disable HTTP.Note: I recommend Chrony instead of ntpd. It's newer and by design more accurate.
systemd-timesyncd
.ntp
./etc/ntp.conf
, with the iburst
option.ntpq -pn
(it may take a minute to synchronize).Instructions for both servers and clients. Exclusive steps are marked "(Server)" or "(Client)".
Since SSL/TLS is not enabled by default for client-server communication, use only trusted networks for this communication.
apt install nut
/etc/nut/nut.conf
and set MODE=netserver
for server or MODE=netclient
for client./etc/nut/ups.conf
and add a declaration for all UPSes (see example below).
usbhid-ups
driver if using USB. Otherwise, check the hardware compatibility list to find the correct driver. If the exact model isn't there, try a similar one.usbhid-ups
, see the example below and usbhid-ups(8).systemctl restart nut-driver.service
/etc/nut/upsd.conf
and set LISTEN ::
.
LISTEN
directives for only the endpoints you wish to listen on./etc/nut/upsd.users
and add users (see example below).
systemctl restart nut-server.service
nut-client.service
./etc/nut/upsmon.conf
and add MONITOR <ups>@<host>[:<port>] <ups-count> <user> <password> <master|slave>
.
ups-count
is typically 1
. If this system is not powered by the UPS but you want to monitor it without shutting down, set it to 0
.RBWARNTIME
(how often upsmon should complain about batteries needing replacement) to an appropriate value, e.g. 604800 (1 week)./etc/nut/upsmon.conf
, add EXEC
to all NOTIFYFLAG
entries you want to run the script for (typically all except LOWBATT
)./etc/nut/upsmon.conf
, set the script to run using format NOTIFYCMD /opt/scripts/nut-notify.sh
.systemctl restart nut-monitor.service
nut-monitor
successfully connected to the server.
upsc
does not use a server user or the monitoring service, so it's not very useful for debugging that.upsrw -s battery.runtime.low=<seconds> <ups>
and upsrw -s battery.charge.low=<percent> <ups>
usbhid-ups
, this is set using offdelay
and ondelay
. Otherwise, it's set using ups.delay.shutdown
and ups.delay.start
. The start delay must be greater than the stop delay.
upsmon -c fsd
Example USB UPS declaration for usbhid-ups
(/etc/nut/ups.conf
):
[alpha]
desc = "PowerWalker VI 3000 RLE"
# usbhid-ups should work for most UPSes with
driver = usbhid-ups
# If you have multiple UPSes connected, see usbhid-ups(8) for more specifying which USB device it should use
port = auto
# Sets "ups.delay.shutdown", the delay between the shutdown command and when the UPS powers off (default 20s)
offdelay = 60
# Sets "ups.delay.start", which has something to do with letting the UPS charge enough to make sure devices may fully boot (default 30s, must be greater than offdelay)
ondelay = 120
Example server users (/etc/nut/upsd.users
):
[admin]
password = <password>
actions = SET
instcmds = ALL
[local]
password = <password>
upsmon master
Example notify script:
#!/bin/bash
echo -e "Time: $(date)\nMessage: $@" | mail -s "NUT: $@" root
upsc -l
upsc <ups>
telnet localhost 3493
LIST UPS
LIST VAR <ups>
-noout -text
prints the data as formatted text instead of raw Base64.openssl x509 -in <cert-file> [-inform der] -noout -text
openssl s_client -connect <site>:443 </dev/null | openssl x509 -noout -text
Create self-signed cert for localhost/localdomain:
openssl req -new -x509 -newkey rsa:2048 -sha256 -nodes -out localhost.crt -keyout localhost.key -config <(
cat <<-EOF
[req]
default_bits = 2048
prompt = no
default_md = sha256
x509_extensions = ext
distinguished_name = dn
[ext]
subjectAltName = @alt_names
basicConstraints = CA:FALSE
#keyUsage = digitalSignature, keyEncipherment
#extendedKeyUsage = serverAuth
[dn]
C = ZZ
ST = Localhost
L = Localhost
O = Localhost
OU = Localhost
emailAddress = webmaster@localhost
CN = localhost
[alt_names]
DNS.1 = *.localdomain.
EOF
)
docker logs pihole 2>&1 | grep "random password"
/etc/pihole/adlists.list
./etc/pihole/whitelist.txt
.pihole -g
to update lists.modprobe msr
apt install linux-tools-generic
git clone https://github.com/opcm/pcm
make
.x
suffixes.pcm
pcm-memory
pcm-latency
pcm-pcie
pcm-iio
pcm-numa
pcm-power
pcm-tsx
pcm-core
pcm-query
pcm-raw
pcm-hw-histogram
pcm-sensor-server
).pcm-sensor
pcm-service
pcm-sensor-server
Is typically run on a Docker host. Includes the agent.
docker run -d -p 8000:8000 -p 9000:9000 --name=portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v ./data:/data portainer/portainer:<version>
/var/run/docker.sock
was mounted, use "local".Must be run on a Docker host. For extra Docker hosts you want to control with another Portainer server.
docker run -d -p 9001:9001 --name portainer_agent --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/docker/volumes:/var/lib/docker/volumes portainer/agent:<version>
_dmarc
subdomain: v=DMARC1; adkim=r; aspf=r; p=quarantine;
postfix libsasl2-modules mailutils
/etc/aliases
, add root: admin@example.net
(to forward everything to admin@example.net
).newaliases
to update the alias DB file. (Optionally restart postfix.service
to make it pick up the change instantly.)main.cf
config.
inet_interfaces = loopback-only
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
smtpd_banner = $myhostname ESMTP
compatibility_level = 2
apikey
as the username for API key access.touch sasl_passwd && chmod 600 sasl_passwd
[relay_domain]:port user@domain:password
postmap sasl_passwd
postfix
.echo "Test from $(hostname) at time $(date)." | mail -s "Test" root
Use this mess to change the ugly From: root@node.example.net
and To: root@node.example.net
to From: "Node" <root@node.example.net>
and To: "Admin" <admin@example.net>
when most/all email coming from the system is from root to some root alias.
smtp_header_checks
file (arbitrary name).
main.cf
: smtp_header_checks = regexp:/etc/postfix/smtp_header_checks
postmap -fq "From: root@$(hostname --fqdn)" regexp:smtp_header_checks
postfix
.echo "Test from $HOSTNAME at time $(date)." | mail -s "Test" root
postconf > /dev/null
postconf -n
mailq
tells you mails are stuck in the mail queue because of previous errors, run postqueue -f
to flush them.Typically used with Grafana and sometimes with Cortex/Thanos in-between.
docker inspect
or the source code.--storage.tsdb.retention.time=15d
and/or --storage.tsdb.retention.size=100GB
(with example values).storage.local.*
and storage.remote.*
flags no longer work../prometheus.yml:/etc/prometheus/prometheus.yml:ro
./data/:/prometheus/:rw
prometheus.yml
.
scrape_interval
, scrape_timeout
and evaluation_interval
) and scrape configs.sum(scrape_series_added) by (job)
, sum(scrape_samples_scraped) by (job)
, prometheus_tsdb_symbol_table_size_bytes
, rate(prometheus_tsdb_head_series_created_total[5m])
, sum(sum_over_time(scrape_series_added[5m])) by (job)
. You can also find some useful stats in the dashboard.This list contains exporters and software with built-in exposed metrics I typically use. Some are described in more detail in separate subsections.
Can be set up either using Docker (prom/node-exporter), using the package manager (prometheus-node-exporter
on Debian), or by building it from source. The Docker method provides a small level of protection as it's given only read-only system access. The package version is almost always out of date and is typically not optimal to use. If Docker isn't available and you want the latest version, build it from source.
See Building and running.
Details:
prometheus
/usr/bin/prometheus-node-exporter
/etc/systemd/system/prometheus-node-exporter.service
/etc/default/prometheus-node-exporter
/var/lib/prometheus/node-exporter/
Instructions:
apt install moreutils
wget <url>
and tar xvf <file>
cp node_exporter*/node_exporter /usr/bin/prometheus-node-exporter
node_exporter -h
useradd -r prometheus
touch /etc/default/prometheus-node-exporter
mkdir -p /var/lib/prometheus/node-exporter/
/etc/systemd/system/prometheus-node-exporter.service
, see prometheus-node-exporter.service./etc/default/prometheus-node-exporter
ARGS="--collector.processes --collector.interrupts --collector.systemd"
(enables more detailed process and interrupt collectors)systemctl enable --now prometheus-node-exporter
--collector.textfile.directory=<dir>
.
/var/lib/prometheus/node-exporter/
ARGS
variable in /etc/default/prometheus-node-exporter
./opt/prometheus/node-exporter/textfile-collectors/
sponge
is installed. For Debian, it's found in the moreutils
package./etc/cron.d/prometheus-node-exporter-textfile-collectors
0 * * * * root /opt/prometheus/node-exporter/textfile-collectors/apt.sh | sponge /var/lib/prometheus/node-exporter/apt.prom
Some I typically use.
Add a HTTP probe job for the services and query for probe success over time.
Example query: avg_over_time(probe_success{job="node"}[1d]) * 100
Add a HTTP probe job for the services and query for probe_ssl_earliest_cert_expiry - time()
.
Example alert rule: probe_ssl_earliest_cert_expiry{job="blackbox"} - time() < 86400 * 30
(30 days)
TODO
Logs are located in /app/storage/logs/laravel/
inside the container.
unzip
.{ "dns": ["1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001"] }
to /etc/docker/daemon.json
.See Counter-Strike: Global Offensive (CS:GO).
radvd
./etc/radvd.conf
apt install samba
systemctl disable --now nmbd
and systemctl mask nmbd
global
section.server string
security = user
server min protocol = SMB3
map to guest = never
guest account = <username>
(typically defaults to nobody
)guest
) to use the guest user: map to guest = bad user
guest ok = {yes|no}
guest ok
is set) (share option): only guest = yes
disable netbios = yes
netbios name = <name>
(defaults to hostname)workgroup = <workgroup>
smb encrypt = required
server smb encrypt = required
server multi channel support
rss
interface option and stuff. Maybe multiple NICs/IP addresses are required.socket options = SO_KEEPALIVE TCP_NODELAY IPTOS_LOWDELAY
socket options = SO_KEEPALIVE IPTOS_THROUGHPUT
aio read size = 1
and aio write size = 1
use sendfile = yes
min receivefile size = 16384
/etc/samba/smb.conf
testparm -t
systemctl restart smbd
useradd -r <name>
smbpasswd -a <user>
sudo pdbedit -L -v
apt install cifs-utils
Add permanent share:
Create a credentials file (/root/.credentials/smb/<whatever>
):
user=<user>
password=<password>
In /etc/fstab
, add: //<share> <mountpoint> cifs vers=3.1.1,uid=<uid>,gid=<gid>,credentials=<file>,iocharset=utf8 0 0
Test it: mount -a
Add automounted share:
/etc/fstab
entry, add ,noauto,x-systemd.automount,x-systemd.idle-timeout=30
.systemctl daemon-reload && systemctl restart remote-fs.target
apt install smartmontools
smartctl -a <dev>
smartctl -t <short|long|conveyance|select> [-C] <dev>
-C
: Foreground mode.PermitRootLogin without-password
in case you need root access to the server with tools that don't play nice with sudo.apt install tftpd-hpa
(note the d
)/etc/default/tftpd-hpa
TFTP_DIRECTORY="<dir>"
(e.g. /var/tftp
)TFTP_OPTIONS="[opt]*"
(see the most relevant options below)--secure
: Change the root directory to the specified TFTP_DIRECTORY
directory.--create
: Allow clients to upload new files. Existing files may be changed regardless.tftp:tftp
has read access.tftp:tftp
has write access to it.systemctl restart tftpd-hpa
apt install unbound dns-root-data
/etc/unbound/unbound.conf
/etc/hosts
contains the short and FQDN hostnames./etc/resolv.conf
.DNSStubListener=no
.DNS=::1
.systemd-resolved
./etc/resolv.conf
.Set:
nameserver 127.0.0.1
nameserver ::1
domain <domain>
search <domain-list>
systemctl restart unbound
drill sigfail.verteiltesysteme.net
should give an rcode of SERVFAIL
.drill sigok.verteiltesysteme.net
should give an rcode of NOERROR
./usr/share/dns/root.hints
.See Ubiquiti UniFi Controllers.
apt install wireguard
resolvconf
: ln -s /usr/bin/resolvectl /usr/local/bin/resolvconf
/etc/wireguard/*.conf
wg-quick {up|down} <conf>
systemctl enable wg-quick@wg0.service
(for config /etc/wireguard/wg0.conf
)Example tunnel config:
[Interface]
# Generate with "wg genkey"
PrivateKey = <HIDDEN>
# Address for the local tunnel interface
Address = 10.234.0.3/31, 2a0f:9400:800f:ff01::1/127
DNS = 1.1.1.1, 2606:4700:4700::1111
[Peer]
# Get with "echo <privkey> | wg pubkey"
PublicKey = <HIDDEN>
# Add static route and reverse path filtering
# "0.0.0.0/0, ::/0" means this will be the default gateway, capturing all traffic
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = vpn.oolacile.hon.systems.:51823
# Keep the connection alive to keep firewall state alive (not very stealthy, though)
PersistentKeepalive = 25
See Storage: ZFS.
{% include footer.md %}