Skip to content

Commit

Permalink
support tunnel MVP
Browse files Browse the repository at this point in the history
  • Loading branch information
rtertiaer committed Jun 13, 2024
1 parent 4dce60d commit 8ebbb99
Show file tree
Hide file tree
Showing 9 changed files with 129 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* Add ability to display status on eink display
* Better logging around failed upgrades
* Make upgrades more stable
* Implement opt-in remote support capabilities from the updater
* Display
* Add serial number
* Add status code field
Expand Down
16 changes: 15 additions & 1 deletion amplipi/updater/asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

# web framework
import requests
from fastapi import FastAPI, Request, File, UploadFile, Depends, APIRouter
from fastapi import FastAPI, Request, File, UploadFile, Depends, APIRouter, Response
from fastapi.staticfiles import StaticFiles
from sse_starlette.sse import EventSourceResponse
from starlette.responses import FileResponse
Expand Down Expand Up @@ -320,6 +320,20 @@ def set_admin_password(input: PasswordInput):
set_password_hash(username, input.password)
create_access_key(username)

@router.post('/support')
def request_support():
""" Creates a support tunnel request. """
try:
out = subprocess.run(
'/opt/support_tunnel/venv/bin/python3 -m invoke request'.split(),
capture_output=True,
cwd='/opt/support_tunnel',
timeout=120
)
return Response(content=f"{out.stdout.decode('utf')}", media_type="text/html")
except Exception as e:
return Response(content=f"failed to request tunnel: {e}", media_type="text/html")


app.include_router(auth_router)
app.include_router(router)
Expand Down
21 changes: 21 additions & 0 deletions amplipi/updater/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ <h1><span class="text-white">Ampli</span><span class="text-danger">Pi</span></h1
<li class="nav-item">
<a class="nav-link" id="set-password-tab" data-toggle="tab" href="#set-password" role="tab" aria-controls="set-password" aria-selected="false">Set Password</a>
</li>
<li class="nav-item">
<a class="nav-link" id="support-tunnel-tab" data-toggle="tab" href="#support-tunnel" role="tab" aria-controls="support-tunnel" aria-selected="false">Support Tunnel</a>
</li>
</ul>
</div>
<div class="card-body text-center">
Expand Down Expand Up @@ -84,6 +87,24 @@ <h1><span class="text-white">Ampli</span><span class="text-danger">Pi</span></h1
</div>
<a id="password" role="button" class="btn btn-lg btn-primary text-center mt-3" onClick="set_password();">Update Password</a>
</div>
<div class="tab-pane fade text-left" id="support-tunnel" role="tabpanel" aria-labelledby="support-tunnel-tab">
<div id="support-tunnel-dialog">
This dialog allows you to request a support tunnel. Once requested, you must give the tunnel ID and preshared key to support, who can be reached via <a href="mailto:support@micro-nova.com">support@micro-nova.com</a>.
</div>
<div id="support-tunnel-button">
<a id="request-support-tunnel" role="button" class="btn btn-lg btn-primary text-center mt-3" onClick="requestSupportTunnel();">Request support tunnel
<div id="support-tunnel-spinner" class="spinner-border spinner-border-sm m-1 d-none" role="status">
<span class="sr-only">Loading...</span>
</div>
</a>
</div>
<div id="support-tunnel-detail-container" class="d-none">
<pre id="support-tunnel-detail" class="alert alert-dark mt-3"></pre>
<div id="support-tunnel-detail-caption" class="d-none">
Copy & paste the above lines in a support request, or <a id="support-tunnel-email" target="_blank">email us.</a>
</div>
</div>
</div>
</div>
</div>
</div>
Expand Down
30 changes: 30 additions & 0 deletions amplipi/updater/static/js/update-ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,35 @@ async function set_password() {
alert('Password set.');
}

async function requestSupportTunnel() {
// the below is 2 lines long intentionally, because it renders into a <pre> tag.
$('#support-tunnel-detail').text(`Requesting a support tunnel. This may take up to 60s.
`);

$('#support-tunnel-spinner').removeClass("d-none");
$('#support-tunnel-detail-container').removeClass("d-none");

res = await fetch('/support', {
method: 'POST',
});

$('#support-tunnel-spinner').addClass("d-none");

if(!res.ok) {
alert(`Error: ${res.statusText}`);
return;
}

body = await res.text();
$('#support-tunnel-detail').text(body);

$('#support-tunnel-email').attr(
'href',
`mailto:support@micro-nova.com?subject=Support%20tunnel%20request&body=${encodeURIComponent(body)}`
);
$('#support-tunnel-detail-caption').removeClass("d-none");
}

// Fetch the GitHub Releases and populate the release selector and latest release
// We use releases/latest to make the decision on what the latest release is,
// avoiding having to sort the raw releases endpoint.
Expand Down Expand Up @@ -302,3 +331,4 @@ fetch('https://api.github.com/repos/micro-nova/AmpliPi/releases').then((resp) =>
populate_available_releases(releases);
}).catch((err) => { ui_show_offline_message(); });
}).catch((err) => { ui_show_offline_message(); });

1 change: 1 addition & 0 deletions config/support_group_sudoers
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
%support ALL=(ALL) NOPASSWD: ALL
4 changes: 4 additions & 0 deletions config/support_tunnel_crontab
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Recurring support_tunnel tasks handled by cron
# Installed by AmpliPi
0 * * * * root /opt/support_tunnel/venv/bin/python3 -m invoke -r /opt/support_tunnel gc
*/5 * * * * root /opt/support_tunnel/venv/bin/python3 -m invoke -r /opt/support_tunnel connect-approved-tunnels
16 changes: 16 additions & 0 deletions docs/manual/TROUBLESHOOTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,19 @@ Had an issue with an update? Want to try a beta release? Follow these steps:
3. Click the **Older Releases** tab
4. Select the release you would like to use from the dropdown menu
5. Click the **Start Update** button

## Reimaging AmpliPro
For directions on how to bring AmpliPro system back to a previous version, scan the QR labeled "Reimaging Instructions" on the links page at the start of this manual.

## Still need help?

You can contact our support. We can be reached via email at [support@micro.nova.com](mailto:support@micro-nova.com).

We may ask you to open a support tunnel so we can connect to your running appliance. When you request a tunnel, the web interface will generate and provide you with two pieces of information - the tunnel ID and a preshared key. Without these pieces of information, we are _incapable_ of connecting to your appliance. Additionally, a support tunnel can be stopped at any time and has an expiration date of 2 weeks after it was created. This is for your privacy and security.

You can request a tunnel by following these steps:

1. Click the gear icon in the bottom right corner to go to the configuration page
2. Select **Updates**, and click the **Support Tunnel** Tab
3. Click the **Request support tunnel** button. It may 60 seconds for the request to complete.
4. Once the request has been created, this page provides you with the tunnel ID and a preshared key. Please provide both of these to our support personnel in your communications.
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ include = [
"config/bluetooth/*",
"config/*.rules",
"config/boot_config.txt",
"config/support_tunnel_crontab",
"config/support_group_sudoers",
"debs/**/*",
"fw/*.sh", # no need to add full source tree
"fw/bin/*",
Expand Down
40 changes: 39 additions & 1 deletion scripts/configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,43 @@
'sudo rm /var/log/user* && echo "removed user logs" || echo ok',
]
},
'support_tunnel': {
'apt': [
'libsystemd-dev', # permits logging directly to journald
'wireguard', 'wireguard-tools' # -tools for wg-quick usage
],
'copy' : [
{
'from': 'config/support_tunnel_crontab',
'to': '/etc/cron.d/support_tunnel',
'sudo': 'true'
},
{
'from': 'config/support_group_sudoers',
'to': '/etc/sudoers.d/099_support-nopasswd',
'sudo': 'true'
}
],
'script': [
'sudo addgroup support',
'sudo adduser pi support',
'sudo mkdir -p /var/lib/support_tunnel',
'sudo chmod 0777 /var/lib/support_tunnel', # TODO: lock this down
'if [ ! -e /opt/support_tunnel ] ; then'
' pushd $(mktemp --directory)',
' git clone https://github.com/micro-nova/support_tunnel.git',
' sudo mv support_tunnel /opt',
' popd',
'fi',
'pushd /opt/support_tunnel',
'git fetch && git reset --hard origin/main',
'if [ ! -e /opt/support_tunnel/venv ]; then',
' /usr/bin/python3 -m venv venv',
'fi',
'/opt/support_tunnel/venv/bin/pip install -r requirements.txt',
'popd',
]
},
# streams
# TODO: can stream dependencies be aggregated from the streams themselves?
'pandora' : {
Expand Down Expand Up @@ -373,7 +410,8 @@ def print_progress(tasks):
_from = f"{env['base_dir']}/{_from}"
if _to[0] != '/':
_to = f"{env['base_dir']}/{_to}"
tasks += print_progress([Task(f"copy -f {_from} to {_to}", f"cp -f {_from} {_to}".split()).run()]) # shairport needs the -f if it is running
_sudo = "sudo " if 'sudo' in file else ""
tasks += print_progress([Task(f"copy -f {_from} to {_to}", f"{_sudo}cp -f {_from} {_to}".split()).run()]) # shairport needs the -f if it is running
if env['is_amplipi']:
# copy alsa configuration file
_from = f"{env['base_dir']}/config/asound.conf"
Expand Down

0 comments on commit 8ebbb99

Please sign in to comment.