Initial commit

This commit is contained in:
2021-05-27 13:15:46 +03:00
commit d121e8417c
66 changed files with 2782 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
{% for key, value in certificate.attributes %}
<span class="badge badge-info" title="{{ key }}={{ value }}">{{ value }}</span>
{% endfor %}

View File

@@ -0,0 +1,270 @@
<div class="modal fade" id="request_submission_modal" role="dialog">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">Request submission</h4>
</div>
<div class="modal-body">
<ul class="nav nav-pills" id="myTab" role="tablist">
<li class="nav-item">
<a class="nav-link active" id="home-tab" data-toggle="tab" href="#snippet-certidude" role="tab" aria-controls="certidude" aria-selected="true">Certidude</a>
</li>
<li class="nav-item">
<a class="nav-link" id="profile-tab" data-toggle="tab" href="#snippet-windows" role="tab" aria-controls="windows" aria-selected="false">Windows</a>
</li>
<li class="nav-item">
<a class="nav-link" id="contact-tab" data-toggle="tab" href="#snippet-unix" role="tab" aria-controls="unix" aria-selected="false">UNIX</a>
</li>
{% if "openvpn" in session.service.protocols %}
<li class="nav-item">
<a class="nav-link" id="contact-tab" data-toggle="tab" href="#snippet-openvpn" role="tab" aria-controls="openvpn" aria-selected="false">OpenVPN</a>
</li>
{% endif %}
{% if "ikev2" in session.service.protocols %}
<li class="nav-item">
<a class="nav-link" id="contact-tab" data-toggle="tab" href="#snippet-strongswan" role="tab" aria-controls="strongswan" aria-selected="false">StrongSwan</a>
</li>
{% endif %}
<li class="nav-item">
<a class="nav-link" id="contact-tab" data-toggle="tab" href="#snippet-lede" role="tab" aria-controls="lede" aria-selected="false">LEDE</a>
</li>
<li class="nav-item">
<a class="nav-link" id="contact-tab" data-toggle="tab" href="#snippet-copypaste" role="tab" aria-controls="copypaste" aria-selected="false">Copypasta</a>
</li>
</ul>
<div class="tab-content" id="myTabContent">
<!-- Certidude client -->
<div class="tab-pane fade show active" id="snippet-certidude" role="tabpanel" aria-labelledby="certidude">
<p>On Ubuntu or Fedora:</p>
<div class="highlight">
<pre class="code"><code>{% include "snippets/certidude-client.sh" %}</code></pre>
</div>
</div>
<!-- Windows -->
<div class="tab-pane fade" id="snippet-windows" role="tabpanel" aria-labelledby="windows">
<p>On Windows execute following PowerShell script</p>
{% if "ikev2" in session.service.protocols %}
<div class="highlight"><pre class="code"><code>{% include "snippets/windows.ps1" %}</code></pre></div>
{% endif %}
</div>
<!-- UNIX-like -->
<div class="tab-pane fade" id="snippet-unix" role="tabpanel" aria-labelledby="unix">
<p>For client certificates generate key pair and submit the signing request with common name set to short hostname:</p>
<div class="highlight">
<pre class="code"><code>{% include "snippets/request-client.sh" %}</code></pre>
</div>
</div>
<!-- OpenVPN as client -->
<div class="tab-pane fade" id="snippet-openvpn" role="tabpanel" aria-labelledby="openvpn">
<p>First acquire certificates using the snippet above.</p>
<p>Then install software:</p>
<div class="highlight"><pre class="code"><code>{% include "snippets/openvpn-client.sh" %}</code></pre></div>
</div>
<!-- StrongSwan as client -->
<div class="tab-pane fade" id="snippet-strongswan" role="tabpanel" aria-labelledby="strongswan">
<p>First acquire certificates using the snippet above.</p>
<p>Then install software:</p>
<div class="highlight">
<pre class="code"><code>{% include "snippets/strongswan-patching.sh" %}</code></pre>
</div>
<p>To configure StrongSwan as roadwarrior:</p>
<div class="highlight"><pre class="code"><code>{% include "snippets/strongswan-client.sh" %}</code></pre></div>
</div>
<!-- Copy & paste -->
<div class="tab-pane fade" id="snippet-copypaste" role="tabpanel" aria-labelledby="copypaste">
<p>Use whatever tools you have available on your platform to generate
keypair and just paste ASCII armored PEM file contents here and hit submit:</p>
<form action="/api/request/" method="post">
<textarea id="request_body" style="width:100%; min-height: 10em;"
placeholder="-----BEGIN CERTIFICATE REQUEST-----"></textarea>
<div class="modal-footer">
<div class="btn-group">
<button type="button" onclick="onSubmitRequest();" class="btn btn-primary"><i class="fa fa-upload"></i> Submit</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal"><i class="fa fa-ban"></i> Close</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="modal fade" id="revocation_list_modal" role="dialog">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">Revocation lists</h4>
</div>
<div class="modal-body">
<p>To fetch <a href="http://{{ authority.namespace }}/api/revoked/">certificate revocation list</a>:</p>
<pre><code>curl http://{{ authority.namespace }}/api/revoked/ > crl.der
curl http://{{ authority.namespace }}/api/revoked/ -L -H "Accept: application/x-pem-file"
curl http://{{ authority.namespace }}/api/revoked/?wait=yes -L -H "Accept: application/x-pem-file" > crl.pem</code></pre>
</div>
<div class="modal-footer">
<button type="button" class="btn" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6 col-lg-4 col-xl-3">
<h3>Signed certificates</h3>
<p>Authority administration
{% if authority.certificate.organization %}of {{ authority.certificate.organization }}{% endif %}
allowed for
{% for user in session.authorization.admin_users %}<a href="mailto:{{ user.mail}}">{{ user.given_name }} {{user.surname }}</a>{% if not loop.last %}, {% endif %}{% endfor %} from {% if "0.0.0.0/0" in session.authorization.admin_subnets %}anywhere{% else %}
{% for subnet in session.authorization.admin_subnets %}{{ subnet }}{% if not loop.last %}, {% endif %}{% endfor %}{% endif %}.
Authority valid from
<time class="timeago" datetime="{{ authority.certificate.signed }}">{{ authority.certificate.signed }}</time>
until
<time class="timeago" datetime="{{ authority.certificate.expires }}">{{ authority.certificate.expires }}</time>.
Authority certificate can be downloaded from <a href="/api/certificate/">here</a>.
Following certificates have been signed:</p>
<div id="signed-filter" class="btn-group-toggle" data-toggle="buttons">
<label class="btn btn-primary"><input id="signed-filter-new" type="checkbox" autocomplete="off">New</label>
<label class="btn btn-primary active"><input id="signed-filter-online" type="checkbox" autocomplete="off" checked>Online</label>
<label class="btn btn-primary"><input id="signed-filter-offline" type="checkbox" autocomplete="off">Lately seen</label>
<label class="btn btn-primary"><input id="signed-filter-dead" type="checkbox" autocomplete="off">Gone</label>
</div>
<div id="signed_certificates">
{% for certificate in session.signed | sort(attribute="signed", reverse=true) %}
{% include "views/signed.html" %}
{% endfor %}
</div>
<p>Showing <span id="signed-filter-counter">-</span> of total <span id="signed-total">-</span> certificates</p>
</div>
<div class="col-sm-6 col-lg-4 col-xl-3">
{% if session.features.token %}
<h3>Tokens</h3>
<p>Tokens allow enrolling smartphones and third party devices.</p>
<ul>
<li>You can issue yourself a token to be used on a mobile device</li>
<li>Enter username to issue a token to issue a token for another user</li>
<li>Enter e-mail address to issue a token to guest users outside domain</li>
</ul>
<p>
<div class="input-group">
<input id="token_username" name="username" type="text" class="form-control" placeholder="Username" aria-describedby="sizing-addon2">
<input id="token_mail" name="mail" type="mail" class="form-control" placeholder="Optional e-mail" aria-describedby="sizing-addon2">
<span class="input-group-btn">
<button class="btn btn-secondary" type="button" onClick="onIssueToken();"><i class="fa fa-send"></i> Send token</button>
</span>
</div>
</p>
<p>Issued tokens:</p>
<ul class="list-group">
{% for token in session.tokens %}
{% include "views/token.html" %}
{% endfor %}
</ul>
<div id="token_qrcode"></div>
{% endif %}
{% if session.authorization.request_subnets %}
<p>&nbsp;</p>
<h3>Pending requests</h3>
<p>Use Certidude client to apply for a certificate.
{% if not session.authorization.request_subnets %}
Request submission disabled.
{% elif "0.0.0.0/0" in session.authorization.request_subnets %}
Request submission is enabled.
{% else %}
Request submission allowed from
{% for subnet in session.authorization.request_subnets %}
{{ subnet }}{% if not loop.last %}, {% endif %}
{% endfor %}.
{% endif %}
See <a href="#request_submission_modal" data-toggle="modal">here</a> for more information on manual signing request upload.
{% if session.authorization.autosign_subnets %}
{% if "0.0.0.0/0" in session.authorization.autosign_subnets %}
All requests are automatically signed.
{% else %}
Requests from
{% for subnet in session.authorization.autosign_subnets %}
{{ subnet }}{% if not loop.last %}, {% endif %}
{% endfor %}
are automatically signed.
{% endif %}
{% endif %}
</p>
<div id="pending_requests">
{% for request in session.requests | sort(attribute="submitted", reverse=true) %}
{% include "views/request.html" %}
{% endfor %}
</div>
{% endif %}
{% if session.builder.profiles %}
<h3>LEDE imagebuilder</h3>
<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.builder.profiles %}
<li><a href="/api/builder/{{ name }}/{{ filename }}">{{ title }}</a></li>
{% endfor %}
</ul>
{% endif %}
</div>
<div class="col-sm-6 col-lg-4 col-xl-3">
<h3>Revoked certificates</h3>
<p>Following certificates have been revoked{% if session.features.crl %}, for more information click
<a href="#revocation_list_modal" data-toggle="modal">here</a>{% endif %}.</p>
{% for certificate in session.revoked | sort(attribute="revoked", reverse=true) %}
{% include "views/revoked.html" %}
{% endfor %}
</div>
<div id="column-log" class="col-sm-6 col-lg-4 col-xl-3 hidden-lg-down">
<div class="loader-container">
<div class="loader"></div>
<p>Loading logs, this might take a while...</p>
</div>
<div class="content" style="display:none;">
<h3>Log</h3>
<div class="btn-group-toggle" data-toggle="buttons">
<label class="btn btn-primary active"><input id="log-level-critical" type="checkbox" autocomplete="off" checked>Critical</label>
<label class="btn btn-primary active"><input id="log-level-error" type="checkbox" autocomplete="off" checked>Error</label>
<label class="btn btn-primary active"><input id="log-level-warning" type="checkbox" autocomplete="off" checked>Warn</label>
<label class="btn btn-primary active"><input id="log-level-info" type="checkbox" autocomplete="off" checked>Info</label>
<label class="btn btn-primary"><input id="log-level-debug" type="checkbox" autocomplete="off">Debug</label>
</div>
<ul id="log-entries" class="list-group">
</ul>
<p>Click here to load more entries</p>
</div>
</div>
</div>

View File

@@ -0,0 +1,31 @@
<h1>Create a rule</h1>
<p>
<datalist id="tag_autocomplete">
</datalist>
<span>Filter</span>
<select id="tags_autocomplete"></select>
attaches attribute
<select>
{% include 'views/tagtypes.html' %}
</select>
<span contenteditable>something</span>
<button>Add rule</button>
</p>
{% for grouper, items in configuration | groupby('tag_id') %}
<h1>Filter {{ items[0].match_key }} is {{ items[0].match_value }}</h1>
<ul>
{% for item in items %}
<li>Attach {{ item.key }} attribute {{ item.value }}</li>
{% endfor %}
</ul>
{% endfor %}

278
templates/views/enroll.html Normal file
View File

@@ -0,0 +1,278 @@
<!-- https://wiki.strongswan.org/projects/strongswan/wiki/AppleIKEv2Profile#Certificate-authentication -->
<!--
Browser status
- Edge doesn't work because they think data: urls are insecure
- iphone QR code scanner's webview is constrained, cant download data: links
- outlook.com via iphone mail client works
- android gmail app works
- chrome works
- firefox works
OS/soft status
- OpenVPN works on everything
- StrongSwan app works on Android
- NetworkManager doesn't support importing .sswan files yet, so no IPSec support for Ubuntu or Fedora here yet
-->
<div id="enroll" class="row">
<div class="loader-container">
<div class="loader"></div>
<p>Generating RSA keypair, this will take a while...</p>
</div>
<div class="col-sm-12 mt-3 edge-broken" style="display:none;">
<!-- https://stackoverflow.com/questions/33154646/data-uri-link-a-href-data-doesnt-work-in-microsoft-edge?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa -->
Microsoft Edge not supported, open the link with Chrome or Firefox
</div>
<div class="col-sm-12 mt-3 option ubuntu linux openvpn">
<div class="card">
<div class="card-block">
<h3 class="card-title">Ubuntu 16.04+</h3>
<p class="card-text">Install OpenVPN plugin for NetworkManager by executing following two command in the terminal:
<pre><code># Ubuntu 16.04 ships with older OpenVPN 2.3, to support newer ciphers add OpenVPN's repo
if [ $(lsb_relase -cs) == "xenial" ]; then
wget -O - https://swupdate.openvpn.net/repos/repo-public.gpg|apt-key add -
echo "deb http://build.openvpn.net/debian/openvpn/release/2.4 xenial main" > /etc/apt/sources.list.d/openvpn-aptrepo.list
apt update
apt install openvpn
fi
sudo apt install -y network-manager-openvpn-gnome
sudo systemctl restart network-manager
</code></pre>
<p>
<a href="javascript:onEnroll('ovpn');" class="btn btn-primary">Fetch OpenVPN profile</a>
<button class="btn btn-secondary" type="button" data-toggle="collapse" data-target="#ubuntu-screenshots" aria-expanded="false" aria-controls="ubuntu-screenshots">
Screenshots
</button>
</p>
<div class="collapse" id="ubuntu-screenshots">
<p>Open up network connections:</p>
<p><img src="/img/ubuntu-01-edit-connections.png"/></p>
<p>Hit <i>Add button</i>:</p>
<p><img src="/img/ubuntu-02-network-connections.png"/></p>
<p>Select <i>Import a saved VPN configuration...</i>:</p>
<p><img src="/img/ubuntu-03-import-saved-config.png"/></p>
<p>Select downloaded file:</p>
<p><img src="/img/ubuntu-04-select-file.png"/></p>
<p>Once profile is successfully imported following dialog appears:</p>
<p><img src="/img/ubuntu-05-profile-imported.png"/></p>
<p>By default all traffic is routed via VPN gateway, route only intranet subnets to the gateway select <i>Routes...</i> under <i>IPv4 Settings</i>:</p>
<p><img src="/img/ubuntu-06-ipv4-settings.png"/></p>
<p>Check <i>Use this connection only for resources on its network</i>:</p>
<p><img src="/img/ubuntu-07-disable-default-route.png"/></p>
<p>To activate the connection select it under <i>VPN Connections</i>:</p>
<p><img src="/img/ubuntu-08-activate-connection.png"/></p>
</div>
</div>
</div>
</div>
<div class="col-sm-12 mt-3 option ubuntu linux openvpn advanced">
<div class="card">
<div class="card-block">
<h3 class="card-title">Ubuntu 18.04+ (advanced)</h3>
<p class="card-text">Copy-paste follownig to terminal as root user:</p>
<pre><code>{% include "snippets/request-client.sh" %}
cat << EOF > '/etc/NetworkManager/system-connections/OpenVPN to {{ authority.namespace }}'
{% include "snippets/networkmanager-openvpn.conf" %}EOF
nmcli con reload
</code></pre>
</div>
</div>
</div>
<div class="col-sm-12 mt-3 option ubuntu linux ikev2 advanced">
<div class="card">
<div class="card-block">
<h3 class="card-title">Ubuntu 18.04+ (advanced)</h3>
<p class="card-text">Copy-paste follownig to terminal as root user:</p>
<pre><code>{% include "snippets/request-client.sh" %}
cat << EOF > '/etc/NetworkManager/system-connections/IPSec to {{ authority.namespace }}'
{% include "snippets/networkmanager-strongswan.conf" %}EOF
nmcli con reload
</code></pre>
</div>
</div>
</div>
<div class="col-sm-12 mt-3 option fedora linux openvpn">
<div class="card">
<div class="card-block">
<h3 class="card-title">Fedora</h3>
<p class="card-text">Install OpenVPN plugin for NetworkManager by running following two commands:</p>
<pre><code>dnf install NetworkManager-openvpn-gnome
systemctl restart NetworkManager</code></pre>
Right click in the NetworkManager icon, select network settings. Hit the + button and select <i>Import from file...</i>, select the downloaded .ovpn file.
Remove the .ovpn file from the Downloads folder.</p>
<a href="javascript:onEnroll('ovpn');" class="btn btn-primary">Fetch OpenVPN profile</a>
</div>
</div>
</div>
<div class="col-sm-12 mt-3 option windows ipsec">
<div class="card">
<div class="card-block">
<h3 class="card-title">Windows</h3>
<p class="card-text">
Import PKCS#12 container to your machine trust store.
Import VPN connection profile by moving the downloaded .pbk file to
<pre><code>%userprofile%\AppData\Roaming\Microsoft\Network\Connections\PBK</code></pre>
or
<pre><code>C:\ProgramData\Microsoft\Network\Connections\Pbk</code></pre></p>
<a href="javascript:onEnroll('p12');" class="btn btn-primary">Fetch PKCS#12 container</a>
<a href="#" class="btn btn-secondary">Fetch IPSec IKEv2 VPN profile</a>
</div>
</div>
</div>
<div class="col-sm-12 mt-3 option windows ikev2">
<div class="card">
<div class="card-block">
<h3 class="card-title">Windows</h3>
<p>To configure IPSec IKEv2 tunnel on Windows, open PowerShell as administrator and copy-paste following:</p>
<div class="highlight"><pre class="code"><code>{% include "snippets/windows.ps1" %}</code></pre></div>
</div>
</div>
</div>
<div class="col-sm-12 mt-3 option windows openvpn">
<div class="card">
<div class="card-block">
<h3 class="card-title">Windows</h3>
<p class="card-text">
Install OpenVPN community edition client.
Move the downloaded .ovpn file to C:\Program Files\OpenVPN\config and
right click in the system tray on OpenVPN icon and select Connect from the menu.
For finishing touch adjust the file permissions so only local
administrator can read that file, remove regular user access to the file.
</p>
<a href="https://openvpn.net/index.php/download/community-downloads.html" class="btn btn-secondary">Get OpenVPN community edition</a>
<a href="javascript:onEnroll('ovpn');" class="btn btn-primary">Fetch OpenVPN profile</a>
<button class="btn btn-secondary" type="button" data-toggle="collapse" data-target="#windows-screenshots" aria-expanded="false" aria-controls="windows-screenshots">
Screenshots
</button>
<div class="collapse" id="windows-screenshots">
<p>Download OpenVPN from the link supplied above:</p>
<p><img src="/img/windows-01-download-openvpn.png"/></p>
<p>Install OpenVPN:</p>
<p><img src="/img/windows-02-install-openvpn.png"/></p>
<p>Move the configuraiton file downloaded from the second button above:</p>
<p><img src="/img/windows-03-move-config-file.png"/></p>
<p>Connect from system tray:</p>
<p><img src="/img/windows-04-connect.png"/></p>
<p>Connection is successfully configured:</p>
<p><img src="/img/windows-05-connected.png"/></p>
</div>
</div>
</div>
</div>
<div class="col-sm-12 mt-3 option mac openvpn">
<div class="card">
<div class="card-block">
<h3 class="card-title">Mac OS X</h3>
<p class="card-text">Download Tunnelblick. Tap on the button above and import the profile.</p>
<a href="https://tunnelblick.net/" target="_blank" class="btn btn-secondary">Get Tunnelblick</a>
<a href="javascript:onEnroll('ovpn');" class="btn btn-primary">Fetch OpenVPN profile</a>
</div>
</div>
</div>
<div class="col-sm-12 mt-3 option iphone ipad openvpn">
<div class="card">
<div class="card-block">
<h3 class="card-title">iPhone/iPad</h3>
<p class="card-text">Install OpenVPN Connect app, tap on the button below.</p>
<a href="https://itunes.apple.com/us/app/openvpn-connect/id590379981?mt=8" target="_blank" class="btn btn-secondary">Get OpenVPN Connect app</a>
<a href="javascript:onEnroll('ovpn');" class="btn btn-primary">Fetch OpenVPN profile</a>
</div>
</div>
</div>
<div class="col-sm-12 mt-3 option iphone ipad ikev2">
<div class="card">
<div class="card-block">
<h3 class="card-title">iPhone/iPad</h3>
<p class="card-text">
Tap the button below, you'll be prompted about configuration profile, tap <i>Allow</i>.
Hit <i>Install</i> in the top-right corner.
Enter your passcode to unlock trust store.
Tap <i>Install</i> and confirm by hitting <i>Install</i>.
Where password for the certificate is prompted, enter 1234.
Hit <i>Done</i>. Go to <i>Settings</i>, open VPN submenu and tap on the VPN profile to connect.
</p>
<a href="javascript:onEnroll('mobileconfig');" class="btn btn-primary">Fetch IPSec IKEv2 VPN profile</a>
</div>
</div>
</div>
<div class="col-sm-12 mt-3 option mac ikev2">
<div class="card">
<div class="card-block">
<h3 class="card-title">Mac OS X</h3>
<p class="card-text">
Click on the button below, you'll be prompted about configuration profile, tap <i>Allow</i>.
Hit <i>Install</i> in the top-right corner.
Enter your passcode to unlock trust store.
Tap <i>Install</i> and confirm by hitting <i>Install</i>.
Where password for the certificate is prompted, enter 1234.
Hit <i>Done</i>. Go to <i>Settings</i>, open VPN submenu and tap on the VPN profile to connect.
</p>
<a href="javascript:onEnroll('mobileconfig');" class="btn btn-primary">Fetch VPN profile</a>
</div>
</div>
</div>
<div class="col-sm-12 mt-3 option android openvpn">
<div class="card">
<div class="card-block">
<h3 class="card-title">Android</h3>
<p class="card-text">Intall OpenVPN Connect app on your device.
Tap on the downloaded .ovpn file, OpenVPN Connect should prompt for import.
Hit <i>Accept</i> and then <i>Connect</i>.
Remember to delete any remaining .ovpn files under the <i>Downloads</i>.
</p>
<a href="https://play.google.com/store/apps/details?id=net.openvpn.openvpn" target="_blank" class="btn btn-secondary">Get OpenVPN Connect app</a>
<a href="javascript:onEnroll('ovpn');" class="btn btn-primary">Fetch OpenVPN profile</a>
</div>
</div>
</div>
<div class="col-sm-12 mt-3 option android ikev2">
<div class="card">
<div class="card-block">
<h3 class="card-title">Android</h3>
<p class="card-text">
Install strongSwan Client app on your device.
Tap on the downloaded .sswan file, StrongSwan Client should prompt for import.
Hit <i>Import certificate from VPN profile</i> and then <i>Import</i> in the top-right corner.
Remember to delete any remaining .sswan files under the <i>Downloads</i>.
</p>
<a href="https://play.google.com/store/apps/details?id=org.strongswan.android" class="btn btn-secondary">Get strongSwan VPN Client app</a>
<a href="javascript:onEnroll('sswan');" class="btn btn-primary">Fetch StrongSwan profile</a>
</div>
</div>
</div>
<div class="col-sm-12 mt-3 option any">
<a href="javascript:$('.option').show();">I did't find an appropriate option for me, show all options</a>
</div>
</div>

View File

@@ -0,0 +1,2 @@
<h1>{{ message.title }}</h1>
<p>{{ message.description }}</p>

View File

@@ -0,0 +1,14 @@
<p>You're viewing this page over insecure channel.
You can give it a try and <a href="https://{{ authority.hostname }}">connect over HTTPS</a>,
if that succeeds all subsequents accesses of this page will go over HTTPS.
</p>
<p>
Click <a href="/api/certificate">here</a> to fetch the certificate of this authority.
Alternatively install certificate on Fedora or Ubuntu with following copy-pastable snippet:
</p>
<div class="highlight">
<pre class="code"><code>{% include "snippets/store-authority.sh" %}
{% include "snippets/update-trust.sh" %}</code></pre>
</div>

View File

@@ -0,0 +1,7 @@
Last seen
<time class="timeago" datetime="{{ certificate.lease.last_seen }}">{{ certificate.lease.last_seen }}</time>
at
<a target="_blank" href="http://{{ certificate.lease.inner_address }}">{{ certificate.lease.inner_address }}</a>{% if certificate.lease.outer_address %}
from
<a target="{{ certificate.lease.outer_address }}" href="https://geoiplookup.net/ip/{{ certificate.lease.outer_address }}">{{ certificate.lease.outer_address }}</a>{% endif %}.
See some stats <a href="http://172.20.1.19:19999/host/{{ certificate.common_name }}/" target="_blank">here</a>.

View File

@@ -0,0 +1,8 @@
<li id="log_entry_{{ entry.id }}" data-keywords="{{ entry.message }}" class="list-group-item justify-content-between filterable{% if entry.fresh %} fresh{% endif %}">
<span>
<i class="fa fa-{{ entry.severity }}-circle"></i>
{{ entry.message }}
</span>
<span class="badge badge-default badge-pill">{{ entry.created }}</span>
</li>

View File

@@ -0,0 +1,64 @@
<div id="request-{{ request.id }}" class="card filterable mt-3"
data-keywords="{{ request.common_name }}|" data-id="{{request.id}}">
<div class="card-header">
{% if certificate.server %}
<i class="fa fa-server"></i>
{% else %}
<i class="fa fa-laptop"></i>
{% endif %}
{{ request.common_name }}
</div>
<div class="card-block">
<p class="mb-1">
Submitted
<time class="timeago" datetime="{{ request.submitted }}">Request was submitted {{ request.submitted }}</time>
from
{% if request.hostname %}{{request.hostname}} ({{request.address}}){% else %}{{request.address}}{% endif %}
</p>
<div class="btn-group">
<button type="button" class="btn btn-secondary" data-toggle="collapse" data-target="#details-{{ request.sha256sum }}"><i class="fa fa-list"></i> Details</button>
<button type="button" class="btn btn-danger"
data-loading-text="<i class='fa fa-circle-o-notch fa-spin'></i> Rejecting..."
onclick="onRejectRequest(event, '{{ request.common_name }}', '{{ request.sha256sum }}');">
<i class="fa fa-trash"></i> Reject</button>
<button type="button" class="btn btn-success"
data-loading-text="<i class='fa fa-circle-o-notch fa-spin'></i> Processing Order"
onclick="onSignRequest(event, '{{ request.common_name }}', '{{ request.sha256sum }}');">
<i class="fa fa-thumbs-up"></i> Approve</button>
<button type="button" class="btn btn-success 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 p in authority.signature.profiles %}
<a class="dropdown-item{% if not request.common_name.match(p.common_name) %} disabled{% endif %}"
{% if not request.common_name.match(p.common_name) %} title="Common name doesn't match expression {{ p.common_name }}"{% endif %}
href="#" onclick="javascript:$.ajax({url:'/api/request/{{request.common_name}}/?sha256sum={{ request.sha256sum }}&profile={{ p.key }}',type:'post'});">
{{ p.key }}, expires in {{ p.lifetime }} days</a>
{% endfor %}
</div>
</div>
<div class="collapse" id="details-{{ request.sha256sum }}">
<p>Use following to fetch the signing request:</p>
<div class="bd-example">
<pre><code class="language-sh" data-lang="sh">wget <a href="/api/request/{{ request.common_name }}/">http://{{ authority.namespace }}/api/request/{{ request.common_name }}/</a>
curl -L http://{{ authority.namespace }}/api/request/{{ request.common_name }}/ \
| openssl req -text -noout</code></pre>
</div>
<div style="overflow: auto; max-width: 100%;">
<table class="table" id="signed_certificates">
<tbody>
<tr><th>Common name</th><td>{{ request.common_name }}</td></tr>
<tr><th>Submitted</th><td>{{ request.submitted | datetime }}
{% if request.address %}from {{ request.address }}
{% if request.hostname %} ({{ request.hostname }}){% endif %}{% endif %}</td></tr>
<tr><th>MD5</th><td>{{ request.md5sum }}</td></tr>
<tr><th>SHA1</th><td>{{ request.sha1sum }}</td></tr>
<tr><th>SHA256</th><td>{{ request.sha256sum }}</td></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,69 @@
<div id="certificate-{{ certificate.common_name | replace('@', '--') | replace('.', '-') }}" class="card filterable mt-3"
data-keywords="{{ certificate.common_name }}|">
<div class="card-body">
<div class="card-header">
{% if certificate.server %}
<i class="fa fa-server"></i>
{% else %}
<i class="fa fa-laptop"></i>
{% endif %}
{{ certificate.common_name }}
</div>
<div class="card-block">
<p>
Serial number {{ certificate.serial | serial }}.
</p>
<p>
Revoked
<time class="timeago" datetime="{{ certificate.revoked }}">Certificate revoked {{ certificate.revoked }}</time>.
Valid from {{ certificate.signed | datetime }} to {{ certificate.expired | datetime }}.
</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>
<div class="btn-group">
<a href="/api/signed/{{ certificate.common_name }}/" class="btn btn-secondary hidden-xs-down"><i class="fa fa-download"></i> Download</a>
</div>
</div>
<div class="collapse" id="details-{{ certificate.sha256sum }}">
<p>To fetch certificate:</p>
<div class="bd-example">
<pre><code class="language-sh" data-lang="sh">wget <a href="/api/revoked/{{ certificate.serial }}/">http://{{ authority.namespace }}/api/revoked/{{ certificate.serial }}/</a>
curl http://{{ authority.namespace }}/api/revoked/{{ certificate.serial }}/ \
| openssl x509 -text -noout</code></pre>
</div>
<p>To perform online certificate status request</p>
<pre><code class="language-bash" data-lang="bash">curl http://{{ authority.namespace }}/api/certificate/ > session.pem
openssl ocsp -issuer session.pem -CAfile session.pem \
-url http://{{ authority.namespace }}/api/ocsp/ \
-serial 0x{{ certificate.serial }}</span></code></pre>
<p>
<table class="table" id="signed_certificates">
<tbody>
<tr><th>Common name</th><td>{{ certificate.common_name }}</td></tr>
<tr><th>Organizational unit</th><td>{{ certificate.organizational_unit }}</td></tr>
<tr><th>Serial number</th><td>{{ certificate.serial }}</td></tr>
<tr><th>Signed</th><td>{{ certificate.signed | datetime }}
{% if certificate.signer %}, by {{ certificate.signer }}{% endif %}</td></tr>
<tr><th>Expired</th><td>{{ certificate.expired | 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 }}
from <a href="https://geoiptool.com/en/?ip={{ certificate.lease.outer_address }}" target="_blank">{{ certificate.lease.outer_address }}</a>
</td></tr>
{% endif %}
<!--
<tr><th>MD5</th><td>{{ certificate.md5sum }}</td></tr>
<tr><th>SHA1</th><td>{{ certificate.sha1sum }}</td></tr>
-->
<tr><th>SHA256</th><td>{{ certificate.sha256sum }}</td></tr>
</tbody>
</table>
</p>
</div>
</div>
</div>
</div>

134
templates/views/signed.html Normal file
View File

@@ -0,0 +1,134 @@
<div id="certificate-{{ certificate.id }}" class="card filterable mt-3"
{% if certificate.lease %}data-last-seen={{ certificate.lease.last_seen }}{% endif %}
data-keywords="{{ certificate.common_name }}|{% if session.tagging %}{% for tag in certificate.tags %}{{ tag.id }}|{% endfor %}{% endif %}{% for key, value in certificate.attributes %}{{ key }}={{ value }}|{% endfor %}" data-id="{{certificate.id}}">
<div class="card-header">
{% if certificate.organizational_unit %}
<i class="fa fa-folder" aria-hidden="true"></i>
{{ certificate.organizational_unit }} /
{% endif %}
{% if certificate.extensions.extended_key_usage and "server_auth" in certificate.extensions.extended_key_usage %}
<i class="fa fa-server"></i>
{% else %}
<i class="fa fa-laptop"></i>
{% endif %}
{{ certificate.common_name }}
</div>
<div class="card-block">
<p>
<i class="fa fa-circle"></i>
<span class="lease">
{% if certificate.lease %}
{% include "views/lease.html" %}
{% endif %}
</span>
Signed
<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>.
</p>
<p>
{% if session.tagging %}
<span class="tags" data-cn="{{ certificate.common_name }}">
{% include "views/tags.html" %}
</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"
onclick="javascript:$(this).button('loading');$.ajax({url:'/api/signed/{{certificate.common_name}}/?sha256sum={{ certificate.sha256sum }}',type:'delete'});">
<i class="fa fa-ban"></i> Revoke</button>
<button type="button" class="btn btn-danger 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">
<a class="dropdown-item" href="#"
onclick="javascript:$(this).button('loading');$.ajax({url:'/api/signed/{{certificate.common_name}}/?sha256sum={{ certificate.sha256sum }}&reason=key_compromise',type:'delete'});">Revoke due to key compromise</a>
<a class="dropdown-item" href="#"
onclick="javascript:$(this).button('loading');$.ajax({url:'/api/signed/{{certificate.common_name}}/?sha256sum={{ certificate.sha256sum }}&reason=cessation_of_operation',type:'delete'});">Revoke due to cessation of operation</a>
<a class="dropdown-item" href="#"
onclick="javascript:$(this).button('loading');$.ajax({url:'/api/signed/{{certificate.common_name}}/?sha256sum={{ certificate.sha256sum }}&reason=privilege_withdrawn',type:'delete'});">Revoke due to withdrawn privilege</a>
</div>
</div>
<div class="btn-group">
{% if session.tagging %}
<button type="button" class="btn btn-default" onclick="onNewTagClicked(event);" 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.tagging %}
<a class="dropdown-item" href="#" data-key="{{ tag_category.name }}" data-cn="{{ certificate.common_name }}"
onclick="onNewTagClicked(event);">{{ tag_category.title }}</a>
{% endfor %}
</div>
{% endif %}
</div>
<div class="collapse" id="details-{{ certificate.sha256sum }}">
<p>To launch shell for this device, click <a href="#" onclick="onLaunchShell('{{ certificate.common_name }}')">here</a>
<p>To fetch certificate:</p>
<div class="bd-example">
<pre><code class="language-sh" data-lang="sh">wget <a href="/api/signed/{{ certificate.common_name }}/">http://{{ authority.namespace }}/api/signed/{{ certificate.common_name }}</a>
curl -L http://{{ authority.namespace }}/api/signed/{{ certificate.common_name }}/ \
| openssl x509 -text -noout</code></pre>
</div>
{% if session.authorization.ocsp_subnets %}
{% if certificate.responder_url %}
<p>To perform online certificate status request{% if "0.0.0.0/0" not in session.authorization.ocsp_subnets %}
from whitelisted {{ session.authorization.ocsp_subnets }} subnets{% endif %}:</p>
<pre><code class="language-bash" data-lang="bash">curl http://{{ authority.namespace }}/api/certificate > session.pem
openssl ocsp -issuer session.pem -CAfile session.pem \
-url {{ certificate.responder_url }} \
-serial 0x{{ certificate.serial }}</code></pre>
{% else %}
<p>Querying OCSP responder disabled for this certificate, see /etc/certidude/profile.conf how to enable if that's desired</p>
{% endif %}
{% endif %}
<p>To fetch script:</p>
<pre><code class="language-bash" data-lang="bash">curl -L --cert-status https://{{ authority.namespace }}:8443/api/signed/{{ certificate.common_name }}/script/ \
--cacert /etc/certidude/authority/{{ authority.namespace }}/ca_cert.pem \
--key /etc/certidude/authority/{{ authority.namespace }}/host_key.pem \
--cert /etc/certidude/authority/{{ authority.namespace }}/host_cert.pem</pre></code>
<div style="overflow: auto; max-width: 100%;">
<table class="table" id="signed_certificates">
<tbody>
<tr><th>Common&nbsp;name</th><td>{{ certificate.common_name }}</td></tr>
<tr><th>Organizational&nbsp;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>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 }}
from <a href="https://geoiptool.com/en/?ip={{ certificate.lease.outer_address }}" target="_blank">{{ certificate.lease.outer_address }}</a>
</td></tr>
{% endif %}
<!--
<tr><th>MD5</th><td>{{ certificate.md5sum }}</td></tr>
<tr><th>SHA1</th><td>{{ certificate.sha1sum }}</td></tr>
-->
<tr><th>SHA256</th><td style="word-wrap:break-word; overflow-wrap: break-word; ">{{ certificate.sha256sum }}</td></tr>
{% if certificate.key_usage %}
<tr><th>Key usage</th><td>{{ certificate.key_usage | join(", ") | replace("_", " ") }}</td></tr>
{% endif %}
{% if certificate.extended_key_usage %}
<tr><th>Extended key usage</th><td>{{ certificate.extended_key_usage | join(", ") | replace("_", " ") }}</td></tr>
{% endif %}
</tbody>
</table>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,6 @@
{% for tag in certificate.tags %}
<span data-cn="{{ certificate.common_name }}"
title="{{ tag }}"
class="badge badge-default"
onClick="onTagClicked(event);">{{ tag }}</span>
{% endfor %}

View File

@@ -0,0 +1,10 @@
<li id="token-{{ token.subject }}-{{ token.uuid }}" class="list-group-item filterable" data-keywords="">
<span>
<i class="fas fa-ticket-alt"></i>
{{ token.uuid }}...
<a href="mailto:{{ token.mail }}">{{ token.subject }}</a>
{% if token.issuer %}{% if token.issuer != token.subject %}by {{ token.issuer }}{% else %}by himself{% endif %}{% else %}via shell{% endif %},
expires
<time class="timeago" datetime="{{ token.expires }}">{{ token.expires }}</time>
</span>
</li>