mirror of
				https://github.com/laurivosandi/certidude
				synced 2025-10-30 17:09:19 +00:00 
			
		
		
		
	Integrate LEDE image builder
This commit is contained in:
		| @@ -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,19 +944,23 @@ 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) | ||||
|  | ||||
|     if skip_packages: | ||||
|         click.echo("Not attempting to install packages from APT as requested...") | ||||
|     else: | ||||
|         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") | ||||
|             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") | ||||
|  | ||||
| @@ -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,6 +1083,9 @@ def certidude_setup_authority(username, kerberos_keytab, nginx_config, country, | ||||
|                 os.makedirs(subdir) | ||||
|  | ||||
|         # 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") | ||||
|  | ||||
|         # 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): | ||||
|             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: | ||||
|   | ||||
| @@ -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,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> | ||||
|       </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 }}"> | ||||
| @@ -63,8 +68,8 @@ | ||||
|         </div> | ||||
|       {% endif %} | ||||
|     </div> | ||||
|       </p> | ||||
|  | ||||
|     <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 | ||||
		Reference in New Issue
	
	Block a user