summaryrefslogtreecommitdiff
path: root/test/integration/common.rb
blob: fb29d7a34753b6444d0cc2275cec022115863132 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../../lib"

require_relative '../common'
require 'mocha/setup'
require 'pty'
require 'expect'

module IntegrationTestHelpers
  VERBOSE = false
  def sh(command)
    puts "$ #{command}" if VERBOSE
    res = system(command)
    status = $?
    raise "Command: #{command} failed:#{status.exitstatus}" unless res
  end

  def tmpdir(&block)
    Dir.mktmpdir do |dir|
      yield(dir)
    end
  end

  def sshd_8_or_later?
    !!(`sshd  -v 2>&1 |grep 'OpenSSH_'` =~ /OpenSSH_8./)
  end

  def set_authorized_key(user, pubkey)
    authorized_key = "/home/#{user}/.ssh/authorized_keys"
    sh "sudo cp #{pubkey} #{authorized_key}"
    sh "sudo chown #{user} #{authorized_key}"
    sh "sudo chmod 0744 #{authorized_key}"
  end

  def sign_user_key(user, pubkey)
    cert = "/etc/ssh/users_ca"
    sh "sudo ssh-keygen -s #{cert} -I user_#{user} -n #{user} -V +52w #{pubkey}"
  end

  def with_agent(&block)
    puts "/usr/bin/ssh-agent -c" if VERBOSE
    agent_out = `/usr/bin/ssh-agent -c`
    agent_out.split("\n").each do |line|
      if line =~ /setenv (\S+) (\S+);/
        ENV[$1] = $2
        puts "ENV[#{$1}]=#{$2}" if VERBOSE
      end
    end
    begin
      yield
    ensure
      sh "/usr/bin/ssh-agent -k > /dev/null"
    end
  end

  def ssh_add(key, password)
    command = "ssh-add #{key}"
    status = nil
    PTY.spawn(command) do |reader, writer, pid|
      begin
        reader.expect(/Enter passphrase for .*:/) { |data| puts data }
        writer.puts(password)
        until reader.eof? do
          line = reader.readline
          puts line if VERBOSE
        end
      rescue Errno::EIO => _e
      end
      pid, status = Process.wait2 pid
    end
    raise "Command: #{command} failed:#{status.exitstatus}" unless status

    status.exitstatus
  end

  def with_sshd_config(sshd_config, &block)
    raise "Failed to copy config" unless system("sudo cp -f /etc/ssh/sshd_config /etc/ssh/sshd_config.original")

    begin
      Tempfile.open('sshd_config') do |f|
        f.write(sshd_config)
        f.close
        system("sudo cp -f #{f.path} /etc/ssh/sshd_config")
      end
      system("sudo chmod 0644 /etc/ssh/sshd_config")
      raise "Failed to restart sshd" unless system("sudo service ssh restart")

      yield
    ensure
      system("sudo cp -f /etc/ssh/sshd_config.original /etc/ssh/sshd_config")
      system("sudo service ssh restart")
    end
  end

  def with_lines_as_tempfile(lines = [], add_pid: true, debug: false, &block)
    Tempfile.open('sshd_config') do |f|
      f.write(lines.join("\n"))
      pidpath = nil
      if add_pid
        pidpath = f.path + '.pid'
        f.write("\nPidFile #{pidpath}\n")
      end
      f.write("\nLogLevel DEBUG3\n") if debug
      f.close
      puts "CONFIG: #{f.path} PID: #{pidpath}" if debug
      yield(f.path, pidpath)
    end
  end

  def port_open?(path)
    Socket.tcp("localhost", 10567, connect_timeout: 1) { true } rescue false # rubocop:disable Style/RescueModifier
  end

  # @yield [pid, port]
  def start_sshd_7_or_later(port = '2200', config: nil, debug: false)
    pid = nil
    sshpidfile = nil
    if config
      with_lines_as_tempfile(config, debug: debug) do |path, pidpath|
        puts "DEBUG - SSH LOG: #{path}-log.txt config: #{path}" if debug
        raise "A leftover sshd is already running" if port_open?(port)

        extra_params = []
        extra_params = ['-E', "#{path}-log.txt"] if debug
        pid = spawn('sudo', '/opt/net-ssh-openssh/sbin/sshd', '-D', '-f', path, '-p', port, *extra_params)
        sshpidfile = pidpath
        yield pid, port
      end
    else
      with_lines_as_tempfile(['']) do |path, pidpath|
        pid = spawn('sudo', '/opt/net-ssh-openssh/sbin/sshd', '-D', '-f', path, '-p', port)
        sshpidfile = pidpath
        yield pid, port
      end
    end
  ensure
    # Our pid is sudo and not sshd, -9 (KILL) on sudo will not clean up its children
    # properly, so we just have to hope that -15 (TERM) will manage to bring
    # down sshd.
    if sshpidfile
      sshpid = File.read(sshpidfile).strip
      system('sudo', 'kill', '-15', sshpid.to_s)
      begin
        Timeout.timeout(20) do
          Process.wait(pid)
        end
      rescue Timeout::Error
        warn "Failed to kill openssh process: #{sshpid}"
        system('sudo', 'kill', '-9', sshpid.to_s)
        raise
      end
    elsif pid
      system('sudo', 'kill', '-15', pid.to_s)
      begin
        Timeout.timeout(20) do
          Process.wait(pid)
        end
      rescue Timeout::Error
        warn "Failed to kill openssh process: #{pid}"
        system('sudo', 'kill', '-9', pid.to_s)
        raise
      end
    end
  end

  def localhost
    'localhost'
  end

  def user
    'net_ssh_1'
  end
end