Initial AFP support implementation

This commit is contained in:
AlexSSD7 2023-09-01 15:15:40 +01:00
commit 7ba79ff803
7 changed files with 100 additions and 15 deletions

View file

@ -72,17 +72,19 @@ var runCmd = &cobra.Command{
return 1
}
lg := slog.With("backend", shareBackendFlag)
shareURI, err := backend.Apply(ctx, sharePWD, &share.VMShareContext{
Instance: i,
FileManager: fm,
NetTapCtx: tapCtx,
})
if err != nil {
slog.Error("Failed to apply (start) file share backend", "backend", shareBackendFlag, "error", err.Error())
lg.Error("Failed to apply (start) file share backend", "error", err.Error())
return 1
}
slog.Info("Started the network share successfully", "type", "ftp")
lg.Info("Started the network share successfully")
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: "+strings.ToUpper(shareBackendFlag)+"\nURL: %v\nUsername: linsk\nPassword: %v\n===========================\n", shareURI, sharePWD)

View file

@ -157,8 +157,7 @@ func (bc *BuildContext) BuildWithInterruptHandler() error {
bc.logger.Info("VM OS installation in progress")
// TODO: Compile select features only.
err = runAlpineSetupCmd(sc, []string{"openssh", "lvm2", "util-linux", "cryptsetup", "vsftpd", "samba"})
err = runAlpineSetupCmd(sc, []string{"openssh", "lvm2", "util-linux", "cryptsetup", "vsftpd", "samba", "netatalk"})
if err != nil {
return errors.Wrap(err, "run alpine setup cmd")
}

42
share/afp.go Normal file
View file

@ -0,0 +1,42 @@
package share
import (
"context"
"fmt"
"net"
"github.com/AlexSSD7/linsk/vm"
"github.com/pkg/errors"
)
type AFPBackend struct {
listenIP net.IP
sharePort uint16
}
func NewAFPBackend(uc *UserConfiguration) (Backend, *VMShareOptions, error) {
sharePort, err := getNetworkSharePort(0)
if err != nil {
return nil, nil, errors.Wrap(err, "get network share port")
}
return &AFPBackend{
listenIP: uc.listenIP,
sharePort: sharePort,
}, &VMShareOptions{
Ports: []vm.PortForwardingRule{{
HostIP: uc.listenIP,
HostPort: sharePort,
VMPort: 548,
}},
}, nil
}
func (b *AFPBackend) Apply(ctx context.Context, sharePWD string, vc *VMShareContext) (string, error) {
err := vc.FileManager.StartAFP(sharePWD)
if err != nil {
return "", errors.Wrap(err, "start afp server")
}
return "afp://" + net.JoinHostPort(b.listenIP.String(), fmt.Sprint(b.sharePort)) + "/linsk", nil
}

View file

@ -11,6 +11,7 @@ type Backend interface {
var backends = map[string]NewBackendFunc{
"ftp": NewFTPBackend,
"smb": NewSMBBackend,
"afp": NewAFPBackend,
}
// Will return nil if no backend is found.

View file

@ -5,8 +5,6 @@ import (
"fmt"
"net"
"log/slog"
"github.com/AlexSSD7/linsk/vm"
"github.com/pkg/errors"
)
@ -61,7 +59,5 @@ func (b *FTPBackend) Apply(ctx context.Context, sharePWD string, vc *VMShareCont
return "", errors.Wrap(err, "start ftp server")
}
slog.Info("Started the network share successfully", "type", "ftp")
return "ftp://" + b.extIP.String() + ":" + fmt.Sprint(b.sharePort), nil
}

View file

@ -7,16 +7,12 @@ import (
"runtime"
"strings"
"log/slog"
"github.com/AlexSSD7/linsk/vm"
"github.com/pkg/errors"
)
const smbPort = 445
// TODO: Test SMB backend on macOS.
type SMBBackend struct {
listenIP net.IP
sharePort *uint16
@ -63,8 +59,6 @@ func (b *SMBBackend) Apply(ctx context.Context, sharePWD string, vc *VMShareCont
return "", errors.Wrap(err, "start smb server")
}
slog.Info("Started the network share successfully", "type", "smb", "ext", vc.NetTapCtx != nil)
var shareURL string
if b.sharePort != nil {
shareURL = "smb://" + net.JoinHostPort(b.listenIP.String(), fmt.Sprint(*b.sharePort)) + "/linsk"

View file

@ -284,7 +284,8 @@ writeable = yes
path = /mnt
force user = linsk
force group = linsk
create mask = 0664`
create mask = 0664
`
err = scpClient.CopyFile(scpCtx, strings.NewReader(sambaCfg), "/etc/samba/smb.conf", "0400")
if err != nil {
@ -312,3 +313,53 @@ create mask = 0664`
return nil
}
func (fm *FileManager) StartAFP(pwd string) error {
// This timeout is for the SCP client exclusively.
scpCtx, scpCtxCancel := context.WithTimeout(fm.vm.ctx, time.Second*5)
defer scpCtxCancel()
scpClient, err := fm.vm.DialSCP()
if err != nil {
return errors.Wrap(err, "dial scp")
}
defer scpClient.Close()
afpCfg := `[Global]
[linsk]
path = /mnt
file perm = 0664
directory perm = 0775
valid users = linsk
force user = linsk
force group = linsk
`
err = scpClient.CopyFile(scpCtx, strings.NewReader(afpCfg), "/etc/afp.conf", "0400")
if err != nil {
return errors.Wrap(err, "copy netatalk config file")
}
scpClient.Close()
sc, err := fm.vm.DialSSH()
if err != nil {
return errors.Wrap(err, "dial ssh")
}
defer func() { _ = sc.Close() }()
_, err = sshutil.RunSSHCmd(fm.vm.ctx, sc, "rc-update add netatalk && rc-service netatalk start")
if err != nil {
return errors.Wrap(err, "add and start netatalk service")
}
err = sshutil.ChangeUnixPass(fm.vm.ctx, sc, "linsk", pwd)
if err != nil {
return errors.Wrap(err, "change unix pass")
}
return nil
}