============ What is Yubiknock? A pair of programs that allow you to take an action of your choice on a system when a remote user connects and enters a yubikey one-time password. The first program (yubiknock.py) listens on a TCP port (default port: 8975); when someone connects, the server looks for a 44 character yubikey OTP. If this is valid, the source IP and the unique ID for that key (the first 12 characters of any OTP) are handed to a second program, yubiknock-authorize. This program can be written in any language and take any action you'd like. Examples: - Allw the client IP to come through the firewall for a predefined time period on a limited set of ports. The client - the computer requesting access with a yubikey - doesn't need special client software. Any kind of web browser, telnet client, netcat, bash shell or z shell can connect to yubiknock.py on the server. The client computer will obviously need a working USB port into which the yubikey can be inserted. ============ Running the server ./yubiknock.py -c ClientIdNumber -p 8975 -e ~/med/programming/perl/yubiknock/yubiknock-authorize Get the Yubico Client API Key from https://api.yubico.com/get-api-key/ or https://upgrade.yubico.com/getapikey/ ============ What to run on the remote machine The yubiknock.py prgram will accept a connection from any program that can connect to a remote tcp port. The following assume you're using the default port 8975, adjust if not. From a command prompt, run either nc yubiknock.server.name.or.ip.address 8975 or telnet yubiknock.server.name.or.ip.address 8975 Once connected, press the button on your yubikey and it will send out the 44 character OTP and a return key. Even more simply, start up any web browser. In the URL bar, put: http://172.27.1.182:8975/ With the cursor right after the last slash, press the button on your yubikey. It will fill in the 44 character OTP and press enter for you. wget, curl or any other command line url downloader should work fine as well; start up the command line similarly with: wget http://172.27.1.182:8975/ and press the button on the yubikey. (Note, for either graphical or command line web downloaders, you probably don't want to use a proxy, so either turn it off in settings or run "unset http_proxy" before a command line tool like wget. Leaving the proxy on gives permission to your proxy server's public IP, not the machine on which you run the command.) And for the pure geek factor, both bash and zsh support making tcp connections right from the shell with no external commands. Here's a bash example. Run the first command from the shell, and enter the second one but don't press Enter. exec 3<>/dev/tcp/172.27.1.182/8975 echo -e "" >&3 ; cat <&3 Place the cursor under the second double quote and press the button on the yubikey. Your key will be filled in and the request sent off. (Thanks to http://thesmithfam.org/blog/2006/05/23/bash-socket-programming-with-devtcp-2/ for the syntax.) ============ Setting up the firewall By default this system allows connections to be _created_ for 25 hours after the yubikey is used. Connections opened in that 25 hour period can continue to be used after the 25 hours is up. For example, if I get access to ssh with yubiknock and log in over ssh, I can continue to type on that connection even after the 25 hours is up. If you want to stop _even existing_ connections when the 25 hours is up, use the same commands as below with two changes: - Remove " -m state --state NEW" from all commands. - Don't add the 3 "ESTABLISHED,RLEATED" rules, and instead put in specific input, forward, and output rules for each type of traffic. (note: this may need some extra effort for the forward chain; I'd recommend not making this change for the forward chain.) First, edit and/or create /etc/yubikey_mappings . Each line of the file connects a single username to one or more yubikeys. For yubiknock the usernames do _not_ necessarily need to exist as usernames on this system; they're there to make it easier for you to identify who has access to what ports. (Other yubikey applications that use this file _do_ link usernames in this file to Unix usernames, but yubiknock doesn't.) Sample lines: jsamson:dddddddffnni gparker:hhhhhhhbdrhj:ggggggghhbbe webmaster:dddddddffnni:hhhhhhhbdrhj:ggggggghhbbe sysmonitor:ggggggghhbbe To get the 12 character yubikey identity, start up an editor, insert the yubikey(s) that person plans to use, press the button, and delete all characters after the first 12. gparker has two physical keys (one for normal use and one which also gives him monitoring privileges, so we add a second colon and second Yubikey ID. webmaster is not an actual account on this system, but both jsamson and gparker are in that role, so all of their keys also give them webmaster access (we define what access they get below). Only gparker's second key is used for system monitoring. Give the user under which yubiknock.py runs sudo privileges. ZZZZ, finishme Now set up the basic firewall structure. The rules we add should work well with whatever firewall you already have in place. At most, you might need to adjust the placement of the two rules we'll add to the INPUT and FORWARD chains. sudo iptables -N DynamicI sudo iptables -N DynamicF sudo iptables -I INPUT -m state --state NEW -j DynamicI sudo iptables -I FORWARD -m state --state NEW -j DynamicF #Replace 8975 in the next 2 commands with the port yubiknock listens on. sudo iptables -I INPUT -p tcp --dport 8975 -j ACCEPT sudo iptables -I OUTPUT -p tcp --sport 8975 -j ACCEPT If your firewall does not yet have INPUT, OUTPUT, and FORWARD rules rules for ESTBLISHED,RELATED traffic, add these: sudo iptables -I INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT sudo iptables -I FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT sudo iptables -I OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT If your firewall does not already allow outbound http requests, you'll need to open up http to at least api.yubico.com : sudo iptables -I OUTPUT -d api.yubico.com -p tcp --dport 8975 -j ACCEPT sudo iptables -I INPUT -s api.yubico.com -p tcp --sport 8975 -j ACCEPT (The above is somewhat discouraged as the hostname is resolved into an ip address _just_ at the time the rule is loaded into the kernel; if the ip address for api.yubico.com is later changed, the rules will stay at the original address.) #For each user {Username} (leave off the brackets in the command) sudo iptables -N UserI-{Username} sudo iptables -N UserF-{Username} Now you need to specify what access to give to a user or key. If you're allowing access to ports on _this_ machine, add rules to the appropriate UserI- chains. None of the remaining rules should have a source address restriction hard-coded; the rules that get automatically added to DynamicI and DynamicF will restrict the source IP address. sudo iptables -A UserI-jsamson -p tcp --dport 22 -j ACCEPT sudo iptables -A UserI-jsamson -p tcp --dport 110 -j ACCEPT sudo iptables -A UserI-gparker -p tcp --dport 22 -j ACCEPT sudo iptables -A UserI-gparker -p udp --dport 111 -j ACCEPT sudo iptables -A UserI-gparker -p udp --dport 2049 -j ACCEPT sudo iptables -A UserI-webmaster -p tcp --dport 8888 -j ACCEPT sudo iptables -A UserI-sysmonitor -p udp --dport 161 -j ACCEPT jsamson needs access to ssh and pop3, and gparker needs access to ssh, portmap (for nfs) and nfs. Because all three of their yubikeys are also listed as keys for the "webmaster" user, they also get access to the development http server at port 8888. Because we only put gpkarker's second key under sysmonitor, only that key grants access to the snmp port. If this is a firewall/router/gateway to machines behind it and you want to give access to those machines, add rules to the appropriate UserF- chains. sudo iptables -A UserF-gparker -d 1.2.3.0/24 -p tcp --dport 80 -j ACCEPT In addition to being able to reach the ports we mentioned above on the computer running the firewall, the above rule lets gparker reach any web servers on an internal subnet. When you have the firewall set up the way you want it, run sudo service iptables save or the equivalent for your distribution to save the new firewall setup for the next time you boot. There's one optional approach if you want to grant access by Yubikey Identity and don't want to have user accounts (or want to grant access to individual keys _in addtion_ to granting access to individual accounts). For each Yubikey Identity (the first 12 characters of any OTP created by that key) sudo iptables -N KeyI-{Identity} sudo iptables -N KeyF-{Identity} (not finished text) ============ Other ideas - Have a yubikey that can halt the system by running "/sbin/shutdown -h now" - In the script that opens the port, set up a corresponding "at" job to shut it down in the future. - Make a symlink to yubiknock-revoke in /etc/cron.hourly/ - ssh callback: Have a yubikey that opens an ssh connection _to_ the yubikey client with "-R 2222:localhost:22", allowing the yubikey client to then come back through the tunnel to connect to an ssh server that only listens on 127.0.0.1 (sshd .... -o "ListenAddress=127.0.0.1"...) - Yubikey for remote self destruct. Use hdparm's security erase feature. And don't email me if it works. :-) - start a service on successful auth, stop it on revoke. - enable an account "sudo passwd -u {Account name}" on auth, disable it on revoke "sudo passwd -l {Account name}". - Put in FORWARD chain firewall rules to allow access to a subnet behind a gateway (or even virtual machines running on the yubiknock server). - Have the system feed up honeypots on some non-public ports. With a successful yubiknock, put in PREROUTING(/DNAT/REDIRECT?) rules to shift incoming requests from that client to an alternate port with the real service. - Add lines to /etc/hosts.allow, later remove them with yubikey-revoke - Kick off an openvpn or ssh vpn or ipsec tunnel to the yubiknock client. - If not running, start a virtual machine for that user and put in forwarding rules for the yubiknock client. - Mount a (local or network, regular or encrypted) drive and give access to the yubikey client. - enable a vlan to the client