Hugo fails to "go get" private module

Hi

A theme module I use is stored on my SSH-only, private Git server. When I try to build the site or use hugo mod get, I get the following error:

failed to execute binary “go” with args [mod download -modcacherw]: go: git.xxx/theme.git: no secure protocol found for repository

When I execute go get git.xxx/theme.git by hand, Go successfully pulls this into my Gocache.

In my config.yaml I have:

module:
  private: "git.xxx/*"
  proxy: "direct"

The go.mod in the Hugo directory has

require git.xxx/theme.git v0.xxxx

The go.mod of the theme is

module git.xxx/theme.git

go 1.24.0

My ~/.gitconfig has the following (which was required to get go get to work)

[url "ssh://git@git.xxx/"]
    insteadOf = https://git.xxx/

I tried setting the GOPRIVATE and GOPROXY env variables by hand and this did not work.

Any ideas on how to fix this?

This is probably not relevant, but I’d like to cross if off the list…

In the past we had some trouble with private repositories if Hugo didn’t have access to certain environment variables, but that was fixed with 6c9ea02.

Let’s check your config:

Please type this and paste the output: hugo config | grep security.exec -A2

You can allow Hugo to access all environment variables by placing this in your site config:

[security.exec]
osenv = ['.*']

It’s worth a quick test.

The output:

  [security.exec]
    allow = ['^(dart-)?sass(-embedded)?$', '^go$', '^git$', '^npx$', '^postcss$', '^tailwindcss$']
    osenv = ['(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG|SYSTEMDRIVE)$']

That actually did the trick! I added the following to my yaml file

security:
  exec:
    osenv: '.*'

Just like that, hugo mod get works.

So, because you said “probably not relevant”, is there some way I can provide you with debug information if you think this is a bug in the codebase? Even if my hugo file can only be edited by me, I’d still rather not have that security exception in my config.

I’m running hugo v0.149.0-66240338f1b908ca3b163384c8229943e74eb290+extended windows/amd64 BuildDate=2025-08-27T15:37:16Z VendorInfo=gohugoio.

I said that because I thought we had caught all the required env vars in 6c9ea02.

If we knew which env vars are being blocked on your (Windows) system, we could add them to the allowlist.

Also, are you running the build command from a Windows console (which one?), or from Git Bash, or something else?

Try placing this in your site config:

[security.exec]
osenv = ['(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG|SYSTEMDRIVE|SSH_\w+)$']

The last entry allows every env var that begins with SSH_.

Normally I’m running hugo from PowerShell v7. Just to test, it also fails without the exception on PowerShell v2 and the classic cmd.

When I run go env I get:

...
set GOPRIVATE=
set GOPROXY=https://proxy.golang.org,direct
...

Here are (almost) all env variables of the shell. I stripped some software-specific ones.

ALLUSERSPROFILE                C:\ProgramData
APPDATA                        C:\Users\xxx\AppData\Roaming
ChocolateyInstall              C:\ProgramData\chocolatey
ChocolateyLastPathUpdate       133999452591408682
CommonProgramFiles             C:\Program Files\Common Files
CommonProgramFiles(x86)        C:\Program Files (x86)\Common Files
CommonProgramW6432             C:\Program Files\Common Files
ComSpec                        C:\WINDOWS\system32\cmd.exe
DOTNET_CLI_TELEMETRY_OPTOUT    1
DOTNET_TELEMETRY_OPTOUT        1
DriverData                     C:\Windows\System32\Drivers\DriverData
FONTCONFIG_FILE                C:\Windows\fonts.conf
FPS_BROWSER_APP_PROFILE_STRING Internet Explorer
FPS_BROWSER_USER_PROFILE_STRI… Default
GIT_SSH                        C:\Program Files\OpenSSH-Win64\ssh.exe
GOPATH                         D:\go\
HOMEDRIVE                      C:
HOMEPATH                       \Users\xxx
LOCALAPPDATA                   C:\Users\xxx\AppData\Local
LOGONSERVER                    \\yyy
NUMBER_OF_PROCESSORS           16
OS                             Windows_NT
PATHEXT                        .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW;.CPL
POWERSHELL_CLI_TELEMETRY_OPTO… 1
POWERSHELL_DISTRIBUTION_CHANN… MSI:Windows 10 Home
POWERSHELL_TELEMETRY_OPTOUT    1
PROCESSOR_ARCHITECTURE         AMD64
PROCESSOR_IDENTIFIER           AMD64 Family 23 Model 113 Stepping 0, AuthenticAMD
PROCESSOR_LEVEL                23
PROCESSOR_REVISION             7100
ProgramData                    C:\ProgramData
ProgramFiles                   C:\Program Files
ProgramFiles(x86)              C:\Program Files (x86)
ProgramW6432                   C:\Program Files
PUBLIC                         C:\Users\Public
SESSIONNAME                    Console
SystemDrive                    C:
SystemRoot                     C:\WINDOWS
TEMP                           C:\Users\xxx\AppData\Local\Temp
TMP                            C:\Users\xxx\AppData\Local\Temp
USERDOMAIN                     yyy
USERDOMAIN_ROAMINGPROFILE      yyy
USERNAME                       xxx
USERPROFILE                    C:\Users\xxx
VBOX_MSI_INSTALL_PATH          C:\Program Files\Oracle\VirtualBox\
VIRTUAL_ENV_DISABLE_PROMPT     1
windir                         C:\WINDOWS

Does not fix the issue. As you can see from the env variables, I do not have any set which begin with “SSH_”.

I’d start by adding this to the allowlist: GIT_SSH

Then add others one at a time to see if you can figure it out.

Quickly scripted this and voila: PROGRAMDATA was the missing one.

security:
  exec:
    osenv: '(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG|SYSTEMDRIVE|PROGRAMDATA)$'

Thanks for your help!

Thank you very much for going through that exercise. I will make sure this gets added to our default allow list.

In case it may be helpful in the future, here is the very basic Python script I used to brute-force the variable.

First, add the exception into your config: (reformat if your config is not YAML)

security:
  exec:
    osenv: '(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+|(XDG_CONFIG_)?HOME|USERPROFILE|SSH_AUTH_SOCK|DISPLAY|LANG|SYSTEMDRIVE|REPLACEME)$'

Save this into the hugo directory:

import os
import subprocess
import shutil
from sys import argv

cfg_name = argv[1]
git_url = argv[2]

def try_var(name):
    shutil.copy(cfg_name, f"{cfg_name}.bak")
    try:
        with open(cfg_name) as f:
            cfg = f.read()

        cfg = cfg.replace("REPLACEME", name)

        with open(cfg_name, "w") as f:
            f.write(cfg)

        run = subprocess.run(
            ["hugo", "mod", "get", git_url],
            capture_output=True,
            text=True
        )

        return run.returncode == 0

    finally:
        shutil.move(f"{cfg_name}.bak", cfg_name)

if __name__ == "__main__":
    vars = list(os.environ.keys())

    for v in vars:
        print(f"Trying {v}")
        if try_var(v):
            print(f"\n{v} works!")
            exit()

    print("Nothing worked :(")

Depending on your OS, you might need to change “hugo” to “hugo.exe” or change it to an absolute path.

Run it with python script.py <hugo config file name> <git url of private repo>
For example: python script.py config.yaml git.myserver.tld/myuser/myrepo.git

See https://github.com/gohugoio/hugo/pull/13965.

1 Like

Thanks for your fast action!

Upon a little inspection, this might be Chocolatey related, as this package manager installs many things to “C:\ProgramData\chocolatey\bin”.

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.