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
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,6 @@ List of contributors, in chronological order:
* Gordian Schoenherr (https://github.com/schoenherrg)
* Silke Hofstra (https://github.com/silkeh)
* Itay Porezky (https://github.com/itayporezky)
* Ales Bregar (https://github.com/abregar)
* JupiterRider (https://github.com/JupiterRider)
* Agustin Henze (https://github.com/agustinhenze)
20 changes: 17 additions & 3 deletions api/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import (
type signingParams struct {
// Don't sign published repository
Skip bool ` json:"Skip" example:"false"`
// GPG key ID to use when signing the release, if not specified default key is used
GpgKey string ` json:"GpgKey" example:"A0546A43624A8331"`
// GPG key ID(s) to use when signing the release, separated by comma, and if not specified, default configured key(s) are used
GpgKey string ` json:"GpgKey" example:"KEY_ID_a, KEY_ID_b"`
// GPG keyring to use (instead of default)
Keyring string ` json:"Keyring" example:"trustedkeys.gpg"`
// GPG secret keyring to use (instead of default) Note: depreciated with gpg2
Expand All @@ -41,7 +41,21 @@ func getSigner(options *signingParams) (pgp.Signer, error) {
}

signer := context.GetSigner()
signer.SetKey(options.GpgKey)

var multiGpgKeys []string
// REST params have priority over config
if options.GpgKey != "" {
for _, p := range strings.Split(options.GpgKey, ",") {
if t := strings.TrimSpace(p); t != "" {
multiGpgKeys = append(multiGpgKeys, t)
}
}
} else if len(context.Config().GpgKeys) > 0 {
multiGpgKeys = context.Config().GpgKeys
}
for _, gpgKey := range multiGpgKeys {
signer.SetKey(gpgKey)
}
signer.SetKeyRing(options.Keyring, options.SecretKeyring)
signer.SetPassphrase(options.Passphrase, options.PassphraseFile)

Expand Down
34 changes: 33 additions & 1 deletion cmd/publish.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package cmd

import (
"strings"

"github.com/aptly-dev/aptly/pgp"
"github.com/smira/commander"
"github.com/smira/flag"
Expand All @@ -12,7 +14,20 @@ func getSigner(flags *flag.FlagSet) (pgp.Signer, error) {
}

signer := context.GetSigner()
signer.SetKey(flags.Lookup("gpg-key").Value.String())

var gpgKeys []string

// CLI args have priority over config
cliKeys := flags.Lookup("gpg-key").Value.Get().([]string)
if len(cliKeys) > 0 {
gpgKeys = cliKeys
} else if len(context.Config().GpgKeys) > 0 {
gpgKeys = context.Config().GpgKeys
}

for _, gpgKey := range gpgKeys {
signer.SetKey(gpgKey)
}
signer.SetKeyRing(flags.Lookup("keyring").Value.String(), flags.Lookup("secret-keyring").Value.String())
signer.SetPassphrase(flags.Lookup("passphrase").Value.String(), flags.Lookup("passphrase-file").Value.String())
signer.SetBatch(flags.Lookup("batch").Value.Get().(bool))
Expand All @@ -26,6 +41,23 @@ func getSigner(flags *flag.FlagSet) (pgp.Signer, error) {

}

type gpgKeyFlag struct {
gpgKeys []string
}

func (k *gpgKeyFlag) Set(value string) error {
k.gpgKeys = append(k.gpgKeys, value)
return nil
}

func (k *gpgKeyFlag) Get() interface{} {
return k.gpgKeys
}

func (k *gpgKeyFlag) String() string {
return strings.Join(k.gpgKeys, ",")
}

func makeCmdPublish() *commander.Command {
return &commander.Command{
UsageLine: "publish",
Expand Down
2 changes: 1 addition & 1 deletion cmd/publish_repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ Example:
}
cmd.Flag.String("distribution", "", "distribution name to publish")
cmd.Flag.String("component", "", "component name to publish (for multi-component publishing, separate components with commas)")
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
cmd.Flag.Var(&gpgKeyFlag{}, "gpg-key", "GPG key ID to use when signing the release (flag is repeatable, can be specified multiple times)")
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)")
cmd.Flag.String("passphrase", "", "GPG passphrase for the key (warning: could be insecure)")
Expand Down
2 changes: 1 addition & 1 deletion cmd/publish_snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ Example:
}
cmd.Flag.String("distribution", "", "distribution name to publish")
cmd.Flag.String("component", "", "component name to publish (for multi-component publishing, separate components with commas)")
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
cmd.Flag.Var(&gpgKeyFlag{}, "gpg-key", "GPG key ID to use when signing the release (flag is repeatable, can be specified multiple times)")
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)")
cmd.Flag.String("passphrase", "", "GPG passphrase for the key (warning: could be insecure)")
Expand Down
2 changes: 1 addition & 1 deletion cmd/publish_switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ This command would switch published repository (with one component) named ppa/wh
`,
Flag: *flag.NewFlagSet("aptly-publish-switch", flag.ExitOnError),
}
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
cmd.Flag.Var(&gpgKeyFlag{}, "gpg-key", "GPG key ID to use when signing the release (flag is repeatable, can be specified multiple times)")
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)")
cmd.Flag.String("passphrase", "", "GPG passphrase for the key (warning: could be insecure)")
Expand Down
2 changes: 1 addition & 1 deletion cmd/publish_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ Example:
`,
Flag: *flag.NewFlagSet("aptly-publish-update", flag.ExitOnError),
}
cmd.Flag.String("gpg-key", "", "GPG key ID to use when signing the release")
cmd.Flag.Var(&gpgKeyFlag{}, "gpg-key", "GPG key ID to use when signing the release (flag is repeatable, can be specified multiple times)")
cmd.Flag.Var(&keyRingsFlag{}, "keyring", "GPG keyring to use (instead of default)")
cmd.Flag.String("secret-keyring", "", "GPG secret keyring to use (instead of default)")
cmd.Flag.String("passphrase", "", "GPG passphrase for the key (warning: could be insecure)")
Expand Down
21 changes: 20 additions & 1 deletion docs/Publish.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,26 @@ Repositories can be published to local directories, Amazon S3 buckets, Azure or

GPG key is required to sign any published repository. The key pari should be generated before publishing.

Publiс part of the key should be exported from your keyring using `gpg --export --armor` and imported on the system which uses a published repository.
Public part of the key should be exported from your keyring using `gpg --export --armor` and imported on the system which uses a published repository.

* Multiple signing keys can be defined in aptly.conf using the gpgKeys array:
```
"gpgKeys": [
"KEY_ID_x",
"KEY_ID_y"
]
```

* It is also possible to pass multiple keys via the CLI using the repeatable `--gpg-key` flag:
```
aptly publish repo my-repo --gpg-key=KEY_ID_a --gpg-key=KEY_ID_b
```
* When using the REST API, the `gpgKey` parameter supports a comma-separated list of key IDs:
```
"gpgKey": "KEY_ID_a,KEY_ID_b"
```
* If `--gpg-key` is specified on the command line, or `gpgKey` is provided via the REST API, it takes precedence over any gpgKeys configuration in aptly.conf.
* With multi-key support, aptly will sign all Release files (both clearsigned and detached signatures) with each provided key, ensuring a smooth key rotation process while maintaining compatibility for existing clients.

#### Parameters

Expand Down
15 changes: 11 additions & 4 deletions pgp/gnupg.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ var (
type GpgSigner struct {
gpg string
version GPGVersion
keyRef string
keyRefs []string
keyring, secretKeyring string
passphrase, passphraseFile string
batch bool
Expand All @@ -35,7 +35,14 @@ func (g *GpgSigner) SetBatch(batch bool) {

// SetKey sets key ID to use when signing files
func (g *GpgSigner) SetKey(keyRef string) {
g.keyRef = keyRef
keyRef = strings.TrimSpace(keyRef)
if keyRef != "" {
if g.keyRefs == nil {
g.keyRefs = []string{keyRef}
} else {
g.keyRefs = append(g.keyRefs, keyRef)
}
}
}

// SetKeyRing allows to set custom keyring and secretkeyring
Expand All @@ -57,8 +64,8 @@ func (g *GpgSigner) gpgArgs() []string {
args = append(args, "--secret-keyring", g.secretKeyring)
}

if g.keyRef != "" {
args = append(args, "-u", g.keyRef)
for _, k := range g.keyRefs {
args = append(args, "-u", k)
}

if g.passphrase != "" || g.passphraseFile != "" {
Expand Down
1 change: 1 addition & 0 deletions system/t02_config/ConfigShowTest_gold
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"gpgProvider": "gpg",
"gpgDisableSign": false,
"gpgDisableVerify": false,
"gpgKeys": [],
"skipContentsPublishing": false,
"skipBz2Publishing": false,
"FileSystemPublishEndpoints": {},
Expand Down
1 change: 1 addition & 0 deletions system/t02_config/ConfigShowYAMLTest_gold
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ download_sourcepackages: false
gpg_provider: gpg
gpg_disable_sign: false
gpg_disable_verify: false
gpg_keys: []
skip_contents_publishing: false
skip_bz2_publishing: false
filesystem_publish_endpoints: {}
Expand Down
8 changes: 5 additions & 3 deletions utils/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,10 @@ type ConfigStructure struct { // nolint: maligned
DownloadSourcePackages bool `json:"downloadSourcePackages" yaml:"download_sourcepackages"`

// Signing
GpgProvider string `json:"gpgProvider" yaml:"gpg_provider"`
GpgDisableSign bool `json:"gpgDisableSign" yaml:"gpg_disable_sign"`
GpgDisableVerify bool `json:"gpgDisableVerify" yaml:"gpg_disable_verify"`
GpgProvider string `json:"gpgProvider" yaml:"gpg_provider"`
GpgDisableSign bool `json:"gpgDisableSign" yaml:"gpg_disable_sign"`
GpgDisableVerify bool `json:"gpgDisableVerify" yaml:"gpg_disable_verify"`
GpgKeys []string `json:"gpgKeys" yaml:"gpg_keys"`

// Publishing
SkipContentsPublishing bool `json:"skipContentsPublishing" yaml:"skip_contents_publishing"`
Expand Down Expand Up @@ -226,6 +227,7 @@ var Config = ConfigStructure{
GpgProvider: "gpg",
GpgDisableSign: false,
GpgDisableVerify: false,
GpgKeys: []string{},
DownloadSourcePackages: false,
PackagePoolStorage: PackagePoolStorage{
Local: &LocalPoolStorage{Path: ""},
Expand Down
3 changes: 3 additions & 0 deletions utils/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ func (s *ConfigSuite) TestSaveConfig(c *C) {
" \"gpgProvider\": \"gpg\",\n"+
" \"gpgDisableSign\": false,\n"+
" \"gpgDisableVerify\": false,\n"+
" \"gpgKeys\": null,\n"+
" \"skipContentsPublishing\": false,\n"+
" \"skipBz2Publishing\": false,\n"+
" \"FileSystemPublishEndpoints\": {\n"+
Expand Down Expand Up @@ -267,6 +268,7 @@ func (s *ConfigSuite) TestSaveYAML2Config(c *C) {
"gpg_provider: \"\"\n"+
"gpg_disable_sign: false\n"+
"gpg_disable_verify: false\n"+
"gpg_keys: []\n"+
"skip_contents_publishing: false\n"+
"skip_bz2_publishing: false\n"+
"filesystem_publish_endpoints: {}\n"+
Expand Down Expand Up @@ -322,6 +324,7 @@ download_sourcepackages: true
gpg_provider: gpg
gpg_disable_sign: true
gpg_disable_verify: true
gpg_keys: []
skip_contents_publishing: true
skip_bz2_publishing: true
filesystem_publish_endpoints:
Expand Down
Loading