@@ -1,14 +1,34 @@ | |||
FROM nginx:1.8 | |||
FROM nginx:1.8-alpine | |||
RUN apk update && apk add \ | |||
python3 \ | |||
openssl \ | |||
&& python3 -m ensurepip \ | |||
&& rm -r /usr/lib/python*/ensurepip \ | |||
&& pip3 install --upgrade pip setuptools \ | |||
&& rm -rf /var/cache/apk/* | |||
RUN pip3 install Jinja2==2.8 | |||
RUN rm /etc/nginx/nginx.conf | |||
RUN rm /etc/nginx/mime.types | |||
COPY nginx.conf /etc/nginx/nginx.conf | |||
COPY mime.types /etc/nginx/mime.types | |||
COPY conf.d/nginx.conf /etc/nginx/nginx.conf | |||
COPY conf.d/mime.types /etc/nginx/mime.types | |||
RUN rm /etc/nginx/conf.d/default.conf | |||
COPY conf.d /etc/nginx/conf.d/ | |||
COPY conf.d/default.conf /etc/nginx/conf.d/default.conf | |||
RUN mkdir /dockersible | |||
COPY dockersible/ /dockersible | |||
COPY backend.conf.j2 / | |||
COPY install.py / | |||
RUN mkdir /certificates | |||
VOLUME ["/certificates"] | |||
COPY entrypoint.sh / | |||
RUN chmod 755 /entrypoint.sh | |||
ENTRYPOINT ["/entrypoint.sh"] | |||
COPY ssl/lesspass.com.crt /etc/ssl/certs/lesspass.com.crt | |||
COPY ssl/lesspass.com.key /etc/ssl/private/lesspass.com.key | |||
COPY ssl/dhparam.pem /etc/ssl/certs/dhparam.pem | |||
COPY ssl/AddTrustExternalCARoot.crt /etc/ssl/certs/AddTrustExternalCARoot.crt | |||
CMD ["nginx", "-g", "daemon off;"] |
@@ -1,2 +1,3 @@ | |||
# nginx | |||
nginx container for lesspass | |||
@@ -1,6 +1,6 @@ | |||
server { | |||
listen 80; | |||
server_name localhost *.oslab.fr *.lesspass.com; | |||
server_name {{ server_name }}; | |||
return 301 https://$server_name$request_uri; | |||
} | |||
@@ -10,12 +10,12 @@ server { | |||
listen [::]:443 ssl; | |||
listen 443 ssl; | |||
server_name localhost *.oslab.fr *.lesspass.com; | |||
server_name {{ server_name }}; | |||
charset utf-8; | |||
ssl_certificate /etc/ssl/certs/lesspass.com.crt; | |||
ssl_certificate_key /etc/ssl/private/lesspass.com.key; | |||
ssl_certificate /etc/ssl/certs/certificate.crt; | |||
ssl_certificate_key /etc/ssl/private/private.key; | |||
ssl_session_cache shared:SSL:20m; | |||
ssl_session_timeout 30m; | |||
@@ -26,8 +26,12 @@ server { | |||
ssl_stapling on; | |||
ssl_stapling_verify on; | |||
ssl_dhparam /etc/ssl/certs/dhparam.pem; | |||
ssl_trusted_certificate /etc/ssl/certs/AddTrustExternalCARoot.crt; | |||
{% if dhparam %} | |||
ssl_dhparam {{ dhparam_path }}; | |||
{% endif %} | |||
{% if ssl_trusted_certificate %} | |||
ssl_trusted_certificate {{ ssl_trusted_certificate_path }}; | |||
{% endif %} | |||
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; | |||
@@ -0,0 +1,15 @@ | |||
############################### | |||
## self-signed certificates ## | |||
############################### | |||
domain=lesspass.com | |||
server_name=localhost | |||
############################### | |||
## use custom certificate ## | |||
############################### | |||
#domain, server_name, private_key and certificate are mandatory | |||
#domain=lesspass.com | |||
#server_name=localhost *.lesspass.com | |||
#private_key=lesspass.com.key | |||
#certificate=lesspass.com.crt | |||
#dhparam=dhparam.pem | |||
#ssl_trusted_certificate=AddTrustExternalCARoot.crt |
@@ -0,0 +1,44 @@ | |||
import os | |||
import shutil | |||
import fnmatch | |||
from jinja2 import Template | |||
def pattern_filter(file, patterns=None): | |||
if patterns is None: | |||
return True | |||
for p in patterns: | |||
if fnmatch.fnmatch(file, p): | |||
return True | |||
return False | |||
def find(paths, patterns=None): | |||
certificates = [] | |||
for root, dirs, files in os.walk(paths): | |||
for file in files: | |||
if pattern_filter(file, patterns.split(',')): | |||
certificates.append({'path': os.path.normpath(os.path.join(root, file))}) | |||
return certificates | |||
def copy(source, destination, basename=None, mode='0755'): | |||
if not os.path.exists(destination): | |||
os.makedirs(destination) | |||
shutil.copy2(src=source, dst=destination) | |||
file_path = os.path.join(destination, os.path.basename(source)) | |||
os.chmod(file_path, int(mode, 8)) | |||
if basename: | |||
os.rename(file_path, os.path.join(destination, basename)) | |||
def template(source, context, destination): | |||
jinja_template = Template(open(source).read()) | |||
with open(destination, 'w') as f: | |||
f.write(jinja_template.render(context)) |
@@ -0,0 +1,18 @@ | |||
import os | |||
import shutil | |||
def copy_certificates(certificates, destination='/etc/ssl', domain='example.org'): | |||
private_key_folder = os.path.join(destination, 'private') | |||
if not os.path.exists(private_key_folder): | |||
os.makedirs(private_key_folder) | |||
private_key = os.path.join(private_key_folder, domain + '.key') | |||
shutil.copy2(certificates['key'], private_key) | |||
os.chmod(private_key, 0o600) | |||
certificates_folder = os.path.join(destination, 'certs') | |||
if not os.path.exists(certificates_folder): | |||
os.makedirs(certificates_folder) | |||
certificate = os.path.join(certificates_folder, domain + '.crt') | |||
shutil.copy2(certificates['crt'], certificate) | |||
os.chmod(certificate, 0o644) |
@@ -0,0 +1,5 @@ | |||
#!/bin/sh | |||
python3 install.py | |||
exec "$@" |
@@ -0,0 +1,59 @@ | |||
import os | |||
import subprocess | |||
from dockersible.files import copy, template | |||
def copy_certificates(): | |||
copy(source=(os.path.join('/certificates', os.environ['private_key'])), | |||
destination='/etc/ssl/private', | |||
basename='private.key', | |||
mode='0600') | |||
copy(source=os.path.join('/certificates', os.environ['certificate']), | |||
destination='/etc/ssl/certs', | |||
basename='certificate.crt', | |||
mode='0644') | |||
context = { | |||
'server_name': os.environ['server_name'], | |||
'dhparam': False, | |||
'ssl_trusted_certificate': False | |||
} | |||
cert_folder = os.path.join('/etc/ssl/certs') | |||
if 'dhparam' in os.environ: | |||
dhparam = os.environ['dhparam'] | |||
copy(source=os.path.join('/certificates', dhparam), destination=cert_folder, mode='0644') | |||
context['dhparam'] = True | |||
context['dhparam_path'] = os.path.join(cert_folder, dhparam) | |||
if 'certificate' in os.environ: | |||
certificate = os.environ['ssl_trusted_certificate'] | |||
copy(source=os.path.join('/certificates', certificate), destination=cert_folder, mode='0644') | |||
context['ssl_trusted_certificate'] = True | |||
context['ssl_trusted_certificate_path'] = os.path.join(cert_folder, certificate) | |||
return context | |||
def create_certificates(): | |||
cmd = """openssl req \ | |||
-new \ | |||
-newkey rsa:4096 \ | |||
-days 365 \ | |||
-nodes \ | |||
-x509 \ | |||
-subj "/C=US/ST=State/L=City/O=Company/CN=%s" \ | |||
-keyout /etc/ssl/private/private.key \ | |||
-out /etc/ssl/certs/certificate.crt""".format(os.environ['domain']) | |||
subprocess.call(cmd, shell=True) | |||
return { | |||
'server_name': os.environ['server_name'], | |||
'dhparam': False, | |||
'ssl_trusted_certificate': False | |||
} | |||
if __name__ == "__main__": | |||
if 'private_key' in os.environ and 'certificate' in os.environ: | |||
context = copy_certificates() | |||
else: | |||
context = create_certificates() | |||
template('/backend.conf.j2', context, '/etc/nginx/conf.d/backend.conf') |
@@ -0,0 +1,3 @@ | |||
{% if dhparam %} | |||
ssl_dhparam {{ dhparam_path }}; | |||
{% endif %} |
@@ -0,0 +1,85 @@ | |||
import os | |||
import shutil | |||
import tempfile | |||
import unittest | |||
from dockersible.ssl import copy_certificates | |||
from dockersible.files import find, copy, template | |||
class DockersibleTestCase(unittest.TestCase): | |||
def test_find(self): | |||
parent_directory = os.path.dirname(os.path.realpath(__file__)) | |||
ssl_directory = os.path.join(parent_directory, 'ssl') | |||
certificates = find(paths=ssl_directory, patterns='*.key,*.crt') | |||
for certificate in certificates: | |||
expected_path = [os.path.join(ssl_directory, 'test.key'), os.path.join(ssl_directory, 'test.crt')] | |||
self.assertTrue(certificate['path'] in expected_path) | |||
def test_copy_certificates(self): | |||
temp_folder = tempfile.mkdtemp() | |||
private_key_origin = os.path.join(temp_folder, 'test.key') | |||
with open(private_key_origin, 'w') as f: f.write('') | |||
certificate_origin = os.path.join(temp_folder, 'test.crt') | |||
with open(certificate_origin, 'w') as f: f.write('') | |||
certificates = { | |||
'key': private_key_origin, | |||
'crt': certificate_origin, | |||
} | |||
copy_certificates(certificates, temp_folder, 'oslab.fr') | |||
private_key = os.path.join(temp_folder, 'private', 'oslab.fr.key') | |||
self.assertTrue(os.path.exists(private_key)) | |||
self.assertTrue((os.stat(private_key).st_mode & 0o777) == 0o600) | |||
self.assertTrue(os.path.exists(private_key_origin)) | |||
certificate = os.path.join(temp_folder, 'certs', 'oslab.fr.crt') | |||
self.assertTrue(os.path.exists(certificate)) | |||
self.assertTrue((os.stat(certificate).st_mode & 0o777) == 0o644) | |||
self.assertTrue(os.path.exists(certificate_origin)) | |||
shutil.rmtree(temp_folder) | |||
def test_copy_file(self): | |||
private_key_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'ssl', 'test.key') | |||
destination = tempfile.mkdtemp() | |||
copy(source=private_key_path, destination=destination) | |||
self.assertTrue(os.path.exists(os.path.join(destination, 'test.key'))) | |||
shutil.rmtree(destination) | |||
def test_copy_file_change_basename(self): | |||
private_key_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'ssl', 'test.key') | |||
destination = tempfile.mkdtemp() | |||
copy(source=private_key_path, destination=destination, basename='lesspass.com.key', mode='0600') | |||
self.assertTrue(os.path.exists(os.path.join(destination, 'lesspass.com.key'))) | |||
shutil.rmtree(destination) | |||
def test_copy_file_change_mode(self): | |||
private_key_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'ssl', 'test.key') | |||
destination = tempfile.mkdtemp() | |||
copy(source=private_key_path, destination=destination) | |||
expected_private_key_path = os.path.join(destination, 'test.key') | |||
self.assertTrue((os.stat(expected_private_key_path).st_mode & 0o777) == 0o755) | |||
copy(source=private_key_path, destination=destination, basename='lesspass.com.key', mode='0600') | |||
expected_private_key_path = os.path.join(destination, 'lesspass.com.key') | |||
self.assertTrue((os.stat(expected_private_key_path).st_mode & 0o777) == 0o600) | |||
shutil.rmtree(destination) | |||
def test_template_module_with_source_file(self): | |||
template_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'templates', 'test.j2') | |||
destination = tempfile.mkdtemp() | |||
context = { | |||
'dhparam': True, | |||
'dhparam_path': '/etc/ssl/certs/dhparam.pem' | |||
} | |||
destination_file = os.path.join(destination, 'test.txt') | |||
template(source=template_path, context=context, destination=destination_file) | |||
self.assertEqual('\nssl_dhparam /etc/ssl/certs/dhparam.pem;\n', open(destination_file).read()) | |||
shutil.rmtree(destination) | |||
if __name__ == '__main__': | |||
unittest.main() |