mirror of
https://github.com/laurivosandi/certidude
synced 2024-12-22 16:25:17 +00:00
Integrate LEDE image builder
This commit is contained in:
parent
345c2802ea
commit
fba8f5d776
@ -82,7 +82,7 @@ class SessionResource(object):
|
|||||||
attributes = {}
|
attributes = {}
|
||||||
for key in listxattr(path):
|
for key in listxattr(path):
|
||||||
if key.startswith(b"user.machine."):
|
if key.startswith(b"user.machine."):
|
||||||
attributes[key[13:]] = getxattr(path, key).decode("ascii")
|
attributes[key[13:].decode("ascii")] = getxattr(path, key).decode("ascii")
|
||||||
|
|
||||||
# Extract lease information from filesystem
|
# Extract lease information from filesystem
|
||||||
try:
|
try:
|
||||||
@ -131,6 +131,9 @@ class SessionResource(object):
|
|||||||
),
|
),
|
||||||
request_submission_allowed = config.REQUEST_SUBMISSION_ALLOWED,
|
request_submission_allowed = config.REQUEST_SUBMISSION_ALLOWED,
|
||||||
authority = dict(
|
authority = dict(
|
||||||
|
builder = dict(
|
||||||
|
profiles = config.IMAGE_BUILDER_PROFILES
|
||||||
|
),
|
||||||
tagging = [dict(name=t[0], type=t[1], title=t[2]) for t in config.TAG_TYPES],
|
tagging = [dict(name=t[0], type=t[1], title=t[2]) for t in config.TAG_TYPES],
|
||||||
lease = dict(
|
lease = dict(
|
||||||
offline = 600, # Seconds from last seen activity to consider lease offline, OpenVPN reneg-sec option
|
offline = 600, # Seconds from last seen activity to consider lease offline, OpenVPN reneg-sec option
|
||||||
@ -208,6 +211,7 @@ def certidude_app(log_handlers=[]):
|
|||||||
from .attrib import AttributeResource
|
from .attrib import AttributeResource
|
||||||
from .bootstrap import BootstrapResource
|
from .bootstrap import BootstrapResource
|
||||||
from .token import TokenResource
|
from .token import TokenResource
|
||||||
|
from .builder import ImageBuilderResource
|
||||||
|
|
||||||
app = falcon.API(middleware=NormalizeMiddleware())
|
app = falcon.API(middleware=NormalizeMiddleware())
|
||||||
app.req_options.auto_parse_form_urlencoded = True
|
app.req_options.auto_parse_form_urlencoded = True
|
||||||
@ -240,6 +244,9 @@ def certidude_app(log_handlers=[]):
|
|||||||
# Bootstrap resource
|
# Bootstrap resource
|
||||||
app.add_route("/api/bootstrap/", BootstrapResource())
|
app.add_route("/api/bootstrap/", BootstrapResource())
|
||||||
|
|
||||||
|
# LEDE image builder resource
|
||||||
|
app.add_route("/api/build/{profile}/{suggested_filename}", ImageBuilderResource())
|
||||||
|
|
||||||
# Add CRL handler if we have any whitelisted subnets
|
# Add CRL handler if we have any whitelisted subnets
|
||||||
if config.CRL_SUBNETS:
|
if config.CRL_SUBNETS:
|
||||||
from .revoked import RevocationListResource
|
from .revoked import RevocationListResource
|
||||||
|
52
certidude/api/builder.py
Normal file
52
certidude/api/builder.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
|
||||||
|
import click
|
||||||
|
import falcon
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
from certidude import config, const
|
||||||
|
from certidude.auth import login_required, authorize_admin
|
||||||
|
from jinja2 import Template
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class ImageBuilderResource(object):
|
||||||
|
@login_required
|
||||||
|
@authorize_admin
|
||||||
|
def on_get(self, req, resp, profile, suggested_filename):
|
||||||
|
model = config.cp2.get(profile, "model")
|
||||||
|
build_script_path = config.cp2.get(profile, "command")
|
||||||
|
overlay_path = config.cp2.get(profile, "overlay")
|
||||||
|
site_script_path = config.cp2.get(profile, "script")
|
||||||
|
suffix = config.cp2.get(profile, "filename")
|
||||||
|
|
||||||
|
build = "/var/lib/certidude/builder/" + profile
|
||||||
|
if not os.path.exists(build + "/overlay/etc/uci-defaults"):
|
||||||
|
os.makedirs(build + "/overlay/etc/uci-defaults")
|
||||||
|
os.system("rsync -av " + overlay_path + "/ " + build + "/overlay/")
|
||||||
|
|
||||||
|
if site_script_path:
|
||||||
|
template = Template(open(site_script_path).read())
|
||||||
|
with open(build + "/overlay/etc/uci-defaults/99-site-config", "w") as fh:
|
||||||
|
fh.write(template.render(authority_name=const.FQDN))
|
||||||
|
|
||||||
|
proc = subprocess.Popen(("/bin/bash", build_script_path),
|
||||||
|
stdout=open(build + "/build.log", "w"), stderr=subprocess.STDOUT,
|
||||||
|
close_fds=True, shell=False,
|
||||||
|
cwd=build,
|
||||||
|
env={"PROFILE":model, "PATH":"/usr/sbin:/usr/bin:/sbin:/bin"},
|
||||||
|
startupinfo=None, creationflags=0)
|
||||||
|
proc.communicate()
|
||||||
|
|
||||||
|
for dname in os.listdir(build):
|
||||||
|
if dname.startswith("lede-imagebuilder-"):
|
||||||
|
for root, dirs, files in os.walk(os.path.join(build, dname, "bin", "targets")):
|
||||||
|
for filename in files:
|
||||||
|
if filename.endswith(suffix):
|
||||||
|
path = os.path.join(root, filename)
|
||||||
|
click.echo("Serving: %s" % path)
|
||||||
|
resp.body = open(path, "rb").read()
|
||||||
|
resp.set_header("Content-Disposition", ("attachment; filename=%s" % suggested_filename))
|
||||||
|
return
|
||||||
|
raise falcon.HTTPNotFound()
|
||||||
|
|
@ -1,5 +1,6 @@
|
|||||||
import falcon
|
import falcon
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
from certidude import const, config, authority
|
from certidude import const, config, authority
|
||||||
from certidude.decorators import serialize
|
from certidude.decorators import serialize
|
||||||
from jinja2 import Environment, FileSystemLoader
|
from jinja2 import Environment, FileSystemLoader
|
||||||
@ -26,9 +27,10 @@ class ScriptResource():
|
|||||||
except AttributeError: # No tags
|
except AttributeError: # No tags
|
||||||
pass
|
pass
|
||||||
|
|
||||||
script = named_tags.get("script", config.SCRIPT_DEFAULT)
|
script = named_tags.get("script", "default.sh")
|
||||||
|
assert script in os.listdir(config.SCRIPT_DIR)
|
||||||
resp.set_header("Content-Type", "text/x-shellscript")
|
resp.set_header("Content-Type", "text/x-shellscript")
|
||||||
resp.body = env.get_template(script).render(
|
resp.body = env.get_template(os.path.join(script)).render(
|
||||||
authority_name=const.FQDN,
|
authority_name=const.FQDN,
|
||||||
common_name=cn,
|
common_name=cn,
|
||||||
other_tags=other_tags,
|
other_tags=other_tags,
|
||||||
|
@ -96,7 +96,7 @@ def setup_client(prefix="client_", dh=False):
|
|||||||
@click.option("-s", "--skip-self", default=False, is_flag=True, help="Skip self enroll")
|
@click.option("-s", "--skip-self", default=False, is_flag=True, help="Skip self enroll")
|
||||||
@click.option("-nw", "--no-wait", default=False, is_flag=True, help="Return immideately if server doesn't autosign")
|
@click.option("-nw", "--no-wait", default=False, is_flag=True, help="Return immideately if server doesn't autosign")
|
||||||
def certidude_enroll(fork, renew, no_wait, kerberos, skip_self):
|
def certidude_enroll(fork, renew, no_wait, kerberos, skip_self):
|
||||||
if not skip_self and os.path.exists(const.CONFIG_PATH):
|
if not skip_self and os.path.exists(const.SERVER_CONFIG_PATH):
|
||||||
click.echo("Self-enrolling authority's web interface certificate")
|
click.echo("Self-enrolling authority's web interface certificate")
|
||||||
from certidude import authority
|
from certidude import authority
|
||||||
authority.self_enroll()
|
authority.self_enroll()
|
||||||
@ -944,19 +944,23 @@ def certidude_setup_openvpn_networkmanager(authority, remote, common_name, **pat
|
|||||||
@click.option("--directory", help="Directory for authority files")
|
@click.option("--directory", help="Directory for authority files")
|
||||||
@click.option("--server-flags", is_flag=True, help="Add TLS Server and IKE Intermediate extended key usage flags")
|
@click.option("--server-flags", is_flag=True, help="Add TLS Server and IKE Intermediate extended key usage flags")
|
||||||
@click.option("--outbox", default="smtp://smtp.%s" % const.DOMAIN, help="SMTP server, smtp://smtp.%s by default" % const.DOMAIN)
|
@click.option("--outbox", default="smtp://smtp.%s" % const.DOMAIN, help="SMTP server, smtp://smtp.%s by default" % const.DOMAIN)
|
||||||
|
@click.option("--skip-packages", is_flag=True, help="Don't attempt to install apt/pip/npm packages")
|
||||||
@fqdn_required
|
@fqdn_required
|
||||||
def certidude_setup_authority(username, kerberos_keytab, nginx_config, country, state, locality, organization, organizational_unit, common_name, directory, authority_lifetime, push_server, outbox, server_flags, title):
|
def certidude_setup_authority(username, kerberos_keytab, nginx_config, country, state, locality, organization, organizational_unit, common_name, directory, authority_lifetime, push_server, outbox, server_flags, title, skip_packages):
|
||||||
assert os.getuid() == 0 and os.getgid() == 0, "Authority can be set up only by root"
|
assert os.getuid() == 0 and os.getgid() == 0, "Authority can be set up only by root"
|
||||||
|
|
||||||
import pwd
|
import pwd
|
||||||
from jinja2 import Environment, PackageLoader
|
from jinja2 import Environment, PackageLoader
|
||||||
env = Environment(loader=PackageLoader("certidude", "templates"), trim_blocks=True)
|
env = Environment(loader=PackageLoader("certidude", "templates"), trim_blocks=True)
|
||||||
|
|
||||||
|
if skip_packages:
|
||||||
|
click.echo("Not attempting to install packages from APT as requested...")
|
||||||
|
else:
|
||||||
click.echo("Installing packages...")
|
click.echo("Installing packages...")
|
||||||
os.system("apt-get install -qq -y cython3 python3-dev python3-mimeparse \
|
os.system("apt-get install -qq -y cython3 python3-dev python3-mimeparse \
|
||||||
python3-markdown python3-pyxattr python3-jinja2 python3-cffi \
|
python3-markdown python3-pyxattr python3-jinja2 python3-cffi \
|
||||||
software-properties-common libsasl2-modules-gssapi-mit npm nodejs \
|
software-properties-common libsasl2-modules-gssapi-mit npm nodejs \
|
||||||
libkrb5-dev libldap2-dev libsasl2-dev")
|
libkrb5-dev libldap2-dev libsasl2-dev gawk libncurses5-dev")
|
||||||
os.system("pip3 install -q --upgrade gssapi falcon humanize ipaddress simplepam")
|
os.system("pip3 install -q --upgrade gssapi falcon humanize ipaddress simplepam")
|
||||||
os.system("pip3 install -q --pre --upgrade python-ldap")
|
os.system("pip3 install -q --pre --upgrade python-ldap")
|
||||||
|
|
||||||
@ -1036,6 +1040,9 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country,
|
|||||||
else:
|
else:
|
||||||
click.echo("Warning: /etc/krb5.keytab or /etc/samba/smb.conf not found, Kerberos unconfigured")
|
click.echo("Warning: /etc/krb5.keytab or /etc/samba/smb.conf not found, Kerberos unconfigured")
|
||||||
|
|
||||||
|
doc_path = os.path.join(os.path.realpath(os.path.dirname(os.path.dirname(__file__))), "doc")
|
||||||
|
script_dir = os.path.join(os.path.realpath(os.path.dirname(__file__)), "templates", "script")
|
||||||
|
|
||||||
static_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), "static")
|
static_path = os.path.join(os.path.realpath(os.path.dirname(__file__)), "static")
|
||||||
certidude_path = sys.argv[0]
|
certidude_path = sys.argv[0]
|
||||||
|
|
||||||
@ -1057,6 +1064,13 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country,
|
|||||||
else:
|
else:
|
||||||
click.echo("Not systemd based OS, don't know how to set up initscripts")
|
click.echo("Not systemd based OS, don't know how to set up initscripts")
|
||||||
|
|
||||||
|
if os.path.exists("/etc/certidude/builder.conf"):
|
||||||
|
click.echo("Image builder config /etc/certidude/builder.conf already exists, remove to regenerate")
|
||||||
|
else:
|
||||||
|
with open("/etc/certidude/builder.conf", "w") as fh:
|
||||||
|
fh.write(env.get_template("server/builder.conf").render(vars()))
|
||||||
|
click.echo("File /etc/certidude/builder.conf created")
|
||||||
|
|
||||||
assert os.getuid() == 0 and os.getgid() == 0
|
assert os.getuid() == 0 and os.getgid() == 0
|
||||||
bootstrap_pid = os.fork()
|
bootstrap_pid = os.fork()
|
||||||
if not bootstrap_pid:
|
if not bootstrap_pid:
|
||||||
@ -1069,6 +1083,9 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country,
|
|||||||
os.makedirs(subdir)
|
os.makedirs(subdir)
|
||||||
|
|
||||||
# Install JavaScript pacakges
|
# Install JavaScript pacakges
|
||||||
|
if skip_packages:
|
||||||
|
click.echo("Not attempting to install packages from NPM as requested...")
|
||||||
|
else:
|
||||||
os.system("npm install --silent -g nunjucks@2.5.2 nunjucks-date@1.2.0 node-forge bootstrap@4.0.0-alpha.6 jquery timeago tether font-awesome qrcode-svg")
|
os.system("npm install --silent -g nunjucks@2.5.2 nunjucks-date@1.2.0 node-forge bootstrap@4.0.0-alpha.6 jquery timeago tether font-awesome qrcode-svg")
|
||||||
|
|
||||||
# Compile nunjucks templates
|
# Compile nunjucks templates
|
||||||
@ -1109,14 +1126,14 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country,
|
|||||||
if not os.path.exists(const.CONFIG_DIR):
|
if not os.path.exists(const.CONFIG_DIR):
|
||||||
click.echo("Creating %s" % const.CONFIG_DIR)
|
click.echo("Creating %s" % const.CONFIG_DIR)
|
||||||
os.makedirs(const.CONFIG_DIR)
|
os.makedirs(const.CONFIG_DIR)
|
||||||
if os.path.exists(const.CONFIG_PATH):
|
if os.path.exists(const.SERVER_CONFIG_PATH):
|
||||||
click.echo("Configuration file %s already exists, remove to regenerate" % const.CONFIG_PATH)
|
click.echo("Configuration file %s already exists, remove to regenerate" % const.SERVER_CONFIG_PATH)
|
||||||
else:
|
else:
|
||||||
os.umask(0o137)
|
os.umask(0o137)
|
||||||
push_token = "".join([random.choice(string.ascii_letters + string.digits) for j in range(0,32)])
|
push_token = "".join([random.choice(string.ascii_letters + string.digits) for j in range(0,32)])
|
||||||
with open(const.CONFIG_PATH, "w") as fh:
|
with open(const.SERVER_CONFIG_PATH, "w") as fh:
|
||||||
fh.write(env.get_template("server/server.conf").render(vars()))
|
fh.write(env.get_template("server/server.conf").render(vars()))
|
||||||
click.echo("Generated %s" % const.CONFIG_PATH)
|
click.echo("Generated %s" % const.SERVER_CONFIG_PATH)
|
||||||
|
|
||||||
# Create directory with 755 permissions
|
# Create directory with 755 permissions
|
||||||
os.umask(0o022)
|
os.umask(0o022)
|
||||||
@ -1183,7 +1200,7 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country,
|
|||||||
from certidude import authority
|
from certidude import authority
|
||||||
authority.self_enroll()
|
authority.self_enroll()
|
||||||
assert os.getuid() == 0 and os.getgid() == 0, "Enroll contaminated environment"
|
assert os.getuid() == 0 and os.getgid() == 0, "Enroll contaminated environment"
|
||||||
click.echo("To enable e-mail notifications install Postfix as sattelite system and set mailer address in %s" % const.CONFIG_PATH)
|
click.echo("To enable e-mail notifications install Postfix as sattelite system and set mailer address in %s" % const.SERVER_CONFIG_PATH)
|
||||||
click.echo()
|
click.echo()
|
||||||
click.echo("Use following commands to inspect the newly created files:")
|
click.echo("Use following commands to inspect the newly created files:")
|
||||||
click.echo()
|
click.echo()
|
||||||
@ -1337,7 +1354,7 @@ def certidude_serve(port, listen, fork):
|
|||||||
if port == 80:
|
if port == 80:
|
||||||
click.echo("WARNING: Please run Certidude behind nginx, remote address is assumed to be forwarded by nginx!")
|
click.echo("WARNING: Please run Certidude behind nginx, remote address is assumed to be forwarded by nginx!")
|
||||||
|
|
||||||
click.echo("Using configuration from: %s" % const.CONFIG_PATH)
|
click.echo("Using configuration from: %s" % const.SERVER_CONFIG_PATH)
|
||||||
|
|
||||||
log_handlers = []
|
log_handlers = []
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ from random import choice
|
|||||||
# Options that are parsed from config file are fetched here
|
# Options that are parsed from config file are fetched here
|
||||||
|
|
||||||
cp = configparser.RawConfigParser()
|
cp = configparser.RawConfigParser()
|
||||||
cp.readfp(codecs.open(const.CONFIG_PATH, "r", "utf8"))
|
cp.readfp(open(const.SERVER_CONFIG_PATH, "r"))
|
||||||
|
|
||||||
AUTHENTICATION_BACKENDS = set([j for j in
|
AUTHENTICATION_BACKENDS = set([j for j in
|
||||||
cp.get("authentication", "backends").split(" ") if j]) # kerberos, pam, ldap
|
cp.get("authentication", "backends").split(" ") if j]) # kerberos, pam, ldap
|
||||||
@ -99,7 +99,10 @@ TOKEN_SECRET = cp.get("token", "secret").encode("ascii")
|
|||||||
# TODO: Check if we don't have base or servers
|
# TODO: Check if we don't have base or servers
|
||||||
|
|
||||||
# The API call for looking up scripts uses following directory as root
|
# The API call for looking up scripts uses following directory as root
|
||||||
SCRIPT_DIR = os.path.join(os.path.dirname(__file__), "templates", "script")
|
SCRIPT_DIR = cp.get("script", "path")
|
||||||
SCRIPT_DEFAULT = "default.sh"
|
|
||||||
|
|
||||||
PROFILES = OrderedDict([[i, [j.strip() for j in cp.get("profile", i).split(",")]] for i in cp.options("profile")])
|
PROFILES = OrderedDict([[i, [j.strip() for j in cp.get("profile", i).split(",")]] for i in cp.options("profile")])
|
||||||
|
|
||||||
|
cp2 = configparser.RawConfigParser()
|
||||||
|
cp2.readfp(open(const.BUILDER_CONFIG_PATH, "r"))
|
||||||
|
IMAGE_BUILDER_PROFILES = [(j, cp2.get(j, "title"), cp2.get(j, "rename")) for j in cp2.sections()]
|
||||||
|
@ -7,7 +7,8 @@ import sys
|
|||||||
KEY_SIZE = 1024 if os.getenv("TRAVIS") else 4096
|
KEY_SIZE = 1024 if os.getenv("TRAVIS") else 4096
|
||||||
RUN_DIR = "/run/certidude"
|
RUN_DIR = "/run/certidude"
|
||||||
CONFIG_DIR = "/etc/certidude"
|
CONFIG_DIR = "/etc/certidude"
|
||||||
CONFIG_PATH = os.path.join(CONFIG_DIR, "server.conf")
|
SERVER_CONFIG_PATH = os.path.join(CONFIG_DIR, "server.conf")
|
||||||
|
BUILDER_CONFIG_PATH = os.path.join(CONFIG_DIR, "builder.conf")
|
||||||
CLIENT_CONFIG_PATH = os.path.join(CONFIG_DIR, "client.conf")
|
CLIENT_CONFIG_PATH = os.path.join(CONFIG_DIR, "client.conf")
|
||||||
SERVICES_CONFIG_PATH = os.path.join(CONFIG_DIR, "services.conf")
|
SERVICES_CONFIG_PATH = os.path.join(CONFIG_DIR, "services.conf")
|
||||||
SERVER_PID_PATH = os.path.join(RUN_DIR, "server.pid")
|
SERVER_PID_PATH = os.path.join(RUN_DIR, "server.pid")
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
|
||||||
import falcon
|
import falcon
|
||||||
import logging
|
import logging
|
||||||
|
import click
|
||||||
|
from asn1crypto import pem, x509
|
||||||
|
|
||||||
logger = logging.getLogger("api")
|
logger = logging.getLogger("api")
|
||||||
|
|
||||||
@ -50,6 +52,17 @@ def whitelist_subject(func):
|
|||||||
except IOError:
|
except IOError:
|
||||||
raise falcon.HTTPNotFound()
|
raise falcon.HTTPNotFound()
|
||||||
else:
|
else:
|
||||||
|
# First attempt to authenticate client with certificate
|
||||||
|
buf = req.get_header("X-SSL-CERT")
|
||||||
|
if buf:
|
||||||
|
header, _, der_bytes = pem.unarmor(buf.replace("\t", "").encode("ascii"))
|
||||||
|
origin_cert = x509.Certificate.load(der_bytes)
|
||||||
|
if origin_cert.native == cert.native:
|
||||||
|
click.echo("Subject authenticated using certificates")
|
||||||
|
return func(self, req, resp, cn, *args, **kwargs)
|
||||||
|
|
||||||
|
# For backwards compatibility check source IP address
|
||||||
|
# TODO: make it disableable
|
||||||
try:
|
try:
|
||||||
inner_address = getxattr(path, "user.lease.inner_address").decode("ascii")
|
inner_address = getxattr(path, "user.lease.inner_address").decode("ascii")
|
||||||
except IOError:
|
except IOError:
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
{% for key, value in certificate.attributes %}
|
{% for key, value in certificate.attributes %}
|
||||||
<span class="attribute icon {{ key | replace('.', ' ') }}" title="{{ key }}={{ value }}">{{ value }}</span>
|
<span class="badge badge-info" title="{{ key }}={{ value }}">{{ value }}</span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -28,7 +28,7 @@ curl -f -L -H "Content-type: application/pkcs10" --data-binary @client_req.pem \
|
|||||||
http://{{ window.location.hostname }}/api/request/?wait=yes > client_cert.pem</code></pre>
|
http://{{ window.location.hostname }}/api/request/?wait=yes > client_cert.pem</code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h5>OpenWrt/LEDE</h5>
|
<h5>Vanilla OpenWrt/LEDE</h5>
|
||||||
|
|
||||||
<p>On OpenWrt/LEDE router to convert it into VPN gateway:</p>
|
<p>On OpenWrt/LEDE router to convert it into VPN gateway:</p>
|
||||||
<div class="highlight">
|
<div class="highlight">
|
||||||
@ -45,6 +45,16 @@ curl -f -L -H "Content-type: application/pkcs10" \
|
|||||||
http://{{ window.location.hostname }}/api/request/?wait=yes</code></pre>
|
http://{{ window.location.hostname }}/api/request/?wait=yes</code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if session.authority.builder %}
|
||||||
|
<h5>OpenWrt/LEDE image builder</h5>
|
||||||
|
<p>Hit a link to generate machine specific image. Note that this might take couple minutes to finish.</p>
|
||||||
|
<ul>
|
||||||
|
{% for name, title, filename in session.authority.builder.profiles %}
|
||||||
|
<li><a href="/api/build/{{ name }}/{{ filename }}">{{ title }}</a></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<h5>SCEP</h5>
|
<h5>SCEP</h5>
|
||||||
<p>Use following as the enrollment URL: http://{{ window.location.hostname }}/cgi-bin/pkiclient.exe</p>
|
<p>Use following as the enrollment URL: http://{{ window.location.hostname }}/cgi-bin/pkiclient.exe</p>
|
||||||
|
|
||||||
|
@ -24,11 +24,17 @@
|
|||||||
Part of {{ certificate.organizational_unit }} organizational unit.
|
Part of {{ certificate.organizational_unit }} organizational unit.
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
{% if session.authority.tagging %}
|
{% if session.authority.tagging %}
|
||||||
<p class="tags" data-cn="{{ certificate.common_name }}">
|
<span class="tags" data-cn="{{ certificate.common_name }}">
|
||||||
{% include "views/tags.html" %}
|
{% include "views/tags.html" %}
|
||||||
</p>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
<span class="attributes" data-cn="{{ certificate.common_name }}">
|
||||||
|
{% include "views/attributes.html" %}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button type="button" class="btn btn-secondary" data-toggle="collapse" data-target="#details-{{ certificate.sha256sum }}"><i class="fa fa-list"></i> Details</button>
|
<button type="button" class="btn btn-secondary" data-toggle="collapse" data-target="#details-{{ certificate.sha256sum }}"><i class="fa fa-list"></i> Details</button>
|
||||||
<button type="button" class="btn btn-danger"
|
<button type="button" class="btn btn-danger"
|
||||||
@ -46,8 +52,7 @@
|
|||||||
onclick="javascript:$(this).button('loading');$.ajax({url:'/api/signed/{{certificate.common_name}}/?sha256sum={{ certificate.sha256sum }}&reason=9',type:'delete'});">Revoke due to withdrawn privilege</a>
|
onclick="javascript:$(this).button('loading');$.ajax({url:'/api/signed/{{certificate.common_name}}/?sha256sum={{ certificate.sha256sum }}&reason=9',type:'delete'});">Revoke due to withdrawn privilege</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="collapse" id="details-{{ certificate.sha256sum }}">
|
|
||||||
<p>
|
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
{% if session.authority.tagging %}
|
{% if session.authority.tagging %}
|
||||||
<button type="button" class="btn btn-default" onclick="onNewTagClicked(this);" data-key="other" data-cn="{{ certificate.common_name }}">
|
<button type="button" class="btn btn-default" onclick="onNewTagClicked(this);" data-key="other" data-cn="{{ certificate.common_name }}">
|
||||||
@ -63,8 +68,8 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</p>
|
|
||||||
|
|
||||||
|
<div class="collapse" id="details-{{ certificate.sha256sum }}">
|
||||||
<p>To fetch certificate:</p>
|
<p>To fetch certificate:</p>
|
||||||
|
|
||||||
<div class="bd-example">
|
<div class="bd-example">
|
||||||
@ -77,7 +82,7 @@ curl http://{{ window.location.hostname }}/api/signed/{{ certificate.common_name
|
|||||||
<pre><code class="language-bash" data-lang="bash">curl http://{{ window.location.hostname }}/api/certificate/ > session.pem
|
<pre><code class="language-bash" data-lang="bash">curl http://{{ window.location.hostname }}/api/certificate/ > session.pem
|
||||||
openssl ocsp -issuer session.pem -CAfile session.pem \
|
openssl ocsp -issuer session.pem -CAfile session.pem \
|
||||||
-url http://{{ window.location.hostname }}/api/ocsp/ \
|
-url http://{{ window.location.hostname }}/api/ocsp/ \
|
||||||
-serial 0x{{ certificate.serial }}</span></code></pre>
|
-serial 0x{{ certificate.serial }}</code></pre>
|
||||||
|
|
||||||
<p>To fetch script:</p>
|
<p>To fetch script:</p>
|
||||||
<pre><code class="language-bash" data-lang="bash">cd /var/lib/certidude/{{ window.location.hostname }}/
|
<pre><code class="language-bash" data-lang="bash">cd /var/lib/certidude/{{ window.location.hostname }}/
|
||||||
|
@ -2,5 +2,5 @@
|
|||||||
<span data-cn="{{ certificate.common_name }}"
|
<span data-cn="{{ certificate.common_name }}"
|
||||||
title="{{ tag.id }}"
|
title="{{ tag.id }}"
|
||||||
class="badge badge-default"
|
class="badge badge-default"
|
||||||
onClick="onTagClicked(this);">{{ tag.value }}</span>
|
onClick="onTagClicked(this);"><i class="fa fa-{{ tag.key }}"></i> {{ tag.value }}</span>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
31
certidude/templates/server/builder.conf
Normal file
31
certidude/templates/server/builder.conf
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
[tpl-archer-c7]
|
||||||
|
# Title shown in the UI
|
||||||
|
title = TP-Link Archer C7 (Access Point)
|
||||||
|
|
||||||
|
# Script to build the image, copy file to /etc/certidude/ and make modifications as necessary
|
||||||
|
command = {{ doc_path }}/build-ap.sh
|
||||||
|
|
||||||
|
# Path to filesystem overlay, used
|
||||||
|
overlay = {{ doc_path }}/overlay
|
||||||
|
|
||||||
|
# Site specific script to be copied to /etc/uci-defaults/99-site-script
|
||||||
|
script =
|
||||||
|
|
||||||
|
# Device/model/profile selection
|
||||||
|
model = archer-c7-v2
|
||||||
|
|
||||||
|
# File that will be picked from the bin/ folder
|
||||||
|
filename = archer-c7-v2-squashfs-factory-eu.bin
|
||||||
|
|
||||||
|
# And renamed to make it TFTP-friendly
|
||||||
|
rename = ArcherC7v2_tp_recovery.bin
|
||||||
|
|
||||||
|
[cf-e380ac]
|
||||||
|
title = Comfast E380AC (Access Point)
|
||||||
|
command = {{ doc_path }}/build-ap.sh
|
||||||
|
overlay = {{ doc_path }}/overlay
|
||||||
|
script =
|
||||||
|
model = cf-e380ac-v2
|
||||||
|
filename = cf-e380ac-v2-squashfs-factory.bin
|
||||||
|
rename = firmware_auto.bin
|
||||||
|
|
@ -198,3 +198,9 @@ default = client, 120,
|
|||||||
srv = server, 365, Server
|
srv = server, 365, Server
|
||||||
gw = server, 3, Gateway
|
gw = server, 3, Gateway
|
||||||
ap = client, 1825, Access Point
|
ap = client, 1825, Access Point
|
||||||
|
|
||||||
|
[script]
|
||||||
|
# Path to the folder with scripts that can be served to the clients, set none to disable scripting
|
||||||
|
path = {{ script_dir }}
|
||||||
|
;path = /etc/certidude/script
|
||||||
|
;path =
|
||||||
|
58
doc/build-ap.sh
Normal file
58
doc/build-ap.sh
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
umask 022
|
||||||
|
|
||||||
|
VERSION=17.01.4
|
||||||
|
BASENAME=lede-imagebuilder-$VERSION-ar71xx-generic.Linux-x86_64
|
||||||
|
FILENAME=$BASENAME.tar.xz
|
||||||
|
URL=http://downloads.lede-project.org/releases/$VERSION/targets/ar71xx/generic/$FILENAME
|
||||||
|
|
||||||
|
PACKAGES="luci luci-app-commands \
|
||||||
|
collectd collectd-mod-conntrack collectd-mod-interface \
|
||||||
|
collectd-mod-iwinfo collectd-mod-load collectd-mod-memory \
|
||||||
|
collectd-mod-network collectd-mod-protocols collectd-mod-tcpconns \
|
||||||
|
collectd-mod-uptime \
|
||||||
|
openssl-util openvpn-openssl curl ca-certificates \
|
||||||
|
htop iftop tcpdump nmap nano -odhcp6c -odhcpd -dnsmasq \
|
||||||
|
-luci-app-firewall \
|
||||||
|
-pppd -luci-proto-ppp -kmod-ppp -ppp -ppp-mod-pppoe \
|
||||||
|
-kmod-ip6tables -ip6tables -luci-proto-ipv6 -kmod-iptunnel6 -kmod-ipsec6"
|
||||||
|
|
||||||
|
|
||||||
|
if [ ! -e $FILENAME ]; then
|
||||||
|
wget -q $URL
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -e $BASENAME ]; then
|
||||||
|
tar xf $FILENAME
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd $BASENAME
|
||||||
|
|
||||||
|
# Copy CA certificate
|
||||||
|
AUTHORITY=$(hostname -f)
|
||||||
|
CERTIDUDE_DIR=/var/lib/certidude/$AUTHORITY
|
||||||
|
if [ -d "$CERTIDUDE_DIR" ]; then
|
||||||
|
mkdir -p overlay/$CERTIDUDE_DIR
|
||||||
|
cp $CERTIDUDE_DIR/ca_cert.pem overlay/$CERTIDUDE_DIR
|
||||||
|
fi
|
||||||
|
|
||||||
|
cat < EOF > overlay/etc/config/certidude
|
||||||
|
|
||||||
|
config authority
|
||||||
|
option url http://$AUTHORITY
|
||||||
|
option authority_path /var/lib/certidude/$AUTHORITY/ca_cert.pem
|
||||||
|
option request_path /var/lib/certidude/$AUTHORITY/client_req.pem
|
||||||
|
option certificate_path /var/lib/certidude/$AUTHORITY/client_cert.pem
|
||||||
|
option key_path /var/lib/certidude/$AUTHORITY/client_key.pem
|
||||||
|
option key_type rsa
|
||||||
|
option key_length 1024
|
||||||
|
option red_led gl-connect:red:wlan
|
||||||
|
option green_led gl-connect:green:lan
|
||||||
|
|
||||||
|
EOF
|
||||||
|
|
||||||
|
make image FILES=../overlay/ PACKAGES="$PACKAGES" PROFILE="$PROFILE"
|
||||||
|
|
32
doc/overlay/etc/profile
Normal file
32
doc/overlay/etc/profile
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
[ -f /etc/banner ] && cat /etc/banner
|
||||||
|
[ -e /tmp/.failsafe ] && cat /etc/banner.failsafe
|
||||||
|
|
||||||
|
export PATH=/usr/bin:/usr/sbin:/bin:/sbin
|
||||||
|
export HOME=$(grep -e "^${USER:-root}:" /etc/passwd | cut -d ":" -f 6)
|
||||||
|
export HOME=${HOME:-/root}
|
||||||
|
export PS1='\u@\h:\w\$ '
|
||||||
|
|
||||||
|
[ -z "$KSH_VERSION" -o \! -s /etc/mkshrc ] || . /etc/mkshrc
|
||||||
|
[ -x /bin/more ] || alias more=less
|
||||||
|
[ -x /usr/bin/vim ] && alias vi=vim || alias vim=vi
|
||||||
|
[ -x /usr/bin/arp ] || arp() { cat /proc/net/arp; }
|
||||||
|
[ -x /usr/bin/ldd ] || ldd() { LD_TRACE_LOADED_OBJECTS=1 $*; }
|
||||||
|
|
||||||
|
HOSTNAME=$(uci get system.@system[0].hostname)
|
||||||
|
DOMAIN=$(uci -q get dhcp.@dnsmasq[0].domain)
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
FQDN=$HOSTNAME.$DOMAIN
|
||||||
|
else
|
||||||
|
FQDN=$HOSTNAME
|
||||||
|
fi
|
||||||
|
|
||||||
|
export PS1='\[\033[01;31m\]$FQDN\[\033[01;34m\] \W #\[\033[00m\] '
|
||||||
|
case "$TERM" in
|
||||||
|
xterm*|rxvt*)
|
||||||
|
echo -ne "\033]0;${USER}@${FQDN}:${PWD}\007"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
;;
|
||||||
|
esac
|
21
doc/overlay/etc/uci-defaults/40-hostname
Normal file
21
doc/overlay/etc/uci-defaults/40-hostname
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MODEL=$(cat /etc/board.json | jsonfilter -e '@["model"]["id"]')
|
||||||
|
|
||||||
|
# Hostname prefix
|
||||||
|
case $MODEL in
|
||||||
|
tl-*|archer-*) VENDOR=tplink ;;
|
||||||
|
cf-*) VENDOR=comfast ;;
|
||||||
|
*) VENDOR=ap ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Network interface with relevant MAC address
|
||||||
|
case $MODEL in
|
||||||
|
tl-wdr*) NIC=wlan1 ;;
|
||||||
|
archer-*) NIC=eth1 ;;
|
||||||
|
cf-e380ac-v2) NIC=eth0 ;;
|
||||||
|
*) NIC=wlan0 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
HOSTNAME=$VENDOR-$(cat /sys/class/net/$NIC/address | cut -d : -f 4- | sed -e 's/://g')
|
||||||
|
uci set system.@system[0].hostname=$HOSTNAME
|
||||||
|
uci set network.lan.hostname=$HOSTNAME
|
||||||
|
|
66
doc/overlay/etc/uci-defaults/50-access-point
Normal file
66
doc/overlay/etc/uci-defaults/50-access-point
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# Disable DHCP servers
|
||||||
|
/etc/init.d/odhcpd disable
|
||||||
|
/etc/init.d/dnsmasq disable
|
||||||
|
|
||||||
|
# Remove firewall rules since AP bridges ethernet to wireless anyway
|
||||||
|
uci delete firewall.@zone[1]
|
||||||
|
uci delete firewall.@zone[0]
|
||||||
|
uci delete firewall.@forwarding[0]
|
||||||
|
for j in $(seq 0 10); do uci delete firewall.@rule[0]; done
|
||||||
|
|
||||||
|
# Remove WAN interface
|
||||||
|
uci delete network.wan
|
||||||
|
uci delete network.wan6
|
||||||
|
|
||||||
|
# Reconfigure DHCP client for bridge over LAN and WAN ports
|
||||||
|
uci delete network.lan.ipaddr
|
||||||
|
uci delete network.lan.netmask
|
||||||
|
uci delete network.lan.ip6assign
|
||||||
|
uci delete network.globals.ula_prefix
|
||||||
|
uci delete network.@switch_vlan[1]
|
||||||
|
uci delete dhcp.@dnsmasq[0].domain
|
||||||
|
uci set network.lan.proto=dhcp
|
||||||
|
uci set network.lan.ipv6=0
|
||||||
|
uci set network.lan.ifname='eth0'
|
||||||
|
uci set network.lan.stp=1
|
||||||
|
|
||||||
|
# Radio ordering differs among models
|
||||||
|
case $(uci get wireless.radio0.hwmode) in
|
||||||
|
11a) uci rename wireless.radio0=radio5ghz;;
|
||||||
|
11g) uci rename wireless.radio0=radio2ghz;;
|
||||||
|
esac
|
||||||
|
case $(uci get wireless.radio1.hwmode) in
|
||||||
|
11a) uci rename wireless.radio1=radio5ghz;;
|
||||||
|
11g) uci rename wireless.radio1=radio2ghz;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Reset virtual SSID-s
|
||||||
|
uci delete wireless.@wifi-iface[1]
|
||||||
|
uci delete wireless.@wifi-iface[0]
|
||||||
|
|
||||||
|
# Pseudorandomize channel selection, should work with 80MHz on 5GHz band
|
||||||
|
case $(uci get system.@system[0].hostname | md5sum) in
|
||||||
|
1*|2*|3*|4*) uci set wireless.radio2ghz.channel=1; uci set wireless.radio5ghz.channel=36 ;;
|
||||||
|
5*|6*|7*|8*) uci set wireless.radio2ghz.channel=5; uci set wireless.radio5ghz.channel=52 ;;
|
||||||
|
9*|0*|a*|b*) uci set wireless.radio2ghz.channel=9; uci set wireless.radio5ghz.channel=100 ;;
|
||||||
|
c*|d*|e*|f*) uci set wireless.radio2ghz.channel=13; uci set wireless.radio5ghz.channel=132 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Create bridge for guests
|
||||||
|
uci set network.guest=interface
|
||||||
|
uci set network.guest.proto='static'
|
||||||
|
uci set network.guest.address='0.0.0.0'
|
||||||
|
uci set network.guest.type='bridge'
|
||||||
|
uci set network.guest.ifname='eth0.156' # tag id 156 for guest network
|
||||||
|
uci set network.guest.ipaddr='0.0.0.0'
|
||||||
|
uci set network.guest.ipv6=0
|
||||||
|
uci set network.guest.stp=1
|
||||||
|
|
||||||
|
# Disable switch tagging and bridge all ports on TP-Link WDR3600/WDR4300
|
||||||
|
case $(cat /etc/board.json | jsonfilter -e '@["model"]["id"]') in
|
||||||
|
tl-wdr*)
|
||||||
|
uci set network.@switch[0].enable_vlan=0
|
||||||
|
uci set network.@switch_vlan[0].ports='0 1 2 3 4 5 6'
|
||||||
|
;;
|
||||||
|
*) ;;
|
||||||
|
esac
|
Loading…
Reference in New Issue
Block a user