mirror of
https://github.com/warp-tech/warpgate.git
synced 2025-09-11 09:04:51 +08:00
External host setting (#162)
This commit is contained in:
parent
6830c0c20d
commit
f0d62412a7
10 changed files with 70 additions and 19 deletions
|
@ -19,31 +19,38 @@ const fn _default_port() -> u16 {
|
||||||
22
|
22
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn _default_username() -> String {
|
fn _default_username() -> String {
|
||||||
"root".to_owned()
|
"root".to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn _default_empty_string() -> String {
|
fn _default_empty_string() -> String {
|
||||||
"".to_owned()
|
"".to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn _default_recordings_path() -> String {
|
fn _default_recordings_path() -> String {
|
||||||
"./data/recordings".to_owned()
|
"./data/recordings".to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn _default_database_url() -> Secret<String> {
|
fn _default_database_url() -> Secret<String> {
|
||||||
Secret::new("sqlite:data/db".to_owned())
|
Secret::new("sqlite:data/db".to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn _default_http_listen() -> String {
|
fn _default_http_listen() -> String {
|
||||||
"0.0.0.0:8888".to_owned()
|
"0.0.0.0:8888".to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn _default_retention() -> Duration {
|
fn _default_retention() -> Duration {
|
||||||
Duration::SECOND * 60 * 60 * 24 * 7
|
Duration::SECOND * 60 * 60 * 24 * 7
|
||||||
}
|
}
|
||||||
|
|
||||||
fn _default_empty_string_vec() -> Vec<String> {
|
#[inline]
|
||||||
|
fn _default_empty_vec<T>() -> Vec<T> {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +96,7 @@ pub struct TargetWebAdminOptions {}
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, Object)]
|
#[derive(Debug, Deserialize, Serialize, Clone, Object)]
|
||||||
pub struct Target {
|
pub struct Target {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
#[serde(default = "_default_empty_string_vec")]
|
#[serde(default = "_default_empty_vec")]
|
||||||
pub allow_roles: Vec<String>,
|
pub allow_roles: Vec<String>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub options: TargetOptions,
|
pub options: TargetOptions,
|
||||||
|
@ -239,6 +246,9 @@ pub struct WarpgateConfigStore {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub recordings: RecordingsConfig,
|
pub recordings: RecordingsConfig,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
pub external_host: Option<String>,
|
||||||
|
|
||||||
#[serde(default = "_default_database_url")]
|
#[serde(default = "_default_database_url")]
|
||||||
pub database_url: Secret<String>,
|
pub database_url: Secret<String>,
|
||||||
|
|
||||||
|
@ -259,6 +269,7 @@ impl Default for WarpgateConfigStore {
|
||||||
users: vec![],
|
users: vec![],
|
||||||
roles: vec![],
|
roles: vec![],
|
||||||
recordings: RecordingsConfig::default(),
|
recordings: RecordingsConfig::default(),
|
||||||
|
external_host: None,
|
||||||
database_url: _default_database_url(),
|
database_url: _default_database_url(),
|
||||||
ssh: SSHConfig::default(),
|
ssh: SSHConfig::default(),
|
||||||
http: HTTPConfig::default(),
|
http: HTTPConfig::default(),
|
||||||
|
|
|
@ -3,6 +3,7 @@ use std::net::ToSocketAddrs;
|
||||||
use crate::common::SessionExt;
|
use crate::common::SessionExt;
|
||||||
use poem::session::Session;
|
use poem::session::Session;
|
||||||
use poem::web::Data;
|
use poem::web::Data;
|
||||||
|
use poem::Request;
|
||||||
use poem_openapi::payload::Json;
|
use poem_openapi::payload::Json;
|
||||||
use poem_openapi::{ApiResponse, Object, OpenApi};
|
use poem_openapi::{ApiResponse, Object, OpenApi};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
@ -20,6 +21,7 @@ pub struct Info {
|
||||||
version: String,
|
version: String,
|
||||||
username: Option<String>,
|
username: Option<String>,
|
||||||
selected_target: Option<String>,
|
selected_target: Option<String>,
|
||||||
|
external_host: Option<String>,
|
||||||
ports: PortsInfo,
|
ports: PortsInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,14 +36,22 @@ impl Api {
|
||||||
#[oai(path = "/info", method = "get", operation_id = "get_info")]
|
#[oai(path = "/info", method = "get", operation_id = "get_info")]
|
||||||
async fn api_get_info(
|
async fn api_get_info(
|
||||||
&self,
|
&self,
|
||||||
|
req: &Request,
|
||||||
session: &Session,
|
session: &Session,
|
||||||
services: Data<&Services>,
|
services: Data<&Services>,
|
||||||
) -> poem::Result<InstanceInfoResponse> {
|
) -> poem::Result<InstanceInfoResponse> {
|
||||||
let config = services.config.lock().await;
|
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 {
|
Ok(InstanceInfoResponse::Ok(Json(Info {
|
||||||
version: env!("CARGO_PKG_VERSION").to_string(),
|
version: env!("CARGO_PKG_VERSION").to_string(),
|
||||||
username: session.get_username(),
|
username: session.get_username(),
|
||||||
selected_target: session.get_target_name(),
|
selected_target: session.get_target_name(),
|
||||||
|
external_host: external_host.map(&str::to_string),
|
||||||
ports: if session.is_authenticated() {
|
ports: if session.is_authenticated() {
|
||||||
PortsInfo {
|
PortsInfo {
|
||||||
ssh: config
|
ssh: config
|
||||||
|
|
|
@ -12,6 +12,7 @@ use warpgate_common::{Services, TargetOptions, WarpgateServerHandle};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct QueryParams {
|
struct QueryParams {
|
||||||
|
#[serde(rename="warpgate-target")]
|
||||||
warpgate_target: Option<String>,
|
warpgate_target: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { api, Target, UserSnapshot } from 'admin/lib/api'
|
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 { 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 error: Error|undefined
|
||||||
let targets: Target[]|undefined
|
let targets: Target[]|undefined
|
||||||
|
@ -20,7 +23,9 @@ load().catch(e => {
|
||||||
error = 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>
|
</script>
|
||||||
|
|
||||||
|
@ -36,7 +41,11 @@ $: sshUsername = getSSHUsername(selectedUser, selectedTarget)
|
||||||
<div class="list-group list-group-flush">
|
<div class="list-group list-group-flush">
|
||||||
{#each targets as target}
|
{#each targets as target}
|
||||||
<!-- svelte-ignore a11y-missing-attribute -->
|
<!-- 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">
|
<strong class="me-auto">
|
||||||
{target.name}
|
{target.name}
|
||||||
</strong>
|
</strong>
|
||||||
|
@ -67,8 +76,8 @@ $: sshUsername = getSSHUsername(selectedUser, selectedTarget)
|
||||||
</div>
|
</div>
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
|
<h3>Access instructions</h3>
|
||||||
{#if selectedTarget?.options.kind === 'TargetSSHOptions'}
|
{#if selectedTarget?.options.kind === 'TargetSSHOptions'}
|
||||||
<h3>Connection instructions</h3>
|
|
||||||
{#if users}
|
{#if users}
|
||||||
<FormGroup floating label="Select a user">
|
<FormGroup floating label="Select a user">
|
||||||
<select bind:value={selectedUser} class="form-control">
|
<select bind:value={selectedUser} class="form-control">
|
||||||
|
@ -81,12 +90,21 @@ $: sshUsername = getSSHUsername(selectedUser, selectedTarget)
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
{/if}
|
{/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} />
|
<input type="text" class="form-control" readonly value={sshUsername} />
|
||||||
|
<CopyButton text={sshUsername} />
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
|
|
||||||
<FormGroup floating label="Example command">
|
<FormGroup floating label="Example command" class="d-flex align-items-center">
|
||||||
<input type="text" class="form-control" readonly value={'ssh ' + sshUsername + '@warpgate-host -p warpgate-port'} />
|
<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>
|
</FormGroup>
|
||||||
{/if}
|
{/if}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
|
|
@ -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}`
|
|
||||||
}
|
|
3
warpgate-web/src/common/http.ts
Normal file
3
warpgate-web/src/common/http.ts
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
export function makeTargetURL (targetName: string): string {
|
||||||
|
return `${location.protocol}//${location.host}/?warpgate-target=${targetName}`
|
||||||
|
}
|
9
warpgate-web/src/common/ssh.ts
Normal file
9
warpgate-web/src/common/ssh.ts
Normal 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'}`
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { faArrowRight } from '@fortawesome/free-solid-svg-icons'
|
import { faArrowRight } from '@fortawesome/free-solid-svg-icons'
|
||||||
import CopyButton from 'common/CopyButton.svelte'
|
import CopyButton from 'common/CopyButton.svelte'
|
||||||
|
import { makeExampleSSHCommand, makeSSHUsername } from 'common/ssh';
|
||||||
import { api, Target, TargetKind } from 'gateway/lib/api'
|
import { api, Target, TargetKind } from 'gateway/lib/api'
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
import Fa from 'svelte-fa'
|
import Fa from 'svelte-fa'
|
||||||
|
@ -13,8 +14,8 @@ let targets: Target[]|undefined
|
||||||
let selectedTarget: Target|undefined
|
let selectedTarget: Target|undefined
|
||||||
let sshUsername: string
|
let sshUsername: string
|
||||||
|
|
||||||
$: sshUsername = `${$serverInfo?.username}:${selectedTarget?.name}`
|
$: sshUsername = makeSSHUsername(selectedTarget?.name, $serverInfo?.username)
|
||||||
$: exampleCommand = `ssh ${sshUsername}@warpgate-host -p ${$serverInfo?.ports.ssh}`
|
$: exampleCommand = makeExampleSSHCommand(selectedTarget?.name, $serverInfo?.username, $serverInfo)
|
||||||
|
|
||||||
async function init () {
|
async function init () {
|
||||||
targets = await api.getTargets()
|
targets = await api.getTargets()
|
||||||
|
@ -22,7 +23,7 @@ async function init () {
|
||||||
|
|
||||||
function selectTarget (target: Target) {
|
function selectTarget (target: Target) {
|
||||||
if (target.kind === TargetKind.Http) {
|
if (target.kind === TargetKind.Http) {
|
||||||
loadURL(`/?warpgate_target=${target.name}`)
|
loadURL(`/?warpgate-target=${target.name}`)
|
||||||
} else if (target.kind === TargetKind.WebAdmin) {
|
} else if (target.kind === TargetKind.WebAdmin) {
|
||||||
loadURL('/@warpgate/admin')
|
loadURL('/@warpgate/admin')
|
||||||
} else {
|
} else {
|
||||||
|
@ -46,7 +47,7 @@ init()
|
||||||
class="list-group-item list-group-item-action target-item"
|
class="list-group-item list-group-item-action target-item"
|
||||||
href={
|
href={
|
||||||
target.kind === TargetKind.Http
|
target.kind === TargetKind.Http
|
||||||
? `/?warpgate_target=${target.name}`
|
? `/?warpgate-target=${target.name}`
|
||||||
: '/@warpgate/admin'
|
: '/@warpgate/admin'
|
||||||
}
|
}
|
||||||
on:click={e => {
|
on:click={e => {
|
||||||
|
|
|
@ -107,6 +107,9 @@
|
||||||
"selected_target": {
|
"selected_target": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"external_host": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"ports": {
|
"ports": {
|
||||||
"$ref": "#/components/schemas/PortsInfo"
|
"$ref": "#/components/schemas/PortsInfo"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { writable } from 'svelte/store'
|
import { writable } from 'svelte/store'
|
||||||
import { api, Info } from './api'
|
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> {
|
export async function reloadServerInfo (): Promise<void> {
|
||||||
serverInfo.set(await api.getInfo())
|
serverInfo.set(await api.getInfo())
|
||||||
|
|
Loading…
Add table
Reference in a new issue