#!/usr/bin/env bash # A log of how I configured FreeBSD spring 2018 on my Intel NUC Dawson Canyon NUC7i3DNH2E (NUC7i3DNH). # Modeline { # vi: foldmarker={,} foldmethod=marker foldlevel=0 tabstop=4 # } # Links { # FreeBSD vs Linux: # http://www.over-yonder.net/~fullermd/rants/bsd4linux/01 # https://forums.freebsd.org/threads/freebsd-vs-linux-10-points-of-superiority-including-motiv.41750/ # } # } # UEFI { # * Reboot, press F2 to enter BIOS. # * Home > Set system time. # * Home > Devices > Onboard Devices: disalbe Bluetooth. # * Home > Security > Set supervisor password # * Home > Boot > enable "Legacy boot" to be able to boot Linux images. # * Home > Boot > Boot Configuration > check "Boot USB devices first" # * Home > Power > check "Low power enabled". # * Home > Power > "After Powerfailure" -> "Last State", so that the server is restarted if it was powwerd on during AC failure. # ** NOTE it seems like my TV monitor is not activated when restarted from last state, so no display function. # * Home > Power > "Button LED" -> Power State # * Home > Power > "SO Indicator Brightness" -> OFF, so that I can sleep without having the LED in my face. # * Home > Power > "Wake on LAN from S4/S5" -> "Power On - Normal Boot", to enable WoL. # NOTE don't set this until after installing the OS and apps, as some juice can be needed to compile everything. # } # Install FreeBSD { # Burn ISO { # Download amd64 image from https://www.freebsd.org/where.html#download cd ~/dl/ curl -OJ https://download.freebsd.org/ftp/releases/amd64/amd64/ISO-IMAGES/11.1/FreeBSD-11.1-RELEASE-amd64-bootonly.iso.xz curl -OJ https://download.freebsd.org/ftp/releases/amd64/amd64/ISO-IMAGES/11.1/CHECKSUM.SHA512-FreeBSD-11.1-RELEASE-amd64 unxz FreeBSD-11.1-RELEASE-amd64-bootonly.iso.xz # Verify checksum sha512sum -c CHECKSUM.SHA512-FreeBSD-11.1-RELEASE-amd64  1  0 # Burn image to USB stick. sudo dd if=./FreeBSD-11.1-RELEASE-amd64-bootonly.iso of=/dev/sdc bs=1M status=progress # } # FreeBSD installer { # Handbook: https://www.freebsd.org/doc/handbook/ # * Reboot, press F10 to enter boot menu and select my USB stick. # * Select "Boot Multi User" # * Select "" # * Continue with defalt US keymap # * Set hostname to "server-name.erikw.me" # * Chose componentes to install: # - doc # - lib32 # - ports # * Accept default IPv4 DHCP & IPv6 SLAAC settings for default NIC. # * Select a German mirror for the distributions. # * For disk partition, select "Auto (ZFS). Select: # - Pol Type/Disks: stripe (no redundance), and select the main disk ada0. # - Pool name: zroot (default) # - Force 4k sectors: yes (default) # - Encrypted: yes # - Partition Scheme: GPT (BIOS + UEFI) # - Swap size: 4g # - Mirror swap: NO # - Encrypted Swap: YES # then select ">>> Install: Proceed with installtion". # * Enter a strong encryption password. # * Enter strong ~root user password. # * Select German timezone # * Select theser services to start at boot: # - local unbound DNS caching # - sshd # - ntpd # - powerd # - dumpdev # * Select security features: # - Clean /tmp on system start # * Create new user ~erikw, and add to additional group wheel so I can become super user. # * Exit to apply changes # * Reboot # # # # # SKIPPED manual partition { # SKIP manual parition - too complex and the installer does a good job. ## Create ZFS pools # Reference: https://www.freebsd.org/doc/handbook/zfs-zpool.html # ## Create ZFS datasets # Reference: https://www.freebsd.org/doc/handbook/zfs-zfs.html # |------+------------+------+-----------+---------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ # | pool | mountpoint | size | encrypted | options | notes | # |------+------------+------+-----------+---------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ # | tank | / | - | yes | | | # | tank | /home | - | yes | | | # | ? | /boot | ? | no | | | # | ? | /tmp | ? | yes | | | # | ? | swap | ? | yes | | should be 2x RAM = 2*16GB. However this old recommendation seems outdated and 1-1.5xRAM seems to be enought, https://askubuntu.com/questions/49109/i-have-16gb-ram-do-i-need-32gb-swap | # |------+------------+------+-----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ # # # Encrytp with Geli # https://www.freebsd.org/doc/handbook/disks-encrypting.html kldstat kldload geom_eli # } # } # } # System conf { # NOTE scroll up in the console by pressing Scroll Lock then Page Up/Down. # NOTE switch between virtual consoles with ALT + F1-8 # ~root configuration { # Change from defautl csh to sh for root: chsh -s /bin/sh root cat << EOF >> ~/.profile # Custom below: # Make sure /usr/local/ comes before /usr, so installed ports like unbound can override system default ones. PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:~/bin export PATH # # Use vi mode set -o vi # Set prompt export PS1='root@\h \W\$ ' # Aliases alias ls='ls -F' # Display / after directories alias ll='ls -l' alias vi=vim EOF # Fix tmux cat << EOF > /root/.tmux.com set-option -g status-keys vi # Use Vi bindings in tmux command prompt. set-option -g history-limit 131072 # Number of terminal lines per window to keep. (2^17) set-window-option -g mode-keys vi # Use Vi bindings in copy and choice mode. set-window-option -g monitor-activity on # Monitor windows for activity. unbind-key C-b # Unbind default prefix key. set-option -g prefix C-a # Seconday prefix key like GNU Screen. bind-key a send-prefix # Send ^A. bind-key b send-prefix # Send ^B. bind-key C-a last-window # Toogle last window. EOF # } # ~toor configuration { # Use toor for day-to-day tasks, using non-standard shell, as recommended at https://www.freebsd.org/doc/en/books/faq/security.html#idp60349640 # Change back to default shell for root chsh -s /bin/tcsh root # Set password for user passwd toor # Change shell to zsh chsh -s /usr/local/bin/zsh toor # Make minimal zshrc cat << EOF > ~/.zshrc source $HOME/.profile autoload -U colors && colors autoload -Uz promptinit promptinit prompt suse zstyle ':completion:*' completer _expand _complete _ignored _correct _approximate zstyle ':completion:*' menu select zstyle ':completion:*' squeeze-slashes true autoload -Uz compinit compinit -C export HISTFILE=~/.zsh_histfile export HISTSIZE=1000000 export SAVEHIST=1000000 unsetopt correct correctall EOF # Try it out su toor id exit # to rootsh # Make shell alias for ~erikw like: alias sudo='sudo -u toor'. # } # Disks { su - ## ZFS config # If the installer did not already set compression, do so on the whole pool, so the dataset inherits it. # The default poolname is zroot, which can be found with $(zpool list). zfs get compression zfs set compression=lz4 zroot # ZFS deduplication seems not be needed as $(zpool list) for zroot has 1.0 in deduplication factor (no duplicaes) for me. # https://constantin.glez.de/2011/07/27/zfs-to-dedupe-or-not-dedupe/ # https://www.freebsd.org/doc/handbook/zfs-zfs.html#zfs-zfs-deduplication # Set reserved space on root and home dataset so we never run out of disk here due to other datasets eating space. # https://www.freebsd.org/doc/handbook/zfs-zfs.html#zfs-zfs-reservation zfs set reservation=16G zroot/ROOT/default zfs set reservation=16G zroot/usr/home # Scrub pool -- do this form time to time zpool scrub zroot zpool status zroot # Make a copy of the encryption key needed to unlock the zroot pool with geli cp /boot/encryption.key /home/erikw/ chmod erikw /home/erikw/encryption.key # TO view swap setup grep swap /etc/fstab swapctl -lg # ZFS snapshots { # Reference: https://www.freebsd.org/doc/handbook/zfs-zfs.html # Create recursive snapshot in the entire pool zfs snapshot -r zroot@snap0_test zfs list -t all zfs list -t snapshot # See stats against original version touch /home/erikw/test zfs list -rt all zroot/usr/home # See diff against original zfs diff zroot/usr/home@snap0_test # Diff 2 snapshots zfs diff zroot/usr/home@snap0_test zroot/usr/home@snap1_test # Rollback rm /home/erikw/.viminfo zfs diff zroot/usr/home@snap0_test zfs rollback zroot/usr/home@snap0_test # Restore individual files ls /.zfs/snapshots cp /usr/home/.zfs/snapshot/snap0_test/erikw/.viminfo /home/erikw/ # Remove snapshot zfs destroy -nv -r zroot@snap0_test # Dry run zfs destroy -r zroot@snap0_test zfs list -t snapshot # Automatic snapshotting with zfstools { # Reference: http://eduardosanchez.me/2015/08/31/zfs-automatic-snapshots/ # Reference: https://blather.michaelwlucas.com/archives/2140 pkg install zfstools # Set which dataset to automatically snapshot with a ZFS property. # Start by enabling it on the whole pool. zfs set com.sun:auto-snapshot=true zroot # See current enabled datasets for auto snapshotting: zfs get com.sun:auto-snapshot # Now disable specific datasets in the pool. zfs list zfs set com.sun:auto-snapshot=false zroot/tmp zfs set com.sun:auto-snapshot=false zroot/usr/ports zfs set com.sun:auto-snapshot=false zroot/usr/src zfs set com.sun:auto-snapshot=false zroot/var/audit zfs set com.sun:auto-snapshot=false zroot/var/crash zfs set com.sun:auto-snapshot=false zroot/var/log zfs set com.sun:auto-snapshot=false zroot/var/tmp # Set up activation through cron: cat << EOF > /etc/cron.d/zfs-auto-snapshot SHELL=/bin/sh PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin # Order of crontab fields # minute hour mday month wday command # Reference: https://www.freebsd.org/doc/handbook/configtuning-cron.html # Reference: crontab(5). #15,30,45 * * * * root /usr/local/sbin/zfs-auto-snapshot frequent 4 0 * * * * root /usr/local/sbin/zfs-auto-snapshot hourly 24 7 0 * * * root /usr/local/sbin/zfs-auto-snapshot daily 7 14 0 * * 7 root /usr/local/sbin/zfs-auto-snapshot weekly 4 28 0 1 * * root /usr/local/sbin/zfs-auto-snapshot monthly 7 EOF # Lets try so it works: /usr/local/sbin/zfs-auto-snapshot hourly 24 zfs list -rt snapshot zroot/usr/home # # } # } # Change GELI passphrase { # See geli(8) # At some point I got tired of the boot passphrase for encrypted root disk, because my server now hosts essential network features like DHCP server and DNS server, and it already happended 2 times that the network was down for many days because I was not home to restart the server. Thus I decided to get rid of the manually entered passphrase for now. geli status geli list #geli setkey -n 0 -K /bootpool/boot/encryption.key -P /dev/ada0p5 # NOPE this is what one would think to work, to use -P to not use a passphrase. However it turns out that even if you use -P, and disable passphrase collection in loader.conf, geli will still prompt for password during boot. # However, it turns out that if we create a BLANK password, geli will apparently try a blank password during boot before prompting the user. geli setkey -n 0 -k /bootpool/boot/encryption.key -K /bootpool/boot/encryption.key /dev/ada0p5 # Make a backup and store in laptop ~/bak/server-name_nuc/ geli backup /dev/ada0p5 geli_backup_ada0p5_2018-08-02 # Disable collection passphrase in early boot for later usage. patch << EOF --- /bootpool/boot/loader.conf - geom_eli_passphrase_prompt="YES" + # Disabelded collection of passphrase during boot for later geli disk encryption, as I only use keyfile now (above). + #geom_eli_passphrase_prompt="YES" + geom_eli_passphrase_prompt="NO" EOF # } # } # Bootloader { # If messing up variables in here, froom the boot menu chose to enter the boot loader, then type "unset var" to remove value set in conf file. cat << EOF >> /boot/loader.conf # Custom below: # Reduce boot menu timeout from 10 seconds to 1. Pause the autoboot by pressing Space. # Settings to 0 does not allow pause with space. # See loader(8). autoboot_delay=1 EOF # } # rc.conf { cat << EOF > /etc/rc.conf # See default values in /etc/defaults/rc.conf # By default everything was set up here in /etc/rc.conf. This is messy, so I migrated out stuff to individual files in /etc/rc.conf.d to the corresponding /etc/rc.d files # First disable everything in this file, but keep dumpdev. # https://github.com/JoeKun/freebsd-configuration/tree/master/etc/rc.conf.d # https://www.rhyous.com/2012/04/12/decoupling-settings-from-etcrc-conf-in-freebsd/ # http://quiddle.net/post/77406007305/using-rcconfd-in-freebsd # Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable # Used by both /etc/{savecore,dumpon} dumpdev="AUTO" EOF # Now create the other files with migrated values from the rc.conf file: cat << EOF > /etc/rc.conf.d/cleartmp clear_tmp_enable="YES" EOF cat << EOF > /etc/rc.conf.d/hostname hostname="server-name.erikw.me" EOF cat << EOF > /etc/rc.conf.d/local_unbound local_unbound_enable="YES" EOF cat << EOF > /etc/rc.conf.d/network ifconfig_em0="DHCP" ifconfig_em0_ipv6="inet6 accept_rtadv" EOF cat << EOF > /etc/rc.conf.d/ntpd ntpd_enable="YES" EOF cat << EOF > /etc/rc.conf.d/powerd powerd_enable="YES" EOF cat << EOF > /etc/rc.conf.d/sshd sshd_enable="YES" EOF cat << EOF > /etc/rc.conf.d/zfs zfs_enable="YES" EOF cat << EOF > /etc/rc.conf.d/syslogd syslogd_enable="YES" EOF cat << EOF >> /etc/rc.conf.d/syscons # Increase vt(4) font size. Try out the fonts with vidfont(1). allscreens_flags="-f vgarom-16x32" # Increase keyboard repeat rate, in the form delay.speed. Default is 500.126. # See kbdcontrol(1), and test with $(kbdcontrol -r speed.delay). keyrate=350.20 EOF # Save some energy by configuring powerd to use adaptive profile. cat << EOF > /etc/rc.conf.d/powerd powerd_enable="YES" # User more power saving options in powerd(8) powerd_flags="-a adaptive" EOF # } # Keymaps { # Map Capslock to Escape # Reference: http://ake.in.th/2015/04/17/remap-capslock-to-ctrl-on-freebsd/ # Try existing layouts kbdmap # Make a copy of US layout cd /usr/share/vt/keymaps/ cp us.kbd us.custom.kbd chmod 744 us.custom.kbd # Change key 58 to produce escape: patch << EOF --- us.custom.kbd - 058 clock clock clock clock clock clock clock clock O + 058 esc esc esc esc esc esc esc esc O EOF chmod 444 us.custom.kbd # Add the new keymap to the index: chmod 744 INDEX.keymaps echo "# Custom below:" >> INDEX.keymaps echo "us.custom.kbd:en:United States of America Custom" >> INDEX.keymaps chmod 444 INDEX.keymaps # Try the layout kbdcontrol -l us.custom.kbd # Load this keymap by default: cat << EOF >> /etc/rc.conf.d/syscons # Use my custom keyboard layot defined in /usr/share/vt/keymaps/{us.custom.kbd,INDEX.keymaps}. keymap="us.custom" EOF # } # SSH { # https://www.freebsd.org/doc/handbook/openssh.html # # Make sure sshd is enabled in /etc/rc.conf: sshd_enable="YES" # and running $(service sshd status) # # # # sshd_config(5) # Verify default config with sshd -T | grep -i PermitRootLogin # Set options cat << EOF >> /etc/ssh/sshd_config ### Custom Below ### # Having DNS on make ssh login very slow. # Reference: http://jrs-s.net/2017/07/01/slow-ssh-logins/ # Reference: https://unix.stackexchange.com/questions/56941/what-is-the-point-of-sshd-usedns-option UseDNS no # Don't allow root logins: PermitRootLogin no # Misc X11Forwarding no Banner /etc/ssh/banner EOF echo "Welcome!" > /etc/ssh/banner service sshd reload # } # motd { # Set custom stuff in /etc/motd.tail mkdir /usr/local/sbin cat << EOF >> /usr/local/sbin/motd_gen.sh #!/usr/bin/env sh cp /etc/motd /etc/motd.bak uname -mnrs > /etc/motd cat /etc/motd.tail >> /etc/motd EOF chmod 744 /usr/local/sbin/motd_gen.sh # laptop$ scp ~/doc/tech/server-name/motd.tail server-name: mv /home/erikw/motd.tail /etc/ # } # Time { # It looks like the default ntp configuration should be able to pick ranom servers, but this does not seem to work, thus # Reference: https://www.freebsd.org/doc/handbook/network-ntp.html cat << EOF >> /etc/ntp.conf # German servers # Reference: http://www.pool.ntp.org/zone/de server 0.de.pool.ntp.org server 1.de.pool.ntp.org server 2.de.pool.ntp.org server 3.de.pool.ntp.org # Comment all default restric-lines, and enable the deny rules for all ntp queries against this machine: restrict default ignore restrict -6 default ignore EOF # Set up timezone with tzsetup # If timezone is right, but time is wrong, then you might need to update timezone in kernel: # Reference: https://forums.freebsd.org/threads/wrong-time-but-right-timezone.56345/ adjkerntz -i # } # Firewall: ipfw { # Reference: https://www.freebsd.org/doc/handbook/firewalls-ipfw.html # Reference: https://www.digitalocean.com/community/tutorials/recommended-steps-for-new-freebsd-10-1-servers # First, in case of fuck-up, disable the default deny-all cat << EOF >> /boot/loader.conf # Temporarily disable ipfs's default deny-all rule, when configuiring ipfw to not be locked out of the system in case of fuck-up. # NOTE by default have this line out-commented! kenrel_options=net.inet.ip.fw.default_to_accept="1" EOF cat << EOF > /etc/rc.conf.d/ipfw firewall_enable="YES" # Logfile is /var/log/security firewall_logging="YES" # My custom firewall script, which first uses /etc/rc.firewall firewall_script="/etc/ipfw.rules" # See /etc/rc.firewall for workstation variables meaning. firewall_type="workstation" # User either port/proto or name from /etc/services # NOTE let ssh be controlled by knockd instead. #firewall_myservices="22/tcp" #firewall_myservices="ssh" firewall_allowservices="any" firewall_logdeny="YES" EOF cat << EOF >> /etc/rc.conf # /etc/rc.firewall does not read /etc/rc.conf.d/ipfw but this file, which is a scam. It should! if [ -e /etc/rc.conf.d/ipfw ]; then . /etc/rc.conf.d/ipfw fi EOF # Create my own ipfw script to have custom rules: cat << EOF > /etc/ipfw.rules #!/bin/sh # Custom ipfw script. # See: https://www.freebsd.org/doc/handbook/firewalls-ipfw.html ## Flush out the list before we begin. #ipfw -q -f flush # Start by sourcing the default scrip, we only want to append to this one. . /etc/rc.firewall ## Set rules command prefix cmd="ipfw -q add" pif="em0" # interface name of NIC attached to Internet # Mosh # Reference: https://github.com/ptudor/freebsd-install/blob/master/etc/ipfw.rules $cmd 24110 allow udp from any to any 60000-61000 in keep-state # Knockd workaround; allow ssh from local network., # From specific host: #$cmd 900 allow tcp from any to me src-ip 192.168.178.21 dst-port 22 # From whole LAN: # Fritz.box local net $cmd 900 allow tcp from any to me src-ip 192.168.178.0/24 dst-port 22 # server-name local net $cmd 900 allow tcp from any to me src-ip 10.0.0.0/8 dst-port 22 EOF service ipfw start # Show loaded configs: ipfw show # Now enable the default deny-all rule in /boot/loader.conf # } # Firewall: pf { # ipfw was too complex to config for openvpn nat.. # Reference: https://www.freebsd.org/doc/handbook/firewalls-pf.html service ipfw stop cat << EOF >/etc/rc.conf.d/pf pf_enable="YES" #pf_flags="" EOF # Logging cat << EOF >/etc/rc.conf.d/pflog pflog_enable="YES" EOF service pflog start cat << EOF >/etc/pf.conf # pf port filter conf. # See pf.conf(5) # == Macros == # Names from /etc/services can be used ext_if = "em0" vpn_if = "tun0" local_subnet = "10.0.0.0/8" vpn_subnet = "172.16.123.0/24" # The direct network attached to this nic. localnet = $ext_if:network srv_znc = "6677" srv_taskd = "53589" srv_webmin = "10000" srv_cupsd_admin = "631" srv_samba = "hosts2-ns netbios-ns netbios-dgm netbios-ssn microsoft-ds" srv_mosh = "60000:61000" # Ports to open for incoming traffic. # ssh - let this be controlled by knockd instead. # ftpd - this enabled active mode, where server opens the data channel to the client. Use this so we don't have to open incomming trafic on the high level ports for passive. mode. Reference: https://stackoverflow.com/questions/1699145/what-is-the-difference-between-active-and-passive-ftp and https://forums.freebsd.org/threads/ftp-ipfw-passive-rules.34618/ # domain - DNS local caching tcp_ingress = "{" \ domain \ ftp \ $srv_cupsd_admin \ $srv_samba \ $srv_taskd \ $srv_webmin \ $srv_znc \ "}" # domain - DNS local caching # isc-dhcpd - my DHCP server # openvpn - OpenVPN server udp_ingress = "{" \ bootps \ domain \ openvpn \ $srv_mosh \ "}" # == Options == # Don't filter at all on loop back interface. Needed for unbound DNS server to work. set skip on lo # Which interface to log set loginterface $ext_if # == Translation == # OpenVPN NAT # Allow traffic to flow from virtual network to my local (and thus Internet) nat on $ext_if inet from $vpn_subnet to any -> $ext_if # == Filtering == # By default, block everything. block in all # Log all dropped incoming packages. block drop log on $ext_if all # Allows connections created by this system to pass out, while retaining state information on those connections. This state information allows return traffic for those connections to pass back and should only be used on machines that can be trusted. pass out all keep state pass in on $ext_if proto tcp to ($ext_if) port $tcp_ingress pass in on $ext_if proto udp to ($ext_if) port $udp_ingress # Knockd # Always allow ssh from local network, then open for externals when knocking succeeds. pass in on $ext_if proto tcp from $local_subnet to $ext_if port ssh # Create a separate IP address table for knockd addresses. Entires in this table gains ssh access. # knockd uses pfctl to modify this talbe. #Inspect the contents wit $(pfctl -t knockd -T show) table persist pass in on $ext_if inet proto tcp from to ($ext_if) port ssh flags S/SA keep state # Allow ICMP (mostly ping) from peers in the same net. pass inet proto icmp from $localnet to any keep state pass inet proto icmp from any to $ext_if keep state # Allow outgoing traceroute pass out on $ext_if inet proto udp from any to any port 33433 >< 33626 keep state EOF # Start pf pfctl -e # Test rules pfctl -nf /etc/pf.conf # Load rules for real pfctl -f /etc/pf.conf # The best reload: pfctl -f /etc/pf.conf && pfctl -sr # Check conf for errors pfctl -vnf /etc/pf.conf service pf onereload # Flush all configs and reload # NOTE this kicks ssh connection of, and you have to connect again. pfctl -F all -f /etc/pf.conf # View final rules: pfctl -sr # Stop pf pfctl -d # Test from other host: # Ports 1-100 # laptop$ nmap -F 10.0.0.2 # Ports 1-65535 # laptop$ nmap -p- 10.0.0.2 # A single port: # laptop$ nmap -p 22 10.0.0.2 # Connect to random port # laptop$ netcat srv 123 # To view logs, use tcpdump as the log is binary tcpdump -r /var/log/pflog # Or with more info like wich rules were applied tcpdump -n -e -ttt -r /var/log/pflog # To view live, use the pflog interface tcpdump -n -e -ttt -i pflog0 # When config esp. ssh works: service pf start # } # knockd { # http://www.zeroflux.org/projects/knock # Note that it adds only obfuscation and possibly security vulnerabilities: http://bsdly.blogspot.de/2012/04/why-not-use-port-knocking.html https://en.wikipedia.org/wiki/Port_knocking#Security_considerations # Now install and conf knockd pkg install knock mkdir /usr/local/etc/rc.conf.d/ cat << EOF > /usr/local/etc/rc.conf.d/knockd knockd_enable="YES" EOF cp /usr/local/etc/knockd.conf.sample /usr/local/etc/knockd.conf # Edit knockd.conf to look like: cat << EOF > /usr/local/etc/knockd.conf [options] logfile = /var/log/knockd.log interface = em0 #[openSSH] # sequence = ... # seq_timeout = 10 # #command = /sbin/ipfw -q add pass proto tcp src-ip %IP% dst-port 22 # # Not giving the rule a number appends it to the last rule. This is alreay too late as rules are evaluated from top to botton. So let's chose a lower range. # command = /sbin/ipfw -q add 1000 pass proto tcp src-ip %IP% dst-port 22 # tcpflags = syn [opencloseSSH] sequence = .... seq_timeout = 10 tcpflags = syn start_command = /sbin/ipfw -q add 1000 pass proto tcp src-ip %IP% dst-port 22 # Close connection after 30m automatically. cmd_timeout = 1800 stop_command = /sbin/ipfw -q delete 1000 pass proto tcp src-ip %IP% dst-port 22 [closeSSH] sequence = .... seq_timeout = 10 #command = /sbin/ipfw -q delete pass proto tcp src-ip %IP% dst-port 22 # The delete command must match the number from when adding it. command = /sbin/ipfw -q delete 1000 pass proto tcp src-ip %IP% dst-port 22 tcpflags = syn EOF # set interface=em0 service knockd start # Now configure my router to let TCP packages on these ports through to my router. # Name rules like: # knock-ssh-open: #1 # knock-ssh-open: #2 # knock-ssh-open: #3 # knock-ssh-close: #1 # knock-ssh-close: #2 # knock-ssh-close: #3 # Tail the log before trying tail -f /var/log/knokcd.log # laptop$ knock 10.0.0.2 x y z # laptop$ ssh 10.0.0.2 # Verify with nmap from another computer that port 22 is hidden: # laptop $ nmap -Pn -p server-name.erikw.me # If it's working the state is "filtered", if not "open". ## Client notes # On Android "Port Knocker", decrease the delay between packages to 300ms. Otherwise it seems like they don't arrive. # } # sudo { # Reference: https://www.freebsd.org/doc/handbook/security-sudo.html pkg install sudo visudo # Uncomment so wheel can use sudo & enable logging per user: # %wheel ALL=(ALL) ALL # ## Uncomment to enable logging of a command's output, except for ## sudoreplay and reboot. Use sudoreplay to play back logged sessions. #Defaults log_output #Defaults!/usr/bin/sudoreplay !log_output #Defaults!/usr/local/bin/sudoreplay !log_output #Defaults!REBOOT !log_output cat << EOF > /usr/local/etc/sudoers.d/99_mystuff # My own sudo settings. # Set cached password timeout in minutes. Defaults:USER_NAME timestamp_timeout=16 # Single password cache for user. Defaults !tty_tickets # Insult when bad password is entered. Defaults insults # Paths so users don't have to use full paths. Defaults secure_path = "/bin:/sbin:/usr/bin:/usr/sbin" # Dir needed for sudoreplay logging set up in /usr/local/etc/sudoers. Defaults iolog_dir=/var/log/sudo-io/%{user} # Command groups. Cmnd_Alias CMDS_POWER = /sbin/halt, /sbin/poweroff, /sbin/shutdown, /sbin/reboot # Let power users issue power commands. %power ALL = NOPASSWD: CMDS_POWER EOF pw group add power pw group show power pw groupmod power -M erikw pw group show power # } # Dyndns with ddclient { # NOTE with ssmtp, sending of email seems to work only when executing myself on commandline, some times, and not when running as a service. pkg install ddclient cp /usr/local/etc/ddclient.conf.sample /usr/local/etc/ddclient.conf cat << EOF > /usr/local/etc/ddclient.conf daemon=300 # check every 300 seconds syslog=yes # log update msgs to syslog mail=root # mail all msgs to root mail-failure=root # mail failed update msgs to root pid=/var/run/ddclient.pid # record PID in file. ssl=yes # use ssl-support. Works with # Source: https://support.loopia.se/wiki/ddclient-linux-och-unix/ ## ## LoopiaDNS (www.loopia.se/loopiadns/) ## (supports variables: wildcard,mx,backupmx) ## protocol=dyndns2 custom=yes server=dyndns.loopia.se use=web, web=dyndns.loopia.se/checkip, web-skip='Current IP Address:' login=... password=... wildcard=yes erikw.me EOF cat << EOF > /usr/local/etc/rc.conf.d/ddclient ddclient_enable="YES" EOF chmod 500 /usr/local/etc/ddclient.conf service ddclient start # Try update myself with ddclient --force # } # Wireless NIC { # Reference: https://www.freebsd.org/doc/handbook/config-network-setup.html # Show modules currently loaded: kldstat # Available kernels at ls /boot/kernel/ # My NUC's wireless card is "Integrated Wireless‡: Intel® Wireless-AC 8265 (IEEE 802.11ac 2x2)" # Unfortunately iwm(4) does not list support for this card. But code for this seems to be merged # https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=220229 # and is said to be included in upcoming FreeBSD 12.0 # If there would be support, I would probably load it like this: kldload if_iwm kldload iwm8000Cfw ifconfig pciconf -l | grep iwm # and add to /etc/loader.conf: # if_iwm_load="YES" # iwm7260fw_load="YES" # Reference: https://forums.freebsd.org/threads/intel-7260.55061/ # * Then connect to a WiFi: # Reference: https://www.freebsd.org/doc/handbook/network-wireless.html # Reference: https://www.freebsd.org/cgi/man.cgi?query=iwm&sektion=4&manpath=freebsd-release-ports # Create the interface ifconfig wlan createwlandeviwm0 up ifconfig wlan0 scan # In case of fuck-up, delete it with. ifconfig wlan0 destroy cat << EOF >> /etc/rc.conf.d/network # WLAN wlans_iwm0="wlan0" #ifconfig_wlan0="WPA DHCP" ifconfig_wlan0="WPA 10.0.0.18 netmask 255.0.0.0" EOF cat << EOF >> /etc/wpa_supplicant.conf network={ ssid="westnet" key_mgmt=WPA-PSK scan_ssid=1 psk="" } EOF # Try it out service netif restart ifconfig wlan0 # Try WPA supplicant manually with wpa_supplicant -i wlan0 -c /etc/wpa_supplicant.conf -dd # NOTE must also update firewall to apply same rule to wlan NIC as to eth NIC. # NOTE must also update router forwarding rules. # NOTE it did not work with westnet when channel 13 was used, but worked when moved to channel 8. # } # Rootkit Hunter { pkg install rkhunter # Run manually: rkhunter --check-all --skip-keypress cat << EOF >> /etc/periodic.conf # rkhunter # Enable daily rkhunter run. Commands comes from the post-install message in pkg-install daily_rkhunter_update_enable="YES" daily_rkhunter_update_flags="--update --nocolors" # Run rkhunter as a part of the daily security check daily_rkhunter_check_enable="YES" daily_rkhunter_check_flags="--checkall --nocolors --skip-keypress" EOF # Config so known problems are not shown as warnings # in /usr/local/etc/rkhunter.conf, set # ALLOW_SSH_ROOT_USER=no cat << EOF >>/usr/local/etc/rkhunter.conf #### CUSTOM BELOW ### PWDLESS_ACCOUNTS='ftp pcguest' ALLOWHIDDENDIR=/etc/.git ALLOWHIDDENFILE=/etc/.gitignore EOF # Property database must be updated with new values: rkhunter --propupd # } # Audit logging { # Reference: https://www.freebsd.org/doc/handbook/audit-config.html # * Start at boot. cat << EOF > /usr/local/etc/rc.conf.d/auditd auditd_enable="YES" EOF # Set selection what to audit, and how much disk space to save: cat << EOF > /etc/security/audit_control dir:/var/audit dist:off flags:lo,aa minfree:20 naflags:lo,aa,ex policy:cnt,argv filesz:2M expire-after:10M EOF # Start service to test. service auditd start # View audit log: praudit /var/audit/current # Track live: praudit /dev/auditpipe # } # Version control /etc & /usr/local/etc { # Unfortunately etckeeper has no freebsd support, so a manual git repo is what is left to do. cd /etc touch .gitignore git init git add . git commit -m "Initial commit" cd /usr/local/etc cat << EOF > .gitignore asciidoc/ fonts/conf.avail/ EOF git init git add . git commit -m "Initial commit" # } # Python { # Set default python version echo >> /etc/make.conf DEFAULT_VERSIONS=python=3.6 python2=2.7 python3=3.6 EOF # pip is not included in python ports. python3.4 -m ensurepip pip3 install --upgrade pip # NOTE at a later point pip3 broke and I did portmaster devel/py-pip pip3 install --upgrade pip # } # Anti-virus with clamAV { # Reference: http://www.octopuscs.com/blogs/Windows/How-to-install-clamav-on-FreeBSD portmaster security/clamav # Note that the rc.conf.d scipts has _ instead of - that the rc.d script has in the name. It seems to be that the 'name' variable in the rc.d script mandates what the rc.conf.d file should be called. cat << EOF > /usr/local/etc/rc.conf.d/clamav_clamd clamav_clamd_enable="YES" EOF cat << EOF > /usr/local/etc/rc.conf.d/clamav_freshclam clamav_freshclam_enable="YES" EOF cat << EOF > /usr/local/etc/clamd.conf ### Own Configuration below ### # Skip scanning emails, like "Heuristics.Phishing.Email.SpoofedDomain". My /home/erikw/.mail/spam/ files gets triggered, and I know they are bad already. PhishingScanURLs no EOF # Make initial scan freshclam clamscan -r -i / # Move files to quarantine mkdir /var/local/clamscan-quarantine chown clamav:clamav /var/local/clamscan-quarantine/ # Schedle a cronjob cat << EOF > /etc/cron.d/clanscan SHELL=/bin/sh PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin # Order of crontab fields # minute hour mday month wday command # Reference: https://www.freebsd.org/doc/handbook/configtuning-cron.html # Reference: crontab(5). @weekly root cron_mail -s "Weekly virus scan" "clamscan --recursive --infected --move=/var/local/clamscan-quarantine / 2>/dev/null" EOF # } # Printer Brother DCP-7070DW { # CUPS { # Reference: https://www.freebsd.org/doc/en_US.ISO8859-1/articles/cups/printing-cups-install.html portmaster print/cups # This package is needed to actaully print. Used to be a part of the core package. portmaster print/cups-filters cat << EOF > /usr/local/etc/rc.conf.d/cupsd cupsd_enable="YES" EOF service cupsd start # Open ports in firwall for configuring from another computer in the local network. cat << EOF >> /etc/rc.conf.d/ipfw # cupsd web configuration interface. firewall_myservices="${firewall_myservices} 631/tcp" EOF service ipfw restart # Config must be updated to allow connection from other computer. This will rewrite the cups conf and restart the daemon. cupsctl --remote-admin --remote-any --share-printers cat << EOF >> /usr/local/etc/cups/cupsd.conf # Allow connection using alias domain name, https://srv:631 ServerAlias * EOF # Avoid using root account for administration. pw group add printadmin pw groupmod printadmin -M erikw pw group show printadmin patch << EOF --- /etc/cups/cups-files.conf - SystemGroup wheel + SystemGroup wheel printadmin EOF service cupsd restart # Install drivers, if generic ones does not do it. # Reference: http://tdotc.eu/2007/06/02/using-the-brother-printer-drivers-with-freebsd/ portmaster port emulators/linux_base-c7 portmaster print/psutils # Reference: https://www.freebsd.org/doc/handbook/linuxemu-lbc-install.html kldload linux kldstat cat << EOF >/etc/rc.conf.d/abi # Load linux emulation kernel module linux_enable="YES" EOF sysctl -w compat.linux.osrelease=2.6.9 sysctl compat.linux.osrelease cat << EOF >>/etc/sysctl.conf # Set Linux kernel version to emulate. Needed for using my Brother printer drivers. compat.linux.osrelease=2.6.9 EOF # Hack to get printing to work. # Puttings these commands direclty in /etc/rc.conf is not good, as this file is sourced many times during boot. cat << EOF >> /usr/local/etc/rc.d/reload_linux_kmod #!/bin/sh # $FreeBSD: head/deskutils/taskd/files/taskd.in 454856 2017-11-24 23:17:50Z dbaio $ # # PROVIDE: reload_linux_kmod # REQUIRE: LOGIN # KEYWORD: shutdown . /etc/rc.subr name=reload_linux_kmod rcvar=reload_linux_kmod_enable load_rc_config $name : ${reload_linux_kmod_enable="NO"} command=/usr/local/bin/${name} command_args="" run_rc_command "$1" EOF cat << EOF >> /usr/local/etc/rc.conf.d/reload_linux_kmod reload_linux_kmod_enable="YES" EOF cat << EOF >> /usr/local/bin/reload_linux_kmod #!/usr/bin/env sh # HACK # For some reason, the printing only works if we after boot unload both linux and linux64 module, then load linux module. # Modules are first loaded from /etc/rc.conf.d/abi. { sleep 60 kldstat | grep -q linux64.ko && kldunload linux64 kldstat | grep -q linux.ko && kldunload linux kldload linux || : } & EOF # By trail-and-error it was found that hostname resolver in the linux emulation was blocking the printing from working. # Reference: https://www.freebsd.org/doc/handbook/linuxemu-lbc-install.html # NOTE this was not true, the issue was to reload the linux kernel. cat << EOF >> /compat/linux/etc/host.conf order hosts, bind multi on EOF su - erikw # Fetch LRP & Cups package. Install Cups before LPR. mkdir ~/src/brother_dcp-7070dw cd ~/src/brother_dcp-7070dw mkdir lpr && cd lpr curl -O -J http://www.brother.com/pub/bsc/linux/dlf/cupswrapperDCP7070DW-2.0.4-2.i386.rpm tar xvjf *.rpm sudo cp -r usr/local/Brother /usr/local sudo cp -r var/spool/lpd/DCP7070DW /var/spool/lpd cd .. mkdir cupswrapper && cd cupswrapper curl -O -J http://www.brother.com/pub/bsc/linux/dlf/dcp7070dwlpr-2.1.0-1.i386.rpm tar xvjf *.rpm sudo cp -r usr/local/Brother/Printer/DCP7070DW/cupswrapper /usr/local/Brother/Printer/DCP7070DW sed -i '' \ -e 's$/etc/init.d/cups$/usr/local/etc/rc.d/cupsd$g' \ -e 's$/etc/init.d$/usr/local/etc/rc.d$g' \ -e 's$/usr/share$/usr/local/share$g' \ -e 's$/usr/lib$/usr/local/lib$g' \ -e 's$/usr/lib64$/usr/local/lib64$g' \ -e 's$/usr/bin$/usr/local/bin$g' \ usr/local/Brother/Printer/DCP7070DW/cupswrapper/cupswrapperDCP7070DW-* exit # to root sh. # Run config /usr/local/Brother/Printer/DCP7070DW/cupswrapper/cupswrapperDCP7070DW-* -i ln -s /usr/local/lib/cups/filter/brlpdwrapperDCP7070DW /usr/local/libexec/cups/filter/brlpdwrapperDCP7070DW # In the error log, this is printed: # "Unable to communicate with avahi-daemon: Daemon not running" # which suggests to install avahi daemon: http://www.alexforencich.com/wiki/en/freebsd/installing_avahi # NOTE this was not needed in the end; skip! portmaster net/avahi portmaster dns/nss_mdns cat << EOF >/usr/local/etc/rc.conf.d/avahi_daemon avahi_daemon_enable="YES" EOF cat << EOF >/usr/local/etc/rc.conf.d/dbus dbus_enable="YES" EOF patch << EOF --- /etc/nsswitch.conf - hosts: files dns + hosts: files dns mdns EOF # Now visit https://srv:631/ from another computer. # Administration > Add Printer > LPD/LPR Host or Printer > connection: socket://10.0.0.6:9100 > # * Set: # - Name: dcp7070dw # - Description: Brother DCP7070-DW # - Location: Berlin # - Share this printer: yes # * Chose: # - Driver: Generic # - Model: Generic PCL Laser Printer # * Set printer default optons: # - Media Size: A4 # - Duplex: DuplexNoTumble # - Toner Saver: On # Set default printer. lpstat -p -d lpoptions -d dcp7070dw # Test it! echo "test" | lpr lpq # Look for errors here tail -f /var/log/cups/error_log # } # Google Cloudprint { # Documentation: https://github.com/armooo/cloudprint # NOTE Google Cloudprint was suspended 2020 portmaster print/cloudprint cloudprint -i dcp7070dw cat << EOF >/usr/local/etc/rc.conf.d/cloudprint cloudprint_enable="YES" cloudprint_flags="-i dcp7070dw" EOF service start cloudprint # If getting authentication problems after not running for a while, log out and re-register cloudprint -l # Manage printers at https://www.google.com/cloudprint#printers # Android: Settings > Advanced > Printing > Cloud Print > triple dots > Settings > Manage printers # Possible to share to other Google users. # } # } # Wake on LAN (WoL) { # Reference: https://wiki.archlinux.org/index.php/Wake-on-LAN # Reference: http://linux-bsd-sharing.blogspot.com/2012/06/howto-enable-wake-on-lan-on-freebsd.html # WoL does not work on WiFi card, only physical Eth. # First make sure that my card supports WOL # List all drivers that have WOl support grep -l IFCAP_WOL /usr/src/sys/dev/*/*.c # Now compare this against my NICS, which of these have the WOL_MAGIC option/capability? ifconfig -m # Collect the MAC address of the card that supports it. ifconfig -m | grep ether # Now, try it from arch: # laptop$ yaourt -S wol # laptop$ sudo wol -i 10.0.0.2 ... # laptop$ sudo wol -i 255.255.255.255 .... # From macOS: # Reference: https://apple.stackexchange.com/questions/95246/wake-other-computers-from-mac-osx # mac$ brew install wakeonlan # mac$ wakeonlan -i 10.255.255.255 .... # From Android: # * https://play.google.com/store/apps/details?id=co.uk.mrwebb.wakeonlan # * https://play.google.com/store/apps/details?id=com.bitklog.wolon # To wake up from remote, set up in router forwarding rule for UDP port 7 & 9 to server. # } # Move /boot to USB stick { # Reference: https://web.archive.org/web/20180218101816/https://vesterman.com/FreeBSD/FullDiskEncryption # Insert USB stick. Shows up as /dev/da0 in this case. # Clear old partitions gpart destroy -F da0 # Create a GPT partitioning scheme on the USB drive: gpart create -s gpt da0 # Add two partitions to the USB drive, the first for the boot loader and the second for /boot: gpart add -t freebsd-boot -s 512k -a 4k da0 gpart add -t freebsd-ufs -l boot -s 1g -a 1m da0 gpart show da0 # Install the bootcode to partition 1 on the USB drive: gpart bootcode -b /boot/pmbr -p /boot/gptboot -i 1 da0 # Create File System newfs -U -L bootstick_straps /dev/da0p2 # Copy existing boot stuff to USB stick mkdir /media/usb_media mount /dev/da0p2 /media/usb_media cp -Rpv /bootpool/ /media/usb_media/ # Now reboot and select the USB drive in BIOS/UEFI # Automatically mount the disk # Reference: https://people.freebsd.org/~trhodes/doc/handbook/geom-glabel.html # If label was forgot with newfs, add it aftwards: gpart modify -i 1 -l bootstick_code da0 gpart modify -i 2 -l bootstick_straps da0 mkdir /media/bootstick_straps cat << EOF >> /etc/fstab /dev/gpt/bootstick_straps /media/bootstick_straps ufs rw,late 0 0 EOF mount -al # Remove encryption key on /bootpool rm /bootpool/boot/encryption.key # Now, always update the USB stick code with updates from /bootpool whenever those are updated from system upgrades. # NOTE that /bootpool does not have the encryption key anymore, only the USB stick!! cat << EOF >> /root/.profile alias bootfiles_copy_to_usbstick='cp -Rpv /bootpool/ /media/bootstick_straps/' EOF # Now reboot again and verify # 1) that the usb stick is mounted # 2) system is not loaded if usb stick is not plugged in # Now update BIOS/UEFI so that USB drivers are highest in the boot order prio. # NOTE I was never able to create an USB stick that my NUC would find during the boot sequence. It's possible to select it manually in the F10 boot mentu, but it was never detected automatically. I tried 4 different sticks from different brands. So I reverted chagned above: added back encryption.key to system disk and commented out alias, and fstab entry. # } # Python setup { # Firt make sure that meta package is not installed pkg delete python # Set default version cat << EOF > /etc/makeconf DEFAULT_VERSIONS=python=3.7 EOF # Install meta package portmaster lang/python # Upgrade to newer version, see /usr/port/UPDATING for latest instructions, here's a copy of py37 # For portmaster users: portmaster -o lang/python37 python36 REINSTALL="$(pkg info -o py36-\* | awk '{printf "%s ", $2}')" pkg delete -f py36-\* portmaster $REINSTALL REBUILD=$(pkg query -g "%n:%dn" '*' | grep py3 | grep -v py37 | cut -d : -f 1 | sort -u) portmaster $REBUILD REBUILD2=$(pkg list | grep python-36 | xargs pkg which | awk '{print $6}' | sort -u) portmaster $REBUILD2 # Additionally I had to rebuild vim myself: portmaster editors/vim # } # } # System Upgrades & Package Management { # TL;DR { freebsd-version freebsd-update fetch znp freebsd-update install znp freebsd-update upgrade -r 11.2-RELEASE # Upgrade to newer release branch. pkg update pkg version -vl '<' znp pkg upgrade portsnap auto portmaster -L less /usr/ports/UPDATING znp portmaster -aGyd --no-confirm # } # FreeBSD source { # For some tasks, like reading kernel module documentation, the base FreeBSD source must be obtained to /usr/src # Reference: https://www.freebsd.org/doc/handbook/svn.html # Get certificates to verify the svn connection: pkg install ca_root_nss # Check out the base svnlite checkout https://svn.FreeBSD.org/base/head /usr/src # But let's switch to our branch freebsd-version svnlite ls https://svn.FreeBSD.org/base/ svnlite ls https://svn.FreeBSD.org/base/releng/ svnlite switch https://svn.FreeBSD.org/base/releng/11.1 /usr/src # See current branch svnlite info /usr/src # Check out the docs svnlite checkout https://svn.FreeBSD.org/doc/release/11.1.0 /usr/doc # Update docs: cd /usr/doc svnlite up # Or simply svnlite up /usr/doc # } # Upgrade FreeBSD base system { ## Patch upgrades { # Reference: https://www.freebsd.org/doc/handbook/updating-upgrading-freebsdupdate.html # A full upgrade of security patches typically consists of freebsd-update fetch freebsd-update install pkg upgrade # A reboot can be needed after $(freebsd-update install), for kernel changes and upgraded programs to work. # If there was problem, then freebsd-update rollback # Enable daily check for updates. Email will be sent when updates were fetched. cat << EOF > /etc/cron.d/freebsd-update SHELL=/bin/sh PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin # Order of crontab fields # minute hour mday month wday command # Reference: https://www.freebsd.org/doc/handbook/configtuning-cron.html # Reference: crontab(5). # Fetch FreeBSD base system updates, and send email if found. @daily root freebsd-update cron EOF ## } ## Major & Minor upgrades { ### Preperations { # First, read the release notes for the new version: https://www.freebsd.org/releases/ # # Make sure that latest patch levels are installed with freebsd-update, and all latest packages with pkg(1) and portmaster are installed. # For major version upgrades, consider using beadm to create a complete bootable copy of the old system. Seems like beadm(1) is being replaced with bectl(8). # Reference: https://www.adminbyaccident.com/freebsd/how-to-update-freebsd-using-beadm/ # Reference: https://www.freebsd.org/cgi/man.cgi?query=beadm # Reference: https://blather.michaelwlucas.com/archives/2363 bectl list # Create snapshot of current bectl create default@yyyy-mm-dd_pre_freebsd_xx.z_upgrade zfs list -t snapshot | grep pre_freebsd # Create new environment to install upgrade to bectl create 12.0-RELEASE bectl activate 12.0-RELEASE reboot freebsd-update install # Optionally make a backup of /etc in case of problem with mergemaster. OR make a zfs snapshot before. ###} ### Upgrade { # Reference: https://www.freebsd.org/doc/handbook/updating-upgrading-freebsdupdate.html#freebsdupdate-upgrade # Check in all unstaged changes in /etc /usr/local/etc git repos cd /etc; git status cd /usr/local/etc; git status # Clean up packages that are not needed before upgrade znp pkg autoremove # Create pre-backup snapshot of the whole system zfs snapshot -r zroot@12.0-RELEASE_upgrade_pre zfs list -t snapshot freebsd-version freebsd-update -r 12.0-RELEASE upgrade # Latest release version can be found at https://www.freebsd.org/releases/ freebsd-update install shutdown -r now freebsd-update install # Continues with clean-up phase. freebsd-version # During upgrade, there might be a lot of configuration conflicts. It can help to look at the complete whole new files: # First revert local changes not in remote branch. svn revert --recursive . # Default to accept the branch versions of a file in conflict. See $(svnlite help switch). svnlite switch --accept tf https://svn.FreeBSD.org/base/releng/12.0 /usr/src # Check in all unstaged changes in /etc /usr/local/etc git repos cd /etc; git status; git add .; git commmit -m "Upgrade to 12.0-RELEASE" # After upgrading a Major FreeBSD version, then the ABI has changes so we must reinstall all binaries: pkg-static upgrade -f portsnap auto # Check if there are blocking vulnerabilities that we need to ignore with "-m DISABLE_VULNERABILITIES=yes" to portmaster pkg audit -F # Upgrade to 12.1: package net/samba410 could not build because conflicting python package versions. Solution, uninstall the package, $(pkg autoremove), then do the re-build of all other packages, then install samba410 (which works, problem was only during full rebuild). portmaster -af -Gyd # Check config files again cd /etc; git status cd /usr/local/etc; git status # End by a last update command, to finish of loose ends: # Upgrade to 12.1: gave "Cannot identify running kernel" here. Solution was to $(ln -s /bootpool/boot /boot) as freebsd-update gets current versin from the file $(sysctl -n kern.bootfile). Reference: https://forums.freebsd.org/threads/cannot-identify-running-kernel.43329/ freebsd-update install # Clean up znp pkg autoremove # fetch newer versions of packages portsnap auto cd /usr/ports make index znp portmaster -aGyd # Check config files again cd /etc; git status cd /usr/local/etc; git status # Create post-backup snapshot of the whole system zfs snapshot -r zroot@12.0-RELEASE_upgrade_post zfs list -t snapshot # Compare the system against how it's expected to look. freebsd-update IDS >> /tmp/outfile.ids # View all files that differ from the system with the release: cat outfile.ids | awk '{ print $1 }' | more # And fix expected permissions e.g. like grep "have 0644" /tmp/outfile.ids | awk '{print $1}' | xargs echo chmod 0644: # Reboot and check system log for errors echo >/var/log/messags reboot less /var/log/messages # Check service that they work # - Samba # - web server (from external network) # - openvpn # - port knocking + ssh # - daily emails about scheduled jobs # Now fetch patch version of the OS freebsd-update fetch freebsd-update install ###} ## } # } # pkg - install binary packages { pkg update # List installed packages: pkg info pkg info vim # To see files installed by package: $ pkg info -l vim # To see which package installed a file: pkg which /usr/local/sbin/lsof # List only top-level installed, not dependencies: pkg prime-origins pkg search vim pkg install vim tmux ## See freebsd_packages.txt for all packages needed # To see pkg-message again after installing, do pkg info -D -x taskd # Note: mosh does not give scrollback in the terminal, thus always use screen/tmux with mosh # Delete package pkg delete vim # Remove now unneeded dependencies: pkg autoremove # This removes all packages that does not refere to any installed packages and are registered as automatic. To see which are not set to automatic: pkg set -A0 # List outdated packages: pkg version -vl '<' # The default is -P is to check against ports, however if you are _not_ interesting in using a ports version of a pkg version, then use pkg version -R -vl '<' # Upgrade packages pkg upgrade # Check for vulnerabilities, Do this from time to time. pkg audit -F # Remove packages which are not in the package index anymore: # Reference: https://raw.githubusercontent.com/freebsd/freebsd-ports/master/UPDATING pkg version -l \? | cut -f 1 -w | grep -v compat | xargs pkg delete -fy # Enable daily audit check # Reference: https://www.freebsd.org/doc/handbook/security-pkg.html cat << EOF >> /etc/periodic.conf # Enable dialiy $(pkg audit) run # old: daily_status_security_pkgaudit_enable="YES" security_status_pkgaudit_enable="YES" EOF # } # ports(7) - install from source { # To findout location of a port: whereis vim # or using the port search cd /usr/ports make fetchindex make search name=vim # or the quicksearch for less output make quicksearch name=vim # Update ports tree: portsnap fetch portsnap update # Or simply all in once: portsnap fetch update # or better portsnap auto # Install packages cd /usr/ports/textproc/p5-ack # Only fetch distfile to /usr/ports/distfiles/ make fetch # Set configuration options make showconfig make config # Or just/finally install and clean up used space afterwards make install clean # Install in to other prefix make PREFIX=/home/erikw/bin/ install # See list of installed packages in freebsd_packages.txt # To remove, remove with pkg pkg remove p5-ack # or use the ports makefile target cd /usr/ports/textprox/p5-ack make deinstall # portmaster { # Manage port updates with portmaster. Seems to be favrouted to portugprade, and don't depend on ruby. # https://www.freebsd.org/doc/handbook/ports-using.html#portmaster cd /usr/ports/ports-mgmt/portmaster make install clean # Enable logging for portmaster. cat << EOF >> /usr/local/etc/portaster.rc PM_LOG=/var/log/portmaster.log EOF # NOTE always read the file /usr/ports/UPDATING before updating! # List ports per cateogy and look for updates portsnap auto # Update /usr/ports itself portmaster -L # Upgrade everything portmaster -a # Upgrade everything, and skip $(make config) portmaster -aG # Or do that with a dry run portmaster -an # Exclude (broken) port from upgrade portmaster -aG -x brokenport # However this will use a ports version if this is newer than the installed version from pkg(1). Use only packages: # Will this still update packages that was not available in pkg but only as port? # It seems to be not advices to mix binary packages and ports, so maye I should let $(portmaster -a) take over and upgrade the binary installed packages? portmaster -PP # Upgrade only one port: portmaster textprox/p5-ack # Ingore conflicting depdendencies e.g. different python versions of the same package portmaster -m -DDISABLE_CONFLICTS print/cloudprint # Conflict on py27-docutils vs py36-docutils # Clean out dependencies that are no longer being dependent on. portmaster -s -d # To ignore known security vulnerabilities thst block a system upgrade (consider wisely!): portmaster -m DISABLE_VULNERABILITIES=yes ... # } # } # Port upgrade emails { # Set up autoatic emails about available updates. cat << EOF > /etc/cron.d/ports-upgrades SHELL=/bin/sh PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin # Order of crontab fields # minute hour mday month wday command # Reference: https://www.freebsd.org/doc/handbook/configtuning-cron.html # Reference: crontab(5). @daily root : Upgradable Ports ; pkg update >/dev/null && portsnap auto >/dev/null && pkg version -vl '<' EOF # } #} # Service setup { # Email with ssmtp { # To send an email, do this. Note, gmail puts these in the Spam mailbox. echo "hej" | mail -v -s "test" user@gmail.com # NOTE seems like ssmtp/loopia does not accept sending outside own domain => send to "root@erikw.me" or "root". # However, sending to root@erikw.me works fine. echo "hej" | mail -v -s "test root2" root@erikw.me # Tip; trigger periodic emails manually by running periodic like periodic daily # NOTE msmtp is supposedly the more modern version of ssmtp. However ssmtp seems to work, so let's upgrade when it's not working. # Reference: https://www.freebsd.org/doc/handbook/outgoing-only.html # portsnap mail/ssmtp - NOPE then we miss the replace option that fixes /etc/mail/mailer.conf cd /usr/ports/mail/ssmtp make install replace clean # Disable sendmail. # Reference: https://www.freebsd.org/doc/handbook/mail-changingmta.html#mail-disable-sendmail cat << EOF > /etc/rc.conf.d/sendmail sendmail_enable="NO" sendmail_submit_enable="NO" sendmail_outbound_enable="NO" sendmail_msp_queue_enable="NO" EOF cat << EOF >> /etc/periodic.conf # Disable sendmail(8) specific tasks, as it's replaced by ssmtp. daily_clean_hoststat_enable="NO" daily_status_mail_rejects_enable="NO" daily_status_include_submit_mailq="NO" daily_submit_queuerun="NO" EOF service sendmail stop # Config ssmtp: cat << EOF > /usr/local/etc/ssmtp/ssmtp.conf root=root@erikw.me mailhub=mail.erikw.me rewriteDomain=erikw.me hostname=_HOSTNAME_ UseTLS=YES UseSTARTTLS=Yes EOF # To map local user to email address for ssmtp, edit /etc/mail.rc. (for other MTAs /etc/aliases is used) # Reference: http://possiblelossofprecision.net/?p=591 cat << EOF >> /etc/mail.rc alias root root@erikw.me alias erikw erikw@erikw.me EOF # Seems ike revaliases could be needed for emails to root to be mapped to right email address, by ddclient? # NOTE seems like mailcluster.loopia.se can only Rsrv to addreeses that have set up mail forward aliases set up in their webinterface. # If they are not added: "Recipient address rejected: User unknown in relay recipient table" # Set up Loopia aliases for {root,admin,erik,erikw,contact}@erikw.me -alias forward to-> user@gmail.com cat << EOF > /etc/usr/local/ssmtp root:root@erikw.me:mail.erikw.me EOF # But to make other programs work, like default configuratin for ddclient that sends email to the local root users mailbox, also cat << EOF >> /etc/aliases # Forward local user root's email to my web host's email server, via ssmtp. root: root@erikw.me EOF # Now try service ssmtp start echo "hej" | mail -v -s "test ssmtp to root" root echo "hej" | mail -v -s "test ssmtp to root@erikw.me" root@erikw.me echo "hej" | mail -v -s "test ssmtp to erikw" erikw # Trace sent mail with tail -f /var/log/maillog # enable further debugging in /usr/local/etc/ssmtp/ssmtp.conf with Debug=YES # Finally reboot for changes to really take effect. # The FreeBSD system cron is using sendmail, and changing seems not possible without recompiling it (no port available). Installing another cron seems cumbersome. # Solution: use my wrapper /usr/local/sbin/cron_mail around all commands executed in cron. # } # restic automatic backup { # Install restic: portsnap auto cd /usr/ports/sysutils/restic make install clean # Install bash, needed for my restic_backup.sh script. pkg update pkg install bash # Also, make bash default root shell. chsh -s /usr/local/bin/bash # Enable clear-screen shortcut cat << EOF > ~/.inputrc Control-L: clear-screen set keymap vi-command Control-L: clear-screen # Ignore case in completion set completion-ignore-case On EOF # FUSE is needed for restic-mount: cat << EOF >> /boot/loader.conf # FUSE kernel module is needed for restic $(kldload fuse) fuse_load="YES" EOF # Instead of the systemd service and timer, set up with cron # NOTE could use chronic to only get email when it fails, but I like to see how much was backed up. http://habilis.net/cronic/ cat << EOF > /etc/cron.d/restic SHELL=/bin/sh PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin # Order of crontab fields # minute hour mday month wday command # Reference: https://www.freebsd.org/doc/handbook/configtuning-cron.html # Reference: crontab(5). @midnight root /usr/local/sbin/restic_backup.sh @monthly root /usr/local/sbin/restic_check.sh EOF # Email seems to be sent automatically to root@erikw.me. Envvar MAILTO could also be set in the crontab file. # } # package dump { # Log installed packes by using pkg-query: cat << EOF >/usr/local/sbin/dump_installed_packages.sh #!/usr/bin/env sh log_dir=/var/local/log/packages_dump date=$(date "+%Y-%m-%d-%H%M%S") log_file_base="${log_dir}/${date}" test -d $log_dir || mkdir -p $log_dir # all packages pkg query "%a %R %v %o %n %c" | sort > "${log_file_base}_all.txt" # pkg packages pkg query "%a %R %o %n" | grep "^0" | grep FreeBSD | cut -d' ' -f3 | sort > "${log_file_base}_pkg.txt" # ports packages pkg query "%a %R %o %n" | grep "^0" | grep -v FreeBSD | cut -d' ' -f3 | sort > "${log_file_base}_ports.txt" EOF # Schedule this to be run every day. cat << EOF > /etc/cron.d/dump_packages SHELL=/bin/sh PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin # Order of crontab fields # minute hour mday month wday command # Reference: https://www.freebsd.org/doc/handbook/configtuning-cron.html # Reference: crontab(5). @daily root /usr/local/sbin/dump_installed_packages.sh EOF # } # Samba network share { # I skipped sharing my cups printer, as it's already discoverable on the network itself. https://wiki.archlinux.org/index.php/CUPS/Printer_sharing#Sharing_via_Samba portmaster net/samba48 # Start daemons cat << EOF > /usr/local/etc/rc.conf.d/samba_server samba_server_enable="YES" nmbd_enable="NO" samba_enable="NO" EOF # Create user for the share with these settings: #Username : pcguest #Password : #Full Name : #Uid : 1002 #Class : #Groups : pcguest #Home : /home/pcguest #Home Mode : #Shell : /usr/sbin/nologin #Locked : no adduser pdbedit -a -u pcguest # No password. # Create datset with refquota # Source: https://docs.oracle.com/cd/E23823_01/html/819-5461/gazvb.html zfs create -o mountpoint=/var/samba zroot/samba zfs set refquota=100g zroot/samba zfs list zroot/samba # Config a public share. mkdir /var/samba/pub chmod 755 /var/samba/pub chown -R pcguest:pcguest /var/samba/pub cat << EOF > /usr/local/etc/smb4.conf # See smb.conf(5) [global] workgroup = WORKGROUP server string = Samba Server %v netbios name = server-name security = user map to guest = Bad User dns proxy = no guest account = pcguest # Make this server show up in Windows' network discovery. # Reference: https://ubuntuforums.org/showthread.php?t=1891378 # Reference: https://askubuntu.com/questions/661611/make-samba-share-visible-in-windows-network wins support = yes local master = yes preferred master = yes name resolve order = bcast host # Non-working attempts to t achieve password-less logon in Windows 10. # Setting this to yes allows ntlmv1, which allows for entering public shares without having to enter something bogus in the login dialog. # NOTE does not seem to work #ntlm auth = yes # Reference: https://serverfault.com/questions/211128/can-samba-security-user-be-used-for-guest-share-without-windows-login-prompt # Not working #username map = /etc/samba/smbusers [homes] comment = Home Directories browseable = yes writable = yes readonly = no create mask = 0755 directory mask = 0755 [pub] comment = Public files path = /var/samba/pub/ public = yes browsable = yes read only = no writable = yes create mask = 0644 directory mask = 0755 force user = pcguest force group = pcguest EOF # Make a symlink. Some programs, like webmin, expects the config to be at this path. ln -s /usr/local/etc/smb4.conf /usr/local/etc/smb.conf # Open ports in firewall. # Reference: https://forums.freebsd.org/threads/what-are-correct-ipfw-settings-for-samba.19675/ cat << EOF >> /etc/rc.conf.d/ipfw # Open samba services #firewall_myservices="${firewall_myservices} hosts2-ns/tcp netbios-ns/tcp netbios-dgm/tcp netbios-ssn/tcp microsoft-ds/tcp" # These names does not work for some reason, so let's use port numbers instead. firewall_myservices="${firewall_myservices} 81 137 138 139 445" EOF # Enable home folder for ~erikw to be mounted. pdbedit -a -u erikw # # Test! # NOTE After adding server-name as network local DNS server, and possibly after setting up DHCP server, it seems like \\server-name name is tried to be resolved as a DNS name instead of samba (WINS?) name. Thus the FQDN is needed \\server-name. or \\srv. service samba_server start # From another Linux computer, scan for samba hosts: smbtree -N # browse what shares are available by smbclient -L server-name -U% # Then connect to a share like smbclient -N -W WORKGROUP '\\server-name\pub' # Or mount it as a filesystem mount -t cifs //server-name/pub /mnt/server-name_pub/ -o password=whatever,workgroup=workgroup # Create a README for legal reasons: cat << EOF > /var/samba/README.bak Hello, these is a public share on this network, served by server-name. You can put your files here temprarily, but do not expect them to persists as everyone has write access here. The server owner take no responsibility for the content put here - you are completely responsibly yourself for all files you put here. EOF cp /var/samba/README.bak /var/samba/pub/README # Set stickbit so that ownly the owner of the file can remove it. This seems to prevent deletion from some smb clients, but not all. chmod +t /var/samba/pub chmod 444 /var/samba/pub/README chown root:wheel /var/samba/pub/README # Make local user ~erikw able to create files in share # Reboot required to group change to take effect. pw groupmod pcguest -M erikw pw group show pcguest # Windows notes. # To list connected smb shares in windows: net use # To disconnect to use, use net use \\server-name\pub /delete # List computers on network net view # List shares net view \\server-name # Mount a share net use x: \\server-name\pub # Tell windows to always use blank password and username for share net use \\server-name\pub "" /user:"" # } # FTP server { # Reference: https://www.freebsd.org/doc/handbook/network-ftp.html # Disallow pcugest to do FTP. echo pcguest >> /etc/ftpusers # Set greeting and motd echo "This is the FTP service by Server, authenticate and behave." >> /etc/ftpwelcome echo "All actions are logged. You are complete responsibly yourself for all files you upload - we take no legal responsibilities." > /etc/ftpmotd # Enable & start the service cat << EOF > /etc/rc.conf.d/ftpd ftpd_enable="YES" EOF service ftpd start # Try the connection ftp erikw@localhost # Enable FTP in the firewall. # Active cat << EOF >> /etc/rc.conf.d/ipfw # FTP - Active mode # ftpd: this enabled active mode, where server opens the data channel to the client. Use this so we don't have to open incomming trafic on the high level ports for passive. mode. # Reference: https://stackoverflow.com/questions/1699145/what-is-the-difference-between-active-and-passive-ftp # Reference: https://forums.freebsd.org/threads/ftp-ipfw-passive-rules.34618/ firewall_myservices="${firewall_myservices} ftp" EOF service ipfw restart # Passive cat << EOF >> /etc/ipfw.rules # FTP - Passive mode # Reference: https://forums.freebsd.org/threads/ftp-ipfw-passive-rules.34618/ # Disabled! because I don't want to expose these ports. Using another FTP server like pure-ftpd would allow to define what range to use. #$cmd 120 allow all from any 1024-65535 to any 1024-65535 in setup keep-state EOF # Don't serve public FTP - dangerous! # But create a new user for an authenticated FTP # Note - some client will requuire to enter a username, use "ftp" or "anonymous" useradd #Username : ftp #Password : #Full Name : #Uid : 1003 #Class : #Groups : ftp #Home : /home/ftp #Home Mode : #Shell : /usr/sbin/nologin #Locked : no echo "Here may be public files." > /home/ftp/README chgrp wheel /home/ftp/README chmod 444 /home/ftp/README # Prevent creation of file in this directory. # See ftpd(8) chmod 555 /home/ftp chgrp wheel /home/ftp # Also create another jailed ftp user that I can use to upload files to myself (feels securer than using my real user ~erikw). adduser #Username : ftp.dump #Password : ***** #Full Name : #Uid : 1004 #Class : #Groups : ftp.dump #Home : /home/ftp.dump #Home Mode : #Shell : /usr/sbin/nologin #Locked : no # Chroot the user to only see the home directory echo ftp.dump >> /etc/ftpchroot cat << EOF > /etc/shells # ftpd(8) requres a valid login shell to be set for the user, but I don't want to allow login for the ftp user # Making nologin a valid shell works around this. /usr/sbin/nologin EOF # WARNING FTP is is plain text, so someone could pick up the password and start uploading shit here. Don't use this user uneless needed. Prefer scp! # Thus, disable for now: echo ftp.dump >>/etc/ftpusers # For the same reason, disallow ~erikw: echo erikw >>/etc/ftpusers # Since I’m not activly using this, and if I would, I would not use the anonymous passwordless user ftp. Thus remove it for now as it only exposes security risk: rmuser ftp # Update /usr/local/etc/rkhunter.conf and remove it form PASSWORDLESS_ACCOUNTS variable. # } # Unbound DNS resolver/cache { # First, make sure that server-name is configured to a static IP in the network, here 192.168.178.55. cat << EOF > /usr/local/etc/unbound/unbound.conf # See unbound.conf(5) server: # Server # Where to log. Logfile is relative to the chroot or directory set. # For FreeBSD's ports unbound; this is /usr/local/etc/unbound. logfile: "unbound.log" # Level of verbosity to log with. verbosity: 1 # Log validation errors. val-log-level: 2 # Connection # Listen on all interfaces, answer queries from the local subnet. interface: 0.0.0.0 interface: ::0 access-control: 192.168.178.0/24 allow # Get network part of IPv6 with ifconfig + https://networklessons.com/ipv6/how-to-find-ipv6-prefix/ access-control: 2003:8c:4d00:4c00::/64 allow # TCP needed for Enable DNS over TLS do-tcp: yes # Performance # Send less data on the network. minimal-responses: yes # Fetch popular cache elements before they expire again. prefetch: yes # Lower latency on requests by fetching public DNSKEYs earlier. prefetch-key: yes # Speed optimization? rrset-roundrobin: yes # Number of threads to spin up. Set this to number of cores in computer. # $ sysctl -a | grep -i core num-threads: 2 # Faster UDP with multithreading (might only on Linux, but fails silently if not). so-reuseport: yes # Cache sizes. # See https://www.unbound.net/documentation/howto_optimise.html # Increase message cache size from default 4m. msg-cache-size: 64m # Cache slab memory allocations. Set this to number of cores in computer. msg-cache-slabs: 2 # Increase RRSet (resource records) cache from default 4m. # Recommendation is rrset-cache = 2 * msg-cache rrset-cache-size: 128m # Number slabs allocations. Set this to number of cores in computer. rrset-cache-slabs: 2 # message vs rrset cache? # "The message cache contains the 'message formats', with DNS rcodes and validation status (like the header part of 'dig' output). The rrset cache contains the RR data (like the data part of 'dig' output)." # Reference: https://www.unbound.net/pipermail/unbound-users/2010-September/001360.html # Security # Send NXDOMAIN for subdomain of known NXDOMAIN. harden-below-nxdomain: yes # Send extra query to enfoce DNSSEC validation on nameserver. Degrated performance. # harden-referral-path: yes # No downgrate when multiple allowed algorithms. harden-algo-downgrade: no # Randomly change case of requested name, and verify response, to foil spoofing attempts. # Using this experimental feature seems to be troublesome in practice. #use-caps-for-id: yes # Why give away information that can be used against us? Disable id.serve & hostname.bind queries. hide-identity: yes # Refuse version.server & version.bind queries. hide-version: yes # DNSSEC # Reference: https://www.unbound.net/documentation/howto_anchor.html # Reference: https://info.menandmice.com/blog/bid/40298/End-to-End-DNSSEC-using-Unbound-DNS # How test it works: # * https://dnssec.vs.uni-due.de/ # * http://conn.internet.nl/connection/ # * https://www.rootcanary.org/test.html # * visiting http://www.rhybar.cz/ should print a log messages: # * $(dig com. SOA +dnssec | grep ad), Authenticated Data flag should be present. # info: validation failure : no keys.... # Run this to set up root.key by fetching trusted keys. # $ unbound-anchor auto-trust-anchor-file: "root.key" # Local domain aliases. # Reference: https://www.bentasker.co.uk/documentation/linux/279-unbound-adding-custom-dns-records local-data: "srv. A 192.168.178.55" local-data: "server-name. A 192.168.178.55" # When using server-name as DNS server in the network, this alias is lost to my router. Let's keep it! local-data: "fritz.box A 192.168.178.1" # Enable remote control interface, that can be accessed e.g. with unbound-control(8). # print statistics without resetting them # $ unbound-control stats_noreset # Dump cache to stdout # $ unbound-control dump_cache # flush cache and reload configuration # $ unbound-control reload # To enable this, run this command to generate server keys. # $ unbound-control-setup # Reference: https://wiki.archlinux.org/index.php/Unbound#Setting_up_unbound-control remote-control: control-enable: yes control-interface: 127.0.0.1 #control-use-cert: no server-key-file: "/usr/local/etc/unbound/unbound_server.key" server-cert-file: "/usr/local/etc/unbound/unbound_server.pem" control-key-file: "/usr/local/etc/unbound/unbound_control.key" control-cert-file: "/usr/local/etc/unbound/unbound_control.pem" # Forward all requests to external (to the network) resolver. # Reference: https://wiki.archlinux.org/index.php/Unbound#Forward_all_remaining_requests forward-zone: name: "." # Cloudflare's DNS servers #forward-addr: 1.1.1.1 #forward-addr: 1.0.0.1 #forward-addr: 2606:4700:4700::1111 #forward-addr:2606:4700:4700::1001 # DNS over TLS. Requires unbound >= 1.6.6 # Reference: http://blog.thestateofme.com/2018/04/04/howto-secure-your-dns-with-a-raspberry-pi-unbound-and-cloudflare-1-1-1-1/ # Connection can be tested with kdig # $ kdig -d @1.1.1.1 +tls-ca +tls-host=cloudflare-dns.com example.com # Reference: https://developers.cloudflare.com/1.1.1.1/dns-over-tls/ forward-addr: 1.1.1.1@853 forward-addr: 1.0.0.1@853 forward-ssl-upstream: yes # Google's DNS servers #forward-addr: 8.8.8.8 #forward-addr: 8.8.4.4 #forward-addr: 2001:4860:4860::8888 #forward-addr: 2001:4860:4860::8844 EOF unbound-checkconf cat << EOF >> /etc/rc.conf.d/ipfw # DNS local caching firewall_myservices="${firewall_myservices} domain/tcp domain/udp" EOF service ipfw restart # Update the generated files in /etc/unbound with resolvconf -i resolvconf -a em0.dhcp < /etc/resolv.conf # Start service cat << EOF > /usr/local/etc/rc.conf.d/unbound unbound_enable="YES" EOF service unbound restart # Verify that we listen on port 53 for ipv4 and ipv6. sockstat | grep :53 # Test the local alias drill srv # From Arch Linux: sudo nscd -K # clear dns cache nslookup srv. "" # Generate keys for local control interface: unbound-control-setup # Fetch DNSSec keys unbound-anchor # The trusted keys must be updated from time to time cat << EOF >/etc/cron.d/unbound-anchor SHELL=/bin/sh PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin/:/usr/local/sbin/ # Order of crontab fields # minute hour mday month wday command # Reference: https://www.freebsd.org/doc/handbook/configtuning-cron.html # Reference: crontab(5). # Fetch trusted DNSSEC keys for unbound. @weekly root cron_mail unbound-anchor EOF # NOTE getting Android to resolv non FQDN hostnames from local domanserver does not work at the moment. # Reference: https://stackoverflow.com/a/8651252/265508 # HOWEVER after setting up isc-dhcpd with Search Domain, it started to work in Android to resolv my local A-records! # To support DNS over TLS, the forward-ssl-upstream setting is needed, which is found in unbound >= 1.6.6. Thus we need to install unbound from ports. portmaster dns/unbound cp /etc/unbound/conf.d/custom.conf /usr/local/etc/unbound/unbound.conf cat << EOF > /etc/rc.conf.d/local_unbound local_unbound_enable="NO" EOF cat << EOF > /usr/local/etc/rc.conf.d/unbound unbound_enable="YES" EOF # } # Webmin { # Reference: https://doxfer.webmin.com/Webmin/Installation#pkg_.28FreeBSD.29 portmaster sysutils/webmin cat << EOF > /usr/local/etc/rc.conf.d/webmin webmin_enable="YES" EOF # Configure webmin: /usr/local/lib/webmin/setup.sh # Use default settings for all, and # ssl: yes # Reference: http://www.webmin.com/ssl.html service webmin start w3m https://localhost:10000 cat << EOF >> /etc/rc.conf.d/ipfw # Webmin firewall_myservices="${firewall_myservices} 10000/tcp" EOF # Make port forward in router: router_config.txt service ipfw restart # Now, from another computer, visit https://srv:1000 # } # Git gitolite server { # Reference: http://gitolite.com/gitolite/fool_proof_setup/ portmaster devel/gitolite # Chose "Create git user" in the config. # Config gitolite su - git gitolite setup -pk /tmp/erikw\@laptop.pub gitolite list-users gitolite list-repos exit # to root shell # Now verify that we can use the git server from another computer: # laptop$ git ls-remote git@srv:gitolite-admin # laptop$ ghq get git@srv:gitolite-admin # laptop$ ghq look git@srv:gitolite-admin # Administraton. # The repo clones in /usr/local/git/repositories are bare, so it needs to be cloned to somewhere. # Make it convenient by cloning on the same machine. # NOTE this happens to work as the username ~erikw on server-name is the same for which I set up the gitloite-admin in the beginning. See conf/gitolite.conf. su - erikw ssh-keygen.sh # create keys for 'localhost' exit su - erikw ghq get git@localhost:gitolite-admin ghq get git@localhost:testing exit mkdir /root/src cd /root/src #* Add a new user: # First obtain their public ssh key, then add it to the admin repo ## Create a new repo: ## edit conf/gitolite.conf and add the new repo, then commit, push and clone the new repo. ## Delete a repo: # First remove it from conf/gitolit.conf, then remove the repo dir. rm -r /usr/local/git/repositories/gitrepotoremove.git ## Rename a repo: # Reference: http://gitolite.com/gitolite/basic-admin/#removingrenaming-a-repo # Edit name in conf/gitolite.conf, then rename it on disk. Move on disk first!. Reference: https://stackoverflow.com/a/5164868/265508 mv /usr/local/git/repositories/old_name /usr/local/git/repositories/new_name # Logs are locate in: /usr/local/git/.gitolite/logs # } # taskd { # Reference: https://gitpitch.com/GothenburgBitFactory/taskserver-setup # Reference: https://www.vultr.com/docs/install-taskserver-taskd-on-freebsd-11 portmaster deskutils/taskd cat << EOF > /etc/rc.conf.d/taskd taskd_enable="YES" EOF # Generate server self-signed root CA & cert, server key & cert and server revocation list cd /usr/local/share/taskd/ cat << EOF > vars BITS=4096 EXPIRATION_DAYS=365 ORGANIZATION="server-name" CN=localhost COUNTRY=DE STATE="Berlin" LOCALITY="Berlin" EOF ./generate.ca ./generate.crl ./generate.server chown taskd:taskd ca.cert.pem ca.key.pem server.cert.pem server.crl.pem server.key.pem chmod 400 ca.cert.pem ca.key.pem server.cert.pem server.crl.pem server.key.pem export TASKDDATA=/var/db/taskd cd $TASKDDATA ln -s /usr/local/share/taskd/ca.cert.pem . ln -s /usr/local/share/taskd/server.cert.pem . ln -s /usr/local/share/taskd/server.crl.pem . ln -s /usr/local/share/taskd/server.key.pem . taskd config server 0.0.0.0:53589 taskd config ca.cert $TASKDDATA/ca.cert.pem taskd config server.cert $TASKDDATA/server.cert.pem taskd config server.crl $TASKDDATA/server.crl.pem taskd config server.key $TASKDDATA/server.key.pem taskd config log /var/log/taskd.log taskd config pid.file /var/run/taskd.pid touch /var/log/taskd.log chown -R taskd:taskd /var/db/taskd/ /var/log/taskd.log # Missing instructions on vultr.com cat << EOF >> /root/profile # Needed so taskd(1) know where the data is. export TASKDDATA=/var/db/taskd EOF touch /var/run/taskd.pid chown taskd:taskd /var/run/taskd.pid cat << EOF >> /etc/rc.conf.d/ipfw # taskd firewall_myservices="${firewall_myservices} 53589/tcp" EOF service ipfw restart service taskd start # Create new user # Reference: https://taskwarrior.org/docs/taskserver/user.html taskd add org public taskd add user public erikw # Missing from instructions; the user running taskd(1) must own these files: chown -R taskd:taskd /var/db/taskd/orgs cd /usr/local/share/taskd/ ./generate.client erikw # Take note of the client key. tar pvczf certs_erikw.tar.gz ca.cert.pem erikw.* # Copy the tarball to a client machine and set up task client # laptop$ mkdir -p ~/.task/certs/server-name # laptop$ cd ~/.task/certs/server-name # laptop$ scp srv:/usr/local/share/taskd/certs_erikw.tar.gz . # laptop$ tar xvzf certs_erikw.tar.gz # laptop$ cat << EOF >> ~/.taskrc # taskd server: server-name #taskd.trust=ignore hostname #taskd.certificate=~/.task/certs/server-name/erikw.cert.pem #taskd.key=~/.task/certs/server-name/erikw.key.pem #taskd.ca=~/.task/certs/server-name/ca.cert.pem #taskd.server=server-name.erikw.me:53589 #taskd.credentials=public/erikw/.... # EOF # laptop$ task diagnostic # Make sure that in the output CA, Cert, and Key are all "readable". # laptop$ task sync init # laptop$ task sync # # If it does not work, debug like # server-name$ taskd server --debug --debug.tls=2 # laptop$ task rc.debug=1 rc.debug.tls=2 sync # Do the same on server-name portmaster deskutils/taskwarrior su - erikw mkdir -p ~/.task/certs/server-name cd ~/.task/certs/server-name cp /usr/local/share/taskd/certs_erikw.tar.gz . tar xvzf certs_erikw.tar.gz # Edit ~/.taskrc to set FreeBSD paths. task sync init exit # to root sh # } # DHCP { # Reference; https://www.freebsd.org/doc/handbook/network-dhcp.html portmaster net/isc-dhcp44-server rm /usr/local/etc/dhcpd6.conf # No IPv6 support needed in local net. cat << EOF >/usr/local/etc/dhcpd.conf # Default search domain that will be provided to clients. If a host is called X, it will be searched under x.dom.tld. # Setting this, enables Android to resolv my local DNS A-records served by unbound. However it means that the FQDN must be used both at Android and Linux, e.g. "srv.", otherwise srv.erikw.me is searched. # Seems like disabling domain-name setting, it will works using "srv." from Android, and "srv" from Linux devices. #option domain-name "erikw.me"; # Let ourselves be the DNS server, and add external fallbacks. #option domain-name-servers 192.168.178.55, 1.1.1.1, 1.0.0.1; option domain-name-servers 10.0.0.2, 1.1.1.1, 1.0.0.1; # Subnet mask that will be given to clients #option subnet-mask 255.0.0.0; # Lease in seconds. # Make it really long, so that clients in my home network can survinve for a while if server-name dies. default-lease-time 10080; max-lease-time 30240; # (Temporary) Which logging facility to use for syslog. Need to enable this in /etc/syslog.conf too. log-facility local1; # This is the offical DHCp server for the local network. authoritative; # Local network definition. # Let's use a big block! https://en.wikipedia.org/wiki/Private_network subnet 10.0.0.0 netmask 255.0.0.0 { # Allocate blocks like this: # 10.0.0.*/8: Static configurations for my devices. # 10.0.1.*/8: Static configurations for other person's devices # 10.0.2.*/8: Dynamic range for DHCP. range 10.0.2.1 10.0.2.254; option routers 10.0.0.1; } # Static configurations. ## My devices host laptop-eth { hardware ethernet ...; fixed-address 10.0.0.3; } EOF # Disable DHCP server in router: router_config.txt # Open ports in firwall. cat << EOF >> /etc/rc.conf.d/ipfw # isc-dhcpd firewall_myservices="${firewall_myservices} bootps/udp" EOF service ipfw restart # Set server-name to be statically configured. cat << EOF > /etc/rc.conf.d/network # DHCP #ifconfig_em0="DHCP" #ifconfig_em0_ipv6="inet6 accept_rtadv" # Static # See defaultrouter in /etc/rc.conf.d/routing # server-name as DHCP server. ifconfig_em0="inet 10.0.0.2 netmask 255.0.0.0" # Fritz.box as DHCP server. #ifconfig_em0="inet 192.168.178.55 netmask 255.255.255.0" EOF cat << EOF > /etc/rc.conf.d/routing # Default destination for static IP configuration. # Fritz.box as DHCP server. #defaultrouter="192.168.178.1" # server-name defaultrouter="10.0.0.1" EOF # Set up some logging tail -f /var/log/messages tail -f /var/db/dhcpd/dhcpd.leases cat << EOF > /etc/syslog.conf # Temporary isc-dhcpd debug logging. # To activate: # $ touch /var/log/dhcpd.log # $ chown dhpcd /var/log/dhcpd.log # $ service syslogd restart # $ service isc-dhcpd restart local1.* EOF portmaster net/tcpdump tcpdump -n -i em0 port bootps or port bootpc # Now test it! service isc-dhcpd onestart cat << EOF > /etc/rc.conf.d/dhcpd dhcpd_enable="YES" dhcpd_flags="-q" dhcpd_ifaces="em0" EOF service isc-dhcpd start # } # ZNC { # A irc bouncer, see https://en.m.wikipedia.org/wiki/BNC_%28software%29 # Reference: https://wiki.znc.in/Installation#FreeBSD portmaster irc/znc su -m znc -c 'znc -d /usr/local/etc/znc --makeconf' #[ .. ] Checking for list of available modules... #[ ** ] #[ ** ] -- Global settings -- #[ ** ] #[ ?? ] Listen on port (1025 to 65534): 6677 #[ ?? ] Listen using SSL (yes/no) [no]: yes #[ ?? ] Listen using both IPv4 and IPv6 (yes/no) [yes]: #[ .. ] Verifying the listener... #[ ** ] Unable to locate pem file: [/usr/local/etc/znc/znc.pem], creating it #[ .. ] Writing Pem file [/usr/local/etc/znc/znc.pem]... #[ ** ] Enabled global modules [webadmin] #[ ** ] #[ ** ] -- Admin user settings -- #[ ** ] #[ ?? ] Username (alphanumeric): admin #[ ?? ] Enter password: #[ ?? ] Confirm password: #[ ?? ] Nick [admin]: #[ ?? ] Alternate nick [admin_]: #[ ?? ] Ident [admin]: #[ ?? ] Real name (optional): #[ ?? ] Bind host (optional): #[ ** ] Enabled user modules [chansaver, controlpanel] #[ ** ] #[ ?? ] Set up a network? (yes/no) [yes]: no #[ ** ] #[ .. ] Writing config [/usr/local/etc/znc/configs/znc.conf]... #[ ** ] #[ ** ] To connect to this ZNC you need to connect to it as your IRC server #[ ** ] using the port that you supplied. You have to supply your login info #[ ** ] as the IRC server password like this: user/network:pass. #[ ** ] #[ ** ] Try something like this in your IRC client... #[ ** ] /server +6677 admin: #[ ** ] #[ ** ] To manage settings, users and networks, point your web browser to #[ ** ] https://:6677/ #[ ** ] #[ ?? ] Launch ZNC now? (yes/no) [yes]: yes #[ .. ] Opening config [/usr/local/etc/znc/configs/znc.conf]... #[ .. ] Loading global module [webadmin]... #[ .. ] Binding to port [+6677]... #[ ** ] Loading user [admin] #[ >> ] [/usr/local/lib/znc/simple_away.so] #[ .. ] Adding server [chat.freenode.net +6697 ]... #[ .. ] Loading user module [chansaver]... #[ .. ] Loading user module [controlpanel]... #[ .. ] Forking into the background... #[ >> ] [pid: 58083] #[ ** ] ZNC 1.7.0 - https://znc.in # NOTE znc.conf needs 664 permissions, group must be able to write, othwrise znc won't start. # Autostart cat << EOF > /usr/local/etc/rc.conf.d/znc znc_enable="YES" EOF # Open ports in firwall. cat << EOF >> /etc/rc.conf.d/ipfw # znc firewall_myservices="${firewall_myservices} 6677/tcp" EOF service ipfw restart # Set up port forwarding in router to 6677. # From another computer, visit https://srv:6677/ # webadmin config ## Global Settings # * Maximum Buffer Size: 1024 # * Global modules to enable: adminlog, fail2ban, lastseen, log, block_motd ## Manage Users # * [Add] # - username: erikw # - nickname: erikw # - alt nickname: erikw1 # - ident: erikw # - real name: Erik Westrup # Modules to enable: chansaver, controlpanel ### Channels # * Buffer size: 1024 ### ZNC Behaviour # * Timzeon: Europe/Berlin # * Max IRC Networks Number: 10 # Connect with an IRC client to # IP: server-name.erikw.me # port: 6677 # SSL: yes # username: erikw # password: ... # # E.g. with Irssi: # * First install the dispatch.pl script so we can send unknonw commands (/znc) to the server: https://wiki.znc.in/Irssi ## Freenode # irssi> /connect zncRemoteFreenode # irssi> /znc AddNetwork freenode # irssi> /znc JumpNetwork freenode # Prefix the port name with '+' to enable SSL connection. # irssi> /znc AddServer chat.freenode.net +7000 # irssi> /znc connect ### Set up nickname identiy. Reference: https://wiki.znc.in/Perform#Bitlbee # First go to the web UI and enable the perform module for this network (not for the user, as that sends those commands to all networks) # irssi> /msg *perform add PRIVMSG NickServ@services. :IDENTIFY ..... # irssi> /msg *perform list # irssi> /msg *perform execute # ### Join channels # irssi> /join #vim # irssi> /join ##archlinux ## Quakenet # irssi> /znc AddNetwork quakenet # irssi> /znc JumpNetwork quakenet # irssi> /znc AddServer irc.quakenet.org 6667 # irssi> /znc connect # irssi> /msg *perform add PRIVMSG Q@CServe.quakenet.org :AUTH erikw ..... # irssi> /msg *perform execute ## EFNet # irssi> /znc AddNetwork efnet # irssi> /znc JumpNetwork efnet # irssi> /znc AddServer efnet.portlane.se 6667 # irssi> /znc connect # irssi> /join #blausoffan # irssi> /join #dwww # irssi> /msg *perform add PRIVMSG Q@CServe.quakenet.org :auth erikwestrup ..... # Now connect to a network by changing the irssi config to connect with # username: erikw/freenode # To change global settings: # irssi> /msg *controlpanel help # Enable fail2ban when all configs are stable. # Push notifications: znc-push { # NOTE I scripted this to bin/znc_push_recompile.sh su - erikw ghq get git@github.com:jreese/znc-push.git ^get^look znc-buildmod push.cpp exit # to root sh cp /home/erikw/src/github.com/jreese/znc-push/push.so /usr/local/lib/znc/ # Load module: # irssi> /msg *status loadmod --type=user push # Also enable it in the webadmin: https://srv:6677/mods/global/webadmin/edituser?user=erikw # Set up push service: PushBullet # The only one I was able to get to work or that is free. # 1) Get my access token at: https://www.pushbullet.com/#settings/account # 2) Find my device ID: # $ curl --header 'Access-Token: .....' https://api.pushbullet.com/v2/devices | jq ". # 3) Test to send notification to all devices: $ curl --header 'Access-Token: .....' --header 'Content-Type: application/json' --data-binary '{"body":"Space Elevator, Mars Hyperloop, Space Model S (Model Space?)","title":"Space Travel Ideas","type":"note"}' --request POST https://api.pushbullet.com/v2/pushes | jq "." # 4) Now configure znc-push # irssi> /msg *push set service pushbullet # irssi> /msg *push set secret ... # irssi> /msg *push set target .... # irssi> /msg *push get # 5) Now try by connecting with another irc client to the same network I'm in, and start a private chat. Then a PushBullet notification should appear. # } # Bitlbee { portmaster irc/bitlbee # NOTE I skipped this in the end, as the list of protocols supported is not long, and the supported ones are 3rd party that I epxect to break often - not something I want to commit to maintain. # Supported protocols: https://wiki.bitlbee.org/ # } # NOTE on 2019-11-18 I disabled ZNC like this: # * Remove /etc/pf.conf rules # $ service znc stop # edit znc_enable="NO" in /usr/local/etc/rc.conf.d/znc # } # iodine IP-over-DNS tunneling { # Reference: https://code.kryo.se/iodine/README.html portmaster net/iodine cat << EOF > /usr/local/etc/rc.conf.d/iodined iodined_enable="YES" # Tunnel password iodined_password="" # Tunnel domain. "i" as in "iodine". iodined_domain="i.erikw.me" EOF # Test connection ## server # Switch over local unbound server to listen on port 9953, and tell iodine to forward all non-tunneled traffic to the local DNS server patch << EOF --- /usr/local/unbound/unbound.conf - port: 53 + port: 9953 EOF service unbound restart iodined -b 9953 -f 172.16.0.0 test.com # If server command has already executed once, it might fail the next time with: "route already in table" # then # $ ifconfig tun0 down # $ ifconfig tun0 destroy # $ netstat -r # $ route delete -net 172.16.0.0/27 172.16.0.0 ## Client: laptop # laptop$ pacman -S iodine # laptop$ iodine -f -r srv test.com # If this failes with: "iodine" open_tun: /dev/net/tun: No such device: No such device # then try reboot the computer. # # laptop$ ip addr show dev dns0 # laptop$ ping ping 172.16.0.1 # To get this to work for real, I must create a subdomain to erikw.me, say i.erikw.me, and delegate the nameserver to point to server-name. # Howevery Loopia does not allow NS delegration of subdomains. Options: # * Switch to another provider which does, e.g.: http://freedns.afraid.org/ # * Delegate all NS at erikw.me to my server, and run an authorative DNS server on server-name, like bind. # } # OpenVPN { # Reference: https://openvpn.net/index.php/open-source/documentation/howto.html # Reference: https://www.digitalocean.com/community/tutorials/how-to-set-up-an-openvpn-server-on-ubuntu-16-04 # Setting up "routed VPN" is probably enough, I don't need access to link layer that is given with "bridged VPN". portmaster security/openvpn cat << EOF > /usr/local/etc/rc.conf.d/openvpn openvpn_enable="YES" EOF ## Generate certs { # Generate the master Certificate Authority (CA) certificate & key # Reference: https://community.openvpn.net/openvpn/wiki/EasyRSA3-OpenVPN-Howto # But for the easyrsa part, use: https://community.openvpn.net/openvpn/wiki/EasyRSA3-OpenVPN-Howto # or https://github.com/OpenVPN/easy-rsa/blob/v3.0.5/README.quickstart.md # mkdir -p /usr/local/etc/easy-rsa/openvpn cp -r /usr/local/share/easy-rsa/* /usr/local/etc/easy-rsa/openvpn cd /usr/local/etc/easy-rsa/openvpn cat << EOF >>vars set_var EASYRSA_REQ_COUNTRY "DE" set_var EASYRSA_REQ_PROVINCE "Berlin" set_var EASYRSA_REQ_CITY "Berlin" set_var EASYRSA_REQ_ORG "server-name" set_var EASYRSA_REQ_EMAIL "user@gmail.com" set_var EASYRSA_REQ_OU "SRV" set_var EASYRSA_CERT_EXPIRE 730 EOF # NOTE the tutorials advice separating ca, server and client. But I'm all of those, so I'm reusing the same directoires. ## CA certs ./easyrsa.real init-pki ./easyrsa.real build-ca # Enter strong password, and common name = openvpn_ca_server-name ## Server certs ./easyrsa.real gen-req openvpn_server_server-name nopass ## Client certs ./easyrsa.real gen-req openvpn_client_laptop ./easyrsa.real gen-req openvpn_client_phone1 nopass # NOTE don't do nopass, then it's easy for someone to use my VPN by just stealing the cert! ./easyrsa.real gen-req openvpn_client_laptop2 ## Inspect requests ./easyrsa.real show-req server-name_openvpn_server ./easyrsa.real show-req openvpn_client_laptop ./easyrsa.real show-req openvpn_client_phone1 ./easyrsa.real show-req openvpn_client_laptop2 ## Sign requests ./easyrsa.real sign-req server openvpn_server_server-name ./easyrsa.real sign-req client openvpn_client_laptop ./easyrsa.real sign-req client openvpn_client_phone1 ./easyrsa.real sign-req client openvpn_client_laptop2 ./easyrsa.real show-cert openvpn_server_server-name ./easyrsa.real show-cert openvpn_client_laptop ./easyrsa.real show-cert openvpn_client_phone1 ./easyrsa.real show-cert openvpn_client_laptop2 ## Troubleshooting { # Client hangs on Auth step: check server logs for expired dates grep expired /var/log/openvpn.log # Control cert expiry date: ./easyrsa.real show-cert openvpn_client_phone1 | grep Validity -A 2 openssl x509 -text -noout -in pki/ca.crt | grep Validity -A 2 # If clients gets stuck on Auth and /var/log/openvpn.log show # VERIFY ERROR: depth=0, error=CRL has expired: CN=openvpn_client_laptop2 # then it means that the CRL file must be re-created: ./easyrsa.real gen-crl service openvpn restart ## } # Generate DH (diffie hellman) paramenters used for TLS handshake. ./easyrsa.real gen-dh # Generate tls-auth key /usr/local/sbin/openvpn --genkey --secret /usr/local/etc/openvpn/ta.key # Revoke a cert: ./easyrsa.real revoke openvpn_client_phone1 # Update revoke list ./easyrsa.real gen-crl # } ## Config for server { mkdir /usr/local/etc/openvpn cp /usr/local/share/examples/openvpn/sample-config-files/server.conf /usr/local/etc/openvpn/openvpn.conf patch << EOF --- /usr/local/etc/openvpn/openvpn.conf +ca /usr/local/etc/easy-rsa/openvpn/pki/ca.crt +cert /usr/local/etc/easy-rsa/openvpn/pki/issued/openvpn_server_server-name.crt +key /usr/local/etc/easy-rsa/openvpn/pki/private/openvpn_server_server-name.key +dh /usr/local/etc/easy-rsa/openvpn/pki/dh.pem +# I already use 10.0.0.0 for my DHCP net, so let's put VPN clients in a sublock of the 172.16.0.0/12 block -> 172.16.123.0/24 +server 172.16.123.0 255.255.255.0 +push "redirect-gateway def1 bypass-dhcp" +push "dhcp-option DNS 172.16.123.1" +push "dhcp-option DNS 1.1.1.1" +tls-auth /usr/local/etc/openvpn/ta.key 0 -;user nobody -;group nobody +user nobody +group nobody +# Verify client certificates against the CRL list: +crl-verify /usr/local/etc/easy-rsa/openvpn/pki/crl.pem EOF # Server must be set to forward IP traffic: sysctl net.inet.ip.forwarding sysctl net.inet.ip.forwarding=1 cat << EOF >> /etc/sysctl.conf # Allow IP forwarding for OpenVPN: sysctl net.inet.ip.forwarding=1 EOF # Routing must also be enabled cat << EOF >>/etc/rc.conf.d/routing # Enable routing functionality so OpenVPN incoming traffic can be routed out of the virtual network. # This also required $(sysctl net.inet.ip.forwarding=1). gateway_enable="YES" EOF service routing restart # Open on router for UDP on port 1194 # Open ports in firwall. cat << EOF >> /etc/rc.conf.d/ipfw # OpenVPN firewall_myservices="${firewall_myservices} 1194/udp" EOF service ipfw restart # Update unbound to allow DNS resolution from virtual network. patch << EOF --- /usr/local/unbound/unbound.conf +# Serve OpenVPN clients +interface: 172.16.123.1 +# Allow OpenVPN clients +access-control: 172.16.123.0/24 allow EOF service unbound restart # However unbound will fail at boot if tun0 is not available. # Fix 1) set rc boot order. # Advantage: we can disable unbound and things still work. # Disadvantage: openvpn file might be updated by package upgrade and we need to update it. patch << EOF --- /usr/local/etc/rc.d/openvpn +# BEFORE: unbound EOF # Fix 2) add a post command to openvpn to start unbound # Advantage: Only modify user conf files # Disadvantage: What if we disable unbound, then this command will fail. cat << EOF >>/usr/local/etc/rc.conf.d/openvpn openvpn_flags='--up "service unbound start"' EOF # I went with Fix 1. # Try server openvpn --config /usr/local/etc/openvpn/openvpn.conf # or service openvpn start # Check that new interface is up: ifconfig tun0 netstat -r # } ## Config for client { mkdir -p /usr/local/etc/openvpn/client-configs/files chmod 700 /usr/local/etc/openvpn/client-configs/files cp /usr/local/share/examples/openvpn/sample-config-files/client.conf /usr/local/etc/openvpn/client-configs/base.conf patch << EOF --- /usr/local/etc/openvpn/client-configs/base.conf -remote my-server-1 1194 +remote server-name.erikw.me 1194 -;user nobody -;group nobody +user nobody +group nobody -ca ca.crt -cert client.crt -key client.key +#ca ca.crt +#cert client.crt +#key client.key +# For Linux system who as update-resolv-conf to parse DHCP options from openvpn to update resolv.conf to set correct DNS server. +# NOTE If this generated file is for Linux, uncomment these lines below. +#script-security 2 +#up /etc/openvpn/update-resolv-conf +#down /etc/openvpn/update-resolv-conf EOF # Create client generation script. cat << EOF > /usr/local/etc/openvpn/client-configs/make_config.sh #!/usr/bin/env bash # First argument: Client identifier used when signing easyrsa certs. clientid="$1" PKI_DIR=/usr/local/etc/easy-rsa/openvpn/pki OUTPUT_DIR=/usr/local/etc/openvpn/client-configs/files BASE_CONFIG=/usr/local/etc/openvpn/client-configs/base.conf OPENVPN_DIR=/usr/local/etc/openvpn cat ${BASE_CONFIG} \ <(echo -e '') \ ${PKI_DIR}/ca.crt \ <(echo -e '\n') \ ${PKI_DIR}/issued/${clientid}.crt \ <(echo -e '\n') \ ${PKI_DIR}/private/${clientid}.key \ <(echo -e '\n') \ ${OPENVPN_DIR}/ta.key \ <(echo -e '') \ > ${OUTPUT_DIR}/${clientid}.ovpn EOF chmod 744 /usr/local/etc/openvpn/client-configs/make_config.sh # Generate client configs cd /usr/local/etc/openvpn/client-configs ./make_config.sh openvpn_client_laptop ./make_config.sh openvpn_client_phone1 ./make_config.sh openvpn_client_laptop2 ls -lrt files # Seems like a restart of the server is needed for it to know about new certs? service openvpn restart # Copy it over to main computer cp /files/openvpn_client_laptop2.ovpn /home/erikw/tmp # From main computer # laptop$ scp srv:tmp/openvpn_client_laptop2.ovpn ~/dropbox/tmp # On mobile phone: find file in Dropbox > Export > Save to device > open up in VPN client app > save passphrase to keychain> Save to device > open up in VPN client app > save passphrase to keychain > rename client config to be just "server-name" > set up autoconnect != on local home wifi ## laptop (Arch Linux) # scp the .ovpn file to laptop # Uncomment the linux lines in the config part of the file. # laptop$ sudo -i # Try it: # laptop$ openvpn --config openvpn_client_laptop.ovpn # laptop$ ping 172.16.123.1 # Install it: # laptop$ mv openvpn_client_laptop.ovpn /etc/openvpn/client/server-name.conf # Starting the service does not ask for passphrase though; https://unix.stackexchange.com/questions/444855/systemd-ask-password-prompt-not-displayed-for-vpn # laptop$ systemctl start openvpn-client@server-name # laptop$ cat << EOF >>/root/.bashrc # alias vpn_server-name_start='openvpn --config /etc/openvpn/client/server-name.conf' # EOF ## mac (macOS) # * For CLI, same as arch. # mac$ openvpn --config /etc/openvpn/client/server-name.conf # * For GUI e.g. Tunnelblick, als copy over the ta.key as the installer can't find the embeded one in the .ovpn file. ## phone1 # transfer the .ovpn file to phone1 some how (ssh -> laptop -> dropbox). After importing it, rename the profile to "server-name". # The good Android OpenVPN client is called "OpenVPN for Android": https://play.google.com/store/apps/details?id=de.blinkt.openvpn # Look at trafic from the server side with tcpdump -i tun0 icmp # } # } # Jails { # Reference: https://www.freebsd.org/doc/handbook/jails-build.html # Using traditinal jails seems very cumbersome -> use a helper: https://github.com/iocage/iocage # Reference: https://dan.langille.org/2015/03/07/getting-started-with-iocage-for-jails-on-freebsd/ # Reference: https://iocage.readthedocs.io/en/latest/ portmaster sysutils/iocage # Activate iocage on my zfs pool $(zpool list). iocage activate zroot # Fetch base system. iocaeg fetch # Chose 11.1-RELEASE. NOTE you must fest a version that is same or lower than host $(freebsd-version). zfs list | grep iocage mount -t fdescfs null /dev/fd # NOTE don't add this to fstab, it does not work to boot system them. # iocage complains about it though, -- how to fix? cat << EOF >>/etc/fstab # Recommended by iocage-create. See fdescfs(5). fdescfs /dev/fd fdescfs rw 0 0 EOF cat << EOF >/usr/local/etc/rc.conf.d/iocage iocage_enable="YES" EOF # Set up NAT for jails patch << EOF --- /etc/pf.conf +jails_subnet = "192.168.5.0/24" +# Jails NAT +nat on $ext_if inet from $jails_subnet to any -> $ext_if +pass inet proto icmp from $jails_subnet to any keep state EOF service pf reload # TODO add tl;dr instructions on how to update jail. # Reference: https://iocage.readthedocs.io/en/latest/advanced-use.html # } # Webserver in Jail { # Create in a jail with iocage with. # Let's allocate 192.168.5.0/24 for jails. iocage create -n webserver ip4_addr="em0|192.168.5.1/24" -r 12.0-RELEASE iocage list # Allow ping from within jail. iocage set allow_raw_sockets=1 webserver # Start this jail on boot iocage set boot=on webserver iocage get boot webserver iocage get -a webserver iocage set notes="Isolated websesrver" webserver # Must set DNS server explicily. Otherwise the hosts /etc/resolv.conf gets copied, which uses 127.0.0.0 as nameserver, which does not work from wihin the jail. iocage set resolver=10.0.0.2 webserver # Set up unbound to allow connection from jail. patch << EOF --- /usr/local/etc/unbound/unbound.conf +# Jail: webserver +# For some reasons it does not work to set the whole netblock 192.168.5.0/24. +access-control: 192.168.5.1 allow EOF service unbound restart # Start jail iocage start webserver # Execute command from root sh in jail. iocage exec webserver ifconfig # Jump in! iocage console webserver # Test networkg config ping google.com pkg install bash # so iocage chroot can work. pkg install nginx service start nginx exit # to rootsh # Must enable forwarding for port 80 from host OS to the jail in /etc/pf.conf, still including the NAT setup in pf.conf for jails above. # Reference: http://kbeezie.com/freebsd-jail-single-ip/ patch << EOF --- /etc/pf.conf # Open connection to specific ports to static Jail. IP_PUB="10.0.0.2" IP_JAIL="192.168.5.1" NET_JAIL="192.168.5.0/24" PORT_JAIL="80" scrub in all nat pass on $ext_if from $NET_JAIL to any -> $IP_PUB rdr pass on $ext_if proto tcp from any to $IP_PUB port $PORT_JAIL -> $IP_JAIL EOF # Also remove www from $tcp_ingress normal rule. service pf reload # Set up cloned IF cat << EOF >>/etc/rc.conf.d/network # Jails # Cloned interface to set up shared Jails IPs on cloned_interfaces="lo1" ipv4_addrs_lo1="192.168.5.1-9/24" EOF service netif restart # Now visit http://10.0.0.2:80 and it should go to jail's /usr/local/www/nginx/html.index! # NOTE I aborted setup here, as it would make my git hook deployment and certbot update more complicated. Still possible but KISS. # Uninstall iocage iocage stop webserver iocage destory --recursive webserver iocage clean --all zfs destroy zroot/iocage umount fdescfs pkg delete py36-iocage # } # Webserver (nginx) { portmaster www/nginx mkidr /usr/local/etc/rc.conf.d cat << EOF >/usr/local/etc/rc.conf.d/nginx nginx_enable="YES" EOF # Add "www" for TCP ingress in /etc/pf.conf # Forward port 80 in router. # Create our own web root. rm /usr/local/www/nginx mkdir /usr/local/www/nginx chmod 555 /usr/local/www/nginx # Now create a local git repo in gitolite and set up so it can install to the www directory. # see repo www.erikw.me # Set server name: patch << EOF --- /usr/local/etc/nginx/nginx.conf server { -server_name localhost; +server_name erikw.me www.erikw.me server-name.erikw.me; } +# Redirect (www|server-name).erikw.me -> erikw.me permanently. +server { + server_name www.erikw.me server-name.erikw.me; + return 301 + $scheme://erikw.me$request_uri; +} EOF service nginx start # Now visit erikw.me from another computer. # Automatic deploy from git-push { # Reference: https://demonastery.org/2012/09/a-hooking-system-for-gitolite/ # Prepare gitolite patch << EOF --- /usr/local/git/.gitolite.rc -GIT_CONFIG_KEYS => '', +GIT_CONFIG_KEYS => '.*', +LOCAL_CODE => "$ENV{HOME}/.gitolite/local-code", EOF gitolite setup # Both root and git user needs to have ssh keys so they can clone and update the nginx repo. ssh-keygen su - git ssh-keygen exit # to rootsh # Set up gitlote-admin su erikw cd ~/src/localhost/gitolite-amdin mkdir -p local-code/hooks/common/hooks.d # Create a hook executor. cat << EOF >local-code/hooks/common/post-receive #!/usr/bin/env bash # set -x run_hook () { echo -en "\e[1;33m$4..\e[00m " echo $1 $2 $3 | $GIT_DIR/hooks/hooks.d/$4 } echo -en "\e[1;33mRunning hooks..\e[00m " while read oldrev newrev refname; do if [ "$refname" = "refs/heads/master" ]; then hooks=$(git cat-file blob $newrev:.hooks 2>/dev/null) if [ -n "$hooks" ]; then # Repo-local hooks defined in .hooks. for hook in $hooks; do run_hook $oldrev $newrev $refname $hook done fi # Global hooks for this repo (ie. set in Gitolite config). hooks=$(git config --get hooks.run) [ -z "$hooks" ] && continue for hook in $hooks; do run_hook $oldrev $newrev $refname $hook done fi done echo -e "\e[1;32mDone.\e[00m" EOF chmod 744 local-code/hooks/common/post-receive # Create our nginx deploy script cat << EOF >local-code/hooks/common/hooks.d/nginx-deploy #!/usr/bin/env sh # Update existing clone of git repo. #set -x DESTDIR=/usr/local/www/nginx unset GIT_DIR # This is set, which must be unset to be able to work on another git repo. echo "Updating $DESTDIR to latest revision." cd $DESTDIR git fetch git rebase # www user must be able to read files. chmod -R o+r $DESTDIR # But not git... chmod -R o-r $DESTDIR/.git EOF chmod 744 local-code/hooks/common/hooks.d/nginx-deploy # add root@server-name.pub and git@server-name.pub to keydir # Set up which repo to use this new hook patch << EOF --- conf/gitolite.conf repo www.erikw.me - RW+ = erikw + RW+ = erikw root git + config hooks.run = nginx-deploy EOF git commit -m "Hooks for nginx deploy" git push origin master exit # to rootsh # Make the initial clone of the repo cd /usr/local/www git clone git@localhost:www.erikw.me nginx chown -R git:git nginx # Now when doing git-push in rep www.erikw.me, the hook will run! #} # HTTPS with TLS cert { # Reference: https://certbot.eff.org/lets-encrypt/freebsd-nginx portmaster security/py-certbot certbot certonly --webroot -w /usr/local/www/nginx -d erikw.me -d www.erikw.me -d server-name.erikw.me service nginx reload # If cert is expired in webbrowser, but $(certbot renew) says it's still good, then # $ certbot renew --force-renew # $ service nginx reload # Enable cronjob to execute $(certbot renew) cat << EOF >/etc/periodic.conf # Certbot cert renewal. # See /usr/local/etc/periodic/weekly/500.certbot weekly_certbot_enable=yes # Service to start and stop between updates. weekly_certbot_service=nginx EOF # Let's test it /usr/local/etc/periodic/weekly/500.certbot ## Wildcard cert { # NOTE I wanted to get a wildcard cert for *.erikw.me, so I can HTTP redirect subdomains with HTTPS. # Reference: https://blogs.msdn.microsoft.com/mihansen/2018/03/15/creating-wildcard-ssl-certificates-with-lets-encrypt/ # The down-side of this is that $(certbot renew) can't automatically renew this, as it has no way to update DNS record challenge, unless a plugin/custom script is used for this. To renew these certs, one has to re-run the original $(certbot certonly) command. # Reference: https://github.com/certbot/certbot/issues/6280 # Delete existing certbot delete certbot certonly --server https://acme-v02.api.letsencrypt.org/directory -w /usr/local/www/nginx --preferred-challenge dns --manual -d erikw.me -d '*.erikw.me' # Do the DNS TXT records. Then update nginx.conf to use the new directory (if forgot to $(certbot delete) before this step). # There's a plugin for LoopiaDNS to update cert with DNS challenge! # https://github.com/runfalk/certbot-loopia pip3 install certbot-loopia cat << EOF >/usr/local/etc/loopiaapi.ini certbot_loopia:auth_user = erikw@loopiaapi certbot_loopia:auth_password = passwordgoeshere EOF chmod 600 /usr/local/etc/loopiaapi.ini certbot certonly --server https://acme-v02.api.letsencrypt.org/directory --authenticator certbot-loopia:auth --certbot-loopia:auth-credentials /usr/local/etc/loopiaapi.ini -d erikw.me -d '*.erikw.me' # Unfortunately this did not work, so I reverted to the non-wildcard cert, so that I don't have to manually update DNS records every month. ##} # # Make IP forwardin in router for 443/tcp # Let through in firewall: cat << EOF >/usr/local/etc/nginx/nginx.conf #user nobody; worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; #gzip on; # http #server { # listen 80; # server_name erikw.me; # #charset koi8-r; # #access_log logs/host.access.log main; # location / { # root /usr/local/www/nginx; # index index.html index.htm; # } # #error_page 404 /404.html; # # redirect server error pages to the static page /50x.html # # # error_page 500 502 503 504 /50x.html; # location = /50x.html { # root /usr/local/www/nginx-dist; # } # # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # # # #location ~ \.php$ { # # proxy_pass http://127.0.0.1; # #} # # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # # # #location ~ \.php$ { # # root html; # # fastcgi_pass 127.0.0.1:9000; # # fastcgi_index index.php; # # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # # include fastcgi_params; # #} # # deny access to .htaccess files, if Apache's document root # # concurs with nginx's one # # # #location ~ /\.ht { # # deny all; # #} #} # HTTPS server { listen 443 ssl; server_name erikw.me; ssl_certificate /usr/local/etc/letsencrypt/live/erikw.me/fullchain.pem; ssl_certificate_key /usr/local/etc/letsencrypt/live/erikw.me/privkey.pem; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; location / { root /usr/local/www/nginx; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/local/www/nginx-dist; } } # Redirect all http -> https # Reference: https://bjornjohansen.no/redirect-to-https-with-nginx server { listen 80 default_server; listen [::]:80 default_server; server_name _; return 301 https://$host$request_uri; } # HTTP: Redirect (www|server-name).erikw.me -> erikw.me permanently. server { listen 80; server_name www.erikw.me server-name.erikw.me; return 301 $scheme://erikw.me$request_uri; } # HTTPS: Redirect (www|server-name).erikw.me -> erikw.me permanently. server { listen 443 ssl; server_name www.erikw.me server-name.erikw.me; return 301 $scheme://erikw.me$request_uri; } } EOF # To renew certs, just do certbot renew # Let's automate this: cat << EOF >/etc/cron.d/certbot-renew SHELL=/bin/sh PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin # Update TLS certs. Run it at midnight and noon, per recommendation. # Reference:https://certbot.eff.org/lets-encrypt/freebsd-nginx 0 0,12 * * * cron_mail -s "certbot renew" "python3.6 -c 'import random; import time; time.sleep(random.random() * 3600)' && certbot renew -q" EOF # } # Access log stats{ # Get stats based on access log portmaster sysutils/goaccess goaccess --log-format=COMBINED /var/log/nginx/access.log # } # } # }