Files
ubicloud/prog/rotate_ssh_key.rb
Enes Cakir 8357856622 Add an identifier to all Prog label methods
While reviewing and reading code, label and helper methods are mixed in
Prog files. It's hard to identify sometimes without in-depth looking.

I propose to define labels with `self.label` class method.
One of clover's predecessor has `work_` prefix, and I think it
was useful.

It has some benefits:
1. It helps to identify label methods easily
2. It helps to keep list of valid labels, `hop` can check target label is
   valid or not
3. It generates `hop_#{label}` methods. Instead of freehand `hop :label`
   calls, we can call `hop_label` method similar to `incr_#{name}`
   semaphore methods.

(2) and (3) have some overlapped benefits. They help to stay in valid
label world in different ways.
2023-08-23 12:09:57 +03:00

61 lines
1.7 KiB
Ruby

# frozen_string_literal: true
require "shellwords"
class Prog::RotateSshKey < Prog::Base
subject_is :sshable
label def start
sshable.update(raw_private_key_2: SshKey.generate.keypair)
hop_install
end
label def install
public_keys = sshable.keys.map(&:public_key).join("\n")
sshable.cmd(<<SH)
set -ueo pipefail
echo #{public_keys.shellescape} > ~/.ssh/authorized_keys2
SH
hop_retire_old_key_on_server
end
label def retire_old_key_on_server
# Test authentication with new key new key at the same time.
Net::SSH.start(sshable.host, "rhizome",
Sshable::COMMON_SSH_ARGS.merge(key_data: [SshKey.from_binary(sshable.raw_private_key_2).private_key])) do |sess|
sess.exec!(<<SH)
set -ueo pipefail
sync ~/.ssh/authorized_keys2
mv ~/.ssh/authorized_keys2 ~/.ssh/authorized_keys
sync ~/.ssh
SH
end
hop_retire_old_key_in_database
end
label def retire_old_key_in_database
changed_records = sshable.this.where(
Sequel.~(raw_private_key_2: nil)
).update(raw_private_key_1: Sequel[:raw_private_key_2], raw_private_key_2: nil)
fail "Unexpected number of changed records: #{changed_records}" unless changed_records == 1
hop_test_rotation
end
label def test_rotation
# Bypass Sshable caching for the test, as it can have an existing
# authorized session.
Net::SSH.start(sshable.host, "rhizome",
Sshable::COMMON_SSH_ARGS.merge(key_data: sshable.keys.map(&:private_key))) do |sess|
ret = sess.exec!("echo key rotated successfully")
fail "Unexpected exit status: #{ret.exitstatus}" unless ret.exitstatus.zero?
fail "Unexpected output message: #{ret}" unless ret == "key rotated successfully\n"
end
pop "key rotated successfully"
end
end