mirror of
				https://github.com/laurivosandi/certidude
				synced 2025-10-30 17:09:19 +00:00 
			
		
		
		
	Bugfixes, OU support and image builder fixes
This commit is contained in:
		| @@ -44,6 +44,7 @@ class SessionResource(object): | ||||
|                 except IOError: | ||||
|                     submission_hostname = None | ||||
|                 yield dict( | ||||
|                     server = authority.server_flags(common_name), | ||||
|                     submitted = submitted, | ||||
|                     common_name = common_name, | ||||
|                     address = submission_address, | ||||
| @@ -103,6 +104,7 @@ class SessionResource(object): | ||||
|  | ||||
|                 yield dict( | ||||
|                     serial = "%x" % cert.serial_number, | ||||
|                     organizational_unit = cert.subject.native.get("organizational_unit_name"), | ||||
|                     common_name = common_name, | ||||
|                     # TODO: key type, key length, key exponent, key modulo | ||||
|                     signed = signed, | ||||
| @@ -158,10 +160,8 @@ class SessionResource(object): | ||||
|                 request_subnets = config.REQUEST_SUBNETS or None, | ||||
|                 admin_subnets=config.ADMIN_SUBNETS or None, | ||||
|                 signature = dict( | ||||
|                     server_certificate_lifetime=config.SERVER_CERTIFICATE_LIFETIME, | ||||
|                     client_certificate_lifetime=config.CLIENT_CERTIFICATE_LIFETIME, | ||||
|                     revocation_list_lifetime=config.REVOCATION_LIST_LIFETIME, | ||||
|                     profiles = [dict(organizational_unit=ou, flags=f, lifetime=lt) for f, lt, ou in config.PROFILES.values()] | ||||
|                     profiles = [dict(name=k, server=v[0]=="server", lifetime=v[1], organizational_unit=v[2], title=v[3]) for k,v in config.PROFILES.items()] | ||||
|                 ) | ||||
|             ) if req.context.get("user").is_admin() else None, | ||||
|             features=dict( | ||||
|   | ||||
| @@ -21,6 +21,7 @@ class ImageBuilderResource(object): | ||||
|         suffix = config.cp2.get(profile, "filename") | ||||
|  | ||||
|         build = "/var/lib/certidude/builder/" + profile | ||||
|         log_path = build + "/build.log" | ||||
|         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/") | ||||
| @@ -31,12 +32,16 @@ class ImageBuilderResource(object): | ||||
|                 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, | ||||
|             stdout=open(log_path, "w"), stderr=subprocess.STDOUT, | ||||
|             close_fds=True, shell=False, | ||||
|             cwd=build, | ||||
|             env={"PROFILE":model, "PATH":"/usr/sbin:/usr/bin:/sbin:/bin"}, | ||||
|             cwd=os.path.dirname(os.path.realpath(build_script_path)), | ||||
|             env={"PROFILE":model, "PATH":"/usr/sbin:/usr/bin:/sbin:/bin", | ||||
|                 "BUILD":build, "OVERLAY":build + "/overlay/"}, | ||||
|             startupinfo=None, creationflags=0) | ||||
|         proc.communicate() | ||||
|         if proc.returncode: | ||||
|             logger.info("Build script finished with non-zero exitcode, see %s for more information" % log_path) | ||||
|             raise falcon.HTTPInternalServerError("Build script finished with non-zero exitcode") | ||||
|  | ||||
|         for dname in os.listdir(build): | ||||
|             if dname.startswith("lede-imagebuilder-"): | ||||
|   | ||||
| @@ -33,6 +33,11 @@ class LeaseResource(object): | ||||
|     @authorize_server | ||||
|     def on_post(self, req, resp): | ||||
|         client_common_name = req.get_param("client", required=True) | ||||
|         if "=" in client_common_name: # It's actually DN, resolve it to CN | ||||
|             _, client_common_name = client_common_name.split(" CN=", 1) | ||||
|             if "," in client_common_name: | ||||
|                 client_common_name, _ = client_common_name.split(",", 1) | ||||
|  | ||||
|         path, buf, cert, signed, expires = authority.get_signed(client_common_name) # TODO: catch exceptions | ||||
|         if req.get_param("serial") and cert.serial_number != req.get_param_as_int("serial"): # OCSP-ish solution for OpenVPN, not exposed for StrongSwan | ||||
|             raise falcon.HTTPForbidden("Forbidden", "Invalid serial number supplied") | ||||
|   | ||||
| @@ -225,7 +225,10 @@ class RequestDetailResource(object): | ||||
|         Sign a certificate signing request | ||||
|         """ | ||||
|         try: | ||||
|             cert, buf = authority.sign(cn, ou=req.get_param("ou"), overwrite=True, signer=req.context.get("user").name) | ||||
|             cert, buf = authority.sign(cn, | ||||
|                 profile=req.get_param("profile", default="default"), | ||||
|                 overwrite=True, | ||||
|                 signer=req.context.get("user").name) | ||||
|             # Mailing and long poll publishing implemented in the function above | ||||
|         except EnvironmentError: # no such CSR | ||||
|             raise falcon.HTTPNotFound() | ||||
|   | ||||
| @@ -38,6 +38,7 @@ class SignedCertificateDetailResource(object): | ||||
|                 common_name = cn, | ||||
|                 signer = signer_username, | ||||
|                 serial_number = "%x" % cert.serial_number, | ||||
|                 organizational_unit = cert.subject.native.get("organizational_unit_name"), | ||||
|                 signed = cert["tbs_certificate"]["validity"]["not_before"].native.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z", | ||||
|                 expires = cert["tbs_certificate"]["validity"]["not_after"].native.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z", | ||||
|                 sha256sum = hashlib.sha256(buf).hexdigest())) | ||||
|   | ||||
| @@ -55,7 +55,7 @@ def self_enroll(): | ||||
|             fh.write(asymmetric.dump_private_key(private_key, None)) | ||||
|     else: | ||||
|         now = datetime.utcnow() | ||||
|         if now - timedelta(days=1) < expires: | ||||
|         if now + timedelta(days=1) < expires: | ||||
|             click.echo("Certificate %s still valid, delete to self-enroll again" % path) | ||||
|             return | ||||
|  | ||||
| @@ -307,7 +307,7 @@ def delete_request(common_name): | ||||
|         config.LONG_POLL_PUBLISH % hashlib.sha256(buf).hexdigest(), | ||||
|         headers={"User-Agent": "Certidude API"}) | ||||
|  | ||||
| def sign(common_name, skip_notify=False, skip_push=False, overwrite=False, ou=None, signer=None): | ||||
| def sign(common_name, skip_notify=False, skip_push=False, overwrite=False, profile="default", signer=None): | ||||
|     """ | ||||
|     Sign certificate signing request by it's common name | ||||
|     """ | ||||
| @@ -320,13 +320,15 @@ def sign(common_name, skip_notify=False, skip_push=False, overwrite=False, ou=No | ||||
|  | ||||
|  | ||||
|     # Sign with function below | ||||
|     cert, buf = _sign(csr, csr_buf, skip_notify, skip_push, overwrite, ou, signer) | ||||
|     cert, buf = _sign(csr, csr_buf, skip_notify, skip_push, overwrite, profile, signer) | ||||
|  | ||||
|     os.unlink(req_path) | ||||
|     return cert, buf | ||||
|  | ||||
| def _sign(csr, buf, skip_notify=False, skip_push=False, overwrite=False, ou=None, signer=None): | ||||
| def _sign(csr, buf, skip_notify=False, skip_push=False, overwrite=False, profile="default", signer=None): | ||||
|     # TODO: CRLDistributionPoints, OCSP URL, Certificate URL | ||||
|     if profile not in config.PROFILES: | ||||
|         raise ValueError("Invalid profile supplied '%s'" % profile) | ||||
|  | ||||
|     assert buf.startswith(b"-----BEGIN CERTIFICATE REQUEST-----") | ||||
|     assert isinstance(csr, CertificationRequest) | ||||
| @@ -367,8 +369,9 @@ def _sign(csr, buf, skip_notify=False, skip_push=False, overwrite=False, ou=None | ||||
|  | ||||
|     # Sign via signer process | ||||
|     dn = {u'common_name': common_name } | ||||
|     if ou: | ||||
|         dn["organizational_unit"] = ou | ||||
|     profile_server_flags, lifetime, dn["organizational_unit_name"], _ = config.PROFILES[profile] | ||||
|     lifetime = int(lifetime) | ||||
|  | ||||
|     builder = CertificateBuilder(dn, csr_pubkey) | ||||
|     builder.serial_number = random.randint( | ||||
|         0x1000000000000000000000000000000000000000, | ||||
| @@ -376,16 +379,14 @@ def _sign(csr, buf, skip_notify=False, skip_push=False, overwrite=False, ou=None | ||||
|  | ||||
|     now = datetime.utcnow() | ||||
|     builder.begin_date = now - timedelta(minutes=5) | ||||
|     builder.end_date = now + timedelta(days=config.SERVER_CERTIFICATE_LIFETIME | ||||
|         if server_flags(common_name) | ||||
|         else config.CLIENT_CERTIFICATE_LIFETIME) | ||||
|     builder.end_date = now + timedelta(days=lifetime) | ||||
|     builder.issuer = certificate | ||||
|     builder.ca = False | ||||
|     builder.key_usage = set(["digital_signature", "key_encipherment"]) | ||||
|  | ||||
|     # OpenVPN uses CN while StrongSwan uses SAN | ||||
|     if server_flags(common_name): | ||||
|         builder.subject_alt_domains = [common_name] | ||||
|     # If we have FQDN and profile suggests server flags, enable them | ||||
|     if server_flags(common_name) and profile_server_flags: | ||||
|         builder.subject_alt_domains = [common_name] # OpenVPN uses CN while StrongSwan uses SAN to match hostname of the server | ||||
|         builder.extended_key_usage = set(["server_auth", "1.3.6.1.5.5.8.2.2", "client_auth"]) | ||||
|     else: | ||||
|         builder.extended_key_usage = set(["client_auth"]) | ||||
|   | ||||
| @@ -1312,7 +1312,7 @@ def certidude_list(verbose, show_key_type, show_extensions, show_path, show_sign | ||||
| def certidude_sign(common_name, overwrite): | ||||
|     from certidude import authority | ||||
|     drop_privileges() | ||||
|     cert = authority.sign(common_name, overwrite) | ||||
|     cert = authority.sign(common_name, overwrite=overwrite) | ||||
|  | ||||
|  | ||||
| @click.command("revoke", help="Revoke certificate") | ||||
|   | ||||
| @@ -60,8 +60,6 @@ USER_MULTIPLE_CERTIFICATES = { | ||||
|     cp.get("authority", "user enrollment")] | ||||
|  | ||||
| REQUEST_SUBMISSION_ALLOWED = cp.getboolean("authority", "request submission allowed") | ||||
| CLIENT_CERTIFICATE_LIFETIME = cp.getint("signature", "client certificate lifetime") | ||||
| SERVER_CERTIFICATE_LIFETIME = cp.getint("signature", "server certificate lifetime") | ||||
| AUTHORITY_CERTIFICATE_URL = cp.get("signature", "authority certificate url") | ||||
| AUTHORITY_CRL_URL = cp.get("signature", "revoked url") | ||||
| AUTHORITY_OCSP_URL = cp.get("signature", "responder url") | ||||
|   | ||||
| @@ -118,7 +118,7 @@ function onRequestSubmitted(e) { | ||||
|             console.info("Going to prepend:", request); | ||||
|             onRequestDeleted(e); // Delete any existing ones just in case | ||||
|             $("#pending_requests").prepend( | ||||
|                 env.render('views/request.html', { request: request })); | ||||
|                 env.render('views/request.html', { request: request, session: session })); | ||||
|             $("#pending_requests time").timeago(); | ||||
|         }, | ||||
|         error: function(response) { | ||||
|   | ||||
| @@ -28,23 +28,105 @@ 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>Vanilla OpenWrt/LEDE</h5> | ||||
|             <h5>OpenVPN gateway on OpenWrt/LEDE router</h5> | ||||
|  | ||||
|             <p>On OpenWrt/LEDE router to convert it into VPN gateway:</p> | ||||
|             <p>First enroll certificates:</p> | ||||
|             <div class="highlight"> | ||||
|               <pre class="code"><code>mkdir -p /var/lib/certidude/{{ window.location.hostname }}; \ | ||||
| grep -c certidude /etc/sysupgrade.conf || echo /var/lib/certidude >> /etc/sysupgrade.conf; \ | ||||
| curl -f http://{{ window.location.hostname }}/api/certificate/ -o /var/lib/certidude/{{ window.location.hostname }}/ca_cert.pem; \ | ||||
| test -e /var/lib/certidude/{{ window.location.hostname }}/client_key.pem || openssl genrsa -out /var/lib/certidude/{{ window.location.hostname }}/client_key.pem 2048; \ | ||||
| test -e /var/lib/certidude/{{ window.location.hostname }}/client_req.pem || read -p "Enter FQDN: " NAME; openssl req -new -sha256 \ | ||||
|   -key /var/lib/certidude/{{ window.location.hostname }}/client_key.pem \ | ||||
|   -out /var/lib/certidude/{{ window.location.hostname }}/client_req.pem -subj "/CN=$NAME"; \ | ||||
|               <pre class="code"><code># Derive FQDN from WAN interface's reverse DNS record | ||||
| FQDN=$(nslookup $(uci get network.wan.ipaddr) |  grep "name =" | head -n1 | cut -d "=" -f 2 | xargs) | ||||
|  | ||||
| mkdir -p /etc/certidude/authority/{{ window.location.hostname }}; \ | ||||
| grep -c certidude /etc/sysupgrade.conf || echo /etc/certidude >> /etc/sysupgrade.conf; \ | ||||
| curl -f http://{{ window.location.hostname }}/api/certificate/ -o /etc/certidude/authority/{{ window.location.hostname }}/ca_cert.pem; \ | ||||
| test -e /etc/certidude/authority/{{ window.location.hostname }}/server_key.pem \ | ||||
|  || openssl genrsa -out /etc/certidude/authority/{{ window.location.hostname }}/server_key.pem 2048; \ | ||||
| test -e /etc/certidude/authority/{{ window.location.hostname }}/server_req.pem \ | ||||
|  || openssl req -new -sha256 \ | ||||
|       -key /etc/certidude/authority/{{ window.location.hostname }}/server_key.pem \ | ||||
|       -out /etc/certidude/authority/{{ window.location.hostname }}/server_req.pem -subj "/CN=$FQDN"; \ | ||||
| curl -f -L -H "Content-type: application/pkcs10" \ | ||||
|   --data-binary @/var/lib/certidude/{{ window.location.hostname }}/client_req.pem \ | ||||
|   -o /var/lib/certidude/{{ window.location.hostname }}/client_cert.pem \ | ||||
|   --data-binary @/etc/certidude/authority/{{ window.location.hostname }}/server_req.pem \ | ||||
|   -o /etc/certidude/authority/{{ window.location.hostname }}/server_cert.pem \ | ||||
|   http://{{ window.location.hostname }}/api/request/?wait=yes</code></pre> | ||||
|             </div> | ||||
|  | ||||
|             <p>Then set up service:</p> | ||||
|             <div class="highlight"> | ||||
|               <pre class="code"><code># Create VPN gateway up/down script for reporting client IP addresses to CA | ||||
| cat <<\EOF > /etc/certidude/updown | ||||
| #!/bin/sh | ||||
| CURL="curl -f --key /etc/certidude/authority/{{ window.location.hostname }}/server_key.pem --cert /etc/certidude/authority/{{ window.location.hostname }}/server_cert.pem --cacert /etc/certidude/authority/{{ window.location.hostname }}/ca_cert.pem https://{{ window.location.hostname }}:8443/api/lease/" | ||||
|  | ||||
| case $PLUTO_VERB in | ||||
|   up-client|down-client) $CURL --data "outer_address=$PLUTO_PEER&inner_address=$PLUTO_PEER_SOURCEIP&client=$PLUTO_PEER_ID" ;; | ||||
|   *) $CURL --data "client=$X509_0_CN&outer_address=$untrusted_ip&inner_address=$ifconfig_pool_remote_ip&serial=$tls_serial_0" ;; | ||||
| esac | ||||
| EOF | ||||
|  | ||||
| chmod +x /etc/certidude/updown | ||||
|  | ||||
|  | ||||
| # Generate Diffie-Hellman parameters file for OpenVPN | ||||
| test -e /etc/certidude/dh.pem \ | ||||
|  || openssl dhparam 2048 -out /etc/certidude/dh.pem | ||||
|  | ||||
| # Create interface definition for tunnel | ||||
| uci set network.vpn=interface | ||||
| uci set network.vpn.name='vpn' | ||||
| uci set network.vpn.ifname=tun_s2c | ||||
| uci set network.vpn.proto='none' | ||||
|  | ||||
| # Create zone definition for VPN interface | ||||
| uci set firewall.vpn=zone | ||||
| uci set firewall.vpn.name='vpn' | ||||
| uci set firewall.vpn.input='ACCEPT' | ||||
| uci set firewall.vpn.forward='ACCEPT' | ||||
| uci set firewall.vpn.output='ACCEPT' | ||||
| uci set firewall.vpn.network='vpn' | ||||
|  | ||||
| # Allow UDP 1194 on WAN interface | ||||
| uci set firewall.openvpn=rule | ||||
| uci set firewall.openvpn.name='Allow OpenVPN' | ||||
| uci set firewall.openvpn.src='wan' | ||||
| uci set firewall.openvpn.dest_port=1194 | ||||
| uci set firewall.openvpn.proto='udp' | ||||
| uci set firewall.openvpn.target='ACCEPT' | ||||
|  | ||||
| # Forward traffic from VPN to LAN | ||||
| uci set firewall.c2s=forwarding | ||||
| uci set firewall.c2s.src='vpn' | ||||
| uci set firewall.c2s.dest='lan' | ||||
|  | ||||
| # Permit DNS queries from VPN | ||||
| uci set dhcp.@dnsmasq[0].localservice='0' | ||||
|  | ||||
| touch /etc/config/openvpn | ||||
| uci set openvpn.s2c=openvpn | ||||
| uci set openvpn.s2c.local=$(uci get network.wan.ipaddr) | ||||
| uci set openvpn.s2c.script_security=2 | ||||
| uci set openvpn.s2c.client_connect='/etc/certidude/updown' | ||||
| uci set openvpn.s2c.tls_version_min='1.2' | ||||
| uci set openvpn.s2c.tls_cipher='TLS-DHE-RSA-WITH-AES-256-GCM-SHA384' | ||||
| uci set openvpn.s2c.cipher='AES-256-CBC' | ||||
| uci set openvpn.s2c.auth='SHA384' | ||||
| uci set openvpn.s2c.dev=tun_s2c | ||||
| uci set openvpn.s2c.server='10.179.43.0 255.255.255.0' | ||||
| uci set openvpn.s2c.key='/etc/certidude/authority/{{ window.location.hostname }}/server_key.pem' | ||||
| uci set openvpn.s2c.cert='/etc/certidude/authority/{{ window.location.hostname }}/server_cert.pem' | ||||
| uci set openvpn.s2c.ca='/etc/certidude/authority/{{ window.location.hostname }}/ca_cert.pem' | ||||
| uci set openvpn.s2c.dh='/etc/certidude/dh.pem' | ||||
| uci set openvpn.s2c.enabled=1 | ||||
| uci set openvpn.s2c.comp_lzo=yes | ||||
| uci add_list openvpn.s2c.push="route-metric 1000" | ||||
| uci add_list openvpn.s2c.push="route $(uci get network.lan.ipaddr) $(uci get network.lan.netmask)" | ||||
| uci add_list openvpn.s2c.push="dhcp-option DNS $(uci get network.lan.ipaddr)" | ||||
| uci add_list openvpn.s2c.push="dhcp-option DOMAIN $(uci get dhcp.@dnsmasq[0].domain)" | ||||
|  | ||||
| /etc/init.d/openvpn restart | ||||
| /etc/init.d/firewall restart</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> | ||||
|   | ||||
| @@ -30,10 +30,11 @@ | ||||
|       </button> | ||||
|       <div class="dropdown-menu"> | ||||
|         {% for p in session.authority.signature.profiles %} | ||||
|           <a class="dropdown-item" href="/api/request/?organizational_unit={{ p.organizational_unit }}&lifetime={{ p.lifetime }}&flags={{ p.flags }}"> | ||||
|             {% if p.organizational_unit %} | ||||
|             {{ p.organizational_unit }} ({{ p.flags }}){% else %} | ||||
|             {{ p.flags | capitalize }}{% endif %}, expires in {{ p.lifetime }} days</a> | ||||
|           <a class="dropdown-item{% if p.server and not request.server %} disabled{% endif %}" | ||||
|             {% if p.server and not request.server %}title="Resubmit with FQDN as common name"{% endif %} | ||||
|             href="#" onclick="javascript:$.ajax({url:'/api/request/{{request.common_name}}/?sha256sum={{ request.sha256sum }}&profile={{ p.name }}',type:'post'});"> | ||||
|             {% if p.title %}{{ p.title }} ({% if p.server %}server{% else %}client{% endif %}){% else %} | ||||
|             {% if p.server %}Server{% else %}Client{% endif %}{% endif %}, expires in {{ p.lifetime }} days</a> | ||||
|         {% endfor %} | ||||
|       </div> | ||||
|     </div> | ||||
|   | ||||
| @@ -1,6 +1,10 @@ | ||||
| <p> | ||||
| <div id="certificate-{{ certificate.common_name | replace('@', '--') | replace('.', '-') }}" class="card"> | ||||
|   <div class="card-header"> | ||||
|     {% if certificate.organizational_unit %} | ||||
|       <i class="fa fa-folder" aria-hidden="true"></i> | ||||
|     {{ certificate.organizational_unit }} / | ||||
|     {% endif %} | ||||
|     {% if certificate.server %} | ||||
|       <i class="fa fa-server"></i> | ||||
|     {% else %} | ||||
| @@ -17,12 +21,9 @@ | ||||
|       </span> | ||||
|  | ||||
|       Signed | ||||
|       <time class="timeago" datetime="{{ certificate.signed }}">Certificate was signed {{ certificate.signed }}</time>, | ||||
|       <time class="timeago" datetime="{{ certificate.signed }}">Certificate was signed {{ certificate.signed }}</time>{% if certificate.signer %} by {{ certificate.signer }}{% endif %}, | ||||
|       expires | ||||
|       <time class="timeago" datetime="{{ certificate.expires }}">Certificate expires {{ certificate.expires }}</time>. | ||||
|       {% if certificate.organizational_unit %} | ||||
|         Part of {{ certificate.organizational_unit }} organizational unit. | ||||
|       {% endif %} | ||||
|     </p> | ||||
|     <p> | ||||
|     {% if session.authority.tagging %} | ||||
| @@ -91,10 +92,10 @@ curl --cert client_cert.pem https://{{ window.location.hostname }}:8443/api/sign | ||||
|       <div style="overflow: auto; max-width: 100%;"> | ||||
|         <table class="table" id="signed_certificates"> | ||||
|           <tbody> | ||||
|             <tr><th>Common name</th><td>{{ certificate.common_name }}</td></tr> | ||||
|             <tr><th>Organizational unit</th><td>{% if certificate.organizational_unit %}{{ certificate.organizational_unit }}{% else %}-{% endif %}</td></tr> | ||||
|             <tr><th>Common name</th><td>{{ certificate.common_name }}</td></tr> | ||||
|             <tr><th>Organizational unit</th><td>{% if certificate.organizational_unit %}{{ certificate.organizational_unit }}{% else %}-{% endif %}</td></tr> | ||||
|             <tr><th>Serial number</th><td style="word-wrap:break-word;">{{ certificate.serial | serial }}</td></tr> | ||||
|             <tr><th>Signed</th><td>{{ certificate.signed | datetime  }}{% if certificate.signer %}, by {{ certificate.signer }}{% endif %}</td></tr> | ||||
|             <tr><th>Signed</th><td>{{ certificate.signed | datetime  }}{% if certificate.signer %} by {{ certificate.signer }}{% endif %}</td></tr> | ||||
|             <tr><th>Expires</th><td>{{ certificate.expires | datetime  }}</td></tr> | ||||
|             {% if certificate.lease %} | ||||
|             <tr><th>Lease</th><td><a href="http://{{ certificate.lease.inner_address }}">{{ certificate.lease.inner_address }}</a> at {{ certificate.lease.last_seen | datetime  }} | ||||
|   | ||||
| @@ -191,13 +191,13 @@ lifetime = 30 | ||||
| # Secret for generating and validating tokens, regenerate occasionally | ||||
| secret = {{ token_secret }} | ||||
|  | ||||
|  | ||||
| [profile] | ||||
| # title, flags, lifetime, organizational unit | ||||
| default = client, 120, | ||||
| srv = server, 365, Server | ||||
| gw = server, 3, Gateway | ||||
| ap = client, 1825, Access Point | ||||
| # name, flags, lifetime, organizational unit, title | ||||
| default = client, 120, Roadwarrior, Roadwarrior | ||||
| gw = server, 30, Gateway, Gateway | ||||
| srv = server, 365, Server, | ||||
| ap = client, 1825, Access Point, Access Point | ||||
| mfp = client, 30, MFP, Printers | ||||
|  | ||||
| [script] | ||||
| # Path to the folder with scripts that can be served to the clients, set none to disable scripting | ||||
|   | ||||
		Reference in New Issue
	
	Block a user