Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 34 additions & 6 deletions .github/workflows/govulncheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,39 @@ jobs:
# Fails on ANY vulnerable module version (deps + stdlib for the CI toolchain),
# regardless of reachability — matches external (Trivy-style) scanners.
#
# ALLOWLIST holds advisories we knowingly accept: each must have no fixed
# version available AND not be present in our build graph (unreachable).
# Everything else — including any future fixable advisory — still fails.
# DEP_ALLOWLIST holds dependency advisories we knowingly accept: each must
# have no fixed version available AND not be present in our build graph
# (unreachable). Everything else still fails.
# - GO-2026-5932: golang.org/x/crypto/openpgp is unmaintained (Fixed in: N/A).
# We do not import openpgp anywhere; verify with:
# go list -deps ./... | grep openpgp # (expect no output)
#
# STDLIB_ALLOWLIST holds standard-library advisories whose only remedy is a
# toolchain bump. Every workflow here — including the release build — pins
# 1.25.12, so moving off it is its own change and is deliberately deferred.
# Listing the IDs rather than skipping stdlib wholesale keeps the gate
# honest: a newly published stdlib advisory still fails until someone
# decides to accept or fix it.
- name: Run govulncheck (module versions)
env:
CGO_ENABLED: "0"
run: |
ALLOWLIST="GO-2026-5932"
DEP_ALLOWLIST="GO-2026-5932"

# Fixed in stdlib@go1.25.13, except GO-2026-5942 (stdlib@go1.26.6).
# Delete these once the pinned toolchain covers them.
STDLIB_ALLOWLIST="
Comment thread
bernie-g marked this conversation as resolved.
GO-2026-5026
GO-2026-5942
GO-2026-5972
GO-2026-6088
GO-2026-6089
GO-2026-6090
GO-2026-6091
GO-2026-6218
"

ALLOWLIST="$DEP_ALLOWLIST $STDLIB_ALLOWLIST"

# Human-readable report (informational; govulncheck exits 3 when it
# finds anything, so don't let that fail the step on its own).
Expand All @@ -48,15 +70,21 @@ jobs:
# Machine-readable gate: fail only on advisories not in the allowlist.
govulncheck -scan module -format json > govulncheck.json || true
findings="$(jq -r 'select(.finding != null) | .finding.osv' govulncheck.json | sort -u)"
allowed="$(tr ' ' '\n' <<< "$ALLOWLIST" | grep -v '^$' | sort -u)"

unexpected=""
for id in $findings; do
if ! grep -qxF "$id" <<< "$(tr ' ' '\n' <<< "$ALLOWLIST")"; then
if ! grep -qxF "$id" <<< "$allowed"; then
unexpected="$unexpected $id"
fi
done

echo "=== Allowlisted (no fix available, not in build graph): $ALLOWLIST ==="
echo "=== Allowlisted: $(tr '\n' ' ' <<< "$allowed")==="

stale="$(comm -23 <(echo "$allowed") <(echo "$findings"))"
if [ -n "$stale" ]; then
echo "::warning::Allowlist entries no longer reported, drop them: $(tr '\n' ' ' <<< "$stale")"
fi

if [ -n "${unexpected// /}" ]; then
echo "::error::Non-allowlisted vulnerabilities found:$unexpected"
Expand Down
2 changes: 1 addition & 1 deletion e2e/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/indece-official/go-ebcdic v1.2.0 // indirect
github.com/infisical/go-sdk v0.7.0 // indirect
github.com/infisical/infisical-kmip v0.3.19 // indirect
github.com/infisical/infisical-kmip v0.3.20 // indirect
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
Expand Down
4 changes: 2 additions & 2 deletions e2e/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -591,8 +591,8 @@ github.com/indece-official/go-ebcdic v1.2.0 h1:nKCubkNoXrGvBp3MSYuplOQnhANCDEY51
github.com/indece-official/go-ebcdic v1.2.0/go.mod h1:RBddVJt0Ks0eDLRG5dhPwBDRiTNA7n+yv0dVFpSs46Q=
github.com/infisical/go-sdk v0.7.0 h1:x9/1PczL+ioVD1jCp4LHQzPpBawatbmkjAC0S1OAtUA=
github.com/infisical/go-sdk v0.7.0/go.mod h1:yEfXF+3YDDXiJ9zzJUSzW6me6XXPPEDK52fSU6JfpCA=
github.com/infisical/infisical-kmip v0.3.19 h1:JDndxhM9+GoHTqSOX47H3KIxIuDmrEHsq17wvhw3RL8=
github.com/infisical/infisical-kmip v0.3.19/go.mod h1:bO1M4YtKyutNg1bREPmlyZspC5duSR7hyQ3lPmLzrIs=
github.com/infisical/infisical-kmip v0.3.20 h1:FAc/SK90qgSyQg24+gpkudmGmGT5+75/1o3EcsEewtQ=
github.com/infisical/infisical-kmip v0.3.20/go.mod h1:bO1M4YtKyutNg1bREPmlyZspC5duSR7hyQ3lPmLzrIs=
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf h1:FtEj8sfIcaaBfAKrE1Cwb61YDtYq9JxChK1c7AKce7s=
github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf/go.mod h1:yrqSXGoD/4EKfF26AOGzscPOgTTJcyAwM2rpixWT+t4=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ require (
github.com/google/uuid v1.6.0
github.com/h2non/filetype v1.1.3
github.com/infisical/go-sdk v0.7.0
github.com/infisical/infisical-kmip v0.3.19
github.com/infisical/infisical-kmip v0.3.20
github.com/jackc/pgx/v5 v5.9.2
github.com/jcmturner/gokrb5/v8 v8.4.4
github.com/masterzen/winrm v0.0.0-20260407182533-5570be7f80cf
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -416,8 +416,8 @@ github.com/indece-official/go-ebcdic v1.2.0 h1:nKCubkNoXrGvBp3MSYuplOQnhANCDEY51
github.com/indece-official/go-ebcdic v1.2.0/go.mod h1:RBddVJt0Ks0eDLRG5dhPwBDRiTNA7n+yv0dVFpSs46Q=
github.com/infisical/go-sdk v0.7.0 h1:x9/1PczL+ioVD1jCp4LHQzPpBawatbmkjAC0S1OAtUA=
github.com/infisical/go-sdk v0.7.0/go.mod h1:yEfXF+3YDDXiJ9zzJUSzW6me6XXPPEDK52fSU6JfpCA=
github.com/infisical/infisical-kmip v0.3.19 h1:JDndxhM9+GoHTqSOX47H3KIxIuDmrEHsq17wvhw3RL8=
github.com/infisical/infisical-kmip v0.3.19/go.mod h1:bO1M4YtKyutNg1bREPmlyZspC5duSR7hyQ3lPmLzrIs=
github.com/infisical/infisical-kmip v0.3.20 h1:FAc/SK90qgSyQg24+gpkudmGmGT5+75/1o3EcsEewtQ=
github.com/infisical/infisical-kmip v0.3.20/go.mod h1:bO1M4YtKyutNg1bREPmlyZspC5duSR7hyQ3lPmLzrIs=
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
Expand Down
53 changes: 47 additions & 6 deletions packages/cmd/kmip.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ Copyright (c) 2023 Infisical Inc.
package cmd

import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"runtime"
"time"

"github.com/Infisical/infisical-merge/packages/api"
"github.com/Infisical/infisical-merge/packages/config"
Expand Down Expand Up @@ -128,7 +130,7 @@ func startKmipServer(cmd *cobra.Command, args []string) {

isResourceAuth := enrollMethod == localkmip.EnrollMethodToken || enrollMethod == localkmip.EnrollMethodAws
if isResourceAuth {
serverConfig.AccessToken = enrollKmipServer(cmd, enrollMethod, serverName)
serverConfig.AccessToken, serverConfig.RefreshAccessToken = enrollKmipServer(cmd, enrollMethod, serverName)
} else {
// No enroll method given: reuse the stored enrollment access token unless explicit identity
// credentials were passed. This keeps a manual restart of an enrolled server from silently
Expand All @@ -138,6 +140,9 @@ func startKmipServer(cmd *cobra.Command, args []string) {
if storedToken, _ := localkmip.LoadStoredAccessToken(serverName); storedToken != "" {
log.Info().Msg("Using stored KMIP server access token")
serverConfig.AccessToken = storedToken
if serverID, canRefresh := localkmip.ResolveAwsRefreshServerID(serverName); canRefresh {
serverConfig.RefreshAccessToken = newAwsRefreshAccessTokenFunc(serverName, serverID)
}
}
}
if serverConfig.AccessToken == "" {
Expand All @@ -148,9 +153,38 @@ func startKmipServer(cmd *cobra.Command, args []string) {
kmip.StartServer(serverConfig)
}

// newAwsRefreshAccessTokenFunc returns a function that re-authenticates the KMIP server via
// AWS STS and persists the new access token, used by the renewal loop when the current token
// is rejected. Bounded timeouts so a hung API call cannot stall renewal indefinitely.
func newAwsRefreshAccessTokenFunc(serverName, kmipServerID string) func() (string, error) {
return func() (string, error) {
refreshClient, err := util.GetRestyClientWithCustomHeaders()
if err != nil {
return "", fmt.Errorf("unable to create HTTP client: %w", err)
}
refreshClient.SetTimeout(30 * time.Second)

ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()

newToken, err := localkmip.LoginKmipServerWithAws(ctx, refreshClient, kmipServerID)
if err != nil {
return "", fmt.Errorf("AWS Auth re-login failed: %w", err)
}

if err := localkmip.SaveAccessToken(serverName, newToken); err != nil {
log.Warn().Msgf("failed to persist refreshed KMIP access token: %v", err)
}

return newToken, nil
}
}

// enrollKmipServer obtains a KMIP server access token via token or AWS enrollment,
// persisting the relevant state under the KMIP server's config file.
func enrollKmipServer(cmd *cobra.Command, enrollMethod, serverName string) string {
// persisting the relevant state under the KMIP server's config file. For AWS enrollment it
// also returns a refresh function so the server can re-authenticate when its access token
// expires before its certificate does; token enrollment is single-use, so no refresh exists.
func enrollKmipServer(cmd *cobra.Command, enrollMethod, serverName string) (string, func() (string, error)) {
httpClient, err := util.GetRestyClientWithCustomHeaders()
if err != nil {
util.HandleError(err, "unable to create HTTP client")
Expand Down Expand Up @@ -178,12 +212,16 @@ func enrollKmipServer(cmd *cobra.Command, enrollMethod, serverName string) strin
if err := localkmip.SaveServerID(serverName, kmipServerID); err != nil {
util.HandleError(err, "failed to save KMIP server id to config")
}
if err := localkmip.SaveEnrollMethod(serverName, localkmip.EnrollMethodAws); err != nil {
util.HandleError(err, "failed to save KMIP enroll method to config")
}
if err := localkmip.SaveDomain(serverName, config.INFISICAL_URL); err != nil {
util.HandleError(err, "failed to save domain to config")
}

log.Info().Msgf("KMIP server authenticated via AWS Auth. State saved to %s", localkmip.GetConfPathDisplay(serverName))
return accessToken

return accessToken, newAwsRefreshAccessTokenFunc(serverName, kmipServerID)
}

// Enrollment token path
Expand All @@ -197,7 +235,7 @@ func enrollKmipServer(cmd *cobra.Command, enrollMethod, serverName string) strin
storedAccessToken, err := localkmip.LoadStoredAccessToken(serverName)
if err == nil && storedAccessToken != "" {
log.Info().Msg("Reusing stored KMIP server access token.")
return storedAccessToken
return storedAccessToken, nil
}
if enrollToken == "" {
util.HandleError(errors.New("--token is required when --enroll-method=token and no access token is stored"))
Expand All @@ -220,6 +258,9 @@ func enrollKmipServer(cmd *cobra.Command, enrollMethod, serverName string) strin
if err := localkmip.SaveAccessToken(serverName, enrollResp.AccessToken); err != nil {
util.HandleError(err, "failed to save KMIP server access token")
}
if err := localkmip.SaveEnrollMethod(serverName, localkmip.EnrollMethodToken); err != nil {
util.HandleError(err, "failed to save KMIP enroll method to config")
}
if err := localkmip.SaveEnrollmentToken(serverName, enrollToken); err != nil {
util.HandleError(err, "failed to save enrollment token to config")
}
Expand All @@ -228,7 +269,7 @@ func enrollKmipServer(cmd *cobra.Command, enrollMethod, serverName string) strin
}

log.Info().Msgf("KMIP server enrolled successfully. Access token saved to %s", localkmip.GetConfPathDisplay(serverName))
return enrollResp.AccessToken
return enrollResp.AccessToken, nil
}

// resolveKmipIdentityCredentials parses the legacy machine-identity credentials.
Expand Down
25 changes: 25 additions & 0 deletions packages/kmip/enroll.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,31 @@ func SaveServerID(name, kmipServerID string) error {
return saveConfKey(name, INFISICAL_KMIP_SERVER_ID_KEY, kmipServerID)
}

// Conf-file only: the env var says what this run asked for, not how the server enrolled.
func LoadStoredEnrollMethod(name string) (string, error) {
return loadConfKey(name, INFISICAL_KMIP_ENROLL_METHOD_KEY)
}

func SaveEnrollMethod(name, method string) error {
return saveConfKey(name, INFISICAL_KMIP_ENROLL_METHOD_KEY, method)
}

func LoadPersistedServerID(name string) (string, error) {
return loadConfKey(name, INFISICAL_KMIP_SERVER_ID_KEY)
}

// Ignores the environment throughout: honouring INFISICAL_KMIP_SERVER_ID here would wire a
// token-enrolled server for an STS refresh it cannot perform. A missing method means the server
// enrolled before it was recorded, where a conf-file server ID identifies AWS enrollment.
func ResolveAwsRefreshServerID(name string) (string, bool) {
method, _ := LoadStoredEnrollMethod(name)
serverID, _ := LoadPersistedServerID(name)
if serverID == "" {
return "", false
}
return serverID, method == EnrollMethodAws || method == ""
}

func GetConfPathDisplay(name string) string {
path, err := kmipConfPath(name)
if err != nil {
Expand Down
Loading
Loading