mirror of
git://git.yoctoproject.org/layerindex-web.git
synced 2025-07-05 13:14:46 +02:00
dockersetup: add HTTPS support and use by default
If we want a minimum level of security we should enable HTTPS. However, the only practical way we can do that without the user having to do further infrastructure setup and/or pay a certification authority is to use a self-signed certificate. Do this by default, and also provide an option to specify a previously obtained certificate/key pair. Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
This commit is contained in:
parent
c717a827d3
commit
cb4955fe0b
|
@ -37,7 +37,7 @@ services:
|
||||||
# - "443:443"
|
# - "443:443"
|
||||||
volumes:
|
volumes:
|
||||||
- layersstatic:/usr/share/nginx/html
|
- layersstatic:/usr/share/nginx/html
|
||||||
# - layerscerts:/etc/letsencrypt
|
- ./docker/certs:/opt/cert
|
||||||
container_name: layersweb
|
container_name: layersweb
|
||||||
layersrabbit:
|
layersrabbit:
|
||||||
image: rabbitmq:alpine
|
image: rabbitmq:alpine
|
||||||
|
@ -62,4 +62,3 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
layersmeta:
|
layersmeta:
|
||||||
layersstatic:
|
layersstatic:
|
||||||
layerscerts:
|
|
||||||
|
|
7
docker/certs/README
Normal file
7
docker/certs/README
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
This directory will be mounted as a volume in the Docker container setup to
|
||||||
|
contain SSL certificates for the web server.
|
||||||
|
|
||||||
|
If you run dockersetup.py and specify a certificate with --cert and
|
||||||
|
corresponding key with --key then they will be copied here; alternatively
|
||||||
|
let the setup script generate a self-signed certificate and key and they
|
||||||
|
will be written here as well.
|
139
dockersetup.py
139
dockersetup.py
|
@ -18,21 +18,28 @@
|
||||||
# It will build and run these containers and set up the database.
|
# It will build and run these containers and set up the database.
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
import argparse
|
import argparse
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
|
import shutil
|
||||||
|
|
||||||
def get_args():
|
def get_args():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(description='Script sets up the Layer Index tool with Docker Containers.')
|
||||||
description='Script sets up the Layer Index tool with Docker Containers.')
|
|
||||||
parser.add_argument('-o', '--hostname', type=str, help='Hostname of your machine. Defaults to localhost if not set.', required=False, default = "localhost")
|
parser.add_argument('-o', '--hostname', type=str, help='Hostname of your machine. Defaults to localhost if not set.', required=False, default = "localhost")
|
||||||
parser.add_argument('-p', '--http-proxy', type=str, help='http proxy in the format http://<myproxy:port>', required=False)
|
parser.add_argument('-p', '--http-proxy', type=str, help='http proxy in the format http://<myproxy:port>', required=False)
|
||||||
parser.add_argument('-s', '--https-proxy', type=str, help='https proxy in the format http://<myproxy:port>', required=False)
|
parser.add_argument('-s', '--https-proxy', type=str, help='https proxy in the format http://<myproxy:port>', required=False)
|
||||||
parser.add_argument('-d', '--databasefile', type=str, help='Location of your database file to import. Must be a .sql file.', required=False)
|
parser.add_argument('-d', '--databasefile', type=str, help='Location of your database file to import. Must be a .sql file.', required=False)
|
||||||
parser.add_argument('-m', '--portmapping', type=str, help='Port mapping in the format HOST:CONTAINER. Default is %(default)s', required=False, default='8080:80')
|
parser.add_argument('-m', '--portmapping', type=str, help='Port mapping in the format HOST:CONTAINER. Default is %(default)s', required=False, default='8080:80,8081:443')
|
||||||
|
parser.add_argument('--no-https', action="store_true", default=False, help='Disable HTTPS (HTTP only) for web server')
|
||||||
|
parser.add_argument('--cert', type=str, help='Existing SSL certificate to use for HTTPS web serving', required=False)
|
||||||
|
parser.add_argument('--cert-key', type=str, help='Existing SSL certificate key to use for HTTPS web serving', required=False)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
port = proxymod = ""
|
port = proxymod = ""
|
||||||
try:
|
try:
|
||||||
if args.http_proxy:
|
if args.http_proxy:
|
||||||
|
@ -42,10 +49,26 @@ def get_args():
|
||||||
except IndexError:
|
except IndexError:
|
||||||
raise argparse.ArgumentTypeError("http_proxy must be in format http://<myproxy:port>")
|
raise argparse.ArgumentTypeError("http_proxy must be in format http://<myproxy:port>")
|
||||||
|
|
||||||
|
for entry in args.portmapping.split(','):
|
||||||
|
if len(entry.split(":")) != 2:
|
||||||
|
raise argparse.ArgumentTypeError("Port mapping must in the format HOST:CONTAINER. Ex: 8080:80. Multiple mappings should be separated by commas.")
|
||||||
|
|
||||||
if len(args.portmapping.split(":")) != 2:
|
if args.no_https:
|
||||||
raise argparse.ArgumentTypeError("Port mapping must in the format HOST:CONTAINER. Ex: 8080:80")
|
if args.cert or args.cert_key:
|
||||||
return args.hostname, args.http_proxy, args.https_proxy, args.databasefile, port, proxymod, args.portmapping
|
raise argparse.ArgumentTypeError("--no-https and --cert/--cert-key options are mutually exclusive")
|
||||||
|
if args.cert and not os.path.exists(args.cert):
|
||||||
|
raise argparse.ArgumentTypeError("Specified certificate file %s does not exist" % args.cert)
|
||||||
|
if args.cert_key and not os.path.exists(args.cert_key):
|
||||||
|
raise argparse.ArgumentTypeError("Specified certificate key file %s does not exist" % args.cert_key)
|
||||||
|
if args.cert_key and not args.cert:
|
||||||
|
raise argparse.ArgumentTypeError("Certificate key file specified but not certificate")
|
||||||
|
cert_key = args.cert_key
|
||||||
|
if args.cert and not cert_key:
|
||||||
|
cert_key = os.path.splitext(args.cert)[0] + '.key'
|
||||||
|
if not os.path.exists(cert_key):
|
||||||
|
raise argparse.ArgumentTypeError("Could not find certificate key, please use --cert-key to specify it")
|
||||||
|
|
||||||
|
return args.hostname, args.http_proxy, args.https_proxy, args.databasefile, port, proxymod, args.portmapping, args.no_https, args.cert, cert_key
|
||||||
|
|
||||||
# Edit http_proxy and https_proxy in Dockerfile
|
# Edit http_proxy and https_proxy in Dockerfile
|
||||||
def edit_dockerfile(http_proxy, https_proxy):
|
def edit_dockerfile(http_proxy, https_proxy):
|
||||||
|
@ -86,15 +109,24 @@ def edit_dockercompose(hostname, dbpassword, secretkey, portmapping):
|
||||||
filedata= readfile("docker-compose.yml")
|
filedata= readfile("docker-compose.yml")
|
||||||
in_layersweb = False
|
in_layersweb = False
|
||||||
in_layersweb_ports = False
|
in_layersweb_ports = False
|
||||||
|
in_layersweb_ports_format = None
|
||||||
newlines = []
|
newlines = []
|
||||||
lines = filedata.splitlines()
|
lines = filedata.splitlines()
|
||||||
for line in lines:
|
for line in lines:
|
||||||
if in_layersweb_ports:
|
if in_layersweb_ports:
|
||||||
format = line[0:line.find("-")].replace("#", "")
|
format = line[0:line.find("-")].replace("#", "")
|
||||||
newlines.append(format + '- "' + portmapping + '"' + "\n")
|
if in_layersweb_ports_format:
|
||||||
|
if format != in_layersweb_ports_format:
|
||||||
in_layersweb_ports = False
|
in_layersweb_ports = False
|
||||||
in_layersweb = False
|
in_layersweb = False
|
||||||
elif "layersweb:" in line:
|
else:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
in_layersweb_ports_format = format
|
||||||
|
for portmap in portmapping.split(','):
|
||||||
|
newlines.append(format + '- "' + portmap + '"' + "\n")
|
||||||
|
continue
|
||||||
|
if "layersweb:" in line:
|
||||||
in_layersweb = True
|
in_layersweb = True
|
||||||
newlines.append(line + "\n")
|
newlines.append(line + "\n")
|
||||||
elif "hostname:" in line:
|
elif "hostname:" in line:
|
||||||
|
@ -117,6 +149,46 @@ def edit_dockercompose(hostname, dbpassword, secretkey, portmapping):
|
||||||
newlines.append(line + "\n")
|
newlines.append(line + "\n")
|
||||||
writefile("docker-compose.yml", ''.join(newlines))
|
writefile("docker-compose.yml", ''.join(newlines))
|
||||||
|
|
||||||
|
|
||||||
|
def edit_nginx_ssl_conf(hostname, https_port, certdir, certfile, keyfile):
|
||||||
|
filedata = readfile('docker/nginx-ssl.conf')
|
||||||
|
newlines = []
|
||||||
|
lines = filedata.splitlines()
|
||||||
|
for line in lines:
|
||||||
|
if 'ssl_certificate ' in line:
|
||||||
|
format = line[0:line.find('ssl_certificate')]
|
||||||
|
newlines.append(format + 'ssl_certificate ' + os.path.join(certdir, certfile) + ';\n')
|
||||||
|
elif 'ssl_certificate_key ' in line:
|
||||||
|
format = line[0:line.find('ssl_certificate_key')]
|
||||||
|
newlines.append(format + 'ssl_certificate_key ' + os.path.join(certdir, keyfile) + ';\n')
|
||||||
|
# Add a line for the dhparam file
|
||||||
|
newlines.append(format + 'ssl_dhparam ' + os.path.join(certdir, 'dhparam.pem') + ';\n')
|
||||||
|
elif 'https://layers.openembedded.org' in line:
|
||||||
|
line = line.replace('https://layers.openembedded.org', 'https://%s:%s' % (hostname, https_port))
|
||||||
|
newlines.append(line + "\n")
|
||||||
|
else:
|
||||||
|
line = line.replace('layers.openembedded.org', hostname)
|
||||||
|
newlines.append(line + "\n")
|
||||||
|
|
||||||
|
# Write to a different file so we can still replace the hostname next time
|
||||||
|
writefile("docker/nginx-ssl-edited.conf", ''.join(newlines))
|
||||||
|
|
||||||
|
|
||||||
|
def edit_dockerfile_web(hostname, no_https):
|
||||||
|
filedata = readfile('Dockerfile.web')
|
||||||
|
newlines = []
|
||||||
|
lines = filedata.splitlines()
|
||||||
|
for line in lines:
|
||||||
|
if line.startswith('COPY ') and line.endswith('/etc/nginx/nginx.conf'):
|
||||||
|
if no_https:
|
||||||
|
srcfile = 'docker/nginx.conf'
|
||||||
|
else:
|
||||||
|
srcfile = 'docker/nginx-ssl-edited.conf'
|
||||||
|
line = 'COPY %s /etc/nginx/nginx.conf' % srcfile
|
||||||
|
newlines.append(line + "\n")
|
||||||
|
writefile("Dockerfile.web", ''.join(newlines))
|
||||||
|
|
||||||
|
|
||||||
def generatepasswords(passwordlength):
|
def generatepasswords(passwordlength):
|
||||||
return ''.join([random.SystemRandom().choice('abcdefghijklmnopqrstuvwxyz0123456789!@#%^&*-_=+') for i in range(passwordlength)])
|
return ''.join([random.SystemRandom().choice('abcdefghijklmnopqrstuvwxyz0123456789!@#%^&*-_=+') for i in range(passwordlength)])
|
||||||
|
|
||||||
|
@ -137,7 +209,22 @@ secretkey = generatepasswords(50)
|
||||||
dbpassword = generatepasswords(10)
|
dbpassword = generatepasswords(10)
|
||||||
|
|
||||||
## Get user arguments and modify config files
|
## Get user arguments and modify config files
|
||||||
hostname, http_proxy, https_proxy, dbfile, port, proxymod, portmapping = get_args()
|
hostname, http_proxy, https_proxy, dbfile, port, proxymod, portmapping, no_https, cert, cert_key = get_args()
|
||||||
|
|
||||||
|
https_port = None
|
||||||
|
http_port = None
|
||||||
|
for portmap in portmapping.split(','):
|
||||||
|
outport, inport = portmap.split(':', 1)
|
||||||
|
if inport == '443':
|
||||||
|
https_port = outport
|
||||||
|
elif inport == '80':
|
||||||
|
http_port = outport
|
||||||
|
if (not https_port) and (not no_https):
|
||||||
|
print("No HTTPS port mapping (to port 443 inside the container) was specified and --no-https was not specified")
|
||||||
|
sys.exit(1)
|
||||||
|
if not (http_port or https_port):
|
||||||
|
print("Port mapping must include a mapping to port 80 or 443 inside the container (or both)")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
if http_proxy:
|
if http_proxy:
|
||||||
edit_gitproxy(proxymod, port)
|
edit_gitproxy(proxymod, port)
|
||||||
|
@ -146,6 +233,33 @@ if http_proxy or https_proxy:
|
||||||
|
|
||||||
edit_dockercompose(hostname, dbpassword, secretkey, portmapping)
|
edit_dockercompose(hostname, dbpassword, secretkey, portmapping)
|
||||||
|
|
||||||
|
edit_dockerfile_web(hostname, no_https)
|
||||||
|
|
||||||
|
if not no_https:
|
||||||
|
local_cert_dir = os.path.abspath('docker/certs')
|
||||||
|
if cert:
|
||||||
|
if os.path.abspath(os.path.dirname(cert)) != local_cert_dir:
|
||||||
|
shutil.copy(cert, local_cert_dir)
|
||||||
|
certfile = os.path.basename(cert)
|
||||||
|
if os.path.abspath(os.path.dirname(cert_key)) != local_cert_dir:
|
||||||
|
shutil.copy(cert_key, local_cert_dir)
|
||||||
|
keyfile = os.path.basename(cert_key)
|
||||||
|
else:
|
||||||
|
print('')
|
||||||
|
print('Generating self-signed SSL certificate. Please specify your hostname (%s) when prompted for the Common Name.' % hostname)
|
||||||
|
certfile = 'setup-selfsigned.crt'
|
||||||
|
keyfile = 'setup-selfsigned.key'
|
||||||
|
return_code = subprocess.call('openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout %s -out %s' % (os.path.join(local_cert_dir, keyfile), os.path.join(local_cert_dir, certfile)), shell=True)
|
||||||
|
if return_code != 0:
|
||||||
|
print("Self-signed certificate generation failed")
|
||||||
|
sys.exit(1)
|
||||||
|
return_code = subprocess.call('openssl dhparam -out %s 2048' % os.path.join(local_cert_dir, 'dhparam.pem'), shell=True)
|
||||||
|
if return_code != 0:
|
||||||
|
print("DH group generation failed")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
edit_nginx_ssl_conf(hostname, https_port, '/opt/cert', certfile, keyfile)
|
||||||
|
|
||||||
## Start up containers
|
## Start up containers
|
||||||
return_code = subprocess.call("docker-compose up -d", shell=True)
|
return_code = subprocess.call("docker-compose up -d", shell=True)
|
||||||
if return_code != 0:
|
if return_code != 0:
|
||||||
|
@ -190,9 +304,10 @@ if return_code != 0:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
ports = portmapping.split(':')
|
if https_port and not no_https:
|
||||||
if ports[1] == '443':
|
|
||||||
protocol = 'https'
|
protocol = 'https'
|
||||||
|
port = https_port
|
||||||
else:
|
else:
|
||||||
protocol = 'http'
|
protocol = 'http'
|
||||||
print("The application should now be accessible at %s://%s:%s" % (protocol, hostname, ports[0]))
|
port = http_port
|
||||||
|
print("The application should now be accessible at %s://%s:%s" % (protocol, hostname, port))
|
||||||
|
|
Loading…
Reference in New Issue
Block a user