Class: Xshellz::Keystore

Inherits:
Object
  • Object
show all
Defined in:
lib/xshellz/keystore.rb

Overview

Local on-disk private-key store backing Sandbox.get_or_create.

One file per sandbox name (sanitized), OpenSSH private key inside, 0600 permissions, in ~/.xshellz/keys/ by default.

Security note: keys are stored in PLAINTEXT on disk, protected only by file permissions (0600, directory 0700). Anyone who can read the file can SSH into the sandbox. Delete the file (+delete+) and kill the box to revoke; pass keystore: false to get_or_create to keep keys purely in-memory instead.

Constant Summary collapse

SAFE_NAME =

Characters allowed to survive sanitization into a key file name.

/[^A-Za-z0-9._-]+/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(dir: self.class.default_dir) ⇒ Keystore

Returns a new instance of Keystore.



35
36
37
# File 'lib/xshellz/keystore.rb', line 35

def initialize(dir: self.class.default_dir)
  @dir = File.expand_path(dir)
end

Instance Attribute Details

#dirObject (readonly)

Returns the value of attribute dir.



20
21
22
# File 'lib/xshellz/keystore.rb', line 20

def dir
  @dir
end

Class Method Details

.default_dirObject



22
23
24
# File 'lib/xshellz/keystore.rb', line 22

def self.default_dir
  File.join(Dir.home, ".xshellz", "keys")
end

.sanitize(name) ⇒ Object

Turn a sandbox name into a safe file basename (no separators, no leading dots - so no traversal and no hidden files).

Raises:

  • (ArgumentError)


28
29
30
31
32
33
# File 'lib/xshellz/keystore.rb', line 28

def self.sanitize(name)
  cleaned = name.to_s.gsub(SAFE_NAME, "_").sub(/\A\.+/, "_")
  raise ArgumentError, "Cannot derive a key file name from #{name.inspect}" if cleaned.empty?

  cleaned
end

Instance Method Details

#delete(name) ⇒ Object

Remove the stored key for name. Returns true when a file was deleted.



60
61
62
63
64
65
66
# File 'lib/xshellz/keystore.rb', line 60

def delete(name)
  path = path_for(name)
  return false unless File.file?(path)

  File.delete(path)
  true
end

#load(name) ⇒ Object

The stored key for name, or nil when none exists.



54
55
56
57
# File 'lib/xshellz/keystore.rb', line 54

def load(name)
  path = path_for(name)
  File.file?(path) ? File.read(path) : nil
end

#path_for(name) ⇒ Object

The absolute path where +name+'s key lives (whether or not it exists).



40
41
42
# File 'lib/xshellz/keystore.rb', line 40

def path_for(name)
  File.join(dir, "#{self.class.sanitize(name)}.key")
end

#save(name, private_key_openssh) ⇒ Object

Persist an OpenSSH private key for name (0600). Returns the path.



45
46
47
48
49
50
51
# File 'lib/xshellz/keystore.rb', line 45

def save(name, private_key_openssh)
  FileUtils.mkdir_p(dir, mode: 0o700)
  path = path_for(name)
  File.write(path, private_key_openssh, perm: 0o600)
  File.chmod(0o600, path)
  path
end