Skip to content

Commit

Permalink
feat: support rsa public keys for cosign
Browse files Browse the repository at this point in the history
as of v1.3.0, cosign allows verifying signatures using RSA instead of ECDSA. this is integrated via the rsa package.

fixes #201

Co-authored-by: Ivan Wallis <iwallis@gmail.com>
  • Loading branch information
2 people authored and phbelitz committed Jun 26, 2023
1 parent f91f328 commit 64cea52
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 6 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ install:
helm install connaisseur helm --atomic --create-namespace --namespace $(NAMESPACE)

dev-install:
helm install --set kubernetes.deployment.replicasCount=1,kubernetes.deployment.imagePullPolicy=Never connaisseur helm --atomic --create-namespace --namespace $(NAMESPACE)
helm install --set kubernetes.deployment.replicasCount=1,kubernetes.deployment.imagePullPolicy=Never,application.logLevel=DEBUG connaisseur helm --atomic --create-namespace --namespace $(NAMESPACE)

uninstall:
helm uninstall connaisseur -n $(NAMESPACE)
Expand Down
6 changes: 6 additions & 0 deletions connaisseur/trust_root.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,17 @@ class ECDSAKey(TrustRootInterface):
def __str__(self) -> str:
return base64.b64encode(self.value.to_der()).decode("utf-8")

def pem(self):
return self.value.to_pem()


class RSAKey(TrustRootInterface):
def __str__(self) -> str:
return base64.b64encode(self.value.save_pkcs1("DER")).decode("utf-8")

def pem(self):
return self.value.save_pkcs1("PEM")


class KMSKey(TrustRootInterface):
pass
Expand Down
12 changes: 8 additions & 4 deletions connaisseur/validators/cosign/cosign_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
WrongKeyError,
)
from connaisseur.image import Image
from connaisseur.trust_root import ECDSAKey, KMSKey, TrustRoot
from connaisseur.trust_root import ECDSAKey, KMSKey, RSAKey, TrustRoot
from connaisseur.util import safe_path_func # nosec
from connaisseur.validators.interface import ValidatorInterface

Expand Down Expand Up @@ -213,7 +213,11 @@ async def __get_cosign_validated_digests(self, image: str, values: dict):
image=str(image),
trust_root=values["name"],
)
elif "Error: no matching signatures:\n\nmain.go:" in stderr:
elif (
"Error: no matching signatures:\n\nmain.go:" in stderr
or "Error: no matching signatures:\ncrypto/rsa: verification error"
in stderr
):
msg = 'No trust data for image "{image}".'
raise NotFoundException(
message=msg,
Expand Down Expand Up @@ -269,13 +273,13 @@ async def __validate_using_trust_root(
# reminder when implementing Keyless validation:
# ["--cert-email", self.value, b""]

if isinstance(trust_root, ECDSAKey):
if isinstance(trust_root, (ECDSAKey, RSAKey)):
return await self.__invoke_cosign(
image,
{
"option_kword": "--key",
"inline_tr": "/dev/stdin",
"trust_root": trust_root.value.to_pem(),
"trust_root": trust_root.pem(),
},
verify_tlog,
)
Expand Down
2 changes: 1 addition & 1 deletion docs/validators/sigstore_cosign.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ kubectl run altsigned --image=docker.io/securesystemsengineering/testimage:co-si
| `name` | - | :heavy_check_mark: | See [basics](../basics.md#validators). |
| `type` | - | :heavy_check_mark: | `cosign`; the validator type must be set to `cosign`. |
| `trustRoots[*].name` | - | :heavy_check_mark: | See [basics](../basics.md#validators). |
| `trustRoots[*].key` | - | :heavy_check_mark: | See [basics](../basics.md#validators). ECDSA public key from `cosign.pub` file or [KMS URI](https://github.com/sigstore/cosign/blob/main/KMS.md). See additional notes [below](#kms-support). |
| `trustRoots[*].key` | - | :heavy_check_mark: | See [basics](../basics.md#validators). ECDSA/RSA public key from `cosign.pub` file or [KMS URI](https://github.com/sigstore/cosign/blob/main/KMS.md). See additional notes [below](#kms-support). |
| `host.rekor` | `rekor.sigstore.dev` | - | Rekor URL to use for validation against the transparency log (default sigstore instance is `rekor.sigstore.dev`). Setting `host` enforces successful transparency log check to pass verification. See additional notes [below](#transparency-log-verification). |
| `auth.` | - | - | Authentication credentials for registries with restricted access (e.g. private registries or ratelimiting). See additional notes [below](#authentication). |
| `auth.secretName` | - | - | Name of a Kubernetes secret in Connaisseur namespace that contains [dockerconfigjson](https://kubernetes.io/docs/concepts/configuration/secret/#docker-config-secrets) for registry authentication. See additional notes [below](#dockerconfigjson). |
Expand Down
7 changes: 7 additions & 0 deletions tests/integration/cases.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ test_cases:
txt: Testing signed cosign image with tag and digest...
type: deploy
ref: securesystemsengineering/testimage:co-signed@sha256:c5327b291d702719a26c6cf8cc93f72e7902df46547106a9930feda2c002a4a7
- id: crsa
txt: Testing signed cosign image, signed with RSA key
type: deploy
ref: securesystemsengineering/testimage:rsa-co-signed
namespace: default
expected_msg: pod/pod-crsa-${RAND} created
expected_result: null
multi-cosigned:
- id: mc-u
txt: Testing multi-cosigned image `threshold` => undefined, not reached...
Expand Down
18 changes: 18 additions & 0 deletions tests/integration/update.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,20 @@ application:
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqxgd/RqCdPnafQmlmX71eICGRBqu
USHEjAv3FZCROHLYts11xR6Peu8ZEvMXOR46L7+z84DRFK6gnTInbIGFmg==
-----END PUBLIC KEY-----
- name: rsa
type: cosign
trustRoots:
- name: default
key: |
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxmEXG8savZ/Q8IJB8dBT
YCKV/ECwkj8zBInilrWSipsKiBwGTugAgKHj7Nvo6pg91DTESpfnryL+UUwAyJ1C
irdUThCZa90vC9SlwYUhC/ftz/dwU8KaiVcWJHCbj4VLLCD7xVKPh65j4x65D8bL
ohbrpZFfboXgG/gJHYhU18q0nmGzuQyGWSxAYcsh8qVcaNa68TvZLqecq/AYvspI
qNIGWekU1BYXoUVt6kBx/fwEKtxESRbgsT1R8ha+q1HTGLMtj71LfWfX9d1bbNeq
2+pXRO8Ut8km2lGEekRNXYb2C+sOX7uA1MSv+gm2JCoVzep69fGTHbqOwf4tm2Qh
AwIDAQAB
-----END PUBLIC KEY-----
policy:
- pattern: "*:*"
validator: dockerhub-basics
Expand Down Expand Up @@ -116,6 +130,10 @@ application:
with:
trustRoot: securesystemsengineering-official
delegations: ["belitzphilipp", "starkteetje"]
- pattern: "docker.io/securesystemsengineering/testimage:rsa-*"
validator: rsa
with:
verifyInTransparencyLog: false
features:
detectionMode: false
automaticChildApproval: true
18 changes: 18 additions & 0 deletions tests/test_trust_root.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@

sample_ecdsa2 = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEi2WD/E/UXF4+yoE5e4cjpJMNgQw\n8PAVALRX+8f8I8B+XneAtnOHDTI8L6wBeFRTzl6G4OmgDyCRYTb5MV3hog==\n-----END PUBLIC KEY-----"

ecdsa_bytes = b"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOXYta5TgdCwXTCnLU09W5T4M4r9f\nQQrqJuADP6U7g5r9ICgPSmZuRHP/1AYUfOQW3baveKsT969EfELKj1lfCA==\n-----END PUBLIC KEY-----\n"
# for some reason the public key differs from the read one :shrug:
rsa_bytes = b"-----BEGIN RSA PUBLIC KEY-----\nMIIBCgKCAQEAs5pC7R5OTSTUMJHUniPkrLfmGDAUxZtRlvIE+pGPCD6cUXH22adv\nkK87xwpupjxdVYuKTFnWHUIyFJwjI3vusievezcAr0E/xxyeo49tWog9kFoooK3q\nmXjpETC8OpvNROZ0K3qhlm9PZkGo3gSJ/B4rMU/d+jkCI8eiUPpdVQOczdBoD5nz\nQAF1mfmffWGsbKY+d8/l77Vset0GXExRzUtnglMhREyHNpDeQUg5OEn+kuGLlTzI\nxpIF+MlbzP3+xmNEzH2iafr0ae2g5kX2880priXpxG8GXW2ybZmPvchclnvFu4Zf\nZcM10FpgYJFvR/9iofFeAka9u5z6VZccmQIDAQAB\n-----END RSA PUBLIC KEY-----\n"


def cb(image, key_args):
return key_args[:2]
Expand Down Expand Up @@ -82,3 +86,17 @@ def test_keys(data, class_, exception):
def test_str(key, out):
k = trust_root.TrustRoot(key)
assert str(k) == out


@pytest.mark.parametrize(
"key, out, exception",
[
(sample_ecdsa, ecdsa_bytes, fix.no_exc()),
(sample_rsa, rsa_bytes, fix.no_exc()),
(sample_mail, b"", pytest.raises(AttributeError)),
],
)
def test_pem(key, out, exception):
k = trust_root.TrustRoot(key)
with exception:
assert k.pem() == out

0 comments on commit 64cea52

Please sign in to comment.