{"record":{"id":"805bdaea6bb0c751","repo":"kubernetes/kops","slug":"digitalocean-access-token-is-required-805bda","errorCode":null,"errorMessage":"DIGITALOCEAN_ACCESS_TOKEN is required","messagePattern":"DIGITALOCEAN_ACCESS_TOKEN is required","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"upup/pkg/fi/cloudup/do/verifier.go","lineNumber":48,"sourceCode":"\t\"golang.org/x/oauth2\"\n\t\"k8s.io/kops/pkg/bootstrap\"\n\t\"k8s.io/kops/pkg/wellknownports\"\n\t\"k8s.io/kops/upup/pkg/fi/cloudup/do/dometadata\"\n)\n\ntype DigitalOceanVerifierOptions struct {\n}\n\ntype digitalOceanVerifier struct {\n\tdoClient *godo.Client\n}\n\nvar _ bootstrap.Verifier = (*digitalOceanVerifier)(nil)\n\nfunc NewVerifier(ctx context.Context, opt *DigitalOceanVerifierOptions) (bootstrap.Verifier, error) {\n\taccessToken := os.Getenv(\"DIGITALOCEAN_ACCESS_TOKEN\")\n\tif accessToken == \"\" {\n\t\treturn nil, errors.New(\"DIGITALOCEAN_ACCESS_TOKEN is required\")\n\t}\n\n\ttokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: accessToken})\n\tdoClient := godo.NewClient(oauth2.NewClient(ctx, tokenSource))\n\n\treturn &digitalOceanVerifier{\n\t\tdoClient: doClient,\n\t}, nil\n}\n\nfunc (o digitalOceanVerifier) VerifyToken(ctx context.Context, rawRequest *http.Request, token string, body []byte) (*bootstrap.VerifyResult, error) {\n\tif !strings.HasPrefix(token, dometadata.DOAuthenticationTokenPrefix) {\n\t\treturn nil, bootstrap.ErrNotThisVerifier\n\t}\n\tserverIDString := strings.TrimPrefix(token, dometadata.DOAuthenticationTokenPrefix)\n\n\tserverID, err := strconv.Atoi(serverIDString)\n\tif err != nil {","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/kubernetes/kops/blob/4c8573c808a73d578c5eadc86d410646ea0b0d73/upup/pkg/fi/cloudup/do/verifier.go#L30-L66","documentation":"The DigitalOcean bootstrap verifier authenticates incoming kubelet requests against the DO API. NewVerifier reads DIGITALOCEAN_ACCESS_TOKEN from the environment and returns this identical error when it is unset/empty, before any API call is made.","triggerScenarios":"Running a kops-built DigitalOcean verifier binary (used by nodeup/bootstrap on new nodes) in an environment without DIGITALOCEAN_ACCESS_TOKEN set — the main() entry point calls NewVerifier which fails immediately.","commonSituations":"Bootstrapping new DO nodes where the verifier was run manually without env; container images missing the env var; secrets not mounted into the verifier's runtime environment.","solutions":["Export DIGITALOCEAN_ACCESS_TOKEN in the environment where the verifier runs","Ensure the node bootstrap/manifest passes the token env var into the verifier process","Verify with printenv DIGITALOCEAN_ACCESS_TOKEN that it is non-empty in the target runtime (container/systemd unit)","Use a DO token scoped to read droplets metadata for the verifier"],"exampleFix":"// systemd unit before\nExecStart=/usr/local/bin/do-verifier\n// after\nEnvironment=DIGITALOCEAN_ACCESS_TOKEN=dop_v1_xxxx\nExecStart=/usr/local/bin/do-verifier","handlingStrategy":"validation","validationCode":"if os.Getenv(\"DIGITALOCEAN_ACCESS_TOKEN\") == \"\" {\n\treturn errors.New(\"verifier requires DIGITALOCEAN_ACCESS_TOKEN in its runtime environment\")\n}","typeGuard":null,"tryCatchPattern":"v, err := do.NewVerifier(ctx, opts)\nif err != nil {\n\tif err.Error() == \"DIGITALOCEAN_ACCESS_TOKEN is required\" {\n\t\t// inject env var into the verifier's runtime (systemd/container)\n\t} else { return err }\n}","preventionTips":["Ensure node bootstrap images/units propagate the token env to the verifier","For containers, pass -e DIGITALOCEAN_ACCESS_TOKEN at runtime","Smoke-test the verifier binary with printenv before production bootstrap"],"tags":["digitalocean","authentication","env-var","verifier"],"backgroundTag":"missing-env-var","analyzedSha":"4c8573c808a73d578c5eadc86d410646ea0b0d73","analyzedAt":"2026-09-05T04:13:19.212Z","contentChangedAt":"2026-09-05T04:13:19.212Z","schemaVersion":2},"datasetVersion":"2026-09-12T07:17:12.445Z"}