Reglas de tiempo en Iptables

Hay escenarios en donde se le solicita al sysadmin que aplique reglas de tiempo para el acceso a determinados equipos en la red local. Para poder ejecutar esta tarea, el sysadmin debe tener previamente establecido en su firewall (basado en reglas iptables), las diferentes ACLs con las direcciones MACs de los equipos de su red local, cada una con sus respectivos privilegios de acceso que les quiere otorgar.
Una vez definido esto, es muy sencillo establecer el tiempo de acceso. Según el Man de Iptables, la regla que se utiliza es time, la cual cuenta con varios parámetros:
--datestart YYYY[-MM[-DD[Thh[:mm[:ss]]]]]
--datestop YYYY[-MM[-DD[Thh[:mm[:ss]]]]]
Only match during the given time, which must be in ISO 8601 "T" notation. The possible time range is 1970-01-01T00:00:00 to 2038-01-19T04:17:07.
If --datestart or --datestop are not specified, it will default to 1970-01-01 and 2038-01-19, respectively.
--timestart hh:mm[:ss]
--timestop hh:mm[:ss]
Only match during the given daytime. The possible time range is 00:00:00 to 23:59:59. Leading zeroes are allowed (e.g. "06:03") and correctly interpreted as base-10.
[!] --monthdays day[,day...]
Only match on the given days of the month. Possible values are 1 to 31. Note that specifying 31 will of course not match on months which do not have a 31st day; the same goes for 28- or 29-day February.
[!] --weekdays day[,day...]
Only match on the given weekdays. Possible values are Mon, Tue, Wed, Thu, Fri, Sat, Sun, or values from 1 to 7, respectively. You may also use two-character variants (Mo, Tu, etc.).
--contiguous
When --timestop is smaller than --timestart value, match this as a single time period instead distinct intervals. See EXAMPLES.
--kerneltz
Use the kernel timezone instead of UTC to determine whether a packet meets the time regulations.
Para nuestro ejemplo de hoy utilizaremos --timestart y --timestopEl tiempo debe ser establecido en HH:MM (y también segundos).
iptables RULE -m time --timestart TIME --timestop TIME -j ACTION
Nota: Si utiliza Ubuntu 18.04.x LTS iptables v1.6.1, no recomendamos utilizar los segundos, ya que en pruebas realizadas la regla falló, sin poder aún establecer la causa, sin embargo creemos que se deba al problema de UTC, que explicaremos al final.
Caso
En el siguiente ejemplo, aplicaremos la regla time de iptables a una ACL (que llamaremos macslimited y que contiene las direcciones MACs con su formato de 6 bloques de dos caracteres hexadecimales) la cual le restringiremos el acceso a los puertos 80,443 durante el día y los abriremos a partir de la 2 PM (14 H).
Adicionalmente la regla la restringiremos para el rango CIDR 192.168.0.0/24 y para dos interfaces de red (enp2s0 para la salida a internet y enp2s8 para la red local local):
# hora permitida de acceso a los puertos
allowtime=14:00
# hora de cierre de los puertos
droptime=23:59
# regla de tiempo
for mac in macslimited; do
  iptables -A INPUT -s 192.168.0.0/24 -i enp2s8 -m mac --mac-source $mac -p tcp -m multiport --dports 80,443 -m time --timestart $allowtime --timestop $droptime -j ACCEPT
  iptables -A FORWARD -s 192.168.0.0/24 -i enp2s8 -m mac --mac-source $mac -p tcp -m multiport --dports 80,443 -o enp2s0 -m time --timestart $allowtime --timestop $droptime -j ACCEPT
done
Adicionalmente podemos establecer los días de la semana de aplicación de la regla:
# hora permitida de acceso a los puertos
allowtime=14:00
# hora de cierre de los puertos
droptime=23:59
# regla de tiempo
for mac in macslimited; do
  iptables -A INPUT -s 192.168.0.0/24 -i enp2s8 -m mac --mac-source $mac -p tcp -m multiport --dports 80,443 -m time --timestart $allowtime --timestop $droptime --weekdays Mon,Tue,Wed,Thu,Fri -j ACCEPT
  iptables -A FORWARD -s 192.168.0.0/24 -i enp2s8 -m mac --mac-source $mac -p tcp -m multiport --dports 80,443 -o enp2s0 -m time --timestart $allowtime --timestop $droptime --weekdays Mon,Tue,Wed,Thu,Fri -j ACCEPT
done
Sin embargo, esta regla tiene una limitante y es que no permite que el horario se extienda más allá de las 24 H; un problema descrito en el portal unix.stackexchange.com
Entonces si queremos que el horario se extienda más allá de la hora 24 (por ejemplo: desde 2 PM hasta las 8 AM del siguiente día), habría que crear un segundo horario, justo cuando termina el primero, o sea, desde 00:00 hasta 08:00 y repetir cada regla de iptables para el nuevo horario.
Una posible solución es invertirlo, o sea, poner el horario en que NO queremos que acceda esta ACL (ejemplo: 08:00 14:00) y cambiamos las reglas de iptables de ACCEPT a DROP. De esta manera el "resto" del tiempo nuestra lista tendría acceso. Pero también tiene sus desventajas, ya que es muy probable que tenga que agregar reglas iptables adicionales para autorizar estos puertos en el horario deseado.
Pero hay un truco (cortesía de novatoz) que nos ayuda a evadir este limite y cumple exactamente con nuestro objetivo:
# verifica la hora de sistema
actualtime=$(date +%H)
# hora permitida de acceso a los puertos
allowtime=14
# hora de cierre de los puertos 
droptime=8
if [ $current_time -ge $allowtime -o $actualtime -lt $droptime ]; then
  for mac in macslimited; do
    iptables -A INPUT -s 192.168.0.0/24 -i enp2s8 -m mac --mac-source $mac -p tcp -m multiport --dports 80,443 -j ACCEPT
    iptables -A FORWARD -s 192.168.0.0/24 -i enp2s8 -m mac --mac-source $mac -p tcp -m multiport --dports 80,443 -o enp2s0 -j ACCEPT
  done
fi
Problemas con el tiempo
Es común que los sistemas no se sincronicen con la exactitud que quisiéramos ni todos usan la misma hora. Por ejemplo, tenemos el "Hardware Time" (controlado por la BIOS y se ajusta según la hora local y sus datos se almacenan en una batería), el "System Time" (ajustado manualmente por el usuario y actualizado por los servidores NTP), que trabajan con el Universal Time Coordinated (UTC). También esta la variante Greenwich Mean Time (GMT), que tiene la misma hora de UTC pero expresada de diferente manera (GMT es una zona horaria y UTC es un estándar de tiempo), por ejemplo 1 p.m. GMT = 13 horas UTC. También está el horario de verano o invierno, la hora Zulu...
En fin, este es un tema que por su extensión no lo abordaremos en esta entrada, sin embargo lo relevante para nuestro caso es que esta situación afecta las aplicaciones que corren bajo Linux, y en especial las que corren a nivel de kernel, como iptables, por tanto también afecta las reglas basadas en tiempo.
Local Time vs UTC vs RTC
Entonces, para que funcionen las reglas de tiempo de iptables de manera correcta existen dos alternativas:
1. Cambiar todo el sistema a UTC-0 (explicado AQUI), incluyendo la BIOS
2. Agregar el parámetro --kerneltz a las reglas:
# hora permitida de acceso a los puertos
allowtime=14:00
# hora de cierre de los puertos
droptime=23:59
for mac in macslimited; do
  iptables -A INPUT -s 192.168.0.0/24 -i enp2s8 -m mac --mac-source $mac -p tcp -m multiport --dports 80,443 -m time --timestart $allowtime --timestop $droptime --kerneltz -j ACCEPT
  iptables -A FORWARD -s 192.168.0.0/24 -i enp2s8 -m mac --mac-source $mac -p tcp -m multiport --dports 80,443 -o enp2s0 -m time --timestart $allowtime --timestop $droptime --kerneltz -j ACCEPT
done
Como lo menciona el Man de iptables, lo que hace el parámetro --kerneltz es usar la zona horaria kernel, en lugar de UTC, para determinar si un paquete cumple con las regulaciones de tiempo, pero esto puede no cumplirse en todos los escenarios.
Hay una solución, no recomendada, y es hacer un forzado para que el sistema sincronice y lea RTC:
timedatectl list-timezones # set your zone
timedatectl set-timezone "America/Bogota" # example timezone
apt install ntp # to sync
ntpq -p  # to sync
crontab -e
# add line
@reboot root /usr/bin/ntpd -n  # to sync
service cron restart
hwclock --localtime --systohc  # to set RTC
timedatectl status # to check
hwclock -r
Y el resultado:
Nosotros recomendamos algo mucho más sencillo con hwclock. Tenga en cuenta los parámetros:
 -s, --hctosys        set the system time from the RTC
 -w, --systohc        set the RTC from the system time
Aquí lo mejor es copiar la hora de sistema (system time) a la hora del hardware (hardware time) y programarlo en crontab para que se ejecute en cada reinicio y así no afectar la configuración por defecto de Linux (en este caso de Ubuntu):
timedatectl set-ntp true
hwclock -w
Con la tecnología de Blogger.