aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorExploding Dragon <explodingfkl@gmail.com>2024-08-06 21:03:33 +0800
committerGitHub <noreply@github.com>2024-08-06 09:03:33 -0400
commitde175e3b06e623280058278cdb0a62de7443cd86 (patch)
tree51dde551a952e329d0035fe25affcf17bcc74fd5
parent94cca8846e7d62c8a295d70c8199d706dfa60e5c (diff)
downloadgitea-de175e3b06e623280058278cdb0a62de7443cd86.tar.gz
gitea-de175e3b06e623280058278cdb0a62de7443cd86.zip
Add signature support for the RPM module (#27069)
close #27031 If the rpm package does not contain a matching gpg signature, the installation will fail. See (#27031) , now auto-signing rpm uploads. This option is turned off by default for compatibility.
-rw-r--r--custom/conf/app.example.ini3
-rw-r--r--modules/setting/packages.go3
-rw-r--r--routers/api/packages/rpm/rpm.go16
-rw-r--r--services/packages/rpm/repository.go38
-rw-r--r--tests/integration/api_packages_rpm_test.go27
5 files changed, 82 insertions, 5 deletions
diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini
index 89d04d381c..62edef597c 100644
--- a/custom/conf/app.example.ini
+++ b/custom/conf/app.example.ini
@@ -2555,7 +2555,8 @@ LEVEL = Info
;LIMIT_SIZE_SWIFT = -1
;; Maximum size of a Vagrant upload (`-1` means no limits, format `1000`, `1 MB`, `1 GiB`)
;LIMIT_SIZE_VAGRANT = -1
-
+;; Enable RPM re-signing by default. (It will overwrite the old signature ,using v4 format, not compatible with CentOS 6 or older)
+;DEFAULT_RPM_SIGN_ENABLED = false
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; default storage for attachments, lfs and avatars
diff --git a/modules/setting/packages.go b/modules/setting/packages.go
index 00fba67b39..bc093e7ea6 100644
--- a/modules/setting/packages.go
+++ b/modules/setting/packages.go
@@ -42,6 +42,8 @@ var (
LimitSizeRubyGems int64
LimitSizeSwift int64
LimitSizeVagrant int64
+
+ DefaultRPMSignEnabled bool
}{
Enabled: true,
LimitTotalOwnerCount: -1,
@@ -97,6 +99,7 @@ func loadPackagesFrom(rootCfg ConfigProvider) (err error) {
Packages.LimitSizeRubyGems = mustBytes(sec, "LIMIT_SIZE_RUBYGEMS")
Packages.LimitSizeSwift = mustBytes(sec, "LIMIT_SIZE_SWIFT")
Packages.LimitSizeVagrant = mustBytes(sec, "LIMIT_SIZE_VAGRANT")
+ Packages.DefaultRPMSignEnabled = sec.Key("DEFAULT_RPM_SIGN_ENABLED").MustBool(false)
return nil
}
diff --git a/routers/api/packages/rpm/rpm.go b/routers/api/packages/rpm/rpm.go
index 11d7729eec..4c822e0999 100644
--- a/routers/api/packages/rpm/rpm.go
+++ b/routers/api/packages/rpm/rpm.go
@@ -133,6 +133,21 @@ func UploadPackageFile(ctx *context.Context) {
}
defer buf.Close()
+ // if rpm sign enabled
+ if setting.Packages.DefaultRPMSignEnabled || ctx.FormBool("sign") {
+ pri, _, err := rpm_service.GetOrCreateKeyPair(ctx, ctx.Package.Owner.ID)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+ buf, err = rpm_service.SignPackage(buf, pri)
+ if err != nil {
+ // Not in rpm format, parsing failed.
+ apiError(ctx, http.StatusBadRequest, err)
+ return
+ }
+ }
+
pck, err := rpm_module.ParsePackage(buf)
if err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
@@ -142,7 +157,6 @@ func UploadPackageFile(ctx *context.Context) {
}
return
}
-
if _, err := buf.Seek(0, io.SeekStart); err != nil {
apiError(ctx, http.StatusInternalServerError, err)
return
diff --git a/services/packages/rpm/repository.go b/services/packages/rpm/repository.go
index c52c8a5dd9..19968f9b30 100644
--- a/services/packages/rpm/repository.go
+++ b/services/packages/rpm/repository.go
@@ -21,14 +21,16 @@ import (
rpm_model "code.gitea.io/gitea/models/packages/rpm"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/log"
packages_module "code.gitea.io/gitea/modules/packages"
rpm_module "code.gitea.io/gitea/modules/packages/rpm"
"code.gitea.io/gitea/modules/util"
packages_service "code.gitea.io/gitea/services/packages"
- "github.com/keybase/go-crypto/openpgp"
- "github.com/keybase/go-crypto/openpgp/armor"
- "github.com/keybase/go-crypto/openpgp/packet"
+ "github.com/ProtonMail/go-crypto/openpgp"
+ "github.com/ProtonMail/go-crypto/openpgp/armor"
+ "github.com/ProtonMail/go-crypto/openpgp/packet"
+ "github.com/sassoftware/go-rpmutils"
)
// GetOrCreateRepositoryVersion gets or creates the internal repository package
@@ -641,3 +643,33 @@ func addDataAsFileToRepo(ctx context.Context, pv *packages_model.PackageVersion,
OpenSize: wc.Written(),
}, nil
}
+
+func SignPackage(rpm *packages_module.HashedBuffer, privateKey string) (*packages_module.HashedBuffer, error) {
+ keyring, err := openpgp.ReadArmoredKeyRing(bytes.NewReader([]byte(privateKey)))
+ if err != nil {
+ // failed to parse key
+ return nil, err
+ }
+ entity := keyring[0]
+ h, err := rpmutils.SignRpmStream(rpm, entity.PrivateKey, nil)
+ if err != nil {
+ // error signing rpm
+ return nil, err
+ }
+ signBlob, err := h.DumpSignatureHeader(false)
+ if err != nil {
+ // error writing sig header
+ return nil, err
+ }
+ if len(signBlob)%8 != 0 {
+ log.Info("incorrect padding: got %d bytes, expected a multiple of 8", len(signBlob))
+ return nil, err
+ }
+
+ // move fp to sign end
+ if _, err := rpm.Seek(int64(h.OriginalSignatureHeaderSize()), io.SeekStart); err != nil {
+ return nil, err
+ }
+ // create signed rpm buf
+ return packages_module.CreateHashedBufferFromReader(io.MultiReader(bytes.NewReader(signBlob), rpm))
+}
diff --git a/tests/integration/api_packages_rpm_test.go b/tests/integration/api_packages_rpm_test.go
index 1dcec6099e..6feceaeb78 100644
--- a/tests/integration/api_packages_rpm_test.go
+++ b/tests/integration/api_packages_rpm_test.go
@@ -24,7 +24,10 @@ import (
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/tests"
+ "github.com/ProtonMail/go-crypto/openpgp"
+ "github.com/sassoftware/go-rpmutils"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestPackageRpm(t *testing.T) {
@@ -431,6 +434,30 @@ gpgkey=%sapi/packages/%s/rpm/repository.key`,
AddBasicAuth(user.Name)
MakeRequest(t, req, http.StatusNotFound)
})
+
+ t.Run("UploadSign", func(t *testing.T) {
+ defer tests.PrintCurrentTest(t)()
+ url := groupURL + "/upload?sign=true"
+ req := NewRequestWithBody(t, "PUT", url, bytes.NewReader(content)).
+ AddBasicAuth(user.Name)
+ MakeRequest(t, req, http.StatusCreated)
+
+ gpgReq := NewRequest(t, "GET", rootURL+"/repository.key")
+ gpgResp := MakeRequest(t, gpgReq, http.StatusOK)
+ pub, err := openpgp.ReadArmoredKeyRing(gpgResp.Body)
+ require.NoError(t, err)
+
+ req = NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s", groupURL, packageName, packageVersion, packageArchitecture))
+ resp := MakeRequest(t, req, http.StatusOK)
+
+ _, sigs, err := rpmutils.Verify(resp.Body, pub)
+ require.NoError(t, err)
+ require.NotEmpty(t, sigs)
+
+ req = NewRequest(t, "DELETE", fmt.Sprintf("%s/package/%s/%s/%s", groupURL, packageName, packageVersion, packageArchitecture)).
+ AddBasicAuth(user.Name)
+ MakeRequest(t, req, http.StatusNoContent)
+ })
})
}
}