From 7c5eae9294994e59a5176eba21093ecd6cdfcf63 Mon Sep 17 00:00:00 2001 From: Jan Tuomi Date: Mon, 29 Sep 2025 00:53:24 +0200 Subject: Improve jails --- templates/etc_crontab.j2 | 3 ++ templates/etc_jail.conf.j2 | 20 +++++---- templates/etc_pf.conf.j2 | 9 +---- templates/etc_rc.conf.j2 | 16 ++++---- templates/usr_local_bin_do_dyndns.sh.j2 | 67 +++++++++++++------------------ templates/usr_local_bin_logto.sh.j2 | 47 ++++++++++++++++++++++ templates/usr_local_etc_rc.d_do_dyndns.j2 | 53 ------------------------ 7 files changed, 99 insertions(+), 116 deletions(-) create mode 100644 templates/usr_local_bin_logto.sh.j2 delete mode 100644 templates/usr_local_etc_rc.d_do_dyndns.j2 (limited to 'templates') diff --git a/templates/etc_crontab.j2 b/templates/etc_crontab.j2 index f7a59d9..a6c48e4 100644 --- a/templates/etc_crontab.j2 +++ b/templates/etc_crontab.j2 @@ -20,3 +20,6 @@ PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin # Adjust the time zone if the CMOS clock keeps local time, as opposed to # UTC time. See adjkerntz(8) for details. 1,31 0-5 * * * root adjkerntz -a + +# Update dynamic DNS +*/1 * * * * root logto -e /var/log/do_dyndns.err /usr/local/bin/do_dyndns.sh diff --git a/templates/etc_jail.conf.j2 b/templates/etc_jail.conf.j2 index 86da8de..708e5c1 100644 --- a/templates/etc_jail.conf.j2 +++ b/templates/etc_jail.conf.j2 @@ -1,18 +1,22 @@ # STARTUP/LOGGING/VNET -$epair = "epair${num}"; # epairXa is host end, epairXb is jail end +$tmp_epair = "epair${num}"; +$epl = "epl${num}"; # eplXa is host end (local network bridge), eplXb is jail end. + # The corresponding pubnet interface is epwX, but that's not created for all jails. vnet; -vnet.interface = "${epair}b"; +vnet.interface = "${epl}b"; persist; -exec.prestart += "ifconfig $epair create || echo 'Failed to create $epair'"; -exec.prestart += "ifconfig ${epair}a up"; -exec.prestart += "ifconfig bridge0 addm ${epair}a"; +exec.prestart += "ifconfig $tmp_epair create || echo 'Failed to create $tmp_epair'"; +exec.prestart += "ifconfig ${tmp_epair}a name ${epl}a"; +exec.prestart += "ifconfig ${tmp_epair}b name ${epl}b"; +exec.prestart += "ifconfig ${epl}a up"; +exec.prestart += "ifconfig brlan0 addm ${epl}a"; exec.clean; exec.start = "/bin/sh /etc/rc"; -exec.poststart = "jexec ${name} ifconfig ${epair}b 192.168.2.${num}/16"; +exec.poststart = "jexec ${name} ifconfig ${epl}b 192.168.2.${num}/16"; exec.poststart += "jexec ${name} route add default 192.168.0.1 || echo 'Failed to add default route'"; exec.stop = "/bin/sh /etc/rc.shutdown"; -exec.poststop += "ifconfig ${epair}b -vnet $name"; # workaround to bug 238326: move ${epair}b from the jail to the host when stopping jail services -exec.poststop += "ifconfig ${epair}b destroy"; # and then destroy the pair by destroying one end of it +exec.poststop += "ifconfig ${epl}b -vnet $name"; # workaround to bug 238326: move ${epl}b from the jail to the host when stopping jail services +exec.poststop += "ifconfig ${epl}b destroy"; # and then destroy the pair by destroying one end of it exec.consolelog = "/var/log/jail_console_${name}.log"; # PERMISSIONS diff --git a/templates/etc_pf.conf.j2 b/templates/etc_pf.conf.j2 index 0bb7fef..a439702 100644 --- a/templates/etc_pf.conf.j2 +++ b/templates/etc_pf.conf.j2 @@ -7,13 +7,6 @@ set skip on lo set block-policy return scrub in -# Redirect HTTP to ingress jail -# TODO: fix these redirects, they seem to not work -rdr on $wan_if proto tcp from any to ($wan_if) port 80 -> $jail_ingress_ip port 80 -rdr on $wan_if proto tcp from any to ($wan_if) port 443 -> $jail_ingress_ip port 443 -rdr on $lan_if proto tcp from any to ($lan_if) port 80 -> $jail_ingress_ip port 80 -rdr on $lan_if proto tcp from any to ($lan_if) port 443 -> $jail_ingress_ip port 443 - # Default block all block in all @@ -21,7 +14,7 @@ block in all pass out all keep state # Allow inbound HTTP/HTTPS on both interfaces -pass in on $wan_if proto tcp from any to ($wan_if) port { 80, 443 } keep state +pass in on $wan_if proto tcp from any to any port { 80, 443 } keep state pass in on $lan_if proto tcp from any to ($lan_if) port { 80, 443 } keep state # Allow SSH only on lan0 diff --git a/templates/etc_rc.conf.j2 b/templates/etc_rc.conf.j2 index 195e599..03b5d10 100644 --- a/templates/etc_rc.conf.j2 +++ b/templates/etc_rc.conf.j2 @@ -8,14 +8,17 @@ moused_nondefault_enable="NO" # Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable dumpdev="AUTO" zfs_enable="YES" +defaultrouter="192.168.0.1" +cloned_interfaces="bridge0 bridge1" ifconfig_igc0_name="lan0" ifconfig_igc1_name="wan0" +ifconfig_bridge0_name="brlan0" +ifconfig_bridge1_name="brwan0" ifconfig_lan0="inet {{ lan_ipv4_cidr }}" ifconfig_lan0_ipv6="inet6 accept_rtadv" -ifconfig_wan0="DHCP" -ifconfig_wan0_ipv6="inet6 accept_rtadv" -cloned_interfaces="bridge0" -ifconfig_bridge0="addm lan0 up" +ifconfig_wan0="up" +ifconfig_brlan0="addm lan0 up" +ifconfig_brwan0="addm wan0 up" zpool_gpt_labels_enable="YES" smartd_enable="YES" pf_enable="YES" @@ -24,12 +27,7 @@ syslogd_flags="-s" jail_enable="YES" jail_parallel_start="YES" jail_reverse_stop="YES" -nginx_enable="YES" clear_tmp_enable="YES" cleanvar_enable="YES" auditd_enable="YES" devd_enable="YES" -do_dyndns_enable="YES" -do_dyndns_domain="jan.systems" -do_dyndns_hostname="pursotin" -do_dyndns_if4="wan0" diff --git a/templates/usr_local_bin_do_dyndns.sh.j2 b/templates/usr_local_bin_do_dyndns.sh.j2 index 1b080b7..f23d635 100644 --- a/templates/usr_local_bin_do_dyndns.sh.j2 +++ b/templates/usr_local_bin_do_dyndns.sh.j2 @@ -1,51 +1,42 @@ #!/bin/sh set -eu -: "${DO_DYNDNS_DOMAIN:?DO_DYNDNS_DOMAIN must be set}" -: "${DO_DYNDNS_HOSTNAME:?DO_DYNDNS_HOSTNAME must be set}" -: "${DO_DYNDNS_IF4:?DO_DYNDNS_IF4 must be set}" -: "${DO_DYNDNS_INTERVAL:?DO_DYNDNS_INTERVAL must be set}" -: "${DO_DYNDNS_AUTH_FILE:?DO_DYNDNS_AUTH_FILE must be set}" - -if [ ! -r "$DO_DYNDNS_AUTH_FILE" ]; then - echo "[ERROR] Auth file '$DO_DYNDNS_AUTH_FILE' not readable" >&2 - exit 1 -fi +DOMAIN="jan.systems" +HOSTNAME="pursotin" +AUTH_FILE="/usr/local/etc/do_dyndns_auth" +IF4="$(jexec ingress ifconfig epw1b)" + +# END OF CONFIG + +info() { + echo "$(date) [INFO]" $@ +} -DOMAIN="$DO_DYNDNS_DOMAIN" -HOSTNAME="$DO_DYNDNS_HOSTNAME" -IF4="$DO_DYNDNS_IF4" -INTERVAL="$DO_DYNDNS_INTERVAL" +err() { + >&2 echo "$(date) [ERROR]" $@ +} -while :; do - IP4=$(ifconfig "$IF4" | awk '/inet / { print $2 }' | head -n1) - DIGITALOCEAN_TOKEN=$(awk '{$1=$1; print}' "$DO_DYNDNS_AUTH_FILE") +if [ ! -r "$AUTH_FILE" ]; then + err "Auth file '$AUTH_FILE' not readable" + exit 1 +fi + +IP4=$(echo "$IF4" | awk '/inet / { print $2 }' | head -n1) +DIGITALOCEAN_TOKEN=$(awk '{$1=$1; print}' "$AUTH_FILE") - echo "[INFO] Updating A record for hostname $HOSTNAME in domain $DOMAIN → $IP4" +info "Updating A record for hostname $HOSTNAME in domain $DOMAIN → $IP4" - RECORD_ID=$(curl -s -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \ - "https://api.digitalocean.com/v2/domains/$DOMAIN/records?per_page=999999" | - jq -r ".domain_records[] | select(.type==\"A\" and .name==\"$HOSTNAME\") | .id") +RECORD_ID=$(curl -s -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \ +"https://api.digitalocean.com/v2/domains/$DOMAIN/records?per_page=999999" | +jq -r ".domain_records[] | select(.type==\"A\" and .name==\"$HOSTNAME\") | .id") - if [ -n "$RECORD_ID" ]; then +if [ -n "$RECORD_ID" ]; then curl -s -X PUT \ -H "Authorization: Bearer $DIGITALOCEAN_TOKEN" \ -H "Content-Type: application/json" \ -d "{\"data\":\"$IP4\"}" \ "https://api.digitalocean.com/v2/domains/$DOMAIN/records/$RECORD_ID" > /dev/null - echo "[INFO] Record $RECORD_ID updated" - else - echo "[WARN] No A record found for hostname \"$HOSTNAME\" in domain \"$DOMAIN\". Skipping." - fi - - SLEEP_SEC=$(printf "%s\n" "$INTERVAL" | awk ' - /^[0-9]+s$/ { gsub(/s$/, ""); print $0; exit } - /^[0-9]+m$/ { gsub(/m$/, ""); print $0 * 60; exit } - /^[0-9]+h$/ { gsub(/h$/, ""); print $0 * 3600; exit } - /^[0-9]+d$/ { gsub(/d$/, ""); print $0 * 86400; exit } - /^[0-9]+$/ { print $0; exit } - { print "invalid"; exit 1 } - ') - - sleep "$SLEEP_SEC" -done + info "Record $RECORD_ID updated" +else + err "No A record found for hostname \"$HOSTNAME\" in domain \"$DOMAIN\". Skipping." +fi diff --git a/templates/usr_local_bin_logto.sh.j2 b/templates/usr_local_bin_logto.sh.j2 new file mode 100644 index 0000000..9bb5aa1 --- /dev/null +++ b/templates/usr_local_bin_logto.sh.j2 @@ -0,0 +1,47 @@ +#!/bin/sh + +set -ue + +# Wrapper for logging to file and prepending a timestamp. +# By default writes both stdout and stderr to the log file. + +usage() { + echo "Usage: logto [-s|-e] " + echo "Flags:" + echo " -s: Write only stdout to the log file." + echo " -e: Write only stderr to the log file." + echo "" + echo "Example usage:" + echo " logto /var/log/my.log run some command" + exit 1 +} + +mode="all" + +while getopts "se" opt; do + case $opt in + s) mode="stdout" ;; + e) mode="stderr" ;; + *) usage ;; + esac +done +shift $((OPTIND-1)) + +if [ $# -lt 2 ]; then + usage +fi + +log_file="$1" +shift + +if [ "$mode" = "stdout" ]; then + out=$(2>/dev/null $@) +elif [ "$mode" = "stderr" ]; then + out=$(2>&1 >/dev/null $@) +else + out=$(2>&1 $@) +fi + +if [ ! -z "$out" ]; then + echo "$(date +"%Y-%m-%dT%H:%M:%S%z")" "$out" >>"$log_file" +fi diff --git a/templates/usr_local_etc_rc.d_do_dyndns.j2 b/templates/usr_local_etc_rc.d_do_dyndns.j2 deleted file mode 100644 index 6c4c18e..0000000 --- a/templates/usr_local_etc_rc.d_do_dyndns.j2 +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/sh -# -# PROVIDE: do_dyndns -# REQUIRE: NETWORKING -# KEYWORD: shutdown - -. /etc/rc.subr - -name="do_dyndns" -rcvar=do_dyndns_enable - -extra_commands="status" -rcvars="do_dyndns_enable do_dyndns_domain do_dyndns_hostname do_dyndns_if4 do_dyndns_interval do_dyndns_auth_file" -stop_cmd="do_dyndns_stop" - -load_rc_config $name - -: "${do_dyndns_enable:=NO}" -: "${do_dyndns_interval:=1m}" -: "${do_dyndns_hostname:=@}" -: "${do_dyndns_auth_file:=/usr/local/etc/do_dyndns_auth}" - -pidfile="/var/run/${name}.pid" -logfile="/var/log/${name}" - -command="/usr/sbin/daemon" -command_args="-cfr -P ${pidfile} -o ${logfile} /bin/sh -c ' - export PATH=\"/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin\" - export DO_DYNDNS_DOMAIN=\"${do_dyndns_domain}\" - export DO_DYNDNS_HOSTNAME=\"${do_dyndns_hostname}\" - export DO_DYNDNS_IF4=\"${do_dyndns_if4}\" - export DO_DYNDNS_INTERVAL=\"${do_dyndns_interval}\" - export DO_DYNDNS_AUTH_FILE=\"${do_dyndns_auth_file}\" - - exec /usr/local/bin/do_dyndns.sh -'" - -do_dyndns_stop() { - echo "Stopping ${name} with SIGINT..." - if [ -f "${pidfile}" ]; then - kill -INT "$(cat "${pidfile}")" - sleep 5 - if kill -0 "$(cat "${pidfile}")" 2>/dev/null; then - echo "Process didn't exit, sending SIGKILL..." - kill -KILL "$(cat "${pidfile}")" - fi - rm -f "${pidfile}" - else - echo "${name}: no pidfile found" - fi -} - -run_rc_command "$1" -- cgit v1.3