Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit5b7ef4f

Browse files
committed
Merge branch 'main' into docs-preview-action
2 parents6b4f62c +114ba45 commit5b7ef4f

File tree

18 files changed

+346
-108
lines changed

18 files changed

+346
-108
lines changed

‎.github/workflows/ci.yaml‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,33 @@ jobs:
11801180
done
11811181
fi
11821182
1183+
-name:SBOM Generation and Attestation
1184+
if:github.ref == 'refs/heads/main'
1185+
env:
1186+
COSIGN_EXPERIMENTAL:1
1187+
run:|
1188+
set -euxo pipefail
1189+
1190+
# Define image base and tags
1191+
IMAGE_BASE="ghcr.io/coder/coder-preview"
1192+
TAGS=("${{ steps.build-docker.outputs.tag }}" "main" "latest")
1193+
1194+
# Generate and attest SBOM for each tag
1195+
for tag in "${TAGS[@]}"; do
1196+
IMAGE="${IMAGE_BASE}:${tag}"
1197+
SBOM_FILE="coder_sbom_${tag//[:\/]/_}.spdx.json"
1198+
1199+
echo "Generating SBOM for image: ${IMAGE}"
1200+
syft "${IMAGE}" -o spdx-json > "${SBOM_FILE}"
1201+
1202+
echo "Attesting SBOM to image: ${IMAGE}"
1203+
cosign clean "${IMAGE}"
1204+
cosign attest --type spdxjson \
1205+
--predicate "${SBOM_FILE}" \
1206+
--yes \
1207+
"${IMAGE}"
1208+
done
1209+
11831210
# GitHub attestation provides SLSA provenance for the Docker images, establishing a verifiable
11841211
# record that these images were built in GitHub Actions with specific inputs and environment.
11851212
# This complements our existing cosign attestations which focus on SBOMs.

‎.github/workflows/release.yaml‎

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,39 @@ jobs:
496496
env:
497497
CODER_BASE_IMAGE_TAG:${{ steps.image-base-tag.outputs.tag }}
498498

499+
-name:SBOM Generation and Attestation
500+
if:${{ !inputs.dry_run }}
501+
env:
502+
COSIGN_EXPERIMENTAL:"1"
503+
run:|
504+
set -euxo pipefail
505+
506+
# Generate SBOM for multi-arch image with version in filename
507+
echo "Generating SBOM for multi-arch image: ${{ steps.build_docker.outputs.multiarch_image }}"
508+
syft "${{ steps.build_docker.outputs.multiarch_image }}" -o spdx-json > coder_${{ steps.version.outputs.version }}_sbom.spdx.json
509+
510+
# Attest SBOM to multi-arch image
511+
echo "Attesting SBOM to multi-arch image: ${{ steps.build_docker.outputs.multiarch_image }}"
512+
cosign clean "${{ steps.build_docker.outputs.multiarch_image }}"
513+
cosign attest --type spdxjson \
514+
--predicate coder_${{ steps.version.outputs.version }}_sbom.spdx.json \
515+
--yes \
516+
"${{ steps.build_docker.outputs.multiarch_image }}"
517+
518+
# If latest tag was created, also attest it
519+
if [[ "${{ steps.build_docker.outputs.created_latest_tag }}" == "true" ]]; then
520+
latest_tag="$(./scripts/image_tag.sh --version latest)"
521+
echo "Generating SBOM for latest image: ${latest_tag}"
522+
syft "${latest_tag}" -o spdx-json > coder_latest_sbom.spdx.json
523+
524+
echo "Attesting SBOM to latest image: ${latest_tag}"
525+
cosign clean "${latest_tag}"
526+
cosign attest --type spdxjson \
527+
--predicate coder_latest_sbom.spdx.json \
528+
--yes \
529+
"${latest_tag}"
530+
fi
531+
499532
-name:GitHub Attestation for Docker image
500533
id:attest_main
501534
if:${{ !inputs.dry_run }}
@@ -612,16 +645,27 @@ jobs:
612645
fi
613646
declare -p publish_args
614647
648+
# Build the list of files to publish
649+
files=(
650+
./build/*_installer.exe
651+
./build/*.zip
652+
./build/*.tar.gz
653+
./build/*.tgz
654+
./build/*.apk
655+
./build/*.deb
656+
./build/*.rpm
657+
./coder_${{ steps.version.outputs.version }}_sbom.spdx.json
658+
)
659+
660+
# Only include the latest SBOM file if it was created
661+
if [[ "${{ steps.build_docker.outputs.created_latest_tag }}" == "true" ]]; then
662+
files+=(./coder_latest_sbom.spdx.json)
663+
fi
664+
615665
./scripts/release/publish.sh \
616666
"${publish_args[@]}" \
617667
--release-notes-file "$CODER_RELEASE_NOTES_FILE" \
618-
./build/*_installer.exe \
619-
./build/*.zip \
620-
./build/*.tar.gz \
621-
./build/*.tgz \
622-
./build/*.apk \
623-
./build/*.deb \
624-
./build/*.rpm
668+
"${files[@]}"
625669
env:
626670
GITHUB_TOKEN:${{ secrets.GITHUB_TOKEN }}
627671
CODER_GPG_RELEASE_KEY_BASE64:${{ secrets.GPG_RELEASE_KEY_BASE64 }}
@@ -663,6 +707,15 @@ jobs:
663707
./build/*.apk
664708
./build/*.deb
665709
./build/*.rpm
710+
./coder_${{ steps.version.outputs.version }}_sbom.spdx.json
711+
retention-days:7
712+
713+
-name:Upload latest sbom artifact to actions (if dry-run)
714+
if:inputs.dry_run && steps.build_docker.outputs.created_latest_tag == 'true'
715+
uses:actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02# v4.6.2
716+
with:
717+
name:latest-sbom-artifact
718+
path:./coder_latest_sbom.spdx.json
666719
retention-days:7
667720

668721
-name:Send repository-dispatch event

‎agent/agent.go‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1408,7 +1408,7 @@ func (a *agent) createTailnet(
14081408
ifrPTYServeErr!=nil&&
14091409
a.gracefulCtx.Err()==nil&&
14101410
!strings.Contains(rPTYServeErr.Error(),"use of closed network connection") {
1411-
a.logger.Error(ctx,"error serving reconnecting PTY",slog.Error(err))
1411+
a.logger.Error(ctx,"error serving reconnecting PTY",slog.Error(rPTYServeErr))
14121412
}
14131413
});err!=nil {
14141414
returnnil,err

‎cli/server.go‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,15 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
620620
returnxerrors.Errorf("parse ssh config options %q: %w",vals.SSHConfig.SSHConfigOptions.String(),err)
621621
}
622622

623+
// The workspace hostname suffix is always interpreted as implicitly beginning with a single dot, so it is
624+
// a config error to explicitly include the dot. This ensures that we always interpret the suffix as a
625+
// separate DNS label, and not just an ordinary string suffix. E.g. a suffix of 'coder' will match
626+
// 'en.coder' but not 'encoder'.
627+
ifstrings.HasPrefix(vals.WorkspaceHostnameSuffix.String(),".") {
628+
returnxerrors.Errorf("you must omit any leading . in workspace hostname suffix: %s",
629+
vals.WorkspaceHostnameSuffix.String())
630+
}
631+
623632
options:=&coderd.Options{
624633
AccessURL:vals.AccessURL.Value(),
625634
AppHostname:appHostname,

‎cli/ssh.go‎

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"os"
1414
"os/exec"
1515
"path/filepath"
16+
"regexp"
1617
"slices"
1718
"strconv"
1819
"strings"
@@ -57,12 +58,14 @@ var (
5758
autostopNotifyCountdown= []time.Duration{30*time.Minute}
5859
// gracefulShutdownTimeout is the timeout, per item in the stack of things to close
5960
gracefulShutdownTimeout=2*time.Second
61+
workspaceNameRe=regexp.MustCompile(`[/.]+|--`)
6062
)
6163

6264
func (r*RootCmd)ssh()*serpent.Command {
6365
var (
6466
stdiobool
6567
hostPrefixstring
68+
hostnameSuffixstring
6669
forwardAgentbool
6770
forwardGPGbool
6871
identityAgentstring
@@ -200,11 +203,14 @@ func (r *RootCmd) ssh() *serpent.Command {
200203
parsedEnv=append(parsedEnv, [2]string{k,v})
201204
}
202205

203-
namedWorkspace:=strings.TrimPrefix(inv.Args[0],hostPrefix)
204-
// Support "--" as a delimiter between owner and workspace name
205-
namedWorkspace=strings.Replace(namedWorkspace,"--","/",1)
206+
deploymentSSHConfig:= codersdk.SSHConfigResponse{
207+
HostnamePrefix:hostPrefix,
208+
HostnameSuffix:hostnameSuffix,
209+
}
206210

207-
workspace,workspaceAgent,err:=getWorkspaceAndAgent(ctx,inv,client,!disableAutostart,namedWorkspace)
211+
workspace,workspaceAgent,err:=findWorkspaceAndAgentByHostname(
212+
ctx,inv,client,
213+
inv.Args[0],deploymentSSHConfig,disableAutostart)
208214
iferr!=nil {
209215
returnerr
210216
}
@@ -563,6 +569,12 @@ func (r *RootCmd) ssh() *serpent.Command {
563569
Description:"Strip this prefix from the provided hostname to determine the workspace name. This is useful when used as part of an OpenSSH proxy command.",
564570
Value:serpent.StringOf(&hostPrefix),
565571
},
572+
{
573+
Flag:"hostname-suffix",
574+
Env:"CODER_SSH_HOSTNAME_SUFFIX",
575+
Description:"Strip this suffix from the provided hostname to determine the workspace name. This is useful when used as part of an OpenSSH proxy command. The suffix must be specified without a leading . character.",
576+
Value:serpent.StringOf(&hostnameSuffix),
577+
},
566578
{
567579
Flag:"forward-agent",
568580
FlagShorthand:"A",
@@ -655,6 +667,30 @@ func (r *RootCmd) ssh() *serpent.Command {
655667
returncmd
656668
}
657669

670+
// findWorkspaceAndAgentByHostname parses the hostname from the commandline and finds the workspace and agent it
671+
// corresponds to, taking into account any name prefixes or suffixes configured (e.g. myworkspace.coder, or
672+
// vscode-coder--myusername--myworkspace).
673+
funcfindWorkspaceAndAgentByHostname(
674+
ctx context.Context,inv*serpent.Invocation,client*codersdk.Client,
675+
hostnamestring,config codersdk.SSHConfigResponse,disableAutostartbool,
676+
) (
677+
codersdk.Workspace, codersdk.WorkspaceAgent,error,
678+
) {
679+
// for suffixes, we don't explicitly get the . and must add it. This is to ensure that the suffix is always
680+
// interpreted as a dotted label in DNS names, not just any string suffix. That is, a suffix of 'coder' will
681+
// match a hostname like 'en.coder', but not 'encoder'.
682+
qualifiedSuffix:="."+config.HostnameSuffix
683+
684+
switch {
685+
caseconfig.HostnamePrefix!=""&&strings.HasPrefix(hostname,config.HostnamePrefix):
686+
hostname=strings.TrimPrefix(hostname,config.HostnamePrefix)
687+
caseconfig.HostnameSuffix!=""&&strings.HasSuffix(hostname,qualifiedSuffix):
688+
hostname=strings.TrimSuffix(hostname,qualifiedSuffix)
689+
}
690+
hostname=normalizeWorkspaceInput(hostname)
691+
returngetWorkspaceAndAgent(ctx,inv,client,!disableAutostart,hostname)
692+
}
693+
658694
// watchAndClose ensures closer is called if the context is canceled or
659695
// the workspace reaches the stopped state.
660696
//
@@ -1413,3 +1449,28 @@ func collectNetworkStats(ctx context.Context, agentConn *workspacesdk.AgentConn,
14131449
DownloadBytesSec:int64(downloadSecs),
14141450
},nil
14151451
}
1452+
1453+
// Converts workspace name input to owner/workspace.agent format
1454+
// Possible valid input formats:
1455+
// workspace
1456+
// owner/workspace
1457+
// owner--workspace
1458+
// owner/workspace--agent
1459+
// owner/workspace.agent
1460+
// owner--workspace--agent
1461+
// owner--workspace.agent
1462+
funcnormalizeWorkspaceInput(inputstring)string {
1463+
// Split on "/", "--", and "."
1464+
parts:=workspaceNameRe.Split(input,-1)
1465+
1466+
switchlen(parts) {
1467+
case1:
1468+
returninput// "workspace"
1469+
case2:
1470+
returnfmt.Sprintf("%s/%s",parts[0],parts[1])// "owner/workspace"
1471+
case3:
1472+
returnfmt.Sprintf("%s/%s.%s",parts[0],parts[1],parts[2])// "owner/workspace.agent"
1473+
default:
1474+
returninput// Fallback
1475+
}
1476+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp