Skip to content
Open
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
39 changes: 38 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ endif

FINCH_CORE_DIR := $(CURDIR)/deps/finch-core

remote-all: arch-test finch install.finch-core-dependencies finch.yaml networks.yaml config.yaml $(OUTDIR)/finch-daemon/[email protected]
remote-all: arch-test finch finch-cred-daemon docker-credential-helper install.finch-core-dependencies finch.yaml networks.yaml config.yaml $(OUTDIR)/finch-daemon/[email protected]

ifeq ($(BUILD_OS), Windows_NT)
include Makefile.windows
Expand Down Expand Up @@ -169,6 +169,43 @@ finch-native: finch-all
finch-all:
$(GO) build -ldflags $(LDFLAGS) -tags "$(GO_BUILD_TAGS)" -o $(OUTDIR)/bin/$(BINARYNAME) $(PACKAGE)/cmd/finch

.PHONY: finch-cred-daemon
finch-cred-daemon:
$(GO) build -ldflags $(LDFLAGS) -o $(OUTDIR)/bin/finch-cred-daemon $(PACKAGE)/cmd/finch/cred-helper

CRED_HELPER_VERSION ?= v0.9.4

.PHONY: docker-credential-helper
ifeq ($(BUILD_OS), Darwin)
docker-credential-helper:
# Download real binary for host daemon
mkdir -p $(OUTDIR)/bin/cred-helpers
ifeq ($(ARCH),arm64)
curl -L https://github.com/docker/docker-credential-helpers/releases/download/$(CRED_HELPER_VERSION)/docker-credential-osxkeychain-$(CRED_HELPER_VERSION).darwin-arm64 -o $(OUTDIR)/bin/cred-helpers/docker-credential-osxkeychain
else
curl -L https://github.com/docker/docker-credential-helpers/releases/download/$(CRED_HELPER_VERSION)/docker-credential-osxkeychain-$(CRED_HELPER_VERSION).darwin-amd64 -o $(OUTDIR)/bin/cred-helpers/docker-credential-osxkeychain
endif
chmod +x $(OUTDIR)/bin/cred-helpers/docker-credential-osxkeychain
# Create dummy script for VM (will be overwritten at runtime)
mkdir -p ~/.finch/cred-helpers
echo '#!/bin/bash' > ~/.finch/cred-helpers/docker-credential-osxkeychain
echo 'echo "Dummy credential helper - will be replaced by bridge script"' >> ~/.finch/cred-helpers/docker-credential-osxkeychain
chmod +x ~/.finch/cred-helpers/docker-credential-osxkeychain
else ifeq ($(BUILD_OS), Windows_NT)
docker-credential-helper:
# Download real binary for host daemon
mkdir -p $(OUTDIR)/bin/cred-helpers
curl -L https://github.com/docker/docker-credential-helpers/releases/download/$(CRED_HELPER_VERSION)/docker-credential-wincred-$(CRED_HELPER_VERSION).windows-amd64.exe -o $(OUTDIR)/bin/cred-helpers/docker-credential-wincred.exe
# Create dummy script for VM (will be overwritten at runtime)
mkdir -p ~/.finch/cred-helpers
echo '#!/bin/bash' > ~/.finch/cred-helpers/docker-credential-wincred
echo 'echo "Dummy credential helper - will be replaced by bridge script"' >> ~/.finch/cred-helpers/docker-credential-wincred
chmod +x ~/.finch/cred-helpers/docker-credential-wincred
else
docker-credential-helper:
@echo "No credential helper needed for Linux"
endif

.PHONY: release
release: check-licenses all download-licenses

Expand Down
110 changes: 110 additions & 0 deletions cmd/finch/cred-helper/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package main

Check failure on line 4 in cmd/finch/cred-helper/main.go

View workflow job for this annotation

GitHub Actions / lint

package-comments: should have a package comment (revive)

import (
"fmt"
"log"
"net"
"os"
"os/exec"
"os/signal"
"strings"
"syscall"
)

// const sockAddress = "/tmp/cred.sock"

Check failure on line 17 in cmd/finch/cred-helper/main.go

View workflow job for this annotation

GitHub Actions / lint

Comment should end in a period (godot)
const hostAddr = "0.0.0.0:8080" // Listen on all interfaces

Check failure on line 18 in cmd/finch/cred-helper/main.go

View workflow job for this annotation

GitHub Actions / lint

File is not properly formatted (gofumpt)

func main() {

// start the socket
fmt.Println("Credential helper daemon started")
socket, err := net.Listen("tcp", hostAddr)

Check failure on line 24 in cmd/finch/cred-helper/main.go

View workflow job for this annotation

GitHub Actions / lint

G102: Binds to all network interfaces (gosec)
if err != nil {
log.Fatal(err)
}

// cleanup of socket as goroutine
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
// os.Remove(hostAddr)
os.Exit(1)
}()

for {

// accept the connection
conn, err := socket.Accept()
if err != nil {
log.Printf("Accept error: %v", err)
continue
}

go func(conn net.Conn) {
defer conn.Close()

Check failure on line 48 in cmd/finch/cred-helper/main.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `conn.Close` is not checked (errcheck)

// create buffer for socket
buf := make([]byte, 4096)

Check failure on line 51 in cmd/finch/cred-helper/main.go

View workflow job for this annotation

GitHub Actions / lint

slice `buf` does not have non-zero initial length (makezero)
n, err := conn.Read(buf)
if err != nil {
log.Printf("Read error: %v", err)
return
}

// need to read in the data
message := strings.TrimSpace(string(buf[:n]))
lines := strings.Split(message, "\n")
if len(lines) == 0 {
conn.Write([]byte("Error: empty message"))

Check failure on line 62 in cmd/finch/cred-helper/main.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `conn.Write` is not checked (errcheck)
return
}

// Parse command and data
cmdName := strings.TrimSpace(lines[0])
var stdinData string
if len(lines) > 1 {
// Decode HTML entities like &quot; -> "
stdinData = strings.ReplaceAll(strings.Join(lines[1:], "\n"), "&quot;", "\"")
}

// Normalize Docker Hub URLs to canonical format
if cmdName == "get" && stdinData != "" {
if strings.Contains(stdinData, "docker.io") || strings.Contains(stdinData, "registry-1.docker.io") {
stdinData = "https://index.docker.io/v1/"
}
}

fmt.Printf("[RECV] Command: '%s', Data: '%s'\n", cmdName, stdinData)

binaryPath := "/Users/ayushkp/Documents/finch-creds/finch/_output/bin/cred-helpers/docker-credential-osxkeychain"
cmd := exec.Command(binaryPath, cmdName)

Check failure on line 84 in cmd/finch/cred-helper/main.go

View workflow job for this annotation

GitHub Actions / lint

G204: Subprocess launched with variable (gosec)
if stdinData != "" {
cmd.Stdin = strings.NewReader(stdinData)
}

// Capture both stdout and stderr
output, err := cmd.CombinedOutput()
if err != nil {
// Check if it's a "credentials not found" error
outputStr := string(output)
if strings.Contains(outputStr, "credentials not found") ||
strings.Contains(outputStr, "could not be found in the keychain") ||
strings.Contains(outputStr, "not correct") {
fmt.Printf("[SEND] No credentials found, allowing anonymous access\n")
conn.Write([]byte("")) // Empty = no credentials, try anonymous

Check failure on line 98 in cmd/finch/cred-helper/main.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `conn.Write` is not checked (errcheck)
return
}
fmt.Printf("[SEND] Error: %v, Output: '%s'\n", err, outputStr)
conn.Write([]byte("")) // Return empty for any error to allow fallback

Check failure on line 102 in cmd/finch/cred-helper/main.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `conn.Write` is not checked (errcheck)
return
}

fmt.Printf("[SEND] Success: '%s'\n", string(output))
conn.Write(output)
}(conn)
}
}
2 changes: 1 addition & 1 deletion deps/finch-core
4 changes: 4 additions & 0 deletions finch.yaml.d/common.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ provision:
sudo systemctl daemon-reload

sudo systemctl restart containerd.service
portForwards:
- guestSocket: "/run/cred.sock"
hostSocket: "/private/tmp/cred.sock"

env:
# Containerd namespace is used by the lima cidata script
# 40-install-containerd.sh. Specifically this variable is defining the
Expand Down
2 changes: 2 additions & 0 deletions finch.yaml.d/mac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,5 @@ hostResolver:
portForwards:
- guestSocket: "/run/finch.sock"
hostSocket: "{{.Dir}}/sock/finch.sock"
- guestSocket: "/run/cred.sock"
hostSocket: "/private/tmp/cred.sock"
9 changes: 8 additions & 1 deletion pkg/config/defaults_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,13 @@ func cpuDefault(cfg *Finch, deps LoadSystemDeps) {
}
}

// Different because the other defaults use single values; this uses a slice
func credHelperDefault(cfg *Finch) {
if cfg.CredsHelpers == nil || len(cfg.CredsHelpers) == 0 {
cfg.CredsHelpers = []string{"osxkeychain"}
}
}

// applyDefaults sets default configuration options if they are not already set.
func applyDefaults(
cfg *Finch,
Expand All @@ -75,6 +82,6 @@ func applyDefaults(
}
vmDefault(cfg, supportsVz)
rosettaDefault(cfg)

credHelperDefault(cfg)
return cfg
}
16 changes: 13 additions & 3 deletions pkg/config/nerdctl_config_applier.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,21 @@ func updateEnvironment(fs afero.Fs, fc *Finch, finchDir, homeDir, limaVMHomeDir
}

//nolint:gosec // G101: Potential hardcoded credentials false positive
const configureCredHelperTemplate = `([ -e "$FINCH_DIR"/cred-helpers/docker-credential-%s ] || \
(echo "error: docker-credential-%s not found in $FINCH_DIR/cred-helpers directory.")) && \
([ -L /usr/local/bin/docker-credential-%s ] || sudo ln -s "$FINCH_DIR"/cred-helpers/docker-credential-%s /usr/local/bin)`
const configureCredHelperTemplate = `[ -x /usr/local/bin/docker-credential-%s ] || (
echo "Creating credential helper script for %s" && \
sudo tee /usr/local/bin/docker-credential-%s > /dev/null << 'CREDEOF'
#!/bin/bash
# Forward to host daemon via TCP
stdin_data=$(cat)
response=$(echo -e "$1\n$stdin_data" | nc host.lima.internal 8080)
echo "$response"
CREDEOF
sudo chmod +x /usr/local/bin/docker-credential-%s
)`

for _, credHelper := range fc.CredsHelpers {
cmdArr = append(cmdArr, fmt.Sprintf(`echo '{"credsStore": "%s"}' > "$FINCH_DIR"/config.json`, credHelper))
// Create VM-side bridge script that forwards to host TCP daemon (only if it doesn't exist)
cmdArr = append(cmdArr, fmt.Sprintf(configureCredHelperTemplate, credHelper, credHelper, credHelper, credHelper))
}

Expand Down
Loading