Working SMB file share
This commit is contained in:
parent
7789923672
commit
76b20570ec
11 changed files with 187 additions and 7 deletions
|
|
@ -1 +1,4 @@
|
|||
echo "PasswordAuthentication no" >> /etc/ssh/sshd_config
|
||||
|
||||
addgroup -g 1000 linsk
|
||||
adduser -G linsk linsk -S -u 1000
|
||||
|
|
@ -2,3 +2,4 @@ openssh
|
|||
lvm2
|
||||
util-linux
|
||||
cryptsetup
|
||||
samba
|
||||
|
|
@ -27,7 +27,7 @@ var lsCmd = &cobra.Command{
|
|||
|
||||
fmt.Print(string(lsblkOut))
|
||||
return 0
|
||||
}))
|
||||
}, nil))
|
||||
|
||||
return nil
|
||||
},
|
||||
|
|
|
|||
33
cmd/run.go
33
cmd/run.go
|
|
@ -4,9 +4,11 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/AlexSSD7/linsk/vm"
|
||||
"github.com/sethvargo/go-password/password"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
|
@ -19,6 +21,12 @@ var runCmd = &cobra.Command{
|
|||
vmMountDevName := args[1]
|
||||
fsType := args[2]
|
||||
|
||||
networkSharePort, err := getClosestAvailPort(9000)
|
||||
if err != nil {
|
||||
slog.Error("Failed to get closest available host port for network file share", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// TODO: `slog` library prints entire stack traces for errors which makes reading errors challenging.
|
||||
|
||||
os.Exit(runVM(args[0], func(ctx context.Context, i *vm.Instance, fm *vm.FileManager) int {
|
||||
|
|
@ -31,10 +39,31 @@ var runCmd = &cobra.Command{
|
|||
return 1
|
||||
}
|
||||
|
||||
fmt.Println("Mounted! Now sleeping")
|
||||
sharePWD, err := password.Generate(16, 10, 0, false, false)
|
||||
if err != nil {
|
||||
slog.Error("Failed to generate ephemeral password for network file share", "error", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
shareURI := "smb://linsk:" + sharePWD + "@127.0.0.1:" + fmt.Sprint(networkSharePort)
|
||||
|
||||
fmt.Fprintf(os.Stderr, "================\n[Network File Share Config]\nThe network file share was started. Please use the credentials below to connect to the file server.\n\nType: SMB\nServer Address: smb://127.0.0.1:%v\nUsername: linsk\nPassword: %v\n\nShare URI: %v\n================\n", networkSharePort, sharePWD, shareURI)
|
||||
|
||||
err = fm.StartSMB([]byte(sharePWD))
|
||||
if err != nil {
|
||||
slog.Error("Failed to start SMB server", "error", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
slog.Info("Started the network share successfully", "type", "smb")
|
||||
|
||||
<-ctx.Done()
|
||||
return 0
|
||||
}))
|
||||
}, []vm.PortForwardingConfig{{
|
||||
HostIP: net.ParseIP("127.0.0.1"), // TODO: Make this changeable.
|
||||
HostPort: networkSharePort,
|
||||
VMPort: 445,
|
||||
}}))
|
||||
|
||||
return nil
|
||||
},
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ var shellCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
return 0
|
||||
}))
|
||||
}, nil))
|
||||
|
||||
return nil
|
||||
},
|
||||
|
|
|
|||
37
cmd/utils.go
37
cmd/utils.go
|
|
@ -2,6 +2,8 @@ package cmd
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"os/user"
|
||||
|
|
@ -36,7 +38,7 @@ func doRootCheck() {
|
|||
}
|
||||
}
|
||||
|
||||
func runVM(passthroughArg string, fn func(context.Context, *vm.Instance, *vm.FileManager) int) int {
|
||||
func runVM(passthroughArg string, fn func(context.Context, *vm.Instance, *vm.FileManager) int, forwardPorts []vm.PortForwardingConfig) int {
|
||||
doRootCheck()
|
||||
|
||||
var passthroughConfig []vm.USBDevicePassthroughConfig
|
||||
|
|
@ -48,6 +50,10 @@ func runVM(passthroughArg string, fn func(context.Context, *vm.Instance, *vm.Fil
|
|||
var forwardPortsConfig []vm.PortForwardingConfig
|
||||
|
||||
for i, fp := range strings.Split(forwardPortsFlagStr, ",") {
|
||||
if fp == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
fpc, err := vm.ParsePortForwardString(fp)
|
||||
if err != nil {
|
||||
slog.Error("Failed to parse port forward string", "index", i, "value", fp, "error", err)
|
||||
|
|
@ -57,6 +63,8 @@ func runVM(passthroughArg string, fn func(context.Context, *vm.Instance, *vm.Fil
|
|||
forwardPortsConfig = append(forwardPortsConfig, fpc)
|
||||
}
|
||||
|
||||
forwardPortsConfig = append(forwardPortsConfig, forwardPorts...)
|
||||
|
||||
// TODO: Alpine image should be downloaded from somewhere.
|
||||
vi, err := vm.NewInstance(slog.Default().With("caller", "vm"), "alpine-img/alpine.qcow2", passthroughConfig, vmDebugFlag, forwardPortsConfig)
|
||||
if err != nil {
|
||||
|
|
@ -144,3 +152,30 @@ func runVM(passthroughArg string, fn func(context.Context, *vm.Instance, *vm.Fil
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getClosestAvailPort(port uint16) (uint16, error) {
|
||||
for i := port; i < 65535; i++ {
|
||||
ln, err := net.Listen("tcp", ":"+fmt.Sprint(i))
|
||||
if err != nil {
|
||||
if opErr, ok := err.(*net.OpError); ok {
|
||||
if sysErr, ok := opErr.Err.(*os.SyscallError); ok {
|
||||
if sysErr.Err == syscall.EADDRINUSE {
|
||||
// The port is in use.
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0, errors.Wrapf(err, "net listen (port %v)", port)
|
||||
}
|
||||
|
||||
err = ln.Close()
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "close ephemeral listener")
|
||||
}
|
||||
|
||||
return i, nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("no available port found")
|
||||
}
|
||||
|
|
|
|||
2
go.mod
2
go.mod
|
|
@ -4,8 +4,10 @@ go 1.21
|
|||
|
||||
require (
|
||||
github.com/alessio/shellescape v1.4.2
|
||||
github.com/bramvdbogaerde/go-scp v1.2.1
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/sethvargo/go-password v0.2.0
|
||||
github.com/spf13/cobra v1.7.0
|
||||
go.uber.org/multierr v1.11.0
|
||||
golang.org/x/crypto v0.12.0
|
||||
|
|
|
|||
11
go.sum
11
go.sum
|
|
@ -1,5 +1,7 @@
|
|||
github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0=
|
||||
github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
|
||||
github.com/bramvdbogaerde/go-scp v1.2.1 h1:BKTqrqXiQYovrDlfuVFaEGz0r4Ou6EED8L7jCXw6Buw=
|
||||
github.com/bramvdbogaerde/go-scp v1.2.1/go.mod h1:s4ZldBoRAOgUg8IrRP2Urmq5qqd2yPXQTPshACY8vQ0=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
|
@ -12,6 +14,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI=
|
||||
github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE=
|
||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
|
|
@ -20,12 +24,19 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
|
|||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
|
||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
|
|
@ -202,3 +203,84 @@ func (fm *FileManager) Mount(devName string, mo MountOptions) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fm *FileManager) StartSMB(pwd []byte) error {
|
||||
scpClient, err := fm.vi.DialSCP()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "dial scp")
|
||||
}
|
||||
|
||||
defer scpClient.Close()
|
||||
|
||||
sambaCfg := `[global]
|
||||
workgroup = WORKGROUP
|
||||
dos charset = cp866
|
||||
unix charset = utf-8
|
||||
|
||||
[linsk]
|
||||
browseable = yes
|
||||
writeable = yes
|
||||
path = /mnt
|
||||
force user = linsk
|
||||
force group = linsk
|
||||
create mask = 0664`
|
||||
|
||||
err = scpClient.CopyFile(fm.vi.ctx, strings.NewReader(sambaCfg), "/etc/samba/smb.conf", "0400")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "copy samba config file")
|
||||
}
|
||||
|
||||
scpClient.Close()
|
||||
|
||||
sc, err := fm.vi.DialSSH()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "dial ssh")
|
||||
}
|
||||
|
||||
defer func() { _ = sc.Close() }()
|
||||
|
||||
_, err = runSSHCmd(sc, "rc-update add samba && rc-service samba start")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "add and start samba service")
|
||||
}
|
||||
|
||||
sess, err := sc.NewSession()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "create new ssh session")
|
||||
}
|
||||
|
||||
pwd = append(pwd, '\n')
|
||||
|
||||
stderr := bytes.NewBuffer(nil)
|
||||
sess.Stderr = stderr
|
||||
|
||||
stdinPipe, err := sess.StdinPipe()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "stdin pipe")
|
||||
}
|
||||
|
||||
// TODO: Timeout for this command
|
||||
|
||||
err = sess.Start("smbpasswd -a linsk")
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "start change samba password cmd")
|
||||
}
|
||||
|
||||
go func() {
|
||||
_, err = stdinPipe.Write(pwd)
|
||||
if err != nil {
|
||||
fm.vi.logger.Error("Failed to write SMB password to smbpasswd stdin", "error", err)
|
||||
}
|
||||
_, err = stdinPipe.Write(pwd)
|
||||
if err != nil {
|
||||
fm.vi.logger.Error("Failed to write repeated SMB password to smbpasswd stdin", "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
err = sess.Wait()
|
||||
if err != nil {
|
||||
return wrapErrWithLog(err, "wait for change samba password cmd", stderr.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -155,6 +155,8 @@ func runSSHCmd(c *ssh.Client, cmd string) ([]byte, error) {
|
|||
return nil, errors.Wrap(err, "create new vm ssh session")
|
||||
}
|
||||
|
||||
// TODO: Timeouts
|
||||
|
||||
defer func() { _ = sess.Close() }()
|
||||
|
||||
stdout := bytes.NewBuffer(nil)
|
||||
|
|
|
|||
15
vm/vm.go
15
vm/vm.go
|
|
@ -19,6 +19,7 @@ import (
|
|||
"log/slog"
|
||||
|
||||
"github.com/alessio/shellescape"
|
||||
"github.com/bramvdbogaerde/go-scp"
|
||||
"github.com/phayes/freeport"
|
||||
"github.com/pkg/errors"
|
||||
"go.uber.org/multierr"
|
||||
|
|
@ -330,6 +331,20 @@ func (vi *Instance) DialSSH() (*ssh.Client, error) {
|
|||
return ssh.Dial("tcp", "localhost:"+fmt.Sprint(vi.sshMappedPort), vi.sshConf)
|
||||
}
|
||||
|
||||
func (vi *Instance) DialSCP() (*scp.Client, error) {
|
||||
if vi.sshConf == nil {
|
||||
return nil, ErrSSHUnavailable
|
||||
}
|
||||
|
||||
sc := scp.NewClient("localhost:"+fmt.Sprint(vi.sshMappedPort), vi.sshConf)
|
||||
err := sc.Connect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &sc, nil
|
||||
}
|
||||
|
||||
func (vi *Instance) SSHUpNotifyChan() chan struct{} {
|
||||
return vi.sshReadyCh
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue