Skip to content

Instantly share code, notes, and snippets.

@andreaso
Created May 22, 2021 06:45
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andreaso/c0802062c6c5deaafd45dd9f7ffaab4d to your computer and use it in GitHub Desktop.
Save andreaso/c0802062c6c5deaafd45dd9f7ffaab4d to your computer and use it in GitHub Desktop.
Not-entirely-serious SSH TOTP implementation, inspired by https://twitter.com/kistel/status/1395375108315824130.
# /etc/systemd/network/listen-on-totp-range.network
[Match]
Name = lo
[Route]
Destination = 2001:db8:67c1:22::/64
Type = local
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
:TOTP - [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -p ipv6-icmp -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -j TOTP
COMMIT
#!/usr/bin/env python3
import subprocess
import pyotp
BASE_ADDRESS = '2001:db8:67c1:22::'
TOTP_SEED_PATH = '/etc/totp-ssh/seed'
IPTABLES_CHAIN = 'TOTP'
def generate_totp():
with open(TOTP_SEED_PATH, 'r') as totp_seed_file:
totp_seed_value = totp_seed_file.readline().rstrip()
totp_code = pyotp.TOTP(totp_seed_value).now()
return totp_code
def update_iptables(totp_code):
totp_ip = f'{BASE_ADDRESS}{totp_code[:-4]}:{totp_code[-4:]}/128'
flush_rules = ['ip6tables', '-F', IPTABLES_CHAIN]
add_rule = ['ip6tables', '-A', IPTABLES_CHAIN, '-d', totp_ip, '-p', 'tcp', '-m', 'tcp',
'--dport', '22', '-j', 'ACCEPT']
subprocess.run(flush_rules, check=True)
subprocess.run(add_rule, check=True)
def main():
totp_code = generate_totp()
update_iptables(totp_code)
if __name__ == "__main__":
main()
[Unit]
Description=Update allowed ssh TOTP address
[Service]
Type=oneshot
ExecStart=/usr/local/bin/totp-ssh
PrivateTmp=yes
ProtectSystem=strict
[Unit]
Description=Update allowed ssh TOTP address
[Timer]
AccuracySec=1
OnCalendar=*:*:00,30
[Install]
WantedBy=timers.target
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment