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 = {}
|
||||
for key in listxattr(path):
|
||||
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
|
||||
try:
|
||||
@ -131,6 +131,9 @@ class SessionResource(object):
|
||||
),
|
||||
request_submission_allowed = config.REQUEST_SUBMISSION_ALLOWED,
|
||||
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],
|
||||
lease = dict(
|
||||
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 .bootstrap import BootstrapResource
|
||||
from .token import TokenResource
|
||||
from .builder import ImageBuilderResource
|
||||
|
||||
app = falcon.API(middleware=NormalizeMiddleware())
|
||||
app.req_options.auto_parse_form_urlencoded = True
|
||||
@ -240,6 +244,9 @@ def certidude_app(log_handlers=[]):
|
||||
# Bootstrap resource
|
||||
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
|
||||
if config.CRL_SUBNETS:
|
||||
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 logging
|
||||
import os
|
||||
from certidude import const, config, authority
|
||||
from certidude.decorators import serialize
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
@ -26,9 +27,10 @@ class ScriptResource():
|
||||
except AttributeError: # No tags
|
||||
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.body = env.get_template(script).render(
|
||||
resp.body = env.get_template(os.path.join(script)).render(
|
||||
authority_name=const.FQDN,
|
||||
common_name=cn,
|
||||
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("-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):
|
||||
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")
|
||||
from certidude import authority
|
||||
authority.self_enroll()
|
||||
@ -944,38 +944,42 @@ def certidude_setup_openvpn_networkmanager(authority, remote, common_name, **pat
|
||||
@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("--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
|
||||
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"
|
||||
|
||||
import pwd
|
||||
from jinja2 import Environment, PackageLoader
|
||||
env = Environment(loader=PackageLoader("certidude", "templates"), trim_blocks=True)
|
||||
|
||||
click.echo("Installing packages...")
|
||||
os.system("apt-get install -qq -y cython3 python3-dev python3-mimeparse \
|
||||
python3-markdown python3-pyxattr python3-jinja2 python3-cffi \
|
||||
software-properties-common libsasl2-modules-gssapi-mit npm nodejs \
|
||||
libkrb5-dev libldap2-dev libsasl2-dev")
|
||||
os.system("pip3 install -q --upgrade gssapi falcon humanize ipaddress simplepam")
|
||||
os.system("pip3 install -q --pre --upgrade python-ldap")
|
||||
|
||||
if not os.path.exists("/usr/lib/nginx/modules/ngx_nchan_module.so"):
|
||||
click.echo("Enabling nginx PPA")
|
||||
os.system("add-apt-repository -y ppa:nginx/stable")
|
||||
os.system("apt-get update -q")
|
||||
os.system("apt-get install -y -q libnginx-mod-nchan")
|
||||
if skip_packages:
|
||||
click.echo("Not attempting to install packages from APT as requested...")
|
||||
else:
|
||||
click.echo("PPA for nginx already enabled")
|
||||
click.echo("Installing packages...")
|
||||
os.system("apt-get install -qq -y cython3 python3-dev python3-mimeparse \
|
||||
python3-markdown python3-pyxattr python3-jinja2 python3-cffi \
|
||||
software-properties-common libsasl2-modules-gssapi-mit npm nodejs \
|
||||
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 --pre --upgrade python-ldap")
|
||||
|
||||
if not os.path.exists("/usr/sbin/nginx"):
|
||||
click.echo("Installing nginx from PPA")
|
||||
os.system("apt-get install -y -q nginx")
|
||||
else:
|
||||
click.echo("Web server nginx already installed")
|
||||
if not os.path.exists("/usr/lib/nginx/modules/ngx_nchan_module.so"):
|
||||
click.echo("Enabling nginx PPA")
|
||||
os.system("add-apt-repository -y ppa:nginx/stable")
|
||||
os.system("apt-get update -q")
|
||||
os.system("apt-get install -y -q libnginx-mod-nchan")
|
||||
else:
|
||||
click.echo("PPA for nginx already enabled")
|
||||
|
||||
if not os.path.exists("/usr/bin/node"):
|
||||
os.symlink("/usr/bin/nodejs", "/usr/bin/node")
|
||||
if not os.path.exists("/usr/sbin/nginx"):
|
||||
click.echo("Installing nginx from PPA")
|
||||
os.system("apt-get install -y -q nginx")
|
||||
else:
|
||||
click.echo("Web server nginx already installed")
|
||||
|
||||
if not os.path.exists("/usr/bin/node"):
|
||||
os.symlink("/usr/bin/nodejs", "/usr/bin/node")
|
||||
|
||||
# Generate secret for tokens
|
||||
token_secret = ''.join(random.choice(string.ascii_letters + string.digits + '!@#$%^&*()') for i in range(50))
|
||||
@ -1036,6 +1040,9 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country,
|
||||
else:
|
||||
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")
|
||||
certidude_path = sys.argv[0]
|
||||
|
||||
@ -1057,6 +1064,13 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country,
|
||||
else:
|
||||
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
|
||||
bootstrap_pid = os.fork()
|
||||
if not bootstrap_pid:
|
||||
@ -1069,7 +1083,10 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country,
|
||||
os.makedirs(subdir)
|
||||
|
||||
# Install JavaScript pacakges
|
||||
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")
|
||||
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")
|
||||
|
||||
# Compile nunjucks templates
|
||||
cmd = 'nunjucks-precompile --include ".html$" --include ".svg" %s > %s.part' % (static_path, bundle_js)
|
||||
@ -1109,14 +1126,14 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country,
|
||||
if not os.path.exists(const.CONFIG_DIR):
|
||||
click.echo("Creating %s" % const.CONFIG_DIR)
|
||||
os.makedirs(const.CONFIG_DIR)
|
||||
if os.path.exists(const.CONFIG_PATH):
|
||||
click.echo("Configuration file %s already exists, remove to regenerate" % const.CONFIG_PATH)
|
||||
if os.path.exists(const.SERVER_CONFIG_PATH):
|
||||
click.echo("Configuration file %s already exists, remove to regenerate" % const.SERVER_CONFIG_PATH)
|
||||
else:
|
||||
os.umask(0o137)
|
||||
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()))
|
||||
click.echo("Generated %s" % const.CONFIG_PATH)
|
||||
click.echo("Generated %s" % const.SERVER_CONFIG_PATH)
|
||||
|
||||
# Create directory with 755 permissions
|
||||
os.umask(0o022)
|
||||
@ -1183,7 +1200,7 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country,
|
||||
from certidude import authority
|
||||
authority.self_enroll()
|
||||
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("Use following commands to inspect the newly created files:")
|
||||
click.echo()
|
||||
@ -1337,7 +1354,7 @@ def certidude_serve(port, listen, fork):
|
||||
if port == 80:
|
||||
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 = []
|
||||
|
||||
|
@ -12,7 +12,7 @@ from random import choice
|
||||
# Options that are parsed from config file are fetched here
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
# The API call for looking up scripts uses following directory as root
|
||||
SCRIPT_DIR = os.path.join(os.path.dirname(__file__), "templates", "script")
|
||||
SCRIPT_DEFAULT = "default.sh"
|
||||
SCRIPT_DIR = cp.get("script", "path")
|
||||
|
||||
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
|
||||
RUN_DIR = "/run/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")
|
||||
SERVICES_CONFIG_PATH = os.path.join(CONFIG_DIR, "services.conf")
|
||||
SERVER_PID_PATH = os.path.join(RUN_DIR, "server.pid")
|
||||
|
@ -1,6 +1,8 @@
|
||||
|
||||
import falcon
|
||||
import logging
|
||||
import click
|
||||
from asn1crypto import pem, x509
|
||||
|
||||
logger = logging.getLogger("api")
|
||||
|
||||
@ -50,6 +52,17 @@ def whitelist_subject(func):
|
||||
except IOError:
|
||||
raise falcon.HTTPNotFound()
|
||||
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:
|
||||
inner_address = getxattr(path, "user.lease.inner_address").decode("ascii")
|
||||
except IOError:
|
||||
@ -58,6 +71,6 @@ def whitelist_subject(func):
|
||||
if req.context.get("remote_addr") != ip_address(inner_address):
|
||||
raise falcon.HTTPForbidden("Forbidden", "Remote address %s mismatch" % req.context.get("remote_addr"))
|
||||
else:
|
||||
return func(self, req, resp, cn, *args, **kwargs)
|
||||
return func(self, req, resp, cn, *args, **kwargs)
|
||||
return wrapped
|
||||
|
||||
|
@ -1,3 +1,3 @@
|
||||
{% 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 %}
|
||||
|
@ -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>
|
||||
</div>
|
||||
|
||||
<h5>OpenWrt/LEDE</h5>
|
||||
<h5>Vanilla OpenWrt/LEDE</h5>
|
||||
|
||||
<p>On OpenWrt/LEDE router to convert it into VPN gateway:</p>
|
||||
<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>
|
||||
</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>
|
||||
<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.
|
||||
{% endif %}
|
||||
</p>
|
||||
<p>
|
||||
{% if session.authority.tagging %}
|
||||
<p class="tags" data-cn="{{ certificate.common_name }}">
|
||||
<span class="tags" data-cn="{{ certificate.common_name }}">
|
||||
{% include "views/tags.html" %}
|
||||
</p>
|
||||
</span>
|
||||
{% endif %}
|
||||
<span class="attributes" data-cn="{{ certificate.common_name }}">
|
||||
{% include "views/attributes.html" %}
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<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-danger"
|
||||
@ -46,25 +52,24 @@
|
||||
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 class="collapse" id="details-{{ certificate.sha256sum }}">
|
||||
<p>
|
||||
<div class="btn-group">
|
||||
{% if session.authority.tagging %}
|
||||
<button type="button" class="btn btn-default" onclick="onNewTagClicked(this);" data-key="other" data-cn="{{ certificate.common_name }}">
|
||||
<i class="fa fa-tag"></i> Tag</button>
|
||||
<button type="button" class="btn btn-default dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
{% for tag_category in session.authority.tagging %}
|
||||
<a class="dropdown-item" href="#" data-key="{{ tag_category.name }}" data-cn="{{ certificate.common_name }}"
|
||||
onclick="onNewTagClicked(this);">{{ tag_category.title }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</p>
|
||||
|
||||
<div class="btn-group">
|
||||
{% if session.authority.tagging %}
|
||||
<button type="button" class="btn btn-default" onclick="onNewTagClicked(this);" data-key="other" data-cn="{{ certificate.common_name }}">
|
||||
<i class="fa fa-tag"></i> Tag</button>
|
||||
<button type="button" class="btn btn-default dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<span class="sr-only">Toggle Dropdown</span>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
{% for tag_category in session.authority.tagging %}
|
||||
<a class="dropdown-item" href="#" data-key="{{ tag_category.name }}" data-cn="{{ certificate.common_name }}"
|
||||
onclick="onNewTagClicked(this);">{{ tag_category.title }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="collapse" id="details-{{ certificate.sha256sum }}">
|
||||
<p>To fetch certificate:</p>
|
||||
|
||||
<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
|
||||
openssl ocsp -issuer session.pem -CAfile session.pem \
|
||||
-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>
|
||||
<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 }}"
|
||||
title="{{ tag.id }}"
|
||||
class="badge badge-default"
|
||||
onClick="onTagClicked(this);">{{ tag.value }}</span>
|
||||
onClick="onTagClicked(this);"><i class="fa fa-{{ tag.key }}"></i> {{ tag.value }}</span>
|
||||
{% 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
|
||||
gw = server, 3, Gateway
|
||||
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