From 4a78ec12c050dda46a12e241ff82ab95fe2be9d1 Mon Sep 17 00:00:00 2001 From: Son Roy Almerol Date: Sun, 3 Nov 2024 16:50:14 -0500 Subject: [PATCH] add msi installer workflows --- .github/workflows/release.yml | 36 ++++++- .github/workflows/set-wix-guids.yml | 62 ++++++++++++ build/package/windows/wix.json | 37 +++++++ cmd/pbs_windows_agent/agent.go | 150 +++++++++++++++------------- go.mod | 6 +- go.sum | 5 +- internal/agent/sftp/config.go | 93 +++++++++-------- internal/utils/win_env.go | 22 ++++ 8 files changed, 294 insertions(+), 117 deletions(-) create mode 100644 .github/workflows/set-wix-guids.yml create mode 100644 build/package/windows/wix.json create mode 100644 internal/utils/win_env.go diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3508261..45e6b23 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -48,13 +48,13 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - release-windows-amd64-agent: name: release agent windows/amd64 runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: wangyoucao577/go-release-action@v1 + id: go_build with: github_token: ${{ secrets.GITHUB_TOKEN }} goos: windows @@ -63,3 +63,37 @@ jobs: executable_compression: upx binary_name: pbs-d2d-agent project_path: ./cmd/pbs_windows_agent + - uses: actions/upload-artifact@v4 + with: + name: windows-binary + path: ${{steps.go_build.outputs.release_asset_dir}}/pbs-d2d-agent.exe + + release-windows-amd64-agent-installer: + name: release agent installer windows/amd64 + runs-on: windows-latest + needs: release-windows-amd64-agent + steps: + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v4 + with: + name: windows-binary + path: $GITHUB_WORKSPACE/build/package/windows/ + - uses: actions/setup-go@v4 + with: + go-version: '1.23' # You may change this to the latest Go version if needed + - env: + MSI_NAME: "${{ github.event.repository.name }}-${{ github.event.release.tag_name }}-windows.msi" + VERSION: ${{ github.event.release.tag_name[1:] }} + shell: pwsh + run: | + go install github.com/wix-go/go-msi/cmd/go-msi@latest + cd ./build/package/windows + go-msi make --msi "$MSI_NAME" --version "$VERSION" + - name: Publish Release Assets + uses: softprops/action-gh-release@v1 + with: + tag: ${{ github.event.release.tag_name }} + files: ./build/package/windows/*.msi + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + diff --git a/.github/workflows/set-wix-guids.yml b/.github/workflows/set-wix-guids.yml new file mode 100644 index 0000000..1dc75d6 --- /dev/null +++ b/.github/workflows/set-wix-guids.yml @@ -0,0 +1,62 @@ +on: + push: + branches: + - main + pull_request: + branches: + - main + +permissions: + contents: write + +jobs: + check-and-set-guid: + runs-on: windows-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Go environment + uses: actions/setup-go@v4 + with: + go-version: '1.23' # You may change this to the latest Go version if needed + + - name: Check for GUIDs in wix.json + id: check_guid + shell: pwsh + run: | + $jsonFile = "./build/package/windows/wix.json" + $wixJson = Get-Content $jsonFile | ConvertFrom-Json + + # Check if any GUID is empty + $needsGuid = $false + if ([string]::IsNullOrEmpty($wixJson.'upgrade-code') -or + [string]::IsNullOrEmpty($wixJson.files.guid) -or + [string]::IsNullOrEmpty($wixJson.env.guid)) { + $needsGuid = $true + } + + if ($needsGuid) { + echo "GUIDs are missing. Setting GUIDs." + echo "needs_guid=true" | Out-File -FilePath $env:GITHUB_ENV -Append + } else { + echo "All GUIDs are set." + echo "needs_guid=false" | Out-File -FilePath $env:GITHUB_ENV -Append + } + + - name: Install go-msi and set GUIDs + if: env.needs_guid == 'true' + run: | + go install github.com/wix-go/go-msi/cmd/go-msi@latest + go-msi set-guid -p ./build/package/windows/wix.json + + - name: Commit and push changes + if: env.needs_guid == 'true' + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git add ./build/package/windows/wix.json + git commit -m "Set GUIDs in wix.json" + git push origin main + shell: pwsh diff --git a/build/package/windows/wix.json b/build/package/windows/wix.json new file mode 100644 index 0000000..aa10365 --- /dev/null +++ b/build/package/windows/wix.json @@ -0,0 +1,37 @@ +{ + "product": "pbs-d2d-backup", + "company": "snry", + "license": "LICENSE", + "upgrade-code": "", + "files": { + "guid": "", + "items": [ + "pbs-d2d-backup.exe" + ] + }, + "directories": [], + "env": { + "guid": "", + "vars": [ + { + "name": "PATH", + "value": "[INSTALLDIR]", + "permanent": "no", + "system": "no", + "action": "set", + "part": "last" + } + ] + }, + "hooks": [ + {"when": "install", "command": "sc.exe create PBSAgent binPath=\"[INSTALLDIR]pbs-d2d-backup.exe\" type=share start=auto DisplayName=\"PBS Agent\""}, + {"when": "install", "command": "sc.exe start PBSAgent"}, + {"when": "uninstall", "command": "sc.exe delete PBSAgent"} + ], + "choco": { + "description": "Orchestrating backups with Proxmox Backup Server", + "project-url": "https://github.com/sonroyaalmerol/pbs-d2d-backup", + "tags": "backup agent", + "license-url": "https://github.com/sonroyaalmerol/pbs-d2d-backup/LICENSE" + } +} diff --git a/cmd/pbs_windows_agent/agent.go b/cmd/pbs_windows_agent/agent.go index 5b59e0c..8e58e20 100644 --- a/cmd/pbs_windows_agent/agent.go +++ b/cmd/pbs_windows_agent/agent.go @@ -6,7 +6,6 @@ package main import ( "context" _ "embed" - "flag" "fmt" "net/url" "os" @@ -17,6 +16,7 @@ import ( "golang.org/x/sys/windows" "github.com/getlantern/systray" + "github.com/kardianos/service" "github.com/sonroyaalmerol/pbs-d2d-backup/internal/agent/sftp" "github.com/sonroyaalmerol/pbs-d2d-backup/internal/agent/snapshots" "github.com/sonroyaalmerol/pbs-d2d-backup/internal/utils" @@ -25,90 +25,107 @@ import ( //go:embed icon/logo.png var icon []byte +var logger service.Logger + +type AgentProgram struct { + Exec func() +} + +func (p *AgentProgram) Start(s service.Service) error { + go p.Exec() + return nil +} +func (p *AgentProgram) Stop(s service.Service) error { + return nil +} + +var svc service.Service + func main() { - if !isAdmin() { - fmt.Println("This program needs to be run as administrator.") - runAsAdmin() - os.Exit(0) + svcConfig := &service.Config{ + Name: "PBSAgent", + DisplayName: "Proxmox Backup Agent", + Description: "Orchestrating backups with Proxmox Backup Server", } - serverUrl := flag.String("server", "", "Server URL (e.g. https://192.168.1.1:8008)") - flag.Parse() + serverUrl, ok := os.LookupEnv("PBS_AGENT_SERVER") + if !ok { + for { + serverUrl = promptInput("PBS Agent", "Server URL") + + _, err := url.ParseRequestURI(serverUrl) + if err == nil { + serverUrl = strings.TrimSuffix(serverUrl, "/") + break + } + } - if *serverUrl == "" { - promptInput := promptInput("Proxmox Backup Agent", "Server URL") - serverUrl = &promptInput + utils.SetEnvironment("PBS_AGENT_SERVER", serverUrl) } - _, err := url.ParseRequestURI(*serverUrl) + var err error + svc, err = service.New( + &AgentProgram{Exec: run(serverUrl)}, + svcConfig, + ) if err != nil { - showMessageBox("Error", fmt.Sprintf("Invalid server URL: %s", err)) + showMessageBox("Error", err.Error()) os.Exit(1) } - // Reserve port 33450-33476 - drives := utils.GetLocalDrives() - ctx := context.Background() + logger, err = svc.Logger(nil) + if err != nil { + showMessageBox("Error", err.Error()) + os.Exit(1) + } - var wg sync.WaitGroup - for _, driveLetter := range drives { - rune := []rune(driveLetter)[0] + svc.Run() +} - sftpConfig, err := sftp.InitializeSFTPConfig(*serverUrl, driveLetter) +func run(serverUrl string) func() { + return func() { + _, err := url.ParseRequestURI(serverUrl) if err != nil { - showMessageBox("Error", fmt.Sprintf("Unable to initialize SFTP: %s", err)) + showMessageBox("Error", fmt.Sprintf("Invalid server URL: %s", err)) os.Exit(1) } - port, err := utils.DriveLetterPort(rune) - if err != nil { - showMessageBox("Error", fmt.Sprintf("Unable to map letter to port: %s", err)) - os.Exit(1) + // Reserve port 33450-33476 + drives := utils.GetLocalDrives() + ctx := context.Background() + + var wg sync.WaitGroup + for _, driveLetter := range drives { + rune := []rune(driveLetter)[0] + + sftpConfig, err := sftp.InitializeSFTPConfig(serverUrl, driveLetter) + if err != nil { + showMessageBox("Error", fmt.Sprintf("Unable to initialize SFTP: %s", err)) + os.Exit(1) + } + + err = sftpConfig.PopulateKeys() + if err != nil { + showMessageBox("Error", fmt.Sprintf("Unable to populate SFTP keys: %s", err)) + os.Exit(1) + } + + port, err := utils.DriveLetterPort(rune) + if err != nil { + showMessageBox("Error", fmt.Sprintf("Unable to map letter to port: %s", err)) + os.Exit(1) + } + + wg.Add(1) + go sftp.Serve(ctx, &wg, sftpConfig, "0.0.0.0", port, fmt.Sprintf("%s:\\", driveLetter)) } - wg.Add(1) - go sftp.Serve(ctx, &wg, sftpConfig, "0.0.0.0", port, fmt.Sprintf("%s:\\", driveLetter)) - } + defer snapshots.CloseAllSnapshots() - defer snapshots.CloseAllSnapshots() + systray.Run(onReady(serverUrl), onExit) + defer systray.Quit() - systray.Run(onReady(*serverUrl), onExit) - defer systray.Quit() - - wg.Wait() -} - -func isAdmin() bool { - processHandle := windows.CurrentProcess() - - var token windows.Token - err := windows.OpenProcessToken(processHandle, windows.TOKEN_QUERY, &token) - if err != nil { - return false - } - defer token.Close() - - sid, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid) - if err != nil { - return false - } - - isMember, err := token.IsMember(sid) - return err == nil && isMember -} - -func runAsAdmin() { - exe, err := os.Executable() - if err != nil { - showMessageBox("Error", fmt.Sprintf("Failed to get executable path: %s", err)) - return - } - - cmd := exec.Command("runas", "/user:Administrator", exe) - cmd.SysProcAttr = &windows.SysProcAttr{HideWindow: true} - err = cmd.Run() - if err != nil { - showMessageBox("Error", fmt.Sprintf("Failed to run as administrator: %s", err)) + wg.Wait() } } @@ -120,7 +137,6 @@ func showMessageBox(title, message string) { } func promptInput(title, prompt string) string { - // This command uses Windows Script Host (WSH) to display an input box. cmd := exec.Command("powershell", "-Command", fmt.Sprintf(` $wshell = New-Object -ComObject WScript.Shell; $wshell.Popup("%s",0,"%s",1+64); @@ -133,7 +149,6 @@ func promptInput(title, prompt string) string { return "" } - // Convert the output to a string and trim any extraneous whitespace or newlines return strings.TrimSpace(string(output)) } @@ -163,4 +178,5 @@ func onReady(serverUrl string) func() { func onExit() { snapshots.CloseAllSnapshots() + _ = svc.Stop() } diff --git a/go.mod b/go.mod index 0331aa8..cf57389 100644 --- a/go.mod +++ b/go.mod @@ -3,11 +3,13 @@ module github.com/sonroyaalmerol/pbs-d2d-backup go 1.23.0 require ( + github.com/getlantern/systray v1.2.2 + github.com/kardianos/service v1.2.2 github.com/mattn/go-sqlite3 v1.14.24 github.com/mxk/go-vss v1.2.0 github.com/pkg/sftp v1.13.7 - github.com/robfig/cron v1.2.0 golang.org/x/crypto v0.28.0 + golang.org/x/sys v0.26.0 ) require ( @@ -17,10 +19,8 @@ require ( github.com/getlantern/hex v0.0.0-20190417191902-c6586a6fe0b7 // indirect github.com/getlantern/hidden v0.0.0-20190325191715-f02dbb02be55 // indirect github.com/getlantern/ops v0.0.0-20190325191751-d70cb0d6f85f // indirect - github.com/getlantern/systray v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-stack/stack v1.8.0 // indirect github.com/kr/fs v0.1.0 // indirect github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect - golang.org/x/sys v0.26.0 // indirect ) diff --git a/go.sum b/go.sum index 23dc384..285dbab 100644 --- a/go.sum +++ b/go.sum @@ -19,6 +19,8 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX60= +github.com/kardianos/service v1.2.2/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= @@ -33,8 +35,6 @@ github.com/pkg/sftp v1.13.7 h1:uv+I3nNJvlKZIQGSr8JVQLNHFU9YhhNpvC14Y6KgmSM= github.com/pkg/sftp v1.13.7/go.mod h1:KMKI0t3T6hfA+lTR/ssZdunHo+uwq7ghoN09/FSu3DY= 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/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= -github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -60,6 +60,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/internal/agent/sftp/config.go b/internal/agent/sftp/config.go index 68ffadf..403dbe0 100644 --- a/internal/agent/sftp/config.go +++ b/internal/agent/sftp/config.go @@ -29,38 +29,52 @@ type SFTPConfig struct { BasePath string `json:"base_path"` } -func InitializeSFTPConfig(server string, basePath string) (*SFTPConfig, error) { - var newSftpConfig SFTPConfig - - newSftpConfig.BasePath = basePath - newSftpConfig.Server = server +func InitializeSFTPConfig(serverUrl string, driveLetter string) (*SFTPConfig, error) { + newSftpConfig := SFTPConfig{ + BasePath: driveLetter, + Server: serverUrl, + } configDir, err := os.UserConfigDir() if err != nil { - return nil, fmt.Errorf("InitializeSFTPConfig: failed to get user config directory -> %w", err) + return &newSftpConfig, fmt.Errorf("ReadFileConfig: failed to get user config directory -> %w", err) } configBasePath := filepath.Join(configDir, "proxmox-agent") err = os.MkdirAll(configBasePath, 0700) if err != nil { - return nil, fmt.Errorf("InitializeSFTPConfig: failed to create proxmox-agent directory -> %w", err) + return &newSftpConfig, fmt.Errorf("ReadFileConfig: failed to create proxmox-agent directory -> %w", err) } - savedConfigPath := filepath.Join(configBasePath, fmt.Sprintf("%s-sftp.json", basePath)) + savedConfigPath := filepath.Join(configBasePath, fmt.Sprintf("%s-sftp.json", driveLetter)) jsonFile, err := os.Open(savedConfigPath) - if err == nil { - byteContent, err := io.ReadAll(jsonFile) - if err == nil { - err = json.Unmarshal(byteContent, &newSftpConfig) - if err == nil { - log.Printf("Using existing config: %s\n", savedConfigPath) - } else { - log.Println(err) - } - } + if err != nil { + return &newSftpConfig, fmt.Errorf("ReadFileConfig: failed to open json file -> %w", err) + } + defer jsonFile.Close() + + byteContent, err := io.ReadAll(jsonFile) + if err != nil { + return &newSftpConfig, fmt.Errorf("ReadFileConfig: failed to read json file content -> %w", err) + } + + var existingConfig SFTPConfig + err = json.Unmarshal(byteContent, &existingConfig) + if err != nil { + return &newSftpConfig, fmt.Errorf("ReadFileConfig: invalid json file (%s) -> %w", string(byteContent), err) + } + + return &existingConfig, nil +} + +func (config *SFTPConfig) PopulateKeys() error { + configDir, err := os.UserConfigDir() + if err != nil { + return fmt.Errorf("ReadFileConfig: failed to get user config directory -> %w", err) } - jsonFile.Close() + + configBasePath := filepath.Join(configDir, "proxmox-agent") configSSH := &ssh.ServerConfig{ NoClientAuth: false, @@ -70,55 +84,46 @@ func InitializeSFTPConfig(server string, basePath string) (*SFTPConfig, error) { }, } - var privateKey, pubKey []byte - var serverKey *string - - if len(newSftpConfig.PrivateKey) == 0 || len(newSftpConfig.PublicKey) == 0 || len(newSftpConfig.ServerKey) == 0 { - privateKey, pubKey, err = utils.GenerateKeyPair(4096) + if len(config.PrivateKey) == 0 || len(config.PublicKey) == 0 || len(config.ServerKey) == 0 { + privateKey, pubKey, err := utils.GenerateKeyPair(4096) if err != nil { - return nil, fmt.Errorf("InitializeSFTPConfig: failed to generate SSH key pair -> %w", err) + return fmt.Errorf("InitializeSFTPConfig: failed to generate SSH key pair -> %w", err) } - newSftpConfig.PrivateKey = privateKey - newSftpConfig.PublicKey = pubKey + config.PrivateKey = privateKey + config.PublicKey = pubKey - serverKey, err = getServerPublicKey(server, string(pubKey), basePath) + serverKey, err := getServerPublicKey(config.Server, string(pubKey), config.BasePath) if err != nil { - return nil, fmt.Errorf("InitializeSFTPConfig: failed to get server public ssh key -> %w", err) + return fmt.Errorf("InitializeSFTPConfig: failed to get server public ssh key -> %w", err) } - newSftpConfig.ServerKey = []byte(*serverKey) + config.ServerKey = []byte(*serverKey) - knownHosts, err := os.OpenFile(filepath.Join(configBasePath, fmt.Sprintf("%s-sftp.json", basePath)), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) + knownHosts, err := os.OpenFile(filepath.Join(configBasePath, fmt.Sprintf("%s-sftp.json", config.BasePath)), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { log.Println(err) } defer knownHosts.Close() - jsonContent, err := json.Marshal(newSftpConfig) + jsonContent, err := json.Marshal(config) if err == nil { if _, err = knownHosts.Write(jsonContent); err != nil { log.Println(err) } } - } else { - privateKey = newSftpConfig.PrivateKey - pubKey = newSftpConfig.PublicKey - serverKeyStr := string(newSftpConfig.ServerKey) - - serverKey = &serverKeyStr } - parsedSigner, err := ssh.ParsePrivateKey(privateKey) + parsedSigner, err := ssh.ParsePrivateKey(config.PrivateKey) if err != nil { - return nil, fmt.Errorf("InitializeSFTPConfig: failed to parse private key to signer -> %w", err) + return fmt.Errorf("InitializeSFTPConfig: failed to parse private key to signer -> %w", err) } configSSH.AddHostKey(parsedSigner) - parsedServerKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(*serverKey)) + parsedServerKey, _, _, _, err := ssh.ParseAuthorizedKey(config.ServerKey) if err != nil { - return nil, fmt.Errorf("InitializeSFTPConfig: failed to parse server key -> %w", err) + return fmt.Errorf("InitializeSFTPConfig: failed to parse server key -> %w", err) } configSSH.PublicKeyCallback = func(conn ssh.ConnMetadata, auth ssh.PublicKey) (*ssh.Permissions, error) { @@ -132,9 +137,9 @@ func InitializeSFTPConfig(server string, basePath string) (*SFTPConfig, error) { return nil, fmt.Errorf("InitializeSFTPConfig: unknown public key for %s -> %w", conn.RemoteAddr().String(), err) } - newSftpConfig.ServerConfig = configSSH + config.ServerConfig = configSSH - return &newSftpConfig, nil + return nil } func logAuthAttempt(conn ssh.ConnMetadata, _ string, err error) { diff --git a/internal/utils/win_env.go b/internal/utils/win_env.go new file mode 100644 index 0000000..17b3ba6 --- /dev/null +++ b/internal/utils/win_env.go @@ -0,0 +1,22 @@ +//go:build windows + +package utils + +import ( + "golang.org/x/sys/windows/registry" +) + +func SetEnvironment(key string, value string) error { + k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SYSTEM\ControlSet001\Control\Session Manager\Environment`, registry.ALL_ACCESS) + if err != nil { + return err + } + defer k.Close() + + err = k.SetStringValue(key, value) + if err != nil { + return err + } + + return nil +}