External host setting (#162)

This commit is contained in:
Eugene 2022-07-03 18:46:07 +02:00 committed by GitHub
parent 6830c0c20d
commit f0d62412a7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 70 additions and 19 deletions

View file

@ -19,31 +19,38 @@ const fn _default_port() -> u16 {
22
}
#[inline]
fn _default_username() -> String {
"root".to_owned()
}
#[inline]
fn _default_empty_string() -> String {
"".to_owned()
}
#[inline]
fn _default_recordings_path() -> String {
"./data/recordings".to_owned()
}
#[inline]
fn _default_database_url() -> Secret<String> {
Secret::new("sqlite:data/db".to_owned())
}
#[inline]
fn _default_http_listen() -> String {
"0.0.0.0:8888".to_owned()
}
#[inline]
fn _default_retention() -> Duration {
Duration::SECOND * 60 * 60 * 24 * 7
}
fn _default_empty_string_vec() -> Vec<String> {
#[inline]
fn _default_empty_vec<T>() -> Vec<T> {
vec![]
}
@ -89,7 +96,7 @@ pub struct TargetWebAdminOptions {}
#[derive(Debug, Deserialize, Serialize, Clone, Object)]
pub struct Target {
pub name: String,
#[serde(default = "_default_empty_string_vec")]
#[serde(default = "_default_empty_vec")]
pub allow_roles: Vec<String>,
#[serde(flatten)]
pub options: TargetOptions,
@ -239,6 +246,9 @@ pub struct WarpgateConfigStore {
#[serde(default)]
pub recordings: RecordingsConfig,
#[serde(default)]
pub external_host: Option<String>,
#[serde(default = "_default_database_url")]
pub database_url: Secret<String>,
@ -259,6 +269,7 @@ impl Default for WarpgateConfigStore {
users: vec![],
roles: vec![],
recordings: RecordingsConfig::default(),
external_host: None,
database_url: _default_database_url(),
ssh: SSHConfig::default(),
http: HTTPConfig::default(),

View file

@ -3,6 +3,7 @@ use std::net::ToSocketAddrs;
use crate::common::SessionExt;
use poem::session::Session;
use poem::web::Data;
use poem::Request;
use poem_openapi::payload::Json;
use poem_openapi::{ApiResponse, Object, OpenApi};
use serde::Serialize;
@ -20,6 +21,7 @@ pub struct Info {
version: String,
username: Option<String>,
selected_target: Option<String>,
external_host: Option<String>,
ports: PortsInfo,
}
@ -34,14 +36,22 @@ impl Api {
#[oai(path = "/info", method = "get", operation_id = "get_info")]
async fn api_get_info(
&self,
req: &Request,
session: &Session,
services: Data<&Services>,
) -> poem::Result<InstanceInfoResponse> {
let config = services.config.lock().await;
let external_host = config
.store
.external_host
.as_deref()
.or_else(|| req.header(http::header::HOST))
.or_else(|| req.original_uri().host());
Ok(InstanceInfoResponse::Ok(Json(Info {
version: env!("CARGO_PKG_VERSION").to_string(),
username: session.get_username(),
selected_target: session.get_target_name(),
external_host: external_host.map(&str::to_string),
ports: if session.is_authenticated() {
PortsInfo {
ssh: config

View file

@ -12,6 +12,7 @@ use warpgate_common::{Services, TargetOptions, WarpgateServerHandle};
#[derive(Deserialize)]
struct QueryParams {
#[serde(rename="warpgate-target")]
warpgate_target: Option<String>,
}

View file

@ -1,7 +1,10 @@
<script lang="ts">
import { api, Target, UserSnapshot } from 'admin/lib/api'
import { getSSHUsername } from 'admin/lib/ssh'
import { makeExampleSSHCommand, makeSSHUsername } from 'common/ssh'
import { Alert, FormGroup, Modal, ModalBody, ModalHeader } from 'sveltestrap'
import { serverInfo } from 'gateway/lib/store'
import CopyButton from 'common/CopyButton.svelte'
import { makeTargetURL } from 'common/http';
let error: Error|undefined
let targets: Target[]|undefined
@ -20,7 +23,9 @@ load().catch(e => {
error = e
})
$: sshUsername = getSSHUsername(selectedUser, selectedTarget)
$: sshUsername = makeSSHUsername(selectedTarget?.name, selectedUser?.username)
$: exampleCommand = makeExampleSSHCommand(selectedTarget?.name, selectedUser?.username, $serverInfo)
$: targetURL = selectedTarget ? makeTargetURL(selectedTarget.name) : ''
</script>
@ -36,7 +41,11 @@ $: sshUsername = getSSHUsername(selectedUser, selectedTarget)
<div class="list-group list-group-flush">
{#each targets as target}
<!-- svelte-ignore a11y-missing-attribute -->
<a class="list-group-item list-group-item-action" on:click={() => selectedTarget = target}>
<a class="list-group-item list-group-item-action" on:click={() => {
if (target.options.kind !== 'TargetWebAdminOptions') {
selectedTarget = target
}
}}>
<strong class="me-auto">
{target.name}
</strong>
@ -67,8 +76,8 @@ $: sshUsername = getSSHUsername(selectedUser, selectedTarget)
</div>
</ModalHeader>
<ModalBody>
<h3>Access instructions</h3>
{#if selectedTarget?.options.kind === 'TargetSSHOptions'}
<h3>Connection instructions</h3>
{#if users}
<FormGroup floating label="Select a user">
<select bind:value={selectedUser} class="form-control">
@ -81,12 +90,21 @@ $: sshUsername = getSSHUsername(selectedUser, selectedTarget)
</FormGroup>
{/if}
<FormGroup floating label="SSH username">
<FormGroup floating label="SSH username" class="d-flex align-items-center">
<input type="text" class="form-control" readonly value={sshUsername} />
<CopyButton text={sshUsername} />
</FormGroup>
<FormGroup floating label="Example command">
<input type="text" class="form-control" readonly value={'ssh ' + sshUsername + '@warpgate-host -p warpgate-port'} />
<FormGroup floating label="Example command" class="d-flex align-items-center">
<input type="text" class="form-control" readonly value={exampleCommand} />
<CopyButton text={exampleCommand} />
</FormGroup>
{/if}
{#if selectedTarget?.options.kind === 'TargetHTTPOptions'}
<FormGroup floating label="Access URL" class="d-flex align-items-center">
<input type="text" class="form-control" readonly value={targetURL} />
<CopyButton text={targetURL} />
</FormGroup>
{/if}
</ModalBody>

View file

@ -1,5 +0,0 @@
import type { Target, UserSnapshot } from './api'
export function getSSHUsername (user: UserSnapshot|undefined, target: Target|undefined): string {
return `${user?.username ?? '<username>'}:${target?.name}`
}

View file

@ -0,0 +1,3 @@
export function makeTargetURL (targetName: string): string {
return `${location.protocol}//${location.host}/?warpgate-target=${targetName}`
}

View file

@ -0,0 +1,9 @@
import type { Info } from 'gateway/lib/api'
export function makeSSHUsername (targetName?: string, username?: string): string {
return `${username ?? 'username'}:${targetName ?? 'target'}`
}
export function makeExampleSSHCommand (targetName?: string, username?: string, serverInfo?: Info): string {
return `ssh ${makeSSHUsername(targetName, username)}@${serverInfo?.externalHost ?? 'warpgate-host'} -p ${serverInfo?.ports.ssh ?? 'warpgate-port'}`
}

View file

@ -1,6 +1,7 @@
<script lang="ts">
import { faArrowRight } from '@fortawesome/free-solid-svg-icons'
import CopyButton from 'common/CopyButton.svelte'
import { makeExampleSSHCommand, makeSSHUsername } from 'common/ssh';
import { api, Target, TargetKind } from 'gateway/lib/api'
import { createEventDispatcher } from 'svelte'
import Fa from 'svelte-fa'
@ -13,8 +14,8 @@ let targets: Target[]|undefined
let selectedTarget: Target|undefined
let sshUsername: string
$: sshUsername = `${$serverInfo?.username}:${selectedTarget?.name}`
$: exampleCommand = `ssh ${sshUsername}@warpgate-host -p ${$serverInfo?.ports.ssh}`
$: sshUsername = makeSSHUsername(selectedTarget?.name, $serverInfo?.username)
$: exampleCommand = makeExampleSSHCommand(selectedTarget?.name, $serverInfo?.username, $serverInfo)
async function init () {
targets = await api.getTargets()
@ -22,7 +23,7 @@ async function init () {
function selectTarget (target: Target) {
if (target.kind === TargetKind.Http) {
loadURL(`/?warpgate_target=${target.name}`)
loadURL(`/?warpgate-target=${target.name}`)
} else if (target.kind === TargetKind.WebAdmin) {
loadURL('/@warpgate/admin')
} else {
@ -46,7 +47,7 @@ init()
class="list-group-item list-group-item-action target-item"
href={
target.kind === TargetKind.Http
? `/?warpgate_target=${target.name}`
? `/?warpgate-target=${target.name}`
: '/@warpgate/admin'
}
on:click={e => {

View file

@ -107,6 +107,9 @@
"selected_target": {
"type": "string"
},
"external_host": {
"type": "string"
},
"ports": {
"$ref": "#/components/schemas/PortsInfo"
}

View file

@ -1,7 +1,7 @@
import { writable } from 'svelte/store'
import { api, Info } from './api'
export const serverInfo = writable<Info|null>(null)
export const serverInfo = writable<Info|undefined>(undefined)
export async function reloadServerInfo (): Promise<void> {
serverInfo.set(await api.getInfo())