parent
fb494bc32a
commit
4a0103a88d
|
@ -30,7 +30,7 @@ pem==17.1.0
|
||||||
raven[flask]==6.1.0
|
raven[flask]==6.1.0
|
||||||
jinja2==2.9.6
|
jinja2==2.9.6
|
||||||
# pyldap==2.4.37 # cannot be installed on rtd - required by ldap auth provider
|
# pyldap==2.4.37 # cannot be installed on rtd - required by ldap auth provider
|
||||||
paramiko==2.2.1 # required for lemur_linuxdst plugin
|
paramiko==2.4.1 # required for the SFTP destination plugin
|
||||||
sphinx
|
sphinx
|
||||||
sphinxcontrib-httpdomain
|
sphinxcontrib-httpdomain
|
||||||
sphinx-rtd-theme
|
sphinx-rtd-theme
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
try:
|
||||||
|
VERSION = __import__('pkg_resources') \
|
||||||
|
.get_distribution(__name__).version
|
||||||
|
except Exception as e:
|
||||||
|
VERSION = 'unknown'
|
|
@ -0,0 +1,179 @@
|
||||||
|
"""
|
||||||
|
.. module: lemur.plugins.lemur_sftp.plugin
|
||||||
|
:platform: Unix
|
||||||
|
:synopsis: Allow the uploading of certificates to SFTP.
|
||||||
|
:copyright: (c) 2015 by Netflix Inc., see AUTHORS for more
|
||||||
|
:license: Apache, see LICENSE for more details.
|
||||||
|
|
||||||
|
Allow the uploading of certificates to SFTP.
|
||||||
|
|
||||||
|
NGINX and Apache export formats are supported.
|
||||||
|
|
||||||
|
Password and RSA private key are supported.
|
||||||
|
Passwords are not encrypted and stored as a plain text.
|
||||||
|
|
||||||
|
Detailed logging when Lemur debug mode is enabled.
|
||||||
|
|
||||||
|
.. moduleauthor:: Dmitry Zykov https://github.com/DmitryZykov
|
||||||
|
"""
|
||||||
|
|
||||||
|
import paramiko
|
||||||
|
|
||||||
|
from flask import current_app
|
||||||
|
from lemur.plugins import lemur_sftp
|
||||||
|
from lemur.common.defaults import common_name
|
||||||
|
from lemur.common.utils import parse_certificate
|
||||||
|
from lemur.plugins.bases import DestinationPlugin
|
||||||
|
|
||||||
|
|
||||||
|
class SFTPDestinationPlugin(DestinationPlugin):
|
||||||
|
title = 'SFTP'
|
||||||
|
slug = 'sftp-destination'
|
||||||
|
description = 'Allow the uploading of certificates to SFTP'
|
||||||
|
version = lemur_sftp.VERSION
|
||||||
|
|
||||||
|
author = 'Dmitry Zykov'
|
||||||
|
author_url = 'https://github.com/DmitryZykov'
|
||||||
|
|
||||||
|
options = [
|
||||||
|
{
|
||||||
|
'name': 'host',
|
||||||
|
'type': 'str',
|
||||||
|
'required': True,
|
||||||
|
'helpMessage': 'The SFTP host.'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'port',
|
||||||
|
'type': 'int',
|
||||||
|
'required': True,
|
||||||
|
'helpMessage': 'The SFTP port, default is 22.',
|
||||||
|
'validation': '^(6553[0-5]|655[0-2][0-9]\d|65[0-4](\d){2}|6[0-4](\d){3}|[1-5](\d){4}|[1-9](\d){0,3})',
|
||||||
|
'default': '22'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'user',
|
||||||
|
'type': 'str',
|
||||||
|
'required': True,
|
||||||
|
'helpMessage': 'The SFTP user. Default is root.',
|
||||||
|
'default': 'root'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'password',
|
||||||
|
'type': 'str',
|
||||||
|
'required': False,
|
||||||
|
'helpMessage': 'The SFTP password (optional when the private key is used).',
|
||||||
|
'default': None
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'privateKeyPath',
|
||||||
|
'type': 'str',
|
||||||
|
'required': False,
|
||||||
|
'helpMessage': 'The path to the RSA private key on the Lemur server (optional).',
|
||||||
|
'default': None
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'privateKeyPass',
|
||||||
|
'type': 'str',
|
||||||
|
'required': False,
|
||||||
|
'helpMessage': 'The password for the encrypted RSA private key (optional).',
|
||||||
|
'default': None
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'destinationPath',
|
||||||
|
'type': 'str',
|
||||||
|
'required': True,
|
||||||
|
'helpMessage': 'The SFTP path where certificates will be uploaded.',
|
||||||
|
'default': '/etc/nginx/certs'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'exportFormat',
|
||||||
|
'required': True,
|
||||||
|
'value': 'NGINX',
|
||||||
|
'helpMessage': 'The export format for certificates.',
|
||||||
|
'type': 'select',
|
||||||
|
'available': [
|
||||||
|
'NGINX',
|
||||||
|
'Apache'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
def upload(self, name, body, private_key, cert_chain, options, **kwargs):
|
||||||
|
|
||||||
|
current_app.logger.debug('SFTP destination plugin is started')
|
||||||
|
|
||||||
|
cn = common_name(parse_certificate(body))
|
||||||
|
host = self.get_option('host', options)
|
||||||
|
port = self.get_option('port', options)
|
||||||
|
user = self.get_option('user', options)
|
||||||
|
password = self.get_option('password', options)
|
||||||
|
ssh_priv_key = self.get_option('privateKeyPath', options)
|
||||||
|
ssh_priv_key_pass = self.get_option('privateKeyPass', options)
|
||||||
|
dst_path = self.get_option('destinationPath', options)
|
||||||
|
export_format = self.get_option('exportFormat', options)
|
||||||
|
|
||||||
|
# prepare files for upload
|
||||||
|
files = {cn + '.key': private_key,
|
||||||
|
cn + '.pem': body}
|
||||||
|
|
||||||
|
if cert_chain:
|
||||||
|
if export_format == 'NGINX':
|
||||||
|
# assemble body + chain in the single file
|
||||||
|
files[cn + '.pem'] += '\n' + cert_chain
|
||||||
|
|
||||||
|
elif export_format == 'Apache':
|
||||||
|
# store chain in the separate file
|
||||||
|
files[cn + '.ca.bundle.pem'] = cert_chain
|
||||||
|
|
||||||
|
# upload files
|
||||||
|
try:
|
||||||
|
current_app.logger.debug('Connecting to {0}@{1}:{2}'.format(user, host, port))
|
||||||
|
ssh = paramiko.SSHClient()
|
||||||
|
|
||||||
|
# allow connection to the new unknown host
|
||||||
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
|
||||||
|
# open the ssh connection
|
||||||
|
if password:
|
||||||
|
current_app.logger.debug('Using password')
|
||||||
|
ssh.connect(host, username=user, port=port, password=password)
|
||||||
|
elif ssh_priv_key:
|
||||||
|
current_app.logger.debug('Using RSA private key')
|
||||||
|
pkey = paramiko.RSAKey.from_private_key_file(ssh_priv_key, ssh_priv_key_pass)
|
||||||
|
ssh.connect(host, username=user, port=port, pkey=pkey)
|
||||||
|
else:
|
||||||
|
current_app.logger.error("No password or private key provided. Can't proceed")
|
||||||
|
raise paramiko.ssh_exception.AuthenticationException
|
||||||
|
|
||||||
|
# open the sftp session inside the ssh connection
|
||||||
|
sftp = ssh.open_sftp()
|
||||||
|
|
||||||
|
# make sure that the destination path exist
|
||||||
|
try:
|
||||||
|
current_app.logger.debug('Creating {0}'.format(dst_path))
|
||||||
|
sftp.mkdir(dst_path)
|
||||||
|
except IOError:
|
||||||
|
current_app.logger.debug('{0} already exist, resuming'.format(dst_path))
|
||||||
|
try:
|
||||||
|
dst_path_cn = dst_path + '/' + cn
|
||||||
|
current_app.logger.debug('Creating {0}'.format(dst_path_cn))
|
||||||
|
sftp.mkdir(dst_path_cn)
|
||||||
|
except IOError:
|
||||||
|
current_app.logger.debug('{0} already exist, resuming'.format(dst_path_cn))
|
||||||
|
|
||||||
|
# upload certificate files to the sftp destination
|
||||||
|
for filename, data in files.items():
|
||||||
|
current_app.logger.debug('Uploading {0} to {1}'.format(filename, dst_path_cn))
|
||||||
|
with sftp.open(dst_path_cn + '/' + filename, 'w') as f:
|
||||||
|
f.write(data)
|
||||||
|
# read only for owner, -r--------
|
||||||
|
sftp.chmod(dst_path_cn + '/' + filename, 0o400)
|
||||||
|
|
||||||
|
ssh.close()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
current_app.logger.error('ERROR in {0}: {1}'.format(e.__class__, e))
|
||||||
|
try:
|
||||||
|
ssh.close()
|
||||||
|
except BaseException:
|
||||||
|
pass
|
|
@ -42,7 +42,7 @@
|
||||||
{{ item.name | titleCase }}
|
{{ item.name | titleCase }}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input name="sub" ng-if="item.type == 'int'" type="number" ng-pattern="/^[0-9]{12,12}$/"
|
<input name="sub" ng-if="item.type == 'int'" type="number" ng-pattern="item.validation?item.validation:'^[0-9]+$'"
|
||||||
class="form-control" ng-model="item.value"/>
|
class="form-control" ng-model="item.value"/>
|
||||||
<select name="sub" ng-if="item.type == 'select'" class="form-control" ng-options="i for i in item.available"
|
<select name="sub" ng-if="item.type == 'select'" class="form-control" ng-options="i for i in item.available"
|
||||||
ng-model="item.value"></select>
|
ng-model="item.value"></select>
|
||||||
|
@ -61,7 +61,7 @@
|
||||||
{{ item.name | titleCase }}
|
{{ item.name | titleCase }}
|
||||||
</label>
|
</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<input name="sub" ng-if="item.type == 'int'" type="number" ng-pattern="/^[0-9]{12,12}$/"
|
<input name="sub" ng-if="item.type == 'int'" type="number" ng-pattern="item.validation?item.validation:'^[0-9]+$'"
|
||||||
class="form-control" ng-model="item.value"/>
|
class="form-control" ng-model="item.value"/>
|
||||||
<select name="sub" ng-if="item.type == 'select'" class="form-control"
|
<select name="sub" ng-if="item.type == 'select'" class="form-control"
|
||||||
ng-options="i for i in item.available" ng-model="item.value"></select>
|
ng-options="i for i in item.available" ng-model="item.value"></select>
|
||||||
|
|
|
@ -19,7 +19,7 @@ lockfile
|
||||||
marshmallow-sqlalchemy
|
marshmallow-sqlalchemy
|
||||||
marshmallow
|
marshmallow
|
||||||
ndg-httpsclient
|
ndg-httpsclient
|
||||||
paramiko # required for lemur_linuxdst plugin
|
paramiko # required for the SFTP destination plugin
|
||||||
pem
|
pem
|
||||||
psycopg2
|
psycopg2
|
||||||
pyjwt
|
pyjwt
|
||||||
|
|
3
setup.py
3
setup.py
|
@ -147,7 +147,8 @@ setup(
|
||||||
'digicert_issuer = lemur.plugins.lemur_digicert.plugin:DigiCertIssuerPlugin',
|
'digicert_issuer = lemur.plugins.lemur_digicert.plugin:DigiCertIssuerPlugin',
|
||||||
'digicert_cis_issuer = lemur.plugins.lemur_digicert.plugin:DigiCertCISIssuerPlugin',
|
'digicert_cis_issuer = lemur.plugins.lemur_digicert.plugin:DigiCertCISIssuerPlugin',
|
||||||
'digicert_cis_source = lemur.plugins.lemur_digicert.plugin:DigiCertCISSourcePlugin',
|
'digicert_cis_source = lemur.plugins.lemur_digicert.plugin:DigiCertCISSourcePlugin',
|
||||||
'csr_export = lemur.plugins.lemur_csr.plugin:CSRExportPlugin'
|
'csr_export = lemur.plugins.lemur_csr.plugin:CSRExportPlugin',
|
||||||
|
'sftp_destination = lemur.plugins.lemur_sftp.plugin:SFTPDestinationPlugin'
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
classifiers=[
|
classifiers=[
|
||||||
|
|
Loading…
Reference in New Issue