Skip to content

Commit cc716aa

Browse files
author
github-actions
committed
Merge branch 'main' into cras
2 parents e97e8ce + 476c356 commit cc716aa

File tree

21 files changed

+418
-25
lines changed

21 files changed

+418
-25
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,20 @@
22

33
## Changes Since Last Release
44

5+
### Behaviour Changes
6+
7+
- Skip attempting to bind inaccessible mount points when handling the
8+
`mount hostfs = yes` configuration option.
9+
510
### Bug Fixes
611

712
- Use correct username (not user's name) when computing `singularity oci` conmon
813
/ singularity state dir.
914
- Write StdErr messages from starter to terminal StdErr when an instance fails
1015
to start. Previously incorrectly written to terminal StdOut.
1116
- Fix incorrect debug message in Cgroups checks.
17+
- Skip invalid environment variables when pulling pulling OCI images to
18+
native SIF, so environment sourcing does not fail.
1219

1320
### New Features & Functionality
1421

@@ -28,6 +35,15 @@
2835
payloads of all valid signatures are displayed.
2936
- `singularity push` now supports pushing cosign signatures in an OCI-SIF to
3037
an OCI registry, via the `--with-cosign` flag.
38+
- `singularity pull` now supports pulling cosign signatures from a registry
39+
to an OCI-SIF, via the `--with-cosign` flag when `--oci` is also specified.
40+
Signatures can only be pulled when the image in the registry is in SquashFS
41+
format. Converting layer formats, or squashing to a single layer, modifies
42+
the image manifest, and would invalidate any signatures.
43+
- The new `singularity key generate-cosign-key-pair` subcommand can be used
44+
to generate a password-protected key-pair for signing OCI-SIF images with
45+
cosign-compatible signatures.
46+
- Added `dnf` definition file bootstrap as an alias for `yum`.
3147

3248
## Requirements / Packaging
3349

@@ -37,6 +53,8 @@
3753
- Ubuntu deb packages are built without libsubid support.
3854
- The RPM spec file no longer includes rules for SLES / openSUSE package builds,
3955
which have been untested / unsupported for some time.
56+
- Make binary builds more reproducible by deriving the GNU build ID
57+
from the Go build ID instead of using a randomly generated one.
4058

4159
### Removed Features
4260

cmd/internal/cli/key.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Copyright (c) 2020, Control Command Inc. All rights reserved.
2-
// Copyright (c) 2017-2021, Sylabs Inc. All rights reserved.
2+
// Copyright (c) 2017-2025, Sylabs Inc. All rights reserved.
33
// This software is licensed under a 3-clause BSD license. Please consult the
44
// LICENSE.md file distributed with the sources of this project regarding your
55
// rights to use or distribute this software.
@@ -94,6 +94,9 @@ func init() {
9494
&keyGlobalPubKeyFlag,
9595
KeyImportCmd, KeyExportCmd, KeyListCmd, KeyPullCmd, KeyPushCmd, KeyRemoveCmd,
9696
)
97+
98+
cmdManager.RegisterSubCmd(KeyCmd, KeyGenerateCosignKeyPairCmd)
99+
cmdManager.RegisterFlagForCmd(&keyOutputKeyPrefixFlag, KeyGenerateCosignKeyPairCmd)
97100
})
98101
}
99102

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright (c) 2025, Sylabs Inc. All rights reserved.
2+
// This software is licensed under a 3-clause BSD license. Please consult the
3+
// LICENSE.md file distributed with the sources of this project regarding your
4+
// rights to use or distribute this software.
5+
6+
package cli
7+
8+
import (
9+
"os"
10+
11+
"github.com/sigstore/cosign/v2/pkg/cosign"
12+
"github.com/sigstore/sigstore/pkg/cryptoutils"
13+
"github.com/spf13/cobra"
14+
"github.com/sylabs/singularity/v4/docs"
15+
"github.com/sylabs/singularity/v4/internal/pkg/util/fs"
16+
"github.com/sylabs/singularity/v4/pkg/cmdline"
17+
"github.com/sylabs/singularity/v4/pkg/sylog"
18+
)
19+
20+
var (
21+
cosignKeyPairPrefix string
22+
23+
// --output-key-prefix
24+
keyOutputKeyPrefixFlag = cmdline.Flag{
25+
ID: "keyOutputKeyPrefixFlag",
26+
Value: &cosignKeyPairPrefix,
27+
DefaultValue: "singularity-cosign",
28+
Name: "output-key-prefix",
29+
Usage: "prefix for .key / .pub files",
30+
}
31+
32+
// KeyNewPairCmd is 'singularity key newpair' and generate a new OpenPGP key pair
33+
KeyGenerateCosignKeyPairCmd = &cobra.Command{
34+
Args: cobra.ExactArgs(0),
35+
DisableFlagsInUseLine: true,
36+
Run: runGenerateCosignKeyPairCmd,
37+
Use: docs.KeyGenerateCosignKeyPairUse,
38+
Short: docs.KeyGenerateCosignKeyPairShort,
39+
Long: docs.KeyGenerateCosignKeyPairLong,
40+
Example: docs.KeyGenerateCosignKeyPairExample,
41+
}
42+
)
43+
44+
func runGenerateCosignKeyPairCmd(_ *cobra.Command, _ []string) {
45+
priKey := cosignKeyPairPrefix + ".key"
46+
pubKey := cosignKeyPairPrefix + ".pub"
47+
48+
exists, err := fs.PathExists(priKey)
49+
if err != nil {
50+
sylog.Fatalf("%v", err)
51+
}
52+
if exists {
53+
sylog.Fatalf("file exists, will not overwrite: %s", priKey)
54+
}
55+
56+
exists, err = fs.PathExists(pubKey)
57+
if err != nil {
58+
sylog.Fatalf("%v", err)
59+
}
60+
if exists {
61+
sylog.Fatalf("file exists, will not overwrite: %s", pubKey)
62+
}
63+
64+
sylog.Infof("Creating cosign key-pair %s.key/.pub", cosignKeyPairPrefix)
65+
66+
kb, err := cosign.GenerateKeyPair(cryptoutils.GetPasswordFromStdIn)
67+
if err != nil {
68+
sylog.Fatalf("%v", err)
69+
}
70+
71+
if err := os.WriteFile(priKey, kb.PrivateBytes, 0o600); err != nil {
72+
sylog.Fatalf("%v", err)
73+
}
74+
if err := os.WriteFile(pubKey, kb.PublicBytes, 0o644); err != nil {
75+
sylog.Fatalf("%v", err)
76+
}
77+
}

cmd/internal/cli/pull.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Copyright (c) 2020, Control Command Inc. All rights reserved.
2-
// Copyright (c) 2018-2023, Sylabs Inc. All rights reserved.
2+
// Copyright (c) 2018-2025, Sylabs Inc. All rights reserved.
33
// This software is licensed under a 3-clause BSD license. Please consult the
44
// LICENSE.md file distributed with the sources of this project regarding your
55
// rights to use or distribute this software.
@@ -51,6 +51,8 @@ var (
5151
unauthenticatedPull bool
5252
// pullDir is the path that the containers will be pulled to, if set.
5353
pullDir string
54+
// pullWithCosign sets whether cosign signatures are pushed when pushing OCI images.
55+
pullWithCosign bool
5456
)
5557

5658
// --library
@@ -118,6 +120,16 @@ var pullAllowUnauthenticatedFlag = cmdline.Flag{
118120
Hidden: true,
119121
}
120122

123+
// --with-cosign
124+
var pullWithCosignFlag = cmdline.Flag{
125+
ID: "pullWithCosignFlag",
126+
Value: &pullWithCosign,
127+
DefaultValue: false,
128+
Name: "with-cosign",
129+
Usage: "pull associated cosign signatures into an OCI-SIF image",
130+
EnvKeys: []string{"WITH_COSIGN"},
131+
}
132+
121133
func init() {
122134
addCmdInit(func(cmdManager *cmdline.CommandManager) {
123135
cmdManager.RegisterCmd(PullCmd)
@@ -147,6 +159,8 @@ func init() {
147159
cmdManager.RegisterFlagForCmd(&commonPlatformFlag, PullCmd)
148160

149161
cmdManager.RegisterFlagForCmd(&commonAuthFileFlag, PullCmd)
162+
163+
cmdManager.RegisterFlagForCmd(&pullWithCosignFlag, PullCmd)
150164
})
151165
}
152166

@@ -244,6 +258,7 @@ func pullRun(cmd *cobra.Command, args []string) {
244258
KeepLayers: keepLayers,
245259
TmpDir: tmpDir,
246260
Platform: getOCIPlatform(),
261+
WithCosign: pullWithCosign,
247262
}
248263
_, err = library.PullToFile(ctx, imgCache, pullTo, ref, pullOpts)
249264
if err != nil && err != library.ErrLibraryPullUnsigned {
@@ -309,6 +324,7 @@ func pullRun(cmd *cobra.Command, args []string) {
309324
KeepLayers: keepLayers,
310325
Platform: getOCIPlatform(),
311326
ReqAuthFile: reqAuthFile,
327+
WithCosign: pullWithCosign,
312328
}
313329

314330
_, err = oci.PullToFile(ctx, imgCache, pullTo, pullFrom, pullOpts)

cmd/internal/cli/push.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Copyright (c) 2020, Control Command Inc. All rights reserved.
2-
// Copyright (c) 2018-2023, Sylabs Inc. All rights reserved.
2+
// Copyright (c) 2018-2025, Sylabs Inc. All rights reserved.
33
// This software is licensed under a 3-clause BSD license. Please consult the
44
// LICENSE.md file distributed with the sources of this project regarding your
55
// rights to use or distribute this software.

docs/content.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) 2017-2024, Sylabs Inc. All rights reserved.
1+
// Copyright (c) 2017-2025, Sylabs Inc. All rights reserved.
22
// Copyright (c) Contributors to the Apptainer project, established as
33
// Apptainer a Series of LF Projects LLC.
44
// This software is licensed under a 3-clause BSD license. Please consult the
@@ -261,7 +261,7 @@ Enterprise Performance Computing (EPC)`
261261
// key newpair
262262
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
263263
KeyNewPairUse string = `newpair`
264-
KeyNewPairShort string = `Create a new key pair`
264+
KeyNewPairShort string = `Create a new PGP key-pair`
265265
KeyNewPairLong string = `
266266
The 'key newpair' command allows you to create a new key or public/private
267267
keys to be stored in the default user local keyring location (e.g.,
@@ -340,6 +340,18 @@ Enterprise Performance Computing (EPC)`
340340
KeyRemoveExample string = `
341341
$ singularity key remove D87FE3AF5C1F063FCBCC9B02F812842B5EEE5934`
342342

343+
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
344+
// key new-cosign-pair
345+
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
346+
KeyGenerateCosignKeyPairUse string = `generate-cosign-key-pair`
347+
KeyGenerateCosignKeyPairShort string = `Generate a new cosign key-pair`
348+
KeyGenerateCosignKeyPairLong string = `
349+
The 'key generate-cosign-key-pair' command allows you to create a new public/private
350+
key-pair that can be used to sign OCI-SIF images with a cosign compatible signature.`
351+
KeyGenerateCosignKeyPairExample string = `
352+
$ singularity key generate-cosign-keypair
353+
$ singularity key generate-cosign-keypair --output-key-prefix=mykeypair`
354+
343355
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
344356
// delete
345357
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

e2e/key/key.go

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Copyright (c) 2020, Control Command Inc. All rights reserved.
2-
// Copyright (c) 2019, Sylabs Inc. All rights reserved.
2+
// Copyright (c) 2019-2025, Sylabs Inc. All rights reserved.
33
// Copyright (c) Contributors to the Apptainer project, established as
44
// Apptainer a Series of LF Projects LLC.
55
// This software is licensed under a 3-clause BSD license. Please consult the
@@ -174,7 +174,7 @@ func (c *ctx) singularityKeyNewpair(t *testing.T) {
174174
{
175175
name: "newpair help",
176176
args: []string{"newpair", "--help"},
177-
stdout: "^Create a new key pair",
177+
stdout: "^Create a new PGP key-pair",
178178
},
179179
{
180180
name: "newpair",
@@ -728,6 +728,70 @@ func (c ctx) singularityKeyCmd(t *testing.T) {
728728
c.singularityKeyRemove(t)
729729
}
730730

731+
func (c *ctx) generateCosignKeypair(t *testing.T) {
732+
testDir := t.TempDir()
733+
734+
tests := []struct {
735+
name string
736+
args []string
737+
consoleOps []string
738+
expectExit int
739+
expectPrivate string
740+
expectPublic string
741+
}{
742+
{
743+
name: "OK",
744+
args: []string{"generate-cosign-key-pair"},
745+
consoleOps: []string{
746+
"test123",
747+
"test123",
748+
},
749+
expectExit: 0,
750+
expectPrivate: filepath.Join(testDir, "singularity-cosign.key"),
751+
expectPublic: filepath.Join(testDir, "singularity-cosign.pub"),
752+
},
753+
{
754+
name: "FilesExist",
755+
args: []string{"generate-cosign-key-pair"},
756+
expectExit: 255,
757+
},
758+
{
759+
name: "Prefix",
760+
args: []string{"generate-cosign-key-pair", "--output-key-prefix", "test"},
761+
consoleOps: []string{
762+
"test123",
763+
"test123",
764+
},
765+
expectExit: 0,
766+
expectPrivate: filepath.Join(testDir, "test.key"),
767+
expectPublic: filepath.Join(testDir, "test.pub"),
768+
},
769+
}
770+
771+
for _, tt := range tests {
772+
c.env.RunSingularity(
773+
t,
774+
e2e.AsSubtest(tt.name),
775+
e2e.WithProfile(e2e.UserProfile),
776+
e2e.ConsoleRun(buildConsoleLines(tt.consoleOps...)...),
777+
e2e.WithDir(testDir),
778+
e2e.WithCommand("key"),
779+
e2e.WithArgs(tt.args...),
780+
e2e.ExpectExit(tt.expectExit),
781+
)
782+
if tt.expectPrivate != "" {
783+
if !e2e.PathExists(t, tt.expectPrivate) {
784+
t.Errorf("Private key %q not found", tt.expectPrivate)
785+
}
786+
}
787+
if tt.expectPublic != "" {
788+
if !e2e.PathExists(t, tt.expectPublic) {
789+
t.Errorf("Public key %q not found", tt.expectPublic)
790+
}
791+
}
792+
}
793+
}
794+
731795
// E2ETests is the main func to trigger the test suite
732796
func E2ETests(env e2e.TestEnv) testhelper.Tests {
733797
c := ctx{
@@ -746,5 +810,6 @@ func E2ETests(env e2e.TestEnv) testhelper.Tests {
746810
t.Run("keyCmd", c.singularityKeyCmd) // Run all the tests in order
747811
t.Run("keyNewpairWithLen", c.singularityKeyNewpairWithLen) // We run a separate test for `key newpair --bit-length` because it requires handling a keyring a specific way
748812
},
813+
"cosign": c.generateCosignKeypair,
749814
}
750815
}

0 commit comments

Comments
 (0)