Class: Sshakery::AuthKeys
- Inherits:
-
Object
- Object
- Sshakery::AuthKeys
- Defined in:
- lib/sshakery/auth_keys.rb
Overview
for reading from and writing to an authorized_keys file.
Constant Summary collapse
- STATE_ATTRIBUTES =
Attributes that help define the current state of the key
[ :errors, :raw_line, :saved ]
- KEY_ATTRIBUTES =
:category: Instance Attributes Attributes that are read from / written to authorized_keys files. They are listed in the order that they should appear in the authorized_keys file
- command
-
(string) -> A forced shell command to run
key.command = 'ls' - permitopen
-
(string) -> TODO: document
- tunnel
-
(integer) -> Port forwarding
key.tunnel = 5950 - from
-
(string) -> IP/host address required for client
- environment
-
(array) -> Array of strings to set shell environment variables Not tested
key.environment.push 'RAILS_ENV=production' - no_agent_forwarding
- (boolean) -> Don’t allow ssh agent authentication forwarding
- no_port_forwarding
-
(boolean) -> Don’t allow port forwarding
- no_pty
-
(boolean) -> Don’t create terminal for client
- no_user_rc
-
(boolean) -> Don’t process user rc files
- no_X11_forwarding
-
(boolean) -> Don’t allow X11 forwarding. Please note the uppercase ‘X’ in X11
- key_type
-
(string) -> Type of key. ‘ssh-dsa’ or ‘ssh-rsa’
- key_data
-
(string) -> A Base64 string of public key data
- note
-
(string) -> A note about a key. No spaces allowed
[ :command, :permitopen, :tunnel, :from, :environment, :no_agent_forwarding, :no_port_forwarding, :no_pty, :no_user_rc, :no_X11_forwarding, :key_type, :key_data, :note ]
- ATTRIBUTES =
A list of all attributes a key has
STATE_ATTRIBUTES+KEY_ATTRIBUTES
- DEFAULTS =
Attribute default values
{ :errors => [] }.freeze
- BOOL_ATTRIBUTES =
Boolean attributes
[ :no_agent_forwarding, :no_port_forwarding, :no_pty, :no_user_rc, :no_X11_forwarding ]
- STR_ATTRIBUTES =
STR_ATTRIBUTES is a list of attributes that are strings
-
gen_raw_linewill return a line containing the contents of these variables (if any)
-
[ :key_type, :key_data, :note ]
- ARR_STR_ATTRIBUTES =
each is equal to a joined string of their values
-
gen_raw_linewill return a line containing the contents
of these variables (if any)
-
[ :environment ]
- SUB_STR_ATTRIBUTES =
add string and substitute attr value
-
gen_raw_linewill return a line containing the contents of these variables (if any)
-
{ :command=>'command="%sub%"', :permitopen=>'permitopen="%sub%"', :tunnel=>'tunnel="%sub%"', :from=>'from="%sub%"' }
- TYPE_REGEX =
A regex for matching ssh key types imported from a pub key file
/ssh-dss|ssh-rsa/- B64_REGEX =
A regex for matching base 64 strings
/[A-Za-z0-9\/\+]+={0,3}/- OPTS_REGEX =
The regex used for reading individual lines/records in an authorized_keys file
{ :key_type=> /(#{TYPE_REGEX}) (?:#{B64_REGEX})/, :key_data=> /(?:#{TYPE_REGEX}) (#{B64_REGEX})/, :note=>/([A-Za-z0-9_\/\+@]+)\s*$/, :command=>/command="([^"]+)"(?: |,)/, :environment=>/([A-Z0-9]+=[^\s]+)(?: |,)/, :from=>/from="([^"])"(?: |,)/, :no_agent_forwarding=>/(no-agent-forwarding)(?: |,)/, :no_port_forwarding=>/(no-port-forwarding)(?: |,)/, :no_pty=>/(no-pty)(?: |,)/, :no_user_rc=>/(no-user-rc)(?: |,)/, :no_X11_forwarding=>/(no-X11-forwarding)(?: |,)/, :permitopen=>/permitopen="([a-z0-9.]+:[\d]+)"(?: |,)/, :tunnel=>/tunnel="(\d+)"(?: |,)/ }
- ERRORS =
This is a list of attribute errors
{ :data_modulus=> {:key_data=>'public key length is not a modulus of 4'}, :data_short => {:key_data=>'public key is too short'}, :data_long => {:key_data=>'public key is too long'}, :data_char => {:key_data=>'public key contains invalid base64 characters'}, :data_nil => {:key_data=>'public key is missing'}, :type_nil => {:key_type=>'missing key type'}, :bool => 'bad value for boolean field' }
Class Attribute Summary collapse
-
.path ⇒ Object
Path to authorized_keys file.
-
.temp_path ⇒ Object
Path to lock file (cannot be the same as key file).
Class Method Summary collapse
-
.all ⇒ Object
Return array of all keys.
-
.destroy(auth_key) ⇒ Object
Delete a key.
-
.find_all_by(fields = {}, with_regex = false) ⇒ Object
Search the authorized_keys file for keys containing a field with a specific value .
-
.write(auth_key, destroy = false) ⇒ Object
Create, update or delete the contents of a key.
Instance Method Summary collapse
-
#all_no=(val) ⇒ Object
Set all boolean attributes at the same time -
val-> (boolean). -
#attr ⇒ Object
set attributes.
-
#destroy ⇒ Object
Remove a key from the file.
-
#gen_raw_line ⇒ Object
Construct the line that will be written to file.
-
#initialize(args = {}) ⇒ AuthKeys
constructor
Create a new key object.
-
#load_pubkey ⇒ Object
Instantiate key based on pubkey file only set key_data,key_type and note attributes.
-
#load_raw_line(opts = OPTS_REGEX.keys) ⇒ Object
Instantiate key object based on contents of raw_line.
-
#raw_getter(field) ⇒ Object
Return the string representation of what the attribute will look like in the authorized_keys file.
-
#raw_setter(xfield, pattern) ⇒ Object
set attribute (field) obtained from matching pattern in raw_line.
-
#save ⇒ Object
Add a key to authorized keys file if it passes validation.
-
#save! ⇒ Object
Add a key to authorized keys file if it passes validation, otherwise raise an error if save doesn’t pass validations.
-
#saved? ⇒ Boolean
Has the key already been saved to file?.
-
#valid? ⇒ Boolean
Validate the key If the validations fail the reason for the failure will be found in @errors.
Constructor Details
#initialize(args = {}) ⇒ AuthKeys
Create a new key object
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
# File 'lib/sshakery/auth_keys.rb', line 254 def initialize(args={}) ATTRIBUTES.each do |attr| instance_variable_set("@#{attr}", args.has_key?( attr ) ? args[attr] : nil ) end self.raw_line = args[:raw_pubkey] || args[:raw_line] if args.has_key? :raw_pubkey self.load_pubkey return end unless self.raw_line.nil? self.load_raw_line end end |
Class Attribute Details
.path ⇒ Object
Path to authorized_keys file
169 170 171 |
# File 'lib/sshakery/auth_keys.rb', line 169 def path @path end |
.temp_path ⇒ Object
Path to lock file (cannot be the same as key file)
172 173 174 |
# File 'lib/sshakery/auth_keys.rb', line 172 def temp_path @temp_path end |
Class Method Details
.all ⇒ Object
Return array of all keys
244 245 246 247 248 249 250 |
# File 'lib/sshakery/auth_keys.rb', line 244 def self.all result = [] File.readlines(self.path).each do |line| result.push( self.new(:raw_line => line )) end return result end |
.destroy(auth_key) ⇒ Object
Delete a key
215 216 217 |
# File 'lib/sshakery/auth_keys.rb', line 215 def self.destroy(auth_key) self.write auth_key, destroy=true end |
.find_all_by(fields = {}, with_regex = false) ⇒ Object
Search the authorized_keys file for keys containing a field with a specific value
Args :
-
fields-> A hash of key value pairs to match against -
with_regex-> Use regex matching (default=false)
Returns :
-
Array-> An array of keys that matched
Usage :
keys = Sshakery.load '/home/someuser/.ssh/authorized_keys'
foo_keys = keys.find_all_by :note=>'foo'
fc_keys = keys.find_all_by :command=>'ls', :no_X11_forwarding=>true
rsa_keys = keys.find_all_by :key_data=>'ssh-rsa'
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/sshakery/auth_keys.rb', line 191 def self.find_all_by(fields ={}, with_regex=false) result = [] self.all.each do |auth_key| all_matched = true fields.each do |field,value| if with_regex && auth_key.send(field).to_s.match(value.to_s) next elsif auth_key.send(field) == value next end all_matched = false end result.push auth_key if all_matched end return result end |
.write(auth_key, destroy = false) ⇒ Object
Create, update or delete the contents of a key
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/sshakery/auth_keys.rb', line 221 def self.write(auth_key, destroy=false) lines = [] FsUtils.atomic_lock(:path=>self.path) do |f| f.each_line do |line| key=self.new(:raw_line => line ) if key.key_data == auth_key.key_data lines.push auth_key.gen_raw_line if destroy==false else lines.push line end end f.rewind f.truncate(0) lines.each do |line| f.puts line end end return true end |
Instance Method Details
#all_no=(val) ⇒ Object
Set all boolean attributes at the same time
-
val-> (boolean)
326 327 328 329 330 |
# File 'lib/sshakery/auth_keys.rb', line 326 def all_no=(val) BOOL_ATTRIBUTES.each do |attr| self.instance_variable_set("@#{attr}",val) end end |
#attr ⇒ Object
set attributes
82 83 84 |
# File 'lib/sshakery/auth_keys.rb', line 82 ATTRIBUTES.each do |attr| #:nodoc: attr_accessor attr end |
#destroy ⇒ Object
Remove a key from the file
Returns :
-
Boolean-> Destroy success status
393 394 395 396 |
# File 'lib/sshakery/auth_keys.rb', line 393 def destroy return false if not self.saved? return self.class.destroy self end |
#gen_raw_line ⇒ Object
Construct the line that will be written to file
Returns :
-
String-> Line that will be written to file
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'lib/sshakery/auth_keys.rb', line 403 def gen_raw_line return nil unless self.valid? line = '' data = [] SUB_STR_ATTRIBUTES.each do |field,field_regex| val = self.raw_getter field data.push val if val.nil? == false end unless data.empty? line = "#{data.join ' ,'}" end data = [] BOOL_ATTRIBUTES.each do |field| val = self.raw_getter field data.push val if val.nil? == false end unless data.empty? if line == '' line += "#{data.join ','} " else line += ",#{data.join ','} " end end data = [] ARR_STR_ATTRIBUTES.each do |field| val = self.raw_getter field data.push val if val.nil? == false end unless data.empty? if line == '' line += "#{data.join ','} " else line += ", #{data.join ','} " end end data = [] STR_ATTRIBUTES.each do |field| val = self.raw_getter field data.push val if val.nil? == false end line += data.join ' ' return line end |
#load_pubkey ⇒ Object
Instantiate key based on pubkey file only set key_data,key_type and note attributes
274 275 276 277 278 279 280 |
# File 'lib/sshakery/auth_keys.rb', line 274 def load_pubkey #filter line data load_raw_line([:key_data,:key_type,:note]) # sanitize old raw line self.raw_line = self.gen_raw_line end |
#load_raw_line(opts = OPTS_REGEX.keys) ⇒ Object
Instantiate key object based on contents of raw_line
284 285 286 287 288 289 290 291 |
# File 'lib/sshakery/auth_keys.rb', line 284 def load_raw_line opts = OPTS_REGEX.keys self.raw_line.chomp! opts.each do |xfield| pattern = OPTS_REGEX[xfield] did_set = raw_setter xfield, pattern #puts did_set end end |
#raw_getter(field) ⇒ Object
Return the string representation of what the attribute will look like in the authorized_keys file
Args :
-
field-> Attribute name
Returns :
-
string-> A string representation of the attribute
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 |
# File 'lib/sshakery/auth_keys.rb', line 340 def raw_getter field val = self.instance_variable_get("@#{field}") return nil if val.nil? == true || val == false if BOOL_ATTRIBUTES.include? field return field.to_s.gsub '_', '-' end if STR_ATTRIBUTES.include? field return val end if ARR_STR_ATTRIBUTES.include? field && val.empty? == false return val.join ' ' end if SUB_STR_ATTRIBUTES.include? field return SUB_STR_ATTRIBUTES[field].sub '%sub%', val end end |
#raw_setter(xfield, pattern) ⇒ Object
set attribute (field) obtained from matching pattern in raw_line
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/sshakery/auth_keys.rb', line 296 def raw_setter xfield,pattern field = "@#{xfield}" m = self.raw_line.match pattern return false if m.nil? #p "#{field} => #{m.inspect}" if BOOL_ATTRIBUTES.include? xfield self.instance_variable_set(field, true) return true end if STR_ATTRIBUTES.include? xfield self.instance_variable_set(field, m[1]) return true end if ARR_STR_ATTRIBUTES.include? xfield self.instance_variable_set(field, m.to_a) return true end if SUB_STR_ATTRIBUTES.include? xfield self.instance_variable_set(field, m[1]) return true end return false end |
#save ⇒ Object
Add a key to authorized keys file if it passes validation. If the validations fail the reason for the failure will be found in @errors.
Returns :
-
boolean-> True if save was successful, otherwise returns false
368 369 370 371 |
# File 'lib/sshakery/auth_keys.rb', line 368 def save return false if not self.valid? return self.class.write(self) end |
#save! ⇒ Object
Add a key to authorized keys file if it passes validation, otherwise raise an error if save doesn’t pass validations
Returns :
-
boolean-> True if save was successful, otherwise raises error
Raises :
-
Error-> Sshakery::Errors::RecordInvalid ( Key did not pass validations )
382 383 384 385 386 |
# File 'lib/sshakery/auth_keys.rb', line 382 def save! unless self.save raise Sshakery::Errors::RecordInvalid.new 'Errors preventing save' end end |
#saved? ⇒ Boolean
Has the key already been saved to file?
Returns :
-
Boolean-> True if has been saved before, otherwise false
501 502 503 504 |
# File 'lib/sshakery/auth_keys.rb', line 501 def saved? return false if not self.valid? return self.saved end |
#valid? ⇒ Boolean
Validate the key If the validations fail the reason for the failure will be found in @errors.
Returns :
-
Boolean-> True if valid, otherwise false
457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 |
# File 'lib/sshakery/auth_keys.rb', line 457 def valid? self.errors = [] BOOL_ATTRIBUTES.each do |field| val = self.raw_getter field unless val.nil? == true || val == true || val == false self.errors.push field=>ERRORS[:bool] end end if self.key_data.nil? self.errors.push ERRORS[:data_nil] return false end if self.key_type.nil? self.errors.push ERRORS[:type_nil] return false end if not self.key_data.match "^#{B64_REGEX}$" self.errors.push ERRORS[:data_char] end if self.key_data.size < 30 self.errors.push ERRORS[:data_short] end if self.key_data.size > 1000 self.errors.push ERRORS[:data_long] end if self.key_data.size % 4 != 0 self.errors.push ERRORS[:data_modulus] end return self.errors.empty? end |