Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DNM] Add ix-remote-assist for testing #1045

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions ix-dev/enterprise/ix-remote-assist/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# iX Remote Assist

[iX Remote Assist](https://truenas.com) Secure remote access for iX Support and Deployment Personel
44 changes: 44 additions & 0 deletions ix-dev/enterprise/ix-remote-assist/app.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
app_version: v1.76.6
capabilities:
- description: Able to perform various network-related operations.
name: NET_ADMIN
- description: Able to bind to a privileged port.
name: NET_RAW
- description: Able to load kernel modules.
name: SYS_MODULE
- description: Able to chown files.
name: CHOWN
- description: Able to bypass permission checks for it's sub-processes.
name: FOWNER
- description: Able to bypass permission checks.
name: DAC_OVERRIDE
categories:
- networking
description: iX Support Remote Assistance
home: https://truenas.com/
host_mounts:
- description: Network device
host_path: /dev/net/tun
icon: https://media.sys.truenas.net/apps/ix-chart/icons/icon.webp
keywords:
- vpn
lib_version: 2.0.24
lib_version_hash: 283cc9c5d0a45474968e1280324fab8fc7e176c41fdafdb6dcf9b9a74efebb9c
maintainers:
- email: dev@ixsystems.com
name: truenas
url: https://www.truenas.com/
name: ix-remote-assist
run_as_context:
- description: Runs as a root user.
gid: 0
group_name: root
uid: 0
user_name: root
screenshots: []
sources:
- https://truenas.com/
- https://hub.docker.com/r/tailscale/tailscale
title: iX Remote Assist
train: enterprise
version: 1.0.0
6 changes: 6 additions & 0 deletions ix-dev/enterprise/ix-remote-assist/item.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
categories:
- networking
icon_url: https://media.sys.truenas.net/apps/ix-chart/icons/icon.webp
screenshots: []
tags:
- vpn
8 changes: 8 additions & 0 deletions ix-dev/enterprise/ix-remote-assist/ix_values.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
images:
image:
repository: tailscale/tailscale
tag: v1.76.6

consts:
remote_assist_container_name: ix-remote-assist
state_path: /var/lib/tailscale
80 changes: 80 additions & 0 deletions ix-dev/enterprise/ix-remote-assist/questions.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
groups:
- name: Remote Assist Configuration
description: |
By enabling this service, you are providing access to TrueNAS
Support personnel for assistance. Please sure all safety precautions
are taken and your data is fully backed up before proceeding.
- name: Storage Configuration
description: Configure Storage for Remote Assist

questions:
- variable: remote_assist
label: ""
group: Remote Assist Configuration
schema:
type: dict
attrs:
- variable: auth_key
label: Auth Key
description: |
The auth key provided by TrueNAS Support .</br>
schema:
type: string
default: ""
required: true
private: true
- variable: storage
label: ""
group: Storage Configuration
schema:
type: dict
hidden: true
attrs:
- variable: state
label: State Storage
description: State Volume.
schema:
type: dict
attrs:
- variable: type
label: Type
description: |
ixVolume: Is dataset created automatically by the system.</br>
schema:
type: string
required: true
immutable: true
default: "ix_volume"
enum:
- value: "ix_volume"
description: ixVolume (Dataset created automatically by the system)
- variable: ix_volume_config
label: ixVolume Configuration
description: The configuration for the ixVolume dataset.
schema:
type: dict
show_if: [["type", "=", "ix_volume"]]
$ref:
- "normalize/ix_volume"
attrs:
- variable: acl_enable
label: Enable ACL
description: Enable ACL for the storage.
schema:
type: boolean
default: false
- variable: dataset_name
label: Dataset Name
description: The name of the dataset to use for storage.
schema:
type: string
required: true
immutable: true
hidden: true
default: "state"
- variable: acl_entries
label: ACL Configuration
schema:
type: dict
show_if: [["acl_enable", "=", true]]
attrs: []
22 changes: 22 additions & 0 deletions ix-dev/enterprise/ix-remote-assist/templates/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{% set tpl = ix_lib.base.render.Render(values) %}

{% set c1 = tpl.add_container(values.consts.remote_assist_container_name, "image") %}

{% do c1.add_caps(["NET_ADMIN", "NET_RAW", "SYS_MODULE", "CHOWN", "FOWNER", "DAC_OVERRIDE"]) %}
{% do c1.healthcheck.set_custom_test("tailscale status") %}

{% do c1.set_network_mode("host") %}

{% do c1.environment.add_env("TS_STATE_DIR", values.consts.state_path) %}
{% do c1.environment.add_env("TS_ACCEPT_DNS", false) %}
{% do c1.environment.add_env("TS_HOSTNAME", "ix-remote-assist") %}
{% do c1.environment.add_env("TS_USERSPACE", false) %}
{% do c1.environment.add_env("TS_AUTH_ONCE", true) %}
{% do c1.environment.add_env("TS_AUTHKEY", values.remote_assist.auth_key) %}
{% do c1.environment.add_env("TS_SOCKET", "/var/run/tailscale/tailscaled.sock") %}

{% do c1.add_tun_device(read_only=False) %}

{% do c1.add_storage(values.consts.state_path, values.storage.state) %}

{{ tpl.render() | tojson }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from render import Render

try:
from .error import RenderError
from .formatter import escape_dollar
from .validations import valid_octal_mode_or_raise, valid_fs_path_or_raise
except ImportError:
from error import RenderError
from formatter import escape_dollar
from validations import valid_octal_mode_or_raise, valid_fs_path_or_raise


class Configs:
def __init__(self, render_instance: "Render"):
self._render_instance = render_instance
self._configs: dict[str, dict] = {}

def add(self, name: str, data: str):
if not isinstance(data, str):
raise RenderError(f"Expected [data] to be a string, got [{type(data)}]")

if name not in self._configs:
self._configs[name] = {"name": name, "data": data}
return

if data == self._configs[name]["data"]:
return

raise RenderError(f"Config [{name}] already added with different data")

def has_configs(self):
return bool(self._configs)

def render(self):
return {
c["name"]: {"content": escape_dollar(c["data"])}
for c in sorted(self._configs.values(), key=lambda c: c["name"])
}


class ContainerConfigs:
def __init__(self, render_instance: "Render", configs: Configs):
self._render_instance = render_instance
self.top_level_configs: Configs = configs
self.container_configs: set[ContainerConfig] = set()

def add(self, name: str, data: str, target: str, mode: str = ""):
self.top_level_configs.add(name, data)

if target == "":
raise RenderError(f"Expected [target] to be set for config [{name}]")
if mode != "":
mode = valid_octal_mode_or_raise(mode)

if target in [c.target for c in self.container_configs]:
raise RenderError(f"Target [{target}] already used for another config")
target = valid_fs_path_or_raise(target)
self.container_configs.add(ContainerConfig(self._render_instance, name, target, mode))

def has_configs(self):
return bool(self.container_configs)

def render(self):
return [c.render() for c in sorted(self.container_configs, key=lambda c: c.source)]


class ContainerConfig:
def __init__(self, render_instance: "Render", source: str, target: str, mode: str):
self._render_instance = render_instance
self.source = source
self.target = target
self.mode = mode

def render(self):
result: dict[str, str | int] = {
"source": self.source,
"target": self.target,
}

if self.mode:
result["mode"] = int(self.mode, 8)

return result
Loading
Loading