Merge remote-tracking branch 'upstream/2014.7' into merge-forward-2015.5

Conflicts:
    salt/cloud/clouds/ec2.py
    salt/utils/validate/ssh.py
This commit is contained in:
Colton Myers 2015-05-11 11:10:47 -06:00
commit f8a36bc155
4 changed files with 79 additions and 302 deletions

View File

@ -1,6 +1,6 @@
Salt - Remote execution system
Copyright 2014 SaltStack Team
Copyright 2014-2015 SaltStack Team
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View File

@ -103,6 +103,7 @@ import os
import logging
import socket
import pprint
import yaml
# Import generic libcloud functions
from salt.cloud.libcloudfuncs import * # pylint: disable=W0614,W0401
@ -863,6 +864,10 @@ def volume_create(name, size=100, snapshot=None, voltype=None, **kwargs):
return conn.volume_create(**create_kwargs)
# Command parity with EC2 and Azure
create_volume = volume_create
def volume_delete(name, **kwargs):
'''
Delete block storage device
@ -895,6 +900,73 @@ def volume_attach(name, server_name, device='/dev/xvdb', **kwargs):
)
# Command parity with EC2 and Azure
attach_volume = volume_attach
def volume_create_attach(name, call=None, **kwargs):
'''
Create and attach volumes to created node
'''
if call == 'function':
raise SaltCloudSystemExit(
'The create_attach_volumes action must be called with '
'-a or --action.'
)
if type(kwargs['volumes']) is str:
volumes = yaml.safe_load(kwargs['volumes'])
else:
volumes = kwargs['volumes']
ret = []
for volume in volumes:
created = False
volume_dict = {
'name': volume['name'],
}
if 'volume_id' in volume:
volume_dict['volume_id'] = volume['volume_id']
elif 'snapshot' in volume:
volume_dict['snapshot'] = volume['snapshot']
else:
volume_dict['size'] = volume['size']
if 'type' in volume:
volume_dict['type'] = volume['type']
if 'iops' in volume:
volume_dict['iops'] = volume['iops']
if 'id' not in volume_dict:
created_volume = create_volume(**volume_dict)
created = True
volume_dict.update(created_volume)
attach = attach_volume(
name=volume['name'],
server_name=name,
device=volume.get('device', None),
call='action'
)
if attach:
msg = (
'{0} attached to {1} (aka {2})'.format(
volume_dict['id'],
name,
volume_dict['name'],
)
)
log.info(msg)
ret.append(msg)
return ret
# Command parity with EC2 and Azure
create_attach_volumes = volume_create_attach
def volume_list(**kwargs):
'''
List block devices

View File

@ -381,6 +381,9 @@ def bootstrap(vm_, opts):
'file_map': salt.config.get_cloud_config_value(
'file_map', vm_, opts, default=None
),
'maxtries': salt.config.get_cloud_config_value(
'wait_for_passwd_maxtries', vm_, opts, default=15
),
}
# forward any info about possible ssh gateway to deploy script
# as some providers need also a 'gateway' configuration
@ -954,6 +957,7 @@ def deploy_script(host,
script_args=None,
script_env=None,
ssh_timeout=15,
maxtries=15,
make_syndic=False,
make_minion=True,
display_ssh_output=True,
@ -999,7 +1003,8 @@ def deploy_script(host,
password=password, key_filename=key_filename,
ssh_timeout=ssh_timeout,
display_ssh_output=display_ssh_output,
gateway=gateway, known_hosts_file=known_hosts_file):
gateway=gateway, known_hosts_file=known_hosts_file,
maxtries=maxtries):
log.debug(
'Logging into {0}:{1} as {2}'.format(

View File

@ -1,300 +0,0 @@
# -*- coding: utf-8 -*-
'''
Library for checking SSH options. This library primarily exists for purposes of
sanitizing options passed into SSH, than for checking to see whether those
options actually work.
'''
from __future__ import absolute_import
# Import python libs
import logging
# Import salt libs
import salt.utils.validate.net as suvn
import salt.utils.validate.user as suvu
log = logging.getLogger(__name__)
def is_clean(options):
'''
Returns True if the string of options passed in does not contain any
injections
'''
opts = options.split()
mode = None
for opt in opts:
if not mode:
if opt.startswith('-'):
# Is an option or group of options, the next string may be an
# argument for this option
for arg in opt.replace('-', ''):
if not allowed_opt(arg):
return False
if uses_path(opt):
mode = 'path'
continue
elif uses_bind(opt):
mode = 'bind'
continue
elif opt == 'I':
mode = 'pkcs11'
continue
elif opt == 'e':
mode = 'escape'
continue
elif opt == 'l':
mode = 'login'
continue
elif opt == 't':
mode = 'tun'
continue
elif opt == 'o':
mode = 'option'
continue
elif opt == 'O':
mode = 'control'
continue
if mode == 'path':
# TODO: Need to put a path checker in here
mode = None
elif mode == 'bind':
if not clean_bind(opt):
return False
mode = None
elif mode == 'pkcs11':
# TODO: Need code to validate PKCS#11 lib, check ssh(1)
pass
elif mode == 'escape':
# Just in case the escape character is in quotes
if len(opt) == 3:
if opt.startswith("'") and opt.endswith("'"):
opt = opt.replace("'", '')
if opt.startswith('"') and opt.endswith('"'):
opt = opt.replace('"', '')
if len(opt) > 1:
return False
mode = None
elif mode == 'login':
if not suvu.valid_username(opt):
return False
mode = None
elif mode == 'tun':
# TODO: What does a tun device look like?
pass
elif mode == 'control':
if not valid_control(opt):
return False
mode = None
elif mode == 'cypher':
if not allowed_cypher(opt) and not allowed_cyphers(opt):
return False
mode = None
elif mode == 'option':
# TODO: Lots of gaps to fill in here
if allowed_config(opt):
# This is an allowed configuration option, let's check to see
# if something specific should follow it
if opt not in ('Cypher', 'Ciphers'):
mode = 'cypher'
continue
def allowed_opt(opt):
'''
Returns True if an option is valid to the `ssh` command
'''
if opt in '1246ACDEFIKLMNOQRSTVWXYabcefgiklmnopqstvwxy':
return True
return False
def allowed_config(conf):
'''
Returns True if a config option is valid to OpenSSH
'''
if conf in (
# 'yes' or 'no'
'BatchMode',
'CheckHostIP',
'ChallengeResponseAuthentication',
'ClearAllForwardings',
'Compression',
'ControlPersist',
'EnableSSHKeysign',
'ExitOnForwardFailure',
'ForwardAgent',
'ForwardX11',
'ForwardX11Trusted',
'GatewayPorts',
'GSSAPIAuthentication',
'GSSAPIDelegateCredentials',
'HashKnownHosts',
'HostbasedAuthentication',
'IdentitiesOnly',
'KbdInteractiveAuthentication',
'NoHostAuthenticationForLocalhost',
'PasswordAuthentication',
'PermitLocalCommand',
'PubkeyAuthentication',
'RhostsRSAAuthentication',
'RSAAuthentication',
'TCPKeepAlive',
'UsePrivilegedPort',
'VisualHostKey',
# 'yes', 'no' or other
'ControlMaster', # 'yes', 'no', 'ask', 'auto', 'autoask'
'RequestTTY', # 'no', 'yes', 'auto', 'forward'
'StrictHostKeyChecking', # 'yes', 'no', 'ask'
'Tunnel', # 'yes', 'point-to-point', 'ethernet', 'no'
'VerifyHostKeyDNS', # 'yes', 'no', 'ask'
# int
'CompressionLevel',
'ConnectionAttempts',
'ConnectTimeout',
'NumberOfPasswordPrompts',
'Port',
'ServerAliveCountMax',
'ServerAliveInterval',
# One or more filenames
'ControlPath', # pathname
'GlobalKnownHostsFile', # one or more filenames
'IdentityFile', # filename
'UserKnownHostsFile', # one or more filenames
'XAuthLocation', # filename
# Some amount of pre-defined text
'AddressFamily', # 'any', 'inet', 'inet6'
'IPQoS', # 'af11', 'af12', 'af13', 'af21', 'af22', 'af23', 'af31',
# 'af32', 'af33', 'af41', 'af42', 'af43', 'cs0', 'cs1',
# 'cs2', 'cs3', 'cs4', 'cs5', 'cs6', 'cs7', 'ef', 'lowdelay',
# 'throughput', 'reliability'
'KbdInteractiveDevices', # 'bsdauth', 'pam', and/or 'skey'
'LogLevel', # 'QUIET', 'FATAL', 'ERROR', 'INFO', 'VERBOSE', 'DEBUG',
# 'DEBUG1', 'DEBUG2', 'DEBUG3'
'Protocol', # '2', '1'
# Host, port, IP or device
'BindAddress', # IP addrs
'DynamicForward', # bind_address:port
'Host', # Host names, IP addrs
'HostName', # hostname, IP addr, or '%h'
'LocalForward', # [bind_address:]port host :hostport
'RemoteForward', # [bind_address:]port host :hostport
'TunnelDevice', # local_tun[:remote_tun]
# Crypto
'Cipher', # DONE
'Ciphers', # DONE
'HostKeyAlgorithms', # TODO: Find out what is valid here
'KexAlgorithms', # TODO: Find out what is valid here
'MACs', # TODO: Find out what is valid here
'PKCS11Provider', # TODO: Find out what is valid here
'PreferredAuthentications', # TODO: Find out what is valid here
# Username
'User', # username
'EscapeChar', # single character + letter, or 'none' TODO, check this
'ForwardX11Timeout', # TIME FORMAT
'HostKeyAlias', # TODO: Find out what is valid here
'IgnoreUnknown', # TODO: Find out what is not invalid here
'LocalCommand', # TODO: Find out how to validate this
'ProxyCommand', # TODO: Find out what is valid here
'RekeyLimit', # '<x>[K|M|G]', optionally followed by int
'SendEnv', # TODO: Find out what is valid here
):
return True
return False
def allowed_cypher(spec):
'''
Returns True if the cypher spec is allowed (SSH 1)
'''
if spec in ('3des', 'blowfish', 'des'):
return True
return False
def allowed_cyphers(spec):
'''
Returns True if the cypher spec is allowed (SSH 2)
'''
if spec in (
'3des-cbc',
'aes128-cbc',
'aes192-cbc',
'aes256-cbc',
'aes128-ctr',
'aes192-ctr',
'aes256-ctr',
'aes128-gcm@openssh.com',
'aes256-gcm@openssh.com',
'arcfour128',
'arcfour256',
'arcfour',
'blowfish-cbc',
'cast128-cbc',
):
return True
return False
def uses_path(opt):
'''
Returns True if an option makes use of a file path
'''
if opt in 'EFiS':
return True
return False
def uses_bind(opt):
'''
Returns True if an option makes use of an Internet address
'''
if opt in 'bDLRW':
return True
return False
def clean_bind(addr):
'''
Splits a bind address on ':', and checks each part to make sure it is either
a valid port (an int) or a valid IPv4 address. This is more for sanitization
purposes than for validity checking.
SSH options that will use this are -b, -D, -L, -R and -W
'''
comps = addr.split()
for part in comps:
try:
int(part)
if not suvn.ipv4_addr(part):
return False
except ValueError:
return False
return True
def valid_control(control):
'''
Checks to see if a multiplex control is valid
'''
if control in ('check', 'forward', 'cancel', 'exit', 'stop'):
return True
return False
# ssh [-1246AaCfgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]
# [-D[bind_address :]port] [-E log_file] [-e escape_char] [-F configfile]
# [-I pkcs11] [-i identity_file]
# [-L[bind_address :]port :host :hostport] [-l login_name]
# [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]
# [-R[bind_address :]port :host :hostport] [-S ctl_path] [-W host :port]
# [-w local_tun[:remote_tun] [user@]hostname [command]
# ssh -Q protocol_feature