CI / test (push) Successful in 4m6s
ed25519 over checksums.txt: llmgw release keygen makes the pair, CI signs with the RELEASE_SIGNING_KEY secret when it is set, and release.Install refuses an unsigned or wrongly signed release when the build embeds a trusted key (internal/release/trusted.pub, empty for now) or LLMGW_RELEASE_KEY names one. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
68 lines
2.1 KiB
Go
68 lines
2.1 KiB
Go
package cli
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"gitea.mynodes.xyz/ilker/llmgw/internal/release"
|
|
)
|
|
|
|
func newReleaseCmd() *cobra.Command {
|
|
c := &cobra.Command{
|
|
Use: "release",
|
|
Short: "Sign releases, and make the key they are signed with",
|
|
}
|
|
c.AddCommand(&cobra.Command{
|
|
Use: "keygen",
|
|
Short: "Make a release signing key pair",
|
|
Long: "Prints a public key and a private key. The private key goes into the CI\n" +
|
|
"secret RELEASE_SIGNING_KEY and nowhere else; the public key into\n" +
|
|
"internal/release/trusted.pub, which every build from then on carries\n" +
|
|
"and checks releases against. Run once, on a machine you trust.",
|
|
Args: cobra.NoArgs,
|
|
RunE: func(cmd *cobra.Command, _ []string) error {
|
|
pub, priv, err := release.GenerateKey()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Fprintf(cmd.OutOrStdout(), "public: %s\nprivate: %s\n", pub, priv)
|
|
fmt.Fprintln(cmd.ErrOrStderr(), "\nThe private key signs every release: put it in the CI secret "+
|
|
"RELEASE_SIGNING_KEY, then clear this terminal's scrollback.")
|
|
return nil
|
|
},
|
|
})
|
|
c.AddCommand(&cobra.Command{
|
|
Use: "sign FILE",
|
|
Short: "Sign a release's checksums, writing FILE.sig",
|
|
Long: "Reads the private key from RELEASE_SIGNING_KEY, never from an argument,\n" +
|
|
"so it does not land in a shell history or a process list.",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
key := os.Getenv("RELEASE_SIGNING_KEY")
|
|
if key == "" {
|
|
return errors.New("RELEASE_SIGNING_KEY is not set")
|
|
}
|
|
data, err := os.ReadFile(args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
sig, err := release.Sign(key, data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if pub := release.TrustedKey(); pub != "" {
|
|
// Signed with a key this build would not accept is a release
|
|
// every upgraded machine refuses: stopped here instead.
|
|
if err := release.Verify(pub, data, sig); err != nil {
|
|
return fmt.Errorf("the signing key does not match this build's trusted key: %w", err)
|
|
}
|
|
}
|
|
return os.WriteFile(args[0]+".sig", []byte(sig+"\n"), 0o644) //nolint:gosec // public
|
|
},
|
|
})
|
|
return c
|
|
}
|