Merge "Use common ApacheSSLContext"
This commit is contained in:
commit
c59894df27
|
@ -29,6 +29,7 @@ from charmhelpers.fetch import (
|
|||
filter_installed_packages,
|
||||
)
|
||||
from charmhelpers.core.hookenv import (
|
||||
NoNetworkBinding,
|
||||
config,
|
||||
is_relation_made,
|
||||
local_unit,
|
||||
|
@ -868,7 +869,7 @@ class ApacheSSLContext(OSContextGenerator):
|
|||
addr = network_get_primary_address(
|
||||
ADDRESS_MAP[net_type]['binding']
|
||||
)
|
||||
except NotImplementedError:
|
||||
except (NotImplementedError, NoNetworkBinding):
|
||||
addr = fallback
|
||||
|
||||
endpoint = resolve_address(net_type)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
# limitations under the License.
|
||||
|
||||
from charmhelpers.core.hookenv import (
|
||||
NoNetworkBinding,
|
||||
config,
|
||||
unit_get,
|
||||
service_name,
|
||||
|
@ -175,7 +176,7 @@ def resolve_address(endpoint_type=PUBLIC, override=True):
|
|||
# configuration is not in use
|
||||
try:
|
||||
resolved_address = network_get_primary_address(binding)
|
||||
except NotImplementedError:
|
||||
except (NotImplementedError, NoNetworkBinding):
|
||||
resolved_address = fallback_addr
|
||||
|
||||
if resolved_address is None:
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
|
||||
# vim: set ts=4:et
|
||||
|
||||
from base64 import b64decode
|
||||
import os
|
||||
import json
|
||||
|
||||
from charmhelpers.core.hookenv import (
|
||||
|
@ -29,14 +27,13 @@ from charmhelpers.core.hookenv import (
|
|||
ERROR,
|
||||
)
|
||||
from charmhelpers.core.strutils import bool_from_string
|
||||
from charmhelpers.contrib.openstack import context
|
||||
from charmhelpers.contrib.openstack.context import (
|
||||
OSContextGenerator,
|
||||
context_complete
|
||||
)
|
||||
from charmhelpers.contrib.hahelpers.apache import (
|
||||
get_ca_cert,
|
||||
get_cert,
|
||||
install_ca_cert,
|
||||
)
|
||||
from charmhelpers.contrib.network.ip import (
|
||||
get_ipv6_addr,
|
||||
|
@ -234,41 +231,14 @@ class ApacheContext(OSContextGenerator):
|
|||
return ctxt
|
||||
|
||||
|
||||
class ApacheSSLContext(OSContextGenerator):
|
||||
class ApacheSSLContext(context.ApacheSSLContext):
|
||||
|
||||
interfaces = ['https']
|
||||
external_ports = [443]
|
||||
service_namespace = 'horizon'
|
||||
|
||||
def __call__(self):
|
||||
''' Grab cert and key from configuration for SSL config '''
|
||||
ctxt = {'ssl_configured': False}
|
||||
use_local_ca = True
|
||||
for rid in relation_ids('certificates'):
|
||||
if related_units(rid):
|
||||
use_local_ca = False
|
||||
|
||||
if use_local_ca:
|
||||
ca_cert = get_ca_cert()
|
||||
if not ca_cert:
|
||||
return ctxt
|
||||
install_ca_cert(b64decode(ca_cert))
|
||||
|
||||
ssl_cert, ssl_key = get_cert()
|
||||
if all([ssl_cert, ssl_key]):
|
||||
with open('/etc/ssl/certs/dashboard.cert', 'wb') as cert_out:
|
||||
cert_out.write(b64decode(ssl_cert))
|
||||
with open('/etc/ssl/private/dashboard.key', 'wb') as key_out:
|
||||
key_out.write(b64decode(ssl_key))
|
||||
os.chmod('/etc/ssl/private/dashboard.key', 0o600)
|
||||
ctxt = {
|
||||
'ssl_configured': True,
|
||||
'ssl_cert': '/etc/ssl/certs/dashboard.cert',
|
||||
'ssl_key': '/etc/ssl/private/dashboard.key',
|
||||
}
|
||||
else:
|
||||
if os.path.exists(SSL_CERT_FILE) and os.path.exists(SSL_KEY_FILE):
|
||||
ctxt = {
|
||||
'ssl_configured': True,
|
||||
'ssl_cert': SSL_CERT_FILE,
|
||||
'ssl_key': SSL_KEY_FILE,
|
||||
}
|
||||
return ctxt
|
||||
return super(ApacheSSLContext, self).__call__()
|
||||
|
||||
|
||||
class RouterSettingContext(OSContextGenerator):
|
||||
|
|
|
@ -376,28 +376,7 @@ def certs_joined(relation_id=None):
|
|||
|
||||
@hooks.hook('certificates-relation-changed')
|
||||
def certs_changed(relation_id=None, unit=None):
|
||||
if config('os-public-hostname'):
|
||||
# NOTE(fnordahl): Kludge to fix LP: #1816621
|
||||
# Long term fix is to use the common ApacheSSLContext from
|
||||
# charm-helpers and adapt the Apache config along the lines of
|
||||
# ``charmhelpers/contrib/openstack/templates/openstack_https_frontend``
|
||||
process_certificates('horizon', relation_id, unit)
|
||||
ssl_dir = '/etc/apache2/ssl/horizon'
|
||||
cert = os.path.join(
|
||||
ssl_dir,
|
||||
'{}_{}'.format('cert', config('os-public-hostname')))
|
||||
key = os.path.join(
|
||||
ssl_dir,
|
||||
'{}_{}'.format('key', config('os-public-hostname')))
|
||||
cert_link = os.path.join(ssl_dir, 'cert_dashboard')
|
||||
key_link = os.path.join(ssl_dir, 'key_dashboard')
|
||||
for source, dest in [(cert, cert_link), (key, key_link)]:
|
||||
if os.path.exists(dest):
|
||||
os.remove(dest)
|
||||
os.symlink(source, dest)
|
||||
else:
|
||||
process_certificates('horizon', relation_id, unit,
|
||||
custom_hostname_link='dashboard')
|
||||
process_certificates('horizon', relation_id, unit)
|
||||
CONFIGS.write_all()
|
||||
service_reload('apache2')
|
||||
enable_ssl()
|
||||
|
|
|
@ -1,57 +1,63 @@
|
|||
<IfModule mod_ssl.c>
|
||||
<VirtualHost _default_:{{ https_port }}>
|
||||
ServerAdmin webmaster@localhost
|
||||
{% if endpoints -%}
|
||||
# Accept connections from non-SNI clients
|
||||
SSLStrictSNIVHostCheck off
|
||||
{% for ext_port in ext_ports -%}
|
||||
NameVirtualHost *:{{ 443 }}
|
||||
{% endfor -%}
|
||||
{% for address, endpoint, ext, int in endpoints -%}
|
||||
<VirtualHost {{ address }}:{{ ext }}>
|
||||
ServerName {{ endpoint }}
|
||||
|
||||
DocumentRoot /var/www
|
||||
<Directory />
|
||||
Options FollowSymLinks
|
||||
AllowOverride None
|
||||
</Directory>
|
||||
<Directory /var/www/>
|
||||
Options Indexes FollowSymLinks MultiViews
|
||||
AllowOverride None
|
||||
Order allow,deny
|
||||
allow from all
|
||||
</Directory>
|
||||
ServerAdmin webmaster@localhost
|
||||
|
||||
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
|
||||
<Directory "/usr/lib/cgi-bin">
|
||||
AllowOverride None
|
||||
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</Directory>
|
||||
DocumentRoot /var/www
|
||||
<Directory />
|
||||
Options FollowSymLinks
|
||||
AllowOverride None
|
||||
</Directory>
|
||||
<Directory /var/www/>
|
||||
Options Indexes FollowSymLinks MultiViews
|
||||
AllowOverride None
|
||||
Order allow,deny
|
||||
allow from all
|
||||
</Directory>
|
||||
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||
LogLevel warn
|
||||
ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
|
||||
<Directory "/usr/lib/cgi-bin">
|
||||
AllowOverride None
|
||||
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
|
||||
Order allow,deny
|
||||
Allow from all
|
||||
</Directory>
|
||||
|
||||
CustomLog ${APACHE_LOG_DIR}/ssl_access.log combined
|
||||
ErrorLog ${APACHE_LOG_DIR}/error.log
|
||||
LogLevel warn
|
||||
|
||||
SSLEngine on
|
||||
SSLProtocol +TLSv1 +TLSv1.1 +TLSv1.2
|
||||
SSLCipherSuite HIGH:!RC4:!MD5:!aNULL:!eNULL:!EXP:!LOW:!MEDIUM
|
||||
{% if ssl_configured %}
|
||||
SSLCertificateFile {{ ssl_cert }}
|
||||
SSLCertificateKeyFile {{ ssl_key }}
|
||||
CustomLog ${APACHE_LOG_DIR}/ssl_access.log combined
|
||||
|
||||
SSLEngine on
|
||||
SSLProtocol +TLSv1 +TLSv1.1 +TLSv1.2
|
||||
SSLCipherSuite HIGH:!RC4:!MD5:!aNULL:!eNULL:!EXP:!LOW:!MEDIUM
|
||||
SSLCertificateFile /etc/apache2/ssl/{{ namespace }}/cert_{{ endpoint }}
|
||||
# See LP 1484489 - this is to support <= 2.4.7 and >= 2.4.8
|
||||
SSLCertificateChainFile /etc/apache2/ssl/{{ namespace }}/cert_{{ endpoint }}
|
||||
SSLCertificateKeyFile /etc/apache2/ssl/{{ namespace }}/key_{{ endpoint }}
|
||||
{% if enforce_ssl %}
|
||||
Header set Strict-Transport-Security "max-age={{ hsts_max_age_seconds }}"
|
||||
Header set Strict-Transport-Security "max-age={{ hsts_max_age_seconds }}"
|
||||
{% endif %}
|
||||
{% else %}
|
||||
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
|
||||
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
|
||||
{% endif %}
|
||||
Header set X-XSS-Protection "1; mode=block"
|
||||
Header set X-Content-Type-Options "nosniff"
|
||||
<FilesMatch "\.(cgi|shtml|phtml|php)$">
|
||||
SSLOptions +StdEnvVars
|
||||
</FilesMatch>
|
||||
<Directory /usr/lib/cgi-bin>
|
||||
SSLOptions +StdEnvVars
|
||||
</Directory>
|
||||
BrowserMatch "MSIE [2-6]" \
|
||||
nokeepalive ssl-unclean-shutdown \
|
||||
downgrade-1.0 force-response-1.0
|
||||
BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
|
||||
Header set X-XSS-Protection "1; mode=block"
|
||||
Header set X-Content-Type-Options "nosniff"
|
||||
<FilesMatch "\.(cgi|shtml|phtml|php)$">
|
||||
SSLOptions +StdEnvVars
|
||||
</FilesMatch>
|
||||
<Directory /usr/lib/cgi-bin>
|
||||
SSLOptions +StdEnvVars
|
||||
</Directory>
|
||||
BrowserMatch "MSIE [2-6]" \
|
||||
nokeepalive ssl-unclean-shutdown \
|
||||
downgrade-1.0 force-response-1.0
|
||||
BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
|
||||
|
||||
</VirtualHost>
|
||||
</IfModule>
|
||||
</VirtualHost>
|
||||
{% endfor -%}
|
||||
{% endif -%}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
from contextlib import contextmanager
|
||||
import io
|
||||
from mock import MagicMock, patch, call
|
||||
from mock import MagicMock, patch
|
||||
|
||||
import hooks.horizon_contexts as horizon_contexts
|
||||
|
||||
|
@ -27,7 +27,6 @@ TO_PATCH = [
|
|||
'related_units',
|
||||
'log',
|
||||
'get_cert',
|
||||
'b64decode',
|
||||
'context_complete',
|
||||
'local_unit',
|
||||
'get_relation_ip',
|
||||
|
@ -95,49 +94,6 @@ class TestHorizonContexts(CharmTestCase):
|
|||
'hsts_max_age_seconds': 15768000,
|
||||
'custom_theme': False})
|
||||
|
||||
@patch.object(horizon_contexts, 'get_ca_cert', lambda: 'ca_cert')
|
||||
@patch.object(horizon_contexts, 'install_ca_cert')
|
||||
@patch('os.chmod')
|
||||
def test_ApacheSSLContext_enabled(self, _chmod, _install_ca_cert):
|
||||
self.relation_ids.return_value = []
|
||||
self.get_cert.return_value = ('cert', 'key')
|
||||
self.b64decode.side_effect = ['ca', 'cert', 'key']
|
||||
with patch_open() as (_open, _file):
|
||||
self.assertEqual(horizon_contexts.ApacheSSLContext()(),
|
||||
{'ssl_configured': True,
|
||||
'ssl_cert': '/etc/ssl/certs/dashboard.cert',
|
||||
'ssl_key': '/etc/ssl/private/dashboard.key'})
|
||||
_open.assert_has_calls([
|
||||
call('/etc/ssl/certs/dashboard.cert', 'wb'),
|
||||
call('/etc/ssl/private/dashboard.key', 'wb')
|
||||
])
|
||||
_file.write.assert_has_calls([
|
||||
call('cert'),
|
||||
call('key')
|
||||
])
|
||||
# Security check on key permissions
|
||||
_chmod.assert_called_with('/etc/ssl/private/dashboard.key', 0o600)
|
||||
_install_ca_cert.assert_called_once()
|
||||
|
||||
@patch.object(horizon_contexts, 'get_ca_cert', lambda: None)
|
||||
def test_ApacheSSLContext_disabled(self):
|
||||
self.relation_ids.return_value = []
|
||||
self.get_cert.return_value = (None, None)
|
||||
self.assertEqual(horizon_contexts.ApacheSSLContext()(),
|
||||
{'ssl_configured': False})
|
||||
|
||||
@patch.object(horizon_contexts.os.path, 'exists')
|
||||
def test_ApacheSSLContext_vault(self, _exists):
|
||||
_exists.return_value = True
|
||||
self.relation_ids.return_value = ['certificates:60']
|
||||
self.related_units.return_value = ['vault/0']
|
||||
self.assertEqual(
|
||||
horizon_contexts.ApacheSSLContext()(),
|
||||
{
|
||||
'ssl_configured': True,
|
||||
'ssl_cert': '/etc/apache2/ssl/horizon/cert_dashboard',
|
||||
'ssl_key': '/etc/apache2/ssl/horizon/key_dashboard'})
|
||||
|
||||
def test_HorizonContext_defaults(self):
|
||||
self.assertEqual(horizon_contexts.HorizonContext()(),
|
||||
{'compress_offline': True,
|
||||
|
|
|
@ -331,33 +331,12 @@ class TestHorizonHooks(CharmTestCase):
|
|||
}),
|
||||
])
|
||||
|
||||
@patch.object(hooks.os, 'symlink')
|
||||
@patch.object(hooks.os, 'remove')
|
||||
@patch.object(hooks.os.path, 'exists')
|
||||
@patch.object(hooks, 'service_reload')
|
||||
@patch.object(hooks, 'process_certificates')
|
||||
def test_certs_changed(self, _process_certificates, _service_reload,
|
||||
_exists, _remove, _symlink):
|
||||
def test_certs_changed(self, _process_certificates, _service_reload):
|
||||
self._call_hook('certificates-relation-changed')
|
||||
_process_certificates.assert_called_with(
|
||||
'horizon', None, None, custom_hostname_link='dashboard')
|
||||
self.assertFalse(_symlink.called)
|
||||
'horizon', None, None)
|
||||
self.CONFIGS.write_all.assert_called_with()
|
||||
_service_reload.assert_called_with('apache2')
|
||||
self.enable_ssl.assert_called_with()
|
||||
_process_certificates.reset_mock()
|
||||
self.config.side_effect = None
|
||||
self.config.return_value = 'somehostname'
|
||||
_exists.return_value = True
|
||||
self._call_hook('certificates-relation-changed')
|
||||
_process_certificates.assert_called_with('horizon', None, None)
|
||||
_remove.assert_has_calls([
|
||||
call('/etc/apache2/ssl/horizon/cert_dashboard'),
|
||||
call('/etc/apache2/ssl/horizon/key_dashboard'),
|
||||
])
|
||||
_symlink.assert_has_calls([
|
||||
call('/etc/apache2/ssl/horizon/cert_somehostname',
|
||||
'/etc/apache2/ssl/horizon/cert_dashboard'),
|
||||
call('/etc/apache2/ssl/horizon/key_somehostname',
|
||||
'/etc/apache2/ssl/horizon/key_dashboard'),
|
||||
])
|
||||
|
|
Loading…
Reference in New Issue