mirror of
https://github.com/valitydev/salt.git
synced 2024-11-08 01:18:58 +00:00
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:
commit
f8a36bc155
2
LICENSE
2
LICENSE
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user