I want to see how to provision a server directly, but looking into SSHKit it's a bit heavy handed and too object oriented for what I need. So lets use net-ssh and net-scp directly.

We'll mock out how to connect to a server, create a user, copy over our public key and setup permissions for everything to work correctly.

Setup

1
2
  bundle init
  bundle add net-ssh net-scp ed25519 bcrypt_pbkdf

That gives us:

1
2
3
4
5
6
  source "https://rubygems.org"

  gem "net-ssh", "~> 7.1"
  gem "ed25519", "~> 1.3"
  gem "bcrypt_pbkdf", "~> 1.1"
  gem "net-scp", "~> 4.0"

Testing server connect

Connect as root and then print the hostname and uname of the machine.

1
2
3
4
5
6
7
8
9
  require 'net/ssh'

  host=ENV['SSH_HOST'] || 'apple.willschenk.com'

  puts "Trying to connect to root@#{host}"
  Net::SSH.start(host, 'root', timeout: 5 ) do |ssh|
    puts ssh.exec! "hostname"
    puts ssh.exec! "uname -a"
  end
1
2
3
Trying to connect to root@apple.willschenk.com
apple
Linux apple 6.1.0-9-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.27-1 (2023-05-08) x86_64 GNU/Linux

Checking the exit status of a command

To see if a command is successful, we can check to see if result.exitstatus is 0 or not 0.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
  require 'net/ssh'

  host=ENV['SSH_HOST'] || 'apple.willschenk.com'

  puts "Trying to connect to root@#{host}"
  Net::SSH.start(host, 'root', timeout: 5 ) do |ssh|
    s = ssh.exec! "notdocker -v"
    puts s
    if s.exitstatus == 0
      puts "Success"
    else
      puts "Error"
    end

    s = ssh.exec! "docker -v"
    puts s
    if s.exitstatus == 0
      puts "Success"
    else
      puts "Error"
    end
  end
1
2
3
4
5
Trying to connect to root@apple.willschenk.com
bash: line 1: notdocker: command not found
Error
Docker version 24.0.2, build cb74dfc
Success

Copy a file over

Lets see how to move a file over to the remote machine.

 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
  require 'net/scp'
  host=ENV['SSH_HOST'] || 'apple.willschenk.com'

  puts "Trying to connect to root@#{host}"
  Net::SSH.start(host, 'root', timeout: 5 ) do |ssh|
    s = ssh.exec! "ls /tmp/copy_file.rb"

    if s.exitstatus == 0
      puts "File exists"
    else
      puts "File doesn't exist"
    end

    puts "Uploading file"

    ssh.scp.upload! "copy_file.rb", "/tmp/copy_file.rb"

    s = ssh.exec! "ls /tmp/copy_file.rb"

    if s.exitstatus == 0
      puts "File exists"
    else
      puts "File doesn't exist"
    end
  end
1
2
3
4
Trying to connect to root@apple.willschenk.com
File doesn't exist
Uploading file
File exists

Example: Create a new user and set authorized keys

 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
  require 'net/scp'
  host=ENV['SSH_HOST'] || 'apple.willschenk.com'

  user = "gituser"
  puts "Trying to connect to root@#{host}"
  Net::SSH.start(host, 'root', timeout: 5 ) do |ssh|
    puts "Creating #{user}"

    res = ssh.exec! "useradd #{user} -m -g docker -s /bin/bash"

    puts res

    res = ssh.exec! "grep #{user} /etc/passwd"
    puts res

    if res.exitstatus != 0
      puts "Didn't create #{user}"
      exit
    end

    remote_home = res.split( /:/ )[5]

    res = ssh.exec! "mkdir -p #{remote_home}/.ssh"
    puts res
    if res.exitstatus != 0
      puts "Error creating directory"
      exit
    end

    puts "Uploading public key"
    ssh.scp.upload! "#{ENV['HOME']}/.ssh/id_ed25519.pub",
                    "#{remote_home}/.ssh/authorized_keys"

    puts "Fixing permissions"
    puts ssh.exec! "chown -R #{user} #{remote_home}/.ssh"
    puts ssh.exec! "chmod 700 #{remote_home}/.ssh"
    puts ssh.exec! "chmod 600 #{remote_home}/.ssh/authorized_keys"
  end
1
2
3
4
5
6
7
Trying to connect to root@apple.willschenk.com
Creating gituser
useradd: user 'gituser' already exists
gituser:x:1004:996::/home/gituser:/bin/bash

Uploading public key
Fixing permissions

Connecting as a different user

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
  require 'net/scp'
  host=ENV['SSH_HOST'] || 'apple.willschenk.com'

  user = "gituser"
  puts "Trying to connect to #{user}@#{host}"
  Net::SSH.start(host, user, timeout: 5 ) do |ssh|
    puts "Creating #{user}"

    puts ssh.exec! "whoami"
    puts ssh.exec! "pwd"
  end
1
2
3
4
Trying to connect to gituser@apple.willschenk.com
Creating gituser
gituser
/home/gituser

Previously

Playing with unocss Slimmer than tailwind, but still cool

2023-07-10

Next

Should Robots Have Rites or Rights

2023-07-11

labnotes

Previously

Playing with unocss Slimmer than tailwind, but still cool

2023-07-10

Next

Flushing DNS cache on OSX I keep forgetting

2023-07-16