From 2c8745c6893fb2696291ae5b17e73df7a873cb21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Tue, 18 May 2021 12:49:28 +0000 Subject: [PATCH 01/21] add volumes to container. --- internal/config/room.go | 7 +++++++ internal/room/manager.go | 12 ++++++++++++ internal/types/room.go | 3 ++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/internal/config/room.go b/internal/config/room.go index 0b1bf94..a312a5d 100644 --- a/internal/config/room.go +++ b/internal/config/room.go @@ -18,6 +18,7 @@ type Room struct { InstanceName string InstanceUrl string + InstanceData string TraefikDomain string TraefikEntrypoint string @@ -62,6 +63,11 @@ func (Room) Init(cmd *cobra.Command) error { return err } + cmd.PersistentFlags().String("instance.data", "", "absolute path on host to a folder, where peristent containers data will be stored") + if err := viper.BindPFlag("instance.data", cmd.PersistentFlags().Lookup("instance.url")); err != nil { + return err + } + // Traefik cmd.PersistentFlags().String("traefik.domain", "neko.lan", "traefik: domain on which will be container hosted") @@ -122,6 +128,7 @@ func (s *Room) Set() { s.InstanceName = viper.GetString("instance.name") s.InstanceUrl = viper.GetString("instance.url") + s.InstanceData = viper.GetString("instance.data") s.TraefikDomain = viper.GetString("traefik.domain") s.TraefikEntrypoint = viper.GetString("traefik.entrypoint") diff --git a/internal/room/manager.go b/internal/room/manager.go index 515327c..6c68afb 100644 --- a/internal/room/manager.go +++ b/internal/room/manager.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "path" "strings" dockerTypes "github.com/docker/docker/api/types" @@ -177,6 +178,15 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro env = append(env, fmt.Sprintf("NEKO_NAT1TO1=%s", strings.Join(manager.config.NAT1To1IPs, ","))) } + binds := []string{} + for hostPath, containerPath := range settings.Mounts { + binds = append( + binds, + // TODO: Check for invalid paths. + path.Join(manager.config.InstanceData, roomName, hostPath)+":"+containerPath, + ) + } + config := &container.Config{ // Hostname Hostname: containerName, @@ -194,6 +204,8 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro } hostConfig := &container.HostConfig{ + // List of volume bindings for this container + Binds: binds, // Port mapping between the exposed port (container) and the host PortBindings: portBindings, // Configuration of the logs for this container diff --git a/internal/types/room.go b/internal/types/room.go index 04dcefd..80e3560 100644 --- a/internal/types/room.go +++ b/internal/types/room.go @@ -59,7 +59,8 @@ type RoomSettings struct { BroadcastPipeline string `json:"broadcast_pipeline,omitempty"` - Envs map[string]string `json:"envs"` + Envs map[string]string `json:"envs"` + Mounts map[string]string `json:"mounts"` } func (settings *RoomSettings) ToEnv() []string { From 40931e8fb10ec6b3e5a110833900d1d73c9d04e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Wed, 19 May 2021 22:22:49 +0200 Subject: [PATCH 02/21] mount update api. --- OpenApi.yml | 14 +++++++++++++ client/src/api/.openapi-generator/VERSION | 2 +- client/src/api/api.ts | 25 +++++++++++++++++++++++ internal/room/manager.go | 4 ++-- internal/types/room.go | 7 ++++++- 5 files changed, 48 insertions(+), 4 deletions(-) diff --git a/OpenApi.yml b/OpenApi.yml index 57b3846..791984d 100644 --- a/OpenApi.yml +++ b/OpenApi.yml @@ -243,6 +243,16 @@ components: format: datetime example: "2021-03-07T21:56:34Z" + RoomMount: + type: object + properties: + host_path: + type: string + example: /profile + container_path: + type: string + example: /home/neko/.config/chromium + RoomSettings: type: object properties: @@ -291,6 +301,10 @@ components: type: string example: CUSTOM_ENV: custom value + mounts: + type: array + items: + $ref: '#/components/schemas/RoomMount' RoomStats: type: object diff --git a/client/src/api/.openapi-generator/VERSION b/client/src/api/.openapi-generator/VERSION index d509cc9..6555596 100644 --- a/client/src/api/.openapi-generator/VERSION +++ b/client/src/api/.openapi-generator/VERSION @@ -1 +1 @@ -5.1.1-SNAPSHOT \ No newline at end of file +5.2.0-SNAPSHOT \ No newline at end of file diff --git a/client/src/api/api.ts b/client/src/api/api.ts index da882f9..f2f1246 100644 --- a/client/src/api/api.ts +++ b/client/src/api/api.ts @@ -107,6 +107,25 @@ export interface RoomMember { */ muted?: boolean; } +/** + * + * @export + * @interface RoomMount + */ +export interface RoomMount { + /** + * + * @type {string} + * @memberof RoomMount + */ + host_path?: string; + /** + * + * @type {string} + * @memberof RoomMount + */ + container_path?: string; +} /** * * @export @@ -203,6 +222,12 @@ export interface RoomSettings { * @memberof RoomSettings */ envs?: { [key: string]: string; }; + /** + * + * @type {Array} + * @memberof RoomSettings + */ + mounts?: Array; } /** * diff --git a/internal/room/manager.go b/internal/room/manager.go index 6c68afb..1d43ce6 100644 --- a/internal/room/manager.go +++ b/internal/room/manager.go @@ -179,11 +179,11 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro } binds := []string{} - for hostPath, containerPath := range settings.Mounts { + for _, mount := range settings.Mounts { binds = append( binds, // TODO: Check for invalid paths. - path.Join(manager.config.InstanceData, roomName, hostPath)+":"+containerPath, + path.Join(manager.config.InstanceData, roomName, mount.HostPath)+":"+mount.ContainerPath, ) } diff --git a/internal/types/room.go b/internal/types/room.go index 80e3560..bfcbdb9 100644 --- a/internal/types/room.go +++ b/internal/types/room.go @@ -39,6 +39,11 @@ type RoomEntry struct { Created time.Time `json:"created"` } +type RoomMount struct { + HostPath string `json:"host_path"` + ContainerPath string `json:"container_path"` +} + type RoomSettings struct { Name string `json:"name"` NekoImage string `json:"neko_image"` @@ -60,7 +65,7 @@ type RoomSettings struct { BroadcastPipeline string `json:"broadcast_pipeline,omitempty"` Envs map[string]string `json:"envs"` - Mounts map[string]string `json:"mounts"` + Mounts []RoomMount `json:"mounts"` } func (settings *RoomSettings) ToEnv() []string { From 39b887a8fc528024f265d89f45a3e0b4abb94a5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Wed, 19 May 2021 22:40:47 +0200 Subject: [PATCH 03/21] add mounts to GUI. --- client/src/components/RoomInfo.vue | 9 ++++++++ client/src/components/RoomsCreate.vue | 31 +++++++++++++++++++++++++-- client/src/store/state.ts | 5 +++++ 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/client/src/components/RoomInfo.vue b/client/src/components/RoomInfo.vue index 7ca72fd..e12e31f 100644 --- a/client/src/components/RoomInfo.vue +++ b/client/src/components/RoomInfo.vue @@ -145,6 +145,15 @@ + +
Mounts
+ + + diff --git a/client/src/components/RoomsCreate.vue b/client/src/components/RoomsCreate.vue index 7467f02..51fb08a 100644 --- a/client/src/components/RoomsCreate.vue +++ b/client/src/components/RoomsCreate.vue @@ -187,7 +187,7 @@ mdi-plus - + - +
:
+ mdi-close
+ +

Mounts

+ mdi-plus +
+ + + + +
:
+ + + +
+ mdi-close +
+
diff --git a/client/src/store/state.ts b/client/src/store/state.ts index 9ec5685..d10cd89 100644 --- a/client/src/store/state.ts +++ b/client/src/store/state.ts @@ -37,6 +37,11 @@ export const state = { // eslint-disable-next-line broadcast_pipeline: '', + + // eslint-disable-next-line + envs: {}, + // eslint-disable-next-line + mounts: [], } as RoomSettings, videoCodecs: [ "VP8", From 79eef5bccff1d6f48c4e6a416f836af876659110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Wed, 19 May 2021 22:44:50 +0200 Subject: [PATCH 04/21] client mounts: add absolutePath rule. --- client/src/components/RoomsCreate.vue | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/client/src/components/RoomsCreate.vue b/client/src/components/RoomsCreate.vue index 51fb08a..a40667c 100644 --- a/client/src/components/RoomsCreate.vue +++ b/client/src/components/RoomsCreate.vue @@ -218,6 +218,7 @@ label="Host path" :value="host_path" @input="$set(data.mounts, index, { host_path: $event, container_path })" + :rules="[ rules.absolutePath ]" autocomplete="off" > @@ -227,6 +228,7 @@ label="Container path" :value="container_path" @input="$set(data.mounts, index, { host_path, container_path: $event})" + :rules="[ rules.absolutePath ]" autocomplete="off" > @@ -308,6 +310,9 @@ export default class RoomsCreate extends Vue { slug(val: string) { return val && !/^[A-Za-z0-9-_.]+$/.test(val) ? 'Should only contain A-Z a-z 0-9 - _ .' : true }, + absolutePath(val: string) { + return val[0] !== "/" ? 'Must be absolute path, starting with /.' : true + } } get nekoImages() { From 0bab1c0b86237fd34d850d6ffc980102fdb78f0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Wed, 19 May 2021 23:28:00 +0200 Subject: [PATCH 05/21] add container name and path check. --- client/src/components/RoomsCreate.vue | 16 +++++++++++----- internal/config/room.go | 16 ++++++++++++++++ internal/room/manager.go | 14 +++++++++++++- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/client/src/components/RoomsCreate.vue b/client/src/components/RoomsCreate.vue index a40667c..fa8aa1c 100644 --- a/client/src/components/RoomsCreate.vue +++ b/client/src/components/RoomsCreate.vue @@ -14,7 +14,7 @@ @@ -218,7 +218,7 @@ label="Host path" :value="host_path" @input="$set(data.mounts, index, { host_path: $event, container_path })" - :rules="[ rules.absolutePath ]" + :rules="[ rules.absolutePath, rules.withoutColon ]" autocomplete="off" > @@ -228,7 +228,7 @@ label="Container path" :value="container_path" @input="$set(data.mounts, index, { host_path, container_path: $event})" - :rules="[ rules.absolutePath ]" + :rules="[ rules.absolutePath, rules.withoutColon ]" autocomplete="off" > @@ -307,11 +307,17 @@ export default class RoomsCreate extends Vue { nonZero(val: string) { return val === "0" ? 'Value cannot be zero.' : true }, - slug(val: string) { - return val && !/^[A-Za-z0-9-_.]+$/.test(val) ? 'Should only contain A-Z a-z 0-9 - _ .' : true + containerName(val: string) { + return val && !/^[a-zA-Z0-9][a-zA-Z0-9_.-]+$/.test(val) ? 'Must only contain a-z A-Z 0-9 _ . -' : true + }, + containerNameStart(val: string) { + return val && /^[_.-]/.test(val) ? 'Cannot start with _ . -' : true }, absolutePath(val: string) { return val[0] !== "/" ? 'Must be absolute path, starting with /.' : true + }, + withoutColon(val: string) { + return val && /:$/.test(val) ? 'Cannnot contain : character.' : true } } diff --git a/internal/config/room.go b/internal/config/room.go index a312a5d..c463462 100644 --- a/internal/config/room.go +++ b/internal/config/room.go @@ -4,6 +4,7 @@ import ( "strconv" "strings" + dockerNames "github.com/docker/docker/daemon/names" "github.com/rs/zerolog/log" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -127,8 +128,23 @@ func (s *Room) Set() { s.NekoImages = viper.GetStringSlice("neko_images") s.InstanceName = viper.GetString("instance.name") + if !dockerNames.RestrictedNamePattern.MatchString(s.InstanceName) { + log.Panic().Msg("invalid `instance.name`, must match " + dockerNames.RestrictedNameChars) + } + s.InstanceUrl = viper.GetString("instance.url") s.InstanceData = viper.GetString("instance.data") + if s.InstanceData != "" { + if !strings.HasPrefix(s.InstanceData, "/") { + log.Panic().Msg("invalid `instance.data`, must be absolute path starting with /.") + } + + if strings.Contains(s.InstanceData, ":") { + log.Panic().Msg("invalid `instance.data`, cannot contain : character") + } + } else { + log.Warn().Msg("missing `instance.data`, container mounts are unavailable") + } s.TraefikDomain = viper.GetString("traefik.domain") s.TraefikEntrypoint = viper.GetString("traefik.entrypoint") diff --git a/internal/room/manager.go b/internal/room/manager.go index 1d43ce6..6e4e6f6 100644 --- a/internal/room/manager.go +++ b/internal/room/manager.go @@ -12,6 +12,7 @@ import ( network "github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/strslice" dockerClient "github.com/docker/docker/client" + dockerNames "github.com/docker/docker/daemon/names" "github.com/docker/go-connections/nat" "github.com/rs/zerolog" "github.com/rs/zerolog/log" @@ -84,6 +85,14 @@ func (manager *RoomManagerCtx) FindByName(name string) (*types.RoomEntry, error) } func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, error) { + if settings.Name != "" && dockerNames.RestrictedNamePattern.MatchString(settings.Name) { + return "", fmt.Errorf("invalid container name, must match " + dockerNames.RestrictedNameChars) + } + + if manager.config.InstanceData == "" && len(settings.Mounts) > 0 { + return "", fmt.Errorf("mounts cannot be specified, because `instance.data` config value is not set") + } + if in, _ := utils.ArrayIn(settings.NekoImage, manager.config.NekoImages); !in { return "", fmt.Errorf("invalid neko image") } @@ -180,9 +189,12 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro binds := []string{} for _, mount := range settings.Mounts { + if strings.Contains(mount.HostPath, ":") || strings.Contains(mount.ContainerPath, ":") { + return "", fmt.Errorf("mount paths cannot contain : character") + } + binds = append( binds, - // TODO: Check for invalid paths. path.Join(manager.config.InstanceData, roomName, mount.HostPath)+":"+mount.ContainerPath, ) } From 47623754ce383ca5e318f9f79b75cf914832ca4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Wed, 19 May 2021 23:32:08 +0200 Subject: [PATCH 06/21] fix error message. --- internal/config/room.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/config/room.go b/internal/config/room.go index c463462..e125087 100644 --- a/internal/config/room.go +++ b/internal/config/room.go @@ -136,7 +136,7 @@ func (s *Room) Set() { s.InstanceData = viper.GetString("instance.data") if s.InstanceData != "" { if !strings.HasPrefix(s.InstanceData, "/") { - log.Panic().Msg("invalid `instance.data`, must be absolute path starting with /.") + log.Panic().Msg("invalid `instance.data`, must be absolute path starting with /") } if strings.Contains(s.InstanceData, ":") { From ab0e3993c6819f0c140df01418696bd37df406c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Wed, 19 May 2021 23:57:36 +0200 Subject: [PATCH 07/21] docker names fix minor bugs. --- client/src/components/RoomInfo.vue | 2 +- internal/room/manager.go | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/client/src/components/RoomInfo.vue b/client/src/components/RoomInfo.vue index e12e31f..cee2be0 100644 --- a/client/src/components/RoomInfo.vue +++ b/client/src/components/RoomInfo.vue @@ -150,7 +150,7 @@ diff --git a/internal/room/manager.go b/internal/room/manager.go index 6e4e6f6..54533bd 100644 --- a/internal/room/manager.go +++ b/internal/room/manager.go @@ -85,7 +85,7 @@ func (manager *RoomManagerCtx) FindByName(name string) (*types.RoomEntry, error) } func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, error) { - if settings.Name != "" && dockerNames.RestrictedNamePattern.MatchString(settings.Name) { + if settings.Name != "" && !dockerNames.RestrictedNamePattern.MatchString(settings.Name) { return "", fmt.Errorf("invalid container name, must match " + dockerNames.RestrictedNameChars) } @@ -319,10 +319,19 @@ func (manager *RoomManagerCtx) GetSettings(id string) (*types.RoomSettings, erro return nil, err } + mounts := []types.RoomMount{} + for _, mount := range container.Mounts { + mounts = append(mounts, types.RoomMount{ + HostPath: mount.Source, + ContainerPath: mount.Destination, + }) + } + settings := types.RoomSettings{ Name: roomName, NekoImage: nekoImage, MaxConnections: epr.Max - epr.Min + 1, + Mounts: mounts, } err = settings.FromEnv(container.Config.Env) From 2eae217180c74e558515aa015efd67cbd23d97eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Wed, 19 May 2021 23:57:46 +0200 Subject: [PATCH 08/21] add data folder to dev. --- dev/.gitignore | 1 + dev/start | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 dev/.gitignore diff --git a/dev/.gitignore b/dev/.gitignore new file mode 100644 index 0000000..1269488 --- /dev/null +++ b/dev/.gitignore @@ -0,0 +1 @@ +data diff --git a/dev/start b/dev/start index 0955193..8388934 100755 --- a/dev/start +++ b/dev/start @@ -9,12 +9,15 @@ fi export $(cat ../.env | sed 's/#.*//g' | xargs) +mkdir -p "./data" + docker run --rm -it \ --name="neko_rooms_server" \ -v "${PWD}/../:/app" \ -e "TZ=${TZ}" \ -e "NEKO_ROOMS_EPR=${NEKO_ROOMS_EPR}" \ -e "NEKO_ROOMS_NAT1TO1=${NEKO_ROOMS_NAT1TO1}" \ + -e "NEKO_ROOMS_INSTANCE_DATA=`realpath ./data`" \ -e "NEKO_ROOMS_TRAEFIK_DOMAIN=${NEKO_ROOMS_TRAEFIK_DOMAIN}" \ -e "NEKO_ROOMS_TRAEFIK_ENTRYPOINT=${NEKO_ROOMS_TRAEFIK_ENTRYPOINT}" \ -e "NEKO_ROOMS_TRAEFIK_NETWORK=${NEKO_ROOMS_TRAEFIK_NETWORK}" \ From 13451186ceec8130e8b4a9aa66efd1f02358c881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Thu, 20 May 2021 00:34:37 +0200 Subject: [PATCH 09/21] clean path traversals. --- internal/config/room.go | 7 +++++-- internal/room/manager.go | 12 ++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/internal/config/room.go b/internal/config/room.go index e125087..1cae9e7 100644 --- a/internal/config/room.go +++ b/internal/config/room.go @@ -1,6 +1,7 @@ package config import ( + "path/filepath" "strconv" "strings" @@ -135,8 +136,10 @@ func (s *Room) Set() { s.InstanceUrl = viper.GetString("instance.url") s.InstanceData = viper.GetString("instance.data") if s.InstanceData != "" { - if !strings.HasPrefix(s.InstanceData, "/") { - log.Panic().Msg("invalid `instance.data`, must be absolute path starting with /") + s.InstanceData = filepath.Clean(s.InstanceData) + + if !filepath.IsAbs(s.InstanceData) { + log.Panic().Msg("invalid `instance.data`, must be an absolute path") } if strings.Contains(s.InstanceData, ":") { diff --git a/internal/room/manager.go b/internal/room/manager.go index 54533bd..668eea1 100644 --- a/internal/room/manager.go +++ b/internal/room/manager.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "path" + "path/filepath" "strings" dockerTypes "github.com/docker/docker/api/types" @@ -189,13 +190,20 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro binds := []string{} for _, mount := range settings.Mounts { - if strings.Contains(mount.HostPath, ":") || strings.Contains(mount.ContainerPath, ":") { + hostPath := filepath.Clean(mount.HostPath) + containerPath := filepath.Clean(mount.ContainerPath) + + if !filepath.IsAbs(hostPath) || !filepath.IsAbs(containerPath) { + return "", fmt.Errorf("mount paths must be absolute") + } + + if strings.Contains(hostPath, ":") || strings.Contains(containerPath, ":") { return "", fmt.Errorf("mount paths cannot contain : character") } binds = append( binds, - path.Join(manager.config.InstanceData, roomName, mount.HostPath)+":"+mount.ContainerPath, + path.Join(manager.config.InstanceData, roomName, hostPath)+":"+containerPath, ) } From f523b949113191346886b4f3fc4f37c657991f60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Thu, 20 May 2021 00:39:22 +0200 Subject: [PATCH 10/21] settings mount: remove root folder. --- internal/room/manager.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/room/manager.go b/internal/room/manager.go index 668eea1..dad3ce0 100644 --- a/internal/room/manager.go +++ b/internal/room/manager.go @@ -328,9 +328,10 @@ func (manager *RoomManagerCtx) GetSettings(id string) (*types.RoomSettings, erro } mounts := []types.RoomMount{} + dataRoot := path.Join(manager.config.InstanceData, roomName) for _, mount := range container.Mounts { mounts = append(mounts, types.RoomMount{ - HostPath: mount.Source, + HostPath: strings.TrimPrefix(mount.Source, dataRoot), ContainerPath: mount.Destination, }) } From f2935b998d9cc4c1a400d3d98fa09774e5c117e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Thu, 20 May 2021 21:57:28 +0200 Subject: [PATCH 11/21] remove colon path limitation. --- client/src/components/RoomsCreate.vue | 7 ++----- internal/config/room.go | 4 ---- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/client/src/components/RoomsCreate.vue b/client/src/components/RoomsCreate.vue index fa8aa1c..aedff85 100644 --- a/client/src/components/RoomsCreate.vue +++ b/client/src/components/RoomsCreate.vue @@ -218,7 +218,7 @@ label="Host path" :value="host_path" @input="$set(data.mounts, index, { host_path: $event, container_path })" - :rules="[ rules.absolutePath, rules.withoutColon ]" + :rules="[ rules.absolutePath ]" autocomplete="off" > @@ -228,7 +228,7 @@ label="Container path" :value="container_path" @input="$set(data.mounts, index, { host_path, container_path: $event})" - :rules="[ rules.absolutePath, rules.withoutColon ]" + :rules="[ rules.absolutePath ]" autocomplete="off" > @@ -315,9 +315,6 @@ export default class RoomsCreate extends Vue { }, absolutePath(val: string) { return val[0] !== "/" ? 'Must be absolute path, starting with /.' : true - }, - withoutColon(val: string) { - return val && /:$/.test(val) ? 'Cannnot contain : character.' : true } } diff --git a/internal/config/room.go b/internal/config/room.go index 1cae9e7..e308fe3 100644 --- a/internal/config/room.go +++ b/internal/config/room.go @@ -141,10 +141,6 @@ func (s *Room) Set() { if !filepath.IsAbs(s.InstanceData) { log.Panic().Msg("invalid `instance.data`, must be an absolute path") } - - if strings.Contains(s.InstanceData, ":") { - log.Panic().Msg("invalid `instance.data`, cannot contain : character") - } } else { log.Warn().Msg("missing `instance.data`, container mounts are unavailable") } From 0e99dd5fb0bfe4d3e0728e9a26ee61175e29548c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Thu, 20 May 2021 22:03:50 +0200 Subject: [PATCH 12/21] mounts instead of binds. --- internal/room/manager.go | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/internal/room/manager.go b/internal/room/manager.go index dad3ce0..efba50e 100644 --- a/internal/room/manager.go +++ b/internal/room/manager.go @@ -10,6 +10,7 @@ import ( dockerTypes "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" + dockerMount "github.com/docker/docker/api/types/mount" network "github.com/docker/docker/api/types/network" "github.com/docker/docker/api/types/strslice" dockerClient "github.com/docker/docker/client" @@ -188,7 +189,7 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro env = append(env, fmt.Sprintf("NEKO_NAT1TO1=%s", strings.Join(manager.config.NAT1To1IPs, ","))) } - binds := []string{} + mounts := []dockerMount.Mount{} for _, mount := range settings.Mounts { hostPath := filepath.Clean(mount.HostPath) containerPath := filepath.Clean(mount.ContainerPath) @@ -197,13 +198,19 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro return "", fmt.Errorf("mount paths must be absolute") } - if strings.Contains(hostPath, ":") || strings.Contains(containerPath, ":") { - return "", fmt.Errorf("mount paths cannot contain : character") - } + mounts = append(mounts, + dockerMount.Mount{ + Type: dockerMount.TypeBind, + Source: path.Join(manager.config.InstanceData, roomName, hostPath), + Target: containerPath, + ReadOnly: false, + Consistency: dockerMount.ConsistencyDefault, - binds = append( - binds, - path.Join(manager.config.InstanceData, roomName, hostPath)+":"+containerPath, + BindOptions: &dockerMount.BindOptions{ + Propagation: dockerMount.PropagationRPrivate, + NonRecursive: false, + }, + }, ) } @@ -224,8 +231,6 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro } hostConfig := &container.HostConfig{ - // List of volume bindings for this container - Binds: binds, // Port mapping between the exposed port (container) and the host PortBindings: portBindings, // Configuration of the logs for this container @@ -243,6 +248,8 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro }, // Total shm memory usage ShmSize: 2 * 10e9, + // Mounts specs used by the container + Mounts: mounts, } networkingConfig := &network.NetworkingConfig{ From edba322ba01aa960ff526ee4e89733b0279c9b8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Sat, 22 May 2021 00:40:21 +0200 Subject: [PATCH 13/21] fix container name test on client. --- client/src/components/RoomsCreate.vue | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/src/components/RoomsCreate.vue b/client/src/components/RoomsCreate.vue index aedff85..b665de2 100644 --- a/client/src/components/RoomsCreate.vue +++ b/client/src/components/RoomsCreate.vue @@ -14,7 +14,7 @@ @@ -301,6 +301,9 @@ export default class RoomsCreate extends Vue { required(val: any) { return val === null || typeof val === 'undefined' || val === "" ? 'This filed is mandatory.' : true }, + minLen: (min: number) => + (val: string) => + val ? (val.length >= min || 'This field must have atleast ' + min + ' characters') : true, onlyPositive(val: number) { return val < 0 ? 'Value cannot be negative.' : true }, @@ -308,7 +311,7 @@ export default class RoomsCreate extends Vue { return val === "0" ? 'Value cannot be zero.' : true }, containerName(val: string) { - return val && !/^[a-zA-Z0-9][a-zA-Z0-9_.-]+$/.test(val) ? 'Must only contain a-z A-Z 0-9 _ . -' : true + return val && !/^[a-zA-Z0-9_.-]+$/.test(val) ? 'Must only contain a-z A-Z 0-9 _ . -' : true }, containerNameStart(val: string) { return val && /^[_.-]/.test(val) ? 'Cannot start with _ . -' : true From e63c43e1503f04e827e570a020a9135e6600d4a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Sat, 22 May 2021 00:45:09 +0200 Subject: [PATCH 14/21] add internal & external storage. --- dev/start | 9 ++++--- internal/config/room.go | 53 ++++++++++++++++++++++++++++------------ internal/room/manager.go | 19 ++++++++++---- 3 files changed, 57 insertions(+), 24 deletions(-) diff --git a/dev/start b/dev/start index 8388934..eabd482 100755 --- a/dev/start +++ b/dev/start @@ -9,15 +9,18 @@ fi export $(cat ../.env | sed 's/#.*//g' | xargs) -mkdir -p "./data" +DATA_PATH="./data" +mkdir -p "${DATA_PATH}" docker run --rm -it \ --name="neko_rooms_server" \ - -v "${PWD}/../:/app" \ + -v "`realpath ..`:/app" \ + -v "`realpath ${DATA_PATH}`:/data" \ -e "TZ=${TZ}" \ -e "NEKO_ROOMS_EPR=${NEKO_ROOMS_EPR}" \ -e "NEKO_ROOMS_NAT1TO1=${NEKO_ROOMS_NAT1TO1}" \ - -e "NEKO_ROOMS_INSTANCE_DATA=`realpath ./data`" \ + -e "NEKO_ROOMS_STORAGE_INTERNAL=/data" \ + -e "NEKO_ROOMS_STORAGE_EXTERNAL=`realpath ${DATA_PATH}`" \ -e "NEKO_ROOMS_TRAEFIK_DOMAIN=${NEKO_ROOMS_TRAEFIK_DOMAIN}" \ -e "NEKO_ROOMS_TRAEFIK_ENTRYPOINT=${NEKO_ROOMS_TRAEFIK_ENTRYPOINT}" \ -e "NEKO_ROOMS_TRAEFIK_NETWORK=${NEKO_ROOMS_TRAEFIK_NETWORK}" \ diff --git a/internal/config/room.go b/internal/config/room.go index e308fe3..167c200 100644 --- a/internal/config/room.go +++ b/internal/config/room.go @@ -18,9 +18,12 @@ type Room struct { NAT1To1IPs []string NekoImages []string + StorageEnabled bool + StorageInternal string + StorageExternal string + InstanceName string InstanceUrl string - InstanceData string TraefikDomain string TraefikEntrypoint string @@ -53,6 +56,23 @@ func (Room) Init(cmd *cobra.Command) error { return err } + // Data + + cmd.PersistentFlags().Bool("storage.enabled", true, "whether storage is enabled, where peristent containers data will be stored") + if err := viper.BindPFlag("storage.enabled", cmd.PersistentFlags().Lookup("storage.enabled")); err != nil { + return err + } + + cmd.PersistentFlags().String("storage.external", "", "external absolute path (on the host) to storage folder") + if err := viper.BindPFlag("storage.external", cmd.PersistentFlags().Lookup("storage.external")); err != nil { + return err + } + + cmd.PersistentFlags().String("storage.internal", "/data", "internal absolute path (inside container) to storage folder") + if err := viper.BindPFlag("storage.internal", cmd.PersistentFlags().Lookup("storage.internal")); err != nil { + return err + } + // Instance cmd.PersistentFlags().String("instance.name", "neko-rooms", "unique instance name (if running muliple on the same host)") @@ -65,11 +85,6 @@ func (Room) Init(cmd *cobra.Command) error { return err } - cmd.PersistentFlags().String("instance.data", "", "absolute path on host to a folder, where peristent containers data will be stored") - if err := viper.BindPFlag("instance.data", cmd.PersistentFlags().Lookup("instance.url")); err != nil { - return err - } - // Traefik cmd.PersistentFlags().String("traefik.domain", "neko.lan", "traefik: domain on which will be container hosted") @@ -128,22 +143,28 @@ func (s *Room) Set() { s.NAT1To1IPs = viper.GetStringSlice("nat1to1") s.NekoImages = viper.GetStringSlice("neko_images") + s.StorageEnabled = viper.GetBool("storage.enabled") + s.StorageInternal = viper.GetString("storage.internal") + s.StorageExternal = viper.GetString("storage.external") + + if s.StorageInternal != "" && s.StorageExternal != "" { + s.StorageInternal = filepath.Clean(s.StorageInternal) + s.StorageExternal = filepath.Clean(s.StorageExternal) + + if !filepath.IsAbs(s.StorageInternal) || !filepath.IsAbs(s.StorageExternal) { + log.Panic().Msg("invalid `storage.internal` or `storage.external`, must be an absolute path") + } + } else { + log.Warn().Msg("missing `storage.internal` or `storage.external`, storage is unavailable") + s.StorageEnabled = false + } + s.InstanceName = viper.GetString("instance.name") if !dockerNames.RestrictedNamePattern.MatchString(s.InstanceName) { log.Panic().Msg("invalid `instance.name`, must match " + dockerNames.RestrictedNameChars) } s.InstanceUrl = viper.GetString("instance.url") - s.InstanceData = viper.GetString("instance.data") - if s.InstanceData != "" { - s.InstanceData = filepath.Clean(s.InstanceData) - - if !filepath.IsAbs(s.InstanceData) { - log.Panic().Msg("invalid `instance.data`, must be an absolute path") - } - } else { - log.Warn().Msg("missing `instance.data`, container mounts are unavailable") - } s.TraefikDomain = viper.GetString("traefik.domain") s.TraefikEntrypoint = viper.GetString("traefik.entrypoint") diff --git a/internal/room/manager.go b/internal/room/manager.go index ac8fdae..5486c03 100644 --- a/internal/room/manager.go +++ b/internal/room/manager.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "os" "path" "path/filepath" "strings" @@ -25,7 +26,8 @@ import ( ) const ( - frontendPort = 8080 + frontendPort = 8080 + roomStoragePath = "./rooms" ) func New(config *config.Room) *RoomManagerCtx { @@ -91,8 +93,8 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro return "", fmt.Errorf("invalid container name, must match " + dockerNames.RestrictedNameChars) } - if manager.config.InstanceData == "" && len(settings.Mounts) > 0 { - return "", fmt.Errorf("mounts cannot be specified, because `instance.data` config value is not set") + if !manager.config.StorageEnabled && len(settings.Mounts) > 0 { + return "", fmt.Errorf("mounts cannot be specified, because storage is disabled or unavailable") } if in, _ := utils.ArrayIn(settings.NekoImage, manager.config.NekoImages); !in { @@ -198,10 +200,17 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro return "", fmt.Errorf("mount paths must be absolute") } + externalPath := path.Join(manager.config.StorageExternal, roomStoragePath, roomName, hostPath) + internalPath := path.Join(manager.config.StorageInternal, roomStoragePath, roomName, hostPath) + + if err := os.MkdirAll(internalPath, os.ModePerm); err != nil { + return "", err + } + mounts = append(mounts, dockerMount.Mount{ Type: dockerMount.TypeBind, - Source: path.Join(manager.config.InstanceData, roomName, hostPath), + Source: externalPath, Target: containerPath, ReadOnly: false, Consistency: dockerMount.ConsistencyDefault, @@ -335,7 +344,7 @@ func (manager *RoomManagerCtx) GetSettings(id string) (*types.RoomSettings, erro } mounts := []types.RoomMount{} - dataRoot := path.Join(manager.config.InstanceData, roomName) + dataRoot := path.Join(manager.config.StorageExternal, roomStoragePath, roomName) for _, mount := range container.Mounts { mounts = append(mounts, types.RoomMount{ HostPath: strings.TrimPrefix(mount.Source, dataRoot), From 51b527dacea5a8394bcea957e95131f9f1c77b01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Sat, 22 May 2021 01:03:06 +0200 Subject: [PATCH 15/21] set storage uid and gid. --- internal/room/manager.go | 6 ++++++ internal/utils/fs.go | 15 +++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 internal/utils/fs.go diff --git a/internal/room/manager.go b/internal/room/manager.go index 5486c03..696ca33 100644 --- a/internal/room/manager.go +++ b/internal/room/manager.go @@ -28,6 +28,8 @@ import ( const ( frontendPort = 8080 roomStoragePath = "./rooms" + roomStorageUid = 1000 + roomStorageGid = 1000 ) func New(config *config.Room) *RoomManagerCtx { @@ -207,6 +209,10 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro return "", err } + if err := utils.ChownR(internalPath, roomStorageUid, roomStorageGid); err != nil { + return "", err + } + mounts = append(mounts, dockerMount.Mount{ Type: dockerMount.TypeBind, diff --git a/internal/utils/fs.go b/internal/utils/fs.go new file mode 100644 index 0000000..cc24a2c --- /dev/null +++ b/internal/utils/fs.go @@ -0,0 +1,15 @@ +package utils + +import ( + "os" + "path/filepath" +) + +func ChownR(path string, uid, gid int) error { + return filepath.Walk(path, func(name string, info os.FileInfo, err error) error { + if err == nil { + err = os.Chown(name, uid, gid) + } + return err + }) +} From 97b577ff550ce255f9225d408d797dd6d969ccd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Sat, 22 May 2021 01:19:37 +0200 Subject: [PATCH 16/21] add mounts hint. --- client/src/components/RoomsCreate.vue | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/src/components/RoomsCreate.vue b/client/src/components/RoomsCreate.vue index b665de2..61aec88 100644 --- a/client/src/components/RoomsCreate.vue +++ b/client/src/components/RoomsCreate.vue @@ -209,8 +209,8 @@ -

Mounts

- mdi-plus +

Mounts

+ mdi-plus
@@ -236,6 +236,9 @@ mdi-close + + Mounts are custom for every room. Host path is relative to <storage path>/room/<room name>. + From 3cb777e0478f3e1818a3bc1bc391fa1fbfff3d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Sat, 22 May 2021 17:50:16 +0200 Subject: [PATCH 17/21] add mount types. --- OpenApi.yml | 4 ++ client/src/api/api.ts | 17 +++++++ client/src/components/RoomsCreate.vue | 2 +- internal/config/room.go | 16 ++++++ internal/room/manager.go | 73 +++++++++++++++++++++------ internal/types/room.go | 13 ++++- 6 files changed, 107 insertions(+), 18 deletions(-) diff --git a/OpenApi.yml b/OpenApi.yml index 791984d..536b02a 100644 --- a/OpenApi.yml +++ b/OpenApi.yml @@ -246,6 +246,10 @@ components: RoomMount: type: object properties: + type: + type: string + enum: [ private, template, public ] + example: private host_path: type: string example: /profile diff --git a/client/src/api/api.ts b/client/src/api/api.ts index f2f1246..18c1af1 100644 --- a/client/src/api/api.ts +++ b/client/src/api/api.ts @@ -113,6 +113,12 @@ export interface RoomMember { * @interface RoomMount */ export interface RoomMount { + /** + * + * @type {string} + * @memberof RoomMount + */ + type?: RoomMountTypeEnum; /** * * @type {string} @@ -126,6 +132,17 @@ export interface RoomMount { */ container_path?: string; } + +/** + * @export + * @enum {string} + */ +export enum RoomMountTypeEnum { + private = 'private', + template = 'template', + public = 'public' +} + /** * * @export diff --git a/client/src/components/RoomsCreate.vue b/client/src/components/RoomsCreate.vue index 61aec88..4513731 100644 --- a/client/src/components/RoomsCreate.vue +++ b/client/src/components/RoomsCreate.vue @@ -210,7 +210,7 @@

Mounts

- mdi-plus + mdi-plus
diff --git a/internal/config/room.go b/internal/config/room.go index 167c200..2c45ac7 100644 --- a/internal/config/room.go +++ b/internal/config/room.go @@ -22,6 +22,8 @@ type Room struct { StorageInternal string StorageExternal string + MountsWhitelist []string + InstanceName string InstanceUrl string @@ -73,6 +75,11 @@ func (Room) Init(cmd *cobra.Command) error { return err } + cmd.PersistentFlags().StringSlice("mounts.whitelist", []string{}, "whitelisted public mounts for containers") + if err := viper.BindPFlag("mounts.whitelist", cmd.PersistentFlags().Lookup("mounts.whitelist")); err != nil { + return err + } + // Instance cmd.PersistentFlags().String("instance.name", "neko-rooms", "unique instance name (if running muliple on the same host)") @@ -159,6 +166,15 @@ func (s *Room) Set() { s.StorageEnabled = false } + s.MountsWhitelist = viper.GetStringSlice("mounts.whitelist") + for _, path := range s.MountsWhitelist { + path = filepath.Clean(path) + + if !filepath.IsAbs(path) { + log.Panic().Msg("invalid `mounts.whitelist`, must be an absolute path") + } + } + s.InstanceName = viper.GetString("instance.name") if !dockerNames.RestrictedNamePattern.MatchString(s.InstanceName) { log.Panic().Msg("invalid `instance.name`, must match " + dockerNames.RestrictedNameChars) diff --git a/internal/room/manager.go b/internal/room/manager.go index 696ca33..ee8d24b 100644 --- a/internal/room/manager.go +++ b/internal/room/manager.go @@ -26,10 +26,11 @@ import ( ) const ( - frontendPort = 8080 - roomStoragePath = "./rooms" - roomStorageUid = 1000 - roomStorageGid = 1000 + frontendPort = 8080 + templateStoragePath = "./templates" + privateStoragePath = "./rooms" + privateStorageUid = 1000 + privateStorageGid = 1000 ) func New(config *config.Room) *RoomManagerCtx { @@ -195,6 +196,8 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro mounts := []dockerMount.Mount{} for _, mount := range settings.Mounts { + readOnly := false + hostPath := filepath.Clean(mount.HostPath) containerPath := filepath.Clean(mount.ContainerPath) @@ -202,23 +205,49 @@ func (manager *RoomManagerCtx) Create(settings types.RoomSettings) (string, erro return "", fmt.Errorf("mount paths must be absolute") } - externalPath := path.Join(manager.config.StorageExternal, roomStoragePath, roomName, hostPath) - internalPath := path.Join(manager.config.StorageInternal, roomStoragePath, roomName, hostPath) + // private container's data + if mount.Type == types.MountPrivate { + // ensure that target exists + internalPath := path.Join(manager.config.StorageInternal, privateStoragePath, roomName, hostPath) + if err := os.MkdirAll(internalPath, os.ModePerm); err != nil { + return "", err + } - if err := os.MkdirAll(internalPath, os.ModePerm); err != nil { - return "", err - } + if err := utils.ChownR(internalPath, privateStorageUid, privateStorageGid); err != nil { + return "", err + } - if err := utils.ChownR(internalPath, roomStorageUid, roomStorageGid); err != nil { - return "", err + // prefix host path + hostPath = path.Join(manager.config.StorageExternal, privateStoragePath, roomName, hostPath) + } else if mount.Type == types.MountTemplate { + // readonly template data + readOnly = true + + // prefix host path + hostPath = path.Join(manager.config.StorageExternal, templateStoragePath, hostPath) + } else if mount.Type == types.MountPublic { + // public whitelisted mounts + var isAllowed = false + for _, path := range manager.config.MountsWhitelist { + if strings.HasPrefix(hostPath, path) { + isAllowed = true + break + } + } + + if !isAllowed { + return "", fmt.Errorf("mount path is not whitelisted in config") + } + } else { + return "", fmt.Errorf("unknown mount type %q", mount.Type) } mounts = append(mounts, dockerMount.Mount{ Type: dockerMount.TypeBind, - Source: externalPath, + Source: hostPath, Target: containerPath, - ReadOnly: false, + ReadOnly: readOnly, Consistency: dockerMount.ConsistencyDefault, BindOptions: &dockerMount.BindOptions{ @@ -349,11 +378,25 @@ func (manager *RoomManagerCtx) GetSettings(id string) (*types.RoomSettings, erro return nil, err } + privateStorageRoot := path.Join(manager.config.StorageExternal, privateStoragePath, roomName) + templateStorageRoot := path.Join(manager.config.StorageExternal, templateStoragePath) + mounts := []types.RoomMount{} - dataRoot := path.Join(manager.config.StorageExternal, roomStoragePath, roomName) for _, mount := range container.Mounts { + mountType := types.MountPublic + hostPath := mount.Source + + if strings.HasPrefix(hostPath, privateStorageRoot) { + mountType = types.MountPrivate + hostPath = strings.TrimPrefix(hostPath, privateStorageRoot) + } else if strings.HasPrefix(hostPath, templateStorageRoot) { + mountType = types.MountTemplate + hostPath = strings.TrimPrefix(hostPath, templateStorageRoot) + } + mounts = append(mounts, types.RoomMount{ - HostPath: strings.TrimPrefix(mount.Source, dataRoot), + Type: mountType, + HostPath: hostPath, ContainerPath: mount.Destination, }) } diff --git a/internal/types/room.go b/internal/types/room.go index bfcbdb9..f6808af 100644 --- a/internal/types/room.go +++ b/internal/types/room.go @@ -39,9 +39,18 @@ type RoomEntry struct { Created time.Time `json:"created"` } +type MountType string + +const ( + MountPrivate MountType = "private" + MountTemplate MountType = "template" + MountPublic MountType = "public" +) + type RoomMount struct { - HostPath string `json:"host_path"` - ContainerPath string `json:"container_path"` + Type MountType `json:"type"` + HostPath string `json:"host_path"` + ContainerPath string `json:"container_path"` } type RoomSettings struct { From 0f08168dff93cffcbefdc16c30bedad2494c42d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Sat, 22 May 2021 19:25:14 +0200 Subject: [PATCH 18/21] add external path to dev. --- dev/.gitignore | 1 + dev/start | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/dev/.gitignore b/dev/.gitignore index 1269488..f3d333e 100644 --- a/dev/.gitignore +++ b/dev/.gitignore @@ -1 +1,2 @@ data +ext diff --git a/dev/start b/dev/start index eabd482..69f345a 100755 --- a/dev/start +++ b/dev/start @@ -12,6 +12,9 @@ export $(cat ../.env | sed 's/#.*//g' | xargs) DATA_PATH="./data" mkdir -p "${DATA_PATH}" +EXTERNAL_PATH="./ext" +mkdir -p "${EXTERNAL_PATH}" + docker run --rm -it \ --name="neko_rooms_server" \ -v "`realpath ..`:/app" \ @@ -21,6 +24,7 @@ docker run --rm -it \ -e "NEKO_ROOMS_NAT1TO1=${NEKO_ROOMS_NAT1TO1}" \ -e "NEKO_ROOMS_STORAGE_INTERNAL=/data" \ -e "NEKO_ROOMS_STORAGE_EXTERNAL=`realpath ${DATA_PATH}`" \ + -e "NEKO_ROOMS_MOUNTS_WHITELIST=`realpath ${EXTERNAL_PATH}`" \ -e "NEKO_ROOMS_TRAEFIK_DOMAIN=${NEKO_ROOMS_TRAEFIK_DOMAIN}" \ -e "NEKO_ROOMS_TRAEFIK_ENTRYPOINT=${NEKO_ROOMS_TRAEFIK_ENTRYPOINT}" \ -e "NEKO_ROOMS_TRAEFIK_NETWORK=${NEKO_ROOMS_TRAEFIK_NETWORK}" \ From acb0b0a1a1d6e6b951fc7e8f9b5ecd73948d3510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Sat, 22 May 2021 19:25:26 +0200 Subject: [PATCH 19/21] client: multiple mount types. --- client/src/components/RoomsCreate.vue | 35 ++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/client/src/components/RoomsCreate.vue b/client/src/components/RoomsCreate.vue index 4513731..b6d6a20 100644 --- a/client/src/components/RoomsCreate.vue +++ b/client/src/components/RoomsCreate.vue @@ -212,8 +212,14 @@

Mounts

mdi-plus
- - + + + + + mdi-close - - Mounts are custom for every room. Host path is relative to <storage path>/room/<room name>. + +

+ Private: Host path is relative to <storage path>/rooms/<room name>/.
+ Template: Host path is relative to <storage path>/templates/ and is readonly.
+ Public: Host path must be whitelisted in config and exists on the host. +

@@ -340,6 +350,23 @@ export default class RoomsCreate extends Vue { return this.$store.state.availableScreens } + get mountTypes() { + return [ + { + text: 'Private', + value: 'private', + }, + { + text: 'Template', + value: 'template', + }, + { + text: 'Public', + value: 'public', + }, + ] + } + addEnv() { Vue.set(this, 'envList', [ ...this.envList, { key: '', val: '' } ]) } From e434059676aa0923ba94f9a3e41cfa6f08c54a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Sat, 22 May 2021 20:10:41 +0200 Subject: [PATCH 20/21] fix mount types on FE. --- client/src/components/RoomsCreate.vue | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/client/src/components/RoomsCreate.vue b/client/src/components/RoomsCreate.vue index b6d6a20..f94e18b 100644 --- a/client/src/components/RoomsCreate.vue +++ b/client/src/components/RoomsCreate.vue @@ -212,18 +212,20 @@

Mounts

mdi-plus
- + @@ -233,7 +235,7 @@ From dcdd2ab8a79e3ec7d4e4489587dbd0948bdf311c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Sat, 22 May 2021 20:11:20 +0200 Subject: [PATCH 21/21] update roadmap. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4814b27..9280933 100644 --- a/README.md +++ b/README.md @@ -90,8 +90,8 @@ docker-compose up -d - [x] add GUI - [x] add HTTPS support - [x] add authentication provider for traefik - - [x] allow specifying custom ENV variables (TODO: FE.) - - [ ] allow mounting direcotries for persistent data + - [x] allow specifying custom ENV variables + - [x] allow mounting direcotries for persistent data - [ ] add upgrade button - [ ] auto pull images, that do not exist - [ ] add bearer token to for API