1
0
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:
Lauri Võsandi 2018-01-03 22:12:02 +00:00
parent 345c2802ea
commit fba8f5d776
18 changed files with 386 additions and 62 deletions

View File

@ -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
View 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()

View File

@ -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,

View File

@ -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 = []

View File

@ -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()]

View File

@ -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")

View File

@ -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

View File

@ -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 %}

View File

@ -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>

View File

@ -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 }}/

View File

@ -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 %}

View 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

View File

@ -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
View 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
View 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

View 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

View 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